Files
geointel/backend/tests/test_sprint155_detection_operator_profiles.py
T
JensandClaude Opus 5 6572e4ad5f scope frontend contracts to the feature, not to one file
93 test files read a single frontend source and asserted identifiers in it. The
MapWorkspace split showed what that costs: 24 tests went red for a move that
changed no behaviour at all. A contract belongs to the feature — a container,
its hooks, its domain layer — not to whichever file currently holds it.

232 read sites now resolve through read_feature(). The distinction that makes
this safe is direction: a *positive* contract ("this is wired") may widen,
because the identifier must still exist somewhere in the feature; a *negative*
one ("this component performs no transport") is a statement about one file, and
widening it would quietly weaken the check. The 73 single-file reads that
remain are exactly those, and a guard now enforces the rule for new tests.

Verified rather than assumed: of the 732 migrated positive assertions, 644 still
match exactly one module — as specific as before — and the other 86 already
spanned a container and its hook by nature. Two apparent misses are an artefact
of the checking regex reading an escaped newline literally.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 22:05:43 +02:00

64 lines
2.9 KiB
Python

from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2]
def test_detection_operator_profiles_define_explicit_historical_yolo_controls_without_promotion_claim() -> None:
profiles = ROOT / "frontend" / "src" / "components" / "detection" / "detectionProfiles.ts"
source = profiles.read_text(encoding="utf-8")
assert "DETECTION_OPERATOR_PROFILES" in source
assert "geointel-building-yolov8s-smallbld-minpx3-img640-ft30-pt" in source
assert "geointel-building-yolov8s-aoi1024expandedminpx4vis035e50-pt" in source
assert "geointel-building-yolov8s-aoi1024bg512r3e50-pt" in source
assert "small-building-balanced-review" in source
assert "expanded-balanced-review" in source
assert "conservative-review" in source
assert "confidenceThreshold: 0.15" in source
assert "confidenceThreshold: 0.35" in source
assert "defaultApproved" not in source
assert "promotionRecommendation" not in source
assert "independentTestProven: false" in source
assert "positiveSampleCount: 7" in source
assert "precision: 0.6140895327792112" in source
assert "recall: 0.6062221049337548" in source
assert "f1: 0.6068607646002744" in source
assert "f1: 0.5432865390636915" in source
assert "maxBackgroundDetections: 0" in source
assert "Slechts drie pure-achtergrondbeelden" in source
assert "ruimtelijke onafhankelijkheid niet bewezen" in source
assert "controlekandidaat en niet als grondwaarheid" in source
def test_detection_lab_surfaces_profiles_as_deliberate_operator_actions() -> None:
lab = "\n".join(
(
(ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx").read_text(encoding="utf-8"),
(ROOT / "frontend" / "src" / "components" / "detection" / "DetectionModelManagement.tsx").read_text(encoding="utf-8"),
)
)
assert "DETECTION_OPERATOR_PROFILES" in lab
assert "Historische YOLO-controleprofielen" in lab
assert "profile.displayName" in lab
assert "profile.confidenceThreshold" in lab
assert "historisch, geen releasebewijs" in lab
assert "historische F1" in lab
assert "Profiel gebruiken" in lab
assert "onApplyOperatorProfile(profile)" in lab
assert "Recommended starting threshold: 0.25" not in lab
def test_detection_workflow_applies_profiles_without_selecting_the_first_arbitrary_asset() -> None:
hook = (ROOT / "frontend" / "src" / "hooks" / "useDetectionWorkflow.ts").read_text(encoding="utf-8")
app = read_feature("shell")
assert "applyDetectionOperatorProfile" in hook
assert "setSelectedDetectionModelId('yolo-configured')" in hook
assert "setSelectedModelAssetId(profile.modelAssetId)" in hook
assert "setDetectionConfidenceThreshold(profile.confidenceThreshold)" in hook
assert "setSelectedModelAssetId(assetResponse.items[0]" not in hook
assert "onApplyOperatorProfile={applyDetectionOperatorProfile}" in app