from datetime import datetime, timedelta, timezone import pytest from app.api.v1 import datasources as datasources_api from app.models.datasource import DataSource def make_datasource( datasource_id: int, source: str, *, name: str | None = None, module: str = "L4", is_active: bool = True, last_status: str | None = None, last_run_at: datetime | None = None, frequency_minutes: int = 60, ) -> DataSource: return DataSource( id=datasource_id, name=name or source, source=source, module=module, priority="P1", frequency_minutes=frequency_minutes, collector_class=source, is_active=is_active, last_status=last_status, last_run_at=last_run_at, ) def test_datasource_product_key_groups_domain_specific_sources(): assert datasources_api.datasource_product_key(make_datasource(1, "aisstream_vessels")) == "vessels" assert datasources_api.datasource_product_key(make_datasource(2, "telegeography_cables")) == "cables" assert datasources_api.datasource_product_key(make_datasource(3, "celestrak_tle")) == "satellites" assert datasources_api.datasource_product_key(make_datasource(4, "ris_live_bgp")) == "bgp" def test_filter_datasources_by_product_status_and_collected_state(): vessels = make_datasource(1, "aisstream_vessels", last_status="success") cables = make_datasource(2, "telegeography_cables", last_status="failed") filtered = datasources_api._filter_datasources_in_memory( [vessels, cables], running_tasks={}, record_counts={"aisstream_vessels": 12, "telegeography_cables": 0}, product="vessels", run_status="success", collected=True, ) assert filtered == [vessels] @pytest.mark.asyncio async def test_trigger_datasource_batch_skips_disabled_and_frequency_window(monkeypatch): now = datetime.now(timezone.utc) disabled = make_datasource(1, "aisstream_vessels", is_active=False) not_due = make_datasource(2, "telegeography_cables", last_run_at=now, frequency_minutes=120) due = make_datasource(3, "ris_live_bgp", last_run_at=now - timedelta(hours=2)) triggered_sources: list[str] = [] async def fake_running_tasks(_db, _ids): return {} async def fake_latest_task_ids(_db, _ids): return {} monkeypatch.setattr(datasources_api, "_load_latest_running_tasks", fake_running_tasks) monkeypatch.setattr(datasources_api, "_load_latest_task_ids", fake_latest_task_ids) monkeypatch.setattr( datasources_api, "run_collector_now", lambda source: triggered_sources.append(source) or True, ) result = await datasources_api._trigger_datasource_batch( object(), [disabled, not_due, due], force=False, ) assert [item["source"] for item in result["triggered"]] == ["ris_live_bgp"] assert {item["reason"] for item in result["skipped"]} == {"disabled", "within_frequency_window"} assert triggered_sources == ["ris_live_bgp"]