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
128 lines
4.4 KiB
Python
128 lines
4.4 KiB
Python
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from tests.frontend_contract import read_map_workspace
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
|
|
|
|
def _load_validator():
|
|
script = ROOT / "scripts" / "validate_detection_false_negative_review_decisions.py"
|
|
spec = importlib.util.spec_from_file_location("false_negative_review_validator", script)
|
|
assert spec and spec.loader
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
def _summary() -> dict:
|
|
return {
|
|
"portfolio_path": "/evidence/portfolio.json",
|
|
"selected_features": [
|
|
{
|
|
"reference_feature_id": "reference-1",
|
|
"sample_slug": "mol",
|
|
"area_m2": 42.0,
|
|
"area_bucket": "small_25_100_m2",
|
|
"source_tile_path": "/storage/tiles/mol.tif",
|
|
"geometry": {
|
|
"type": "Polygon",
|
|
"coordinates": [[[5.0, 51.0], [5.001, 51.0], [5.001, 51.001], [5.0, 51.0]]],
|
|
},
|
|
"properties": {"source_feature_id": "grb-1"},
|
|
},
|
|
{
|
|
"reference_feature_id": "reference-2",
|
|
"sample_slug": "mol",
|
|
"area_m2": 18.0,
|
|
"area_bucket": "tiny_lt_25_m2",
|
|
"source_tile_path": "/storage/tiles/mol.tif",
|
|
"geometry": {
|
|
"type": "Polygon",
|
|
"coordinates": [[[5.01, 51.0], [5.011, 51.0], [5.011, 51.001], [5.01, 51.0]]],
|
|
},
|
|
},
|
|
],
|
|
}
|
|
|
|
|
|
def test_false_negative_validator_exports_only_explicit_confirmed_misses() -> None:
|
|
validator = _load_validator()
|
|
report, confirmed = validator.validate(
|
|
_summary(),
|
|
[
|
|
{
|
|
"reference_feature_id": "reference-1",
|
|
"review_decision": "confirmed_model_false_negative",
|
|
"review_notes": "Visible roof with no suitable candidate.",
|
|
},
|
|
{
|
|
"reference_feature_id": "reference-2",
|
|
"review_decision": "qa_alignment_mismatch",
|
|
"review_notes": "Candidate overlaps the footprint.",
|
|
},
|
|
],
|
|
)
|
|
|
|
assert report["status"] == "complete"
|
|
assert report["confirmed_model_false_negative_count"] == 1
|
|
assert report["decision_counts"]["qa_alignment_mismatch"] == 1
|
|
assert len(confirmed["features"]) == 1
|
|
assert confirmed["features"][0]["properties"]["reference_feature_id"] == "reference-1"
|
|
|
|
|
|
def test_false_negative_validator_fails_closed_for_incomplete_or_invalid_reviews() -> None:
|
|
validator = _load_validator()
|
|
report, confirmed = validator.validate(
|
|
_summary(),
|
|
[
|
|
{
|
|
"reference_feature_id": "reference-1",
|
|
"review_decision": "unreviewed",
|
|
"review_notes": "",
|
|
},
|
|
{
|
|
"reference_feature_id": "reference-2",
|
|
"review_decision": "imagery_obscured_or_uncertain",
|
|
"review_notes": "Tile edge prevents a decision.",
|
|
},
|
|
],
|
|
)
|
|
assert report["status"] == "review_required"
|
|
assert confirmed["features"] == []
|
|
|
|
with pytest.raises(SystemExit, match="Invalid review decision"):
|
|
validator.validate(
|
|
_summary(),
|
|
[
|
|
{
|
|
"reference_feature_id": "reference-1",
|
|
"review_decision": "confirmed_model_false_positive",
|
|
"review_notes": "wrong role",
|
|
},
|
|
{
|
|
"reference_feature_id": "reference-2",
|
|
"review_decision": "unreviewed",
|
|
"review_notes": "",
|
|
},
|
|
],
|
|
)
|
|
|
|
|
|
def test_readiness_compiles_false_negative_review_validator() -> None:
|
|
readiness = (ROOT / "scripts" / "run_readiness_check.sh").read_text(encoding="utf-8")
|
|
assert "validate_detection_false_negative_review_decisions.py" in readiness
|
|
|
|
|
|
def test_map_explains_strict_and_diagnostic_detection_matching() -> None:
|
|
workspace = read_map_workspace()
|
|
# Both matching methods must be named; the exact label may change.
|
|
assert "strikte" in workspace.casefold()
|
|
assert "Rechthoekcontrole" in workspace
|
|
assert "possible_box_to_footprint_mismatch_count" in workspace
|
|
assert "De kerncijfers hierboven gebruiken strikte GRB-footprints" in workspace
|