"""channels plugins/wechat_ilink 层单元测试共享 fixture。 提供 ``fake_ilink_client`` 桩,模拟 ``ILinkClient`` (``yuxi.channels.plugins.wechat_ilink.ilink_client.ILinkClient``)。 微信 iLink 适配器测试通过此桩注入客户端依赖,不发起真实 HTTP 请求。 桩使用 ``AsyncMock()``,预设主要方法返回值(消息列表 / client_id / 二维码 / 空字节等),调用方可按需覆盖。方法签名以 ``ILinkClient`` 实现为准。单文件独有的 helper 应保留在对应测试文件内 (见 testing-guidelines.md)。 注意:凭证管理(get_credentials / set_credentials / clear_credentials) 与 qr_token 缓存(get_qr_token / set_qr_token / clear_qr_token)已分别 迁移至 ``CredentialService`` 与 ``QrLoginService`` 会话管理, ``ILinkClient`` 不再持有这些方法,本桩亦不预设。 """ from __future__ import annotations from unittest.mock import AsyncMock import pytest @pytest.fixture def fake_ilink_client() -> AsyncMock: """ILinkClient 桩。 ``AsyncMock()`` 模拟 ``ILinkClient``,预设主要方法返回值: - ``get_updates`` 返回 ``{"msgs": [], "get_updates_buf": "fake-cursor"}``; - ``send_message`` 返回 ``{"client_id": "fake-client-id"}``; - ``get_bot_qrcode`` 返回 ``{"qrcode_img_content": "", "qrcode": "fake-qrcode"}``; - ``get_qrcode_status`` 返回 ``{"status": "CONFIRM"}``; - ``getconfig`` 返回 ``{}``; - ``getuploadurl`` 返回含 upload_url / encrypt_query_param / aes_key / file_id 的字典; - ``cdn_upload`` 返回 ``None``; - ``cdn_download`` 返回 ``b""``; - ``get_context_token`` 返回 ``None``; - ``get_cursor`` 返回 ``""``; - ``set_context_token`` 返回 ``None``。 """ client = AsyncMock() # 配置读取:按 key 返回合理默认值(与 manifest.json 默认值一致) _config_defaults = { "baseurl": "https://ilinkai.weixin.qq.com", "channel_version": "2.0.0", "cdn_url": "https://novac2c.cdn.weixin.qq.com/c2c", "long_poll_timeout_ms": 35000, "qr_poll_interval_ms": 2000, "qr_timeout_ms": 120000, "max_message_length": 4000, } client.get_channel_config.side_effect = lambda key, default=None, **kw: _config_defaults.get(key, default) # 长轮询 client.get_updates.return_value = { "msgs": [], "get_updates_buf": "fake-cursor", } # 消息发送 client.send_message.return_value = {"client_id": "fake-client-id"} # 二维码登录 client.get_bot_qrcode.return_value = { "qrcode_img_content": "", "qrcode": "fake-qrcode", } client.get_qrcode_status.return_value = { "status": "CONFIRM", "redirect_host": None, "bot_token": "fake-bot-token", "ilink_bot_id": "fake-bot-id", "ilink_user_id": "fake-user-id", "baseurl": "https://ilinkai.weixin.qq.com", } # 健康探活 client.getconfig.return_value = {} client.getconfig_with_token.return_value = {} # CDN 上传/下载 client.getuploadurl.return_value = { "upload_url": "https://fake-cdn.example.com/upload", "encrypt_query_param": "fake-encrypt-param", "aes_key": "fake-aes-key", "file_id": "fake-file-id", } client.cdn_upload.return_value = None client.cdn_download.return_value = b"" # context_token 运行时缓存(ILinkClient 自管理) client.get_context_token.return_value = None client.set_context_token.return_value = None # cursor 缓存(仅读,写入由框架通过 PollResult.next_cursor 管理) client.get_cursor.return_value = "" return client