"""Run the whole manufacturing smoke suite in dependency order. python Backend/smoke/run_all.py Order matters: m4 creates the isolated SMOKE-PRD warehouse and drains it, m5 consumes the run m4 leaves with its terminal stage InProgress. Each script is individually re-runnable, but m5 deliberately refuses to run twice against an already-approved terminal stage — that guard is what stops it silently asserting against the wrong state. Prerequisites: ERPCore on :5224 and AuthHex on :5602 (override with ERP_SMOKE_API / ERP_SMOKE_AUTH / ERP_SMOKE_USER / ERP_SMOKE_PASSWORD). """ from __future__ import annotations import re import subprocess import sys from pathlib import Path SCRIPTS = [ ("M2 templates + graph validation", "m2_templates.py"), ("M3 run creation / board / quantities", "m3_runs.py"), ("M4 stage start / complete / approve / transfer", "m4_stage_actions.py"), ("M4b UOM conversion on stock inputs", "m4b_uom_conversion.py"), ("M5 terminal receipt + cost pool", "m5_receipt.py"), ("M6+M7 leftover / rework / cancel", "m6_m7_leftover_rework_cancel.py"), # UOM engine. These run after the manufacturing scripts because they reuse the # SMOKE-PRD warehouse m4 creates, and they drain it before seeding their own stock. ("UOM conversion direction is enforced", "uom_direction.py"), ("UOM non-base sales consume converted qty", "uom_sales_nonbase.py"), ("UOM cross-unit GRN against a PO", "uom_grn_po_cross.py"), ] SUMMARY = re.compile(r"^(\S+): (\d+)/(\d+) assertions passed") def main() -> int: here = Path(__file__).parent results = [] failed = False for label, script in SCRIPTS: print(f"\n{'=' * 70}\n{label} ({script})\n{'=' * 70}", flush=True) proc = subprocess.run( [sys.executable, script, *sys.argv[1:]], cwd=here, capture_output=True, text=True, encoding="utf-8", errors="replace", ) sys.stdout.write(proc.stdout) if proc.stderr.strip(): sys.stderr.write(proc.stderr) passed = total = 0 for line in proc.stdout.splitlines(): m = SUMMARY.match(line.strip()) if m: passed, total = int(m.group(2)), int(m.group(3)) results.append((label, passed, total, proc.returncode)) if proc.returncode != 0: failed = True print(f"\n{'=' * 70}\nSUITE SUMMARY\n{'=' * 70}") grand_passed = grand_total = 0 for label, passed, total, rc in results: grand_passed += passed grand_total += total state = "OK " if rc == 0 else "FAIL" print(f" [{state}] {label:<48} {passed}/{total}") print(f"\n TOTAL: {grand_passed}/{grand_total} assertions" + (" — ALL GREEN" if not failed else " (SUITE FAILED)")) return 1 if failed else 0 if __name__ == "__main__": sys.exit(main())