test(gui_2): add Phase 1 invariant tests (test_gui_2_result.py, 2 tests)
TIER-2 READ conductor/code_styleguides/error_handling.md end-to-end before Phase 1. Adds tests/test_gui_2_result.py with 2 Phase 1 invariant tests: 1. test_phase_1_inventory_has_42_rows: parses tests/artifacts/PHASE1_SITE_INVENTORY.md and asserts the Site Inventory table contains exactly 42 rows. 2. test_phase_1_audit_has_42_migration_target_sites: runs scripts/audit_exception_handling.py --src src --json, finds the src/gui_2.py file record, counts sites in the migration-target category set (excludes INTERNAL_COMPLIANT, INTERNAL_PROGRAMMER_RAISE, BOUNDARY_FASTAPI, BOUNDARY_SDK, BOUNDARY_CONVERSION), and asserts the count is 42. This locks the 42-site migration target count: if the audit heuristic or inventory drift, the test catches it before Phase 2. Both tests pass: tests/test_gui_2_result.py::test_phase_1_inventory_has_42_rows PASSED tests/test_gui_2_result.py::test_phase_1_audit_has_42_migration_target_sites PASSED
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
"""
|
||||
Tests for the Phase 1 invariant contract of result_migration_gui_2_20260619.
|
||||
|
||||
This file locks the Phase 1 site inventory contract: there are exactly 42
|
||||
migration-target error-handling sites in src/gui_2.py. Both tests are static
|
||||
invariants that must pass before Phase 1 closes and Phase 2 begins:
|
||||
|
||||
- test_phase_1_inventory_has_42_rows: parses the markdown inventory table
|
||||
(tests/artifacts/PHASE1_SITE_INVENTORY.md) and asserts the Site Inventory
|
||||
table contains exactly 42 rows.
|
||||
- test_phase_1_audit_has_42_migration_target_sites: invokes the audit script
|
||||
(scripts/audit_exception_handling.py --src src --json), finds the
|
||||
src/gui_2.py file record, and counts the sites whose category is in the
|
||||
migration-target set (i.e., NOT INTERNAL_COMPLIANT, NOT
|
||||
INTERNAL_PROGRAMMER_RAISE, NOT BOUNDARY_*).
|
||||
|
||||
The migration-target category set is defined per
|
||||
conductor/code_styleguides/error_handling.md as: any category that is not one
|
||||
of the 5 "leave-as-is" categories. The migration-target sites are the ones
|
||||
the Phase 2-N migration will touch; the leave-as-is categories are legitimate
|
||||
non-migration patterns (compliant internal try/except, programmer raises,
|
||||
and the 3 boundary categories).
|
||||
"""
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
INVENTORY_PATH = Path("tests/artifacts/PHASE1_SITE_INVENTORY.md")
|
||||
EXPECTED_SITE_COUNT = 42
|
||||
MIGRATION_EXCLUDE_CATEGORIES = frozenset({
|
||||
"INTERNAL_COMPLIANT",
|
||||
"INTERNAL_PROGRAMMER_RAISE",
|
||||
"BOUNDARY_FASTAPI",
|
||||
"BOUNDARY_SDK",
|
||||
"BOUNDARY_CONVERSION",
|
||||
})
|
||||
|
||||
|
||||
def test_phase_1_inventory_has_42_rows():
|
||||
"""
|
||||
Parse tests/artifacts/PHASE1_SITE_INVENTORY.md and verify the "Site Inventory"
|
||||
markdown table contains exactly 42 rows.
|
||||
|
||||
The Site Inventory table begins with a header row of the form
|
||||
"| L# | Category | Phase | ..." and a separator row "|---...". Each data row
|
||||
has the form "| <line_number> | <CATEGORY> | <phase_number> | ...". The test
|
||||
locates the header by its leading "| L#" sentinel and counts subsequent rows
|
||||
that match the data-row pattern until the first non-table line.
|
||||
"""
|
||||
text = INVENTORY_PATH.read_text(encoding="utf-8")
|
||||
lines = text.splitlines()
|
||||
header_idx = None
|
||||
for i, line in enumerate(lines):
|
||||
if line.startswith("| L#"):
|
||||
header_idx = i
|
||||
break
|
||||
assert header_idx is not None, (
|
||||
f"Could not find '| L#' header in {INVENTORY_PATH}. "
|
||||
f"The inventory file format may have changed."
|
||||
)
|
||||
rows = []
|
||||
for line in lines[header_idx + 2:]:
|
||||
if not line.startswith("|"):
|
||||
break
|
||||
if re.match(r"^\|\s*\d+\s*\|", line):
|
||||
rows.append(line)
|
||||
assert len(rows) == EXPECTED_SITE_COUNT, (
|
||||
f"PHASE1_SITE_INVENTORY.md has {len(rows)} site rows; expected "
|
||||
f"{EXPECTED_SITE_COUNT}. The inventory must list exactly 42 migration-target "
|
||||
f"sites in src/gui_2.py."
|
||||
)
|
||||
|
||||
|
||||
def test_phase_1_audit_has_42_migration_target_sites():
|
||||
"""
|
||||
Invoke scripts/audit_exception_handling.py --src src --json, parse the JSON
|
||||
output, and verify that the src/gui_2.py file record contains exactly 42
|
||||
sites in the migration-target category set.
|
||||
|
||||
A site is "migration-target" when its category is NOT one of:
|
||||
- INTERNAL_COMPLIANT (legitimate compliant internal try/except)
|
||||
- INTERNAL_PROGRAMMER_RAISE (raise for impossible/programmer states)
|
||||
- BOUNDARY_FASTAPI (FastAPI HTTPException boundary)
|
||||
- BOUNDARY_SDK (SDK call boundary conversion)
|
||||
- BOUNDARY_CONVERSION (broad except used as conversion boundary)
|
||||
|
||||
The migration-target set is therefore:
|
||||
INTERNAL_BROAD_CATCH | INTERNAL_SILENT_SWALLOW | INTERNAL_RETHROW |
|
||||
INTERNAL_OPTIONAL_RETURN | UNCLEAR.
|
||||
|
||||
This test pins the audit output to the same 42 the inventory declares, so a
|
||||
future audit-script regression or inventory drift will surface here.
|
||||
"""
|
||||
result = subprocess.run(
|
||||
["uv", "run", "python", "scripts/audit_exception_handling.py", "--src", "src", "--json"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
assert result.returncode == 0, (
|
||||
f"audit_exception_handling.py exited {result.returncode}; stderr:\n"
|
||||
f"{result.stderr[:2000]}"
|
||||
)
|
||||
data = json.loads(result.stdout)
|
||||
gui2_files = [f for f in data.get("files", []) if "gui_2" in f.get("filename", "")]
|
||||
assert gui2_files, (
|
||||
"audit JSON contained no file record matching 'gui_2' in filename. "
|
||||
f"Filenames seen: {[f.get('filename') for f in data.get('files', [])][:10]}"
|
||||
)
|
||||
gui2 = gui2_files[0]
|
||||
findings = gui2.get("findings", [])
|
||||
migration_sites = [f for f in findings if f.get("category") not in MIGRATION_EXCLUDE_CATEGORIES]
|
||||
assert len(migration_sites) == EXPECTED_SITE_COUNT, (
|
||||
f"src/gui_2.py has {len(migration_sites)} migration-target sites in the audit; "
|
||||
f"expected {EXPECTED_SITE_COUNT}. Categories seen: "
|
||||
f"{sorted({f.get('category') for f in migration_sites})}. "
|
||||
f"This must match the 42 sites declared in PHASE1_SITE_INVENTORY.md."
|
||||
)
|
||||
Reference in New Issue
Block a user