"""A downloaded export must carry its own provenance and its own limits. Counterpart to ``test_export_provenance_member``: this reads the file the service actually writes, so it proves the member survives serialisation rather than only that the helper builds it. """ from __future__ import annotations import json from datetime import datetime, timezone from pathlib import Path from uuid import uuid4 from app.models import Dataset, Export from app.services.export_service import ExportService from app.services.storage_service import StorageService from app.services.vector_feature_service import VectorFeatureService from tests.test_sprint107_map_selection_export import FakeSession, _govern_fixture_dataset BBOX = {"min_x": 4.9, "min_y": 50.9, "max_x": 5.2, "max_y": 51.2, "crs": "EPSG:4326"} def _dataset(dataset_id): dataset = _govern_fixture_dataset( Dataset( id=dataset_id, project_id=uuid4(), name="grb-buildings.geojson", dataset_type="vector", source="grb", source_name="grb", source_version="2024-06", observed_at=datetime(2024, 6, 1, tzinfo=timezone.utc), status="ready", ) ) return dataset def _selection(*, feature_count: int, total: int, truncated: bool, warning: str | None = None): return { "selection_bbox": BBOX, "feature_count": feature_count, "total_feature_count": total, "limit": 250, "truncated": truncated, "summary": {"selection_edge_warning": warning} if warning else {}, "geojson": { "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": {"type": "Point", "coordinates": [5.0, 51.0]}, "properties": {"vector_feature_id": f"vf-{index}"}, } for index in range(feature_count) ], }, } def _export(monkeypatch, tmp_path: Path, selection) -> dict: dataset_id = uuid4() dataset = _dataset(dataset_id) db = FakeSession({(Dataset, dataset_id): dataset}) export_path = tmp_path / "selection.geojson" monkeypatch.setattr(StorageService, "dataset_export_path", lambda *_args: str(export_path)) monkeypatch.setattr(VectorFeatureService, "select_features_by_bbox", lambda *_args, **_kwargs: selection) ExportService.export_vector_selection_geojson(db, dataset_id, BBOX, limit=250, name="selection") assert [item for item in db.added if isinstance(item, Export)] return json.loads(export_path.read_text(encoding="utf-8")) def test_a_truncated_export_file_states_that_it_is_partial(monkeypatch, tmp_path: Path) -> None: written = _export(monkeypatch, tmp_path, _selection(feature_count=2, total=1_400, truncated=True)) provenance = written["geointel_provenance"] assert provenance["complete"] is False assert "1400" in provenance["completeness_note"].replace(".", "") # The features are still there; the file simply no longer implies it holds # everything the selection contains. assert len(written["features"]) == 2 def test_a_complete_export_file_says_so(monkeypatch, tmp_path: Path) -> None: written = _export(monkeypatch, tmp_path, _selection(feature_count=2, total=2, truncated=False)) provenance = written["geointel_provenance"] assert provenance["complete"] is True assert provenance["completeness_note"] is None def test_the_file_identifies_its_source_edition(monkeypatch, tmp_path: Path) -> None: written = _export(monkeypatch, tmp_path, _selection(feature_count=1, total=1, truncated=False)) provenance = written["geointel_provenance"] assert provenance["source_name"] == "grb" assert provenance["source_version"] == "2024-06" assert provenance["observed_at"].startswith("2024-06-01") assert provenance["selection_bbox"] == BBOX def test_selection_caveats_travel_into_the_file(monkeypatch, tmp_path: Path) -> None: written = _export( monkeypatch, tmp_path, _selection(feature_count=1, total=1, truncated=False, warning="22 objecten liggen deels buiten de selectie."), ) assert written["geointel_provenance"]["warnings"] == ["22 objecten liggen deels buiten de selectie."] def test_the_file_remains_a_valid_feature_collection(monkeypatch, tmp_path: Path) -> None: """The provenance is a foreign member, not a change to the GeoJSON shape.""" written = _export(monkeypatch, tmp_path, _selection(feature_count=1, total=1, truncated=False)) assert written["type"] == "FeatureCollection" assert isinstance(written["features"], list) assert written["features"][0]["type"] == "Feature"