"""test_programme_gates.py — T21.2 §21.2G: mandatory-gate accounting.""" from __future__ import annotations import sys from pathlib import Path import pytest REPO = Path(__file__).resolve().parent.parent sys.path.insert(0, str(REPO / "src")) from rune import programme_gates as pg # noqa: E402 def test_212g1_mandatory_cannot_become_optional(): g = pg.GateAccounting() g.declare("A", mandatory=True, requirement="do the thing") with pytest.raises(pg.GateDemotionError): g.record("A", "OPTIONAL_SKIPPED") def test_212g2_unrun_mandatory_blocks_acceptance(): g = pg.GateAccounting() g.declare("A", mandatory=True, requirement="a") g.declare("B", mandatory=True, requirement="b") g.declare("C", mandatory=True, requirement="c") g.record("A", "PASS", evidence="done") g.record("B", "PASS", evidence="done") # C not run ok, blockers = g.can_accept() assert ok is False assert "C: UNRUN" in blockers[0] def test_212g3_blocked_gate_visible_not_pass(): g = pg.GateAccounting() g.declare("C", mandatory=True, requirement="c") g.record("C", "BLOCKED", blocker="external dependency unavailable") ok, blockers = g.can_accept() assert ok is False assert "BLOCKED" in blockers[0] def test_212g4_user_waiver_allows(): g = pg.GateAccounting() g.declare("C", mandatory=True, requirement="c") g.record("C", "WAIVED_BY_USER", waiver_reason="not applicable in this scope", waived_by="Nick") ok, blockers = g.can_accept() assert ok is True def test_212g5_optional_skip_does_not_block(): g = pg.GateAccounting() g.declare("D", mandatory=False, requirement="nice to have") g.record("D", "OPTIONAL_SKIPPED") ok, _ = g.can_accept() assert ok is True def test_212g6_all_pass_accepts(): g = pg.GateAccounting() g.declare("A", mandatory=True, requirement="a") g.declare("B", mandatory=True, requirement="b") g.record("A", "PASS", evidence="ok") g.record("B", "PASS", evidence="ok") ok, _ = g.can_accept() assert ok is True