feat(polygon): add full-frame operator review
This commit is contained in:
@@ -6,6 +6,7 @@ from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.routing import APIRoute
|
||||
@@ -333,6 +334,104 @@ def test_polygon_api_rejects_tampered_ground_qualification(
|
||||
assert "SHA-256" in failure.value.detail or "файла" in failure.value.detail
|
||||
|
||||
|
||||
def _review_pack(root: Path, run_id: str, frame_id: str) -> Path:
|
||||
identity = "d" * 64
|
||||
pack = root.parent / "polygon-review-packs" / run_id / f"review-{identity}"
|
||||
frames = pack / "frames"
|
||||
frames.mkdir(parents=True)
|
||||
frame_path = frames / f"{frame_id}.npz"
|
||||
np.savez_compressed(
|
||||
frame_path,
|
||||
schema=np.asarray(["missioncore.goose-ground-review-frame/v1"]),
|
||||
frame_id=np.asarray([frame_id]),
|
||||
source_point_count=np.asarray([3], dtype=np.int32),
|
||||
xyz_cm=np.asarray([[0, 0, 0], [100, -100, 50]], dtype="<i2"),
|
||||
remission_u8=np.asarray([12, 255], dtype=np.uint8),
|
||||
flags=np.asarray([15, 1], dtype=np.uint8),
|
||||
)
|
||||
metric = {
|
||||
"ground_iou": 0.5,
|
||||
"natural_ground_recall": 0.6,
|
||||
"obstacle_non_ground_recall": 0.95,
|
||||
"latency_ms": 20.0,
|
||||
}
|
||||
manifest = {
|
||||
"schema_version": "missioncore.goose-ground-review-pack/v1",
|
||||
"source_run_id": run_id,
|
||||
"identity_sha256": identity,
|
||||
"source_id": "goose-3d/v2025-08-22",
|
||||
"preview_points": 12_000,
|
||||
"frame_count": 1,
|
||||
"frames": [
|
||||
{
|
||||
"sequence": 0,
|
||||
"frame_id": frame_id,
|
||||
"source_point_count": 3,
|
||||
"point_count": 2,
|
||||
"relative_path": f"frames/{frame_id}.npz",
|
||||
"sha256": hashlib.sha256(frame_path.read_bytes()).hexdigest(),
|
||||
"byte_length": frame_path.stat().st_size,
|
||||
"current": metric,
|
||||
"patchworkpp": {**metric, "ground_iou": 0.7},
|
||||
"ground_iou_delta": 0.2,
|
||||
}
|
||||
],
|
||||
"safety": {
|
||||
"visualization_only": True,
|
||||
"actuator_authority": False,
|
||||
"navigation_or_safety_accepted": False,
|
||||
},
|
||||
}
|
||||
(pack / "manifest.json").write_text(json.dumps(manifest), encoding="utf-8")
|
||||
return frame_path
|
||||
|
||||
|
||||
def test_polygon_api_publishes_all_frame_review_without_paths(tmp_path: Path) -> None:
|
||||
root = tmp_path / "runs"
|
||||
run, frame_id = _qualification_run(root)
|
||||
_review_pack(root, run.run_id, frame_id)
|
||||
router = build_polygon_router(root_provider=lambda: root)
|
||||
|
||||
review = _endpoint(
|
||||
router,
|
||||
"/api/v1/polygon/runs/{run_id}/qualification/review",
|
||||
"GET",
|
||||
)(run_id=run.run_id)
|
||||
frame = _endpoint(
|
||||
router,
|
||||
"/api/v1/polygon/runs/{run_id}/qualification/review/frames/{frame_id}",
|
||||
"GET",
|
||||
)(run_id=run.run_id, frame_id=frame_id)
|
||||
|
||||
assert review["schema_version"] == "missioncore.polygon-ground-review/v1"
|
||||
assert review["frame_count"] == 1
|
||||
assert review["frames"][0]["ground_iou_delta"] == pytest.approx(0.2)
|
||||
assert "relative_path" not in review["frames"][0]
|
||||
assert frame["schema_version"] == "missioncore.polygon-ground-review-frame/v1"
|
||||
assert frame["points_xyz_m"] == [[0.0, 0.0, 0.0], [1.0, -1.0, 0.5]]
|
||||
assert frame["intensity_0_255"] == [12, 255]
|
||||
assert frame["ground_truth_ground"] == [1, 0]
|
||||
assert frame["patchwork_ground"] == [1, 0]
|
||||
assert str(tmp_path) not in repr({"review": review, "frame": frame})
|
||||
|
||||
|
||||
def test_polygon_api_rejects_tampered_review_frame(tmp_path: Path) -> None:
|
||||
root = tmp_path / "runs"
|
||||
run, frame_id = _qualification_run(root)
|
||||
frame_path = _review_pack(root, run.run_id, frame_id)
|
||||
frame_path.write_bytes(b"tampered")
|
||||
router = build_polygon_router(root_provider=lambda: root)
|
||||
|
||||
with pytest.raises(HTTPException) as failure:
|
||||
_endpoint(
|
||||
router,
|
||||
"/api/v1/polygon/runs/{run_id}/qualification/review/frames/{frame_id}",
|
||||
"GET",
|
||||
)(run_id=run.run_id, frame_id=frame_id)
|
||||
assert failure.value.status_code == 500
|
||||
assert "SHA-256" in failure.value.detail or "файл" in failure.value.detail.lower()
|
||||
|
||||
|
||||
class _FakeWorkerGateway:
|
||||
def __init__(self) -> None:
|
||||
self.calls: list[tuple[str, str]] = []
|
||||
|
||||
Reference in New Issue
Block a user