feat(scope): make Belgium and North Sea operational default
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
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"
|
||||
Reference in New Issue
Block a user