"""Unit tests for HomeAssistantZeroconf.""" from unittest.mock import AsyncMock, MagicMock, patch import pytest # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def make_zeroconf(**kwargs): defaults = dict( port=6053, mac_address="aa:bb:cc:dd:ee:ff", host_ip_address="192.168.1.100", name="lva-test", ) defaults.update(kwargs) with patch("linux_voice_assistant.zeroconf.AsyncZeroconf") as mock_zc_cls: mock_zc = MagicMock() mock_zc_cls.return_value = mock_zc from linux_voice_assistant.zeroconf import HomeAssistantZeroconf instance = HomeAssistantZeroconf(**defaults) instance._mock_zc = mock_zc instance._mock_zc_cls = mock_zc_cls return instance # --------------------------------------------------------------------------- # __init__ # --------------------------------------------------------------------------- class TestInit: def test_name_stored(self): zc = make_zeroconf(name="my-lva") assert zc.name == "my-lva" def test_port_stored(self): zc = make_zeroconf(port=1234) assert zc.port == 1234 def test_mac_address_stored(self): zc = make_zeroconf(mac_address="11:22:33:44:55:66") assert zc.mac_address == "11:22:33:44:55:66" def test_host_ip_stored(self): zc = make_zeroconf(host_ip_address="10.0.0.1") assert zc.host_ip_address == "10.0.0.1" def test_name_defaults_to_mac_when_not_provided(self): with patch("linux_voice_assistant.zeroconf.AsyncZeroconf"): from linux_voice_assistant.zeroconf import HomeAssistantZeroconf zc = HomeAssistantZeroconf( port=6053, mac_address="aa:bb:cc:dd:ee:ff", host_ip_address="192.168.1.1", name=None, ) assert zc.name == "aa:bb:cc:dd:ee:ff" def test_async_zeroconf_instantiated(self): with patch("linux_voice_assistant.zeroconf.AsyncZeroconf") as mock_cls: mock_cls.return_value = MagicMock() from linux_voice_assistant.zeroconf import HomeAssistantZeroconf HomeAssistantZeroconf( port=6053, mac_address="aa:bb:cc:dd:ee:ff", host_ip_address="192.168.1.1", ) mock_cls.assert_called_once() # --------------------------------------------------------------------------- # register_server() # --------------------------------------------------------------------------- class TestRegisterServer: @pytest.mark.asyncio async def test_register_service_called(self): zc = make_zeroconf() zc._mock_zc.async_register_service = AsyncMock() with patch("linux_voice_assistant.zeroconf.AsyncServiceInfo"): await zc.register_server() zc._mock_zc.async_register_service.assert_called_once() @pytest.mark.asyncio async def test_service_name_contains_device_name(self): zc = make_zeroconf(name="lva-aabbccddee") zc._mock_zc.async_register_service = AsyncMock() captured = {} with patch("linux_voice_assistant.zeroconf.AsyncServiceInfo") as mock_info_cls: mock_info_cls.side_effect = lambda *a, **kw: captured.update({"args": a, "kwargs": kw}) or MagicMock() await zc.register_server() # First positional arg is service type, second is full service name assert "lva-aabbccddee" in captured["args"][1] @pytest.mark.asyncio async def test_service_type_is_esphomelib(self): zc = make_zeroconf() zc._mock_zc.async_register_service = AsyncMock() captured = {} with patch("linux_voice_assistant.zeroconf.AsyncServiceInfo") as mock_info_cls: mock_info_cls.side_effect = lambda *a, **kw: captured.update({"args": a, "kwargs": kw}) or MagicMock() await zc.register_server() assert captured["args"][0] == "_esphomelib._tcp.local." @pytest.mark.asyncio async def test_service_properties_contain_mac(self): zc = make_zeroconf(mac_address="aa:bb:cc:dd:ee:ff") zc._mock_zc.async_register_service = AsyncMock() captured = {} with patch("linux_voice_assistant.zeroconf.AsyncServiceInfo") as mock_info_cls: mock_info_cls.side_effect = lambda *a, **kw: captured.update({"args": a, "kwargs": kw}) or MagicMock() await zc.register_server() props = captured["kwargs"].get("properties", {}) assert "mac" in props assert props["mac"] == "aa:bb:cc:dd:ee:ff" @pytest.mark.asyncio async def test_service_properties_contain_version(self): zc = make_zeroconf() zc._mock_zc.async_register_service = AsyncMock() captured = {} with patch("linux_voice_assistant.zeroconf.AsyncServiceInfo") as mock_info_cls: mock_info_cls.side_effect = lambda *a, **kw: captured.update({"args": a, "kwargs": kw}) or MagicMock() await zc.register_server() props = captured["kwargs"].get("properties", {}) assert "version" in props @pytest.mark.asyncio async def test_service_port_matches(self): zc = make_zeroconf(port=9999) zc._mock_zc.async_register_service = AsyncMock() captured = {} with patch("linux_voice_assistant.zeroconf.AsyncServiceInfo") as mock_info_cls: mock_info_cls.side_effect = lambda *a, **kw: captured.update({"args": a, "kwargs": kw}) or MagicMock() await zc.register_server() assert captured["kwargs"].get("port") == 9999 @pytest.mark.asyncio async def test_service_address_matches_host_ip(self): import socket zc = make_zeroconf(host_ip_address="10.0.0.5") zc._mock_zc.async_register_service = AsyncMock() captured = {} with patch("linux_voice_assistant.zeroconf.AsyncServiceInfo") as mock_info_cls: mock_info_cls.side_effect = lambda *a, **kw: captured.update({"args": a, "kwargs": kw}) or MagicMock() await zc.register_server() addresses = captured["kwargs"].get("addresses", []) assert socket.inet_aton("10.0.0.5") in addresses