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,124 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from pyproj import Transformer
|
||||
from shapely.geometry import box
|
||||
|
||||
from app.core.errors import AppError
|
||||
from app.services.detection_qa_service import DetectionQaService
|
||||
from app.services.qa_service import QaService
|
||||
|
||||
|
||||
def test_tile_coverage_transforms_projected_manifest_bounds_to_epsg4326() -> None:
|
||||
dataset_id = uuid4()
|
||||
to_lambert = Transformer.from_crs("EPSG:4326", "EPSG:31370", always_xy=True)
|
||||
left, bottom = to_lambert.transform(5.11, 51.18)
|
||||
right, top = to_lambert.transform(5.13, 51.20)
|
||||
manifest = {
|
||||
"source_dataset_id": str(dataset_id),
|
||||
"crs": "EPSG:31370",
|
||||
"tiles": [{"bounds": [left, bottom, right, top], "crs": "EPSG:31370"}],
|
||||
}
|
||||
|
||||
coverage = DetectionQaService.build_tile_coverage(
|
||||
manifest,
|
||||
manifest_path="/app/storage/tiles/manifest.json",
|
||||
expected_dataset_id=dataset_id,
|
||||
)
|
||||
|
||||
min_x, min_y, max_x, max_y = coverage.geometry.bounds
|
||||
assert min_x == pytest.approx(5.11, abs=0.001)
|
||||
assert min_y == pytest.approx(51.18, abs=0.001)
|
||||
assert max_x == pytest.approx(5.13, abs=0.001)
|
||||
assert max_y == pytest.approx(51.20, abs=0.001)
|
||||
assert coverage.tile_count == 1
|
||||
|
||||
|
||||
def test_tile_coverage_rejects_manifest_for_different_dataset() -> None:
|
||||
manifest = {
|
||||
"source_dataset_id": str(uuid4()),
|
||||
"crs": "EPSG:4326",
|
||||
"tiles": [{"bounds": [5.0, 51.0, 5.1, 51.1]}],
|
||||
}
|
||||
|
||||
with pytest.raises(AppError) as exc_info:
|
||||
DetectionQaService.build_tile_coverage(
|
||||
manifest,
|
||||
manifest_path="/app/storage/tiles/manifest.json",
|
||||
expected_dataset_id=uuid4(),
|
||||
)
|
||||
|
||||
assert exc_info.value.code == "DETECTION_QA_COVERAGE_MISMATCH"
|
||||
|
||||
|
||||
def test_coverage_filter_reports_outside_and_boundary_clipped_population() -> None:
|
||||
dataset_id = uuid4()
|
||||
coverage = DetectionQaService.build_tile_coverage(
|
||||
{
|
||||
"source_dataset_id": str(dataset_id),
|
||||
"crs": "EPSG:4326",
|
||||
"tiles": [{"bounds": [0.0, 0.0, 1.0, 1.0]}],
|
||||
},
|
||||
manifest_path="/app/storage/tiles/manifest.json",
|
||||
expected_dataset_id=dataset_id,
|
||||
)
|
||||
|
||||
population = DetectionQaService.filter_population(
|
||||
[
|
||||
({"id": "inside"}, box(0.1, 0.1, 0.2, 0.2)),
|
||||
({"id": "crossing"}, box(0.8, 0.8, 1.2, 1.2)),
|
||||
({"id": "outside"}, box(2.0, 2.0, 3.0, 3.0)),
|
||||
],
|
||||
coverage,
|
||||
)
|
||||
|
||||
assert population.raw_count == 3
|
||||
assert population.evaluated_count == 2
|
||||
assert population.excluded_outside_count == 1
|
||||
assert population.clipped_boundary_count == 1
|
||||
assert population.geometries[1][1].bounds == pytest.approx((0.8, 0.8, 1.0, 1.0))
|
||||
|
||||
|
||||
def test_coverage_filter_preserves_prefiltered_database_population_count() -> None:
|
||||
dataset_id = uuid4()
|
||||
coverage = DetectionQaService.build_tile_coverage(
|
||||
{
|
||||
"source_dataset_id": str(dataset_id),
|
||||
"crs": "EPSG:4326",
|
||||
"tiles": [{"bounds": [0.0, 0.0, 1.0, 1.0]}],
|
||||
},
|
||||
manifest_path="/app/storage/tiles/manifest.json",
|
||||
expected_dataset_id=dataset_id,
|
||||
)
|
||||
|
||||
population = DetectionQaService.filter_population(
|
||||
[
|
||||
({"id": "inside"}, box(0.1, 0.1, 0.2, 0.2)),
|
||||
({"id": "crossing"}, box(0.8, 0.8, 1.2, 1.2)),
|
||||
],
|
||||
coverage,
|
||||
raw_count=3,
|
||||
)
|
||||
|
||||
assert population.raw_count == 3
|
||||
assert population.evaluated_count == 2
|
||||
assert population.excluded_outside_count == 1
|
||||
assert population.clipped_boundary_count == 1
|
||||
|
||||
|
||||
def test_iou_matching_keeps_exact_results_with_many_spatially_disjoint_references() -> None:
|
||||
references = [({"id": f"outside-{index}"}, box(index + 10, 10, index + 10.5, 10.5)) for index in range(100)]
|
||||
references.append(({"id": "match"}, box(0.0, 0.0, 1.0, 1.0)))
|
||||
|
||||
evidence = QaService._match_io_u_evidence(
|
||||
[({"id": "candidate"}, box(0.0, 0.0, 1.0, 1.0))],
|
||||
references,
|
||||
0.5,
|
||||
)
|
||||
|
||||
assert evidence.matches == 1
|
||||
assert evidence.false_positives == 0
|
||||
assert evidence.false_negatives == 100
|
||||
assert evidence.match_evidence[0]["reference_feature_id"] == "match"
|
||||
Reference in New Issue
Block a user