""" Tests for engine.mic module. """ from datetime import datetime from unittest.mock import patch from engine.events import MicLevelEvent class TestMicMonitorImport: """Tests for module import behavior.""" def test_mic_monitor_imports_without_error(self): """MicMonitor can be imported even without sounddevice.""" from engine.mic import MicMonitor assert MicMonitor is not None class TestMicMonitorInit: """Tests for MicMonitor initialization.""" def test_init_sets_threshold(self): """Threshold is set correctly.""" from engine.mic import MicMonitor monitor = MicMonitor(threshold_db=60) assert monitor.threshold_db == 60 def test_init_defaults(self): """Default values are set correctly.""" from engine.mic import MicMonitor monitor = MicMonitor() assert monitor.threshold_db == 50 def test_init_db_starts_at_negative(self): """_db starts at negative value.""" from engine.mic import MicMonitor monitor = MicMonitor() assert monitor.db == -99.0 class TestMicMonitorProperties: """Tests for MicMonitor properties.""" def test_excess_returns_positive_when_above_threshold(self): """excess returns positive value when above threshold.""" from engine.mic import MicMonitor monitor = MicMonitor(threshold_db=50) with patch.object(monitor, "_db", 60.0): assert monitor.excess == 10.0 def test_excess_returns_zero_when_below_threshold(self): """excess returns zero when below threshold.""" from engine.mic import MicMonitor monitor = MicMonitor(threshold_db=50) with patch.object(monitor, "_db", 40.0): assert monitor.excess == 0.0 class TestMicMonitorAvailable: """Tests for MicMonitor.available property.""" def test_available_is_bool(self): """available returns a boolean.""" from engine.mic import MicMonitor monitor = MicMonitor() assert isinstance(monitor.available, bool) class TestMicMonitorStop: """Tests for MicMonitor.stop method.""" def test_stop_does_nothing_when_no_stream(self): """stop() does nothing if no stream exists.""" from engine.mic import MicMonitor monitor = MicMonitor() monitor.stop() assert monitor._stream is None class TestMicMonitorEventEmission: """Tests for MicMonitor event emission.""" def test_subscribe_adds_callback(self): """subscribe() adds a callback.""" from engine.mic import MicMonitor monitor = MicMonitor() def callback(e): return None monitor.subscribe(callback) assert callback in monitor._subscribers def test_unsubscribe_removes_callback(self): """unsubscribe() removes a callback.""" from engine.mic import MicMonitor monitor = MicMonitor() def callback(e): return None monitor.subscribe(callback) monitor.unsubscribe(callback) assert callback not in monitor._subscribers def test_emit_calls_subscribers(self): """_emit() calls all subscribers.""" from engine.mic import MicMonitor monitor = MicMonitor() received = [] def callback(event): received.append(event) monitor.subscribe(callback) event = MicLevelEvent( db_level=60.0, excess_above_threshold=10.0, timestamp=datetime.now() ) monitor._emit(event) assert len(received) == 1 assert received[0].db_level == 60.0 def test_emit_handles_subscriber_exception(self): """_emit() handles exceptions in subscribers gracefully.""" from engine.mic import MicMonitor monitor = MicMonitor() def bad_callback(event): raise RuntimeError("test") monitor.subscribe(bad_callback) event = MicLevelEvent( db_level=60.0, excess_above_threshold=10.0, timestamp=datetime.now() ) monitor._emit(event)