This repository has been archived on 2026-03-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Timmy-time-dashboard/tests/infrastructure/test_event_broadcaster.py
Alexander Whitestone 9d78eb31d1 ruff (#169)
* polish: streamline nav, extract inline styles, improve tablet UX

- Restructure desktop nav from 8+ flat links + overflow dropdown into
  5 grouped dropdowns (Core, Agents, Intel, System, More) matching
  the mobile menu structure to reduce decision fatigue
- Extract all inline styles from mission_control.html and base.html
  notification elements into mission-control.css with semantic classes
- Replace JS-built innerHTML with secure DOM construction in
  notification loader and chat history
- Add CONNECTING state to connection indicator (amber) instead of
  showing OFFLINE before WebSocket connects
- Add tablet breakpoint (1024px) with larger touch targets for
  Apple Pencil / stylus use and safe-area padding for iPad toolbar
- Add active-link highlighting in desktop dropdown menus
- Rename "Mission Control" page title to "System Overview" to
  disambiguate from the chat home page
- Add "Home — Timmy Time" page title to index.html

https://claude.ai/code/session_015uPUoKyYa8M2UAcyk5Gt6h

* fix(security): move auth-gate credentials to environment variables

Hardcoded username, password, and HMAC secret in auth-gate.py replaced
with os.environ lookups. Startup now refuses to run if any variable is
unset. Added AUTH_GATE_SECRET/USER/PASS to .env.example.

https://claude.ai/code/session_015uPUoKyYa8M2UAcyk5Gt6h

* refactor(tooling): migrate from black+isort+bandit to ruff

Replace three separate linting/formatting tools with a single ruff
invocation. Updates tox.ini (lint, format, pre-push, pre-commit envs),
.pre-commit-config.yaml, and CI workflow. Fixes all ruff errors
including unused imports, missing raise-from, and undefined names.
Ruff config maps existing bandit skips to equivalent S-rules.

https://claude.ai/code/session_015uPUoKyYa8M2UAcyk5Gt6h

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-11 12:23:35 -04:00

192 lines
6.2 KiB
Python

"""Tests for the event broadcaster (infrastructure.events.broadcaster)."""
from dataclasses import dataclass
from enum import Enum
from unittest.mock import AsyncMock, MagicMock
from infrastructure.events.broadcaster import (
EVENT_ICONS,
EVENT_LABELS,
EventBroadcaster,
format_event_for_display,
get_event_icon,
get_event_label,
)
# ── 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"