Files
geointel/backend/tests/test_dataset_consumption_gate.py
T
Jens faeb58ef6d
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
Initial public release
2026-08-31 21:56:53 +02:00

328 lines
12 KiB
Python

from __future__ import annotations
from types import SimpleNamespace
from uuid import uuid4
import pytest
from shapely.geometry import box
from app.core.errors import AppError
from app.models import Dataset, SourceRegistry, SourceSnapshot
from app.services.coverage_registry_service import CoverageRegistryService, SOURCE_DEFINITIONS
import app.services.dataset_consumption_gate_service as gate_module
from app.services.dataset_consumption_gate_service import DatasetConsumptionGate
from app.services.export_service import ExportService
def _governed_dataset(
*,
source_key: str = "grb",
classification: str = "authoritative",
snapshot_freshness_status: str = "current",
) -> Dataset:
source_id = uuid4()
snapshot_id = uuid4()
checksum = "a" * 64
source = SourceRegistry(
id=source_id,
source_key=source_key,
display_name=f"{source_key} test source",
classification=classification,
authority_name="GeoIntel test authority",
authority_scope_json={"scope": "test"},
usage_policy_json={
"ground_truth_allowed": classification == "authoritative",
"validation_authority": {"building_validation": "primary"}
if classification == "authoritative"
else {},
},
)
snapshot = SourceSnapshot(
id=snapshot_id,
source_registry_id=source_id,
snapshot_key="test-snapshot",
checksum_sha256=checksum,
freshness_status=snapshot_freshness_status,
ingest_status="ingested",
)
dataset = Dataset(
id=uuid4(),
project_id=uuid4(),
name="governed.tif",
dataset_type="raster",
source=source_key,
source_name=source_key,
dataset_role="source",
checksum_sha256=checksum,
source_registry_id=source_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 = source
dataset.source_snapshot = snapshot
return dataset
def test_governed_dataset_passes_production_inference_and_authoritative_coverage() -> None:
dataset = _governed_dataset()
inference = DatasetConsumptionGate.assert_eligible(dataset, purpose="production_inference")
coverage = DatasetConsumptionGate.assert_eligible(dataset, purpose="authoritative_coverage")
assert inference.eligible is True
assert coverage.eligible is True
@pytest.mark.parametrize(
("field", "value", "error_code"),
(
("provenance_status", "incomplete", "DATASET_PROVENANCE_INCOMPLETE"),
("validation_status", "failed", "DATASET_QUARANTINED"),
("quarantine_status", "quarantined", "DATASET_QUARANTINED"),
),
)
def test_explicit_unsafe_states_can_never_be_relaxed(field: str, value: str, error_code: str) -> None:
dataset = _governed_dataset()
setattr(dataset, field, value)
with pytest.raises(AppError) as exc_info:
DatasetConsumptionGate.assert_eligible(
dataset,
purpose="production_inference",
fixture_mode=True,
)
assert exc_info.value.code == error_code
assert field.replace("_status", "") in " ".join(exc_info.value.details["reasons"])
def test_legacy_fixture_can_support_fixture_qa_but_never_authoritative_coverage() -> None:
fixture = Dataset(
id=uuid4(),
project_id=uuid4(),
name="fixture.tif",
dataset_type="raster",
source="fixture",
)
qa = DatasetConsumptionGate.assert_eligible(fixture, purpose="quality_assessment")
with pytest.raises(AppError) as inference_error:
DatasetConsumptionGate.assert_eligible(
fixture,
purpose="production_inference",
fixture_mode=True,
)
with pytest.raises(AppError) as export_error:
DatasetConsumptionGate.assert_eligible(fixture, purpose="export")
with pytest.raises(AppError) as fixture_export_error:
DatasetConsumptionGate.assert_eligible(fixture, purpose="export", fixture_mode=True)
coverage = DatasetConsumptionGate.evaluate(fixture, purpose="authoritative_coverage")
assert qa.fixture_legacy_exception is True
assert inference_error.value.code == "DATASET_PROVENANCE_INCOMPLETE"
assert "fixture_qa_only" in inference_error.value.details["reasons"]
assert export_error.value.code == "DATASET_PROVENANCE_INCOMPLETE"
assert fixture_export_error.value.code == "DATASET_PROVENANCE_INCOMPLETE"
assert "fixture_qa_only" in fixture_export_error.value.details["reasons"]
assert coverage.eligible is False
assert "fixture_not_authoritative_coverage" in coverage.reasons
def test_unprovenanced_persistent_dataset_is_blocked(monkeypatch) -> None:
dataset = Dataset(
id=uuid4(),
project_id=uuid4(),
name="manual.tif",
dataset_type="raster",
source="manual_upload",
)
monkeypatch.setattr(gate_module, "sa_inspect", lambda _dataset: SimpleNamespace(transient=False))
with pytest.raises(AppError) as exc_info:
DatasetConsumptionGate.assert_eligible(
dataset,
purpose="production_inference",
fixture_mode=True,
)
assert exc_info.value.code == "DATASET_PROVENANCE_INCOMPLETE"
assert "phase2_provenance_missing" in exc_info.value.details["reasons"]
assert "fixture_source_required" in exc_info.value.details["reasons"]
def test_transient_orm_test_double_can_only_bypass_missing_legacy_fields_for_qa() -> None:
transient = Dataset(
id=uuid4(),
project_id=uuid4(),
name="transient-test.tif",
dataset_type="raster",
source="manual_upload",
)
decision = DatasetConsumptionGate.assert_eligible(transient, purpose="quality_assessment")
coverage = DatasetConsumptionGate.evaluate(transient, purpose="authoritative_coverage")
with pytest.raises(AppError) as production_error:
DatasetConsumptionGate.assert_eligible(transient, purpose="production_inference")
assert decision.fixture_legacy_exception is True
assert production_error.value.code == "DATASET_PROVENANCE_INCOMPLETE"
assert coverage.eligible is False
assert "phase2_provenance_missing" in coverage.reasons
@pytest.mark.parametrize("purpose", ("production_inference", "derived_processing", "export"))
def test_passed_manual_or_experimental_dataset_cannot_cross_production_boundary(purpose: str) -> None:
"""A syntactically valid manual upload remains experimental, never production-ready."""
manual = _governed_dataset(source_key="manual", classification="experimental")
manual.source = "manual_upload"
with pytest.raises(AppError) as exc_info:
DatasetConsumptionGate.assert_eligible(manual, purpose=purpose) # type: ignore[arg-type]
assert exc_info.value.code == "DATASET_PROVENANCE_INCOMPLETE"
assert "experimental_source_not_allowed_for_purpose" in exc_info.value.details["reasons"]
def test_fully_governed_demo_fixture_still_cannot_enter_production_inference() -> None:
fixture = _governed_dataset(source_key="fixture", classification="experimental")
fixture.source_metadata = {"fixture": True, "usage": "offline demo raster workflow only"}
with pytest.raises(AppError) as exc_info:
DatasetConsumptionGate.assert_eligible(fixture, purpose="production_inference")
assert exc_info.value.code == "DATASET_PROVENANCE_INCOMPLETE"
assert "experimental_source_not_allowed_for_purpose" in exc_info.value.details["reasons"]
def test_reference_validation_requires_authoritative_ground_truth_reference() -> None:
reference = _governed_dataset()
reference.dataset_type = "vector"
reference.dataset_role = "reference"
decision = DatasetConsumptionGate.assert_eligible(
reference,
purpose="reference_validation",
reference_task="building_validation",
)
assert decision.eligible is True
reference.source_registry.classification = "corroborative"
with pytest.raises(AppError) as exc_info:
DatasetConsumptionGate.assert_eligible(
reference,
purpose="reference_validation",
reference_task="building_validation",
)
assert exc_info.value.code == "DATASET_PROVENANCE_INCOMPLETE"
assert "reference_source_not_authoritative" in exc_info.value.details["reasons"]
def test_pending_regional_building_authority_cannot_become_truth_without_approval() -> None:
reference = _governed_dataset(source_key="spw_picc", classification="authoritative")
reference.dataset_type = "vector"
reference.dataset_role = "reference"
reference.source_registry.authority_scope_json = {"zone": "Wallonia"}
reference.source_registry.usage_policy_json = {
"ground_truth_allowed": True,
"validation_authority": {"building_validation": "regional_primary_pending_contract"},
}
with pytest.raises(AppError) as exc_info:
DatasetConsumptionGate.assert_eligible(
reference,
purpose="reference_validation",
reference_task="building_validation",
)
assert exc_info.value.code == "DATASET_PROVENANCE_INCOMPLETE"
assert "reference_task_authority_not_approved" in exc_info.value.details["reasons"]
def test_source_snapshot_must_belong_to_the_dataset_source_registry() -> None:
dataset = _governed_dataset()
dataset.source_snapshot.source_registry_id = uuid4()
with pytest.raises(AppError) as exc_info:
DatasetConsumptionGate.assert_eligible(dataset, purpose="production_inference")
assert exc_info.value.code == "DATASET_PROVENANCE_INCOMPLETE"
assert "source_snapshot_registry_mismatch" in exc_info.value.details["reasons"]
@pytest.mark.parametrize("freshness_status", ("unknown", "review_required", "due", "stale"))
def test_non_consumable_source_snapshot_freshness_is_blocked_at_production_boundaries(
freshness_status: str,
) -> None:
dataset = _governed_dataset(snapshot_freshness_status=freshness_status)
for purpose in ("production_inference", "authoritative_coverage"):
with pytest.raises(AppError) as exc_info:
DatasetConsumptionGate.assert_eligible(dataset, purpose=purpose) # type: ignore[arg-type]
assert exc_info.value.code == "DATASET_PROVENANCE_INCOMPLETE"
assert "source_snapshot_freshness_not_eligible" in exc_info.value.details["reasons"]
def test_coverage_registry_ignores_explicitly_incomplete_materialization() -> None:
definition = next(item for item in SOURCE_DEFINITIONS if item.contract.source_name == "digitaal_vlaanderen")
unsafe_materialization = SimpleNamespace(
id=uuid4(),
status="ready",
source_name="grb",
validation_status="passed",
provenance_status="incomplete",
lineage_status="complete",
quarantine_status="not_quarantined",
)
matches, fully_covered = CoverageRegistryService._matching_datasets(
[unsafe_materialization],
definition,
"buildings",
"flanders",
box(4.0, 50.8, 4.1, 50.9),
)
assert matches == []
assert fully_covered is False
def test_vector_export_is_fail_closed_before_selection(monkeypatch) -> None:
dataset = _governed_dataset()
dataset.dataset_type = "vector"
dataset.status = "quarantined"
queried = False
class _Session:
@staticmethod
def get(model, item_id):
return dataset if model is Dataset and item_id == dataset.id else None
def _unexpected_selection(*_args, **_kwargs):
nonlocal queried
queried = True
raise AssertionError("unsafe dataset must be rejected before querying vector features")
monkeypatch.setattr(
"app.services.export_service.VectorFeatureService.select_features_by_bbox",
_unexpected_selection,
)
with pytest.raises(AppError) as exc_info:
ExportService.export_vector_selection_geojson(
_Session(),
dataset.id,
{"min_x": 4.0, "min_y": 50.0, "max_x": 4.1, "max_y": 50.1, "crs": "EPSG:4326"},
)
assert exc_info.value.code == "DATASET_QUARANTINED"
assert queried is False