"""Tests for GET /api/sync/status — sync monitor. Seeds 2 SyncState cursors and 3 SyncRun audit rows and asserts the aggregate shape ``{states, recent_runs}``, ordering of runs (newest first), and the ``recent`` cap. """ from __future__ import annotations import datetime as dt from sqlalchemy.ext.asyncio import AsyncSession from app.db.models import SyncRun, SyncState async def _seed(session: AsyncSession) -> None: session.add_all( [ SyncState( state_id=1, entity_type="message", channel="wechat", scope_id="wx_owner_1", last_version="v100", last_timestamp=1000, last_synced_at=dt.datetime(2026, 8, 1, 10, 0, 0, tzinfo=dt.UTC), ), SyncState( state_id=2, entity_type="customer", channel="whatsapp", scope_id=None, last_version="v9", last_timestamp=2000, ), SyncRun( run_id=1, entity_type="message", channel="wechat", scope_id="wx_owner_1", status="success", started_at=dt.datetime(2026, 8, 1, 9, 0, 0, tzinfo=dt.UTC), finished_at=dt.datetime(2026, 8, 1, 9, 0, 5, tzinfo=dt.UTC), records_fetched=100, records_upserted=95, ), SyncRun( run_id=2, entity_type="customer", channel="whatsapp", status="failed", started_at=dt.datetime(2026, 8, 1, 10, 0, 0, tzinfo=dt.UTC), records_fetched=0, records_upserted=0, error="upstream 500", ), SyncRun( run_id=3, entity_type="message", channel="wechat", status="running", started_at=dt.datetime(2026, 8, 1, 11, 0, 0, tzinfo=dt.UTC), records_fetched=0, records_upserted=0, ), ] ) await session.commit() async def test_status_returns_states_and_recent_runs(make_client): """``GET /api/sync/status`` -> both keys present with correct shapes.""" client = await make_client(_seed) r = await client.get("/api/sync/status") assert r.status_code == 200 body = r.json() assert set(body.keys()) == {"states", "recent_runs"} assert len(body["states"]) == 2 for key in ( "state_id", "entity_type", "channel", "scope_id", "last_version", "last_timestamp", "last_synced_at", ): assert key in body["states"][0] assert len(body["recent_runs"]) == 3 for key in ( "run_id", "entity_type", "channel", "scope_id", "status", "started_at", "finished_at", "records_fetched", "records_upserted", "error", ): assert key in body["recent_runs"][0] async def test_recent_runs_ordered_newest_first(make_client): """Runs ordered by ``started_at desc`` -> run 3, 2, 1.""" client = await make_client(_seed) r = await client.get("/api/sync/status") body = r.json() ids = [run["run_id"] for run in body["recent_runs"]] assert ids == [3, 2, 1] async def test_recent_limit_caps_results(make_client): """``?recent=2`` -> only the 2 most recent runs.""" client = await make_client(_seed) r = await client.get("/api/sync/status", params={"recent": 2}) assert r.status_code == 200 body = r.json() assert len(body["recent_runs"]) == 2 assert [run["run_id"] for run in body["recent_runs"]] == [3, 2] async def test_empty_db_returns_empty_lists(make_client): """No seed -> 200 with empty states + recent_runs (no crash).""" async def empty_seed(session: AsyncSession) -> None: await session.commit() client = await make_client(empty_seed) r = await client.get("/api/sync/status") assert r.status_code == 200 body = r.json() assert body == {"states": [], "recent_runs": []}