Remediate the 2026-09-03 feature-test findings
This commit is contained in:
80
tests/test_plugins/test_network_validation.py
Normal file
80
tests/test_plugins/test_network_validation.py
Normal file
@@ -0,0 +1,80 @@
|
||||
"""Network reference data has to survive being typed at.
|
||||
|
||||
Three findings from the 2026-09-03 run, all in the same family: a validation
|
||||
that looked like one and was not.
|
||||
|
||||
* cidr was checked for a '/' and nothing else, so 'notanip/xx' was stored.
|
||||
_devices_in_subnet then swallowed the ValueError from ip_network and
|
||||
returned an empty list, so the subnet looked real and permanently empty.
|
||||
* vlannumber had no range check, so 99999 and -5 were accepted - and a
|
||||
non-numeric value was worse, because the duplicate probe compared a string
|
||||
against an integer column, which MySQL coerces to 0, so 'xyz' matched an
|
||||
existing VLAN 0 and the 409 named a row that had nothing to do with it.
|
||||
* deleting a VLAN that subnets still reference answered 400, while every
|
||||
sibling in-use guard in the product answers 409.
|
||||
"""
|
||||
import pytest
|
||||
|
||||
|
||||
def _vlan(client, headers, number, name='Test VLAN'):
|
||||
return client.post('/api/network/vlans', headers=headers,
|
||||
json={'vlannumber': number, 'name': name})
|
||||
|
||||
|
||||
@pytest.mark.parametrize('cidr', [
|
||||
'notanip/xx',
|
||||
'10.1.1.0/33',
|
||||
'hello/24',
|
||||
'10.1.1.0',
|
||||
'/24',
|
||||
])
|
||||
def test_a_subnet_refuses_something_that_is_not_a_network(client, db,
|
||||
auth_headers, cidr):
|
||||
response = client.post('/api/network/subnets', headers=auth_headers,
|
||||
json={'cidr': cidr, 'name': 'Bad subnet'})
|
||||
assert response.status_code == 400, (cidr, response.get_json())
|
||||
|
||||
|
||||
def test_a_real_subnet_is_accepted_and_normalised(client, db, auth_headers):
|
||||
"""A host address with a prefix is the same network; storing both spellings
|
||||
would make two rows nobody can tell apart."""
|
||||
response = client.post('/api/network/subnets', headers=auth_headers,
|
||||
json={'cidr': '10.1.1.5/24', 'name': 'Real subnet'})
|
||||
assert response.status_code in (200, 201), response.get_json()
|
||||
assert response.get_json()['data']['cidr'] == '10.1.1.0/24'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('number', [0, 4095, 99999, -5, 'xyz'])
|
||||
def test_a_vlan_number_outside_the_standard_is_refused(client, db,
|
||||
auth_headers, number):
|
||||
response = _vlan(client, auth_headers, number)
|
||||
assert response.status_code == 400, (number, response.get_json())
|
||||
|
||||
|
||||
@pytest.mark.parametrize('number', [1, 100, 4094])
|
||||
def test_a_valid_vlan_number_is_accepted(client, db, auth_headers, number):
|
||||
response = _vlan(client, auth_headers, number, name='VLAN %s' % number)
|
||||
assert response.status_code in (200, 201), response.get_json()
|
||||
|
||||
|
||||
def test_a_duplicate_vlan_names_the_number_it_actually_clashed_with(
|
||||
client, db, auth_headers):
|
||||
assert _vlan(client, auth_headers, 42, name='First').status_code in (200, 201)
|
||||
clash = _vlan(client, auth_headers, 42, name='Second')
|
||||
assert clash.status_code == 409
|
||||
assert '42' in clash.get_json()['data']['error']['message']
|
||||
|
||||
|
||||
def test_deleting_a_vlan_in_use_is_a_conflict_like_every_other_guard(
|
||||
client, db, auth_headers):
|
||||
created = _vlan(client, auth_headers, 77, name='In use')
|
||||
vlanid = created.get_json()['data']['vlanid']
|
||||
subnet = client.post('/api/network/subnets', headers=auth_headers,
|
||||
json={'cidr': '10.7.7.0/24', 'name': 'Attached',
|
||||
'vlanid': vlanid})
|
||||
assert subnet.status_code in (200, 201), subnet.get_json()
|
||||
|
||||
refused = client.delete('/api/network/vlans/%d' % vlanid,
|
||||
headers=auth_headers)
|
||||
assert refused.status_code == 409, refused.get_json()
|
||||
assert refused.get_json()['data']['error']['code'] == 'CONFLICT'
|
||||
Reference in New Issue
Block a user