refactor: phase 4 - event-driven architecture foundation

- Add EventBus class with pub/sub messaging (thread-safe)
- Add emitter Protocol classes (EventEmitter, Startable, Stoppable)
- Add event emission to NtfyPoller (NtfyMessageEvent)
- Add event emission to MicMonitor (MicLevelEvent)
- Update StreamController to publish stream start/end events
- Add comprehensive tests for eventbus and emitters modules
This commit is contained in:
2026-03-15 16:20:52 -07:00
parent 35e5c8d38b
commit 15de46722a
12 changed files with 704 additions and 22 deletions

View File

@@ -5,6 +5,7 @@ Tests for engine.ntfy module.
import time
from unittest.mock import MagicMock, patch
from engine.events import NtfyMessageEvent
from engine.ntfy import NtfyPoller
@@ -68,3 +69,54 @@ class TestNtfyPollerDismiss:
poller.dismiss()
assert poller._message is None
class TestNtfyPollerEventEmission:
"""Tests for NtfyPoller event emission."""
def test_subscribe_adds_callback(self):
"""subscribe() adds a callback."""
poller = NtfyPoller("http://example.com/topic")
def callback(e):
return None
poller.subscribe(callback)
assert callback in poller._subscribers
def test_unsubscribe_removes_callback(self):
"""unsubscribe() removes a callback."""
poller = NtfyPoller("http://example.com/topic")
def callback(e):
return None
poller.subscribe(callback)
poller.unsubscribe(callback)
assert callback not in poller._subscribers
def test_emit_calls_subscribers(self):
"""_emit() calls all subscribers."""
poller = NtfyPoller("http://example.com/topic")
received = []
def callback(event):
received.append(event)
poller.subscribe(callback)
event = NtfyMessageEvent(title="Test", body="Body")
poller._emit(event)
assert len(received) == 1
assert received[0].title == "Test"
def test_emit_handles_subscriber_exception(self):
"""_emit() handles exceptions in subscribers gracefully."""
poller = NtfyPoller("http://example.com/topic")
def bad_callback(event):
raise RuntimeError("test")
poller.subscribe(bad_callback)
event = NtfyMessageEvent(title="Test", body="Body")
poller._emit(event)