Files
geointel/backend/tests/test_sprint205_dhmv_terrain.py
T
JensandClaude Opus 5 39c12822bc extract the map workspace's domain layer out of the component
MapWorkspace.tsx opened with ~590 lines of theme catalogue, dataset matching
and label formatting above a 3.200-line component. None of it is React, all of
it is independently testable, and both render paths read from it, so it belongs
beside the pure helpers that already live in mapWorkspaceUtils.

The contract tests that read MapWorkspace.tsx would have gone red for a move
that changes no behaviour at all — 24 of them. That is the brittleness the
frontend_contract helper exists to remove, so it gains read_map_workspace():
the workspace is one feature spread over several modules, and a contract
belongs to the feature rather than to whichever file currently holds it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 18:49:06 +02:00

656 lines
26 KiB
Python

from __future__ import annotations
from pathlib import Path
from uuid import uuid4
import numpy as np
import pytest
import rasterio
from fastapi.testclient import TestClient
from geoalchemy2.shape import from_shape
from pyproj import Transformer
from rasterio.io import MemoryFile
from rasterio.transform import from_origin
from shapely.geometry import MultiPolygon, box
from app.core.config import Settings
from app.core.errors import AppError
from app.db.session import get_db
from app.main import app
from app.models import Area, Dataset, DatasetVersion, Job, Project, SourceRegistry, SourceSnapshot
from app.schemas.dhmv import DhmvAcquireRequest, TerrainPartitionSelectionRequest, TerrainSelectionRequest
from app.services.dhmv_acquisition_service import DhmvAcquisitionService
from app.services.terrain_analysis_service import TerrainAnalysisService
from tests.frontend_contract import read_map_workspace
ROOT = Path(__file__).resolve().parents[2]
class FakeQuery:
def __init__(self, results=None):
self.results = list(results or [])
def filter(self, *_args):
return self
def order_by(self, *_args):
return self
def first(self):
return self.results[0] if self.results else None
def one_or_none(self):
return self.first()
def all(self):
return list(self.results)
class FakeSession:
def __init__(self, rows=None, query_result=None):
self.rows = rows or {}
self.query_result = query_result
self.added = []
def get(self, model, row_id):
row = self.rows.get((model, row_id))
if row is not None:
return row
return next((item for item in self.added if isinstance(item, model) and item.id == row_id), None)
def add(self, row):
self.added.append(row)
def flush(self):
# Exercise the governed source/snapshot import path with database-like
# primary-key assignment instead of silently falling back to legacy
# fixture behavior.
for row in self.added:
if getattr(row, "id", None) is None:
row.id = uuid4()
def commit(self):
return None
def rollback(self):
return None
def refresh(self, row):
return row
def query(self, model):
rows = [
row
for (row_model, _row_id), row in self.rows.items()
if row_model is model and isinstance(row, model)
]
rows.extend(row for row in self.added if isinstance(row, model))
if isinstance(self.query_result, model):
rows.append(self.query_result)
elif isinstance(self.query_result, list):
rows.extend(row for row in self.query_result if isinstance(row, model))
return FakeQuery(rows)
class FakeResponse:
def __init__(self, content: bytes, content_type: str):
self.content = content
self.headers = {"Content-Type": content_type, "Content-Length": str(len(content))}
def __enter__(self):
return self
def __exit__(self, *_args):
return None
def read(self, limit: int):
return self.content[:limit]
def lambert_bbox_payload(*, side_m: float = 100.0, product_key: str = "dtm_1m", area_id=None) -> DhmvAcquireRequest:
west, south = 200_000.0, 210_000.0
transformer = Transformer.from_crs("EPSG:31370", "EPSG:4326", always_xy=True)
min_x, min_y = transformer.transform(west, south)
max_x, max_y = transformer.transform(west + side_m, south + side_m)
return DhmvAcquireRequest(
bbox={"min_x": min_x, "min_y": min_y, "max_x": max_x, "max_y": max_y, "crs": "EPSG:4326"},
area_id=area_id,
product_key=product_key,
resolution_m=5.0,
force_refresh=True,
)
def elevation_tiff(*, left: float, top: float, width: int, height: int, resolution: float = 5.0) -> bytes:
rows, columns = np.indices((height, width))
values = (20.0 + columns * 0.5 + rows * 1.0).astype("float32")
with MemoryFile() as memory:
with memory.open(
driver="GTiff",
width=width,
height=height,
count=1,
dtype="float32",
crs="EPSG:31370",
transform=from_origin(left, top, resolution, resolution),
nodata=-9999.0,
) as output:
output.write(values, 1)
return memory.read()
def constant_elevation_tiff(*, left: float, top: float, value: float) -> bytes:
values = np.full((20, 20), value, dtype="float32")
with MemoryFile() as memory:
with memory.open(
driver="GTiff",
width=20,
height=20,
count=1,
dtype="float32",
crs="EPSG:31370",
transform=from_origin(left, top, 5.0, 5.0),
nodata=-9999.0,
) as output:
output.write(values, 1)
return memory.read()
def edge_elevation_tiff(*, left: float, top: float, x_resolution: float, y_resolution: float = 5.0) -> bytes:
rows, columns = np.indices((20, 20))
values = (20.0 + columns * 0.5 + rows).astype("float32")
with MemoryFile() as memory:
with memory.open(
driver="GTiff",
width=20,
height=20,
count=1,
dtype="float32",
crs="EPSG:31370",
transform=from_origin(left, top, x_resolution, y_resolution),
nodata=-9999.0,
) as output:
output.write(values, 1)
return memory.read()
def multipart_tiff(content: bytes) -> tuple[bytes, str]:
boundary = "wcs-test"
payload = (
f"--{boundary}\r\nContent-Type: text/xml\r\nContent-ID: GML-Part\r\n\r\n<coverage/>\r\n"
f"--{boundary}\r\nContent-Type: image/tiff\r\nContent-ID: coverage.tif\r\n\r\n"
).encode() + content + f"\r\n--{boundary}--\r\n".encode()
return payload, f'multipart/mixed; boundary="{boundary}"'
def test_dhmv_registry_is_governed_and_semantically_explicit() -> None:
products = DhmvAcquisitionService.list_products()
assert [item["key"] for item in products] == ["dtm_1m", "dsm_1m"]
assert {item["coverage_id"] for item in products} == {"DHMVII_DTM_1m", "DHMVII_DSM_1m"}
assert all(item["native_resolution_m"] == 1.0 for item in products)
assert all(item["source_crs"] == "EPSG:31370" for item in products)
assert all("TAW" in item["vertical_reference"] for item in products)
assert all(item["acquisition_period"] == "2013-2015" for item in products)
assert "waterdiepte" in products[0]["limitation_message"]
def test_dhmv_request_uses_bounded_official_wcs_scaling() -> None:
prepared = DhmvAcquisitionService._prepared_request(lambert_bbox_payload(), Settings(_env_file=None))
assert prepared["coverage_id"] == "DHMVII_DTM_1m"
assert prepared["params"]["SCALEFACTOR"] == "5"
assert prepared["params"]["SUBSET"][0].startswith("x(")
assert prepared["params"]["SUBSET"][1].startswith("y(")
assert "geo.api.vlaanderen.be%2FDHMV" not in prepared["request_url"]
assert prepared["request_url"].startswith("https://geo.api.vlaanderen.be/DHMV/wcs?")
assert prepared["width"] * prepared["height"] <= 12_000_000
assert len(prepared["request_hash"]) == 64
with pytest.raises(AppError) as exc_info:
DhmvAcquisitionService._prepared_request(
lambert_bbox_payload(product_key="arbitrary"),
Settings(_env_file=None),
)
assert exc_info.value.code == "DHMV_PRODUCT_NOT_SUPPORTED"
def test_dhmv_request_rejects_unsafe_size_and_resolution() -> None:
with pytest.raises(AppError) as exc_info:
DhmvAcquisitionService._prepared_request(lambert_bbox_payload(side_m=5.0), Settings(_env_file=None))
assert exc_info.value.code == "DHMV_SELECTION_TOO_SMALL"
payload = lambert_bbox_payload()
payload.resolution_m = 0.5
with pytest.raises(Exception):
DhmvAcquireRequest.model_validate(payload.model_dump())
def test_dhmv_large_scope_is_bounded_into_mosaicable_wcs_tiles() -> None:
prepared = DhmvAcquisitionService._prepared_request(
lambert_bbox_payload(side_m=15_000.0),
Settings(_env_file=None),
)
tile_bounds = DhmvAcquisitionService._tile_bounds(prepared)
assert len(tile_bounds) == 4
assert all(bounds[2] - bounds[0] <= 10_000.0 for bounds in tile_bounds)
assert all(bounds[3] - bounds[1] <= 10_000.0 for bounds in tile_bounds)
left = elevation_tiff(left=200_000, top=210_100, width=20, height=20)
right = elevation_tiff(left=200_100, top=210_100, width=20, height=20)
mosaic = DhmvAcquisitionService._mosaic_geotiffs([left, right])
with MemoryFile(mosaic) as memory, memory.open() as dataset:
assert dataset.crs.to_epsg() == 31370
assert dataset.res == pytest.approx((5.0, 5.0))
assert dataset.width == 40
assert dataset.height == 20
assert dataset.nodata == -9999.0
def test_dhmv_mosaic_harmonizes_only_bounded_wcs_edge_grid_rounding() -> None:
regular = elevation_tiff(left=200_000, top=210_100, width=20, height=20)
rounded_edge = edge_elevation_tiff(left=200_100, top=210_100, x_resolution=4.76555, y_resolution=5.0008)
diagnostics: dict[str, object] = {}
mosaic = DhmvAcquisitionService._mosaic_geotiffs(
[regular, rounded_edge],
expected_resolution_m=5.0,
diagnostics=diagnostics,
)
with MemoryFile(mosaic) as memory, memory.open() as dataset:
assert dataset.res == pytest.approx((5.0, 5.0))
assert diagnostics["harmonized_tile_indexes"] == [1]
assert diagnostics["harmonization_method"] == "rasterio_merge_target_resolution"
unsafe_edge = edge_elevation_tiff(left=200_100, top=210_100, x_resolution=4.5)
with pytest.raises(AppError) as exc_info:
DhmvAcquisitionService._mosaic_geotiffs([regular, unsafe_edge], expected_resolution_m=5.0)
assert exc_info.value.code == "DHMV_TILE_MISMATCH"
def test_dhmv_multipart_geotiff_is_extracted_and_invalid_response_fails_closed() -> None:
tiff = elevation_tiff(left=200_000, top=210_100, width=20, height=20)
multipart, content_type = multipart_tiff(tiff)
assert DhmvAcquisitionService._extract_geotiff(multipart, content_type) == tiff
with pytest.raises(AppError) as exc_info:
DhmvAcquisitionService._extract_geotiff(b"<ServiceException/>", "text/xml")
assert exc_info.value.code == "DHMV_PROVIDER_INVALID_RESPONSE"
def test_dhmv_fetch_sends_explicit_accept_header_required_by_official_wcs() -> None:
observed_headers: dict[str, str | None] = {}
def opener(request, **_kwargs):
observed_headers["accept"] = request.get_header("Accept")
observed_headers["user_agent"] = request.get_header("User-agent")
return FakeResponse(b"II*\x00test", "image/tiff")
content, content_type = DhmvAcquisitionService._fetch(
"https://geo.api.vlaanderen.be/DHMV/wcs?bounded=true",
Settings(_env_file=None),
opener,
)
assert content == b"II*\x00test"
assert content_type == "image/tiff"
assert observed_headers == {
"accept": "*/*",
"user_agent": "GeoIntel/0.1 bounded-dhmv-acquisition",
}
def test_dhmv_acquisition_clips_validates_and_persists_via_dataset_service(tmp_path) -> None:
project_id = uuid4()
area_id = uuid4()
payload = lambert_bbox_payload(area_id=area_id)
area_geometry = MultiPolygon([box(payload.bbox.min_x, payload.bbox.min_y, payload.bbox.max_x, payload.bbox.max_y)])
db = FakeSession(
{
(Project, project_id): Project(id=project_id, name="Mol"),
(Area, area_id): Area(
id=area_id,
project_id=project_id,
name="Gemeente Mol",
geometry=from_shape(area_geometry, srid=4326),
),
}
)
settings = Settings(_env_file=None, storage_root=str(tmp_path), dhmv_resolution_m=5.0)
prepared = DhmvAcquisitionService._prepared_request(payload, settings)
tiff = elevation_tiff(
left=prepared["bbox_epsg31370"][0],
top=prepared["bbox_epsg31370"][3],
width=prepared["width"],
height=prepared["height"],
)
multipart, content_type = multipart_tiff(tiff)
result = DhmvAcquisitionService.acquire(
db,
project_id,
payload,
settings=settings,
opener=lambda *_args, **_kwargs: FakeResponse(multipart, content_type),
)
dataset = next(item for item in db.added if isinstance(item, Dataset))
version = next(item for item in db.added if isinstance(item, DatasetVersion))
source = next(item for item in db.added if isinstance(item, SourceRegistry))
snapshot = next(item for item in db.added if isinstance(item, SourceSnapshot))
assert result["output_dataset_id"] == str(dataset.id)
assert dataset.source_name == "digitaal_vlaanderen_dhmv"
assert dataset.area_id == area_id
assert dataset.dataset_type == "raster"
assert dataset.crs == "EPSG:31370"
assert dataset.checksum_sha256 == version.checksum_sha256
assert dataset.source_metadata["surface_model"] == "terrain"
assert dataset.source_metadata["native_resolution_m"] == 1.0
assert dataset.source_metadata["analysis_resolution_m"] == 5.0
assert dataset.source_metadata["nodata_value"] == -9999.0
assert dataset.provenance_metadata["water_depth_available"] is False
assert dataset.provenance_metadata["water_volume_available"] is False
assert len(dataset.provenance_metadata["response_sha256"]) == 64
assert dataset.source_registry_id == source.id
assert dataset.source_snapshot_id == snapshot.id
assert dataset.validation_status == "passed"
assert dataset.provenance_status == "complete"
assert dataset.lineage_status == "not_applicable"
assert dataset.quarantine_status == "not_quarantined"
assert snapshot.source_registry_id == source.id
assert snapshot.checksum_sha256 == dataset.checksum_sha256
assert snapshot.ingest_status == "ingested"
assert snapshot.freshness_status == "current"
with rasterio.open(dataset.storage_path) as stored:
assert stored.crs.to_epsg() == 31370
assert stored.count == 1
assert stored.nodata == -9999.0
assert stored.res == pytest.approx((5.0, 5.0))
def test_terrain_analysis_returns_governed_elevation_relief_and_slope(tmp_path) -> None:
project_id = uuid4()
dataset_id = uuid4()
path = tmp_path / "terrain.tif"
path.write_bytes(elevation_tiff(left=200_000, top=210_100, width=20, height=20))
to_wgs84 = Transformer.from_crs("EPSG:31370", "EPSG:4326", always_xy=True)
min_x, min_y = to_wgs84.transform(200_000, 210_000)
max_x, max_y = to_wgs84.transform(200_100, 210_100)
dataset = Dataset(
id=dataset_id,
project_id=project_id,
name="dhmvii_terrain_5m.tif",
dataset_type="raster",
source="official WCS",
source_name="digitaal_vlaanderen_dhmv",
source_metadata={
"product_key": "dtm_1m",
"surface_model": "terrain",
"vertical_reference": "TAW (Tweede Algemene Waterpassing)",
},
status="ready",
storage_path=str(path),
)
db = FakeSession({(Dataset, dataset_id): dataset})
payload = TerrainSelectionRequest(
bbox={"min_x": min_x, "min_y": min_y, "max_x": max_x, "max_y": max_y, "crs": "EPSG:4326"}
)
result = TerrainAnalysisService.analyze(db, project_id, dataset_id, payload, settings=Settings(_env_file=None))
metrics = {item["metric_key"]: item for item in result["summary"]["metrics"]}
assert result["sample_count"] > 300
assert result["coverage_ratio"] > 0.99
assert result["resolution_m"] == 5.0
assert result["summary"]["metric_unit"] == "m TAW"
assert metrics["relief_m"]["metric_value"] > 20
assert metrics["slope_mean_deg"]["metric_value"] == pytest.approx(12.6044, abs=0.01)
assert result["unsupported_metrics"] == ["water_depth_m", "water_volume_m3"]
assert "Waterdiepte" in result["limitation_message"]
def test_partitioned_terrain_analysis_is_exact_across_municipality_boundaries(tmp_path) -> None:
project_id = uuid4()
transformer = Transformer.from_crs("EPSG:31370", "EPSG:4326", always_xy=True)
min_x, min_y = transformer.transform(200_000, 210_000)
middle_x, _ = transformer.transform(200_100, 210_000)
max_x, max_y = transformer.transform(200_200, 210_100)
paths = [tmp_path / "left-terrain.tif", tmp_path / "right-terrain.tif"]
paths[0].write_bytes(constant_elevation_tiff(left=200_000, top=210_100, value=10.0))
paths[1].write_bytes(constant_elevation_tiff(left=200_100, top=210_100, value=20.0))
datasets = [
Dataset(
id=uuid4(),
project_id=project_id,
area_id=uuid4(),
name=path.name,
dataset_type="raster",
source="official WCS",
source_name="digitaal_vlaanderen_dhmv",
source_metadata={
"product_key": "dtm_1m",
"surface_model": "terrain",
"bbox_epsg4326": [left, min_y, right, max_y],
},
status="ready",
storage_path=str(path),
)
for path, left, right in (
(paths[0], min_x, middle_x),
(paths[1], middle_x, max_x),
)
]
db = FakeSession(query_result=datasets)
payload = TerrainPartitionSelectionRequest(
bbox={"min_x": min_x, "min_y": min_y, "max_x": max_x, "max_y": max_y, "crs": "EPSG:4326"},
product_key="dtm_1m",
)
result = TerrainAnalysisService.analyze_partitions(
db,
project_id,
payload,
settings=Settings(_env_file=None),
)
metrics = {item["metric_key"]: item["metric_value"] for item in result["summary"]["metrics"]}
assert result["partition_count"] == 2
assert set(result["dataset_ids"]) == {str(dataset.id) for dataset in datasets}
assert result["sample_count"] >= 790
assert metrics["terrain_elevation_mean_m"] == pytest.approx(15.0, abs=0.1)
assert metrics["terrain_elevation_min_m"] == 10.0
assert metrics["terrain_elevation_max_m"] == 20.0
assert metrics["terrain_elevation_p90_m"] == 20.0
assert "2 persistente gemeentelijke rasterpartities" in result["limitation_message"]
def test_terrain_analysis_rejects_non_dhmv_raster(tmp_path) -> None:
project_id = uuid4()
dataset_id = uuid4()
path = tmp_path / "other.tif"
path.write_bytes(elevation_tiff(left=200_000, top=210_100, width=20, height=20))
dataset = Dataset(
id=dataset_id,
project_id=project_id,
name="other.tif",
dataset_type="raster",
source="manual",
source_name="manual",
status="ready",
storage_path=str(path),
)
db = FakeSession({(Dataset, dataset_id): dataset})
with pytest.raises(AppError) as exc_info:
TerrainAnalysisService.analyze(db, project_id, dataset_id, TerrainSelectionRequest(bbox=lambert_bbox_payload().bbox))
assert exc_info.value.code == "INVALID_TERRAIN_DATASET"
def test_terrain_renderer_returns_browser_png(tmp_path) -> None:
project_id = uuid4()
dataset_id = uuid4()
path = tmp_path / "terrain.tif"
path.write_bytes(elevation_tiff(left=200_000, top=210_100, width=20, height=20))
dataset = Dataset(
id=dataset_id,
project_id=project_id,
name="terrain.tif",
dataset_type="raster",
source="official",
source_name="digitaal_vlaanderen_dhmv",
source_metadata={"product_key": "dtm_1m", "surface_model": "terrain"},
status="ready",
storage_path=str(path),
)
db = FakeSession({(Dataset, dataset_id): dataset})
assert TerrainAnalysisService.render_png(db, project_id, dataset_id).startswith(b"\x89PNG\r\n\x1a\n")
def test_dhmv_endpoints_use_canonical_envelopes(monkeypatch) -> None:
project_id = uuid4()
output_dataset_id = uuid4()
db = FakeSession({(Project, project_id): Project(id=project_id, name="Mol")})
monkeypatch.setattr(
DhmvAcquisitionService,
"acquire",
lambda *_args, **_kwargs: {
"output_dataset_id": str(output_dataset_id),
"provider": "digitaal_vlaanderen_dhmv",
"reused": False,
},
)
monkeypatch.setattr(
TerrainAnalysisService,
"analyze",
lambda *_args, **_kwargs: {
"dataset_id": str(output_dataset_id),
"product_key": "dtm_1m",
"surface_model": "terrain",
"selection_bbox": lambert_bbox_payload().bbox.model_dump(),
"sample_count": 100,
"slope_sample_count": 81,
"coverage_ratio": 1.0,
"resolution_m": 5.0,
"vertical_reference": "TAW",
"summary": {
"metric_label": "Gemiddelde terreinhoogte",
"metric_value": 25.0,
"metric_unit": "m TAW",
"aggregation_method": "mean",
"primary_metric_key": "terrain_elevation_mean_m",
"metrics": [],
},
"unsupported_metrics": ["water_depth_m", "water_volume_m3"],
"limitation_message": "Terrain height is not water depth.",
"generated_at": "2026-07-18T00:00:00Z",
},
)
monkeypatch.setattr(
TerrainAnalysisService,
"analyze_partitions",
lambda *_args, **_kwargs: {
"dataset_id": str(output_dataset_id),
"dataset_ids": [str(output_dataset_id)],
"partition_count": 1,
"product_key": "dtm_1m",
"surface_model": "terrain",
"selection_bbox": lambert_bbox_payload().bbox.model_dump(),
"sample_count": 100,
"slope_sample_count": 81,
"coverage_ratio": 1.0,
"resolution_m": 5.0,
"vertical_reference": "TAW",
"summary": {
"metric_label": "Gemiddelde terreinhoogte",
"metric_value": 25.0,
"metric_unit": "m TAW",
"aggregation_method": "mean",
"primary_metric_key": "terrain_elevation_mean_m",
"metrics": [],
},
"unsupported_metrics": ["water_depth_m", "water_volume_m3"],
"limitation_message": "Terrain height is not water depth.",
"generated_at": "2026-07-18T00:00:00Z",
},
)
app.dependency_overrides[get_db] = lambda: db
try:
products = TestClient(app).get(f"/api/v1/projects/{project_id}/datasets/dhmv/products")
acquisition = TestClient(app).post(
f"/api/v1/projects/{project_id}/datasets/dhmv/acquire",
json=lambert_bbox_payload().model_dump(mode="json"),
)
terrain = TestClient(app).post(
f"/api/v1/projects/{project_id}/datasets/{output_dataset_id}/raster/terrain/select",
json={"bbox": lambert_bbox_payload().bbox.model_dump()},
)
regional_terrain = TestClient(app).post(
f"/api/v1/projects/{project_id}/datasets/raster/terrain/select",
json={"bbox": lambert_bbox_payload().bbox.model_dump(), "product_key": "dtm_1m"},
)
finally:
app.dependency_overrides.clear()
assert products.status_code == 200
assert set(products.json()) == {"data"}
assert products.json()["data"]["total"] == 2
assert acquisition.status_code == 200
assert set(acquisition.json()) == {"data"}
assert acquisition.json()["data"]["job_type"] == "raster.dhmv.acquire"
assert acquisition.json()["data"]["output_dataset_id"] == str(output_dataset_id)
assert terrain.status_code == 200
assert set(terrain.json()) == {"data"}
assert terrain.json()["data"]["sample_count"] == 100
assert terrain.json()["data"]["unsupported_metrics"] == ["water_depth_m", "water_volume_m3"]
assert regional_terrain.status_code == 200
assert set(regional_terrain.json()) == {"data"}
assert regional_terrain.json()["data"]["partition_count"] == 1
assert any(isinstance(item, Job) for item in db.added)
def test_frontend_and_runtime_expose_dhmv_workflow() -> None:
capabilities_source = (
ROOT / "frontend" / "src" / "lib" / "datasetCapabilities.ts"
).read_text(encoding="utf-8")
map_source = read_map_workspace()
hook_source = (ROOT / "frontend" / "src" / "hooks" / "useMapSelectionExtract.ts").read_text(encoding="utf-8")
service_source = (ROOT / "frontend" / "src" / "services" / "api" / "datasets.ts").read_text(encoding="utf-8")
assert "digitaal_vlaanderen_dhmv" in capabilities_source
assert "isMapRasterDataset" in capabilities_source
assert "Hoogte & reliëf" in map_source
assert "terrainImageUrl" in map_source
assert "analysisMode === 'current' && activeTheme.id === 'buildings' && mapSelectionBbox" in map_source
assert "selectTerrain" in hook_source
assert "/raster/terrain/select" in service_source
for path in (
ROOT / ".env.example",
ROOT / "docker-compose.yml",
ROOT / "docker-compose.unraid.yml",
ROOT / "deploy" / "unraid" / "run-dockerman-container.sh",
ROOT / "deploy" / "unraid" / "geointel-unraid-template.xml",
):
content = path.read_text(encoding="utf-8")
assert "DHMV_ENABLED" in content
assert "DHMV_RESOLUTION_M" in content
assert "DHMV_MAX_PIXELS" in content
def test_dhmv_operator_is_packaged_and_release_checked() -> None:
operator = (ROOT / "scripts" / "provision_mol_dhmv.py").read_text(encoding="utf-8")
readiness = (ROOT / "scripts" / "run_readiness_check.sh").read_text(encoding="utf-8")
backend_dockerfile = (ROOT / "backend" / "Dockerfile").read_text(encoding="utf-8")
all_in_one_dockerfile = (ROOT / "deploy" / "unraid" / "Dockerfile.all-in-one").read_text(encoding="utf-8")
assert "/datasets/dhmv/acquire" in operator
assert "/raster/terrain/select" in operator
assert "water_depth_m" in operator
assert "py_compile scripts/provision_mol_dhmv.py" in readiness
assert "COPY . /app" in backend_dockerfile
assert "COPY scripts/provision_mol_dhmv.py /app/scripts/provision_mol_dhmv.py" in all_in_one_dockerfile