"""Tests for GET /api/sms — SMS log. Seeds 3 SMS rows (2 for user u1 at create_time 1000/3000, 1 for u2 at 2000) and asserts filters + ordering (``create_time desc``) + pagination. """ from __future__ import annotations from sqlalchemy.ext.asyncio import AsyncSession from app.db.models import Sms async def _seed(session: AsyncSession) -> None: session.add_all( [ Sms( sms_id="s1", channel="sms", user_id="u1", receive_name="Alice", content_preview="hello", content="hello, this is the full body", create_time=1000, direction=1, sender="111", customer_phone="999", ), Sms( sms_id="s2", channel="sms", user_id="u2", receive_name="Bob", content_preview="hi", create_time=2000, direction=0, ), Sms( sms_id="s3", channel="sms", user_id="u1", receive_name="Alice", content_preview="bye", content="bye for now", create_time=3000, direction=1, ), ] ) await session.commit() async def test_default_list_returns_all_ordered_desc(make_client): """No filters -> 3 rows ordered by ``create_time desc``.""" client = await make_client(_seed) r = await client.get("/api/sms") assert r.status_code == 200 body = r.json() assert body["total"] == 3 assert [it["sms_id"] for it in body["items"]] == ["s3", "s2", "s1"] for key in ( "sms_id", "customer_phone", "content_preview", "content", "sender", "direction", "user_id", "receive_name", "create_time", "sim_phone", "raw_json", ): assert key in body["items"][0] async def test_raw_json_serializes_verbatim(make_client): """``raw_json`` round-trips the verbatim upstream SMS record. Every camelCase key must come back unmolested so the all-fields drawer can render every column the 云客 短信记录 page shows. Rows without a blob serialize as JSON null. """ async def seed(session: AsyncSession) -> None: await _seed(session) session.add( Sms( sms_id="s_raw", channel="sms", user_id="u1", receive_name="Alice", content_preview="raw body", content="raw body full text", create_time=9000, direction=1, customer_phone="999", raw_json={ "id": "s_raw", "customerPhone": "999", "sms": "raw body full text", "smsSub": "raw body", "sender": "111", "direction": 1, "userId": "u1", "senderDepartment": "Sales", "receiveName": "Alice", "createTime": 9000, "simPhone": "13900000000", "simNumber": "1", "source": "yunke", }, ) ) await session.commit() client = await make_client(seed) r = await client.get("/api/sms", params={"user_id": "u1"}) assert r.status_code == 200 by_id = {it["sms_id"]: it for it in r.json()["items"]} assert by_id["s_raw"]["raw_json"] == { "id": "s_raw", "customerPhone": "999", "sms": "raw body full text", "smsSub": "raw body", "sender": "111", "direction": 1, "userId": "u1", "senderDepartment": "Sales", "receiveName": "Alice", "createTime": 9000, "simPhone": "13900000000", "simNumber": "1", "source": "yunke", } # Seeded without an explicit raw_json -> JSON null. assert by_id["s1"]["raw_json"] is None async def test_filter_by_user_id(make_client): """``?user_id=u1`` -> 2 SMS for Alice.""" client = await make_client(_seed) r = await client.get("/api/sms", params={"user_id": "u1"}) assert r.status_code == 200 body = r.json() assert body["total"] == 2 assert {it["sms_id"] for it in body["items"]} == {"s1", "s3"} async def test_time_window_filter(make_client): """``?start=1500&end=2500`` -> only s2.""" client = await make_client(_seed) r = await client.get("/api/sms", params={"start": 1500, "end": 2500}) assert r.status_code == 200 body = r.json() assert body["total"] == 1 assert body["items"][0]["sms_id"] == "s2" async def test_filters_compose(make_client): """``user_id=u1 & end=2000`` -> only s1.""" client = await make_client(_seed) r = await client.get("/api/sms", params={"user_id": "u1", "end": 2000}) assert r.status_code == 200 body = r.json() assert body["total"] == 1 assert body["items"][0]["sms_id"] == "s1" async def test_pagination(make_client): """``page_size=1&page=1`` -> 1 item, total 3.""" client = await make_client(_seed) r = await client.get("/api/sms", params={"page": 1, "page_size": 1}) assert r.status_code == 200 body = r.json() assert body["total"] == 3 assert len(body["items"]) == 1 assert body["items"][0]["sms_id"] == "s3" async def test_content_field_serializes(make_client): """``content`` (full SMS body) round-trips so the SMS view can show the whole message in a detail dialog — present when set, None when unset. """ client = await make_client(_seed) r = await client.get("/api/sms") assert r.status_code == 200 by_id = {it["sms_id"]: it for it in r.json()["items"]} assert by_id["s1"]["content"] == "hello, this is the full body" assert by_id["s3"]["content"] == "bye for now" # s2 was seeded without content -> None. assert by_id["s2"]["content"] is None