CI Pipeline / best-practices (push) Successful in 10s
CI Pipeline / documentation (push) Successful in 10s
CI Pipeline / lint (push) Failing after 52s
Auto-version and Release / release (push) Successful in 52s
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / type-check (push) Successful in 32s
CI Pipeline / canary (push) Successful in 34s
CI Pipeline / unit-tests (push) Successful in 3m11s
CI Pipeline / required-checks (push) Failing after 2s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 8s
deploy / deploy (push) Failing after 7m32s
- 21.2B: resolve_cwd returns repo_identity (git:<path> stripped) not scaffold_location; worktree override; unknown project raises; no repo → refuse; 8 held-outs incl. old-bug reproduction. - 21.2C: wait_guard covers time.sleep(N) (inline + heredoc), polling loops (while/until + status on full command), repeated delay+status; 7 new held-outs; legitimate cases still pass. - 21.2D: canonical hook source at config/mimo-hooks/event-wait-guard.ts; scripts/install_mimo_hook.py verifies hash; drift test detects canonical↔installed divergence. - 21.2G: programme_gates.py — mandatory gates end in PASS/BLOCKED/ WAIVED_BY_USER only; GateDemotionError prevents mandatory→optional demotion; 6 held-outs.
65 lines
2.0 KiB
Python
65 lines
2.0 KiB
Python
"""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
|