from __future__ import annotations from datetime import UTC, datetime from pathlib import Path from uuid import uuid4 import pytest from app.core.errors import AppError from app.models import Dataset from app.services.temporal_compatibility_service import TemporalCompatibilityService ROOT = Path(__file__).parents[2] def dataset( *, dataset_type: str, source_name: str, observed_at: datetime | None = None, valid_from: datetime | None = None, valid_to: datetime | None = None, temporal_granularity: str | None = None, source_metadata: dict | None = None, ) -> Dataset: return Dataset( id=uuid4(), project_id=uuid4(), name="temporal-source", dataset_type=dataset_type, source=source_name, source_name=source_name, observed_at=observed_at, valid_from=valid_from, valid_to=valid_to, temporal_granularity=temporal_granularity, source_metadata=source_metadata, ) def test_historical_orthophoto_is_rejected_for_detection() -> None: historical = dataset( dataset_type="raster", source_name="digitaal_vlaanderen_orthophoto", observed_at=datetime(2020, 1, 1, tzinfo=UTC), valid_from=datetime(2020, 1, 1, tzinfo=UTC), valid_to=datetime(2020, 12, 31, 23, 59, 59, tzinfo=UTC), temporal_granularity="year", source_metadata={"product_key": "2020", "supports_detection": False}, ) with pytest.raises(AppError) as exc_info: TemporalCompatibilityService.ensure_detection_source_supported(historical) assert exc_info.value.code == "DETECTION_SOURCE_TEMPORALLY_UNSUPPORTED" assert exc_info.value.status_code == 422 def test_historical_detection_qa_rejects_current_reference() -> None: historical = dataset( dataset_type="raster", source_name="digitaal_vlaanderen_orthophoto", valid_from=datetime(2020, 1, 1, tzinfo=UTC), valid_to=datetime(2020, 12, 31, 23, 59, 59, tzinfo=UTC), temporal_granularity="year", source_metadata={"product_key": "2020", "supports_detection": False}, ) current_reference = dataset( dataset_type="vector", source_name="grb", valid_from=datetime(2026, 7, 1, tzinfo=UTC), valid_to=datetime(2026, 7, 31, 23, 59, 59, tzinfo=UTC), temporal_granularity="month", ) with pytest.raises(AppError) as exc_info: TemporalCompatibilityService.assess_detection_qa(historical, current_reference) assert exc_info.value.code == "DETECTION_QA_TEMPORAL_MISMATCH" assert exc_info.value.status_code == 422 def test_historical_detection_qa_accepts_overlapping_reference_edition() -> None: historical = dataset( dataset_type="raster", source_name="digitaal_vlaanderen_orthophoto", valid_from=datetime(2020, 1, 1, tzinfo=UTC), valid_to=datetime(2020, 12, 31, 23, 59, 59, tzinfo=UTC), temporal_granularity="year", source_metadata={"product_key": "2020", "supports_detection": False}, ) historical_reference = dataset( dataset_type="vector", source_name="manual", valid_from=datetime(2020, 6, 1, tzinfo=UTC), valid_to=datetime(2020, 6, 30, 23, 59, 59, tzinfo=UTC), temporal_granularity="month", ) result = TemporalCompatibilityService.assess_detection_qa(historical, historical_reference) assert result["status"] == "compatible" assert result["candidate_historical"] is True assert result["candidate_interval"]["start"].startswith("2020-01-01") assert result["reference_interval"]["start"].startswith("2020-06-01") def test_current_source_with_unbounded_current_reference_remains_supported() -> None: current = dataset( dataset_type="raster", source_name="digitaal_vlaanderen_orthophoto", observed_at=datetime(2026, 7, 17, tzinfo=UTC), valid_from=datetime(2026, 7, 17, tzinfo=UTC), temporal_granularity="snapshot", source_metadata={"product_key": "most_recent", "supports_detection": True}, ) current_reference = dataset( dataset_type="vector", source_name="grb", observed_at=datetime(2026, 7, 16, tzinfo=UTC), temporal_granularity="snapshot", ) TemporalCompatibilityService.ensure_detection_source_supported(current) result = TemporalCompatibilityService.assess_detection_qa(current, current_reference) assert result["status"] == "compatible" assert result["candidate_historical"] is False def test_detection_frontend_has_no_implicit_first_raster_fallback() -> None: source = (ROOT / "frontend" / "src" / "hooks" / "useDetectionWorkflow.ts").read_text(encoding="utf-8") assert "rasterDatasets[0]" not in source assert "setSelectedDetectionDatasetId(rasterDatasets" not in source assert "const datasetId = selectedDetectionDatasetId" in source