test: raise coverage 68.7% -> ~80.4%; add ~250 tests for new egress/DDNS/network paths
Unit Tests / test (push) Successful in 12m6s
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:
+26
-137
@@ -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))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user