from __future__ import annotations import importlib.util from pathlib import Path import pytest 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 = ( ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx" ).read_text(encoding="utf-8") assert "Strikte matches" in workspace assert "Rechthoekcontrole" in workspace assert "possible_box_to_footprint_mismatch_count" in workspace assert "De kerncijfers hierboven gebruiken strikte GRB-footprints" in workspace