lankasailendra/BMCTESTMAIN
0
1"""Offline preflight for the client-executor UX fixes (2026-09-09).2 3 python scripts/preflight_executor_ux.py # exits non-zero on any failure4 5No tenant, no network, no app run. Covers:6 * a tenant with no CI never blocks a run — CMDB/Asset in scope or not — and7 is reported as a warning that says the rest of the suite still runs;8 * executor_prep's colour helpers and the bmc_prefetch_echo.json fallback that9 the failure message now points at;10 * the installer's supported-Python detection (the 3.14 greenlet failure);11 * the .bat files: balanced blocks, no bare `python` call, colour + settings12 wired, and the run banner present.13"""14import os15import re16import subprocess17import sys18import tempfile19from pathlib import Path20 21REPO = Path(__file__).resolve().parent.parent22sys.path.insert(0, str(REPO / "reference" / "itsm"))23sys.path.insert(0, str(REPO / "scripts"))24 25FAILS: list[str] = []26 27 28def check(label: str, ok: bool, detail: str = "") -> None:29 print(f"[{'ok ' if ok else 'FAIL'}] {label}" + (f"\n {detail}" if detail and not ok else ""))30 if not ok:31 FAILS.append(label)32 33 34# ---------------------------------------------------------------- CI gating35import instance_runtime as ir # noqa: E40236 37BASE_OK = {38 "company": "Acme", "support_group": "Service Desk",39 "op_cat_tier1": "Hardware", "product_cat_tier1": "Software",40 "product_cat_name": "Windows", "discovery_modules": ["incident"],41 "ci_recon_identity": "", "ci_name": "", # the failing tenant42}43 44os.environ.pop("BMC_CLIENT_MODULES", None)45errs = ir.validate_runtime_prefetch(dict(BASE_OK))46check("INC run with no CI: not fatal", not any("ci_" in e for e in errs), f"errors={errs}")47warns = ir.runtime_prefetch_warnings(dict(BASE_OK))48check("INC run with no CI: warns instead", any("AST:ComputerSystem" in w for w in warns), f"warnings={warns}")49 50os.environ["BMC_CLIENT_MODULES"] = "Incident, CMDB"51errs = ir.validate_runtime_prefetch(dict(BASE_OK))52# A tenant with no CI must never block a run, CMDB in scope or not: the CMDB and53# Asset cases fail on the missing CI, the rest of the suite still runs. This54# reversed an earlier gate that made the whole run fatal.55check("CMDB run with no CI: still not fatal", not any("ci_" in e for e in errs), f"errors={errs}")56_w = ir.runtime_prefetch_warnings(dict(BASE_OK))57check(58 "CMDB run with no CI: warns instead",59 any("AST:ComputerSystem" in w for w in _w),60 f"warnings={_w}",61)62check(63 "CMDB run with no CI: the warning says the rest of the suite still runs",64 any("rest of the suite still" in w for w in _w),65 f"warnings={_w}",66)67 68os.environ["BMC_CLIENT_MODULES"] = "Incident"69cfg = dict(BASE_OK, ci_name="ci-1", ci_recon_identity="recon-1")70check("CI present: no warning", ir.runtime_prefetch_warnings(cfg) == [])71check("unrelated gaps still fatal",72 any("company" in e for e in ir.validate_runtime_prefetch(dict(BASE_OK, company=""))))73 74smoke = dict(BASE_OK, _smoke_runtime_cache=True)75check("smoke cache with no CI: not fatal",76 not any("ci_" in e for e in ir.validate_runtime_prefetch(smoke)))77 78# --------------------------------------------------- executor_prep colour/echo79import executor_prep as ep # noqa: E40280 81check("colour helpers exist", all(hasattr(ep, n) for n in ("_err", "_warn", "_ok", "_dump_prefetch_echo")))82check("colour off when piped", ep._COLOR is False, "stdout is not a tty here, so colour must be disabled")83with tempfile.TemporaryDirectory() as td:84 out = ep._dump_prefetch_echo(Path(td), {"form_echo": [{"form": "AST:ComputerSystem", "status": 200, "rows": 0}]})85 check("failed prefetch writes bmc_prefetch_echo.json",86 out is not None and out.is_file() and "AST:ComputerSystem" in out.read_text())87 check("no echo captured -> no empty file", ep._dump_prefetch_echo(Path(td), {}) is None)88 89# ------------------------------------------------------------- installer90import install_client_executor as ice # noqa: E40291 92check("3.12 supported", ice._py_supported((3, 12)))93check("3.11 supported", ice._py_supported((3, 11)))94check("3.14 rejected (the greenlet failure)", not ice._py_supported((3, 14)))95check("3.13 rejected", not ice._py_supported((3, 13)))96check("3.9 rejected", not ice._py_supported((3, 9)))97check("unsupported message names the cause",98 "greenlet" in ice._unsupported_python_message() and "3.12" in ice._unsupported_python_message())99check("probe rejects a bogus interpreter", ice._probe_python(["definitely-not-python"]) == "")100check("probe accepts this python when supported",101 bool(ice._probe_python([sys.executable])) == ice._py_supported())102 103# ------------------------------------------------------------------ .bat files104BAT = REPO / "reference" / "client_executor" / "testexecute-itsm.bat"105bat = BAT.read_text(encoding="utf-8", errors="replace")106 107check("PREFETCH_ECHO is a USER SETTING",108 re.search(r"USER SETTINGS.*?set PREFETCH_ECHO=0.*?DO NOT EDIT", bat, re.S) is not None)109check("PREFETCH_ECHO maps to BMC_PREFETCH_ECHO", 'if "%PREFETCH_ECHO%"=="1" set BMC_PREFETCH_ECHO=1' in bat)110check("COLOR setting maps to BMC_NO_COLOR", 'if "%COLOR%"=="0" set BMC_NO_COLOR=1' in bat)111check("errors are coloured", bat.count("%C_ERR%ERROR") >= 5)112check("run banner present", "TEST RUN" in bat or "RUNNING TEST SCRIPTS" in bat)113check("run banner names the report paths", "HTML report" in bat and "Live view" in bat)114check("venv python preferred", '.venv\\Scripts\\python.exe" set "PY=' in bat)115check("no bare python invocation",116 not re.search(r"(?m)^\s*(start [^\n]*/MIN )?python ", bat),117 "every call must go through %PY%")118check("no unescaped redirection in the version probe", "^<=" not in bat)119 120opens = len(re.findall(r"\(\s*$", bat, re.M))121closes = len(re.findall(r"(?m)^\s*\)", bat))122check("if-blocks balanced", opens == closes, f"{opens} open vs {closes} close")123 124INSTALL_BAT = REPO / "Install-HelixCoPilot-Client.bat"125ib = INSTALL_BAT.read_text(encoding="utf-8", errors="replace")126check("installer bat falls back to py -3", 'set "PY=py -3"' in ib)127check("installer bat tells the user what to install", "python.org/downloads" in ib)128 129RUN = REPO / "reference" / "client_executor" / "itsm_runtime" / "run_tests.py"130rt = RUN.read_text(encoding="utf-8")131check("run_tests prints a start banner", "TEST RUN STARTING" in rt and "_count_selected_tests" in rt)132check("run_tests prints the pytest command", "Command :" in rt)133check("run_tests prints a verdict", "TEST RUN FINISHED" in rt)134 135for path in (136 REPO / "reference" / "itsm" / "instance_runtime.py",137 REPO / "reference" / "itsm" / "executor_prep.py",138 RUN,139 REPO / "scripts" / "install_client_executor.py",140):141 r = subprocess.run([sys.executable, "-m", "py_compile", str(path)], capture_output=True, text=True)142 check(f"compiles: {path.name}", r.returncode == 0, r.stderr.strip())143 144print(f"\n{'ALL PASS' if not FAILS else str(len(FAILS)) + ' FAILURE(S): ' + ', '.join(FAILS)}")145sys.exit(1 if FAILS else 0)146 