mirror of
https://github.com/craigerl/aprsd.git
synced 2026-01-27 07:56:07 -05:00
The client registry defined a protocol that all drivers had to implement. This patch ensures that all methods are consistent in the protocol definition.
387 lines
14 KiB
Python
387 lines
14 KiB
Python
import unittest
|
|
from unittest import mock
|
|
|
|
from aprsd.client.client import APRSDClient
|
|
from aprsd.client.drivers.registry import DriverRegistry
|
|
from aprsd.packets import core
|
|
from tests.mock_client_driver import MockClientDriver
|
|
|
|
|
|
class TestAPRSDClient(unittest.TestCase):
|
|
"""Unit tests for the APRSDClient class."""
|
|
|
|
def setUp(self):
|
|
"""Set up test fixtures."""
|
|
# Reset singleton instances
|
|
APRSDClient._instance = None
|
|
APRSDClient.driver = None
|
|
# Reset DriverRegistry singleton - the singleton decorator stores instance here
|
|
DriverRegistry.instance = None
|
|
|
|
# Mock APRSISDriver to prevent it from being checked
|
|
self.aprsis_patcher = mock.patch('aprsd.client.drivers.aprsis.APRSISDriver')
|
|
mock_aprsis_class = self.aprsis_patcher.start()
|
|
mock_aprsis_class.is_enabled.return_value = False
|
|
mock_aprsis_class.is_configured.return_value = False
|
|
|
|
self.mock_driver = MockClientDriver()
|
|
# Create a mock registry instance
|
|
mock_registry_instance = mock.MagicMock()
|
|
mock_registry_instance.get_driver.return_value = self.mock_driver
|
|
# Patch DriverRegistry to return our mock instance
|
|
self.registry_patcher = mock.patch(
|
|
'aprsd.client.client.DriverRegistry', return_value=mock_registry_instance
|
|
)
|
|
self.mock_registry = self.registry_patcher.start()
|
|
|
|
def tearDown(self):
|
|
"""Clean up after tests."""
|
|
if hasattr(APRSDClient, '_instance'):
|
|
if APRSDClient._instance:
|
|
APRSDClient._instance.close()
|
|
APRSDClient._instance = None
|
|
APRSDClient.driver = None
|
|
self.registry_patcher.stop()
|
|
self.aprsis_patcher.stop()
|
|
|
|
def test_singleton_pattern(self):
|
|
"""Test that APRSDClient is a singleton."""
|
|
client1 = APRSDClient(auto_connect=False)
|
|
client2 = APRSDClient(auto_connect=False)
|
|
self.assertIs(client1, client2)
|
|
self.assertEqual(id(client1), id(client2))
|
|
|
|
def test_init_with_auto_connect(self):
|
|
"""Test initialization with auto_connect=True."""
|
|
client = APRSDClient(auto_connect=True)
|
|
# Should have called setup_connection
|
|
self.assertIsNotNone(client.driver)
|
|
|
|
def test_init_without_auto_connect(self):
|
|
"""Test initialization with auto_connect=False."""
|
|
client = APRSDClient(auto_connect=False)
|
|
self.assertIsNotNone(client.driver)
|
|
self.assertFalse(client.connected)
|
|
|
|
def test_stats(self):
|
|
"""Test stats() method."""
|
|
client = APRSDClient(auto_connect=False)
|
|
stats = client.stats()
|
|
self.assertIsInstance(stats, dict)
|
|
|
|
stats_serializable = client.stats(serializable=True)
|
|
self.assertIsInstance(stats_serializable, dict)
|
|
|
|
def test_stats_no_driver(self):
|
|
"""Test stats() when driver is None."""
|
|
client = APRSDClient(auto_connect=False)
|
|
client.driver = None
|
|
stats = client.stats()
|
|
self.assertEqual(stats, {})
|
|
|
|
def test_is_enabled(self):
|
|
"""Test is_enabled() static method."""
|
|
# Stop the registry patcher temporarily to use real registry
|
|
self.registry_patcher.stop()
|
|
try:
|
|
# Reset singleton
|
|
DriverRegistry.instance = None
|
|
registry = DriverRegistry()
|
|
mock_driver_class = mock.MagicMock()
|
|
mock_driver_class.is_enabled.return_value = True
|
|
registry.drivers = [mock_driver_class]
|
|
|
|
result = APRSDClient.is_enabled()
|
|
self.assertTrue(result)
|
|
finally:
|
|
# Restart the patcher
|
|
self.registry_patcher.start()
|
|
|
|
def test_is_enabled_no_drivers(self):
|
|
"""Test is_enabled() with no drivers."""
|
|
# Stop the registry patcher temporarily to use real registry
|
|
self.registry_patcher.stop()
|
|
try:
|
|
# Reset singleton
|
|
DriverRegistry.instance = None
|
|
registry = DriverRegistry()
|
|
registry.drivers = []
|
|
|
|
result = APRSDClient.is_enabled()
|
|
self.assertFalse(result)
|
|
finally:
|
|
# Restart the patcher
|
|
self.registry_patcher.start()
|
|
|
|
def test_is_configured(self):
|
|
"""Test is_configured() static method."""
|
|
# Stop the registry patcher temporarily to use real registry
|
|
self.registry_patcher.stop()
|
|
try:
|
|
# Reset singleton
|
|
DriverRegistry.instance = None
|
|
registry = DriverRegistry()
|
|
mock_driver_class = mock.MagicMock()
|
|
mock_driver_class.is_enabled.return_value = True
|
|
mock_driver_class.is_configured.return_value = True
|
|
registry.drivers = [mock_driver_class]
|
|
|
|
result = APRSDClient.is_configured()
|
|
self.assertTrue(result)
|
|
finally:
|
|
# Restart the patcher
|
|
self.registry_patcher.start()
|
|
|
|
def test_is_configured_no_drivers(self):
|
|
"""Test is_configured() with no drivers."""
|
|
# Stop the registry patcher temporarily to use real registry
|
|
self.registry_patcher.stop()
|
|
try:
|
|
# Reset singleton
|
|
DriverRegistry.instance = None
|
|
registry = DriverRegistry()
|
|
registry.drivers = []
|
|
|
|
result = APRSDClient.is_configured()
|
|
self.assertFalse(result)
|
|
finally:
|
|
# Restart the patcher
|
|
self.registry_patcher.start()
|
|
|
|
def test_login_success_property(self):
|
|
"""Test login_success method."""
|
|
client = APRSDClient(auto_connect=False)
|
|
self.mock_driver.login_status['success'] = True
|
|
self.assertTrue(client.login_success())
|
|
|
|
self.mock_driver.login_status['success'] = False
|
|
self.assertFalse(client.login_success())
|
|
|
|
def test_login_success_no_driver(self):
|
|
"""Test login_success method when driver is None."""
|
|
client = APRSDClient(auto_connect=False)
|
|
client.driver = None
|
|
self.assertFalse(client.login_success())
|
|
|
|
def test_login_failure_property(self):
|
|
"""Test login_failure method."""
|
|
client = APRSDClient(auto_connect=False)
|
|
self.mock_driver.login_status['message'] = 'Test failure'
|
|
self.assertEqual(client.login_failure(), 'Test failure')
|
|
|
|
def test_login_failure_no_driver(self):
|
|
"""Test login_failure method when driver is None."""
|
|
client = APRSDClient(auto_connect=False)
|
|
client.driver = None
|
|
self.assertIsNone(client.login_failure())
|
|
|
|
def test_set_filter(self):
|
|
"""Test set_filter() method."""
|
|
client = APRSDClient(auto_connect=False)
|
|
filter_str = 'test filter'
|
|
client.set_filter(filter_str)
|
|
self.assertEqual(client.filter, filter_str)
|
|
self.assertEqual(self.mock_driver.filter, filter_str)
|
|
|
|
def test_set_filter_no_driver(self):
|
|
"""Test set_filter() when driver is None."""
|
|
client = APRSDClient(auto_connect=False)
|
|
client.driver = None
|
|
filter_str = 'test filter'
|
|
client.set_filter(filter_str)
|
|
self.assertEqual(client.filter, filter_str)
|
|
|
|
def test_get_filter(self):
|
|
"""Test get_filter() method."""
|
|
client = APRSDClient(auto_connect=False)
|
|
filter_str = 'test filter'
|
|
client.set_filter(filter_str)
|
|
# get_filter returns driver.filter, not client.filter
|
|
self.mock_driver.filter = filter_str
|
|
result = client.get_filter()
|
|
self.assertEqual(result, filter_str)
|
|
|
|
def test_get_filter_no_driver(self):
|
|
"""Test get_filter() when driver is None."""
|
|
client = APRSDClient(auto_connect=False)
|
|
client.driver = None
|
|
result = client.get_filter()
|
|
self.assertIsNone(result)
|
|
|
|
def test_is_alive(self):
|
|
"""Test is_alive() method."""
|
|
client = APRSDClient(auto_connect=False)
|
|
self.mock_driver._alive = True
|
|
self.assertTrue(client.is_alive())
|
|
|
|
self.mock_driver._alive = False
|
|
self.assertFalse(client.is_alive())
|
|
|
|
def test_connect(self):
|
|
"""Test connect() method."""
|
|
client = APRSDClient(auto_connect=False)
|
|
self.assertFalse(client.connected)
|
|
# Make sure driver.is_alive returns True after setup_connection
|
|
self.mock_driver._alive = True
|
|
client.connect()
|
|
self.assertTrue(client.connected)
|
|
self.assertTrue(client.running)
|
|
|
|
def test_connect_already_connected(self):
|
|
"""Test connect() when already connected."""
|
|
client = APRSDClient(auto_connect=False)
|
|
client.connected = True
|
|
client.connect()
|
|
# Should still be connected
|
|
self.assertTrue(client.connected)
|
|
|
|
def test_connect_no_driver(self):
|
|
"""Test connect() when driver is None."""
|
|
client = APRSDClient(auto_connect=False)
|
|
client.driver = None
|
|
client.connect()
|
|
# Should get a driver from registry
|
|
self.assertIsNotNone(client.driver)
|
|
|
|
def test_close(self):
|
|
"""Test close() method."""
|
|
client = APRSDClient(auto_connect=False)
|
|
client.connected = True
|
|
client.running = True
|
|
client.close()
|
|
self.assertFalse(client.connected)
|
|
self.assertFalse(client.running)
|
|
self.mock_driver.close.assert_called()
|
|
|
|
def test_close_no_driver(self):
|
|
"""Test close() when driver is None."""
|
|
client = APRSDClient(auto_connect=False)
|
|
client.driver = None
|
|
client.connected = True
|
|
client.close()
|
|
self.assertFalse(client.connected)
|
|
|
|
def test_reset(self):
|
|
"""Test reset() method."""
|
|
client = APRSDClient(auto_connect=False)
|
|
client.connected = True
|
|
client.filter = 'test filter'
|
|
client.auto_connect = True
|
|
|
|
client.reset()
|
|
self.mock_driver.close.assert_called()
|
|
self.mock_driver.setup_connection.assert_called()
|
|
self.mock_driver.set_filter.assert_called_with('test filter')
|
|
|
|
def test_reset_no_driver(self):
|
|
"""Test reset() when driver is None."""
|
|
client = APRSDClient(auto_connect=False)
|
|
client.driver = None
|
|
# Should not raise exception
|
|
client.reset()
|
|
|
|
def test_reset_no_auto_connect(self):
|
|
"""Test reset() with auto_connect=False."""
|
|
client = APRSDClient(auto_connect=False)
|
|
client.auto_connect = False
|
|
client.reset()
|
|
self.mock_driver.close.assert_called()
|
|
# Should not call setup_connection
|
|
self.mock_driver.setup_connection.assert_not_called()
|
|
|
|
def test_send(self):
|
|
"""Test send() method."""
|
|
client = APRSDClient(auto_connect=False)
|
|
client.running = True
|
|
packet = mock.MagicMock(spec=core.Packet)
|
|
|
|
result = client.send(packet)
|
|
self.assertTrue(result)
|
|
self.mock_driver.send.assert_called_with(packet)
|
|
|
|
def test_send_not_running(self):
|
|
"""Test send() when not running."""
|
|
client = APRSDClient(auto_connect=False)
|
|
client.running = False
|
|
packet = mock.MagicMock(spec=core.Packet)
|
|
|
|
result = client.send(packet)
|
|
self.assertFalse(result)
|
|
self.mock_driver.send.assert_not_called()
|
|
|
|
def test_consumer(self):
|
|
"""Test consumer() method."""
|
|
client = APRSDClient(auto_connect=False)
|
|
client.running = True
|
|
callback = mock.MagicMock()
|
|
|
|
client.consumer(callback, raw=True)
|
|
self.mock_driver.consumer.assert_called_with(callback=callback, raw=True)
|
|
|
|
def test_consumer_not_running(self):
|
|
"""Test consumer() when not running."""
|
|
client = APRSDClient(auto_connect=False)
|
|
client.running = False
|
|
callback = mock.MagicMock()
|
|
|
|
result = client.consumer(callback)
|
|
self.assertIsNone(result)
|
|
self.mock_driver.consumer.assert_not_called()
|
|
|
|
def test_decode_packet(self):
|
|
"""Test decode_packet() method."""
|
|
client = APRSDClient(auto_connect=False)
|
|
packet = mock.MagicMock(spec=core.Packet)
|
|
# Configure the side_effect to return our packet
|
|
self.mock_driver.decode_packet.side_effect = lambda *args, **kwargs: packet
|
|
|
|
result = client.decode_packet(frame='test')
|
|
self.assertEqual(result, packet)
|
|
self.mock_driver.decode_packet.assert_called_with(frame='test')
|
|
|
|
def test_decode_packet_exception(self):
|
|
"""Test decode_packet() with exception."""
|
|
client = APRSDClient(auto_connect=False)
|
|
self.mock_driver.decode_packet.side_effect = Exception('Decode error')
|
|
|
|
result = client.decode_packet(frame='test')
|
|
self.assertIsNone(result)
|
|
|
|
def test_keepalive_check(self):
|
|
"""Test keepalive_check() method."""
|
|
client = APRSDClient(auto_connect=False)
|
|
client._checks = False
|
|
self.mock_driver._alive = True
|
|
|
|
# First check should not reset
|
|
with mock.patch.object(client, 'reset') as mock_reset:
|
|
client.keepalive_check()
|
|
self.assertTrue(client._checks)
|
|
mock_reset.assert_not_called()
|
|
|
|
# Second check with dead driver should reset
|
|
self.mock_driver._alive = False
|
|
with mock.patch.object(client, 'reset') as mock_reset:
|
|
client.keepalive_check()
|
|
mock_reset.assert_called()
|
|
|
|
def test_keepalive_log(self):
|
|
"""Test keepalive_log() method."""
|
|
import datetime
|
|
|
|
client = APRSDClient(auto_connect=False)
|
|
self.mock_driver._keepalive = datetime.datetime.now()
|
|
|
|
with mock.patch('aprsd.client.client.LOGU') as mock_logu:
|
|
client.keepalive_log()
|
|
mock_logu.opt.assert_called()
|
|
|
|
def test_keepalive_log_no_keepalive(self):
|
|
"""Test keepalive_log() when keepalive is None."""
|
|
client = APRSDClient(auto_connect=False)
|
|
self.mock_driver._keepalive = None
|
|
|
|
with mock.patch('aprsd.client.client.LOGU') as mock_logu:
|
|
client.keepalive_log()
|
|
mock_logu.opt.assert_called()
|