ForcePilot/backend/test/unit/channel/startup/test_dispatch_service.py
Kris 6c95dc006a test: 新增渠道模块全链路单元测试用例与目录结构
完成渠道模块的单元测试目录搭建,新增多个领域模型、端口、中间件、事件、服务以及基础设施层的单元测试文件,同时补充了conftest.py的环境变量配置,完善测试基础环境。
2026-05-30 21:55:35 +08:00

131 lines
5.3 KiB
Python

from __future__ import annotations
from unittest.mock import AsyncMock, MagicMock
import pytest
from yuxi.channel.application.service.dispatch_service import DispatchService
from yuxi.channel.domain.exception.agent_crash_error import AgentCrashError
from yuxi.channel.domain.exception.recoverable_error import RecoverableError
from yuxi.channel.domain.model.message.dispatch_result import DispatchResult
class TestDispatchService:
@pytest.fixture
def mock_content_filter(self):
filter_mock = AsyncMock()
filter_mock.check.return_value = MagicMock(passed=True, violations=[], masked_fields=[])
return filter_mock
@pytest.fixture
def mock_bot_loop_guard(self):
guard = AsyncMock()
guard.check.return_value = True
return guard
@pytest.fixture
def mock_circuit_breaker(self):
cb = AsyncMock()
cb.is_available.return_value = True
return cb
@pytest.fixture
def mock_session_resolver(self):
resolver = AsyncMock()
session = MagicMock()
session.thread_id = "thread123"
session.agent_id = "1"
resolver.resolve.return_value = session
return resolver
@pytest.fixture
def mock_delivery_service(self):
delivery = AsyncMock()
delivery.deliver.return_value = DispatchResult(success=True, message_id="msg123")
return delivery
@pytest.fixture
def dispatch_service(self, mock_content_filter, mock_bot_loop_guard, mock_circuit_breaker, mock_session_resolver, mock_delivery_service):
return DispatchService(
content_filter=mock_content_filter,
bot_loop_guard=mock_bot_loop_guard,
circuit_breaker=mock_circuit_breaker,
session_resolver=mock_session_resolver,
delivery_service=mock_delivery_service,
)
@pytest.fixture
def sample_payload(self):
return {
"message_id": "msg123",
"channel_type": "web",
"content": "Hello",
"sender_id": "user1",
"session_id": "session123",
"trace_id": "trace123",
"metadata": {"is_group": False},
}
@pytest.mark.asyncio
async def test_dispatch_success(self, dispatch_service, sample_payload):
result = await dispatch_service.dispatch(sample_payload)
assert isinstance(result, DispatchResult)
@pytest.mark.asyncio
async def test_dispatch_content_blocked(self, dispatch_service, mock_content_filter, sample_payload):
mock_content_filter.check.return_value = MagicMock(passed=False, violations=["badword"], masked_fields=[])
result = await dispatch_service.dispatch(sample_payload)
assert result.success is True
@pytest.mark.asyncio
async def test_dispatch_bot_loop_detected(self, dispatch_service, mock_bot_loop_guard, sample_payload):
mock_bot_loop_guard.check.return_value = False
result = await dispatch_service.dispatch(sample_payload)
assert result.success is True
@pytest.mark.asyncio
async def test_dispatch_session_none(self, dispatch_service, mock_session_resolver, sample_payload):
mock_session_resolver.resolve.return_value = None
result = await dispatch_service.dispatch(sample_payload)
assert result.success is False
assert result.error == "session_error"
@pytest.mark.asyncio
async def test_dispatch_circuit_open(self, dispatch_service, mock_circuit_breaker, sample_payload):
mock_circuit_breaker.is_available.return_value = False
result = await dispatch_service.dispatch(sample_payload)
assert result.success is False
assert result.error == "circuit_open"
@pytest.mark.asyncio
async def test_dispatch_delivery_success(self, dispatch_service, mock_delivery_service, sample_payload):
result = await dispatch_service.dispatch(sample_payload)
mock_circuit_breaker.record_success.assert_awaited_once()
@pytest.mark.asyncio
async def test_dispatch_agent_crash(self, dispatch_service, mock_delivery_service, sample_payload):
mock_delivery_service.deliver.side_effect = AgentCrashError("agent crashed")
with pytest.raises(AgentCrashError):
await dispatch_service.dispatch(sample_payload)
mock_circuit_breaker.record_failure.assert_awaited_once()
@pytest.mark.asyncio
async def test_dispatch_recoverable_error(self, dispatch_service, mock_delivery_service, sample_payload):
mock_delivery_service.deliver.side_effect = RecoverableError("recoverable")
with pytest.raises(RecoverableError):
await dispatch_service.dispatch(sample_payload)
@pytest.mark.asyncio
async def test_dispatch_generic_error(self, dispatch_service, mock_delivery_service, sample_payload):
mock_delivery_service.deliver.side_effect = Exception("generic error")
result = await dispatch_service.dispatch(sample_payload)
assert result.success is False
mock_circuit_breaker.record_failure.assert_awaited_once()
@pytest.mark.asyncio
async def test_dispatch_with_metrics(self, dispatch_service, sample_payload):
metrics = AsyncMock()
dispatch_service._metrics = metrics
await dispatch_service.dispatch(sample_payload)
metrics.record_worker_dispatch_total.assert_awaited()