from __future__ import annotations from unittest.mock import AsyncMock, MagicMock import pytest from yuxi.channel.application.service.inbound_service import InboundService from yuxi.channel.domain.model.message.unified_message import UnifiedMessage from yuxi.channel.domain.model.message.peer import Peer from yuxi.channel.domain.service.message_context import MessageContext from yuxi.channel.domain.service.pipeline import Pipeline class TestInboundService: @pytest.fixture def mock_pipeline(self): pipeline = MagicMock(spec=Pipeline) pipeline.execute = AsyncMock(return_value=MagicMock(spec=MessageContext)) return pipeline @pytest.fixture def mock_event_publisher(self): return AsyncMock() @pytest.fixture def mock_message_log_repo(self): return AsyncMock() @pytest.fixture def sample_message(self): return UnifiedMessage( message_id="msg123", channel_type="web", sender=Peer(id="user1", name="User"), content="Hello", ) @pytest.mark.asyncio async def test_submit_basic(self, mock_pipeline, sample_message): service = InboundService(mock_pipeline) result = await service.submit(sample_message, channel_type="web") mock_pipeline.execute.assert_awaited_once() assert result is not None @pytest.mark.asyncio async def test_submit_with_trace_id(self, mock_pipeline, sample_message): service = InboundService(mock_pipeline) result = await service.submit(sample_message, channel_type="web", trace_id="trace123") ctx = mock_pipeline.execute.call_args[0][0] assert ctx.trace_id == "trace123" @pytest.mark.asyncio async def test_submit_creates_log(self, mock_pipeline, sample_message, mock_message_log_repo): service = InboundService(mock_pipeline, message_log_repo=mock_message_log_repo) await service.submit(sample_message, channel_type="web") mock_message_log_repo.create_log.assert_awaited_once() @pytest.mark.asyncio async def test_submit_publishes_event(self, mock_pipeline, sample_message, mock_event_publisher): service = InboundService(mock_pipeline, event_publisher=mock_event_publisher) await service.submit(sample_message, channel_type="web") mock_event_publisher.publish.assert_not_awaited() @pytest.mark.asyncio async def test_submit_aborted_message(self, mock_pipeline, sample_message, mock_event_publisher): aborted_ctx = MagicMock(spec=MessageContext) aborted_ctx.is_aborted = True aborted_ctx.is_skipped = False aborted_ctx.message = sample_message aborted_ctx.trace_id = "trace123" aborted_ctx.abort_reason = "test abort" aborted_ctx.abort_code = "TEST_ABORT" mock_pipeline.execute = AsyncMock(return_value=aborted_ctx) service = InboundService(mock_pipeline, event_publisher=mock_event_publisher) await service.submit(sample_message, channel_type="web") mock_event_publisher.publish.assert_awaited_once() @pytest.mark.asyncio async def test_submit_skipped_message(self, mock_pipeline, sample_message, mock_event_publisher): skipped_ctx = MagicMock(spec=MessageContext) skipped_ctx.is_aborted = False skipped_ctx.is_skipped = True skipped_ctx.message = sample_message skipped_ctx.trace_id = "trace123" mock_pipeline.execute = AsyncMock(return_value=skipped_ctx) service = InboundService(mock_pipeline, event_publisher=mock_event_publisher) await service.submit(sample_message, channel_type="web") mock_event_publisher.publish.assert_not_awaited() @pytest.mark.asyncio async def test_submit_log_exception_handling(self, mock_pipeline, sample_message, mock_message_log_repo): mock_message_log_repo.create_log.side_effect = Exception("log error") service = InboundService(mock_pipeline, message_log_repo=mock_message_log_repo) result = await service.submit(sample_message, channel_type="web") assert result is not None @pytest.mark.asyncio async def test_submit_event_exception_handling(self, mock_pipeline, sample_message, mock_event_publisher): mock_event_publisher.publish.side_effect = Exception("publish error") aborted_ctx = MagicMock(spec=MessageContext) aborted_ctx.is_aborted = True aborted_ctx.is_skipped = False aborted_ctx.message = sample_message aborted_ctx.trace_id = "trace123" aborted_ctx.abort_reason = "test" aborted_ctx.abort_code = "TEST" mock_pipeline.execute = AsyncMock(return_value=aborted_ctx) service = InboundService(mock_pipeline, event_publisher=mock_event_publisher) result = await service.submit(sample_message, channel_type="web") assert result is not None