Files
yunke/yunke-server/tests/test_api_sms.py
T
浅念 ff7e9cb1d9 feat: backend read api for all entities (messages/groups/calls/sms/org/sync)
Adds the read-only API surface the upcoming Vue frontend needs to browse all
mirrored entities. Follows the existing customer-router pattern: Annotated
deps, Page[T] envelope, select+where+order_by, count() for total.

Endpoints (all GET):
* /api/departments          -> list[DepartmentOut] (path-ordered, plain list)
* /api/staff                -> Page[StaffOut] (department_id, is_on_job filters)
* /api/accounts             -> Page[AccountOut] (channel filter; unified
                             wechat+whatsapp projection built in memory)
* /api/messages             -> Page[MessageOut] (channel/owner/talker/room/
                             [start,end] filters; ordered timestamp asc)
* /api/groups               -> Page[GroupOut] (channel/owner filters)
* /api/groups/{id}/members  -> list[GroupMemberOut] (404 on unknown room)
* /api/calls                -> Page[PhoneCallOut] (channel/user/time window;
                             ordered start_call_time desc)
* /api/sms                  -> Page[SmsOut] (user/time window; create_time desc)
* /api/sync/status          -> {states, recent_runs} monitor payload

Schemas (Pydantic v2, from_attributes=True): MessageOut, GroupOut,
GroupMemberOut, PhoneCallOut, SmsOut, DepartmentOut, StaffOut, AccountOut
(unified across two tables), SyncStateOut, SyncRunOut, SyncStatusOut.
Only human-relevant fields per entity are exposed.

Tests: per-router TDD via httpx ASGITransport against an in-memory SQLite
override of get_session. A shared make_client factory in conftest.py builds
the seeded client, deduping the ~40-line fixture across the six new files.
41 new tests cover shape + every documented filter + pagination + ordering
(nulls-last verified on all desc/asc time columns).

Suite: 226 -> 267 passing. ruff check clean.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-01 16:15:40 +08:00

112 lines
3.2 KiB
Python

"""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",
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",
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",
"sender",
"direction",
"user_id",
"receive_name",
"create_time",
"sim_phone",
):
assert key in body["items"][0]
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"