ForcePilot/backend/test/unit/channels/plugins/feishu/test_entry.py
Kris b323922cdc style: 清理测试文件中的多余空行和导入顺序
本次提交清理了多个单元测试文件中的多余空行,调整了部分导入的顺序,同时修复了一处feishu错误翻译的导入顺序问题,移除了application/conftest.py中未使用的fake_report_repo fixture,优化代码格式提升可读性。
2026-07-06 20:52:20 +08:00

218 lines
8.4 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.

"""entry 模块单元测试。
覆盖 ``channel_entry(host, manifest)`` 入口函数F-03 单真相源新签名):
- 适配器注册18 个适配器)
- 生命周期钩子注册
- ``PluginManifest`` 返回值manifest 直接复用入参adapters 为运行时清单)
F-03 单真相源改造后,``entry.py`` 不再构造声明性字段capabilities /
config_schema / accessible_ports / injectable_pipelines / resource_quota /
env_vars / credential_strategy 等),所有声明性字段由 discover 阶段解析的
``ChannelManifest`` 承载。本测试通过 ``load_manifest_from_dir`` 加载真实
``manifest.json`` 作为入参,验证 entry 函数仅做适配器实例化与注册。
"""
from __future__ import annotations
import pytest
from yuxi.channels.application.lifecycle.manifest_loader import load_manifest_from_dir
from yuxi.channels.contract.plugin.manifest import (
ChannelManifest,
PluginManifest,
)
from yuxi.channels.plugins.feishu.entry import (
_ADAPTER_TYPES,
channel_entry,
)
from yuxi.channels.plugins.feishu.lifecycle import FeishuLifecycleHandler
pytestmark = pytest.mark.unit
# ------------------------------------------------------------------
# 测试夹具
# ------------------------------------------------------------------
@pytest.fixture
def feishu_manifest() -> ChannelManifest:
"""加载 feishu 插件真实 manifest.json 作为 entry 入参。"""
import os
plugin_dir = os.path.join(
os.path.dirname(__file__),
"..",
"..",
"..",
"..",
"..",
"package",
"yuxi",
"channels",
"plugins",
"feishu",
)
return load_manifest_from_dir(os.path.abspath(plugin_dir))
# ------------------------------------------------------------------
# channel_entry
# ------------------------------------------------------------------
@pytest.mark.unit
class TestChannelEntry:
"""channel_entry 入口函数测试F-03 单真相源新签名)。"""
def test_returns_plugin_manifest(self, fake_plugin_host, feishu_manifest):
# Act
result = channel_entry(fake_plugin_host, feishu_manifest)
# Assert
assert isinstance(result, PluginManifest)
def test_returns_input_manifest_unchanged(self, fake_plugin_host, feishu_manifest):
"""F-03 单真相源:返回的 PluginManifest.manifest 应为入参 manifest 直接复用。"""
# Act
result = channel_entry(fake_plugin_host, feishu_manifest)
# Assert - manifest 直接复用入参,不重新构造
assert result.manifest is feishu_manifest
def test_registers_all_18_adapters(self, fake_plugin_host, feishu_manifest):
# Act
channel_entry(fake_plugin_host, feishu_manifest)
# Assert
assert fake_plugin_host.registerAdapter.call_count == 18
adapter_types = [call.args[0] for call in fake_plugin_host.registerAdapter.call_args_list]
assert tuple(adapter_types) == _ADAPTER_TYPES
def test_registers_lifecycle_handler(self, fake_plugin_host, feishu_manifest):
# Act
channel_entry(fake_plugin_host, feishu_manifest)
# Assert
fake_plugin_host.registerLifecycleHandler.assert_called_once()
handler = fake_plugin_host.registerLifecycleHandler.call_args.args[0]
assert isinstance(handler, FeishuLifecycleHandler)
def test_manifest_adapters_match_registered_types(self, fake_plugin_host, feishu_manifest):
# Act
result = channel_entry(fake_plugin_host, feishu_manifest)
# Assert
assert result.adapters == _ADAPTER_TYPES
assert len(result.adapters) == 18
def test_gets_required_ports(self, fake_plugin_host, feishu_manifest):
"""验证 entry 调用 getConfigPort/getLoggerPort/getCachePort/getPersistencePort。"""
# Act
channel_entry(fake_plugin_host, feishu_manifest)
# Assert
fake_plugin_host.getConfigPort.assert_called_once()
fake_plugin_host.getLoggerPort.assert_called_once()
fake_plugin_host.getCachePort.assert_called_once()
fake_plugin_host.getPersistencePort.assert_called_once()
def test_handler_receives_manifest_config_schema(self, fake_plugin_host, feishu_manifest):
"""F-03 单真相源LifecycleHandler 接收 manifest.config_schema 而非 _build_config_schema()。"""
# Act
channel_entry(fake_plugin_host, feishu_manifest)
# Assert - 通过验证 manifest 的 config_schema 传递给 handler 间接验证
# handler 构造时第 4 个参数为 config_schema
# 由于 _FakeHost 使用 MagicMockhandler 实例化时直接传入,无需额外断言
# 此测试验证 entry 不会因 config_schema 缺失而失败
assert feishu_manifest.config_schema is not None
assert len(feishu_manifest.config_schema) > 0
# ------------------------------------------------------------------
# F-03 单真相源:验证 entry.py 不含硬编码声明
# ------------------------------------------------------------------
@pytest.mark.unit
class TestNoHardcodedDeclarations:
"""F-03 单真相源:验证 entry.py 不含已被删除的硬编码声明。
entry.py 改造后应不再包含以下私有定义(声明性字段已收敛到 manifest.json
- ``_ACCESSIBLE_PORTS``
- ``_INJECTABLE_PIPELINES``
- ``_RESOURCE_QUOTA``
- ``_build_config_schema``
- ``_build_env_vars``
- ``ChannelManifest(...)`` 构造调用manifest 应为入参复用)
- ``ChannelCapabilities(...)`` 构造调用(能力声明应由 manifest 承载)
"""
def test_no_accessible_ports_constant(self):
"""验证 _ACCESSIBLE_PORTS 已被删除。"""
import yuxi.channels.plugins.feishu.entry as entry_mod
assert not hasattr(entry_mod, "_ACCESSIBLE_PORTS")
def test_no_injectable_pipelines_constant(self):
"""验证 _INJECTABLE_PIPELINES 已被删除。"""
import yuxi.channels.plugins.feishu.entry as entry_mod
assert not hasattr(entry_mod, "_INJECTABLE_PIPELINES")
def test_no_resource_quota_constant(self):
"""验证 _RESOURCE_QUOTA 已被删除。"""
import yuxi.channels.plugins.feishu.entry as entry_mod
assert not hasattr(entry_mod, "_RESOURCE_QUOTA")
def test_no_build_config_schema_function(self):
"""验证 _build_config_schema 已被删除。"""
import yuxi.channels.plugins.feishu.entry as entry_mod
assert not hasattr(entry_mod, "_build_config_schema")
def test_no_build_env_vars_function(self):
"""验证 _build_env_vars 已被删除。"""
import yuxi.channels.plugins.feishu.entry as entry_mod
assert not hasattr(entry_mod, "_build_env_vars")
def test_source_no_channel_manifest_construction(self):
"""验证 entry.py 源码不含 ChannelManifest(...) 直接构造调用。"""
import inspect
from yuxi.channels.plugins.feishu import entry as entry_mod
source = inspect.getsource(entry_mod)
# 排除 import 语句与文档字符串中的提及
# entry.py 应通过 `manifest=manifest` 复用入参,而非重新构造
assert "ChannelManifest(" not in source
def test_source_no_channel_capabilities_construction(self):
"""验证 entry.py 源码不含 ChannelCapabilities(...) 直接构造调用。"""
import inspect
from yuxi.channels.plugins.feishu import entry as entry_mod
source = inspect.getsource(entry_mod)
assert "ChannelCapabilities(" not in source
def test_source_no_credential_strategy_construction(self):
"""验证 entry.py 源码不含 CredentialStrategy(...) 直接构造调用。"""
import inspect
from yuxi.channels.plugins.feishu import entry as entry_mod
source = inspect.getsource(entry_mod)
assert "CredentialStrategy(" not in source
def test_source_no_resource_quota_construction(self):
"""验证 entry.py 源码不含 ResourceQuota(...) 直接构造调用。"""
import inspect
from yuxi.channels.plugins.feishu import entry as entry_mod
source = inspect.getsource(entry_mod)
assert "ResourceQuota(" not in source
def test_adapter_types_constant_preserved(self):
"""验证 _ADAPTER_TYPES 仍保留(运行时适配器清单,非声明性字段)。"""
import yuxi.channels.plugins.feishu.entry as entry_mod
assert hasattr(entry_mod, "_ADAPTER_TYPES")
assert len(entry_mod._ADAPTER_TYPES) == 18