218 lines
6.4 KiB
Python
218 lines
6.4 KiB
Python
|
|
"""Canonical SQLite run-identity contracts for every terminal artifact class."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from testing.tests.test_hermes_cli_support import (
|
||
|
|
Path,
|
||
|
|
_completed_result,
|
||
|
|
_pending_terminal_record,
|
||
|
|
lanes,
|
||
|
|
pytest,
|
||
|
|
sys,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class _NoDatabase:
|
||
|
|
"""Fail if malformed run authority reaches a Kanban transaction."""
|
||
|
|
|
||
|
|
def __init__(self) -> None:
|
||
|
|
self.accesses = []
|
||
|
|
|
||
|
|
def scoped_current_board(self, *_args, **_kwargs):
|
||
|
|
self.accesses.append("scope")
|
||
|
|
raise AssertionError("noncanonical authority reached the database")
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.parametrize(
|
||
|
|
"value",
|
||
|
|
[
|
||
|
|
False,
|
||
|
|
True,
|
||
|
|
0,
|
||
|
|
-1,
|
||
|
|
2**63,
|
||
|
|
"0",
|
||
|
|
"01",
|
||
|
|
"9223372036854775808",
|
||
|
|
"9" * 150,
|
||
|
|
"+1",
|
||
|
|
" 1",
|
||
|
|
],
|
||
|
|
)
|
||
|
|
def test_run_identity_rejects_noncanonical_or_non_sqlite_values(value):
|
||
|
|
assert lanes.canonical_run_id(value) is None
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.parametrize("value", [1, 2**63 - 1, "1", "9223372036854775807"])
|
||
|
|
def test_run_identity_accepts_the_positive_sqlite_int64_domain(value):
|
||
|
|
assert lanes.canonical_run_id(value) == int(value)
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.parametrize("run_id", [False, True, 0, -1, 2**63, 10**149])
|
||
|
|
def test_terminal_writer_refuses_invalid_runtime_run_ids(tmp_path: Path, run_id):
|
||
|
|
with pytest.raises(ValueError, match="SQLite int64"):
|
||
|
|
lanes._write_terminal_record(
|
||
|
|
lanes.state_path("cassandra", "t_invalid_writer"),
|
||
|
|
board="cassandra",
|
||
|
|
task_id="t_invalid_writer",
|
||
|
|
run_id=run_id,
|
||
|
|
structured=_completed_result(),
|
||
|
|
summary="completed",
|
||
|
|
metadata={},
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.parametrize(
|
||
|
|
("state", "suffix", "parser"),
|
||
|
|
[
|
||
|
|
("pending", "terminal.pending.json", "_terminal_identity"),
|
||
|
|
("committed", "terminal.committed.json", "_terminal_identity"),
|
||
|
|
(
|
||
|
|
"prepared",
|
||
|
|
f"terminal.prepared-{'a' * 32}.json",
|
||
|
|
"_terminal_evidence_identity",
|
||
|
|
),
|
||
|
|
(
|
||
|
|
"conflict",
|
||
|
|
f"terminal.conflict-{'a' * 32}.json",
|
||
|
|
"_terminal_evidence_identity",
|
||
|
|
),
|
||
|
|
],
|
||
|
|
)
|
||
|
|
@pytest.mark.parametrize("encoded", ["0", "01", "9223372036854775808", "9" * 150])
|
||
|
|
def test_every_terminal_filename_class_rejects_noncanonical_run_ids(
|
||
|
|
tmp_path: Path,
|
||
|
|
monkeypatch,
|
||
|
|
state: str,
|
||
|
|
suffix: str,
|
||
|
|
parser: str,
|
||
|
|
encoded: str,
|
||
|
|
):
|
||
|
|
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "cli-lanes")
|
||
|
|
board = lanes.STATE_ROOT / "cassandra"
|
||
|
|
board.mkdir(parents=True)
|
||
|
|
path = board / f"t_filename.run-{encoded}.{suffix}"
|
||
|
|
|
||
|
|
assert getattr(lanes, parser)(path) is None, state
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.parametrize("run_id", [False, True, 0, -1, 2**63, 10**149])
|
||
|
|
@pytest.mark.parametrize("state", ["pending", "prepared", "committed", "conflict"])
|
||
|
|
def test_every_terminal_record_class_rejects_invalid_run_ids(run_id, state):
|
||
|
|
record = _pending_terminal_record(
|
||
|
|
"cassandra", "t_record", run_id, "invalid identity"
|
||
|
|
)
|
||
|
|
record["kanban_state"] = state
|
||
|
|
|
||
|
|
assert lanes._terminal_record_valid(record) is False
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.parametrize("run_id", [False, 0, 2**63, 10**149])
|
||
|
|
def test_retirement_staging_rejects_invalid_document_run_ids(
|
||
|
|
tmp_path: Path,
|
||
|
|
monkeypatch,
|
||
|
|
run_id,
|
||
|
|
):
|
||
|
|
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "cli-lanes")
|
||
|
|
board = lanes.STATE_ROOT / "cassandra"
|
||
|
|
board.mkdir(parents=True)
|
||
|
|
staged = board / f".retire.{'a' * 16}.{'b' * 16}.0"
|
||
|
|
lanes.atomic_json(
|
||
|
|
staged,
|
||
|
|
_pending_terminal_record(
|
||
|
|
"cassandra", "t_staged", run_id, "invalid staged identity"
|
||
|
|
),
|
||
|
|
)
|
||
|
|
snapshot = lanes._open_terminal_recovery_snapshot(staged)
|
||
|
|
assert snapshot is not None
|
||
|
|
try:
|
||
|
|
assert lanes._staged_terminal_authority(staged, snapshot) is None
|
||
|
|
finally:
|
||
|
|
snapshot.close()
|
||
|
|
assert lanes._recover_retirement_staging() == 0
|
||
|
|
assert not staged.exists()
|
||
|
|
assert len(list((board / "quarantine").glob("*.quarantine"))) == 1
|
||
|
|
|
||
|
|
|
||
|
|
def test_noncanonical_pending_filename_is_quarantined_before_db_access(
|
||
|
|
tmp_path: Path,
|
||
|
|
monkeypatch,
|
||
|
|
):
|
||
|
|
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "cli-lanes")
|
||
|
|
board = lanes.STATE_ROOT / "cassandra"
|
||
|
|
board.mkdir(parents=True)
|
||
|
|
pending = board / "t_noncanonical.run-01.terminal.pending.json"
|
||
|
|
lanes.atomic_json(
|
||
|
|
pending,
|
||
|
|
_pending_terminal_record(
|
||
|
|
"cassandra", "t_noncanonical", 1, "noncanonical filename"
|
||
|
|
),
|
||
|
|
)
|
||
|
|
database = _NoDatabase()
|
||
|
|
monkeypatch.setitem(
|
||
|
|
sys.modules,
|
||
|
|
"hermes_cli",
|
||
|
|
type("HermesCli", (), {"kanban_db": database})(),
|
||
|
|
)
|
||
|
|
|
||
|
|
assert lanes.recover_pending_finalizations() == 0
|
||
|
|
assert database.accesses == []
|
||
|
|
assert not pending.exists()
|
||
|
|
assert len(list((board / "quarantine").glob("*.quarantine"))) == 1
|
||
|
|
|
||
|
|
|
||
|
|
def test_noncanonical_prepared_filename_is_quarantined_before_db_access(
|
||
|
|
tmp_path: Path,
|
||
|
|
monkeypatch,
|
||
|
|
):
|
||
|
|
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "cli-lanes")
|
||
|
|
board = lanes.STATE_ROOT / "cassandra"
|
||
|
|
board.mkdir(parents=True)
|
||
|
|
prepared = (
|
||
|
|
board / f"t_prepared.run-01.terminal.prepared-{'a' * 32}.json"
|
||
|
|
)
|
||
|
|
record = _pending_terminal_record(
|
||
|
|
"cassandra", "t_prepared", 1, "noncanonical prepared filename"
|
||
|
|
)
|
||
|
|
record["kanban_state"] = "prepared"
|
||
|
|
lanes.atomic_json(prepared, record)
|
||
|
|
database = _NoDatabase()
|
||
|
|
monkeypatch.setitem(
|
||
|
|
sys.modules,
|
||
|
|
"hermes_cli",
|
||
|
|
type("HermesCli", (), {"kanban_db": database})(),
|
||
|
|
)
|
||
|
|
|
||
|
|
assert lanes.recover_pending_finalizations() == 0
|
||
|
|
assert database.accesses == []
|
||
|
|
assert not prepared.exists()
|
||
|
|
assert len(list((board / "quarantine").glob("*.quarantine"))) == 1
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.parametrize(
|
||
|
|
("state", "suffix"),
|
||
|
|
[
|
||
|
|
("committed", "terminal.committed.json"),
|
||
|
|
("conflict", f"terminal.conflict-{'a' * 32}.json"),
|
||
|
|
],
|
||
|
|
)
|
||
|
|
def test_gc_quarantines_noncanonical_retained_evidence(
|
||
|
|
tmp_path: Path,
|
||
|
|
monkeypatch,
|
||
|
|
state: str,
|
||
|
|
suffix: str,
|
||
|
|
):
|
||
|
|
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "cli-lanes")
|
||
|
|
board = lanes.STATE_ROOT / "cassandra"
|
||
|
|
board.mkdir(parents=True)
|
||
|
|
artifact = board / f"t_retained.run-01.{suffix}"
|
||
|
|
record = _pending_terminal_record(
|
||
|
|
"cassandra", "t_retained", 1, "noncanonical retained filename"
|
||
|
|
)
|
||
|
|
record["kanban_state"] = state
|
||
|
|
lanes.atomic_json(artifact, record)
|
||
|
|
|
||
|
|
assert lanes.gc_lane_artifacts() == 1
|
||
|
|
assert not artifact.exists()
|
||
|
|
assert len(list((board / "quarantine").glob("*.quarantine"))) == 1
|