"""Fixture payloads for ``POST /wechatweb/callRecord/queryWeChatVoiceList`` with ``callSource=4`` (WhatsApp voice calls). The WhatsApp voice-call endpoint is the SAME unified voice interface as the WeChat one (``queryWeChatVoiceList``) — only the request flag differs (``callSource="4"`` for WA vs. ``"0"`` for WeChat). The response envelope is identical to :mod:`tests.cassettes.voiceCall_fixture` (no ``basicResult`` wrapper; the row list and pagination fields sit directly under ``data``):: {"message":"调用成功","code":"10000","success":true,"traceId":"...", "data":{"data":[...], "total":.., "pageNum":.., "pageSize":..}} WA voice records carry the same core schema as WeChat voice calls plus a few WA-specific fields that have no matching ``PhoneCall`` column and survive only via ``raw_json``: ``weChatId`` (here the owning WA account id, e.g. ``@s.whatsapp.net``), ``callFileId``, ``nextId``, ``isRoom`` and ``from``. Live ``total`` is 143 per phase0-findings.md §4 (WA voice capture). The cassette ships three records across two pages so a test can drive the page-number paginator across the empty-page stop signal: * page 1 -> 2 of the 3 fixture records, ``total=3`` * page 2 -> the remaining 1 record, ``total=3`` * (page 3 -> empty ``data`` array, served inline by the test) Records deliberately exercise the documented mapping edges: * ``wa_vc_1001`` — incoming connected call WITH a recording (``isSend=0`` -> ``incoming_call=1``; ``callStatus=1``; non-null ``ossFileName``; ``duration`` as a string). * ``wa_vc_1002`` — outgoing no-answer call with NO recording (``isSend=1`` -> ``incoming_call=0``; ``callStatus=2``; ``ossFileName=null``). * ``wa_vc_1003`` — outgoing connected call with a recording. """ from __future__ import annotations from typing import Any # --------------------------------------------------------------------------- # Capture-shaped WA voice-call records (3 total across 2 pages). # --------------------------------------------------------------------------- WA_VOICE_RECORDS: list[dict[str, Any]] = [ { # Incoming connected call with a recording. "id": "wa_vc_1001", "userId": "staff_user_001", "userName": "Alice Wang", "userWeChatNickName": "Alice WA", "userWeChatAlias": "alice_wa", "talker": "8613800000001@s.whatsapp.net", "talkerNickName": "WA Customer A", "talkerAlias": "wa_a", "callType": 1, "isSend": 0, # 0=incoming -> incoming_call=1 "callStatus": 1, # 1=connected w/ recording "ossFileName": ( "https://yunke-pcfile.oss-cn-beijing.aliyuncs.com/whatsapp-call/" "8m0atd/wa001/ossCallback/1700000001000_1700000151000.mp3" ), "duration": "151", # string seconds -> call_duration int 151 "startTime": "2026-01-15 10:30:00", # parsed to ms-epoch UTC "weChatId": "8613800000000@s.whatsapp.net", # owning WA account "callFileId": "cf_wa_1001", "nextId": "wa_vc_1002", "isRoom": 0, "from": 4, # WA source flag "version": "v1.20260731.wa1001", }, { # Outgoing no-answer call, no recording. "id": "wa_vc_1002", "userId": "staff_user_001", "userName": "Alice Wang", "userWeChatNickName": "Alice WA", "userWeChatAlias": "alice_wa", "talker": "8613800000002@s.whatsapp.net", "talkerNickName": "WA Lead B", "talkerAlias": "", "callType": 1, "isSend": 1, # 1=outgoing -> incoming_call=0 "callStatus": 2, # 2=no answer "ossFileName": None, # no recording -> record_file None "duration": "0", "startTime": "2026-01-15 11:00:00", "weChatId": "8613800000000@s.whatsapp.net", "callFileId": "cf_wa_1002", "nextId": "wa_vc_1003", "isRoom": 0, "from": 4, "version": "v1.20260731.wa1002", }, { # Outgoing connected call with a recording. "id": "wa_vc_1003", "userId": "staff_user_002", "userName": "Bob Chen", "userWeChatNickName": "Bob WA", "userWeChatAlias": "bob_wa", "talker": "8613800000003@s.whatsapp.net", "talkerNickName": "WA Customer C", "talkerAlias": "wa_c", "callType": 1, "isSend": 1, # outgoing -> incoming_call=0 "callStatus": 1, # connected w/ recording "ossFileName": ( "https://yunke-pcfile.oss-cn-beijing.aliyuncs.com/whatsapp-call/" "8m0atd/wa002/ossCallback/1700000003000_1700000045000.mp3" ), "duration": "45", "startTime": "2026-01-15 12:00:00", "weChatId": "8613800000004@s.whatsapp.net", "callFileId": "cf_wa_1003", "nextId": None, "isRoom": 0, "from": 4, "version": "v1.20260731.wa1003", }, ] # Default page size for the fixture (and the scraper). total=3 means the # fixture fills exactly 2 pages of size 2 (2 + 1 records) plus a # terminating empty page. TOTAL = len(WA_VOICE_RECORDS) DEFAULT_PAGE_SIZE = 2 def make_wa_voice_list_response( records: list[dict[str, Any]], *, total: int, page_num: int = 1, page_size: int = DEFAULT_PAGE_SIZE, trace_id: str | None = None, ) -> dict[str, Any]: """Build a capture-shaped ``queryWeChatVoiceList`` (callSource=4) envelope. Shape (no ``basicResult`` wrapper; pagination fields directly under ``data``):: {"message":"调用成功","code":"10000","success":true,"traceId":"...", "data":{"data":[...], "total":.., "pageNum":.., "pageSize":..}} """ return { "message": "调用成功", "code": "10000", "success": True, "traceId": trace_id or f"trace-wa-{page_num}", "data": { "data": list(records), "total": total, "pageNum": page_num, "pageSize": page_size, }, }