"""IMP-#91 u14 — unit tests for the status-board marker updater. Exercises ``parse_outcomes`` (nodeid → axis/mdx outcome mapping) and ``update_board_text`` (idempotent marker rewrite). u15 will wire the CLI into the GitHub Actions workflow; these tests guard the contract. """ from __future__ import annotations import re import sys from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(REPO_ROOT / "scripts")) import update_status_board as usb # noqa: E402 IMP95_BOARD_PATH = ( REPO_ROOT / "docs" / "architecture" / "PHASE-Z-PIPELINE-STATUS-BOARD.md" ) IMP95_MARKER_RE = re.compile( r"()(.*?)()", re.DOTALL ) IMP95_EXPECTED_AXES = {"j1", "j2", "j3", "j4", "j5", "j6", "j7", "j8"} IMP95_ALLOWED_VALUES = {"pending", "trace-only", "guarded", "active"} SAMPLE_REPORT = { "tests": [ { "nodeid": "tests/integration/test_multi_mdx_regression.py::test_normalize_snapshot_matches[01]", "outcome": "passed", }, { "nodeid": "tests/integration/test_multi_mdx_regression.py::test_v4_ranking_snapshot_matches[02]", "outcome": "passed", }, { "nodeid": "tests/integration/test_multi_mdx_regression.py::test_layout_snapshot_matches[03]", "outcome": "failed", }, { "nodeid": "tests/integration/test_multi_mdx_regression.py::test_pipeline_run_produces_step20_status[02]", "outcome": "passed", }, ] } def test_parse_outcomes_maps_known_axes_only() -> None: outcomes = usb.parse_outcomes(SAMPLE_REPORT) assert outcomes == { ("F0", "01"): "PASS", ("F1", "02"): "PASS", ("F4", "03"): "FAIL", } def test_update_board_text_rewrites_markers() -> None: board = "F0/01: ? F1/02: old" outcomes = {("F0", "01"): "PASS"} result = usb.update_board_text(board, outcomes) assert "PASS" in result assert "?" in result def test_update_board_text_is_idempotent() -> None: board = "old" outcomes = {("F2", "05"): "PASS"} once = usb.update_board_text(board, outcomes) twice = usb.update_board_text(once, outcomes) assert once == twice == "PASS" def test_imp95_markers_present_and_well_formed() -> None: """IMP-95 sub-axis markers all present in section 9 with allowed values.""" board = IMP95_BOARD_PATH.read_text(encoding="utf-8") matches = IMP95_MARKER_RE.findall(board) axes = {axis for _, axis, _, _ in matches} assert axes == IMP95_EXPECTED_AXES, ( f"IMP-95 axes drift: expected {IMP95_EXPECTED_AXES}, got {axes}" ) for _, axis, value, _ in matches: assert value in IMP95_ALLOWED_VALUES, ( f"IMP-95 {axis} value {value!r} outside allowed set {IMP95_ALLOWED_VALUES}" ) def test_imp95_markers_isolated_from_imp91_updater() -> None: """`update_board_text` (IMP-91) MUST NOT rewrite IMP-95 markers — distinct closing tag.""" sample = ( "trace-only " "?" ) outcomes = {("F0", "01"): "PASS"} rewritten = usb.update_board_text(sample, outcomes) assert "trace-only" in rewritten assert "PASS" in rewritten def test_imp95_marker_rewrite_is_idempotent() -> None: """Regex-driven IMP-95 cell rewrite is byte-identical on second application.""" board = "pending" def rewrite(text: str, value: str) -> str: return IMP95_MARKER_RE.sub( lambda m: f"{m.group(1)}{value}{m.group(4)}", text ) once = rewrite(board, "trace-only") twice = rewrite(once, "trace-only") assert once == twice == "trace-only"