from __future__ import annotations from typing import Any import pytest from activity_core import sync_service from activity_core.sync_schedules import ScheduleSyncResult @pytest.mark.asyncio async def test_run_sync_runs_requested_sections(monkeypatch) -> None: calls: list[str] = [] async def fake_definitions(session_factory: object) -> int: calls.append("definitions") return 2 async def fake_event_types(session_factory: object) -> int: calls.append("event_types") return 5 async def fake_schedules( temporal_client: object, session_factory: object, ) -> ScheduleSyncResult: calls.append("schedules") return ScheduleSyncResult(upserted=3, paused=1, deleted_orphans=2) monkeypatch.setattr(sync_service, "sync_activity_definitions", fake_definitions) monkeypatch.setattr(sync_service, "sync_event_types", fake_event_types) monkeypatch.setattr(sync_service, "sync_with_session_factory", fake_schedules) result = await sync_service.run_sync( session_factory=object(), temporal_client=object(), definitions=True, schedules=True, event_types=True, ) assert calls == ["definitions", "event_types", "schedules"] assert result["ok"] is True assert result["ran"] == { "definitions": True, "schedules": True, "event_types": True, } assert result["definitions"] == {"synced": 2} assert result["event_types"] == {"synced": 5} assert result["schedules"] == { "upserted": 3, "paused": 1, "deleted_orphans": 2, } assert result["errors"] == [] @pytest.mark.asyncio async def test_run_sync_collects_errors_and_continues(monkeypatch) -> None: calls: list[str] = [] async def failing_definitions(session_factory: object) -> int: calls.append("definitions") raise RuntimeError("definition parse failed") async def fake_schedules( temporal_client: object, session_factory: object, ) -> ScheduleSyncResult: calls.append("schedules") return ScheduleSyncResult(upserted=1) monkeypatch.setattr( sync_service, "sync_activity_definitions", failing_definitions, ) monkeypatch.setattr(sync_service, "sync_with_session_factory", fake_schedules) result = await sync_service.run_sync( session_factory=object(), temporal_client=object(), definitions=True, schedules=True, event_types=False, ) assert calls == ["definitions", "schedules"] assert result["ok"] is False assert result["definitions"] == {"synced": 0} assert result["schedules"]["upserted"] == 1 assert result["errors"] == [ { "stage": "definitions", "type": "RuntimeError", "message": "definition parse failed", } ] @pytest.mark.asyncio async def test_run_sync_reports_missing_temporal_client_for_schedules() -> None: result = await sync_service.run_sync( session_factory=object(), temporal_client=None, definitions=False, schedules=True, event_types=False, ) assert result["ok"] is False assert result["errors"] == [ { "stage": "schedules", "type": "RuntimeError", "message": "Temporal client is required for schedule sync", } ] def test_record_error_bounds_error_count() -> None: result: dict[str, Any] = { "ok": True, "errors": [], } for i in range(25): sync_service._record_error(result, "stage", RuntimeError(f"boom {i}")) assert result["ok"] is False assert len(result["errors"]) == 20 assert result["errors"][0]["message"] == "boom 0" assert result["errors"][-1]["message"] == "boom 19"