ForcePilot/backend/test/unit/channels/plugins/wecom/test_manifest.py

227 lines
7.9 KiB
Python
Raw Normal View History

"""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