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

194 lines
6.4 KiB
Python

from __future__ import annotations
from pathlib import Path
from uuid import UUID, uuid4
from app.core.errors import AppError
from app.models import Dataset
from app.services.storage_service import StorageService
from app.services.vector_operations_service import VectorOperationsService
class FakeSession:
def __init__(self, datasets):
self.datasets = {dataset.id: dataset for dataset in datasets}
self.added = []
def get(self, model, item_id):
return self.datasets.get(item_id)
def add(self, value):
self.added.append(value)
def commit(self):
return None
def refresh(self, _value):
return None
def _make_vector_dataset(dataset_id: UUID, project_id: UUID, raw: str) -> Dataset:
path = Path(f"./tests/.tmp_{dataset_id}.geojson")
path.write_text(raw, encoding="utf-8")
return Dataset(
id=dataset_id,
project_id=project_id,
name=f"{dataset_id}.geojson",
dataset_type="vector",
source="test",
storage_path=str(path),
original_filename=f"{dataset_id}.geojson",
stored_filename=f"{dataset_id}.geojson",
content_type="application/geo+json",
)
def test_vector_inspect_extracts_feature_count_and_bbox(tmp_path) -> None:
dataset_id = uuid4()
project_id = uuid4()
source_path = tmp_path / f"{dataset_id}.geojson"
source_path.write_text(
'{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[4.1,51.2]}},{"type":"Feature","geometry":{"type":"Point","coordinates":[4.2,51.3]}}]}',
encoding="utf-8",
)
dataset = Dataset(
id=dataset_id,
project_id=project_id,
name="vector.geojson",
dataset_type="vector",
source="test",
storage_path=str(source_path),
original_filename="vector.geojson",
stored_filename="vector.geojson",
content_type="application/geo+json",
)
db = FakeSession([dataset])
payload = VectorOperationsService.inspect(db, dataset_id)
assert payload.feature_count == 2
assert payload.geometry_type_summary["Point"] == 2
assert payload.bounds_json == {"min_x": 4.1, "min_y": 51.2, "max_x": 4.2, "max_y": 51.3}
def test_vector_bbox_and_stats_share_summary(tmp_path) -> None:
dataset_id = uuid4()
project_id = uuid4()
source_path = tmp_path / f"{dataset_id}.geojson"
source_path.write_text(
'{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[4.0,51.0]}}]}',
encoding="utf-8",
)
dataset = Dataset(
id=dataset_id,
project_id=project_id,
name="vector.geojson",
dataset_type="vector",
source="test",
storage_path=str(source_path),
original_filename="vector.geojson",
stored_filename="vector.geojson",
content_type="application/geo+json",
)
db = FakeSession([dataset])
bbox = VectorOperationsService.bbox(db, dataset_id)
stats = VectorOperationsService.stats(db, dataset_id)
assert bbox["feature_count"] == 1
assert stats["feature_count"] == 1
assert stats["geometry_type_summary"]["Point"] == 1
def test_vector_intersect_creates_derived_dataset(monkeypatch, tmp_path) -> None:
source_id = uuid4()
target_id = uuid4()
project_id = uuid4()
source_path = tmp_path / f"{source_id}.geojson"
target_path = tmp_path / f"{target_id}.geojson"
source_path.write_text(
'{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[4.1,51.2]}}]}',
encoding="utf-8",
)
target_path.write_text(
'{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[4.0,51.0],[4.0,52.0],[5.0,52.0],[5.0,51.0],[4.0,51.0]]]}}]}',
encoding="utf-8",
)
source = Dataset(
id=source_id,
project_id=project_id,
name="source.geojson",
dataset_type="vector",
source="test",
storage_path=str(source_path),
original_filename="source.geojson",
stored_filename="source.geojson",
content_type="application/geo+json",
)
target = Dataset(
id=target_id,
project_id=project_id,
name="target.geojson",
dataset_type="vector",
source="test",
storage_path=str(target_path),
original_filename="target.geojson",
stored_filename="target.geojson",
content_type="application/geo+json",
)
db = FakeSession([source, target])
persisted = {}
def _persist_dataset_file(project_id: str, dataset_id: str, dataset_type: str, original_filename: str, content: bytes, content_type: str | None):
persisted["project_id"] = project_id
persisted["dataset_id"] = dataset_id
output = tmp_path / f"{dataset_id}_{dataset_type}.geojson"
output.write_bytes(content)
return {
"original_filename": original_filename,
"stored_filename": output.name,
"content_type": content_type or "application/geo+json",
"size_bytes": len(content),
"checksum_sha256": "test",
"storage_path": str(output),
}
monkeypatch.setattr(StorageService, "persist_dataset_file", _persist_dataset_file)
derived_id = VectorOperationsService.intersect(db, source_id, target_id, "intersect_output")
assert derived_id is not None
assert isinstance(derived_id, UUID)
assert persisted["project_id"] == str(project_id)
def test_vector_operations_reject_invalid_geometry(tmp_path) -> None:
dataset_id = uuid4()
project_id = uuid4()
source_path = tmp_path / f"{dataset_id}.geojson"
source_path.write_text(
'{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":"invalid"}}]}',
encoding="utf-8",
)
dataset = Dataset(
id=dataset_id,
project_id=project_id,
name="invalid.geojson",
dataset_type="vector",
source="test",
storage_path=str(source_path),
original_filename="invalid.geojson",
stored_filename="invalid.geojson",
content_type="application/geo+json",
)
db = FakeSession([dataset])
try:
VectorOperationsService.inspect(db, dataset_id)
except AppError as exc:
assert exc.code == "INVALID_GEOMETRY"
else:
raise AssertionError("Invalid geometry should raise INVALID_GEOMETRY")