key-cape 82b471f prepares approval:read on informed-decision-sitting-requester for present_existing intake reads (INFD-WP-0010). The registration doc, JSON and test no longer forbid read (applied state stays create-only; read is recorded as preparedScopes), and create_sitting_approvals checks only approve and consume, so it keeps working once read is live. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 5502@bnt-lap001 Assistant-Session: 68b3b02f-5d0f-4e71-8421-796f62e50196
94 lines
3.6 KiB
Python
94 lines
3.6 KiB
Python
import importlib.util
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
spec = importlib.util.spec_from_file_location(
|
|
"create_sitting_approvals",
|
|
Path(__file__).resolve().parents[1] / "tools" / "create_sitting_approvals.py",
|
|
)
|
|
create = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(create)
|
|
|
|
|
|
def test_dry_run_lists_seven_and_skips_c01():
|
|
report = create.dry_run()
|
|
assert report["posted"] is False
|
|
assert report["skipped"] == ["infd-20260914-c01"]
|
|
assert len(report["memo_ids"]) == 7
|
|
assert "infd-20260914-c01" not in report["memo_ids"]
|
|
assert report["memo_ids"][0] == "infd-20260914-c02"
|
|
assert report["memo_ids"][-1] == "infd-20260914-d04"
|
|
|
|
|
|
def test_require_attended_refuses_this_shell():
|
|
with pytest.raises(create.Refused, match="attended_reader_required"):
|
|
create.require_attended()
|
|
|
|
|
|
def test_created_receipt_has_seven_requested_and_no_entries():
|
|
receipt = json.loads((Path(__file__).resolve().parents[1] / "docs" / "evidence" / "2026-09-15-sitting-approval-creates.json").read_text())
|
|
assert receipt["status"] == "created"
|
|
assert receipt["human_entries_created"] is False
|
|
assert receipt["credential_values_emitted"] is False
|
|
assert len(receipt["requests"]) == 7
|
|
assert receipt["skipped"] == ["infd-20260914-c01"]
|
|
for row in receipt["requests"]:
|
|
approval = row["approval"]
|
|
assert approval["status"] == "requested"
|
|
assert approval["entries"] == []
|
|
assert approval["binding"]["human_control"] is True
|
|
assert approval["binding"]["digest"].startswith("sha256:")
|
|
|
|
|
|
def test_policy_package_pins_match_created_receipt():
|
|
root = Path(__file__).resolve().parents[1] / "docs" / "batches" / "2026-09-14"
|
|
receipt = json.loads((Path(__file__).resolve().parents[1] / "docs" / "evidence" / "2026-09-15-sitting-approval-creates.json").read_text())
|
|
package = (root / "policy-package.md").read_text()
|
|
assert "c01" not in package or "infd-20260914-c01" not in package
|
|
for row in receipt["requests"]:
|
|
assert f'memo:{row["memo_id"]}' in package
|
|
assert row["approval"]["id"] in package
|
|
assert row["approval"]["binding"]["digest"] in package
|
|
|
|
|
|
def test_failed_receipt_does_not_block_retry(tmp_path, monkeypatch):
|
|
receipt = tmp_path / "r.json"
|
|
monkeypatch.setattr(create, "RECEIPT", receipt)
|
|
receipt.write_text('{"status": "failed", "phase": "preflight"}\n')
|
|
assert create.existing_receipt_blocks() is False
|
|
receipt.write_text('{"status": "created", "phase": "seven_unapproved_requests_created"}\n')
|
|
assert create.existing_receipt_blocks() is True
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"origin,ok",
|
|
[
|
|
("http://127.0.0.1:18281", True),
|
|
("http://approval-engine.approval-engine.svc.cluster.local:8080", True),
|
|
("https://evil.example", False),
|
|
("http://10.43.103.108:8080", False),
|
|
(None, False),
|
|
],
|
|
)
|
|
def test_approval_origin_is_loopback_or_in_cluster_only(origin, ok):
|
|
assert create._approval_origin_ok(origin) is ok
|
|
|
|
|
|
def _exchange(granted):
|
|
def exchange(scope):
|
|
return (200, {}) if scope in granted else (400, {})
|
|
return exchange
|
|
|
|
|
|
@pytest.mark.parametrize("granted", [set(), {"approval:read"}])
|
|
def test_excess_scope_check_allows_read(granted):
|
|
create.refuse_excess_scopes(_exchange(granted))
|
|
assert "approval:read" not in create.EXCESS_SCOPES
|
|
|
|
|
|
@pytest.mark.parametrize("scope", ["approval:approve", "approval:consume"])
|
|
def test_excess_scope_check_refuses_approve_and_consume(scope):
|
|
with pytest.raises(create.Refused, match="excess_scope_not_refused"):
|
|
create.refuse_excess_scopes(_exchange({"approval:read", scope}))
|