from pathlib import Path from unittest.mock import Mock from src.audio_player import AudioPlayer from src.config_loader import AudioSettings def config(**values: object) -> AudioSettings: base = dict(enabled=True, backend="pygame", file_path="prompt.wav", volume=.7, overlap_policy="skip") base.update(values) return AudioSettings(**base) def test_disabled_and_missing_file_are_safe(tmp_path: Path) -> None: assert AudioPlayer(config(enabled=False)).play() is False player = AudioPlayer(config()) player.load(tmp_path / "missing.wav") assert player.play() is False def test_mock_backend_load_play_volume_and_close(tmp_path: Path) -> None: path = tmp_path / "prompt.wav" path.write_bytes(b"test") channel = Mock() channel.get_busy.return_value = False sound = Mock() sound.play.return_value = channel mixer = Mock() mixer.get_init.return_value = True mixer.Sound.return_value = sound backend = Mock(mixer=mixer) player = AudioPlayer(config(), backend) player.load(path) assert player.play() sound.set_volume.assert_called_once_with(.7) player.close() player.close() def test_overlap_skip_and_restart(tmp_path: Path) -> None: path = tmp_path / "prompt.wav" path.write_bytes(b"x") for policy, expected in (("skip", 1), ("restart", 2)): channel = Mock() channel.get_busy.return_value = True sound = Mock(play=Mock(return_value=channel)) mixer = Mock() mixer.get_init.return_value = True mixer.Sound.return_value = sound player = AudioPlayer(config(overlap_policy=policy), Mock(mixer=mixer)) player.load(path) assert player.play() player.play() assert sound.play.call_count == expected