ForcePilot/backend/test/unit/channels/plugins/wechat_mp/conftest.py
Kris 41bd0a618c refactor: 统一将日志方法从 warn 改为 warning
将代码库中所有使用 logger.warn 的地方替换为标准的 logger.warning,对齐日志方法命名规范,修复多处测试和业务代码中的方法调用不匹配问题。
2026-07-13 17:32:29 +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.warning = AsyncMock()
logger.error = AsyncMock()
logger.exception = AsyncMock()
return logger
@pytest.fixture
def fake_persistence_port() -> AsyncMock:
"""PersistencePort 桩。
``PersistencePort`` 的方法为异步,使用 ``AsyncMock()``。
"""
return AsyncMock()