ForcePilot/backend/test/unit/channels/plugins/wechat_mp/conftest.py
Kris 532e7c59ec test: add and fix multi-channel unit tests
1. 新增QQBot、Wecom、Dingtalk、Instagram、WechatMp等渠道的单元测试夹具与测试用例
2. 修复WechatILink的缓存键顺序与测试用例
3. 补全Feishu适配器的日志断言与测试逻辑
4. 清理WechatILink冗余的clear_context_token测试代码
2026-07-08 22:59:24 +08:00

122 lines
4.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""channels plugins/wechat_mp 层单元测试共享 fixture。
提供 ``fake_wechat_mp_client`` 桩,模拟 ``WeChatMpClient``
``yuxi.channels.plugins.wechat_mp.wechat_mp_client.WeChatMpClient``)。
微信公众号适配器测试通过此桩注入客户端依赖,不发起真实 HTTP 请求。
桩使用 ``AsyncMock()``预设主要方法返回值errcode=0 响应 / media_id /
token / 用户信息等),调用方可按需覆盖。方法签名以 ``WeChatMpClient``
实现为准。单文件独有的 helper 应保留在对应测试文件内
(见 testing-guidelines.md
同时提供 ``fake_config_port`` / ``fake_cache_port`` / ``fake_logger_port`` /
``fake_persistence_port`` 端口桩,供适配器测试注入 DI 依赖。
"""
from __future__ import annotations
from unittest.mock import AsyncMock, MagicMock
import pytest
@pytest.fixture
def fake_wechat_mp_client() -> AsyncMock:
"""WeChatMpClient 桩。
``AsyncMock()`` 模拟 ``WeChatMpClient``,预设主要方法返回值:
- ``send_custom_message`` 返回 ``{"errcode": 0, "msgid": "msg_123"}``
- ``upload_media_cached`` / ``upload_media`` 返回 ``"media_id_abc"``
- ``download_media`` 返回 ``b"fake_image_bytes"``
- ``get_user_info`` 返回含 openid / nickname / headimgurl 的字典;
- ``get_user_list`` 返回含 total / count / data / next_openid 的字典;
- ``get_callback_ip`` 返回 ``{"ip_list": ["127.0.0.1"]}``
- ``refresh_access_token`` / ``get_access_token`` 返回 token 字符串;
- ``check_customer_service_window`` / ``consume_customer_service_quota``
返回 ``True``
- ``mark_user_message`` / ``mark_other_event`` / ``set_active`` /
``attach_http_client`` / ``detach_http_client`` 返回 ``None``
- ``drain`` 返回 ``True``。
"""
client = AsyncMock()
# 客服消息发送
client.send_custom_message.return_value = {"errcode": 0, "msgid": "msg_123"}
# 媒体上传/下载
client.upload_media_cached.return_value = "media_id_abc"
client.upload_media.return_value = "media_id_abc"
client.download_media.return_value = b"fake_image_bytes"
# 用户信息查询
client.get_user_info.return_value = {
"openid": "o123",
"nickname": "TestUser",
"headimgurl": "http://example.com/avatar.jpg",
}
client.get_user_list.return_value = {
"total": 1,
"count": 1,
"data": {"openid": ["o123", "o456"]},
"next_openid": "o456",
}
# 通用 API
client.get_callback_ip.return_value = {"ip_list": ["127.0.0.1"]}
# access_token 中控
client.refresh_access_token.return_value = "fake_access_token"
client.get_access_token.return_value = "fake_access_token"
# 客服消息窗口与额度
client.check_customer_service_window.return_value = True
client.consume_customer_service_quota.return_value = True
client.mark_user_message.return_value = None
client.mark_other_event.return_value = None
# 生命周期管理
client.set_active.return_value = None
client.drain.return_value = True
client.attach_http_client.return_value = None
client.detach_http_client.return_value = None
return client
@pytest.fixture
def fake_config_port() -> AsyncMock:
"""ConfigPort 桩。
``ConfigPort`` 的 ``get`` 方法为异步,使用 ``AsyncMock()``。
默认返回 ``None``,调用方可按需覆盖。
"""
return AsyncMock()
@pytest.fixture
def fake_cache_port() -> AsyncMock:
"""CachePort 桩。
``CachePort`` 的 ``get`` / ``set`` / ``delete`` / ``acquireAdvisoryLock``
等方法为异步,使用 ``AsyncMock()``。默认返回 ``None``,调用方可按需覆盖。
"""
return AsyncMock()
@pytest.fixture
def fake_logger_port() -> MagicMock:
"""LoggerPort 桩。
``LoggerPort`` 的 ``info`` / ``warn`` / ``error`` / ``exception`` /
``debug`` 方法为异步,使用 ``MagicMock()`` + 按方法挂 ``AsyncMock()``
以精确模拟同步/异步混合的端口协议。
"""
logger = MagicMock()
logger.debug = AsyncMock()
logger.info = AsyncMock()
logger.warn = AsyncMock()
logger.error = AsyncMock()
logger.exception = AsyncMock()
return logger
@pytest.fixture
def fake_persistence_port() -> AsyncMock:
"""PersistencePort 桩。
``PersistencePort`` 的方法为异步,使用 ``AsyncMock()``。
"""
return AsyncMock()