ForcePilot/backend/test/unit/channels/plugins/feishu/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

172 lines
6.8 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.

"""feishu 插件 manifest.json 一致性测试。
验证 manifest.json 经 A1-A4 修正后的关键不变量:
- A1``capabilities.status`` / ``capabilities.probeable`` 为 ``true``
与 entry.py 注册的 StatusAdapter / ProbeableAdapter 一致。
- A2``transport_mode`` 为 ``"stream"``,与 feishu 仅注册 StreamConnector
(无 Puller的实际传输模式一致。
- A3``config_schema`` 含 ``ws_endpoint_path`` 配置项stream_connector
实际读取),不含死字段 ``ws_endpoint``。
- A4``capability_requirements`` 中引用的所有配置键均在 ``config_schema``
中声明F-03 单真相源:声明性字段交叉引用完整)。
测试通过 ``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 feishu_manifest() -> ChannelManifest:
"""加载 feishu 插件真实 manifest.json。"""
plugin_dir = os.path.join(
os.path.dirname(__file__),
"..",
"..",
"..",
"..",
"..",
"package",
"yuxi",
"channels",
"plugins",
"feishu",
)
return load_manifest_from_dir(os.path.abspath(plugin_dir))
# ------------------------------------------------------------------
# A1: status / probeable 能力声明与适配器注册一致
# ------------------------------------------------------------------
@pytest.mark.unit
class TestStatusProbeableCapabilities:
"""A1status / probeable 声明为 true与 entry.py 注册的适配器一致。"""
def test_status_capability_is_true(self, feishu_manifest):
assert feishu_manifest.capabilities.status is True
def test_probeable_capability_is_true(self, feishu_manifest):
assert feishu_manifest.capabilities.probeable is True
# ------------------------------------------------------------------
# A2: transport_mode 声明
# ------------------------------------------------------------------
@pytest.mark.unit
class TestTransportMode:
"""A2transport_mode 为 stream与仅注册 StreamConnector 一致。"""
def test_transport_mode_is_stream(self, feishu_manifest):
assert feishu_manifest.transport_mode == "stream"
# ------------------------------------------------------------------
# A3: ws_endpoint_path 配置项存在ws_endpoint 死字段已移除
# ------------------------------------------------------------------
@pytest.mark.unit
class TestWsEndpointConfigKey:
"""A3config_schema 含 ws_endpoint_path不含死字段 ws_endpoint。"""
def test_ws_endpoint_path_exists_in_config_schema(self, feishu_manifest):
keys = {f.key for f in feishu_manifest.config_schema}
assert "ws_endpoint_path" in keys
def test_ws_endpoint_dead_field_removed(self, feishu_manifest):
keys = {f.key for f in feishu_manifest.config_schema}
assert "ws_endpoint" not in keys
def test_ws_endpoint_path_has_default_value(self, feishu_manifest):
"""ws_endpoint_path 默认值应为飞书标准 WS 端点路径。"""
field = next(f for f in feishu_manifest.config_schema if f.key == "ws_endpoint_path")
assert field.default == "/open-apis/event/v1/get_ws_endpoint"
# ------------------------------------------------------------------
# A4: capability_requirements 引用的配置键均在 config_schema 中声明
# ------------------------------------------------------------------
@pytest.mark.unit
class TestCapabilityRequirementsConsistency:
"""A4capability_requirements 交叉引用完整性校验。
每项 capability_requirements 为 ``(能力字段名, (所需配置键清单))``
所有引用的配置键必须在 config_schema 中声明F-03 单真相源)。
"""
def test_capability_requirements_not_empty(self, feishu_manifest):
assert len(feishu_manifest.capability_requirements) > 0
def test_all_referenced_config_keys_exist_in_schema(self, feishu_manifest):
schema_keys = {f.key for f in feishu_manifest.config_schema}
for _capability, required_keys in feishu_manifest.capability_requirements:
for key in required_keys:
assert key in schema_keys, f"capability_requirements 引用的配置键 '{key}' 未在 config_schema 中声明"
def test_qr_login_requirements_reference_qr_redirect_uri(self, feishu_manifest):
"""supports_qr_login 依赖 enable_qr_login 与 qr_redirect_uri
(与 login_adapter.py 实际读取的配置键一致)。"""
req = dict(feishu_manifest.capability_requirements)
assert "supports_qr_login" in req
required = set(req["supports_qr_login"])
assert "enable_qr_login" in required
assert "qr_redirect_uri" in required
def test_media_capabilities_require_app_credentials(self, feishu_manifest):
"""媒体能力(图片/视频出入站)依赖 app_id 与 app_secret。"""
req = dict(feishu_manifest.capability_requirements)
for cap in (
"supports_image_inbound",
"supports_image_outbound",
"supports_video_outbound",
):
assert cap in req
required = set(req[cap])
assert "app_id" in required
assert "app_secret" in required
def test_lifecycle_probeable_status_have_empty_requirements(self, feishu_manifest):
"""lifecycle / probeable / status 为基础能力,无额外配置依赖。"""
req = dict(feishu_manifest.capability_requirements)
for cap in ("lifecycle", "probeable", "status"):
assert cap in req
assert req[cap] == ()
# ------------------------------------------------------------------
# F-03: qr_redirect_uri 配置键一致性login_adapter 修复验证)
# ------------------------------------------------------------------
@pytest.mark.unit
class TestQrRedirectUriConfigKey:
"""验证 qr_redirect_uri 配置键在 manifest 中声明login_adapter 读取此键)。
login_adapter.py 原 read ``qr_login_redirect_uri``manifest 未声明),
已修正为 ``qr_redirect_uri``(与 manifest 及 wizard_adapter 一致)。
"""
def test_qr_redirect_uri_in_config_schema(self, feishu_manifest):
keys = {f.key for f in feishu_manifest.config_schema}
assert "qr_redirect_uri" in keys
def test_qr_login_redirect_uri_not_in_config_schema(self, feishu_manifest):
"""死键 qr_login_redirect_uri 不应出现在 config_schema。"""
keys = {f.key for f in feishu_manifest.config_schema}
assert "qr_login_redirect_uri" not in keys