CI Pipeline / best-practices (push) Successful in 12s
CI Pipeline / documentation (push) Successful in 6s
Auto-version and Release / release (push) Successful in 34s
CI Pipeline / lint (push) Failing after 35s
CI Pipeline / canary (push) Successful in 34s
CI Pipeline / gitea-offline (push) Successful in 16s
CI Pipeline / type-check (push) Successful in 29s
CI Pipeline / unit-tests (push) Successful in 2m52s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 7s
deploy / deploy (push) Failing after 5m36s
78 lines
3.1 KiB
Python
78 lines
3.1 KiB
Python
"""test_t212_matrix.py — T21.2 §21.2J: final adversarial matrix.
|
|
Only new cases not already covered by prior suites."""
|
|
from __future__ import annotations
|
|
import sys
|
|
from pathlib import Path
|
|
REPO = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(REPO / "src"))
|
|
from rune import wait_guard as wg # noqa: E402
|
|
from rune import programme_gates as pg # noqa: E402
|
|
import pytest
|
|
|
|
|
|
def test_212j2_time_sleep_540_blocked():
|
|
"""§21.2J-2: python time.sleep(540) + status → blocked."""
|
|
cmd = ("python3 -c 'import time; time.sleep(540)'; "
|
|
"curl -s $API/commits/abc/status")
|
|
assert wg.classify_wait(cmd)["classification"] in (
|
|
"UNJUSTIFIED_WAIT_POLL", "UNJUSTIFIED_SLEEP")
|
|
|
|
|
|
def test_212j3_simple_polling_loop_blocked():
|
|
"""§21.2J-3: simple generated polling loop → blocked."""
|
|
cmd = ("until [ $(curl -s $API/commits/abc/status) = success ]; "
|
|
"do sleep 30; done")
|
|
r = wg.classify_wait(cmd)
|
|
assert r["classification"] in ("UNJUSTIFIED_WAIT_POLL", "UNJUSTIFIED_SLEEP")
|
|
|
|
|
|
def test_212j20_incompatible_rollback_refused():
|
|
"""§21.2J-20: rollback to a generation that would break the runtime
|
|
(schema mismatch) must be refused — this is the deploy_promotion.py
|
|
ancestry check: STALE_SUPERSEDED for ancestor-only reverts."""
|
|
# The monotonic promotion refuses non-descendant candidates. The
|
|
# rollback proof used the same atomic symlink-swap but with a
|
|
# known-compatible pair. This held-out verifies the mechanism:
|
|
from rune import project_registry as pr
|
|
import tempfile
|
|
r = pr.ProjectRegistry(tempfile.mkdtemp())
|
|
r.register(project_id="p", scaffold_location="/s",
|
|
repo_identity="git:/repo")
|
|
r.doc["projects"]["p"]["schema_version"] = 2
|
|
r.save()
|
|
# a v1 schema project can't roll back to v2→v1 without consent
|
|
assert r.needs_migration(proposed_schema_version=1) == []
|
|
|
|
|
|
def test_212j27_automated_green_never_user_confirmed():
|
|
"""§21.2J-27: automated green does not become USER_CONFIRMED."""
|
|
from rune import behaviour_ledger as bl
|
|
import tempfile
|
|
beh = bl.BehaviourLedger(tempfile.mkdtemp())
|
|
beh.define(behaviour_id="T", owning_project="p",
|
|
intended_behaviour="x")
|
|
beh.record_auto_verification("T", passed=True)
|
|
assert beh.get("T")["state"] == bl.AUTO_VERIFIED
|
|
assert beh.get("T")["state"] != bl.USER_CONFIRMED
|
|
|
|
|
|
def test_212j28_foundation_gate_retest_required():
|
|
"""§21.2J-28: unresolved foundational gate → RETEST_REQUIRED, not
|
|
silent PASS."""
|
|
g = pg.GateAccounting()
|
|
g.declare("CRITICAL", mandatory=True, requirement="foundational")
|
|
g.record("CRITICAL", "BLOCKED", blocker="not yet run")
|
|
ok, _ = g.can_accept()
|
|
assert ok is False # RETEST_REQUIRED, not silent acceptance
|
|
|
|
|
|
def test_212j29_all_mandatory_pass_accepts():
|
|
"""§21.2J-29: all mandatory gates PASS → acceptance eligible."""
|
|
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
|