完成渠道模块的单元测试目录搭建,新增多个领域模型、端口、中间件、事件、服务以及基础设施层的单元测试文件,同时补充了conftest.py的环境变量配置,完善测试基础环境。
117 lines
3.8 KiB
Python
117 lines
3.8 KiB
Python
from __future__ import annotations
|
|
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
import pytest
|
|
|
|
from yuxi.channel.worker.outbox_retry import OutboxRetryWorker
|
|
|
|
|
|
class TestOutboxRetryWorker:
|
|
@pytest.fixture
|
|
def mock_outbox_repo(self):
|
|
return AsyncMock()
|
|
|
|
@pytest.fixture
|
|
def mock_adapters(self):
|
|
adapter = AsyncMock()
|
|
adapter.send_message.return_value = MagicMock(success=True)
|
|
return {"web": adapter}
|
|
|
|
@pytest.fixture
|
|
def worker(self, mock_outbox_repo, mock_adapters):
|
|
return OutboxRetryWorker(
|
|
outbox_repo=mock_outbox_repo,
|
|
adapters=mock_adapters,
|
|
poll_interval=1.0,
|
|
)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_start(self, worker):
|
|
await worker.start()
|
|
assert worker.is_running is True
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_stop(self, worker):
|
|
await worker.start()
|
|
await worker.stop()
|
|
assert worker.is_running is False
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_is_running_property(self, worker):
|
|
assert worker.is_running is False
|
|
await worker.start()
|
|
assert worker.is_running is True
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_run_loop(self, worker, mock_outbox_repo):
|
|
entry = MagicMock()
|
|
entry.id = 1
|
|
entry.channel_type = "web"
|
|
entry.payload = {"content": "Hello"}
|
|
entry.retry_count = 0
|
|
mock_outbox_repo.dequeue_pending.return_value = [entry]
|
|
mock_outbox_repo.mark_delivered.return_value = True
|
|
|
|
await worker.start()
|
|
await worker.stop()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_retry_success(self, worker, mock_outbox_repo, mock_adapters):
|
|
entry = MagicMock()
|
|
entry.id = 1
|
|
entry.channel_type = "web"
|
|
entry.payload = {"content": "Hello"}
|
|
entry.retry_count = 0
|
|
mock_outbox_repo.dequeue_pending.return_value = [entry]
|
|
mock_outbox_repo.mark_delivered.return_value = True
|
|
|
|
await worker.start()
|
|
await worker.stop()
|
|
mock_adapters["web"].send_message.assert_awaited_once()
|
|
mock_outbox_repo.mark_delivered.assert_awaited_once_with(1)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_retry_failure(self, worker, mock_outbox_repo, mock_adapters):
|
|
entry = MagicMock()
|
|
entry.id = 1
|
|
entry.channel_type = "web"
|
|
entry.payload = {"content": "Hello"}
|
|
entry.retry_count = 0
|
|
mock_outbox_repo.dequeue_pending.return_value = [entry]
|
|
mock_adapters["web"].send_message.return_value = MagicMock(success=False)
|
|
mock_outbox_repo.mark_failed.return_value = True
|
|
|
|
await worker.start()
|
|
await worker.stop()
|
|
mock_outbox_repo.mark_failed.assert_awaited_once_with(1, "retry")
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_retry_no_adapter(self, worker, mock_outbox_repo):
|
|
entry = MagicMock()
|
|
entry.id = 1
|
|
entry.channel_type = "unknown"
|
|
entry.payload = {"content": "Hello"}
|
|
entry.retry_count = 0
|
|
mock_outbox_repo.dequeue_pending.return_value = [entry]
|
|
mock_outbox_repo.mark_failed.return_value = True
|
|
|
|
await worker.start()
|
|
await worker.stop()
|
|
mock_outbox_repo.mark_failed.assert_awaited_once_with(1, "no adapter")
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_retry_exception(self, worker, mock_outbox_repo, mock_adapters):
|
|
entry = MagicMock()
|
|
entry.id = 1
|
|
entry.channel_type = "web"
|
|
entry.payload = {"content": "Hello"}
|
|
entry.retry_count = 0
|
|
mock_outbox_repo.dequeue_pending.return_value = [entry]
|
|
mock_adapters["web"].send_message.side_effect = Exception("send error")
|
|
mock_outbox_repo.mark_failed.return_value = True
|
|
|
|
await worker.start()
|
|
await worker.stop()
|
|
mock_outbox_repo.mark_failed.assert_awaited_once_with(1, "send error")
|