261 lines
11 KiB
Python
261 lines
11 KiB
Python
"""Fail-closed coverage for the one-time Soteria publication release."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import hashlib
|
|
import sqlite3
|
|
import sys
|
|
from contextlib import contextmanager, nullcontext
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
from testing.tests.test_hermes_cli_support import _load
|
|
|
|
|
|
ROOT = Path(__file__).parents[2]
|
|
sys.path.insert(0, str(ROOT / "services/hermes/scm-common/scripts"))
|
|
release = _load("release_soteria_publication_retry")
|
|
|
|
|
|
def _receipt() -> dict:
|
|
return {
|
|
"source": {
|
|
"board": release.BOARD,
|
|
"task_id": release.CHILD,
|
|
"root_task_id": release.ROOT,
|
|
"run_id": release.SOURCE_RUN,
|
|
"worker_ordinal": release.ORDINAL,
|
|
},
|
|
"head": release.PRESERVED_HEAD,
|
|
"result_digest": release.RECEIPT_DIGEST,
|
|
"title": "Replace cache literals safely.",
|
|
"body": "Preserved validation evidence.",
|
|
}
|
|
|
|
|
|
def _pool(tmp_path: Path, *, active: bool = False, digest: str | None = None, run_id: str = release.FAILED_RUN) -> tuple[Path, str]:
|
|
"""Create only the terminal pool columns consumed by the release guard."""
|
|
database = tmp_path / "pool.db"
|
|
connection = sqlite3.connect(database)
|
|
connection.execute(
|
|
"CREATE TABLE assignments(board,task_id,run_id,payload_json,result_json,result_digest,state,worker_ordinal,attempt)"
|
|
)
|
|
resume = {"source": {"run_id": release.SOURCE_RUN, "worker_ordinal": 0}, "head": release.PRESERVED_HEAD}
|
|
result = {
|
|
"structured": {"status": "blocked"}, "returncode": 1,
|
|
"capacity_failure": False, "scm_submission": None,
|
|
}
|
|
encoded = json.dumps(result, separators=(",", ":"))
|
|
actual_digest = hashlib.sha256(encoded.encode()).hexdigest()
|
|
connection.execute(
|
|
"INSERT INTO assignments VALUES(?,?,?,?,?,?,?,?,?)",
|
|
(release.BOARD, release.CHILD, run_id, json.dumps({"scm_resume": resume}), encoded,
|
|
digest or actual_digest, "finalized", 0, 1),
|
|
)
|
|
if active:
|
|
connection.execute(
|
|
"INSERT INTO assignments VALUES(?,?,?,?,?,?,?,?,?)",
|
|
(release.BOARD, release.CHILD, "11", "{}", "{}", "", "assigned", 0, 1),
|
|
)
|
|
connection.commit()
|
|
connection.close()
|
|
return database, actual_digest
|
|
|
|
|
|
def _native(monkeypatch, *, event=None, latest=None, status="blocked", block_kind="capability"):
|
|
"""Install the actual scalar contract expected from the native adapter."""
|
|
event = event or (release.FAILED_EVENT_ID, int(release.FAILED_RUN), "blocked")
|
|
latest = latest or (int(release.FAILED_RUN), "blocked", "blocked")
|
|
state = SimpleNamespace(
|
|
task=SimpleNamespace(status=status, current_run_id=None, block_kind=block_kind),
|
|
event=event, latest=latest,
|
|
)
|
|
|
|
class Connection:
|
|
def execute(self, statement, _args):
|
|
row = state.event if "task_events" in statement else state.latest
|
|
return SimpleNamespace(fetchone=lambda: row)
|
|
|
|
def close(self):
|
|
return None
|
|
|
|
native = SimpleNamespace(
|
|
scoped_current_board=lambda _board: nullcontext(), connect=lambda **_kwargs: Connection(),
|
|
get_task=lambda _connection, _task_id: state.task,
|
|
parent_ids=lambda _connection, _task_id: [release.ROOT],
|
|
)
|
|
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=native))
|
|
monkeypatch.setitem(sys.modules, "hermes_cli.kanban_db", native)
|
|
return state
|
|
|
|
|
|
def _receipt_state(monkeypatch, receipt: dict, *, issued: str = release.FAILED_RUN):
|
|
"""Supply the exact sidecar rows without accepting arbitrary metadata."""
|
|
retry = (release.SOURCE_RUN, 0, issued, "", 1, release.RETRY_AFTER, "9")
|
|
provenance = (release.SOURCE_RAW_SHA, "normalized-title-from-sealed-receipt-sha256:" + "a" * 64)
|
|
|
|
class Connection:
|
|
def execute(self, statement, _args):
|
|
row = retry if "publication_retries" in statement else provenance
|
|
return SimpleNamespace(fetchone=lambda: row)
|
|
|
|
monkeypatch.setattr(release.supervisor_state, "publication_retry", lambda *_args: receipt)
|
|
monkeypatch.setattr(release.supervisor_state, "_connect", lambda _board: nullcontext(Connection()))
|
|
|
|
|
|
def _remote(monkeypatch, *, head: str = release.REMOTE_HEAD):
|
|
"""Return a scalar broker PR response without contacting a live broker."""
|
|
value = {"state": "open", "merged": False, "head": {"ref": release.BRANCH, "sha": head}, "base": {"ref": "main"}}
|
|
monkeypatch.setattr(release.scm_broker_client, "read", lambda _path: json.dumps(value).encode())
|
|
|
|
|
|
def test_exact_historical_fixture_passes_all_release_guards(tmp_path, monkeypatch):
|
|
"""The copied run-10/native/receipt fixture admits only its reviewed form."""
|
|
pool, digest = _pool(tmp_path)
|
|
monkeypatch.setattr(release, "FAILED_RESULT_SHA", digest)
|
|
_native(monkeypatch)
|
|
_receipt_state(monkeypatch, _receipt())
|
|
_remote(monkeypatch)
|
|
|
|
release._pool_guard(pool, release.FAILED_RUN)
|
|
release._native_guard(release.FAILED_RUN, release.FAILED_EVENT_ID, "blocked", "blocked")
|
|
release._receipt_guard(release.FAILED_RUN)
|
|
release._remote_guard()
|
|
|
|
|
|
@pytest.mark.parametrize("change", ["later_event", "changed_receipt", "changed_head", "terminal_digest", "active"])
|
|
def test_conflicting_evidence_fails_closed_without_a_release(tmp_path, monkeypatch, change):
|
|
"""A later action or altered evidence cannot pass the operator preconditions."""
|
|
receipt = _receipt()
|
|
pool, digest = _pool(tmp_path, active=change == "active", digest="f" * 64 if change == "terminal_digest" else None)
|
|
monkeypatch.setattr(release, "FAILED_RESULT_SHA", digest if change != "terminal_digest" else "e" * 64)
|
|
state = _native(monkeypatch, event=(210, None, "blocked") if change == "later_event" else None)
|
|
if change == "changed_receipt":
|
|
receipt["result_digest"] = "e" * 64
|
|
_receipt_state(monkeypatch, receipt)
|
|
_remote(monkeypatch, head="d" * 40 if change == "changed_head" else release.REMOTE_HEAD)
|
|
|
|
with pytest.raises(ValueError):
|
|
if change in {"terminal_digest", "active"}:
|
|
release._pool_guard(pool, release.FAILED_RUN)
|
|
elif change == "later_event":
|
|
assert state.event[0] == 210
|
|
release._native_guard(release.FAILED_RUN, release.FAILED_EVENT_ID, "blocked", "blocked")
|
|
elif change == "changed_receipt":
|
|
release._receipt_guard(release.FAILED_RUN)
|
|
else:
|
|
release._remote_guard()
|
|
|
|
|
|
def test_exact_cas_release_is_repeatable_and_preserves_budget_and_provenance(tmp_path, monkeypatch):
|
|
"""A crash before native unblock can retry the CAS without spending another budget."""
|
|
state_db = tmp_path / "state.db"
|
|
connection = sqlite3.connect(state_db)
|
|
connection.executescript(
|
|
"CREATE TABLE publication_retries(board,child_task_id,source_run_id,source_ordinal,issued_run_id,"
|
|
"resolved_run_id,reissue_count,retry_after,last_reissued_run_id);"
|
|
"CREATE TABLE publication_retry_provenance(board,child_task_id,raw_result_sha256,reconstruction);"
|
|
)
|
|
connection.execute(
|
|
"INSERT INTO publication_retries VALUES(?,?,?,?,?,?,?,?,?)",
|
|
(release.BOARD, release.CHILD, release.SOURCE_RUN, 0, release.FAILED_RUN, "", 1, release.RETRY_AFTER, "9"),
|
|
)
|
|
connection.execute(
|
|
"INSERT INTO publication_retry_provenance VALUES(?,?,?,?)",
|
|
(release.BOARD, release.CHILD, release.SOURCE_RAW_SHA, "normalized-title-from-sealed-receipt-sha256:" + "a" * 64),
|
|
)
|
|
connection.commit()
|
|
connection.close()
|
|
|
|
@contextmanager
|
|
def connect(_board):
|
|
connection = sqlite3.connect(state_db)
|
|
try:
|
|
yield connection
|
|
connection.commit()
|
|
finally:
|
|
connection.close()
|
|
|
|
for name in ("_pool_guard", "_native_guard", "_receipt_guard", "_remote_guard"):
|
|
monkeypatch.setattr(release, name, lambda *_args: None)
|
|
monkeypatch.setattr(release.supervisor_state, "_connect", connect)
|
|
|
|
assert release.release(tmp_path / "unused.db") is True
|
|
assert release.release(tmp_path / "unused.db") is False
|
|
connection = sqlite3.connect(state_db)
|
|
retry = connection.execute("SELECT issued_run_id,reissue_count,retry_after,last_reissued_run_id FROM publication_retries").fetchone()
|
|
provenance = connection.execute("SELECT raw_result_sha256,reconstruction FROM publication_retry_provenance").fetchone()
|
|
connection.close()
|
|
assert retry == ("", 1, release.RETRY_AFTER, "9")
|
|
assert provenance[0] == release.SOURCE_RAW_SHA
|
|
|
|
|
|
def test_published_confirmation_requires_run11_triage_and_exact_pr_prose(tmp_path, monkeypatch):
|
|
"""The post-push recovery accepts only the unchanged run-11 confirmation."""
|
|
pool, digest = _pool(tmp_path, run_id=release.CONFIRM_RUN)
|
|
monkeypatch.setattr(release, "FAILED_RESULT_SHA", digest)
|
|
_native(
|
|
monkeypatch,
|
|
event=(release.CONFIRM_EVENT_ID, int(release.CONFIRM_RUN), "block_loop_detected"),
|
|
latest=(int(release.CONFIRM_RUN), "blocked", "blocked"),
|
|
status="triage",
|
|
)
|
|
receipt = _receipt()
|
|
_receipt_state(monkeypatch, receipt, issued=release.CONFIRM_RUN)
|
|
pull = {
|
|
"state": "open", "merged": False,
|
|
"head": {"ref": release.BRANCH, "sha": release.PRESERVED_HEAD},
|
|
"base": {"ref": "main"},
|
|
"title": release._draft_title(receipt["title"]), "body": receipt["body"],
|
|
}
|
|
monkeypatch.setattr(release.scm_broker_client, "read", lambda _path: json.dumps(pull).encode())
|
|
|
|
release._pool_guard(pool, release.CONFIRM_RUN)
|
|
release._native_guard(release.CONFIRM_RUN, release.CONFIRM_EVENT_ID, "triage", "block_loop_detected")
|
|
release._published_remote_guard(release._receipt_guard(release.CONFIRM_RUN))
|
|
|
|
pull["body"] = "changed"
|
|
with pytest.raises(ValueError, match="published pull"):
|
|
release._published_remote_guard(receipt)
|
|
|
|
|
|
def test_published_confirmation_cas_is_repeatable_without_spending_budget(tmp_path, monkeypatch):
|
|
"""A crash before native triage specification leaves one harmless rerun path."""
|
|
state_db = tmp_path / "state.db"
|
|
connection = sqlite3.connect(state_db)
|
|
connection.execute(
|
|
"CREATE TABLE publication_retries(board,child_task_id,source_run_id,source_ordinal,issued_run_id,"
|
|
"resolved_run_id,reissue_count,retry_after,last_reissued_run_id)"
|
|
)
|
|
connection.execute(
|
|
"INSERT INTO publication_retries VALUES(?,?,?,?,?,?,?,?,?)",
|
|
(release.BOARD, release.CHILD, release.SOURCE_RUN, 0, release.CONFIRM_RUN, "", 1, release.RETRY_AFTER, "9"),
|
|
)
|
|
connection.commit()
|
|
connection.close()
|
|
|
|
@contextmanager
|
|
def connect(_board):
|
|
connection = sqlite3.connect(state_db)
|
|
try:
|
|
yield connection
|
|
connection.commit()
|
|
finally:
|
|
connection.close()
|
|
|
|
monkeypatch.setattr(release, "_pool_guard", lambda *_args: None)
|
|
monkeypatch.setattr(release, "_native_guard", lambda *_args: None)
|
|
monkeypatch.setattr(release, "_receipt_guard", lambda *_args: {})
|
|
monkeypatch.setattr(release, "_published_remote_guard", lambda *_args: None)
|
|
monkeypatch.setattr(release.supervisor_state, "_connect", connect)
|
|
|
|
assert release.confirm_published_release(tmp_path / "unused.db") is True
|
|
assert release.confirm_published_release(tmp_path / "unused.db") is False
|
|
connection = sqlite3.connect(state_db)
|
|
row = connection.execute("SELECT issued_run_id,reissue_count,retry_after,last_reissued_run_id FROM publication_retries").fetchone()
|
|
connection.close()
|
|
assert row == ("", 1, release.RETRY_AFTER, "9")
|