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
189 lines
5.8 KiB
Python
189 lines
5.8 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
from uuid import uuid4
|
|
|
|
from app.core.errors import AppError
|
|
from app.services import geojson_service
|
|
from app.services.dataset_service import DatasetService
|
|
|
|
|
|
def test_parse_geojson_payload_extracts_metadata() -> None:
|
|
payload = {
|
|
"type": "FeatureCollection",
|
|
"features": [
|
|
{
|
|
"type": "Feature",
|
|
"geometry": {
|
|
"type": "Point",
|
|
"coordinates": [4.5, 51.3],
|
|
},
|
|
}
|
|
],
|
|
}
|
|
|
|
metadata = geojson_service.parse_geojson_payload(payload)
|
|
|
|
assert metadata["feature_count"] == 1
|
|
assert metadata["feature_geometry_count"] == 1
|
|
assert metadata["bounds_json"] == {
|
|
"min_x": 4.5,
|
|
"min_y": 51.3,
|
|
"max_x": 4.5,
|
|
"max_y": 51.3,
|
|
}
|
|
assert metadata["geometry_types"] == ["Point"]
|
|
|
|
|
|
def test_parse_geojson_payload_rejects_non_feature_collection() -> None:
|
|
payload = {"type": "Feature", "features": []}
|
|
|
|
try:
|
|
geojson_service.parse_geojson_payload(payload)
|
|
except ValueError as exc:
|
|
assert "FeatureCollection" in str(exc)
|
|
else:
|
|
raise AssertionError("Invalid GeoJSON should raise ValueError")
|
|
|
|
|
|
def test_get_dataset_geojson_reads_stored_payload(tmp_path, monkeypatch) -> None:
|
|
file_path = tmp_path / "dataset.geojson"
|
|
file_path.write_text(
|
|
json.dumps({"type": "FeatureCollection", "features": []}),
|
|
encoding="utf-8",
|
|
)
|
|
|
|
dataset = SimpleNamespace(dataset_type="vector", storage_path=str(file_path))
|
|
monkeypatch.setattr(DatasetService, "get_dataset", lambda _db, _id: dataset)
|
|
|
|
payload = DatasetService.get_dataset_geojson(Path("."), uuid4())
|
|
|
|
assert payload["type"] == "FeatureCollection"
|
|
|
|
|
|
def test_get_dataset_geojson_rejects_invalid_stored_json(tmp_path, monkeypatch) -> None:
|
|
file_path = tmp_path / "invalid.geojson"
|
|
file_path.write_text("not-json", encoding="utf-8")
|
|
|
|
dataset = SimpleNamespace(dataset_type="vector", storage_path=str(file_path))
|
|
monkeypatch.setattr(DatasetService, "get_dataset", lambda _db, _id: dataset)
|
|
|
|
try:
|
|
DatasetService.get_dataset_geojson(Path("."), uuid4())
|
|
except AppError as exc:
|
|
assert exc.code == "INVALID_GEOJSON"
|
|
else:
|
|
raise AssertionError("Invalid stored payload should raise AppError")
|
|
|
|
|
|
def test_parse_geojson_payload_returns_vector_metadata() -> None:
|
|
payload = {
|
|
"type": "FeatureCollection",
|
|
"features": [
|
|
{
|
|
"type": "Feature",
|
|
"geometry": {
|
|
"type": "Polygon",
|
|
"coordinates": [
|
|
[
|
|
[4.3, 51.2],
|
|
[4.4, 51.2],
|
|
[4.4, 51.3],
|
|
[4.3, 51.3],
|
|
[4.3, 51.2],
|
|
]
|
|
],
|
|
},
|
|
}
|
|
],
|
|
"crs": {"type": "name", "properties": {"name": "EPSG:31370"}},
|
|
}
|
|
|
|
metadata = geojson_service.parse_geojson_payload(payload)
|
|
|
|
assert metadata["feature_count"] == 1
|
|
assert metadata["feature_geometry_count"] == 1
|
|
assert metadata["geometry_types"] == ["Polygon"]
|
|
assert metadata["bounds_json"] == {
|
|
"min_x": 4.3,
|
|
"min_y": 51.2,
|
|
"max_x": 4.4,
|
|
"max_y": 51.3,
|
|
}
|
|
assert metadata["crs"] == "EPSG:31370"
|
|
assert metadata["approximate_area_m2"] is not None
|
|
assert metadata["approximate_area_m2"] >= 0.0
|
|
|
|
|
|
def test_parse_geojson_payload_reports_z_dimension_for_canonical_2d_storage() -> None:
|
|
metadata = geojson_service.parse_geojson_payload(
|
|
{
|
|
"type": "FeatureCollection",
|
|
"features": [
|
|
{
|
|
"type": "Feature",
|
|
"geometry": {"type": "Point", "coordinates": [5.08, 51.18, 0.0]},
|
|
"properties": {},
|
|
}
|
|
],
|
|
}
|
|
)
|
|
|
|
assert metadata["z_dimension_feature_count"] == 1
|
|
assert metadata["canonical_storage_dimension"] == "2D"
|
|
|
|
|
|
def test_parse_geojson_payload_rejects_invalid_geometry() -> None:
|
|
payload = {
|
|
"type": "FeatureCollection",
|
|
"features": [
|
|
{
|
|
"type": "Feature",
|
|
"geometry": {
|
|
"type": "Polygon",
|
|
"coordinates": "invalid",
|
|
},
|
|
}
|
|
],
|
|
}
|
|
|
|
try:
|
|
geojson_service.parse_geojson_payload(payload)
|
|
except ValueError as exc:
|
|
assert "Invalid feature geometry" in str(exc)
|
|
else:
|
|
raise AssertionError("Invalid geometry should raise ValueError")
|
|
|
|
|
|
def test_get_dataset_geojson_accepts_legacy_geojson_type(tmp_path, monkeypatch) -> None:
|
|
file_path = tmp_path / "legacy.geojson"
|
|
file_path.write_text(
|
|
json.dumps({"type": "FeatureCollection", "features": []}),
|
|
encoding="utf-8",
|
|
)
|
|
|
|
dataset = SimpleNamespace(dataset_type="geojson", storage_path=str(file_path))
|
|
monkeypatch.setattr(DatasetService, "get_dataset", lambda _db, _id: dataset)
|
|
|
|
payload = DatasetService.get_dataset_geojson(Path("."), uuid4())
|
|
assert payload["type"] == "FeatureCollection"
|
|
|
|
|
|
def test_vector_summary_supports_legacy_geojson_type(monkeypatch) -> None:
|
|
dataset = SimpleNamespace(
|
|
dataset_type="geojson",
|
|
metadata_json={
|
|
"feature_count": 7,
|
|
"geometry_types": ["Point"],
|
|
"bounds_json": {"min_x": 0.0, "min_y": 0.0, "max_x": 1.0, "max_y": 1.0},
|
|
},
|
|
storage_path="",
|
|
)
|
|
monkeypatch.setattr(DatasetService, "get_dataset", lambda _db, _id: dataset)
|
|
|
|
summary = DatasetService.vector_summary(Path("."), uuid4())
|
|
assert summary["feature_count"] == 7
|
|
assert summary["geometry_types"] == ["Point"]
|