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

147 lines
6.0 KiB
Python

from __future__ import annotations
from unittest.mock import AsyncMock, MagicMock
import pytest
from yuxi.channel.application.service.delivery_service import DeliveryService
from yuxi.channel.domain.exception.agent_crash_error import AgentCrashError
from yuxi.channel.domain.model.message.dispatch_result import DispatchResult, SendResult
from yuxi.channel.domain.model.session.channel_session import ChannelSession
class TestDeliveryService:
@pytest.fixture
def mock_adapters(self):
adapter = AsyncMock()
adapter.send_message.return_value = SendResult(success=True)
adapter.capabilities.media = True
return {"web": adapter}
@pytest.fixture
def mock_message_repo(self):
return AsyncMock()
@pytest.fixture
def mock_outbox_repo(self):
return AsyncMock()
@pytest.fixture
def mock_event_publisher(self):
return AsyncMock()
@pytest.fixture
def mock_agent_port(self):
agent = AsyncMock()
agent.stream_chat.return_value = "Hello, user!"
return agent
@pytest.fixture
def delivery_service(self, mock_adapters, mock_message_repo, mock_outbox_repo, mock_event_publisher, mock_agent_port):
return DeliveryService(
adapters=mock_adapters,
message_repo=mock_message_repo,
outbox_repo=mock_outbox_repo,
event_publisher=mock_event_publisher,
agent_port=mock_agent_port,
)
@pytest.fixture
def sample_session(self):
return ChannelSession(
id=1,
thread_id="thread123",
user_id="user1",
agent_id="1",
channel_type="web",
channel_session_key="key1",
status="active",
title=None,
)
@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": {"deliver": True},
"attachments": [{"url": "http://example.com/image.png", "media_type": "image"}],
}
@pytest.mark.asyncio
async def test_deliver_success(self, delivery_service, sample_payload, sample_session):
result = await delivery_service.deliver(sample_payload, sample_session)
assert result.success is True
@pytest.mark.asyncio
async def test_deliver_no_deliver(self, delivery_service, mock_message_repo, sample_payload, sample_session):
sample_payload["metadata"]["deliver"] = False
result = await delivery_service.deliver(sample_payload, sample_session)
assert result.success is True
mock_message_repo.save_assistant_message.assert_awaited_once()
@pytest.mark.asyncio
async def test_deliver_agent_crash(self, delivery_service, mock_agent_port, sample_payload, sample_session):
mock_agent_port.stream_chat.side_effect = AgentCrashError("agent crashed")
result = await delivery_service.deliver(sample_payload, sample_session)
assert result.success is False
assert result.error == "agent_crash"
@pytest.mark.asyncio
async def test_deliver_send_failure(self, delivery_service, mock_adapters, mock_outbox_repo, sample_payload, sample_session):
mock_adapters["web"].send_message.return_value = SendResult(success=False, error="send failed")
result = await delivery_service.deliver(sample_payload, sample_session)
mock_outbox_repo.enqueue.assert_awaited_once()
@pytest.mark.asyncio
async def test_deliver_no_adapter(self, delivery_service, sample_payload, sample_session):
sample_payload["channel_type"] = "unknown"
result = await delivery_service.deliver(sample_payload, sample_session)
assert result.success is False
assert "no adapter" in result.error
@pytest.mark.asyncio
async def test_deliver_publishes_event(self, delivery_service, mock_event_publisher, sample_payload, sample_session):
await delivery_service.deliver(sample_payload, sample_session)
mock_event_publisher.publish.assert_awaited_once()
@pytest.mark.asyncio
async def test_deliver_saves_message(self, delivery_service, mock_message_repo, sample_payload, sample_session):
await delivery_service.deliver(sample_payload, sample_session)
mock_message_repo.save_assistant_message.assert_awaited_once()
@pytest.mark.asyncio
async def test_deliver_with_media(self, delivery_service, mock_adapters, sample_payload, sample_session):
await delivery_service.deliver(sample_payload, sample_session)
mock_adapters["web"].send_media.assert_awaited_once()
@pytest.mark.asyncio
async def test_deliver_typing_indicator(self, delivery_service, mock_adapters, sample_payload, sample_session):
mock_adapters["web"].capabilities.typing = True
await delivery_service.deliver(sample_payload, sample_session)
@pytest.mark.asyncio
async def test_call_agent_timeout(self, delivery_service, mock_agent_port, sample_payload, sample_session):
mock_agent_port.stream_chat.side_effect = TimeoutError("timeout")
with pytest.raises(AgentCrashError):
await delivery_service._call_agent(sample_payload, sample_session, "Hello", "web", "msg123", "trace123")
@pytest.mark.asyncio
async def test_try_send_success(self, delivery_service, mock_adapters, sample_session):
result = await delivery_service._try_send(
mock_adapters["web"], "thread123", "Hello", "web", "trace123", "msg123"
)
assert result.success is True
@pytest.mark.asyncio
async def test_try_send_failure(self, delivery_service, mock_adapters, sample_session):
mock_adapters["web"].send_message.side_effect = Exception("send error")
result = await delivery_service._try_send(
mock_adapters["web"], "thread123", "Hello", "web", "trace123", "msg123"
)
assert result.success is False