Initial public release
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
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
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
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"""<?xml version="1.0" encoding="UTF-8"?>
|
||||
<WCS_Capabilities version="1.0.0" xmlns="http://www.opengis.net/wcs">
|
||||
<ContentMetadata>
|
||||
<CoverageOfferingBrief>
|
||||
<name>depth_model_20m_lat</name>
|
||||
<label>Belgian Continental Shelf depth model</label>
|
||||
</CoverageOfferingBrief>
|
||||
</ContentMetadata>
|
||||
</WCS_Capabilities>
|
||||
"""
|
||||
|
||||
|
||||
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"<ServiceExceptionReport>boom</ServiceExceptionReport>", "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
|
||||
Reference in New Issue
Block a user