"""Tests for the event broadcaster (infrastructure.events.broadcaster).""" import pytest from unittest.mock import AsyncMock, MagicMock, patch from dataclasses import dataclass from enum import Enum from infrastructure.events.broadcaster import ( EventBroadcaster, event_broadcaster, get_event_icon, get_event_label, format_event_for_display, EVENT_ICONS, EVENT_LABELS, ) # ── Fake EventLogEntry for testing ────────────────────────────────────────── class FakeEventType(Enum): TASK_CREATED = "task.created" TASK_ASSIGNED = "task.assigned" BID_SUBMITTED = "bid.submitted" AGENT_JOINED = "agent.joined" SYSTEM_INFO = "system.info" @dataclass class FakeEventLogEntry: id: str = "evt-abc123" event_type: FakeEventType = FakeEventType.TASK_CREATED source: str = "test" task_id: str = "task-1" agent_id: str = "agent-1" timestamp: str = "2026-03-06T12:00:00Z" data: dict = None def __post_init__(self): if self.data is None: self.data = {} class TestEventBroadcaster: """Test EventBroadcaster class.""" def test_init(self): b = EventBroadcaster() assert b._ws_manager is None async def test_broadcast_no_ws_manager(self): b = EventBroadcaster() # _get_ws_manager returns None => returns 0 count = await b.broadcast(FakeEventLogEntry()) assert count == 0 async def test_broadcast_with_ws_manager(self): b = EventBroadcaster() mock_ws = MagicMock() mock_ws.broadcast_json = AsyncMock(return_value=3) b._ws_manager = mock_ws event = FakeEventLogEntry() count = await b.broadcast(event) assert count == 3 mock_ws.broadcast_json.assert_awaited_once() # Verify payload structure payload = mock_ws.broadcast_json.call_args[0][0] assert payload["type"] == "event" assert payload["payload"]["id"] == "evt-abc123" assert payload["payload"]["event_type"] == "task.created" async def test_broadcast_ws_error_returns_zero(self): b = EventBroadcaster() mock_ws = MagicMock() mock_ws.broadcast_json = AsyncMock(side_effect=RuntimeError("ws down")) b._ws_manager = mock_ws count = await b.broadcast(FakeEventLogEntry()) assert count == 0 def test_broadcast_sync_no_loop(self): """broadcast_sync should not crash when no event loop is running.""" b = EventBroadcaster() # This should silently pass (no event loop) b.broadcast_sync(FakeEventLogEntry()) class TestEventIcons: """Test icon/label lookup functions.""" def test_known_icon(self): assert get_event_icon("task.created") == "📝" assert get_event_icon("agent.joined") == "🟢" def test_unknown_icon_returns_bullet(self): assert get_event_icon("nonexistent") == "•" def test_known_label(self): assert get_event_label("task.created") == "New task" assert get_event_label("task.failed") == "Task failed" def test_unknown_label_returns_type(self): assert get_event_label("custom.event") == "custom.event" def test_all_icons_have_labels(self): """Every icon key should also have a label.""" for key in EVENT_ICONS: assert key in EVENT_LABELS, f"Missing label for icon key: {key}" class TestFormatEventForDisplay: """Test format_event_for_display helper.""" def test_task_created_truncates_description(self): event = FakeEventLogEntry( event_type=FakeEventType.TASK_CREATED, data={"description": "A" * 100}, ) result = format_event_for_display(event) assert result["description"].endswith("...") assert len(result["description"]) <= 63 def test_task_created_short_description(self): event = FakeEventLogEntry( event_type=FakeEventType.TASK_CREATED, data={"description": "Short task"}, ) result = format_event_for_display(event) assert result["description"] == "Short task" def test_task_assigned(self): event = FakeEventLogEntry( event_type=FakeEventType.TASK_ASSIGNED, agent_id="agent-12345678-long", data={"bid_sats": 500}, ) result = format_event_for_display(event) assert "agent-12" in result["description"] assert "500 sats" in result["description"] def test_bid_submitted(self): event = FakeEventLogEntry( event_type=FakeEventType.BID_SUBMITTED, data={"bid_sats": 250}, ) result = format_event_for_display(event) assert "250 sats" in result["description"] def test_agent_joined_with_persona(self): event = FakeEventLogEntry( event_type=FakeEventType.AGENT_JOINED, data={"persona_id": "forge"}, ) result = format_event_for_display(event) assert "forge" in result["description"] def test_agent_joined_no_persona(self): event = FakeEventLogEntry( event_type=FakeEventType.AGENT_JOINED, data={}, ) result = format_event_for_display(event) assert result["description"] == "New agent" def test_generic_event_with_message(self): event = FakeEventLogEntry( event_type=FakeEventType.SYSTEM_INFO, data={"message": "All systems go"}, ) result = format_event_for_display(event) assert result["description"] == "All systems go" def test_generic_event_no_data(self): event = FakeEventLogEntry( event_type=FakeEventType.SYSTEM_INFO, data={}, ) result = format_event_for_display(event) assert result["description"] == "" def test_output_structure(self): event = FakeEventLogEntry() result = format_event_for_display(event) assert "id" in result assert "icon" in result assert "label" in result assert "type" in result assert "source" in result assert "timestamp" in result assert "time_short" in result assert result["time_short"] == "12:00:00"