"""Versioned journey registry and mandatory collected-test coverage validation.""" import json from pathlib import Path ROOT = Path(__file__).resolve().parents[2] REGISTRY = ROOT / "docs/usecases/registry.json" def load_cases(): data = json.loads(REGISTRY.read_text(encoding="utf-8")) if data.get("schema_version") != 1 or not isinstance(data.get("cases"), list): raise ValueError("Unsupported use-case registry schema") cases = data["cases"] if not cases: raise ValueError("The use-case registry cannot be empty") ids = set() for case in cases: uid = case["id"] if uid in ids: raise ValueError(f"Duplicate use-case id: {uid}") ids.add(uid) if case["confirmation"] not in {"inferred", "observed", "user-confirmed"}: raise ValueError(f"Unknown confirmation for {uid}") if case["transfer_status"] not in {"pending-user-review", "accepted", "deferred"}: raise ValueError(f"Unknown transfer status for {uid}") if case["transfer_status"] == "accepted" and not case.get("acceptance_ref"): raise ValueError(f"Accepted case {uid} needs a user acceptance reference") for field in ("narrative", "fixture"): path = (ROOT / case[field]).resolve() if not path.is_relative_to(ROOT) or not path.is_file(): raise ValueError(f"Missing or outside-repo {field} for {uid}") fixture = json.loads((ROOT / case["fixture"]).read_text(encoding="utf-8")) if fixture.get("synthetic") is not True or fixture.get("usecase_id") != uid: raise ValueError(f"Fixture must be synthetic and linked to {uid}") if not isinstance(case["established"], bool): raise ValueError(f"established must be a boolean for {uid}") if set(case["tests"]) != {"fast", "browser"}: raise ValueError(f"Expected fast and browser test layers for {uid}") if case["established"]: if case["confirmation"] != "user-confirmed" or not case["contract"]: raise ValueError(f"Established case {uid} needs user confirmation and contract") for layer in ("fast", "browser"): if not case["tests"].get(layer): raise ValueError(f"Established case {uid} missing {layer} coverage") return cases def validate_coverage(cases, collected): """Require exact node IDs, UC marker, and correct fast/browser layer.""" ids = {case["id"] for case in cases} for nodeid, markers in collected.items(): if markers["usecases"] - ids: raise ValueError(f"Use-case test has an unregistered ID: {nodeid}") for case in cases: if not case["established"]: continue for layer, nodeids in case["tests"].items(): for nodeid in nodeids: markers = collected.get(nodeid) if markers is None: raise ValueError(f"{case['id']} missing collected {layer} test: {nodeid}") if case["id"] not in markers["usecases"]: raise ValueError(f"{nodeid} is not marked for {case['id']}") if markers.get("disabled"): raise ValueError(f"Protected use-case test cannot skip/xfail: {nodeid}") if markers["browser"] != (layer == "browser"): raise ValueError(f"Wrong regression layer for {nodeid}")