from unittest.mock import MagicMock, patch from napalm.base.exceptions import ConnectionException from napalm_sonos import SonosDriver def create_mock_speaker_info(): return { "player_name": "Living Room", "zone_name": "Living Room", "uid": "RINCON_000E58ABCDE01400", "model_name": "Sonos One", "software_version": "15.2", "hardware_version": "1.20.1", "serial_number": "000E58ABCDE", "mac_address": "00:0E:58:AB:CD:EF", "ip_address": "192.168.1.100", } class TestSonosDriver: def setup_method(self): self.driver = SonosDriver("192.168.1.100") self.mock_device = MagicMock() self.mock_device.get_speaker_info.return_value = create_mock_speaker_info() self.mock_device.volume = 30 self.mock_device.mute = False self.mock_device.get_playback_state.return_value = "PLAYING" self.mock_device.get_current_track_info.return_value = { "title": "Test Song", "artist": "Test Artist", "album": "Test Album", } self.driver._device = self.mock_device self.driver._speaker_info = create_mock_speaker_info() def test_open_success(self): driver = SonosDriver("192.168.1.100") with patch("soco.SoCo") as mock_soco: mock_instance = MagicMock() mock_instance.get_speaker_info.return_value = create_mock_speaker_info() mock_soco.return_value = mock_instance driver.open() assert driver._device is not None assert driver._speaker_info is not None assert driver._speaker_info["player_name"] == "Living Room" def test_open_failure(self): driver = SonosDriver("192.168.1.100") with patch("soco.SoCo") as mock_soco: mock_soco.side_effect = Exception("Connection refused") try: driver.open() assert False, "Expected ConnectionException" except ConnectionException: pass def test_close(self): self.driver.close() assert self.driver._device is None assert self.driver._speaker_info is None def test_is_alive(self): with patch("socket.create_connection") as mock_conn: mock_conn.return_value.__enter__.return_value = MagicMock() result = self.driver.is_alive() assert result["is_alive"] is True def test_is_alive_no_device(self): self.driver._device = None result = self.driver.is_alive() assert result["is_alive"] is False def test_get_facts(self): facts = self.driver.get_facts() assert facts["vendor"] == "Sonos" assert facts["model"] == "Sonos One" assert facts["serial_number"] == "000E58ABCDE" assert facts["os_version"] == "15.2" assert facts["hostname"] == "Living Room" assert "wlan0" in facts["interface_list"] def test_get_facts_empty(self): self.driver._speaker_info = None assert self.driver.get_facts() == {} def test_get_interfaces(self): with patch("socket.create_connection") as mock_conn: mock_conn.return_value.__enter__.return_value = MagicMock() ifaces = self.driver.get_interfaces() assert "wlan0" in ifaces assert ifaces["wlan0"]["is_up"] is True assert ifaces["wlan0"]["mac_address"] == "00:0E:58:AB:CD:EF" def test_get_interfaces_ip(self): ifaces_ip = self.driver.get_interfaces_ip() assert "wlan0" in ifaces_ip assert "192.168.1.100" in ifaces_ip["wlan0"]["ipv4"] def test_get_playback_state(self): state = self.driver.get_playback_state() assert state == "PLAYING" def test_get_volume(self): vol = self.driver.get_volume() assert vol == 30 def test_set_volume(self): self.driver.set_volume(50) assert self.mock_device.volume == 50 def test_set_volume_clamped(self): self.driver.set_volume(200) assert self.mock_device.volume == 100 def test_get_mute(self): assert self.driver.get_mute() is False def test_set_mute(self): self.driver.set_mute(True) assert self.mock_device.mute is True def test_play(self): self.driver.play() self.mock_device.play.assert_called_once() def test_pause(self): self.driver.pause() self.mock_device.pause.assert_called_once() def test_stop(self): self.driver.stop() self.mock_device.stop.assert_called_once() def test_next_track(self): self.driver.next_track() self.mock_device.next.assert_called_once() def test_previous_track(self): self.driver.previous_track() self.mock_device.previous.assert_called_once() def test_get_current_track(self): track = self.driver.get_current_track() assert track["title"] == "Test Song" assert track["artist"] == "Test Artist" def test_get_group_info(self): mock_coordinator = MagicMock() mock_coordinator.player_name = "Living Room" mock_coordinator.uid = "RINCON_000E58ABCDE01400" mock_member = MagicMock() mock_member.player_name = "Kitchen" mock_member.uid = "RINCON_000E58ABCDE01401" mock_member.ip_address = "192.168.1.101" mock_group = MagicMock() mock_group.coordinator = mock_coordinator mock_group.members = [mock_coordinator, mock_member] self.mock_device.group = mock_group group = self.driver.get_group_info() assert group["coordinator"] == "Living Room" assert len(group["members"]) == 2 def test_get_zone_info(self): zone = self.driver.get_zone_info() assert zone["player_name"] == "Living Room" assert zone["model_name"] == "Sonos One" assert zone["ip_address"] == "192.168.1.100" def test_run_device_action_play(self): result = self.driver.run_device_action("play") assert result["success"] is True assert result["action"] == "play" self.mock_device.play.assert_called_once() def test_run_device_action_pause(self): result = self.driver.run_device_action("pause") assert result["success"] is True self.mock_device.pause.assert_called_once() def test_run_device_action_stop(self): result = self.driver.run_device_action("stop") assert result["success"] is True self.mock_device.stop.assert_called_once() def test_run_device_action_set_volume(self): result = self.driver.run_device_action("set_volume:42") assert result["success"] is True assert self.mock_device.volume == 42 def test_run_device_action_set_volume_invalid(self): result = self.driver.run_device_action("set_volume:abc") assert result["success"] is False def test_run_device_action_toggle_mute(self): result = self.driver.run_device_action("toggle_mute") assert result["success"] is True assert self.mock_device.mute is True def test_run_device_action_unknown(self): result = self.driver.run_device_action("foobar") assert result["success"] is False assert "Unknown" in result["message"]