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

172 lines
6.8 KiB
Python
Raw Normal View History

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