import json import pytest from app.core.geo import PathPoint from app.models.buildings import BuildingsQueryRequest from app.models.terrain import TerrainProfileRequest from app.services import buildings as buildings_service from app.services import terrain as terrain_service class FakeResult: def __init__(self, rows: list[dict[str, object]]) -> None: self._rows = rows def mappings(self) -> "FakeResult": return self def all(self) -> list[dict[str, object]]: return self._rows class FakeSession: def __init__(self, rows: list[dict[str, object]]) -> None: self.rows = rows self.calls: list[dict[str, object]] = [] def execute(self, statement: object, params: dict[str, object]) -> FakeResult: self.calls.append({"statement": str(statement), "params": params}) return FakeResult(self.rows) def test_buildings_query_bbox_returns_feature_collection() -> None: feature = { "type": "Feature", "id": 123, "geometry": {"type": "Polygon", "coordinates": []}, "properties": { "height_m": 12.0, "levels": 4, "building_type": "apartments", "source": "estimated", }, } db = FakeSession(rows=[{"feature": json.dumps(feature)}]) request = BuildingsQueryRequest(bbox=(30.0, 59.0, 31.0, 60.0)) response = buildings_service.query_buildings(request, db, limit=10) assert response.type == "FeatureCollection" assert response.features == [feature] assert db.calls[0]["params"]["limit"] == 10 def test_building_heights_along_returns_height_per_point() -> None: db = FakeSession(rows=[{"i": 0, "height_m": 15.0}, {"i": 1, "height_m": 0.0}]) points = [ PathPoint(lat=59.0, lon=30.0, distance_m=0), PathPoint(lat=59.1, lon=30.1, distance_m=1), ] heights = buildings_service.building_heights_along(points, db) assert heights == [15.0, 0.0] assert "jsonb_to_recordset" in db.calls[0]["statement"] def test_buildings_query_rejects_invalid_bbox() -> None: with pytest.raises(ValueError, match="bbox"): BuildingsQueryRequest(bbox=(31.0, 59.0, 30.0, 60.0)) def test_terrain_profile_applies_building_heights(monkeypatch: pytest.MonkeyPatch) -> None: def fake_heights(points: list[PathPoint], db: object) -> list[float]: return [12.0] + [0.0] * (len(points) - 1) monkeypatch.setattr(buildings_service, "building_heights_along", fake_heights) request = TerrainProfileRequest( start={"lat": 59.0, "lon": 30.0}, end={"lat": 59.1, "lon": 30.1}, samples=2, include_buildings=True, include_canopy=False, ) response = terrain_service.terrain_profile(request, db=object()) assert response.samples[0].building_m == 12.0 assert response.samples[0].surface_m == response.samples[0].ground_m + 12.0