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
+26 -137
View File
@@ -1,15 +1,13 @@
#!/usr/bin/env python3
"""
Unit tests for network/DNS/DHCP Flask endpoints in api/app.py.
Unit tests for network/DNS Flask endpoints in api/app.py.
Covers:
GET /api/dns/records
POST /api/dns/records
DELETE /api/dns/records
GET /api/dns/status
GET /api/dhcp/leases
POST /api/dhcp/reservations
DELETE /api/dhcp/reservations
GET /api/dns/overview
GET /api/network/info
POST /api/network/test
"""
@@ -150,149 +148,40 @@ class TestGetDnsStatus(unittest.TestCase):
self.assertIn('error', json.loads(r.data))
class TestGetDhcpLeases(unittest.TestCase):
class TestGetDnsOverview(unittest.TestCase):
def setUp(self):
app.config['TESTING'] = True
self.client = app.test_client()
@patch('app.ddns_manager')
@patch('app.config_manager')
@patch('app.network_manager')
def test_get_dhcp_leases_returns_200_with_list(self, mock_nm):
mock_nm.get_dhcp_leases.return_value = [
{'mac': 'aa:bb:cc:dd:ee:ff', 'ip': '192.168.1.101', 'hostname': 'laptop'},
]
r = self.client.get('/api/dhcp/leases')
def test_get_dns_overview_returns_200(self, mock_nm, mock_cm, mock_dm):
mock_nm.get_dns_overview.return_value = {
'mode': 'pic_ngo',
'provider': 'pic_ngo',
'effective_domain': 'mycell.pic.ngo',
'internal_domain': 'cell',
'public_ip': '1.2.3.4',
'public_records': [],
'internal_records': [],
'service_subdomains': [],
'registration_status': {'registered': True},
}
r = self.client.get('/api/dns/overview')
self.assertEqual(r.status_code, 200)
data = json.loads(r.data)
self.assertIsInstance(data, list)
self.assertEqual(len(data), 1)
self.assertEqual(data[0]['hostname'], 'laptop')
self.assertEqual(data['mode'], 'pic_ngo')
self.assertEqual(data['effective_domain'], 'mycell.pic.ngo')
mock_nm.get_dns_overview.assert_called_once_with(mock_cm, mock_dm)
@patch('app.ddns_manager')
@patch('app.config_manager')
@patch('app.network_manager')
def test_get_dhcp_leases_returns_empty_list_when_no_leases(self, mock_nm):
mock_nm.get_dhcp_leases.return_value = []
r = self.client.get('/api/dhcp/leases')
self.assertEqual(r.status_code, 200)
self.assertEqual(json.loads(r.data), [])
@patch('app.network_manager')
def test_get_dhcp_leases_returns_500_on_exception(self, mock_nm):
mock_nm.get_dhcp_leases.side_effect = Exception('dnsmasq not running')
r = self.client.get('/api/dhcp/leases')
self.assertEqual(r.status_code, 500)
self.assertIn('error', json.loads(r.data))
class TestAddDhcpReservation(unittest.TestCase):
def setUp(self):
app.config['TESTING'] = True
self.client = app.test_client()
@patch('app.network_manager')
def test_add_reservation_returns_200_on_valid_body(self, mock_nm):
mock_nm.add_dhcp_reservation.return_value = True
r = self.client.post(
'/api/dhcp/reservations',
data=json.dumps({'mac': 'aa:bb:cc:dd:ee:ff', 'ip': '192.168.1.50', 'hostname': 'printer'}),
content_type='application/json',
)
self.assertEqual(r.status_code, 200)
data = json.loads(r.data)
self.assertIn('success', data)
@patch('app.network_manager')
def test_add_reservation_returns_400_when_no_body(self, mock_nm):
r = self.client.post('/api/dhcp/reservations')
self.assertEqual(r.status_code, 400)
self.assertIn('error', json.loads(r.data))
mock_nm.add_dhcp_reservation.assert_not_called()
@patch('app.network_manager')
def test_add_reservation_returns_400_when_mac_missing(self, mock_nm):
r = self.client.post(
'/api/dhcp/reservations',
data=json.dumps({'ip': '192.168.1.50'}),
content_type='application/json',
)
self.assertEqual(r.status_code, 400)
self.assertIn('error', json.loads(r.data))
@patch('app.network_manager')
def test_add_reservation_returns_400_when_ip_missing(self, mock_nm):
r = self.client.post(
'/api/dhcp/reservations',
data=json.dumps({'mac': 'aa:bb:cc:dd:ee:ff'}),
content_type='application/json',
)
self.assertEqual(r.status_code, 400)
self.assertIn('error', json.loads(r.data))
@patch('app.network_manager')
def test_add_reservation_uses_empty_hostname_when_omitted(self, mock_nm):
mock_nm.add_dhcp_reservation.return_value = True
self.client.post(
'/api/dhcp/reservations',
data=json.dumps({'mac': 'aa:bb:cc:dd:ee:ff', 'ip': '192.168.1.50'}),
content_type='application/json',
)
mock_nm.add_dhcp_reservation.assert_called_once_with('aa:bb:cc:dd:ee:ff', '192.168.1.50', '')
@patch('app.network_manager')
def test_add_reservation_returns_500_on_exception(self, mock_nm):
mock_nm.add_dhcp_reservation.side_effect = Exception('dnsmasq config error')
r = self.client.post(
'/api/dhcp/reservations',
data=json.dumps({'mac': 'aa:bb:cc:dd:ee:ff', 'ip': '192.168.1.50'}),
content_type='application/json',
)
self.assertEqual(r.status_code, 500)
self.assertIn('error', json.loads(r.data))
class TestDeleteDhcpReservation(unittest.TestCase):
def setUp(self):
app.config['TESTING'] = True
self.client = app.test_client()
@patch('app.network_manager')
def test_delete_reservation_returns_200_on_success(self, mock_nm):
mock_nm.remove_dhcp_reservation.return_value = True
r = self.client.delete(
'/api/dhcp/reservations',
data=json.dumps({'mac': 'aa:bb:cc:dd:ee:ff'}),
content_type='application/json',
)
self.assertEqual(r.status_code, 200)
data = json.loads(r.data)
self.assertIn('success', data)
@patch('app.network_manager')
def test_delete_reservation_returns_400_when_mac_missing(self, mock_nm):
r = self.client.delete(
'/api/dhcp/reservations',
data=json.dumps({'hostname': 'printer'}),
content_type='application/json',
)
self.assertEqual(r.status_code, 400)
self.assertIn('error', json.loads(r.data))
mock_nm.remove_dhcp_reservation.assert_not_called()
@patch('app.network_manager')
def test_delete_reservation_returns_400_when_no_body(self, mock_nm):
r = self.client.delete('/api/dhcp/reservations')
self.assertEqual(r.status_code, 400)
self.assertIn('error', json.loads(r.data))
@patch('app.network_manager')
def test_delete_reservation_returns_500_on_exception(self, mock_nm):
mock_nm.remove_dhcp_reservation.side_effect = Exception('reservation not found')
r = self.client.delete(
'/api/dhcp/reservations',
data=json.dumps({'mac': 'aa:bb:cc:dd:ee:ff'}),
content_type='application/json',
)
def test_get_dns_overview_returns_500_on_exception(self, mock_nm, mock_cm, mock_dm):
mock_nm.get_dns_overview.side_effect = Exception('boom')
r = self.client.get('/api/dns/overview')
self.assertEqual(r.status_code, 500)
self.assertIn('error', json.loads(r.data))