from __future__ import annotations import asyncio import pytest import respx from httpx import Response from app.clients.mci_server_client import MciServerClient from app.models.event import EventStatus, EventType, OutboundEvent from app.repositories.event_store import EventStore from app.services.event_publisher import EventPublisher @pytest.fixture def publisher(tmp_path): db_path = tmp_path / "events.db" store = EventStore(str(db_path)) client = MciServerClient("http://mock-mci-server/msg-platform") pub = EventPublisher(store, client, max_retry=2, base_delay_seconds=1, interval_seconds=0.1) yield pub @respx.mock def test_publish_delivers_event(publisher): route = respx.post("http://mock-mci-server/msg-platform/api/v1/channel-account/event").mock( return_value=Response(200) ) event = OutboundEvent(EventType.HEARTBEAT, "bot-001", "rescue", {"online_status": "online"}) publisher.store.save(event) async def run(): await publisher._tick() asyncio.run(run()) assert route.called pending = publisher.store.fetch_pending(limit=10) assert len(pending) == 0 @respx.mock def test_publish_retries_then_dead_letter(publisher): route = respx.post("http://mock-mci-server/msg-platform/api/v1/channel-account/event").mock( return_value=Response(500) ) event = OutboundEvent(EventType.HEARTBEAT, "bot-001", "rescue", {}) publisher.store.save(event) async def run(): await publisher._tick() asyncio.run(run()) assert route.call_count == 1 updated = publisher.store.get(event.event_id) assert updated is not None assert updated.status == EventStatus.RETRYING assert updated.attempt_count == 1