from __future__ import annotations import io from pathlib import Path from uuid import uuid4 import pytest from app.core.config import Settings from app.schemas.bathymetry import MdkBathymetryAcquireRequest from app.schemas.operations import VectorSelectionBBox from app.services.mdk_bathymetry_acquisition_service import MdkBathymetryAcquisitionService CAPABILITIES_XML = b""" depth_model_20m_lat """ class FakeResponse: def __init__(self, content: bytes, content_type: str = "application/xml") -> None: self._stream = io.BytesIO(content) self.headers = {"Content-Type": content_type} def read(self, limit: int = -1) -> bytes: return self._stream.read(limit) def __enter__(self): return self def __exit__(self, *args): return False def _payload(**overrides) -> MdkBathymetryAcquireRequest: values = { "bbox": VectorSelectionBBox(min_x=2.5, min_y=51.3, max_x=2.6, max_y=51.4), "force_refresh": True, } values.update(overrides) return MdkBathymetryAcquireRequest(**values) def _settings(**overrides) -> Settings: values = { "mdk_bathymetry_acquisition_enabled": True, "mdk_bathymetry_coverage_id": "depth_model_20m_lat", } values.update(overrides) return Settings(**values) def test_acquisition_fails_closed_when_disabled() -> None: settings = _settings(mdk_bathymetry_acquisition_enabled=False) with pytest.raises(Exception) as exc_info: MdkBathymetryAcquisitionService.acquire(None, uuid4(), _payload(), settings=settings) assert getattr(exc_info.value, "code", None) == "MDK_BATHYMETRY_ACQUISITION_DISABLED" def test_acquisition_fails_closed_without_coverage_id() -> None: settings = _settings(mdk_bathymetry_coverage_id=None) with pytest.raises(Exception) as exc_info: MdkBathymetryAcquisitionService.acquire(None, uuid4(), _payload(), settings=settings) assert getattr(exc_info.value, "code", None) == "MDK_BATHYMETRY_COVERAGE_NOT_CONFIGURED" def test_acquisition_rejects_oversized_bbox() -> None: settings = _settings(mdk_bathymetry_max_bbox_deg2=0.001) with pytest.raises(Exception) as exc_info: MdkBathymetryAcquisitionService.acquire(None, uuid4(), _payload(), settings=settings) assert getattr(exc_info.value, "code", None) == "MDK_BATHYMETRY_BBOX_TOO_LARGE" def test_acquisition_requires_reachable_probe() -> None: settings = _settings() def failing_opener(request, timeout=None): raise OSError("connection refused") with pytest.raises(Exception) as exc_info: MdkBathymetryAcquisitionService.acquire(None, uuid4(), _payload(), settings=settings, opener=failing_opener) assert getattr(exc_info.value, "code", None) == "MDK_BATHYMETRY_ENDPOINT_NOT_READY" def test_acquisition_requires_advertised_coverage_id() -> None: settings = _settings(mdk_bathymetry_coverage_id="not_advertised_coverage") def opener(request, timeout=None): return FakeResponse(CAPABILITIES_XML) with pytest.raises(Exception) as exc_info: MdkBathymetryAcquisitionService.acquire(None, uuid4(), _payload(), settings=settings, opener=opener) assert getattr(exc_info.value, "code", None) == "MDK_BATHYMETRY_COVERAGE_NOT_ADVERTISED" def test_acquisition_rejects_non_geotiff_coverage_response() -> None: settings = _settings() responses = [] def opener(request, timeout=None): url = request.full_url if hasattr(request, "full_url") else str(request) responses.append(url) if "GetCapabilities" in url: return FakeResponse(CAPABILITIES_XML) return FakeResponse(b"boom", "application/xml") with pytest.raises(Exception) as exc_info: MdkBathymetryAcquisitionService.acquire(None, uuid4(), _payload(), settings=settings, opener=opener) assert getattr(exc_info.value, "code", None) == "MDK_BATHYMETRY_INVALID_RESPONSE" assert any("GetCoverage" in url for url in responses) coverage_urls = [url for url in responses if "GetCoverage" in url] assert "coverage=depth_model_20m_lat" in coverage_urls[0] assert "format=GeoTIFF" in coverage_urls[0] def test_get_coverage_url_is_bounded_and_pinned() -> None: settings = _settings() bbox = [2.5, 51.3, 2.6, 51.4] url = MdkBathymetryAcquisitionService._get_coverage_url(settings, "depth_model_20m_lat", bbox) assert url.startswith("https://") assert "request=GetCoverage" in url assert "version=1.0.0" in url assert "crs=EPSG%3A4326" in url or "crs=EPSG:4326" in url width, height = MdkBathymetryAcquisitionService._pixel_dimensions(bbox) assert 1 <= width <= MdkBathymetryAcquisitionService.MAX_PIXELS_PER_SIDE assert 1 <= height <= MdkBathymetryAcquisitionService.MAX_PIXELS_PER_SIDE def test_source_module_never_disables_tls_verification() -> None: source = ( Path(__file__).resolve().parents[1] / "app" / "services" / "mdk_bathymetry_acquisition_service.py" ).read_text(encoding="utf-8") assert "_create_unverified_context" not in source assert "CERT_NONE" not in source assert "check_hostname = False" not in source