Files
Timmy-time-dashboard/tests/integrations/test_presence_watcher.py
hermes 19e7e61c92
All checks were successful
Tests / lint (push) Successful in 4s
Tests / test (push) Successful in 1m14s
[loop-cycle] refactor: DRY PRESENCE_FILE — single source of truth in workshop_state (#381) (#382)
2026-03-18 22:33:06 -04:00

96 lines
2.8 KiB
Python

"""Tests for the presence file watcher in dashboard.app."""
import asyncio
import json
from unittest.mock import AsyncMock, patch
import pytest
# Common patches to eliminate delays and inject mock ws_manager
_FAST = {
"dashboard.app._PRESENCE_POLL_SECONDS": 0.01,
"dashboard.app._PRESENCE_INITIAL_DELAY": 0,
}
def _patches(mock_ws, presence_file):
"""Return a combined context manager for presence watcher patches."""
from contextlib import ExitStack
stack = ExitStack()
stack.enter_context(patch("dashboard.app.PRESENCE_FILE", presence_file))
stack.enter_context(patch("infrastructure.ws_manager.handler.ws_manager", mock_ws))
for key, val in _FAST.items():
stack.enter_context(patch(key, val))
return stack
@pytest.mark.asyncio
async def test_presence_watcher_broadcasts_on_file_change(tmp_path):
"""Watcher reads presence.json and broadcasts via ws_manager."""
from dashboard.app import _presence_watcher
presence_file = tmp_path / "presence.json"
state = {
"version": 1,
"liveness": "2026-03-18T21:47:12Z",
"current_focus": "Reviewing PR #267",
"mood": "focused",
}
presence_file.write_text(json.dumps(state))
mock_ws = AsyncMock()
with _patches(mock_ws, presence_file):
task = asyncio.create_task(_presence_watcher())
await asyncio.sleep(0.15)
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
mock_ws.broadcast.assert_called_with("timmy_state", state)
@pytest.mark.asyncio
async def test_presence_watcher_synthesised_state_when_missing(tmp_path):
"""Watcher broadcasts synthesised idle state when file is absent."""
from dashboard.app import _SYNTHESIZED_STATE, _presence_watcher
missing_file = tmp_path / "no-such-file.json"
mock_ws = AsyncMock()
with _patches(mock_ws, missing_file):
task = asyncio.create_task(_presence_watcher())
await asyncio.sleep(0.15)
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
mock_ws.broadcast.assert_called_with("timmy_state", _SYNTHESIZED_STATE)
@pytest.mark.asyncio
async def test_presence_watcher_handles_bad_json(tmp_path):
"""Watcher logs warning on malformed JSON and doesn't crash."""
from dashboard.app import _presence_watcher
presence_file = tmp_path / "presence.json"
presence_file.write_text("{bad json!!!")
mock_ws = AsyncMock()
with _patches(mock_ws, presence_file):
task = asyncio.create_task(_presence_watcher())
await asyncio.sleep(0.15)
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
# Should not have broadcast anything on bad JSON
mock_ws.broadcast.assert_not_called()