From 85ed53e6fe6669c143f1644cb394d25ae36a0061 Mon Sep 17 00:00:00 2001 From: SANVI SHUKLA Date: Tue, 31 Mar 2026 20:10:41 +0530 Subject: [PATCH 1/2] Add guardrails and smoke harness for MUIO v5.5 pull (#389) --- scripts/smoke.bat | 5 +++ scripts/smoke.sh | 9 +++++ scripts/verify_base.sh | 78 ++++++++++++++++++++++++++++++++++++++++++ tests/smoke_test.py | 55 +++++++++++++++++++++++++++++ 4 files changed, 147 insertions(+) create mode 100644 scripts/smoke.bat create mode 100755 scripts/smoke.sh create mode 100755 scripts/verify_base.sh create mode 100644 tests/smoke_test.py diff --git a/scripts/smoke.bat b/scripts/smoke.bat new file mode 100644 index 00000000..6a53a276 --- /dev/null +++ b/scripts/smoke.bat @@ -0,0 +1,5 @@ +@echo off +REM smoke.bat - Windows wrapper for the minimal stdlib unittest smoke harness. +echo Running MUIOGO Smoke Tests... +python "%~dp0..\tests\smoke_test.py" +pause diff --git a/scripts/smoke.sh b/scripts/smoke.sh new file mode 100755 index 00000000..de28a0f4 --- /dev/null +++ b/scripts/smoke.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# smoke.sh - Wrapper for the minimal stdlib unittest smoke harness. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +echo "Running MUIOGO Smoke Tests..." +python3 "$PROJECT_ROOT/tests/smoke_test.py" diff --git a/scripts/verify_base.sh b/scripts/verify_base.sh new file mode 100755 index 00000000..6b78b216 --- /dev/null +++ b/scripts/verify_base.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# verification_checklist.sh - High-level sanity check for MUIOGO base. +# Fail if any command fails. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +BOLD="\033[1m" +GREEN="\033[92m" +YELLOW="\033[93m" +RED="\033[91m" +RESET="\033[0m" + +# Disable colours when not in a real terminal +if [ ! -t 1 ]; then + BOLD="" GREEN="" YELLOW="" RED="" RESET="" +fi + +_print_header() { + echo -e "\n${BOLD}=== $1 ===${RESET}" +} + +_print_pass() { echo -e " ${GREEN}[PASS]${RESET} $1"; } +_print_fail() { echo -e " ${RED}[FAIL]${RESET} $1"; } +_print_warn() { echo -e " ${YELLOW}[WARN]${RESET} $1"; } + +# 1. Environment & Solvers check +_print_header "Step 1: setup.sh --check" +# We skip demo-data in the smoke check as it's not strictly required for API/Backend sanity. +if "$SCRIPT_DIR/setup.sh" --check --no-demo-data; then + _print_pass "Environment and solvers are ready." +else + _print_fail "Environment check failed. Run ./scripts/setup.sh first." + exit 1 +fi + +# 2. Syntax Check (py_compile) +_print_header "Step 2: Python syntax check" +python3 -m py_compile "$PROJECT_ROOT/API/app.py" +_print_pass "app.py is syntactically valid." + +# 3. Smoke Test Harness +_print_header "Step 3: smoke_test.py (unittest)" +if python3 "$PROJECT_ROOT/tests/smoke_test.py"; then + _print_pass "Smoke tests passed." +else + _print_fail "Smoke tests failed." + exit 1 +fi + +# 4. Unresolved Merge state +_print_header "Step 4: Check for unresolved git merge/rebase" +if [ -d "$PROJECT_ROOT/.git" ]; then + if [ -f "$PROJECT_ROOT/.git/MERGE_HEAD" ] || [ -f "$PROJECT_ROOT/.git/REBASE_HEAD" ] || [ -f "$PROJECT_ROOT/.git/CHERRY_PICK_HEAD" ]; then + _print_fail "Unresolved git merge/rebase detected (.git/MERGE_HEAD or similar exists)." + exit 1 + else + _print_pass "No unresolved git merge/rebase found." + fi +else + _print_warn ".git directory not found (skipping git state check)." +fi + +# 5. Check for conflict markers +_print_header "Step 5: Check for conflict markers" +# Use ^ to match markers at the beginning of the line to avoid decorative comments +if grep -rnE "^<<<<<<<|^=======|^>>>>>>>" "$PROJECT_ROOT/API" "$PROJECT_ROOT/WebAPP/App" --exclude-dir=__pycache__ > /dev/null 2>&1; then + _print_fail "Conflict markers (<<<<<<<, =======, >>>>>>>) found in the codebase!" + grep -rnE "^<<<<<<<|^=======|^>>>>>>>" "$PROJECT_ROOT/API" "$PROJECT_ROOT/WebAPP/App" --exclude-dir=__pycache__ + exit 1 +else + _print_pass "No conflict markers found in key backend/frontend folders." +fi + +_print_header "Verification Complete" +_print_pass "Codebase is healthy and ready for upstream pull." +echo "" diff --git a/tests/smoke_test.py b/tests/smoke_test.py new file mode 100644 index 00000000..ef7c3314 --- /dev/null +++ b/tests/smoke_test.py @@ -0,0 +1,55 @@ +import unittest +import sys +import os +from pathlib import Path + +# Add project root to sys.path to allow importing API.app +PROJECT_ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(PROJECT_ROOT / "API")) + +class SmokeTest(unittest.TestCase): + def setUp(self): + """Initialize the Flask test client.""" + try: + from app import app + app.config['TESTING'] = True + app.config['DEBUG'] = False + self.app = app.test_client() + except ImportError as e: + self.fail(f"Failed to import API.app: {e}") + except Exception as e: + self.fail(f"Failed to initialize Flask app: {e}") + + def test_app_import(self): + """Basic check if the app module can be imported.""" + import app + self.assertIsNotNone(app.app) + + def test_index_route(self): + """Check if the home route returns 200.""" + response = self.app.get('/') + self.assertEqual(response.status_code, 200) + + def test_get_session_route(self): + """Check if /getSession returns 200 (even if session is empty).""" + response = self.app.get('/getSession') + self.assertEqual(response.status_code, 200) + self.assertIn('session', response.get_json()) + + def test_get_cases_route(self): + """Check if /getCases returns 200 or 404 (handled error).""" + response = self.app.get('/getCases') + # /getCases returns 200 with list or 404 if directory error + self.assertIn(response.status_code, [200, 404]) + + def test_config_validity(self): + """Verify that the Config class resolves paths correctly.""" + from Classes.Base import Config + self.assertTrue(Config.BASE_DIR.exists()) + self.assertTrue(Config.WEBAPP_PATH.exists()) + # Note: We don't check for writability here as per 'read-only' constraint, + # but we check if the paths are resolved to the correct absolute locations. + self.assertIn("MUIOGO", str(Config.BASE_DIR)) + +if __name__ == '__main__': + unittest.main() From 1b58afe5ffcd7952e0f3718add3a734a9e670a63 Mon Sep 17 00:00:00 2001 From: SANVI SHUKLA Date: Tue, 31 Mar 2026 20:19:12 +0530 Subject: [PATCH 2/2] Refine verification script output and add maintainer notes to header --- scripts/verify_base.sh | 83 ++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/scripts/verify_base.sh b/scripts/verify_base.sh index 6b78b216..7cc41936 100755 --- a/scripts/verify_base.sh +++ b/scripts/verify_base.sh @@ -1,6 +1,22 @@ #!/usr/bin/env bash -# verification_checklist.sh - High-level sanity check for MUIOGO base. -# Fail if any command fails. +# ============================================================================== +# MUIOGO VERIFICATION GATEWAY +# ============================================================================== +# This script is the primary safety gate for upstream pulls from OSeMOSYS/MUIO. +# It ensures that MUIOGO's portability, security, and stability are preserved. +# +# MAINTAINER NOTES (Always Review these files during a v5.x pull): +# 1. API/app.py - Core Flask config and blueprint registration. +# 2. API/Classes/Case/OsemosysClass.py - Model execution logic. +# 3. API/Routes/Case/CaseRoute.py - Backend API for case management. +# 4. WebAPP/Classes/Osemosys.Class.js - Frontend solver coordination. +# +# REJECTED PATTERNS: +# - Hardcoded paths (e.g. C:\) +# - Insecure session/path handling +# - Breaking Python 3.10-3.12 support +# ============================================================================== + set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -21,58 +37,61 @@ _print_header() { echo -e "\n${BOLD}=== $1 ===${RESET}" } -_print_pass() { echo -e " ${GREEN}[PASS]${RESET} $1"; } -_print_fail() { echo -e " ${RED}[FAIL]${RESET} $1"; } -_print_warn() { echo -e " ${YELLOW}[WARN]${RESET} $1"; } +_print_status() { + if [ "$1" -eq 0 ]; then + echo -e " ${GREEN}[PASS]${RESET} $2" + else + echo -e " ${RED}[FAIL]${RESET} $2" + [ -n "${3:-}" ] && echo -e "\n${3}" + exit 1 + fi +} # 1. Environment & Solvers check -_print_header "Step 1: setup.sh --check" -# We skip demo-data in the smoke check as it's not strictly required for API/Backend sanity. -if "$SCRIPT_DIR/setup.sh" --check --no-demo-data; then - _print_pass "Environment and solvers are ready." +_print_header "Step 1: Environment & Solvers" +# Run setup check quietly, only show output on failure +if OUTPUT=$("$SCRIPT_DIR/setup.sh" --check --no-demo-data 2>&1); then + _print_status 0 "Python venv and solvers (GLPK/CBC) are ready." else - _print_fail "Environment check failed. Run ./scripts/setup.sh first." - exit 1 + _print_status 1 "Environment check failed. Run ./scripts/setup.sh first." "$OUTPUT" fi # 2. Syntax Check (py_compile) -_print_header "Step 2: Python syntax check" -python3 -m py_compile "$PROJECT_ROOT/API/app.py" -_print_pass "app.py is syntactically valid." +_print_header "Step 2: Python Syntax Check" +if python3 -m py_compile "$PROJECT_ROOT/API/app.py" 2>/dev/null; then + _print_status 0 "app.py is syntactically valid." +else + _print_status 1 "Syntax error detected in app.py." +fi # 3. Smoke Test Harness -_print_header "Step 3: smoke_test.py (unittest)" -if python3 "$PROJECT_ROOT/tests/smoke_test.py"; then - _print_pass "Smoke tests passed." +_print_header "Step 3: API & Route Smoke Tests" +if OUTPUT=$(python3 "$PROJECT_ROOT/tests/smoke_test.py" 2>&1); then + _print_status 0 "Smoke tests passed (API boot and core routes)." else - _print_fail "Smoke tests failed." - exit 1 + _print_status 1 "Smoke tests failed." "$OUTPUT" fi # 4. Unresolved Merge state -_print_header "Step 4: Check for unresolved git merge/rebase" +_print_header "Step 4: Git Merge Integrity" if [ -d "$PROJECT_ROOT/.git" ]; then if [ -f "$PROJECT_ROOT/.git/MERGE_HEAD" ] || [ -f "$PROJECT_ROOT/.git/REBASE_HEAD" ] || [ -f "$PROJECT_ROOT/.git/CHERRY_PICK_HEAD" ]; then - _print_fail "Unresolved git merge/rebase detected (.git/MERGE_HEAD or similar exists)." - exit 1 + _print_status 1 "Unresolved git merge/rebase detected (.git/MERGE_HEAD exists)." else - _print_pass "No unresolved git merge/rebase found." + _print_status 0 "No unresolved git merge/rebase state found." fi else - _print_warn ".git directory not found (skipping git state check)." + echo -e " ${YELLOW}[SKIP]${RESET} Not a git repository." fi -# 5. Check for conflict markers -_print_header "Step 5: Check for conflict markers" +# 5. Conflict Markers scan +_print_header "Step 5: Conflict Marker Scan" # Use ^ to match markers at the beginning of the line to avoid decorative comments if grep -rnE "^<<<<<<<|^=======|^>>>>>>>" "$PROJECT_ROOT/API" "$PROJECT_ROOT/WebAPP/App" --exclude-dir=__pycache__ > /dev/null 2>&1; then - _print_fail "Conflict markers (<<<<<<<, =======, >>>>>>>) found in the codebase!" + _print_status 1 "Conflict markers found in the codebase!" grep -rnE "^<<<<<<<|^=======|^>>>>>>>" "$PROJECT_ROOT/API" "$PROJECT_ROOT/WebAPP/App" --exclude-dir=__pycache__ - exit 1 else - _print_pass "No conflict markers found in key backend/frontend folders." + _print_status 0 "No conflict markers found in key backend/frontend folders." fi -_print_header "Verification Complete" -_print_pass "Codebase is healthy and ready for upstream pull." -echo "" +echo -e "\n${BOLD}${GREEN}Verification Complete: MUIOGO is healthy and ready for upstream pull.${RESET}\n"