Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a13eab-1e6a-7b33-9c37-372e90ada3a2
138 lines
4.1 KiB
Python
138 lines
4.1 KiB
Python
from dataclasses import replace
|
|
from datetime import timedelta
|
|
|
|
import pytest
|
|
|
|
from kings_guard.cadence import HeartbeatCadence, load_qonto_assistant_source_cadence
|
|
from kings_guard.contracts import EvidenceClass, StreamHeartbeat
|
|
from kings_guard.stream import evaluate_stream
|
|
|
|
NOW = "2026-10-15T12:00:00Z"
|
|
|
|
|
|
def approval_cadence():
|
|
return replace(
|
|
load_qonto_assistant_source_cadence(),
|
|
source_system="approval-engine",
|
|
heartbeats=tuple(
|
|
HeartbeatCadence(
|
|
"audit-core.heartbeat",
|
|
name,
|
|
EvidenceClass.LOAD_BEARING,
|
|
timedelta(hours=24),
|
|
"nothing-to-report",
|
|
)
|
|
for name in ("issuance", "revocation")
|
|
),
|
|
reconciliations=(),
|
|
)
|
|
|
|
|
|
def assess(*beats):
|
|
return evaluate_stream(
|
|
(), approval_cadence(), now=NOW, watching_since="2026-10-12T12:00:00Z", heartbeats=beats
|
|
)
|
|
|
|
|
|
def test_unscoped_shared_transport_heartbeat_cannot_vouch_for_transition_classes():
|
|
beat = StreamHeartbeat(
|
|
"approval-engine",
|
|
NOW,
|
|
"audit-core.heartbeat",
|
|
"nothing-to-report",
|
|
counts={"issuance": 0, "revocation": 0},
|
|
)
|
|
result = assess(beat)
|
|
assert "stream:heartbeat_missing:issuance" in result.findings
|
|
assert "stream:heartbeat_missing:revocation" in result.findings
|
|
|
|
|
|
def test_fresh_issuance_heartbeat_does_not_hide_stale_revocation():
|
|
fresh = StreamHeartbeat(
|
|
"approval-engine",
|
|
NOW,
|
|
"audit-core.heartbeat",
|
|
"nothing-to-report",
|
|
covered_event_classes=("issuance",),
|
|
)
|
|
stale = StreamHeartbeat(
|
|
"approval-engine",
|
|
"2026-10-12T12:00:00Z",
|
|
"audit-core.heartbeat",
|
|
"nothing-to-report",
|
|
covered_event_classes=("revocation",),
|
|
)
|
|
result = assess(stale, fresh)
|
|
assert "stream:heartbeat_missing:revocation" in result.findings
|
|
assert "stream:heartbeat_missing:issuance" not in result.findings
|
|
|
|
|
|
def test_fresh_heartbeats_for_both_classes_satisfy_their_own_obligations():
|
|
beats = tuple(
|
|
StreamHeartbeat(
|
|
"approval-engine",
|
|
NOW,
|
|
"audit-core.heartbeat",
|
|
"nothing-to-report",
|
|
covered_event_classes=(name,),
|
|
)
|
|
for name in ("issuance", "revocation")
|
|
)
|
|
assert not assess(*beats).findings
|
|
|
|
|
|
def test_explicit_coverage_cannot_override_wrong_transport_event_class():
|
|
beat = StreamHeartbeat(
|
|
"approval-engine",
|
|
NOW,
|
|
"other.heartbeat",
|
|
"nothing-to-report",
|
|
covered_event_classes=("issuance", "revocation"),
|
|
)
|
|
assert len(assess(beat).findings) == 2
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"coverage", ["revocation", ["revocation"], ("",), ("revocation", "revocation"), (None,)]
|
|
)
|
|
def test_malformed_coverage_is_refused_instead_of_matching_by_substring(coverage):
|
|
with pytest.raises(ValueError, match="coverage"):
|
|
StreamHeartbeat(
|
|
"approval-engine",
|
|
NOW,
|
|
"audit-core.heartbeat",
|
|
"nothing-to-report",
|
|
covered_event_classes=coverage,
|
|
)
|
|
|
|
|
|
def test_qonto_source_adapter_publishes_its_shared_heartbeat_coverage():
|
|
from kings_guard.live import _confirm_stream
|
|
|
|
counts = {"audit.allow": 0, "audit.deny": 0}
|
|
event = {
|
|
"stream_id": "qonto-assistant.audit",
|
|
"stream_instance_id": "instance",
|
|
"stream_sequence": 1,
|
|
"event_class": "audit.heartbeat",
|
|
"timestamp": NOW,
|
|
"assertion": "nothing-to-report",
|
|
"source_transition_counts": counts,
|
|
}
|
|
snapshot = {
|
|
"stream_id": event["stream_id"],
|
|
"stream_instance_id": "instance",
|
|
"last_stream_sequence": 1,
|
|
"source_transition_counts": counts,
|
|
}
|
|
beats, reconciliation, _, corrections = _confirm_stream([event], snapshot)
|
|
assert not corrections
|
|
assert beats[0].covered_event_classes == ("audit.allow", "audit.deny", "audit.heartbeat")
|
|
result = evaluate_stream(
|
|
(),
|
|
load_qonto_assistant_source_cadence(),
|
|
now=NOW,
|
|
heartbeats=beats,
|
|
reconciliation=reconciliation,
|
|
)
|
|
assert not result.findings
|