import copy import json from types import SimpleNamespace import pytest from secrets_engine import cli from secrets_engine.catalog import validate_entry from secrets_engine.config import Config from secrets_engine.errors import BackendError, DecisionError from tests.test_catalog import VALID def _config(tmp_path): return Config( catalog_dir=tmp_path, policy_dir=tmp_path, evidence_dir=tmp_path / "evidence", hub_url="", bao_addr="http://127.0.0.1:8200", topic_id="test-topic", ) def _records(tmp_path): path = next((tmp_path / "evidence").glob("evidence-*.jsonl")) return [json.loads(line) for line in path.read_text().splitlines()] def _provision_args(entry): return SimpleNamespace( catalog_id=entry.id, stage=entry.stage, field="api_token", generate=False, from_file="/tmp/test-value-file", bootstrap_token_file=None, ) def test_provision_backend_exception_has_attempt_and_terminal_evidence( tmp_path, monkeypatch ): entry = validate_entry(copy.deepcopy(VALID)) monkeypatch.setattr(cli, "get_entry", lambda *_args: entry) monkeypatch.setattr(cli, "_require_lane_approval", lambda *_args: None) monkeypatch.setattr(cli.OpenBaoClient, "resolve", lambda *_args, **_kwargs: object()) monkeypatch.setattr( cli, "provision_from_file", lambda *_args, **_kwargs: (_ for _ in ()).throw( BackendError("fake-SUPER-SECRET-backend-message") ), ) with pytest.raises(BackendError): cli.cmd_provision(_config(tmp_path), _provision_args(entry)) records = _records(tmp_path) assert [record["result"] for record in records] == [ "attempt", "failed-BackendError", ] assert records[-1]["detail"]["approval_status"] == "not-required" assert "fake-SUPER-SECRET" not in json.dumps(records) def test_provision_decision_rejection_is_recorded_before_backend( tmp_path, monkeypatch ): data = copy.deepcopy(VALID) data["approval"] = { "model": "decision", "decision_ref": "CCR-2026-0001", } entry = validate_entry(data) monkeypatch.setattr(cli, "get_entry", lambda *_args: entry) monkeypatch.setattr( cli, "_require_lane_approval", lambda *_args: (_ for _ in ()).throw(DecisionError("not approved")), ) monkeypatch.setattr( cli.OpenBaoClient, "resolve", lambda *_args, **_kwargs: pytest.fail("backend must not be reached"), ) with pytest.raises(DecisionError): cli.cmd_provision(_config(tmp_path), _provision_args(entry)) records = _records(tmp_path) assert [record["result"] for record in records] == [ "attempt", "failed-DecisionError", ] assert records[-1]["detail"]["approval_status"] == "rejected" assert records[-1]["detail"]["decision_ref"] == "CCR-2026-0001" def test_production_handler_fails_closed_before_backend(tmp_path, monkeypatch): data = copy.deepcopy(VALID) data.update( stage="prod", approval={"model": "decision", "decision_ref": "CCR-2026-0001"}, ) entry = validate_entry(data) monkeypatch.setattr(cli, "get_entry", lambda *_args: entry) monkeypatch.delenv("SECRETS_ENGINE_UNSAFE_DEMO", raising=False) monkeypatch.setattr( cli.OpenBaoClient, "resolve", lambda *_args, **_kwargs: pytest.fail("backend must not be reached"), ) with pytest.raises(DecisionError, match="production action 'provision'"): cli.cmd_provision(_config(tmp_path), _provision_args(entry)) records = _records(tmp_path) assert [record["result"] for record in records] == [ "attempt", "failed-DecisionError", ]