forked from Rockachopa/Timmy-time-dashboard
feat: broadcast Timmy state changes via WS relay (#380)
Co-authored-by: Kimi Agent <kimi@timmy.local> Co-committed-by: Kimi Agent <kimi@timmy.local>
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
"""Tests for GET /api/world/state endpoint."""
|
||||
"""Tests for GET /api/world/state endpoint and /api/world/ws relay."""
|
||||
|
||||
import json
|
||||
import time
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -10,6 +10,7 @@ from dashboard.routes.world import (
|
||||
_STALE_THRESHOLD,
|
||||
_build_world_state,
|
||||
_read_presence_file,
|
||||
broadcast_world_state,
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -166,3 +167,55 @@ def test_world_state_endpoint_full_fallback(client, tmp_path):
|
||||
data = resp.json()
|
||||
assert data["timmyState"]["mood"] == "idle"
|
||||
assert data["version"] == 1
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# broadcast_world_state
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_broadcast_world_state_sends_timmy_state():
|
||||
"""broadcast_world_state sends timmy_state JSON to connected clients."""
|
||||
from dashboard.routes.world import _ws_clients
|
||||
|
||||
ws = AsyncMock()
|
||||
_ws_clients.append(ws)
|
||||
try:
|
||||
presence = {
|
||||
"version": 1,
|
||||
"mood": "exploring",
|
||||
"current_focus": "testing",
|
||||
"energy": 0.8,
|
||||
"confidence": 0.9,
|
||||
}
|
||||
await broadcast_world_state(presence)
|
||||
|
||||
ws.send_text.assert_called_once()
|
||||
msg = json.loads(ws.send_text.call_args[0][0])
|
||||
assert msg["type"] == "timmy_state"
|
||||
assert msg["mood"] == "exploring"
|
||||
assert msg["activity"] == "testing"
|
||||
finally:
|
||||
_ws_clients.clear()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_broadcast_world_state_removes_dead_clients():
|
||||
"""Dead WebSocket connections are cleaned up on broadcast."""
|
||||
from dashboard.routes.world import _ws_clients
|
||||
|
||||
dead_ws = AsyncMock()
|
||||
dead_ws.send_text.side_effect = ConnectionError("gone")
|
||||
_ws_clients.append(dead_ws)
|
||||
try:
|
||||
await broadcast_world_state({"mood": "idle"})
|
||||
assert dead_ws not in _ws_clients
|
||||
finally:
|
||||
_ws_clients.clear()
|
||||
|
||||
|
||||
def test_world_ws_endpoint_accepts_connection(client):
|
||||
"""WebSocket endpoint at /api/world/ws accepts connections."""
|
||||
with client.websocket_connect("/api/world/ws"):
|
||||
pass # Connection accepted — just close it
|
||||
|
||||
Reference in New Issue
Block a user