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,327 @@
|
||||
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
|
||||
Reference in New Issue
Block a user