mirror of
https://github.com/blw1138/Zordon.git
synced 2026-06-09 13:39:24 -05:00
24eb7b5616
* Add tests and new github workflow * Add new unit tests * Add Github CI workflow * Workflow fix * Add pytest install to workflow file * More CI / test updates * More test cleanup * Whitespace cleanup and link complexity override * More whitespace cleanup * Make lint less strict * More lint tweaks
160 lines
6.5 KiB
Python
160 lines
6.5 KiB
Python
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from src.utilities.zeroconf_server import ZeroconfServer
|
|
|
|
|
|
class TestConfigure:
|
|
"""Configuring service parameters."""
|
|
|
|
def test_configure_sets_attributes(self, zeroconf_server_instance):
|
|
with patch('socket.gethostbyname', return_value='192.168.1.1'):
|
|
ZeroconfServer.configure('_zordon._tcp.local.', 'test-server', 8080)
|
|
|
|
assert zeroconf_server_instance.service_type == '_zordon._tcp.local.'
|
|
assert zeroconf_server_instance.server_name == 'test-server'
|
|
assert zeroconf_server_instance.server_port == 8080
|
|
|
|
def test_configure_calls_sync_class(self, zeroconf_server_instance):
|
|
with patch('socket.gethostbyname', return_value='192.168.1.1'):
|
|
ZeroconfServer.configure('_zordon._tcp.local.', 'test-server', 8080)
|
|
|
|
assert ZeroconfServer.service_type == '_zordon._tcp.local.'
|
|
assert ZeroconfServer.server_port == 8080
|
|
|
|
def test_configure_stops_on_gaierror(self, zeroconf_server_instance):
|
|
import socket
|
|
with patch('socket.gethostbyname', side_effect=socket.gaierror):
|
|
with patch.object(zeroconf_server_instance, '_stop') as mock_stop:
|
|
ZeroconfServer.configure('_zordon._tcp.local.', 'test-server', 8080)
|
|
mock_stop.assert_called_once()
|
|
|
|
|
|
class TestSyncClass:
|
|
"""_sync_class propagates instance attrs to class level."""
|
|
|
|
def test_sync_class_propagates_all_attrs(self, zeroconf_server_instance):
|
|
zeroconf_server_instance.service_type = '_test._tcp.'
|
|
zeroconf_server_instance.server_name = 'foo'
|
|
zeroconf_server_instance.server_port = 9999
|
|
zeroconf_server_instance.server_ip = '10.0.0.1'
|
|
zeroconf_server_instance.properties = {'key': 'val'}
|
|
|
|
ZeroconfServer._sync_class()
|
|
|
|
assert ZeroconfServer.service_type == '_test._tcp.'
|
|
assert ZeroconfServer.server_name == 'foo'
|
|
assert ZeroconfServer.server_port == 9999
|
|
assert ZeroconfServer.server_ip == '10.0.0.1'
|
|
assert ZeroconfServer.properties == {'key': 'val'}
|
|
|
|
|
|
class TestStartStop:
|
|
"""Service lifecycle."""
|
|
|
|
def test_start_raises_without_configure(self, zeroconf_server_instance):
|
|
with pytest.raises(RuntimeError, match='configure'):
|
|
ZeroconfServer.start()
|
|
|
|
@patch('src.utilities.zeroconf_server.ServiceBrowser')
|
|
def test_start_listen_only_skips_register(self, mock_browser, zeroconf_server_instance):
|
|
with patch('socket.gethostbyname', return_value='192.168.1.1'):
|
|
ZeroconfServer.configure('_zordon._tcp.local.', 'test', 8080)
|
|
|
|
with patch.object(zeroconf_server_instance, '_register_service') as mock_register:
|
|
ZeroconfServer.start(listen_only=True)
|
|
mock_register.assert_not_called()
|
|
|
|
@patch('src.utilities.zeroconf_server.ServiceBrowser')
|
|
def test_start_registers_service(self, mock_browser, zeroconf_server_instance):
|
|
with patch('socket.gethostbyname', return_value='192.168.1.1'):
|
|
ZeroconfServer.configure('_zordon._tcp.local.', 'test', 8080)
|
|
|
|
with patch.object(zeroconf_server_instance, '_register_service') as mock_register:
|
|
ZeroconfServer.start(listen_only=False)
|
|
mock_register.assert_called_once()
|
|
|
|
def test_stop_unregisters_and_closes(self, zeroconf_server_instance):
|
|
zeroconf_server_instance.service_info = MagicMock()
|
|
|
|
with patch.object(zeroconf_server_instance, '_unregister_service') as mock_unreg:
|
|
with patch.object(zeroconf_server_instance.zeroconf, 'close') as mock_close:
|
|
ZeroconfServer.stop()
|
|
mock_unreg.assert_called_once()
|
|
mock_close.assert_called_once()
|
|
|
|
|
|
class TestRegisterService:
|
|
"""Service registration with Zeroconf."""
|
|
|
|
@patch('src.utilities.zeroconf_server.ServiceInfo')
|
|
@patch('socket.gethostbyname')
|
|
def test_registers_service_info(self, mock_gethostbyname, mock_service_info,
|
|
zeroconf_server_instance):
|
|
mock_gethostbyname.return_value = '192.168.1.1'
|
|
zeroconf_server_instance.service_type = '_zordon._tcp.local.'
|
|
zeroconf_server_instance.server_name = 'test'
|
|
zeroconf_server_instance.server_port = 8080
|
|
zeroconf_server_instance.properties = {}
|
|
|
|
# Replace real Zeroconf with a mock so we don't actually register
|
|
zeroconf_server_instance.zeroconf = MagicMock()
|
|
|
|
mock_info = MagicMock()
|
|
mock_service_info.return_value = mock_info
|
|
|
|
with patch('socket.inet_aton', return_value=b'\xc0\xa8\x01\x01'):
|
|
zeroconf_server_instance._register_service()
|
|
|
|
zeroconf_server_instance.zeroconf.register_service.assert_called_once_with(mock_info)
|
|
assert zeroconf_server_instance.service_info == mock_info
|
|
|
|
|
|
class TestFoundHostnames:
|
|
"""Discovery cache."""
|
|
|
|
def test_found_hostnames_empty_initially(self, zeroconf_server_instance):
|
|
result = zeroconf_server_instance._found_hostnames()
|
|
assert result == []
|
|
|
|
@patch('socket.gethostname', return_value='my-machine')
|
|
def test_hostnames_sorted_with_local_first(self, mock_hostname, zeroconf_server_instance):
|
|
zeroconf_server_instance.client_cache = {
|
|
'other-machine': MagicMock(),
|
|
'my-machine': MagicMock(),
|
|
}
|
|
|
|
result = zeroconf_server_instance._found_hostnames()
|
|
# sort_key returns False (0) for local → sorted first
|
|
assert result[0] == 'my-machine'
|
|
|
|
def test_get_hostname_properties_returns_decoded(self, zeroconf_server_instance):
|
|
info = MagicMock()
|
|
info.properties = {b'key': b'value', b'num': b'42'}
|
|
zeroconf_server_instance.client_cache['server-1'] = info
|
|
|
|
result = zeroconf_server_instance._get_hostname_properties('server-1')
|
|
assert result == {'key': 'value', 'num': '42'}
|
|
|
|
def test_get_hostname_properties_returns_empty_for_unknown(self, zeroconf_server_instance):
|
|
result = zeroconf_server_instance._get_hostname_properties('unknown')
|
|
assert result == {}
|
|
|
|
|
|
class TestForwarders:
|
|
"""Classmethod forwarders delegate to instance."""
|
|
|
|
def test_found_hostnames_forwarder(self, zeroconf_server_instance):
|
|
zeroconf_server_instance.client_cache['svr'] = MagicMock()
|
|
result = ZeroconfServer.found_hostnames()
|
|
assert 'svr' in result
|
|
|
|
def test_get_hostname_properties_forwarder(self, zeroconf_server_instance):
|
|
info = MagicMock()
|
|
info.properties = {}
|
|
zeroconf_server_instance.client_cache['svr'] = info
|
|
|
|
result = ZeroconfServer.get_hostname_properties('svr')
|
|
assert result == {}
|