from __future__ import annotations import pytest from helpers import load_pilot, observation_from_fixture from kings_guard.cadence import load_qonto_assistant_source_cadence from kings_guard.contracts import StreamCompleteness from kings_guard.live import capture_qonto_assistant_events, qonto_assistant_available from kings_guard.posture import PostureEvaluator pytestmark = pytest.mark.skipif( not qonto_assistant_available(), reason="qonto-assistant checkout is required for live observation", ) def test_fixture_regression_case_is_retained() -> None: observation = observation_from_fixture() assert observation.observation_id == "req-qonto-deny-credential-exfil" assert observation.deny_reason == "credential_exfil" def test_real_emitted_qonto_events_reach_the_evaluator() -> None: fixture = load_pilot() capture = capture_qonto_assistant_events(fixture.genome) assert len(capture.events) >= 2 decisions = {event["decision"] for event in capture.events} assert "allow" in decisions assert "deny" in decisions assert capture.mapping_notes assert capture.stream_notes assert all( observation.source_system == "qonto-assistant" for observation in capture.observations ) deny = next(item for item in capture.observations if item.decision.value == "deny") evaluation = PostureEvaluator().evaluate(fixture.genome, deny) assert evaluation.assessment.posture.value in {"elevated", "inflamed"} assert evaluation.signals for signal in evaluation.signals: for request in signal.effector_requests: assert request.authority_boundary in {"advisory_only", "metadata_only"} # The source now emits identity binding and egress directly. The adapter must # preserve those values instead of masking them with its legacy mapping hints. for event, observation in zip(capture.events, capture.observations, strict=True): assert observation.identity_binding == event["identity_binding"] assert observation.egress_destination == event["egress_destination"] assert capture.corrections_for_source == () assert capture.stream_corrections == () cadence = load_qonto_assistant_source_cadence() stream_evaluation = PostureEvaluator().evaluate_with_stream( fixture.genome, deny, cadence, now=capture.heartbeats[-1].timestamp, watching_since=capture.heartbeats[0].timestamp, observations=capture.observations, heartbeats=capture.heartbeats, reconciliation=capture.reconciliation, ) assert stream_evaluation.assessment.stream_completeness is StreamCompleteness.COMPLETE assert stream_evaluation.stream is not None assert stream_evaluation.stream.findings == ()