122 lines
4.4 KiB
Python
122 lines
4.4 KiB
Python
|
|
"""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()
|