完成渠道模块的单元测试目录搭建,新增多个领域模型、端口、中间件、事件、服务以及基础设施层的单元测试文件,同时补充了conftest.py的环境变量配置,完善测试基础环境。
131 lines
5.3 KiB
Python
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()
|