Files
geointel/backend/tests/test_run_state_consistency.py
T
Codex 0aff8e3b8c
GeoIntel release gates / Compile, test, contracts and builds (push) Canceled after 0s
GeoIntel release gates / Python and npm vulnerability policy (push) Canceled after 0s
GeoIntel release gates / GIS image, SBOM and container scan (push) Canceled after 0s
feat(scope): make Belgium and North Sea operational default
2026-07-22 02:11:48 +02:00

143 lines
4.7 KiB
Python

from __future__ import annotations
from uuid import uuid4
import pytest
from app.core.config import Settings
from app.core.errors import AppError
from app.models import AnalysisRun, Dataset, Job, Project
from app.services.detection_service import DetectionService
from app.services.job_service import JobService
from app.services.segmentation_service import SegmentationService
class FakeSession:
"""Minimal session double without rollback support, mirroring existing test doubles."""
def __init__(self, objects=None) -> None:
self.objects = objects or {}
self.added = []
self.commits = 0
def get(self, model, item_id):
return self.objects.get((model, item_id))
def add(self, item) -> None:
self.added.append(item)
if getattr(item, "id", None) is not None:
self.objects[(item.__class__, item.id)] = item
def commit(self) -> None:
self.commits += 1
def refresh(self, item) -> None:
pass
def _project_and_dataset():
project_id = uuid4()
dataset_id = uuid4()
project = Project(id=project_id, name="Mol")
dataset = Dataset(
id=dataset_id,
project_id=project_id,
name="ortho.tif",
dataset_type="raster",
source="user_upload",
storage_path="storage/uploads/ortho.tif",
)
db = FakeSession(objects={(Project, project_id): project, (Dataset, dataset_id): dataset})
return db, project_id, dataset_id
def _statuses(db: FakeSession) -> tuple[list[str], list[str]]:
runs = [item.status for item in db.added if isinstance(item, AnalysisRun)]
jobs = [item.status for item in db.added if isinstance(item, Job)]
return runs, jobs
def test_invalid_fixture_detections_mark_run_and_job_failed() -> None:
db, project_id, dataset_id = _project_and_dataset()
with pytest.raises(AppError) as exc_info:
DetectionService.run_detection(
db=db,
project_id=project_id,
dataset_id=dataset_id,
model_id="manual-fixture-detector",
confidence_threshold=0.5,
parameters_json={"fixture_mode": True, "fixture_detections": "not-a-list"},
settings=Settings(_env_file=None),
)
assert exc_info.value.code == "INVALID_FIXTURE_DETECTIONS"
run_statuses, job_statuses = _statuses(db)
assert run_statuses and all(status == "failed" for status in run_statuses)
assert job_statuses and all(status == "failed" for status in job_statuses)
def test_invalid_fixture_segmentations_mark_run_and_job_failed() -> None:
db, project_id, dataset_id = _project_and_dataset()
with pytest.raises(AppError) as exc_info:
SegmentationService.run_segmentation(
db=db,
project_id=project_id,
dataset_id=dataset_id,
model_id="fixture-segmenter",
confidence_threshold=0.5,
parameters_json={"fixture_mode": True, "fixture_segmentations": "not-a-list"},
settings=Settings(_env_file=None),
)
assert exc_info.value.code == "INVALID_FIXTURE_SEGMENTATIONS"
run_statuses, job_statuses = _statuses(db)
assert run_statuses and all(status == "failed" for status in run_statuses)
assert job_statuses and all(status == "failed" for status in job_statuses)
def test_unexpected_error_in_sync_job_marks_job_failed() -> None:
project_id = uuid4()
db = FakeSession(objects={(Project, project_id): Project(id=project_id, name="Mol")})
def exploding_operation():
raise RuntimeError("unexpected internal failure")
with pytest.raises(RuntimeError):
JobService.run_sync_job(
db=db,
project_id=project_id,
job_type="test.unexpected",
parameters={},
operation=exploding_operation,
)
jobs = [item for item in db.added if isinstance(item, Job)]
assert jobs
final_job = jobs[-1]
assert final_job.status == "failed"
assert "Unexpected internal error" in (final_job.error_message or "")
def test_app_error_in_sync_job_still_marks_job_failed() -> None:
project_id = uuid4()
db = FakeSession(objects={(Project, project_id): Project(id=project_id, name="Mol")})
def failing_operation():
raise AppError(code="SOME_DOMAIN_ERROR", message="Bounded failure", status_code=422)
with pytest.raises(AppError):
JobService.run_sync_job(
db=db,
project_id=project_id,
job_type="test.bounded",
parameters={},
operation=failing_operation,
)
jobs = [item for item in db.added if isinstance(item, Job)]
assert jobs
assert jobs[-1].status == "failed"
assert jobs[-1].error_message == "Bounded failure"