新增了Twitch、Telegram、Discord、Slack、Mattermost、WeChat、Zalo等多渠道的单元测试用例,覆盖了令牌处理、速率限制、消息去重、会话解析、格式转换、安全策略等模块 同时在测试配置中添加了测试用的OpenAI API密钥环境变量
167 lines
6.2 KiB
Python
167 lines
6.2 KiB
Python
from __future__ import annotations
|
|
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from yuxi.channels.manager import ChannelManager
|
|
from yuxi.channels.registry import ChannelRegistry
|
|
from yuxi.channels.services.runtime_state import RuntimeState
|
|
|
|
|
|
class TestChannelManagerPhases:
|
|
def test_initial_phase(self):
|
|
manager = ChannelManager()
|
|
assert manager.phase == "not_started"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_load_config_sets_phase(self):
|
|
with (
|
|
patch("yuxi.channels.manager._load_builtin_adapters"),
|
|
patch("yuxi.channels.manager._BUILTIN_ADAPTERS", {}),
|
|
patch("yuxi.config") as mock_config,
|
|
):
|
|
mock_config.channels = {}
|
|
manager = ChannelManager()
|
|
await manager.load_config()
|
|
assert manager.phase == "config_loaded"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_load_config_idempotent(self):
|
|
with (
|
|
patch("yuxi.channels.manager._load_builtin_adapters"),
|
|
patch("yuxi.channels.manager._BUILTIN_ADAPTERS", {}),
|
|
patch("yuxi.config") as mock_config,
|
|
):
|
|
mock_config.channels = {}
|
|
manager = ChannelManager()
|
|
await manager.load_config()
|
|
|
|
mock_config.channels = {"wx": {"enabled": True}}
|
|
await manager.load_config()
|
|
assert manager._channels_config == {}
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_initialize_sets_fully_running(self):
|
|
with (
|
|
patch("yuxi.channels.manager._load_builtin_adapters"),
|
|
patch("yuxi.channels.manager._BUILTIN_ADAPTERS", {}),
|
|
patch("yuxi.config") as mock_config,
|
|
):
|
|
mock_config.channels = {}
|
|
mock_pg = MagicMock()
|
|
mock_db = AsyncMock()
|
|
mock_pg.__aenter__ = AsyncMock(return_value=mock_db)
|
|
mock_pg.__aexit__ = AsyncMock(return_value=None)
|
|
with (
|
|
patch(
|
|
"yuxi.channels.manager.pg_manager.get_async_session_context",
|
|
return_value=mock_pg,
|
|
),
|
|
patch.object(ChannelManager, "_ensure_virtual_department", AsyncMock()),
|
|
patch.object(ChannelManager, "_ensure_default_agent_config", AsyncMock()),
|
|
):
|
|
manager = ChannelManager()
|
|
await manager.initialize()
|
|
assert manager.phase == "fully_running"
|
|
assert manager._initialized is True
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_start_subscriptions_only_after_channels_started(self):
|
|
manager = ChannelManager()
|
|
assert manager.phase == "not_started"
|
|
|
|
await manager.start_subscriptions()
|
|
assert manager.phase == "not_started"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_prepare_bootstrap_only_after_config_loaded(self):
|
|
manager = ChannelManager()
|
|
await manager.prepare_bootstrap()
|
|
assert manager.phase == "not_started"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_start_channels_allows_from_config_loaded_or_bootstrapped(self):
|
|
with (
|
|
patch("yuxi.channels.manager._load_builtin_adapters"),
|
|
patch("yuxi.channels.manager._BUILTIN_ADAPTERS", {}),
|
|
patch("yuxi.config") as mock_config,
|
|
):
|
|
mock_config.channels = {}
|
|
registry = ChannelRegistry()
|
|
manager = ChannelManager(registry=registry)
|
|
|
|
await manager.load_config()
|
|
assert manager.phase == "config_loaded"
|
|
|
|
await manager.start_channels()
|
|
assert manager.phase == "channels_started"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_startup_idempotent(self):
|
|
with (
|
|
patch("yuxi.channels.manager._load_builtin_adapters"),
|
|
patch("yuxi.channels.manager._BUILTIN_ADAPTERS", {}),
|
|
patch("yuxi.config") as mock_config,
|
|
):
|
|
mock_config.channels = {}
|
|
manager = ChannelManager()
|
|
|
|
manager._initialized = True
|
|
await manager.startup()
|
|
assert manager.phase == "not_started"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_startup_creates_context(self):
|
|
with (
|
|
patch("yuxi.channels.manager._load_builtin_adapters"),
|
|
patch("yuxi.channels.manager._BUILTIN_ADAPTERS", {}),
|
|
patch("yuxi.config") as mock_config,
|
|
):
|
|
mock_config.channels = {}
|
|
mock_pg = MagicMock()
|
|
mock_db = AsyncMock()
|
|
mock_pg.__aenter__ = AsyncMock(return_value=mock_db)
|
|
mock_pg.__aexit__ = AsyncMock(return_value=None)
|
|
with (
|
|
patch(
|
|
"yuxi.channels.manager.pg_manager.get_async_session_context",
|
|
return_value=mock_pg,
|
|
),
|
|
patch.object(ChannelManager, "_ensure_virtual_department", AsyncMock()),
|
|
patch.object(ChannelManager, "_ensure_default_agent_config", AsyncMock()),
|
|
):
|
|
manager = ChannelManager()
|
|
await manager.startup()
|
|
assert manager.context is not None
|
|
assert manager.broadcaster is not None
|
|
assert manager.watcher is not None
|
|
assert manager.doctor is not None
|
|
assert manager.phase == "fully_running"
|
|
|
|
def test_new_properties_default(self):
|
|
manager = ChannelManager()
|
|
assert manager.context is None
|
|
assert manager.broadcaster is None
|
|
assert manager.watcher is None
|
|
assert manager.doctor is None
|
|
assert isinstance(manager.runtime_state, RuntimeState)
|
|
assert manager.runtime_state.phase == "not_started"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_diagnose_channels_no_doctor(self):
|
|
manager = ChannelManager()
|
|
issues = await manager.diagnose_channels()
|
|
assert issues == []
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_auto_fix_no_doctor(self):
|
|
manager = ChannelManager()
|
|
result = await manager.auto_fix_channel(None)
|
|
assert result is False
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_reload_config_no_watcher(self):
|
|
manager = ChannelManager()
|
|
result = await manager.reload_config_now()
|
|
assert result == {} |