feat(polygon): qualify GOOSE ground providers

This commit is contained in:
DCCONSTRUCTIONS
2026-07-25 16:02:20 +03:00
parent 60ba64004b
commit d6decbe05c
14 changed files with 2718 additions and 22 deletions
+143 -3
View File
@@ -1,5 +1,7 @@
from __future__ import annotations
import hashlib
import json
from collections.abc import Callable
from pathlib import Path
from typing import Any
@@ -191,6 +193,146 @@ def test_polygon_api_rejects_corrupt_evidence_without_partial_response(
assert "целостности" in failure.value.detail
def _qualification_run(root: Path) -> tuple[QualificationRun, str]:
store = QualificationRunStore(root)
admitted = store.create(_run("goose-ground-test"))
starting = store.transition(
admitted.run_id,
RunState.STARTING,
expected_revision=0,
observed_at_utc="2026-07-24T15:35:01Z",
host_monotonic_ns=1,
)
store.transition(
admitted.run_id,
RunState.RUNNING,
expected_revision=starting.revision,
observed_at_utc="2026-07-24T15:35:02Z",
host_monotonic_ns=2,
)
evidence = root / admitted.run_id / "evidence"
failures = evidence / "failures"
failures.mkdir(parents=True)
frame_id = "2022-07-22_flight__0071_0001"
report = {
"schema_version": "missioncore.goose-ground-qualification-report/v1",
"identity_sha256": SHA_A,
"source_id": "goose-3d/v2025-08-22",
"split": "validation",
"frame_count": 961,
"aggregates": {"current": {}, "patchworkpp": {}},
"degradations": {},
"checks": [],
"worst_frames": [{"frame_id": frame_id}],
"decision": {"status": "shadow-candidate", "passed": True},
"safety": {"navigation_or_safety_accepted": False},
"frames": [{"private": "not-published"}],
}
preview = {
"schema_version": "missioncore.goose-ground-qualification-failure-preview/v1",
"source_id": "goose-3d/v2025-08-22",
"frame_id": frame_id,
"source_point_count": 2,
"point_count": 2,
"sampling": "deterministic-even-index",
"points_xyz_m": [[0, 0, 0], [1, 1, 1]],
"ground_truth_ground": [1, 0],
"evaluated": [1, 1],
"current_ground": [0, 0],
"patchwork_ground": [1, 0],
"current_disagreement": [1, 0],
"patchwork_disagreement": [0, 0],
"safety": {"navigation_or_safety_accepted": False},
}
for artifact_id, kind, path, value in (
(
"qualification-report",
"goose-ground-qualification-report",
evidence / "qualification.json",
report,
),
(
"failure-preview-01",
"goose-ground-qualification-failure-preview",
failures / f"{frame_id}.json",
preview,
),
):
path.write_text(json.dumps(value), encoding="utf-8")
store.register_artifact(
admitted.run_id,
QualificationArtifact(
artifact_id=artifact_id,
kind=kind,
relative_path=path.relative_to(root / admitted.run_id).as_posix(),
sha256=hashlib.sha256(path.read_bytes()).hexdigest(),
byte_length=path.stat().st_size,
source_of_record=True,
),
)
current = store.load(admitted.run_id)
stopping = store.transition(
admitted.run_id,
RunState.STOPPING,
expected_revision=current.revision,
observed_at_utc="2026-07-24T15:35:04Z",
host_monotonic_ns=4,
)
completed = store.transition(
admitted.run_id,
RunState.COMPLETED,
expected_revision=stopping.revision,
observed_at_utc="2026-07-24T15:35:05Z",
host_monotonic_ns=5,
reason="qualification-evidence-sealed",
)
return completed, frame_id
def test_polygon_api_publishes_verified_ground_qualification(
tmp_path: Path,
) -> None:
root = tmp_path / "runs"
run, frame_id = _qualification_run(root)
router = build_polygon_router(root_provider=lambda: root)
qualification = _endpoint(
router,
"/api/v1/polygon/runs/{run_id}/qualification",
"GET",
)(run_id=run.run_id)
preview = _endpoint(
router,
"/api/v1/polygon/runs/{run_id}/qualification/failures/{frame_id}",
"GET",
)(run_id=run.run_id, frame_id=frame_id)
assert qualification["schema_version"] == "missioncore.polygon-ground-qualification/v1"
assert qualification["frame_count"] == 961
assert "frames" not in qualification
assert preview["schema_version"] == "missioncore.polygon-ground-failure-preview/v1"
assert preview["frame_id"] == frame_id
assert str(tmp_path) not in repr({"qualification": qualification, "preview": preview})
def test_polygon_api_rejects_tampered_ground_qualification(
tmp_path: Path,
) -> None:
root = tmp_path / "runs"
run, _ = _qualification_run(root)
(root / run.run_id / "evidence/qualification.json").write_text("{}", encoding="utf-8")
router = build_polygon_router(root_provider=lambda: root)
with pytest.raises(HTTPException) as failure:
_endpoint(
router,
"/api/v1/polygon/runs/{run_id}/qualification",
"GET",
)(run_id=run.run_id)
assert failure.value.status_code == 500
assert "SHA-256" in failure.value.detail or "файла" in failure.value.detail
class _FakeWorkerGateway:
def __init__(self) -> None:
self.calls: list[tuple[str, str]] = []
@@ -286,9 +428,7 @@ class _FakeWorkerGateway:
"control_available": True,
"active_run_id": self.active_run_id,
"run_state": "running" if self.active_run_id else None,
"active_provider_ids": (
["px4-gazebo-stock-rover"] if self.active_run_id else []
),
"active_provider_ids": (["px4-gazebo-stock-rover"] if self.active_run_id else []),
"provider_profile": STOCK_ROVER_PROVIDER_PROFILE.to_dict(),
"isolation": {
"network": "loopback-only-netns",