Files
geointel/backend/tests/test_dataset_geojson_error_reporting.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

126 lines
4.2 KiB
Python

"""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"