Initial public release
GeoIntel release gates / Compile, test, contracts and builds (push) Successful in 1m49s
GeoIntel release gates / Python and npm vulnerability policy (push) Successful in 21s
GeoIntel release gates / Production AI image, SBOM and container scan (push) Successful in 5m39s
GeoIntel release gates / Deploy exact gated revision to Unraid (push) Failing after 58m43s
GeoIntel release gates / Compile, test, contracts and builds (push) Successful in 1m49s
GeoIntel release gates / Python and npm vulnerability policy (push) Successful in 21s
GeoIntel release gates / Production AI image, SBOM and container scan (push) Successful in 5m39s
GeoIntel release gates / Deploy exact gated revision to Unraid (push) Failing after 58m43s
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from uuid import uuid4
|
||||
|
||||
from geoalchemy2.shape import from_shape
|
||||
import pytest
|
||||
from shapely.geometry import box
|
||||
|
||||
from app.core.config import Settings
|
||||
from app.core.errors import AppError
|
||||
from app.models import Area, Dataset, DatasetVersion, SourceRegistry, SourceSnapshot
|
||||
from app.services.tile_manifest_service import TileManifestService, canonical_manifest_json
|
||||
|
||||
|
||||
class FakeSession:
|
||||
def __init__(self, objects=None) -> None:
|
||||
self.objects = objects or {}
|
||||
|
||||
def get(self, model, item_id):
|
||||
return self.objects.get((model, item_id))
|
||||
|
||||
|
||||
def _dataset_and_session(*, with_area: bool = True):
|
||||
project_id = uuid4()
|
||||
dataset_id = uuid4()
|
||||
registry_id = uuid4()
|
||||
snapshot_id = uuid4()
|
||||
checksum = "a" * 64
|
||||
area = None
|
||||
area_id = uuid4() if with_area else None
|
||||
if area_id is not None:
|
||||
area = Area(
|
||||
id=area_id,
|
||||
project_id=project_id,
|
||||
name="Inference AOI",
|
||||
geometry=from_shape(box(4.0, 51.0, 5.0, 52.0), srid=4326),
|
||||
)
|
||||
registry = SourceRegistry(
|
||||
id=registry_id,
|
||||
source_key="governed-test-raster",
|
||||
display_name="Governed test raster",
|
||||
classification="derived",
|
||||
authority_name="GeoIntel",
|
||||
)
|
||||
snapshot = SourceSnapshot(
|
||||
id=snapshot_id,
|
||||
source_registry_id=registry_id,
|
||||
snapshot_key="snapshot-1",
|
||||
checksum_sha256=checksum,
|
||||
ingest_status="ingested",
|
||||
freshness_status="current",
|
||||
)
|
||||
dataset = Dataset(
|
||||
id=dataset_id,
|
||||
project_id=project_id,
|
||||
area_id=area_id,
|
||||
name="orthophoto.tif",
|
||||
dataset_type="raster",
|
||||
source="governed-test-raster",
|
||||
source_name="governed-test-raster",
|
||||
storage_path="storage/uploads/orthophoto.tif",
|
||||
size_bytes=123,
|
||||
checksum_sha256=checksum,
|
||||
crs="EPSG:4326",
|
||||
bounds_json={"min_x": 4.0, "min_y": 51.0, "max_x": 5.0, "max_y": 52.0},
|
||||
source_registry_id=registry_id,
|
||||
source_snapshot_id=snapshot_id,
|
||||
data_contract_key="geointel.raster.geotiff",
|
||||
data_contract_version="1.0.0",
|
||||
validation_status="passed",
|
||||
provenance_status="complete",
|
||||
lineage_status="not_applicable",
|
||||
quarantine_status="not_quarantined",
|
||||
status="ready",
|
||||
)
|
||||
dataset.source_registry = registry
|
||||
dataset.source_snapshot = snapshot
|
||||
version = DatasetVersion(
|
||||
id=uuid4(),
|
||||
dataset_id=dataset_id,
|
||||
version=3,
|
||||
checksum_sha256=checksum,
|
||||
)
|
||||
dataset.versions.append(version)
|
||||
objects = {(Dataset, dataset_id): dataset}
|
||||
if area is not None:
|
||||
objects[(Area, area.id)] = area
|
||||
return FakeSession(objects), dataset, area
|
||||
|
||||
|
||||
def _manifest(tmp_path: Path, db: FakeSession, dataset: Dataset) -> Path:
|
||||
tile_path = tmp_path / "tile_0000.tif"
|
||||
tile_path.write_bytes(b"immutable tile")
|
||||
payload = {
|
||||
**TileManifestService.dataset_binding(db, dataset),
|
||||
"tile_set_id": "tile-set-1",
|
||||
"crs": "EPSG:4326",
|
||||
"source_crs": "EPSG:4326",
|
||||
"dataset_crs": "EPSG:4326",
|
||||
"bounds": [4.0, 51.0, 5.0, 52.0],
|
||||
"count": 1,
|
||||
"tiles": [
|
||||
{
|
||||
"path": str(tile_path),
|
||||
"bounds": [4.0, 51.0, 5.0, 52.0],
|
||||
"crs": "EPSG:4326",
|
||||
"index": 0,
|
||||
**TileManifestService.tile_integrity(tile_path),
|
||||
}
|
||||
],
|
||||
}
|
||||
manifest_path = tmp_path / "manifest.json"
|
||||
manifest_path.write_text(canonical_manifest_json(payload), encoding="utf-8")
|
||||
return manifest_path
|
||||
|
||||
|
||||
def _validate(tmp_path: Path, db: FakeSession, dataset: Dataset, *, prefix: str = "DETECTION"):
|
||||
manifest_path = tmp_path / "manifest.json"
|
||||
payload = json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||
return TileManifestService.validate_for_inference(
|
||||
db,
|
||||
dataset,
|
||||
payload,
|
||||
manifest_path=manifest_path,
|
||||
settings=Settings(_env_file=None, storage_root=str(tmp_path)),
|
||||
error_prefix=prefix,
|
||||
)
|
||||
|
||||
|
||||
def test_versioned_tile_manifest_binds_dataset_version_snapshot_area_and_tile_bytes(tmp_path: Path) -> None:
|
||||
db, dataset, _area = _dataset_and_session()
|
||||
manifest_path = _manifest(tmp_path, db, dataset)
|
||||
|
||||
evidence = _validate(tmp_path, db, dataset)
|
||||
|
||||
assert evidence["manifest_path"] == str(manifest_path.resolve())
|
||||
assert evidence["source_dataset_id"] == str(dataset.id)
|
||||
assert evidence["source_snapshot_id"] == str(dataset.source_snapshot_id)
|
||||
assert evidence["dataset_version_id"] == str(dataset.versions[0].id)
|
||||
assert evidence["source_area_id"] == str(dataset.area_id)
|
||||
assert evidence["tile_count"] == 1
|
||||
assert evidence["tile_union_bounds_epsg4326"] == pytest.approx([4.0, 51.0, 5.0, 52.0])
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("field", "value", "expected_code"),
|
||||
[
|
||||
("source_dataset_id", lambda: str(uuid4()), "DETECTION_TILE_MANIFEST_DATASET_MISMATCH"),
|
||||
("source_dataset_checksum_sha256", lambda: "b" * 64, "DETECTION_TILE_MANIFEST_PROVENANCE_MISMATCH"),
|
||||
("source_snapshot_id", lambda: str(uuid4()), "DETECTION_TILE_MANIFEST_PROVENANCE_MISMATCH"),
|
||||
("dataset_version", lambda: 99, "DETECTION_TILE_MANIFEST_PROVENANCE_MISMATCH"),
|
||||
("source_area_geometry_sha256", lambda: "c" * 64, "DETECTION_TILE_MANIFEST_PROVENANCE_MISMATCH"),
|
||||
],
|
||||
)
|
||||
def test_tile_manifest_rejects_dataset_or_provenance_mismatch(
|
||||
tmp_path: Path,
|
||||
field: str,
|
||||
value,
|
||||
expected_code: str,
|
||||
) -> None:
|
||||
db, dataset, _area = _dataset_and_session()
|
||||
manifest_path = _manifest(tmp_path, db, dataset)
|
||||
payload = json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||
payload[field] = value()
|
||||
manifest_path.write_text(canonical_manifest_json(payload), encoding="utf-8")
|
||||
|
||||
with pytest.raises(AppError) as exc_info:
|
||||
_validate(tmp_path, db, dataset)
|
||||
|
||||
assert exc_info.value.code == expected_code
|
||||
|
||||
|
||||
def test_tile_manifest_rejects_tile_bytes_changed_after_manifest_creation(tmp_path: Path) -> None:
|
||||
db, dataset, _area = _dataset_and_session()
|
||||
manifest_path = _manifest(tmp_path, db, dataset)
|
||||
payload = json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||
Path(payload["tiles"][0]["path"]).write_bytes(b"tampered tile")
|
||||
|
||||
with pytest.raises(AppError) as exc_info:
|
||||
_validate(tmp_path, db, dataset)
|
||||
|
||||
assert exc_info.value.code == "DETECTION_TILE_MANIFEST_TILE_INTEGRITY_MISMATCH"
|
||||
|
||||
|
||||
def test_tile_manifest_rejects_dataset_without_version_binding(tmp_path: Path) -> None:
|
||||
db, dataset, _area = _dataset_and_session()
|
||||
_manifest(tmp_path, db, dataset)
|
||||
dataset.versions.clear()
|
||||
|
||||
with pytest.raises(AppError) as exc_info:
|
||||
_validate(tmp_path, db, dataset)
|
||||
|
||||
assert exc_info.value.code == "DETECTION_TILE_MANIFEST_PROVENANCE_MISMATCH"
|
||||
assert "dataset_version_id" in exc_info.value.details["missing_fields"]
|
||||
|
||||
|
||||
def test_segmentation_tile_manifest_rejects_union_outside_dataset_scope(tmp_path: Path) -> None:
|
||||
db, dataset, _area = _dataset_and_session(with_area=False)
|
||||
manifest_path = _manifest(tmp_path, db, dataset)
|
||||
payload = json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||
payload["bounds"] = [4.0, 51.0, 6.0, 52.0]
|
||||
payload["tiles"][0]["bounds"] = [4.0, 51.0, 6.0, 52.0]
|
||||
manifest_path.write_text(canonical_manifest_json(payload), encoding="utf-8")
|
||||
|
||||
with pytest.raises(AppError) as exc_info:
|
||||
_validate(tmp_path, db, dataset, prefix="SEGMENTATION")
|
||||
|
||||
assert exc_info.value.code == "SEGMENTATION_TILE_MANIFEST_SCOPE_MISMATCH"
|
||||
Reference in New Issue
Block a user