ForcePilot/backend/test/unit/channels/test_registry.py
Kris 3264900bc9 test: 新增多渠道单元测试用例并配置测试环境变量
新增了Twitch、Telegram、Discord、Slack、Mattermost、WeChat、Zalo等多渠道的单元测试用例,覆盖了令牌处理、速率限制、消息去重、会话解析、格式转换、安全策略等模块
同时在测试配置中添加了测试用的OpenAI API密钥环境变量
2026-05-12 00:56:47 +08:00

133 lines
4.3 KiB
Python

from __future__ import annotations
from unittest.mock import MagicMock
import pytest
from yuxi.channels.base import BaseChannelAdapter
from yuxi.channels.registry import ChannelRegistry, _BUILTIN_ADAPTERS, register_builtin_adapter
def _make_adapter_cls(channel_id: str) -> type[BaseChannelAdapter]:
cid = channel_id
class _TestAdapter(BaseChannelAdapter):
channel_id = cid
channel_type = MagicMock(value="test")
async def connect(self) -> None: ...
async def disconnect(self) -> None: ...
async def send(self, response): ...
def normalize_inbound(self, raw): ...
def format_outbound(self, response): ...
async def health_check(self): ...
return _TestAdapter
class TestChannelRegistry:
def test_init_empty(self):
registry = ChannelRegistry()
assert registry.list_channels() == []
def test_register_and_get(self):
registry = ChannelRegistry()
cls = _make_adapter_cls("test_chan")
registry.register("test_chan", cls)
assert registry.get("test_chan") is cls
def test_get_nonexistent(self):
registry = ChannelRegistry()
assert registry.get("nonexistent") is None
def test_list_channels(self):
registry = ChannelRegistry()
registry.register("a", _make_adapter_cls("a"))
registry.register("b", _make_adapter_cls("b"))
channels = registry.list_channels()
assert set(channels) == {"a", "b"}
def test_unregister(self):
registry = ChannelRegistry()
cls = _make_adapter_cls("test_chan")
registry.register("test_chan", cls)
registry.unregister("test_chan")
assert registry.get("test_chan") is None
def test_unregister_nonexistent_does_nothing(self):
registry = ChannelRegistry()
registry.unregister("nonexistent")
def test_register_overwrites(self):
registry = ChannelRegistry()
cls1 = _make_adapter_cls("test_chan")
cls2 = _make_adapter_cls("test_chan")
registry.register("test_chan", cls1)
registry.register("test_chan", cls2)
assert registry.get("test_chan") is cls2
def test_list_channels_includes_builtin(self):
registry = ChannelRegistry()
registry.register("custom_chan", _make_adapter_cls("custom_chan"))
saved = dict(_BUILTIN_ADAPTERS)
_BUILTIN_ADAPTERS.clear()
_BUILTIN_ADAPTERS["builtin_chan"] = _make_adapter_cls("builtin_chan")
channels = set(registry.list_channels())
_BUILTIN_ADAPTERS.clear()
_BUILTIN_ADAPTERS.update(saved)
assert "custom_chan" in channels
assert "builtin_chan" in channels
def test_get_falls_back_to_builtin(self):
registry = ChannelRegistry()
cls = _make_adapter_cls("builtin_chan")
saved = dict(_BUILTIN_ADAPTERS)
_BUILTIN_ADAPTERS.clear()
_BUILTIN_ADAPTERS["builtin_chan"] = cls
result = registry.get("builtin_chan")
_BUILTIN_ADAPTERS.clear()
_BUILTIN_ADAPTERS.update(saved)
assert result is cls
def test_registered_takes_priority_over_builtin(self):
registry = ChannelRegistry()
cls1 = _make_adapter_cls("test_chan")
cls2 = _make_adapter_cls("test_chan")
registry.register("test_chan", cls1)
saved = dict(_BUILTIN_ADAPTERS)
_BUILTIN_ADAPTERS.clear()
_BUILTIN_ADAPTERS["test_chan"] = cls2
result = registry.get("test_chan")
_BUILTIN_ADAPTERS.clear()
_BUILTIN_ADAPTERS.update(saved)
assert result is cls1
class TestBuiltinAdapterRegistration:
def teardown_method(self):
_BUILTIN_ADAPTERS.pop("_test_reg_adapter", None)
def test_register_builtin_adapter_decorator(self):
@register_builtin_adapter
class _TestRegAdapter(BaseChannelAdapter):
channel_id = "_test_reg_adapter"
channel_type = MagicMock()
async def connect(self) -> None: ...
async def disconnect(self) -> None: ...
async def send(self, response): ...
def normalize_inbound(self, raw): ...
def format_outbound(self, response): ...
async def health_check(self): ...
assert "_test_reg_adapter" in _BUILTIN_ADAPTERS
assert issubclass(_BUILTIN_ADAPTERS["_test_reg_adapter"], BaseChannelAdapter)