ForcePilot/backend/test/unit/channels/plugins/wecom/test_manifest.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

227 lines
7.9 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.

"""wecom 插件 manifest.json 字段完整性测试。
验证 ``manifest.json`` 经 ``load_manifest_from_dir`` 加载后的关键不变量:
- 渠道标识(``channel_type`` / ``provides``
- 能力声明26 项 capabilities 字段)
- 配置 schema23 项 config_schema
- 可访问端口13 项 accessible_ports
- 资源配额(``max_connections`` == 50
- 凭证策略(``credential_strategy.type`` == ``"hybrid"``
- 传输模式(``transport_mode`` == ``"both"``
- 可注入管道(``injectable_pipelines`` == ``["inbound", "outbound"]``
- 生命周期8 项 lifecycle 钩子)
测试通过 ``load_manifest_from_dir`` 加载真实 ``manifest.json``,确保校验
基于实际文件内容而非硬编码预期值。
"""
from __future__ import annotations
import os
import pytest
from yuxi.channels.application.lifecycle.manifest_loader import load_manifest_from_dir
from yuxi.channels.contract.plugin.manifest import ChannelManifest
pytestmark = pytest.mark.unit
@pytest.fixture
def wecom_manifest() -> ChannelManifest:
"""加载 wecom 插件真实 manifest.json。"""
plugin_dir = os.path.join(
os.path.dirname(__file__),
"..",
"..",
"..",
"..",
"..",
"package",
"yuxi",
"channels",
"plugins",
"wecom",
)
return load_manifest_from_dir(os.path.abspath(plugin_dir))
# ------------------------------------------------------------------
# 加载与渠道标识
# ------------------------------------------------------------------
@pytest.mark.unit
class TestManifestLoading:
"""manifest.json 加载与渠道标识测试。"""
def test_load_manifest_does_not_raise(self, wecom_manifest) -> None:
# Act / Assert - 加载不抛异常即可fixture 已完成加载)
assert wecom_manifest is not None
def test_channel_type_is_wecom(self, wecom_manifest) -> None:
assert wecom_manifest.channel_type == "wecom"
def test_provides_contains_wecom(self, wecom_manifest) -> None:
assert list(wecom_manifest.provides) == ["wecom"]
# ------------------------------------------------------------------
# 能力声明26 项)
# ------------------------------------------------------------------
@pytest.mark.unit
class TestCapabilities:
"""capabilities 字段完整性测试26 项)。"""
def test_capabilities_has_26_fields(self, wecom_manifest) -> None:
"""验证 capabilities 含 26 项能力字段。"""
from dataclasses import fields
cap_fields = [f.name for f in fields(wecom_manifest.capabilities)]
assert len(cap_fields) == 26
def test_rich_message_true(self, wecom_manifest) -> None:
assert wecom_manifest.capabilities.rich_message is True
def test_streaming_false(self, wecom_manifest) -> None:
assert wecom_manifest.capabilities.streaming is False
def test_message_recall_true(self, wecom_manifest) -> None:
assert wecom_manifest.capabilities.message_recall is True
def test_supports_card_update_true(self, wecom_manifest) -> None:
assert wecom_manifest.capabilities.supports_card_update is True
def test_supports_card_update_streaming_true(self, wecom_manifest) -> None:
assert wecom_manifest.capabilities.supports_card_update_streaming is True
def test_supports_image_inbound_true(self, wecom_manifest) -> None:
assert wecom_manifest.capabilities.supports_image_inbound is True
def test_supports_qr_login_true(self, wecom_manifest) -> None:
assert wecom_manifest.capabilities.supports_qr_login is True
def test_identity_resolver_true(self, wecom_manifest) -> None:
assert wecom_manifest.capabilities.identity_resolver is True
def test_agent_collaboration_false(self, wecom_manifest) -> None:
assert wecom_manifest.capabilities.agent_collaboration is False
# ------------------------------------------------------------------
# 配置 schema23 项)
# ------------------------------------------------------------------
@pytest.mark.unit
class TestConfigSchema:
"""config_schema 字段完整性测试23 项)。"""
def test_config_schema_has_23_fields(self, wecom_manifest) -> None:
assert len(wecom_manifest.config_schema) == 23
def test_required_keys_present(self, wecom_manifest) -> None:
"""验证必填配置键均在 schema 中声明。"""
keys = {f.key for f in wecom_manifest.config_schema}
for required_key in ("corp_id", "corp_secret", "agent_id", "encoding_aes_key", "token", "webhook_url"):
assert required_key in keys
def test_optional_secret_keys_present(self, wecom_manifest) -> None:
"""验证可选 Secret 配置键在 schema 中声明。"""
keys = {f.key for f in wecom_manifest.config_schema}
assert "external_contact_secret" in keys
assert "kf_secret" in keys
# ------------------------------------------------------------------
# accessible_ports13 项)
# ------------------------------------------------------------------
@pytest.mark.unit
class TestAccessiblePorts:
"""accessible_ports 字段完整性测试13 项)。"""
def test_accessible_ports_has_13_items(self, wecom_manifest) -> None:
assert len(wecom_manifest.accessible_ports) == 13
def test_core_ports_present(self, wecom_manifest) -> None:
"""验证核心端口在 accessible_ports 中声明。"""
ports = set(wecom_manifest.accessible_ports)
for port in ("ConfigPort", "LoggerPort", "CachePort", "PersistencePort"):
assert port in ports
# ------------------------------------------------------------------
# resource_quota
# ------------------------------------------------------------------
@pytest.mark.unit
class TestResourceQuota:
"""resource_quota 资源配额测试。"""
def test_max_connections_is_50(self, wecom_manifest) -> None:
assert wecom_manifest.resource_quota is not None
assert wecom_manifest.resource_quota.max_connections == 50
# ------------------------------------------------------------------
# credential_strategy
# ------------------------------------------------------------------
@pytest.mark.unit
class TestCredentialStrategy:
"""credential_strategy 凭证策略测试。"""
def test_type_is_hybrid(self, wecom_manifest) -> None:
assert wecom_manifest.credential_strategy is not None
assert wecom_manifest.credential_strategy.type.value == "hybrid"
# ------------------------------------------------------------------
# transport_mode
# ------------------------------------------------------------------
@pytest.mark.unit
class TestTransportMode:
"""transport_mode 传输模式测试。"""
def test_transport_mode_is_both(self, wecom_manifest) -> None:
assert wecom_manifest.transport_mode == "both"
# ------------------------------------------------------------------
# injectable_pipelines
# ------------------------------------------------------------------
@pytest.mark.unit
class TestInjectablePipelines:
"""injectable_pipelines 可注入管道测试。"""
def test_injectable_pipelines_contains_inbound_outbound(self, wecom_manifest) -> None:
assert list(wecom_manifest.injectable_pipelines) == ["inbound", "outbound"]
# ------------------------------------------------------------------
# lifecycle8 项)
# ------------------------------------------------------------------
@pytest.mark.unit
class TestLifecycle:
"""lifecycle 生命周期钩子测试。"""
def test_lifecycle_has_8_hooks(self, wecom_manifest) -> None:
assert len(wecom_manifest.lifecycle) == 8
def test_lifecycle_contains_all_hooks(self, wecom_manifest) -> None:
"""验证 8 个生命周期钩子全部声明。"""
hooks = set(wecom_manifest.lifecycle)
for hook in ("init", "start", "stop", "pause", "resume", "unload", "reconfigure", "fail"):
assert hook in hooks