"""A precise failure must not be reported as a generic one. ``get_dataset_geojson`` wrapped the JSON parse, the metadata read, the CRS resolution and the canonicalisation in one ``try``, and reported everything as "Stored dataset is not valid JSON" with a 500. An operator whose dataset has an unusable CRS was sent to inspect a file that parses perfectly well, and the specific AppError the canonicaliser raised — with its own code and status — never reached them. """ from __future__ import annotations import json from pathlib import Path from uuid import uuid4 import pytest from app.core.errors import AppError from app.models import Dataset from app.services.dataset_service import DatasetService from app.services.vector_feature_service import VectorFeatureService class FakeSession: def __init__(self, dataset: Dataset) -> None: self.dataset = dataset def get(self, _model, item_id): return self.dataset if item_id == self.dataset.id else None def _dataset(tmp_path: Path, payload: str) -> Dataset: path = tmp_path / "features.geojson" path.write_text(payload, encoding="utf-8") return Dataset( id=uuid4(), project_id=uuid4(), name="features.geojson", dataset_type="geojson", source="test", status="ready", storage_path=str(path), crs="EPSG:4326", ) def test_a_valid_dataset_is_returned(tmp_path: Path) -> None: dataset = _dataset( tmp_path, json.dumps( { "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": {"type": "Point", "coordinates": [5.0, 51.0]}, "properties": {}, } ], } ), ) result = DatasetService.get_dataset_geojson(FakeSession(dataset), dataset.id) assert result["type"] == "FeatureCollection" def test_unparseable_bytes_are_reported_as_invalid_json(tmp_path: Path) -> None: dataset = _dataset(tmp_path, "{ not json") with pytest.raises(AppError) as exc_info: DatasetService.get_dataset_geojson(FakeSession(dataset), dataset.id) assert exc_info.value.code == "INVALID_GEOJSON" assert "JSON" in exc_info.value.message def test_a_parseable_file_that_is_not_a_feature_collection_says_so(tmp_path: Path) -> None: """Valid JSON, wrong shape. Telling the operator it is not JSON sends them to inspect a file that parses perfectly well.""" dataset = _dataset(tmp_path, json.dumps({"type": "Feature", "geometry": None})) with pytest.raises(AppError) as exc_info: DatasetService.get_dataset_geojson(FakeSession(dataset), dataset.id) assert exc_info.value.code == "INVALID_GEOJSON" assert "FeatureCollection" in exc_info.value.message # The canonicaliser's own status survives; it is a bad request, not a # server fault. assert exc_info.value.status_code == 400 def test_a_canonicalisation_failure_keeps_its_own_code(tmp_path: Path, monkeypatch) -> None: dataset = _dataset(tmp_path, json.dumps({"type": "FeatureCollection", "features": []})) def failing(*_args, **_kwargs): raise AppError(code="INVALID_CRS", message="Unusable source CRS", status_code=409) monkeypatch.setattr(VectorFeatureService, "canonicalize_geojson_payload", staticmethod(failing)) with pytest.raises(AppError) as exc_info: DatasetService.get_dataset_geojson(FakeSession(dataset), dataset.id) assert exc_info.value.code == "INVALID_CRS" assert exc_info.value.status_code == 409 def test_an_unexpected_failure_still_becomes_a_server_error(tmp_path: Path, monkeypatch) -> None: """A genuine bug must not masquerade as a client mistake.""" dataset = _dataset(tmp_path, json.dumps({"type": "FeatureCollection", "features": []})) def exploding(*_args, **_kwargs): raise RuntimeError("pyproj blew up") monkeypatch.setattr(VectorFeatureService, "canonicalize_geojson_payload", staticmethod(exploding)) with pytest.raises(AppError) as exc_info: DatasetService.get_dataset_geojson(FakeSession(dataset), dataset.id) assert exc_info.value.status_code == 500 assert exc_info.value.code == "DATASET_GEOJSON_UNREADABLE"