mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 20:12:00 -06:00
Enable bulk creation tests for device components
This commit is contained in:
parent
75906f7591
commit
0ad613e6b4
@ -2952,9 +2952,10 @@ class FrontPortCreateForm(ComponentForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
parent = Device.objects.get(pk=self.initial['device'])
|
||||
parent = Device.objects.get(pk=self.initial.get('device'))
|
||||
|
||||
# Determine which rear port positions are occupied. These will be excluded from the list of available mappings.
|
||||
# Determine which rear port positions are occupied. These will be excluded from the list of available
|
||||
# mappings.
|
||||
occupied_port_positions = [
|
||||
(front_port.rear_port_id, front_port.rear_port_position)
|
||||
for front_port in parent.frontports.all()
|
||||
|
@ -682,10 +682,11 @@ class ConsolePortTestCase(StandardTestCases.Views):
|
||||
|
||||
# Disable inapplicable views
|
||||
test_get_object = None
|
||||
test_create_object = None
|
||||
test_bulk_edit_objects = None
|
||||
|
||||
# TODO
|
||||
test_create_object = None
|
||||
def test_bulk_create_objects(self):
|
||||
return self._test_bulk_create_objects(expected_count=3)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -717,6 +718,14 @@ class ConsolePortTestCase(StandardTestCases.Views):
|
||||
"Device 1,Console Port 6",
|
||||
)
|
||||
|
||||
cls.bulk_create_data = {
|
||||
'device': device.pk,
|
||||
'name_pattern': 'Console Port [4-6]',
|
||||
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
||||
'description': 'A console port',
|
||||
'tags': 'Alpha,Bravo,Charlie',
|
||||
}
|
||||
|
||||
|
||||
class ConsoleServerPortTestCase(StandardTestCases.Views):
|
||||
model = ConsoleServerPort
|
||||
@ -727,6 +736,9 @@ class ConsoleServerPortTestCase(StandardTestCases.Views):
|
||||
# TODO
|
||||
test_create_object = None
|
||||
|
||||
def test_bulk_create_objects(self):
|
||||
return self._test_bulk_create_objects(expected_count=3)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
device = create_test_device('Device 1')
|
||||
@ -756,6 +768,14 @@ class ConsoleServerPortTestCase(StandardTestCases.Views):
|
||||
"Device 1,Console Server Port 6",
|
||||
)
|
||||
|
||||
cls.bulk_create_data = {
|
||||
'device': device.pk,
|
||||
'name_pattern': 'Console Server Port [4-6]',
|
||||
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
||||
'description': 'A console server port',
|
||||
'tags': 'Alpha,Bravo,Charlie',
|
||||
}
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'device': device.pk,
|
||||
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
||||
@ -773,6 +793,9 @@ class PowerPortTestCase(StandardTestCases.Views):
|
||||
# TODO
|
||||
test_create_object = None
|
||||
|
||||
def test_bulk_create_objects(self):
|
||||
return self._test_bulk_create_objects(expected_count=3)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
device = create_test_device('Device 1')
|
||||
@ -804,6 +827,16 @@ class PowerPortTestCase(StandardTestCases.Views):
|
||||
"Device 1,Power Port 6",
|
||||
)
|
||||
|
||||
cls.bulk_create_data = {
|
||||
'device': device.pk,
|
||||
'name_pattern': 'Power Port [4-6]]',
|
||||
'type': PowerPortTypeChoices.TYPE_IEC_C14,
|
||||
'maximum_draw': 100,
|
||||
'allocated_draw': 50,
|
||||
'description': 'A power port',
|
||||
'tags': 'Alpha,Bravo,Charlie',
|
||||
}
|
||||
|
||||
|
||||
class PowerOutletTestCase(StandardTestCases.Views):
|
||||
model = PowerOutlet
|
||||
@ -814,6 +847,9 @@ class PowerOutletTestCase(StandardTestCases.Views):
|
||||
# TODO
|
||||
test_create_object = None
|
||||
|
||||
def test_bulk_create_objects(self):
|
||||
return self._test_bulk_create_objects(expected_count=3)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
device = create_test_device('Device 1')
|
||||
@ -851,6 +887,16 @@ class PowerOutletTestCase(StandardTestCases.Views):
|
||||
"Device 1,Power Outlet 6",
|
||||
)
|
||||
|
||||
cls.bulk_create_data = {
|
||||
'device': device.pk,
|
||||
'name_pattern': 'Power Outlet [4-6]',
|
||||
'type': PowerOutletTypeChoices.TYPE_IEC_C13,
|
||||
'power_port': powerports[1].pk,
|
||||
'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
|
||||
'description': 'A power outlet',
|
||||
'tags': 'Alpha,Bravo,Charlie',
|
||||
}
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'device': device.pk,
|
||||
'type': PowerOutletTypeChoices.TYPE_IEC_C13,
|
||||
@ -866,6 +912,9 @@ class InterfaceTestCase(StandardTestCases.Views):
|
||||
# TODO
|
||||
test_create_object = None
|
||||
|
||||
def test_bulk_create_objects(self):
|
||||
return self._test_bulk_create_objects(expected_count=3)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
device = create_test_device('Device 1')
|
||||
@ -914,6 +963,22 @@ class InterfaceTestCase(StandardTestCases.Views):
|
||||
"Device 1,Interface 6,1000BASE-T (1GE)",
|
||||
)
|
||||
|
||||
cls.bulk_create_data = {
|
||||
'device': device.pk,
|
||||
'name_pattern': 'Interface [4-6]',
|
||||
'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
|
||||
'enabled': False,
|
||||
'lag': interfaces[3].pk,
|
||||
'mac_address': EUI('01:02:03:04:05:06'),
|
||||
'mtu': 2000,
|
||||
'mgmt_only': True,
|
||||
'description': 'A front port',
|
||||
'mode': InterfaceModeChoices.MODE_TAGGED,
|
||||
'untagged_vlan': vlans[0].pk,
|
||||
'tagged_vlans': [v.pk for v in vlans[1:4]],
|
||||
'tags': 'Alpha,Bravo,Charlie',
|
||||
}
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'device': device.pk,
|
||||
'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
|
||||
@ -938,6 +1003,9 @@ class FrontPortTestCase(StandardTestCases.Views):
|
||||
# TODO
|
||||
test_create_object = None
|
||||
|
||||
def test_bulk_create_objects(self):
|
||||
return self._test_bulk_create_objects(expected_count=3)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
device = create_test_device('Device 1')
|
||||
@ -978,6 +1046,17 @@ class FrontPortTestCase(StandardTestCases.Views):
|
||||
"Device 1,Front Port 6,8P8C,Rear Port 6,1",
|
||||
)
|
||||
|
||||
cls.bulk_create_data = {
|
||||
'device': device.pk,
|
||||
'name_pattern': 'Front Port [4-6]',
|
||||
'type': PortTypeChoices.TYPE_8P8C,
|
||||
'rear_port_set': [
|
||||
'{}:1'.format(rp.pk) for rp in rearports[3:6]
|
||||
],
|
||||
'description': 'New description',
|
||||
'tags': 'Alpha,Bravo,Charlie',
|
||||
}
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'type': PortTypeChoices.TYPE_8P8C,
|
||||
'description': 'New description',
|
||||
@ -993,6 +1072,9 @@ class RearPortTestCase(StandardTestCases.Views):
|
||||
# TODO
|
||||
test_create_object = None
|
||||
|
||||
def test_bulk_create_objects(self):
|
||||
return self._test_bulk_create_objects(expected_count=3)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
device = create_test_device('Device 1')
|
||||
@ -1022,6 +1104,15 @@ class RearPortTestCase(StandardTestCases.Views):
|
||||
"Device 1,Rear Port 6,8P8C,1",
|
||||
)
|
||||
|
||||
cls.bulk_create_data = {
|
||||
'device': device.pk,
|
||||
'name_pattern': 'Rear Port [4-6]',
|
||||
'type': PortTypeChoices.TYPE_8P8C,
|
||||
'positions': 3,
|
||||
'description': 'A rear port',
|
||||
'tags': 'Alpha,Bravo,Charlie',
|
||||
}
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'type': PortTypeChoices.TYPE_8P8C,
|
||||
'description': 'New description',
|
||||
@ -1033,11 +1124,14 @@ class DeviceBayTestCase(StandardTestCases.Views):
|
||||
|
||||
# Disable inapplicable views
|
||||
test_get_object = None
|
||||
test_create_object = None
|
||||
|
||||
# TODO
|
||||
test_create_object = None
|
||||
test_bulk_edit_objects = None
|
||||
|
||||
def test_bulk_create_objects(self):
|
||||
return self._test_bulk_create_objects(expected_count=3)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
device1 = create_test_device('Device 1')
|
||||
@ -1069,6 +1163,13 @@ class DeviceBayTestCase(StandardTestCases.Views):
|
||||
"Device 1,Device Bay 6",
|
||||
)
|
||||
|
||||
cls.bulk_create_data = {
|
||||
'device': device2.pk,
|
||||
'name_pattern': 'Device Bay [4-6]',
|
||||
'description': 'A device bay',
|
||||
'tags': 'Alpha,Bravo,Charlie',
|
||||
}
|
||||
|
||||
|
||||
class InventoryItemTestCase(StandardTestCases.Views):
|
||||
model = InventoryItem
|
||||
|
@ -1,5 +1,6 @@
|
||||
from django.contrib.auth.models import Permission, User
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.forms.models import model_to_dict as model_to_dict_
|
||||
from django.test import Client, TestCase as _TestCase, override_settings
|
||||
from django.urls import reverse, NoReverseMatch
|
||||
from rest_framework.test import APIClient
|
||||
@ -56,6 +57,30 @@ class TestCase(_TestCase):
|
||||
expected_status, response.status_code, getattr(response, 'data', 'No data')
|
||||
))
|
||||
|
||||
def assertInstanceEquals(self, instance, data):
|
||||
"""
|
||||
Compare a model instance to a dictionary, checking that its attribute values match those specified
|
||||
in the dictionary.
|
||||
"""
|
||||
model_dict = model_to_dict_(instance, fields=data.keys())
|
||||
|
||||
for key in list(model_dict.keys()):
|
||||
|
||||
# TODO: Differentiate between tags assigned to the instance and a M2M field for tags (ex: ConfigContext)
|
||||
if key == 'tags':
|
||||
model_dict[key] = ','.join(sorted([tag.name for tag in model_dict['tags']]))
|
||||
|
||||
# Convert ManyToManyField to list of instance PKs
|
||||
elif model_dict[key] and type(model_dict[key]) in (list, tuple) and hasattr(model_dict[key][0], 'pk'):
|
||||
model_dict[key] = [obj.pk for obj in model_dict[key]]
|
||||
|
||||
# Omit any dictionary keys which are not instance attributes
|
||||
relevant_data = {
|
||||
k: v for k, v in data.items() if hasattr(instance, k)
|
||||
}
|
||||
|
||||
self.assertDictEqual(model_dict, relevant_data)
|
||||
|
||||
|
||||
class APITestCase(TestCase):
|
||||
client_class = APIClient
|
||||
@ -92,6 +117,9 @@ class StandardTestCases:
|
||||
# CSV lines used for bulk import of new objects
|
||||
csv_data = ()
|
||||
|
||||
# Form data used when creating multiple objects
|
||||
bulk_create_data = {}
|
||||
|
||||
# Form data to be used when editing multiple objects at once
|
||||
bulk_edit_data = {}
|
||||
|
||||
@ -104,6 +132,10 @@ class StandardTestCases:
|
||||
if self.model is None:
|
||||
raise Exception("Test case requires model to be defined")
|
||||
|
||||
#
|
||||
# URL functions
|
||||
#
|
||||
|
||||
def _get_base_url(self):
|
||||
"""
|
||||
Return the base format for a URL for the test's model. Override this to test for a model which belongs
|
||||
@ -138,6 +170,13 @@ class StandardTestCases:
|
||||
else:
|
||||
raise Exception("Invalid action for URL resolution: {}".format(action))
|
||||
|
||||
#
|
||||
# Standard view tests
|
||||
# These methods will run by default. To disable a test, nullify its method on the subclasses TestCase:
|
||||
#
|
||||
# test_list_objects = None
|
||||
#
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
|
||||
def test_list_objects(self):
|
||||
# Attempt to make the request without required permissions
|
||||
@ -331,3 +370,32 @@ class StandardTestCases:
|
||||
|
||||
# Check that all objects were deleted
|
||||
self.assertEqual(self.model.objects.count(), 0)
|
||||
|
||||
#
|
||||
# Optional view tests
|
||||
# These methods will run only if the required data
|
||||
#
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
|
||||
def _test_bulk_create_objects(self, expected_count):
|
||||
initial_count = self.model.objects.count()
|
||||
request = {
|
||||
'path': self._get_url('add'),
|
||||
'data': post_data(self.bulk_create_data),
|
||||
'follow': False, # Do not follow 302 redirects
|
||||
}
|
||||
|
||||
# Attempt to make the request without required permissions
|
||||
with disable_warnings('django.request'):
|
||||
self.assertHttpStatus(self.client.post(**request), 403)
|
||||
|
||||
# Assign the required permission and submit again
|
||||
self.add_permissions(
|
||||
'{}.add_{}'.format(self.model._meta.app_label, self.model._meta.model_name)
|
||||
)
|
||||
response = self.client.post(**request)
|
||||
self.assertHttpStatus(response, 302)
|
||||
|
||||
self.assertEqual(initial_count + expected_count, self.model.objects.count())
|
||||
for instance in self.model.objects.order_by('-pk')[:expected_count]:
|
||||
self.assertInstanceEquals(instance, self.bulk_create_data)
|
||||
|
@ -812,7 +812,7 @@ class InterfaceCreateForm(ComponentForm):
|
||||
(group.name, [(vlan.pk, vlan) for vlan in global_group_vlans])
|
||||
)
|
||||
|
||||
parent = VirtualMachine.objects.get(pk=self.initial['virtual_machine'])
|
||||
parent = VirtualMachine.objects.get(pk=self.initial.get('virtual_machine'))
|
||||
site = getattr(parent.cluster, 'site', None)
|
||||
if site is not None:
|
||||
|
||||
|
@ -198,10 +198,11 @@ class InterfaceTestCase(StandardTestCases.Views):
|
||||
|
||||
# Disable inapplicable tests
|
||||
test_list_objects = None
|
||||
test_create_object = None
|
||||
test_import_objects = None
|
||||
|
||||
# TODO
|
||||
test_create_object = None
|
||||
def test_bulk_create_objects(self):
|
||||
return self._test_bulk_create_objects(expected_count=3)
|
||||
|
||||
def _get_base_url(self):
|
||||
# Interface belongs to the DCIM app, so we have to override the base URL
|
||||
@ -262,6 +263,21 @@ class InterfaceTestCase(StandardTestCases.Views):
|
||||
"Device 1,Interface 6,1000BASE-T (1GE)",
|
||||
)
|
||||
|
||||
cls.bulk_create_data = {
|
||||
'virtual_machine': virtualmachines[1].pk,
|
||||
'name_pattern': 'Interface [4-6]',
|
||||
'type': InterfaceTypeChoices.TYPE_VIRTUAL,
|
||||
'enabled': False,
|
||||
'mgmt_only': False,
|
||||
'mac_address': EUI('01-02-03-04-05-06'),
|
||||
'mtu': 2000,
|
||||
'description': 'New description',
|
||||
'mode': InterfaceModeChoices.MODE_TAGGED,
|
||||
'untagged_vlan': vlans[0].pk,
|
||||
'tagged_vlans': [v.pk for v in vlans[1:4]],
|
||||
'tags': 'Alpha,Bravo,Charlie',
|
||||
}
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'virtual_machine': virtualmachines[1].pk,
|
||||
'enabled': False,
|
||||
|
Loading…
Reference in New Issue
Block a user