feat(provenance): govern source snapshots and data inputs

This commit is contained in:
Jens
2026-08-01 23:46:17 +02:00
parent cebeb5f3b4
commit 5b3c17b494
96 changed files with 20156 additions and 351 deletions
@@ -18,7 +18,7 @@ from app.core.config import Settings
from app.core.errors import AppError
from app.db.session import get_db
from app.main import app
from app.models import Area, Dataset, DatasetVersion, Job, Project
from app.models import Area, Dataset, DatasetVersion, Job, Project, SourceRegistry, SourceSnapshot
from app.schemas.orthophoto import OrthophotoAcquireRequest
from app.services.orthophoto_acquisition_service import OrthophotoAcquisitionService
@@ -41,6 +41,15 @@ class FakeSession:
def add(self, row):
self.added.append(row)
def flush(self):
# The governed importer persists source identities and immutable
# snapshots before the Dataset. Mirror the database-generated UUIDs
# so this harness exercises that Phase 2 path rather than the legacy
# no-registry fallback.
for row in self.added:
if getattr(row, "id", None) is None:
row.id = uuid4()
def commit(self):
return None
@@ -50,13 +59,23 @@ class FakeSession:
def refresh(self, row):
return row
def query(self, _model):
return FakeQuery(self.query_result)
def query(self, model):
rows = [
row
for (row_model, _row_id), row in self.rows.items()
if row_model is model and isinstance(row, model)
]
rows.extend(row for row in self.added if isinstance(row, model))
if isinstance(self.query_result, model):
rows.append(self.query_result)
elif isinstance(self.query_result, list):
rows.extend(row for row in self.query_result if isinstance(row, model))
return FakeQuery(rows)
class FakeQuery:
def __init__(self, result):
self.result = result
def __init__(self, results):
self.results = list(results)
def filter(self, *_args):
return self
@@ -65,7 +84,10 @@ class FakeQuery:
return self
def first(self):
return self.result
return self.results[0] if self.results else None
def one_or_none(self):
return self.first()
class FakeImageResponse:
@@ -222,12 +244,23 @@ def test_regional_orthophoto_products_bind_provider_and_governed_scope(
)
dataset = next(row for row in db.added if isinstance(row, Dataset))
source = next(row for row in db.added if isinstance(row, SourceRegistry))
snapshot = next(row for row in db.added if isinstance(row, SourceSnapshot))
assert result["provider"] == provider
assert result["layer"] == layer
assert dataset.source_name == provider
assert dataset.source_metadata["coverage_zone"] == coverage_zone
assert dataset.source_metadata["license_note"]
assert dataset.provenance_metadata["request_url"].startswith(prepared["product"].wms_url)
assert dataset.source_registry_id == source.id
assert dataset.source_snapshot_id == snapshot.id
assert dataset.validation_status == "passed"
assert dataset.provenance_status == "complete"
assert dataset.quarantine_status == "not_quarantined"
assert snapshot.source_registry_id == source.id
assert snapshot.checksum_sha256 == dataset.checksum_sha256
assert snapshot.ingest_status == "ingested"
assert snapshot.freshness_status == "current"
prepared = OrthophotoAcquisitionService._prepared_request(_selection_payload(product_key="1971"), settings)
assert prepared["params"]["LAYERS"] == "OKZPAN71VL"
@@ -291,6 +324,8 @@ def test_orthophoto_acquisition_persists_georeferenced_raster_and_provenance(tmp
assert len(datasets) == 1
assert len(versions) == 1
dataset = datasets[0]
source = next(row for row in db.added if isinstance(row, SourceRegistry))
snapshot = next(row for row in db.added if isinstance(row, SourceSnapshot))
assert result["output_dataset_id"] == str(dataset.id)
assert result["reused"] is False
assert dataset.project_id == project_id
@@ -301,6 +336,15 @@ def test_orthophoto_acquisition_persists_georeferenced_raster_and_provenance(tmp
assert dataset.crs == "EPSG:31370"
assert dataset.provenance_metadata["acquisition"] == "explicit_bounded_map_selection"
assert dataset.provenance_metadata["request_hash"] == prepared["request_hash"]
assert dataset.source_registry_id == source.id
assert dataset.source_snapshot_id == snapshot.id
assert dataset.validation_status == "passed"
assert dataset.provenance_status == "complete"
assert dataset.lineage_status == "not_applicable"
assert dataset.quarantine_status == "not_quarantined"
assert snapshot.source_registry_id == source.id
assert snapshot.checksum_sha256 == dataset.checksum_sha256
assert snapshot.freshness_status == "current"
assert dataset.source_metadata["attribution"].startswith("Bron: Orthofotomozaiek Vlaanderen")
assert dataset.storage_path is not None
with rasterio.open(dataset.storage_path) as stored: