Start autonomous Belgium and North Sea RC
GeoIntel CI / docs-smoke (push) Canceled after 0s
GeoIntel CI / contract-smoke (push) Canceled after 0s

This commit is contained in:
Codex
2026-07-18 00:12:34 +02:00
parent 9513f8613e
commit 9c402e0df2
36 changed files with 2235 additions and 115 deletions
+2 -2
View File
@@ -204,9 +204,9 @@ def test_compose_mounts_demo_fixtures_for_backend_runtime() -> None:
def test_compose_has_backend_and_frontend_healthchecks() -> None:
compose = (ROOT / "docker-compose.yml").read_text(encoding="utf-8")
assert "http://127.0.0.1:8000/health" in compose
assert "http://127.0.0.1:8000/health/ready" in compose
assert "urllib.request.urlopen" in compose
assert "http://127.0.0.1/health" in compose
assert "http://127.0.0.1/health/ready" in compose
assert "wget -q -O -" in compose
assert "start_period: 30s" in compose
assert "start_period: 10s" in compose
+77 -11
View File
@@ -1,34 +1,100 @@
from __future__ import annotations
from types import SimpleNamespace
from fastapi.testclient import TestClient
from app.api.routes import health
from app.main import app
def test_health_endpoint_returns_status_payload() -> None:
client = TestClient(app)
response = client.get("/health")
READY_DATABASE = {
"database": "ok",
"postgis": "ok:3.4 USE_GEOS=1 USE_PROJ=1",
"migration": "ok:202607160001",
}
def test_liveness_is_independent_from_database(monkeypatch) -> None:
monkeypatch.setattr(
health,
"_database_checks",
lambda: (_ for _ in ()).throw(AssertionError("must not query DB")),
)
response = TestClient(app).get("/health/live")
assert response.status_code == 200
assert response.json()["status"] == "ok"
def test_readiness_returns_ok_only_when_all_checks_pass(monkeypatch) -> None:
monkeypatch.setattr(health, "_database_checks", lambda: READY_DATABASE.copy())
monkeypatch.setattr(health, "_storage_check", lambda _: "ok")
response = TestClient(app).get("/health/ready")
assert response.status_code == 200
payload = response.json()
assert payload["status"] in {"ok", "degraded"}
assert payload["status"] == "ok"
assert payload["service"] == "geointel-backend"
assert payload["version"] == "0.1.0"
assert payload["database"] == "ok"
assert payload["postgis"].startswith("ok:")
assert payload["migration"] == "ok:202607160001"
assert payload["storage"] == "ok"
def test_system_capabilities_reports_gis_dependency_flags(monkeypatch) -> None:
def test_compatibility_health_is_fail_closed(monkeypatch) -> None:
monkeypatch.setattr(
health,
"_database_checks",
lambda: {
"database": "degraded",
"postgis": "degraded",
"migration": "degraded",
},
)
monkeypatch.setattr(health, "_storage_check", lambda _: "ok")
response = TestClient(app).get("/health")
assert response.status_code == 503
assert response.json()["status"] == "degraded"
def test_system_capabilities_report_runtime_state(monkeypatch) -> None:
monkeypatch.setattr(
health,
"_dependency_enabled",
lambda module_name: module_name in {"rasterio", "geopandas"},
)
monkeypatch.setattr(health, "_database_checks", lambda: READY_DATABASE.copy())
monkeypatch.setattr(
health.ModelRegistryService,
"get_model_capability",
lambda *args, **kwargs: SimpleNamespace(
configured=True,
status="configured",
),
)
client = TestClient(app)
response = client.get("/api/v1/system/capabilities")
response = TestClient(app).get("/api/v1/system/capabilities")
assert response.status_code == 200
payload = response.json()
assert payload["data"]["rasterio"] is True
assert payload["data"]["geopandas"] is True
payload = response.json()["data"]
assert payload["postgis"] is True
assert payload["rasterio"] is True
assert payload["geopandas"] is True
assert payload["yolo"] is True
assert payload["yolo_status"] == "configured"
assert payload["version"]
def test_requests_receive_a_correlation_id() -> None:
client = TestClient(app)
generated = client.get("/health/live")
retained = client.get("/health/live", headers={"x-request-id": "test-request"})
assert generated.headers["x-request-id"]
assert retained.headers["x-request-id"] == "test-request"
@@ -0,0 +1,75 @@
from __future__ import annotations
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
SCRIPTS = ROOT / "scripts"
def read(name: str) -> str:
return (SCRIPTS / name).read_text(encoding="utf-8")
def test_backup_is_atomic_read_only_and_checksum_bound() -> None:
script = read("backup_release_state.sh")
assert "pg_dump" in script
assert "-Fc" in script
assert "--no-owner" in script
assert "CHECKSUMS.sha256" in script
assert "database-password" not in script.lower()
assert "mv \"$PARTIAL\" \"$FINAL\"" in script
assert "rm -rf -- \"$PARTIAL\"" in script
assert "DROP DATABASE" not in script
assert "pg_restore --clean" not in script
def test_backup_verification_is_read_only() -> None:
script = read("verify_release_backup.sh")
assert "sha256sum -c CHECKSUMS.sha256" in script
assert "pg_restore --list" in script
assert "createdb" not in script
assert "dropdb" not in script
assert "pg_restore --clean" not in script
def test_restore_smoke_is_forced_to_generated_isolated_database() -> None:
script = read("restore_release_backup_smoke.sh")
assert "--confirm-isolated-restore" in script
assert "geointel_restore_verify_" in script
assert 'if [ "$TARGET_DB" = "$DB_NAME" ]' in script
assert "createdb" in script
assert "dropdb --if-exists" in script
assert "pg_restore \\\n --clean" not in script
assert '"production_database_untouched": True' in script
def test_release_safety_scripts_have_valid_bash_syntax() -> None:
for name in (
"backup_release_state.sh",
"verify_release_backup.sh",
"restore_release_backup_smoke.sh",
):
result = subprocess.run(
["bash", "-n", f"scripts/{name}"],
cwd=ROOT,
capture_output=True,
text=True,
check=False,
)
assert result.returncode == 0, f"{name}: {result.stderr}"
def test_readiness_gate_checks_release_safety_scripts() -> None:
readiness = read("run_readiness_check.sh")
for name in (
"backup_release_state.sh",
"verify_release_backup.sh",
"restore_release_backup_smoke.sh",
):
assert f"bash -n scripts/{name}" in readiness
+82
View File
@@ -0,0 +1,82 @@
from __future__ import annotations
import importlib.util
import json
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
SCRIPT = ROOT / "scripts" / "capture_release_evidence.py"
def load_script():
spec = importlib.util.spec_from_file_location("capture_release_evidence", SCRIPT)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def test_release_evidence_manifest_is_secret_free_and_read_only(tmp_path: Path) -> None:
module = load_script()
args = module.parse_args(
[
"--output",
str(tmp_path / "evidence.json"),
"--release-id",
"test-rc",
]
)
manifest = module.build_manifest(args)
assert manifest["schema_version"] == 1
assert manifest["release_id"] == "test-rc"
assert manifest["read_only"] is True
assert manifest["scope"] == "Belgium and the Belgian North Sea"
assert "DATABASE_URL" not in json.dumps(manifest).replace(
'"DATABASE_URL": false',
"",
).replace(
'"DATABASE_URL": true',
"",
)
assert manifest["storage"] == {"requested": False}
assert manifest["live"] == {"requested": False}
def test_release_evidence_cli_writes_single_head_manifest(tmp_path: Path) -> None:
output = tmp_path / "baseline.json"
result = subprocess.run(
[
sys.executable,
str(SCRIPT),
"--output",
str(output),
"--release-id",
"test-cli",
],
cwd=ROOT,
capture_output=True,
text=True,
check=False,
timeout=60,
)
assert result.returncode == 0, result.stderr
payload = json.loads(output.read_text(encoding="utf-8"))
assert payload["git"]["commit"]
assert payload["migrations"]["single_head"] is True
assert payload["files"]["docs/RC_SCOPE_FREEZE_BELGIUM_NORTH_SEA.md"]["sha256"]
assert payload["files"]["docs/RC_ROADMAP_BELGIUM_NORTH_SEA.md"]["sha256"]
def test_readiness_gate_compiles_release_evidence_command() -> None:
readiness = (ROOT / "scripts" / "run_readiness_check.sh").read_text(
encoding="utf-8"
)
assert "py_compile scripts/capture_release_evidence.py" in readiness
@@ -0,0 +1,50 @@
from __future__ import annotations
from datetime import datetime, timezone
from unittest.mock import MagicMock
from app.models import AnalysisRun, Job
from app.services.runtime_reconciliation_service import RuntimeReconciliationService
def test_reconciliation_terminalizes_only_running_work() -> None:
db = MagicMock()
jobs = MagicMock()
runs = MagicMock()
jobs.filter.return_value.update.return_value = 5
runs.filter.return_value.update.return_value = 2
db.query.side_effect = [jobs, runs]
finished_at = datetime(2026, 7, 17, 20, 0, tzinfo=timezone.utc)
result = RuntimeReconciliationService.reconcile(
db,
finished_at=finished_at,
)
assert result.interrupted_jobs == 5
assert result.interrupted_analysis_runs == 2
jobs.filter.assert_called_once()
runs.filter.assert_called_once()
job_values = jobs.filter.return_value.update.call_args.args[0]
run_values = runs.filter.return_value.update.call_args.args[0]
assert job_values[Job.status] == "failed"
assert job_values[Job.finished_at] == finished_at
assert "PROCESS_INTERRUPTED" in job_values[Job.error_message]
assert run_values[AnalysisRun.status] == "failed"
assert run_values[AnalysisRun.finished_at] == finished_at
assert "PROCESS_INTERRUPTED" in run_values[AnalysisRun.error_message]
db.commit.assert_called_once_with()
def test_startup_reconciliation_is_enabled_only_in_all_in_one_runtime() -> None:
start_script = (
__import__("pathlib").Path(__file__).resolve().parents[2]
/ "deploy"
/ "unraid"
/ "all-in-one-start.sh"
).read_text(encoding="utf-8")
assert (
'GEOINTEL_RECONCILE_INTERRUPTED_RUNS_ON_STARTUP:-true'
in start_script
)
@@ -99,7 +99,7 @@ def test_unraid_all_in_one_runtime_starts_embedded_postgis_backend_and_nginx() -
assert "migrate_compose_volume_if_needed" in dockerman_script
assert "docker rm -f geointel" in dockerman_script
assert "proxy_pass http://127.0.0.1:8000/api/" in nginx_config
assert "proxy_pass http://127.0.0.1:8000/health" in nginx_config
assert "proxy_pass http://127.0.0.1:8000/health/ready" in nginx_config
assert "location = /geointel-icon.png" in nginx_config
assert "frontend/node_modules" in dockerignore
assert "storage" in dockerignore