Source: tests/test_pilot_provenance.py · Plain text

tests/test_pilot_provenance.py

"""Tests for pilot manifest provenance before any raw image reads."""

from __future__ import annotations

import hashlib
import json
import tempfile
from pathlib import Path

import pytest

from mcss.data.pilot_provenance import validate_pilot_entry

PROJECT = Path(__file__).resolve().parents[1]


@pytest.fixture
def artifact_dir() -> Path:
    output_root = PROJECT / "outputs"
    output_root.mkdir(exist_ok=True)
    with tempfile.TemporaryDirectory(prefix="test_pilot_provenance_", dir=output_root) as directory:
        yield Path(directory)


def test_valid_entry_returns_manifest_hashes_without_image_decoding(artifact_dir: Path) -> None:
    entry, prepared_root, online_path, query_path = _write_entry(artifact_dir)

    provenance = validate_pilot_entry(entry, prepared_root=prepared_root)

    assert provenance.episode_id == "train--scene_a--cam_00"
    assert provenance.online_manifest_sha256 == _sha256(online_path)
    assert provenance.query_manifest_sha256 == _sha256(query_path)
    assert provenance.online_rgb_references == 2
    assert provenance.query_rgb_references == 1
    assert provenance.query_depth_references == 1


def test_valid_dev_entry_binds_to_val_prepared_split(artifact_dir: Path) -> None:
    entry, prepared_root, _, _ = _write_entry(artifact_dir, split_id="dev")

    provenance = validate_pilot_entry(entry, prepared_root=prepared_root)

    assert provenance.split_id == "dev"
    assert (prepared_root / "val" / "scene_a" / "rgb" / "000000.png").is_file()


def test_rejects_cross_split_or_frame_alias_reference(artifact_dir: Path) -> None:
    entry, prepared_root, online_path, _ = _write_entry(artifact_dir)
    online = json.loads(online_path.read_text(encoding="utf-8"))
    online["stream"][0]["rgb"] = online["warmup"][0]["rgb"]
    online_path.write_text(json.dumps(online), encoding="utf-8")

    with pytest.raises(ValueError, match="online rgb reference"):
        validate_pilot_entry(entry, prepared_root=prepared_root)


def test_rejects_online_query_metadata_mismatch_before_reference_use(artifact_dir: Path) -> None:
    entry, prepared_root, _, query_path = _write_entry(artifact_dir)
    query = json.loads(query_path.read_text(encoding="utf-8"))
    query["scene_id"] = "other_scene"
    query_path.write_text(json.dumps(query), encoding="utf-8")

    with pytest.raises(ValueError, match="identity mismatch"):
        validate_pilot_entry(entry, prepared_root=prepared_root)


def test_rejects_query_rgb_alias_even_with_a_distinct_frame_id(artifact_dir: Path) -> None:
    entry, prepared_root, online_path, query_path = _write_entry(artifact_dir)
    online = json.loads(online_path.read_text(encoding="utf-8"))
    query = json.loads(query_path.read_text(encoding="utf-8"))
    query["query"][0]["rgb"] = online["warmup"][0]["rgb"]
    query_path.write_text(json.dumps(query), encoding="utf-8")

    with pytest.raises(ValueError, match="query rgb reference"):
        validate_pilot_entry(entry, prepared_root=prepared_root)


def _write_entry(
    directory: Path, *, split_id: str = "train"
) -> tuple[dict[str, object], Path, Path, Path]:
    prepared_root = directory / "prepared"
    prepared_split = {"train": "train", "dev": "val"}[split_id]
    scene_root = prepared_root / prepared_split / "scene_a"
    rgb_root = scene_root / "rgb"
    depth_root = scene_root / "depth"
    rgb_root.mkdir(parents=True)
    depth_root.mkdir(parents=True)
    for frame_id in range(3):
        (rgb_root / f"{frame_id:06d}.png").write_bytes(b"not decoded in provenance tests")
    (depth_root / "000002.npy").write_bytes(b"not decoded in provenance tests")
    identity = {
        "episode_id": f"{split_id}--scene_a--cam_00",
        "scene_id": "scene_a",
        "split_id": split_id,
        "query_vault_id": "qv-test",
    }
    online = {
        "schema_version": "mcss.dynamic.online_episode.v1",
        **identity,
        "image_size": [2, 2],
        "declared_length": 2,
        "warmup": [_online_frame(0, rgb_root / "000000.png")],
        "stream": [_online_frame(1, rgb_root / "000001.png")],
    }
    query = {
        "schema_version": "mcss.dynamic.query_vault.v1",
        **identity,
        "image_size": [2, 2],
        "query": [_query_frame(2, rgb_root / "000002.png", depth_root / "000002.npy")],
    }
    online_path = directory / "episode.online.json"
    query_path = directory / "episode.query.json"
    online_path.write_text(json.dumps(online), encoding="utf-8")
    query_path.write_text(json.dumps(query), encoding="utf-8")
    entry = {
        **identity,
        "online_manifest": str(online_path),
        "query_manifest": str(query_path),
        "stream_steps": 1,
        "query_count": 1,
    }
    return entry, prepared_root, online_path, query_path


def _online_frame(frame_id: int, rgb: Path) -> dict[str, object]:
    return {
        "frame_id": frame_id,
        "intrinsics": [[2.0, 0.0, 0.5], [0.0, 2.0, 0.5], [0.0, 0.0, 1.0]],
        "c2w": [
            [1.0, 0.0, 0.0, 0.0],
            [0.0, 1.0, 0.0, 0.0],
            [0.0, 0.0, 1.0, 0.0],
            [0.0, 0.0, 0.0, 1.0],
        ],
        "rgb": str(rgb),
    }


def _query_frame(frame_id: int, rgb: Path, depth: Path) -> dict[str, object]:
    return {**_online_frame(frame_id, rgb), "depth": str(depth)}


def _sha256(path: Path) -> str:
    return hashlib.sha256(path.read_bytes()).hexdigest()