"""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")