import httpx import numpy as np import pytest import rasterio from rasterio.transform import from_origin from pipeline.download import noise def test_download_tile_splits_after_retries(monkeypatch, tmp_path): monkeypatch.setattr(noise, "MAX_RETRIES", 1) monkeypatch.setattr(noise, "MIN_TILE_SIZE", 50) def fake_fetch_tile_bytes( wcs_base, coverage_id, min_e, min_n, max_e, max_n, wcs_version="1.0.0", ): if max_e - min_e > 50 or max_n - min_n > 50: raise httpx.TimeoutException("too large") return b"II*\x00fake-tiff" monkeypatch.setattr(noise, "_fetch_tile_bytes", fake_fetch_tile_bytes) paths, failures = noise._download_tile("base", "coverage", 0, 0, 100, 100, tmp_path) assert failures == [] assert len(paths) == 4 assert sorted(path.name for path in paths) == [ "tile_0_0_50_50.tif", "tile_0_50_50_100.tif", "tile_50_0_100_50.tif", "tile_50_50_100_100.tif", ] def test_download_tile_reports_unsplittable_failure(monkeypatch, tmp_path): monkeypatch.setattr(noise, "MAX_RETRIES", 1) monkeypatch.setattr(noise, "MIN_TILE_SIZE", 100) def fake_fetch_tile_bytes(*args, **kwargs): raise httpx.ConnectError("offline") monkeypatch.setattr(noise, "_fetch_tile_bytes", fake_fetch_tile_bytes) paths, failures = noise._download_tile("base", "coverage", 0, 0, 100, 100, tmp_path) assert paths == [] assert failures == [(0, 0, 100, 100)] def test_download_tile_treats_non_tiff_response_as_failure(monkeypatch, tmp_path): monkeypatch.setattr(noise, "MAX_RETRIES", 1) monkeypatch.setattr(noise, "MIN_TILE_SIZE", 100) def fake_fetch_tile_bytes(*args, **kwargs): raise noise.NoGeoTiffError("xml exception") monkeypatch.setattr(noise, "_fetch_tile_bytes", fake_fetch_tile_bytes) paths, failures = noise._download_tile("base", "coverage", 0, 0, 100, 100, tmp_path) assert paths == [] assert failures == [(0, 0, 100, 100)] def test_download_raster_tolerates_missing_tiles_when_allowed(monkeypatch, tmp_path): monkeypatch.setattr(noise, "BNG_MIN_E", 0) monkeypatch.setattr(noise, "BNG_MAX_E", 100) monkeypatch.setattr(noise, "BNG_MIN_N", 0) monkeypatch.setattr(noise, "BNG_MAX_N", 100) monkeypatch.setattr(noise, "TILE_SIZE", 100) def fake_download_tile(*args, **kwargs): return [], [(0, 0, 100, 100)] monkeypatch.setattr(noise, "_download_tile", fake_download_tile) paths = noise.download_raster( tmp_path, "base", "coverage", "Airport", allow_missing_tiles=True, ) assert paths == [] def test_download_raster_raises_on_missing_strict_tiles(monkeypatch, tmp_path): monkeypatch.setattr(noise, "BNG_MIN_E", 0) monkeypatch.setattr(noise, "BNG_MAX_E", 100) monkeypatch.setattr(noise, "BNG_MIN_N", 0) monkeypatch.setattr(noise, "BNG_MAX_N", 100) monkeypatch.setattr(noise, "TILE_SIZE", 100) def fake_download_tile(*args, **kwargs): return [], [(0, 0, 100, 100)] monkeypatch.setattr(noise, "_download_tile", fake_download_tile) with pytest.raises(RuntimeError, match=r"\[Road\] Failed to download"): noise.download_raster(tmp_path, "base", "coverage", "Road") def test_sample_noise_at_postcodes_uses_local_maximum(monkeypatch, tmp_path): monkeypatch.setattr(noise, "POSTCODE_NOISE_RADIUS_M", 15) monkeypatch.setattr(noise, "RESOLUTION", 10) tile_path = tmp_path / "noise.tif" data = np.array( [ [0, 0, 0, 0, 0], [0, 70, 0, 0, 0], [0, 0, 55, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], ], dtype=np.float32, ) with rasterio.open( tile_path, "w", driver="GTiff", height=data.shape[0], width=data.shape[1], count=1, dtype=data.dtype, crs="EPSG:27700", transform=from_origin(0, 50, 10, 10), nodata=0, ) as dataset: dataset.write(data, 1) result = noise.sample_noise_at_postcodes( [tile_path], easting=np.array([25.0]), northing=np.array([25.0]), label="Road", col_name="road_noise_lden_db", ) assert result.to_list() == [70.0]