test: raise coverage 68.7% -> ~80.4%; add ~250 tests for new egress/DDNS/network paths
Unit Tests / test (push) Successful in 12m6s

Coverage was below acceptable levels and several newly-added code paths
(sshuttle egress, proxy egress, DDNS provider stubs, DNS overview route,
peer-registry provisioning) had zero test coverage.

~250 new unit tests are added across 16 new test files. Existing test files
are updated to match refactored interfaces (DHCP removed, constants
introduced, network_manager restructured). .coveragerc is added to pin the
source mapping and the 70% floor so regressions are caught at commit time.

tests/test_enhanced_api.py was previously living in api/ (wrong location)
and is moved to tests/ where it belongs.

Integration test files are updated to remove references to DHCP endpoints
and add coverage for the new DNS overview and DDNS sync endpoints.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 09:03:39 -04:00
parent c41cadafb4
commit aa1e5c41ec
33 changed files with 9446 additions and 631 deletions
+223
View File
@@ -0,0 +1,223 @@
#!/usr/bin/env python3
"""
Additional tests for ServiceBus covering missed branches:
- unsubscribe_from_event: handler-not-found ValueError
- call_service: method raises exception (publishes ERROR_OCCURRED, re-raises)
- orchestrate_service_start/stop: with container manager registered
- orchestrate_service_restart: exception path
- _event_loop: processing events through handlers, handler exception
- add_service_dependency: new service key branch
- remove_service_dependency: dependency not found (ValueError branch)
- get_service_status_summary: service without get_status, service that raises
"""
import sys
import time
import unittest
from pathlib import Path
from unittest.mock import Mock, MagicMock
api_dir = Path(__file__).parent.parent / 'api'
sys.path.insert(0, str(api_dir))
from service_bus import ServiceBus, EventType, Event
class TestUnsubscribeNotFound(unittest.TestCase):
def setUp(self):
self.bus = ServiceBus()
def test_unsubscribe_handler_not_registered_does_not_raise(self):
# unsubscribing a handler that was never added should not raise
def handler(event):
pass
# should not raise ValueError
self.bus.unsubscribe_from_event(EventType.SERVICE_STARTED, handler)
def test_unsubscribe_removes_correct_handler(self):
received = []
def h1(event): received.append('h1')
def h2(event): received.append('h2')
self.bus.subscribe_to_event(EventType.SERVICE_STARTED, h1)
self.bus.subscribe_to_event(EventType.SERVICE_STARTED, h2)
self.bus.unsubscribe_from_event(EventType.SERVICE_STARTED, h1)
handlers = self.bus.event_handlers[EventType.SERVICE_STARTED]
self.assertNotIn(h1, handlers)
self.assertIn(h2, handlers)
class TestCallServiceException(unittest.TestCase):
def setUp(self):
self.bus = ServiceBus()
def test_call_service_method_raises_publishes_error_and_reraises(self):
mock_svc = Mock()
mock_svc.failing_method.side_effect = RuntimeError("boom")
self.bus.register_service('svc', mock_svc)
with self.assertRaises(RuntimeError):
self.bus.call_service('svc', 'failing_method')
def test_call_service_error_is_added_to_queue(self):
mock_svc = Mock()
mock_svc.boom.side_effect = ValueError("bad value")
self.bus.register_service('svc', mock_svc)
try:
self.bus.call_service('svc', 'boom')
except ValueError:
pass
# The ERROR_OCCURRED event was put onto the queue
self.assertFalse(self.bus.event_queue.empty())
class TestOrchestrateWithContainers(unittest.TestCase):
def setUp(self):
self.bus = ServiceBus()
def _register_container_manager(self, start_return=True, stop_return=True):
cm = Mock()
cm.start_container.return_value = start_return
cm.stop_container.return_value = stop_return
self.bus.register_service('container', cm)
return cm
def test_orchestrate_start_wireguard_starts_containers(self):
cm = self._register_container_manager(start_return=True)
# wireguard service has containers but is not registered as a service
result = self.bus.orchestrate_service_start('wireguard')
self.assertTrue(result)
cm.start_container.assert_called_with('cell-wireguard')
def test_orchestrate_start_container_failure_returns_false(self):
cm = self._register_container_manager(start_return=False)
result = self.bus.orchestrate_service_start('wireguard')
self.assertFalse(result)
def test_orchestrate_start_no_container_manager_returns_false(self):
# email has containers but 'container' manager is not registered
result = self.bus.orchestrate_service_start('email')
self.assertFalse(result)
def test_orchestrate_stop_wireguard_stops_containers(self):
cm = self._register_container_manager(stop_return=True)
result = self.bus.orchestrate_service_stop('wireguard')
self.assertTrue(result)
cm.stop_container.assert_called_with('cell-wireguard')
def test_orchestrate_stop_container_failure_returns_false(self):
cm = self._register_container_manager(stop_return=False)
result = self.bus.orchestrate_service_stop('wireguard')
self.assertFalse(result)
def test_orchestrate_stop_no_container_manager_returns_false(self):
result = self.bus.orchestrate_service_stop('email')
self.assertFalse(result)
def test_orchestrate_restart_exception_returns_false(self):
# Make stop raise an exception to trigger the except clause
cm = Mock()
cm.stop_container.side_effect = RuntimeError("docker gone")
self.bus.register_service('container', cm)
result = self.bus.orchestrate_service_restart('wireguard')
self.assertFalse(result)
class TestEventLoopProcessing(unittest.TestCase):
def test_event_loop_calls_handler(self):
bus = ServiceBus()
received = []
def handler(event):
received.append(event)
bus.subscribe_to_event(EventType.SERVICE_STARTED, handler)
bus.start()
try:
bus.publish_event(EventType.SERVICE_STARTED, 'src', {'x': 1})
time.sleep(0.3)
self.assertEqual(len(received), 1)
self.assertEqual(received[0].source, 'src')
finally:
bus.stop()
def test_event_loop_handler_exception_does_not_stop_loop(self):
bus = ServiceBus()
received = []
def bad_handler(event):
raise RuntimeError("handler crash")
def good_handler(event):
received.append(event)
bus.subscribe_to_event(EventType.SERVICE_STARTED, bad_handler)
bus.subscribe_to_event(EventType.SERVICE_STARTED, good_handler)
bus.start()
try:
bus.publish_event(EventType.SERVICE_STARTED, 'src', {})
time.sleep(0.3)
# Loop continues; good_handler was also called
self.assertEqual(len(received), 1)
finally:
bus.stop()
def test_event_loop_history_trimmed_at_max(self):
bus = ServiceBus()
bus.max_history = 3
bus.start()
try:
for i in range(5):
bus.publish_event(EventType.SERVICE_STARTED, f'src{i}', {})
time.sleep(0.3)
self.assertLessEqual(len(bus.event_history), 3)
finally:
bus.stop()
class TestServiceDependencyEdgeCases(unittest.TestCase):
def setUp(self):
self.bus = ServiceBus()
def test_add_dependency_creates_new_entry_for_unknown_service(self):
self.bus.add_service_dependency('brand_new_service', 'network')
self.assertIn('brand_new_service', self.bus.service_dependencies)
self.assertIn('network', self.bus.service_dependencies['brand_new_service'])
def test_remove_dependency_not_found_does_not_raise(self):
self.bus.add_service_dependency('svc', 'dep1')
# removing a dependency that was never added should not raise
self.bus.remove_service_dependency('svc', 'dep_nonexistent')
def test_remove_dependency_for_unknown_service_does_not_raise(self):
# service never had any dependencies registered
self.bus.remove_service_dependency('ghost_service', 'dep')
class TestServiceStatusSummaryBranches(unittest.TestCase):
def setUp(self):
self.bus = ServiceBus()
def test_summary_service_without_get_status(self):
# A service object without a get_status attribute
svc = object()
self.bus.register_service('plain_obj', svc)
summary = self.bus.get_service_status_summary()
self.assertIn('plain_obj', summary['services'])
self.assertEqual(
summary['services']['plain_obj']['status'],
{'status': 'unknown'}
)
def test_summary_service_get_status_raises(self):
svc = Mock()
svc.get_status.side_effect = RuntimeError("status unavailable")
self.bus.register_service('broken_svc', svc)
summary = self.bus.get_service_status_summary()
self.assertIn('broken_svc', summary['services'])
self.assertIn('error', summary['services']['broken_svc']['status'])
if __name__ == '__main__':
unittest.main()