from __future__ import annotations import json import subprocess from pathlib import Path ROOT = Path(__file__).resolve().parents[2] SCRIPT = ROOT / "scripts" / "activate_promoted_yolo_candidate.py" CANDIDATE_KEY = "geointel-building-yolov8s-aoi1024bg512r3e50-pt|512|64|0.35" def _write_model(models_dir: Path) -> Path: model_file = models_dir / "geointel-building-yolov8s-aoi1024bg512r3e50.pt" model_file.parent.mkdir(parents=True) model_file.write_bytes(b"local promoted model") return model_file def _write_report(path: Path, *, promotion_status: str = "promote_candidate") -> None: rejection_reasons = [] if promotion_status == "promote_candidate" else ["background_false_positive_pressure"] path.write_text( json.dumps( { "gates": { "max_background_detections_per_sample": 0, "min_background_samples": 2, "min_mean_f1": 0.25, "min_positive_samples": 7, }, "recommended_candidate": { "background_sample_count": 3, "background_samples": [["arendonk_heide", 0], ["lommel_heide", 0], ["postel_bos", 0]], "candidate_key": CANDIDATE_KEY, "max_background_detections": 0, "mean_f1": 0.32086574003576274, "mean_precision": 0.8400057773951873, "mean_recall": 0.20213514285308795, "model_asset_id": "geointel-building-yolov8s-aoi1024bg512r3e50-pt", "positive_sample_count": 7, "promotion_status": promotion_status, "rejection_reasons": rejection_reasons, "threshold": 0.35, "tile_overlap": 64, "tile_size": 512, "total_background_detections": 0, }, } ), encoding="utf-8", ) def _run_activation(tmp_path: Path, *extra_args: str) -> subprocess.CompletedProcess[str]: models_dir = tmp_path / "models" _write_model(models_dir) report_path = tmp_path / "promotion_report.json" _write_report(report_path) env_file = tmp_path / ".env" env_file.write_text("GEOINTEL_ENV=production\nYOLO_ENABLED=false\n", encoding="utf-8") return subprocess.run( [ "python", str(SCRIPT), "--promotion-report", str(report_path), "--candidate-key", CANDIDATE_KEY, "--models-dir", str(models_dir), "--container-model-dir", "/app/models", "--env-file", str(env_file), "--json", *extra_args, ], cwd=ROOT, capture_output=True, text=True, timeout=30, check=False, ) def test_promoted_yolo_activation_dry_run_validates_report_and_model(tmp_path: Path) -> None: result = _run_activation(tmp_path) assert result.returncode == 0, result.stderr payload = json.loads(result.stdout) assert payload["status"] == "ready_to_apply" assert payload["applied"] is False assert payload["will_download_models"] is False assert payload["candidate"]["candidate_key"] == CANDIDATE_KEY assert payload["candidate"]["threshold"] == 0.35 assert payload["env_updates"]["YOLO_ENABLED"] == "true" assert payload["env_updates"]["YOLO_MODEL_PATH"] == "/app/models/geointel-building-yolov8s-aoi1024bg512r3e50.pt" def test_promoted_yolo_activation_apply_updates_env_file(tmp_path: Path) -> None: result = _run_activation(tmp_path, "--apply") assert result.returncode == 0, result.stderr payload = json.loads(result.stdout) assert payload["status"] == "applied" env_text = (tmp_path / ".env").read_text(encoding="utf-8") assert "GEOINTEL_ENV=production" in env_text assert "GEOINTEL_INSTALL_AI=true" in env_text assert "YOLO_ENABLED=true" in env_text assert "YOLO_MODELS_DIR=/app/models" in env_text assert "YOLO_MODEL_PATH=/app/models/geointel-building-yolov8s-aoi1024bg512r3e50.pt" in env_text def test_promoted_yolo_activation_rejects_non_promoted_report(tmp_path: Path) -> None: models_dir = tmp_path / "models" _write_model(models_dir) report_path = tmp_path / "promotion_report.json" _write_report(report_path, promotion_status="reject") result = subprocess.run( [ "python", str(SCRIPT), "--promotion-report", str(report_path), "--candidate-key", CANDIDATE_KEY, "--models-dir", str(models_dir), "--env-file", str(tmp_path / ".env"), "--json", ], cwd=ROOT, capture_output=True, text=True, timeout=30, check=False, ) assert result.returncode == 3 payload = json.loads(result.stdout) assert payload["status"] == "candidate_not_promoted" assert "rejection_reasons" in payload def test_readiness_gate_compiles_promoted_activation_script() -> None: readiness = (ROOT / "scripts" / "run_readiness_check.sh").read_text(encoding="utf-8") assert "-m py_compile scripts/activate_promoted_yolo_candidate.py" in readiness