Files
NODEDC_MISSION_CORE/tests/test_reference_graph_parity.py
T

162 lines
4.7 KiB
Python

from __future__ import annotations
import json
from pathlib import Path
from k1link.perception.contracts import LocalObstacleMap, SourceAccounting, ThreatAssessment
from k1link.perception.graph_contracts import (
REFERENCE_GRAPH_ID_V2,
DeliveredFrame,
GraphRunMode,
GraphState,
TerminalOutcome,
TerminalOutcomeType,
build_graph_run_result_v2,
)
from k1link.perception.reference_graph_parity import (
compare_reference_graph_to_accepted_ledgers,
)
def _result(threats: tuple[ThreatAssessment, ...] = ()):
obstacle_map = LocalObstacleMap(
source_id="RAVNOVES00",
session_id="20260720T065719Z_viewer_live",
frame_id="frame-000000",
graph_id=REFERENCE_GRAPH_ID_V2,
generated_monotonic_ns=1,
output_age_ns=0,
occupied=(),
unknown=(),
camera_uncertainty=(),
accounting=SourceAccounting(1, 1, 0, 0),
)
return build_graph_run_result_v2(
graph_id=REFERENCE_GRAPH_ID_V2,
source_profile_id="m4-ravnoves00-recorded-realtime/v1",
run_mode=GraphRunMode.LOSSLESS_REPLAY,
state=GraphState.STOPPED,
admitted_count=1,
outcomes=(
TerminalOutcome(
source_id="RAVNOVES00",
session_id="20260720T065719Z_viewer_live",
frame_id="frame-000000",
sequence=0,
outcome=TerminalOutcomeType.DELIVERED,
stage_id="threat",
reason="object-payload-delivered",
),
),
deliveries=(DeliveredFrame(0, obstacle_map, threats),),
queue_high_watermarks=(("detector", 1),),
)
def _write_ledgers(root: Path, *, threat_assessments: list[dict[str, object]]) -> tuple[Path, Path]:
temporal = root / "temporal.jsonl"
threat = root / "threat.jsonl"
temporal.write_text(
json.dumps(
{
"sequence": 0,
"frame_id": "frame-000000",
"current": [],
"rolling_retained": [],
"held": [],
"expired": [],
}
)
+ "\n",
"utf-8",
)
threat.write_text(
json.dumps(
{
"sequence": 0,
"frame_id": "frame-000000",
"camera_proposals": [],
"assessments": threat_assessments,
}
)
+ "\n",
"utf-8",
)
return temporal, threat
def test_reference_graph_parity_accepts_exact_frame_semantics(tmp_path: Path) -> None:
temporal, threat = _write_ledgers(tmp_path, threat_assessments=[])
report = compare_reference_graph_to_accepted_ledgers(
_result(),
temporal_frames_path=temporal,
threat_frames_path=threat,
expected_frames=1,
)
assert report.accepted is True
assert dict(report.mismatch_counts) == {
"camera_uncertainty": 0,
"current": 0,
"expired": 0,
"held": 0,
"rolling_retained": 0,
"source_binding": 0,
"threat_assessments": 0,
}
def test_reference_graph_parity_reports_threat_drift(tmp_path: Path) -> None:
temporal, threat = _write_ledgers(tmp_path, threat_assessments=[{"component_id": "x"}])
report = compare_reference_graph_to_accepted_ledgers(
_result(),
temporal_frames_path=temporal,
threat_frames_path=threat,
expected_frames=1,
)
assert report.accepted is False
assert dict(report.mismatch_counts)["threat_assessments"] == 1
def test_reference_graph_parity_treats_assessment_order_as_nonsemantic(
tmp_path: Path,
) -> None:
first = {
"schema_version": "missioncore.threat-assessment/v1",
"assessment_id": "threat-first",
"component_id": "component-first",
"rig_profile_id": "rig-test",
"corridor_profile_id": "corridor-test",
"qualification": "unqualified",
"relative_speed_mps": None,
"closest_approach_m": None,
"ttc_seconds": None,
"corridor_intersection": "unknown",
"decision": "unknown",
"reason_codes": ["test-unavailable"],
"authority": "replay-simulated",
"physical_collision_accepted": False,
"actuation_allowed": False,
}
second = {
**first,
"assessment_id": "threat-second",
"component_id": "component-second",
}
temporal, threat = _write_ledgers(
tmp_path,
threat_assessments=[second, first],
)
report = compare_reference_graph_to_accepted_ledgers(
_result((ThreatAssessment.from_dict(first), ThreatAssessment.from_dict(second))),
temporal_frames_path=temporal,
threat_frames_path=threat,
expected_frames=1,
)
assert report.accepted is True