lankasailendra/BMCTESTMAIN
0
1"""Preflight: reference/itsm/module_forms.json is valid and complete.2 3Run after ANY hand edit to the module->form map, tenant override included:4 python scripts/preflight_module_forms.py5 BMC_MODULE_FORMS=/path/to/tenant_module_forms.json python scripts/preflight_module_forms.py6 7Catches the edits that fail silently at run time: a module the catalog uses but8the map does not cover, a form name that is not a real AR form name, a label9that no longer matches the catalog, and an override that removed a core module.10"""11 12import json13import os14import re15import sys16 17ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))18sys.path.insert(0, os.path.join(ROOT, "src"))19 20from module_forms import load_module_forms, scan_plan # noqa: E40221 22CATALOG = os.path.join(ROOT, "reference", "itsm", "regression_catalog.json")23 24# Modules whose cases exist in the catalog but that have no AR form of their own.25NO_FORM_MODULES = {"helixgpt"}26 27# Core modules: a tenant override may re-point them, never delete them.28CORE = {"incident", "change", "work_order", "problem", "known_error"}29 30FORM_RE = re.compile(r"^[A-Za-z][A-Za-z0-9_. ]*:[A-Za-z0-9_][A-Za-z0-9_ .\-]*$")31 32failures: list = []33notes: list = []34 35 36def fail(msg):37 failures.append(msg)38 39 40def main() -> int:41 cfg = load_module_forms()42 mods = cfg.get("modules") or {}43 44 if not mods:45 fail("module_forms.json loaded no modules at all")46 print_report()47 return 148 49 # 1. shape50 for key, entry in sorted(mods.items()):51 if key != key.lower() or " " in key:52 fail(f"module key {key!r} must be lower_snake_case")53 if not entry.get("forms"):54 fail(f"module {key!r} lists no forms")55 for form in entry.get("forms") or []:56 if not FORM_RE.match(form):57 fail(f"module {key!r}: {form!r} is not a valid AR form name")58 if not str(entry.get("label") or "").strip():59 fail(f"module {key!r} has no label")60 61 # 2. core modules survive every override62 for key in sorted(CORE - set(mods)):63 fail(f"core module {key!r} is missing - an override must not remove it")64 65 # 3. every catalog module is covered66 try:67 with open(CATALOG, encoding="utf-8") as fh:68 cases = json.load(fh).get("test_cases") or []69 except Exception as exc:70 fail(f"cannot read the catalog: {exc}")71 cases = []72 73 labels = {str(c.get("module") or "").strip() for c in cases}74 labels.discard("")75 known = {str(e.get("label") or "").strip().lower() for e in mods.values()}76 known |= {k.replace("_", " ") for k in mods}77 for label in sorted(labels):78 if label.lower() in NO_FORM_MODULES:79 continue80 if label.lower() not in known:81 fail(82 f"catalog module {label!r} maps to no entry - its cases will get "83 f"no required fields"84 )85 86 # 4. a label must not point at two modules87 seen: dict = {}88 for key, entry in mods.items():89 lab = str(entry.get("label") or "").strip().lower()90 if lab in seen:91 fail(f"label {entry.get('label')!r} is used by both {seen[lab]!r} and {key!r}")92 seen[lab] = key93 94 # 5. the scan plan is sane, scoped and unscoped95 forms, optional, pairs = scan_plan()96 for form in cfg.get("always") or []:97 if form not in forms:98 fail(f"'always' form {form!r} is not in the unscoped scan plan")99 if len(forms) != len(set(forms)):100 fail("the scan plan lists a form twice")101 for key in sorted(CORE & set(mods)):102 scoped, _opt, _p = scan_plan([key])103 for form in mods[key]["forms"]:104 if form not in scoped:105 fail(f"scan_plan([{key!r}]) dropped {form!r}")106 for form in cfg.get("always") or []:107 if form not in scoped:108 fail(f"scan_plan([{key!r}]) dropped the always-form {form!r}")109 110 # 6. a form shared by an optional and a required module stays required111 for form in sorted(optional):112 for key, entry in mods.items():113 if form in entry["forms"] and not entry.get("optional"):114 fail(f"{form!r} is silenced but required module {key!r} needs it")115 116 notes.append(f"{len(mods)} module(s), {len(forms)} form(s) in a full scan")117 notes.append(f"{len(optional)} form(s) may 403/404 silently")118 if os.environ.get("BMC_MODULE_FORMS"):119 notes.append(f"override applied: {os.environ['BMC_MODULE_FORMS']}")120 121 print_report()122 return 1 if failures else 0123 124 125def print_report():126 for n in notes:127 print(f" - {n}")128 if failures:129 print(f"\nFAIL ({len(failures)}):")130 for f in failures:131 print(f" x {f}")132 else:133 print("ALL PASS")134 135 136if __name__ == "__main__":137 sys.exit(main())138 