8d5a05a419
- Implemented smoke tests for UOM directionality, ensuring conversions are one-directional and correctly validated. - Added tests for receiving and selling items in different UOMs, verifying correct quantity handling and error responses. - Created a UOM conversions panel in the frontend to allow users to manage UOM conversions for items. - Introduced hooks for allowed UOMs to optimize fetching and caching of UOM data for document line forms. - Developed utility functions for consistent UOM formatting and conversion handling across the application.
76 lines
2.8 KiB
Python
76 lines
2.8 KiB
Python
"""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())
|