"""An exported GeoJSON must say what it is and what it leaves out. The vector selection export caps its features and records ``truncated`` in the export *record*. The file itself said nothing: an operator downloads ``mol-selection.geojson``, opens it in QGIS and sees 250 buildings where the workbench said 1.400, with nothing in the file to indicate the difference. For a product whose promise is that an export is a reproducible result, a file that looks complete and is not is the sharpest possible violation. RFC 7946 allows foreign members on a FeatureCollection, and the detection export already uses one; this makes that convention uniform. """ from __future__ import annotations from datetime import datetime, timezone from uuid import uuid4 from app.services.export_service import ExportService def test_provenance_names_the_source_and_the_moment() -> None: dataset_id = uuid4() project_id = uuid4() member = ExportService.provenance_member( source="vector_selection", project_id=project_id, dataset_id=dataset_id, source_name="grb", source_version="2024-06", observed_at=datetime(2024, 6, 1, tzinfo=timezone.utc), ) assert member["source"] == "vector_selection" assert member["dataset_id"] == str(dataset_id) assert member["project_id"] == str(project_id) assert member["source_name"] == "grb" assert member["source_version"] == "2024-06" assert member["observed_at"].startswith("2024-06-01") assert member["exported_at"] def test_a_truncated_export_says_so_in_words() -> None: member = ExportService.provenance_member( source="vector_selection", project_id=uuid4(), dataset_id=uuid4(), feature_count=250, total_feature_count=1_400, truncated=True, ) assert member["complete"] is False assert member["feature_count"] == 250 assert member["total_feature_count"] == 1_400 assert "1400" in member["completeness_note"].replace(".", "").replace(",", "") assert "250" in member["completeness_note"] def test_a_complete_export_is_stated_as_complete() -> None: member = ExportService.provenance_member( source="detection_run", project_id=uuid4(), dataset_id=uuid4(), feature_count=12, total_feature_count=12, truncated=False, ) assert member["complete"] is True assert member["completeness_note"] is None def test_counts_that_disagree_are_treated_as_incomplete() -> None: """A caller that forgets the flag must not produce a file claiming completeness.""" member = ExportService.provenance_member( source="vector_selection", project_id=uuid4(), dataset_id=uuid4(), feature_count=100, total_feature_count=140, truncated=False, ) assert member["complete"] is False def test_selection_context_travels_with_the_file() -> None: area_id = uuid4() bbox = {"min_x": 5.0, "min_y": 51.1, "max_x": 5.2, "max_y": 51.3, "crs": "EPSG:4326"} member = ExportService.provenance_member( source="vector_selection", project_id=uuid4(), dataset_id=uuid4(), selection_bbox=bbox, selection_area_id=area_id, warnings=["22 van de 100 objecten liggen deels buiten de selectie."], ) assert member["selection_bbox"] == bbox assert member["selection_area_id"] == str(area_id) assert member["warnings"] == ["22 van de 100 objecten liggen deels buiten de selectie."] def test_empty_context_is_omitted_rather_than_written_as_null_noise() -> None: member = ExportService.provenance_member( source="dataset", project_id=uuid4(), dataset_id=uuid4(), ) assert "selection_bbox" not in member assert "warnings" not in member assert "source_version" not in member def test_the_member_attaches_under_a_reserved_key() -> None: collection = {"type": "FeatureCollection", "features": []} ExportService.attach_provenance( collection, ExportService.provenance_member(source="dataset", project_id=uuid4(), dataset_id=uuid4()), ) assert collection["type"] == "FeatureCollection" assert collection["geointel_provenance"]["source"] == "dataset"