ForcePilot/backend/test/unit/channels/plugins/qqbot/test_constants.py
Kris 361dbe2149 test: 批量修复单元测试与集成测试用例的各类问题
本次提交修复了多个测试文件中的问题:
1.  为各管道测试类添加tracer_port属性初始化
2.  修复QQBot客户端关闭异常的错误类型匹配
3.  修正配对接口查询的状态值小写格式
4.  移除ChannelType枚举值的显式.value调用
5.  新增QQ常量的导出项与Instagram测试桩函数
6.  修复API接口返回值解析,正确访问data字段
7.  调整企业微信富文本消息的断言结构
8.  修正微信iLink的消息类型测试用例
9.  替换废弃的datetime.utc相关导入为UTC常量
10. 修复QQBot白名单适配器的返回值结构
11. 调整outbox工具的channel_msg_id处理逻辑
12. 新增多个适配器与服务的测试用例,覆盖异常降级、缓存处理等场景
13. 重构部分QQBot适配器的辅助函数测试,清理冗余代码
14. 修复微信iLink客户端的上传接口调用参数与缓存清理逻辑
15. 修正配置导出接口的敏感字段过滤与返回值结构
2026-07-09 04:23:07 +08:00

262 lines
10 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.

"""qqbot 插件 _constants.py 模块测试。
验证共享常量模块的正确性:
- 模块导出完整(``__all__`` 与实际定义一致)。
- 常量值与 ``manifest.json`` 中对应字段对齐F-03 单真相源):
- ``CHANNEL_TARGET`` 与 manifest ``channel_type`` 一致。
- ``HTTP_TIMEOUT_SECONDS`` 与 manifest ``http_timeout_ms.default`` 对齐。
- ``HTTP_MAX_CONNECTIONS`` 与 manifest ``resource_quota.max_connections`` 对齐。
- ``WS_HEARTBEAT_INTERVAL_MS`` 与 manifest ``ws_heartbeat_interval_ms.default`` 对齐。
- ``DEFAULT_INTENTS`` 与 manifest ``intents.default`` 对齐。
- ``MAX_MESSAGE_LENGTH`` 与 manifest ``max_message_length`` 对齐。
_constants.py 作为 ConfigPort 未命中时的本地回退默认值,其值必须与
manifest 声明保持同步,避免双源不一致。
"""
from __future__ import annotations
import os
import pytest
from yuxi.channels.application.lifecycle.manifest_loader import load_manifest_from_dir
from yuxi.channels.plugins.qqbot import _constants
pytestmark = pytest.mark.unit
@pytest.fixture
def qqbot_manifest():
"""加载 qqbot 插件真实 manifest.json。"""
plugin_dir = os.path.join(
os.path.dirname(__file__),
"..",
"..",
"..",
"..",
"..",
"package",
"yuxi",
"channels",
"plugins",
"qqbot",
)
return load_manifest_from_dir(os.path.abspath(plugin_dir))
# ------------------------------------------------------------------
# 模块导出完整性
# ------------------------------------------------------------------
@pytest.mark.unit
class TestConstantsExports:
"""验证 _constants.py 模块导出完整。"""
def test_all_contains_channel_target(self):
assert "CHANNEL_TARGET" in _constants.__all__
def test_all_contains_qqbot_api_dep(self):
assert "QQBOT_API_DEP" in _constants.__all__
def test_all_contains_http_constants(self):
assert "HTTP_TIMEOUT_SECONDS" in _constants.__all__
assert "HTTP_MAX_CONNECTIONS" in _constants.__all__
assert "HTTP_MAX_KEEPALIVE_CONNECTIONS" in _constants.__all__
def test_all_contains_ws_constants(self):
assert "WS_HEARTBEAT_INTERVAL_MS" in _constants.__all__
assert "WS_STALL_TIMEOUT_MS" in _constants.__all__
def test_all_contains_cache_constants(self):
assert "USER_CACHE_TTL" in _constants.__all__
assert "IDENTITY_CACHE_TTL" in _constants.__all__
assert "TOKEN_DEFAULT_TTL" in _constants.__all__
def test_all_matches_defined_names(self):
"""``__all__`` 应恰好包含模块中定义的全部公开常量。"""
expected = {
"CHANNEL_TARGET",
"QQBOT_API_DEP",
"API_BASE_URL",
"TOKEN_ENDPOINT_PATH",
"GATEWAY_ENDPOINT_PATH",
"USERS_ENDPOINT_PREFIX",
"C2C_MESSAGES_PREFIX",
"GROUP_MESSAGES_PREFIX",
"MESSAGES_SUFFIX",
"FILES_SUFFIX",
"INTERACTIONS_PREFIX",
"HTTP_TIMEOUT_SECONDS",
"HTTP_MAX_CONNECTIONS",
"HTTP_MAX_KEEPALIVE_CONNECTIONS",
"WS_HEARTBEAT_INTERVAL_MS",
"WS_STALL_TIMEOUT_MS",
"CONNECT_TIMEOUT_MS",
"TOKEN_REFRESH_LEAD_TIME_SEC",
"TOKEN_DEFAULT_TTL",
"USER_CACHE_TTL",
"IDENTITY_CACHE_TTL",
"MSG_CACHE_TTL_SECONDS",
"DEFAULT_INTENTS",
"PASSIVE_REPLY_LIMIT_C2C",
"PASSIVE_REPLY_LIMIT_GROUP",
"PASSIVE_REPLY_TTL_C2C_MS",
"PASSIVE_REPLY_TTL_GROUP_MS",
"GROUP_ACTIVE_QPM_ACCOUNT_VERIFIED",
"GROUP_ACTIVE_QPM_ACCOUNT_UNVERIFIED",
"GROUP_ACTIVE_QPM_PER_GROUP",
"RECALL_TIME_LIMIT_SECONDS",
"PASSIVE_COUNTER_KEY_PREFIX",
"ACTIVE_QPM_KEY_PREFIX",
"ACTIVE_QPM_GROUP_KEY_PREFIX",
"INTERACTION_ACK_TIMEOUT_SECONDS",
"MAX_MESSAGE_LENGTH",
}
assert set(_constants.__all__) == expected
# ------------------------------------------------------------------
# 常量值与 manifest 对齐F-03 单真相源)
# ------------------------------------------------------------------
@pytest.mark.unit
class TestConstantsAlignWithManifest:
"""验证 _constants.py 常量值与 manifest.json 声明对齐。"""
def test_channel_target_matches_manifest_channel_type(self, qqbot_manifest):
assert _constants.CHANNEL_TARGET == qqbot_manifest.channel_type
def test_channel_target_matches_manifest_provides(self, qqbot_manifest):
"""CHANNEL_TARGET 也应出现在 manifest provides 中。"""
assert _constants.CHANNEL_TARGET in qqbot_manifest.provides
def test_http_timeout_aligns_with_http_timeout_ms(self, qqbot_manifest):
"""HTTP_TIMEOUT_SECONDS与 manifest http_timeout_ms.default毫秒对齐。"""
field = next(f for f in qqbot_manifest.config_schema if f.key == "http_timeout_ms")
assert _constants.HTTP_TIMEOUT_SECONDS == field.default / 1000.0
def test_http_max_connections_aligns_with_resource_quota(self, qqbot_manifest):
"""HTTP_MAX_CONNECTIONS 与 manifest resource_quota.max_connections 对齐。"""
assert qqbot_manifest.resource_quota is not None
assert _constants.HTTP_MAX_CONNECTIONS == qqbot_manifest.resource_quota.max_connections
def test_ws_heartbeat_aligns_with_ws_heartbeat_interval_ms(self, qqbot_manifest):
"""WS_HEARTBEAT_INTERVAL_MS 与 manifest ws_heartbeat_interval_ms.default 对齐。"""
field = next(f for f in qqbot_manifest.config_schema if f.key == "ws_heartbeat_interval_ms")
assert _constants.WS_HEARTBEAT_INTERVAL_MS == field.default
def test_ws_stall_timeout_aligns_with_ws_stall_timeout_ms(self, qqbot_manifest):
"""WS_STALL_TIMEOUT_MS 与 manifest ws_stall_timeout_ms.default 对齐。"""
field = next(f for f in qqbot_manifest.config_schema if f.key == "ws_stall_timeout_ms")
assert _constants.WS_STALL_TIMEOUT_MS == field.default
def test_default_intents_aligns_with_intents_default(self, qqbot_manifest):
"""DEFAULT_INTENTS 与 manifest intents.default 对齐。"""
field = next(f for f in qqbot_manifest.config_schema if f.key == "intents")
assert _constants.DEFAULT_INTENTS == field.default
def test_max_message_length_aligns_with_manifest(self, qqbot_manifest):
"""MAX_MESSAGE_LENGTH 与 manifest max_message_length 对齐。"""
assert _constants.MAX_MESSAGE_LENGTH == qqbot_manifest.max_message_length
def test_user_cache_ttl_aligns_with_user_cache_ttl(self, qqbot_manifest):
"""USER_CACHE_TTL 与 manifest user_cache_ttl.default 对齐。"""
field = next(f for f in qqbot_manifest.config_schema if f.key == "user_cache_ttl")
assert _constants.USER_CACHE_TTL == field.default
def test_token_default_ttl_aligns_with_token_cache_ttl(self, qqbot_manifest):
"""TOKEN_DEFAULT_TTL 与 manifest token_cache_ttl.default 对齐。"""
field = next(f for f in qqbot_manifest.config_schema if f.key == "token_cache_ttl")
assert _constants.TOKEN_DEFAULT_TTL == field.default
# ------------------------------------------------------------------
# 常量值合理性
# ------------------------------------------------------------------
@pytest.mark.unit
class TestConstantsSanity:
"""验证 _constants.py 常量值的基本合理性。"""
def test_channel_target_is_qqbot(self):
assert _constants.CHANNEL_TARGET == "qqbot"
def test_qqbot_api_dep_is_string(self):
assert isinstance(_constants.QQBOT_API_DEP, str)
assert _constants.QQBOT_API_DEP == "qqbot_api"
def test_api_base_url_starts_with_https(self):
assert _constants.API_BASE_URL.startswith("https://")
def test_http_timeout_positive(self):
assert _constants.HTTP_TIMEOUT_SECONDS > 0
def test_http_max_connections_positive(self):
assert _constants.HTTP_MAX_CONNECTIONS > 0
def test_http_keepalive_le_max_connections(self):
"""keepalive 连接数上限不应超过最大连接数。"""
assert _constants.HTTP_MAX_KEEPALIVE_CONNECTIONS <= _constants.HTTP_MAX_CONNECTIONS
def test_ws_heartbeat_positive(self):
assert _constants.WS_HEARTBEAT_INTERVAL_MS > 0
def test_ws_stall_timeout_gt_heartbeat(self):
"""卡死检测超时应大于心跳间隔。"""
assert _constants.WS_STALL_TIMEOUT_MS > _constants.WS_HEARTBEAT_INTERVAL_MS
def test_default_intents_positive(self):
assert _constants.DEFAULT_INTENTS > 0
def test_token_default_ttl_positive(self):
assert _constants.TOKEN_DEFAULT_TTL > 0
def test_token_refresh_lead_time_positive(self):
assert _constants.TOKEN_REFRESH_LEAD_TIME_SEC > 0
def test_token_refresh_lead_time_lt_default_ttl(self):
"""刷新提前量应小于默认 TTL。"""
assert _constants.TOKEN_REFRESH_LEAD_TIME_SEC < _constants.TOKEN_DEFAULT_TTL
def test_user_cache_ttl_positive(self):
assert _constants.USER_CACHE_TTL > 0
def test_identity_cache_ttl_positive(self):
assert _constants.IDENTITY_CACHE_TTL > 0
def test_identity_cache_ttl_le_user_cache_ttl(self):
"""身份解析缓存 TTL 应小于等于用户缓存 TTL。"""
assert _constants.IDENTITY_CACHE_TTL <= _constants.USER_CACHE_TTL
def test_max_message_length_positive(self):
assert _constants.MAX_MESSAGE_LENGTH > 0
def test_msg_cache_ttl_positive(self):
assert _constants.MSG_CACHE_TTL_SECONDS > 0
def test_recall_time_limit_positive(self):
assert _constants.RECALL_TIME_LIMIT_SECONDS > 0
def test_interaction_ack_timeout_positive(self):
assert _constants.INTERACTION_ACK_TIMEOUT_SECONDS > 0
def test_passive_reply_limits_positive(self):
assert _constants.PASSIVE_REPLY_LIMIT_C2C > 0
assert _constants.PASSIVE_REPLY_LIMIT_GROUP > 0
def test_passive_reply_ttls_positive(self):
assert _constants.PASSIVE_REPLY_TTL_C2C_MS > 0
assert _constants.PASSIVE_REPLY_TTL_GROUP_MS > 0
def test_group_active_qpm_positive(self):
assert _constants.GROUP_ACTIVE_QPM_ACCOUNT_VERIFIED > 0
assert _constants.GROUP_ACTIVE_QPM_ACCOUNT_UNVERIFIED > 0
assert _constants.GROUP_ACTIVE_QPM_PER_GROUP > 0
def test_group_active_qpm_verified_gt_unverified(self):
"""认证账号 QPM 应大于等于未认证账号 QPM。"""
assert _constants.GROUP_ACTIVE_QPM_ACCOUNT_VERIFIED >= _constants.GROUP_ACTIVE_QPM_ACCOUNT_UNVERIFIED