Implement approval engine production readiness
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a05e2e-805b-7042-a750-71f473bceea2
This commit is contained in:
parent
ebce5abb27
commit
2bd2d19a98
30 changed files with 1679 additions and 53 deletions
82
tests/test_audit.py
Normal file
82
tests/test_audit.py
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import json
|
||||
from datetime import timedelta
|
||||
|
||||
import pytest
|
||||
|
||||
from approval_engine.audit import AuditCoreSink, AuditDeliveryError, OutboxWorker
|
||||
from approval_engine.store import Engine
|
||||
from tests.conftest import FROZEN, approve
|
||||
|
||||
|
||||
class Response:
|
||||
def __init__(self, status):
|
||||
self.status = status
|
||||
|
||||
def getcode(self):
|
||||
return self.status
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
|
||||
def test_audit_sender_adapts_envelope_and_rereads_token(tmp_path):
|
||||
token = tmp_path / "token"
|
||||
token.write_text("first")
|
||||
seen = []
|
||||
|
||||
def open_request(request, timeout):
|
||||
seen.append((request, timeout))
|
||||
return Response(202)
|
||||
|
||||
engine = Engine(":memory:", clock=lambda: FROZEN)
|
||||
approve(engine)
|
||||
sink = AuditCoreSink("http://audit-core:8080", token, opener=open_request)
|
||||
sink(engine.undrained()[0]["payload"])
|
||||
token.write_text("second")
|
||||
sink(engine.undrained()[0]["payload"])
|
||||
first, second = (item[0] for item in seen)
|
||||
assert first.get_header("Authorization") == "Bearer first"
|
||||
assert second.get_header("Authorization") == "Bearer second"
|
||||
assert first.get_header("Idempotency-key") == engine.undrained()[0]["event_id"]
|
||||
body = json.loads(first.data)
|
||||
assert body["id"] == body["correlation_id"] or body["correlation_id"]
|
||||
assert body["type"] == "approval.issuance"
|
||||
assert body["source"] == "approval-engine"
|
||||
engine.close()
|
||||
|
||||
|
||||
def test_nonaccepted_audit_status_remains_pending(tmp_path):
|
||||
token = tmp_path / "token"
|
||||
token.write_text("token")
|
||||
engine = Engine(":memory:", clock=lambda: FROZEN)
|
||||
approve(engine)
|
||||
sink = AuditCoreSink(
|
||||
"http://audit-core:8080", token, opener=lambda *_args, **_kwargs: Response(503)
|
||||
)
|
||||
result = engine.drain(sink)
|
||||
assert result == {"delivered": 0, "failed": 1}
|
||||
assert engine.undrained()[0]["last_error"] == "AuditDeliveryError"
|
||||
engine.close()
|
||||
|
||||
|
||||
def test_worker_emits_due_heartbeat_and_drains():
|
||||
now = [FROZEN]
|
||||
engine = Engine(":memory:", clock=lambda: now[0])
|
||||
delivered = []
|
||||
worker = OutboxWorker(engine, delivered.append, heartbeat_interval_seconds=300)
|
||||
first = worker.run_once()
|
||||
assert first["delivered"] == 1
|
||||
assert delivered[0]["action"] == "approval.heartbeat"
|
||||
now[0] += timedelta(seconds=301)
|
||||
second = worker.run_once()
|
||||
assert second["delivered"] == 1
|
||||
assert len(delivered) == 2
|
||||
engine.close()
|
||||
|
||||
|
||||
def test_sender_rejects_empty_token(tmp_path):
|
||||
token = tmp_path / "token"
|
||||
token.write_text("")
|
||||
sink = AuditCoreSink("http://audit-core", token)
|
||||
with pytest.raises(AuditDeliveryError, match="credential"):
|
||||
sink({})
|
||||
Loading…
Add table
Add a link
Reference in a new issue