diff --git a/netbox/circuits/tests/test_views.py b/netbox/circuits/tests/test_views.py index 38365521a..3356fca8f 100644 --- a/netbox/circuits/tests/test_views.py +++ b/netbox/circuits/tests/test_views.py @@ -17,6 +17,8 @@ class ProviderTestCase(ViewTestCases.PrimaryObjectViewTestCase): Provider(name='Provider 3', slug='provider-3', asn=65003), ]) + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + cls.form_data = { 'name': 'Provider X', 'slug': 'provider-x', @@ -26,7 +28,7 @@ class ProviderTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'noc_contact': 'noc@example.com', 'admin_contact': 'admin@example.com', 'comments': 'Another provider', - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], } cls.csv_data = ( @@ -96,6 +98,8 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase): Circuit(cid='Circuit 3', provider=providers[0], type=circuittypes[0]), ]) + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + cls.form_data = { 'cid': 'Circuit X', 'provider': providers[1].pk, @@ -106,7 +110,7 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'commit_rate': 1000, 'description': 'A new circuit', 'comments': 'Some comments', - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], } cls.csv_data = ( diff --git a/netbox/dcim/tests/test_views.py b/netbox/dcim/tests/test_views.py index a6ce89ec4..b9a02b318 100644 --- a/netbox/dcim/tests/test_views.py +++ b/netbox/dcim/tests/test_views.py @@ -76,6 +76,8 @@ class SiteTestCase(ViewTestCases.PrimaryObjectViewTestCase): Site(name='Site 3', slug='site-3', region=regions[0]), ]) + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + cls.form_data = { 'name': 'Site X', 'slug': 'site-x', @@ -94,7 +96,7 @@ class SiteTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'contact_phone': '123-555-9999', 'contact_email': 'hank@stricklandpropane.com', 'comments': 'Test site', - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], } cls.csv_data = ( @@ -196,13 +198,15 @@ class RackReservationTestCase(ViewTestCases.PrimaryObjectViewTestCase): RackReservation(rack=rack, user=user2, units=[7, 8, 9], description='Reservation 3'), ]) + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + cls.form_data = { 'rack': rack.pk, 'units': "10,11,12", 'user': user3.pk, 'tenant': None, 'description': 'Rack reservation', - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], } cls.csv_data = ( @@ -250,6 +254,8 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase): Rack(name='Rack 3', site=sites[0]), )) + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + cls.form_data = { 'name': 'Rack X', 'facility_id': 'Facility X', @@ -268,7 +274,7 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'outer_depth': 500, 'outer_unit': RackDimensionUnitChoices.UNIT_MILLIMETER, 'comments': 'Some comments', - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], } cls.csv_data = ( @@ -350,6 +356,8 @@ class DeviceTypeTestCase( DeviceType(model='Device Type 3', slug='device-type-3', manufacturer=manufacturers[0]), ]) + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + cls.form_data = { 'manufacturer': manufacturers[1].pk, 'model': 'Device Type X', @@ -359,7 +367,7 @@ class DeviceTypeTestCase( 'is_full_depth': True, 'subdevice_role': '', # CharField 'comments': 'Some comments', - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], } cls.bulk_edit_data = { @@ -947,6 +955,8 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase): Device(name='Device 3', site=sites[0], rack=racks[0], device_type=devicetypes[0], device_role=deviceroles[0], platform=platforms[0]), ]) + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + cls.form_data = { 'device_type': devicetypes[1].pk, 'device_role': deviceroles[1].pk, @@ -967,7 +977,7 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'vc_position': None, 'vc_priority': None, 'comments': 'A new device', - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], 'local_context_data': None, } @@ -1008,7 +1018,7 @@ class ConsolePortTestCase(ViewTestCases.DeviceComponentViewTestCase): 'name': 'Console Port X', 'type': ConsolePortTypeChoices.TYPE_RJ45, 'description': 'A console port', - 'tags': tags, + 'tags': sorted([t.pk for t in tags]), } cls.bulk_create_data = { @@ -1018,7 +1028,7 @@ class ConsolePortTestCase(ViewTestCases.DeviceComponentViewTestCase): 'label_pattern': 'Serial[3-5]', 'type': ConsolePortTypeChoices.TYPE_RJ45, 'description': 'A console port', - 'tags': tags, + 'tags': sorted([t.pk for t in tags]), } cls.bulk_edit_data = { @@ -1054,7 +1064,7 @@ class ConsoleServerPortTestCase(ViewTestCases.DeviceComponentViewTestCase): 'name': 'Console Server Port X', 'type': ConsolePortTypeChoices.TYPE_RJ45, 'description': 'A console server port', - 'tags': tags, + 'tags': [t.pk for t in tags], } cls.bulk_create_data = { @@ -1062,7 +1072,7 @@ class ConsoleServerPortTestCase(ViewTestCases.DeviceComponentViewTestCase): 'name_pattern': 'Console Server Port [4-6]', 'type': ConsolePortTypeChoices.TYPE_RJ45, 'description': 'A console server port', - 'tags': tags, + 'tags': [t.pk for t in tags], } cls.bulk_edit_data = { @@ -1100,7 +1110,7 @@ class PowerPortTestCase(ViewTestCases.DeviceComponentViewTestCase): 'maximum_draw': 100, 'allocated_draw': 50, 'description': 'A power port', - 'tags': tags, + 'tags': [t.pk for t in tags], } cls.bulk_create_data = { @@ -1110,7 +1120,7 @@ class PowerPortTestCase(ViewTestCases.DeviceComponentViewTestCase): 'maximum_draw': 100, 'allocated_draw': 50, 'description': 'A power port', - 'tags': tags, + 'tags': [t.pk for t in tags], } cls.bulk_edit_data = { @@ -1156,7 +1166,7 @@ class PowerOutletTestCase(ViewTestCases.DeviceComponentViewTestCase): 'power_port': powerports[1].pk, 'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B, 'description': 'A power outlet', - 'tags': tags, + 'tags': [t.pk for t in tags], } cls.bulk_create_data = { @@ -1166,7 +1176,7 @@ class PowerOutletTestCase(ViewTestCases.DeviceComponentViewTestCase): 'power_port': powerports[1].pk, 'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B, 'description': 'A power outlet', - 'tags': tags, + 'tags': [t.pk for t in tags], } cls.bulk_edit_data = { @@ -1226,7 +1236,7 @@ class InterfaceTestCase( 'mode': InterfaceModeChoices.MODE_TAGGED, 'untagged_vlan': vlans[0].pk, 'tagged_vlans': [v.pk for v in vlans[1:4]], - 'tags': tags, + 'tags': [t.pk for t in tags], } cls.bulk_create_data = { @@ -1242,7 +1252,7 @@ class InterfaceTestCase( 'mode': InterfaceModeChoices.MODE_TAGGED, 'untagged_vlan': vlans[0].pk, 'tagged_vlans': [v.pk for v in vlans[1:4]], - 'tags': tags, + 'tags': [t.pk for t in tags], } cls.bulk_edit_data = { @@ -1298,7 +1308,7 @@ class FrontPortTestCase(ViewTestCases.DeviceComponentViewTestCase): 'rear_port': rearports[3].pk, 'rear_port_position': 1, 'description': 'New description', - 'tags': tags, + 'tags': [t.pk for t in tags], } cls.bulk_create_data = { @@ -1309,7 +1319,7 @@ class FrontPortTestCase(ViewTestCases.DeviceComponentViewTestCase): '{}:1'.format(rp.pk) for rp in rearports[3:6] ], 'description': 'New description', - 'tags': tags, + 'tags': [t.pk for t in tags], } cls.bulk_edit_data = { @@ -1346,7 +1356,7 @@ class RearPortTestCase(ViewTestCases.DeviceComponentViewTestCase): 'type': PortTypeChoices.TYPE_8P8C, 'positions': 3, 'description': 'A rear port', - 'tags': tags, + 'tags': [t.pk for t in tags], } cls.bulk_create_data = { @@ -1355,7 +1365,7 @@ class RearPortTestCase(ViewTestCases.DeviceComponentViewTestCase): 'type': PortTypeChoices.TYPE_8P8C, 'positions': 3, 'description': 'A rear port', - 'tags': tags, + 'tags': [t.pk for t in tags], } cls.bulk_edit_data = { @@ -1393,14 +1403,14 @@ class DeviceBayTestCase(ViewTestCases.DeviceComponentViewTestCase): 'device': device.pk, 'name': 'Device Bay X', 'description': 'A device bay', - 'tags': tags, + 'tags': [t.pk for t in tags], } cls.bulk_create_data = { 'device': device.pk, 'name_pattern': 'Device Bay [4-6]', 'description': 'A device bay', - 'tags': tags, + 'tags': [t.pk for t in tags], } cls.bulk_edit_data = { @@ -1441,7 +1451,7 @@ class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase): 'serial': '123ABC', 'asset_tag': 'ABC123', 'description': 'An inventory item', - 'tags': tags, + 'tags': [t.pk for t in tags], } cls.bulk_create_data = { @@ -1453,7 +1463,7 @@ class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase): 'part_id': '123456', 'serial': '123ABC', 'description': 'An inventory item', - 'tags': tags, + 'tags': [t.pk for t in tags], } cls.bulk_edit_data = { @@ -1518,6 +1528,8 @@ class CableTestCase( Cable(termination_a=interfaces[1], termination_b=interfaces[4], type=CableTypeChoices.TYPE_CAT6).save() Cable(termination_a=interfaces[2], termination_b=interfaces[5], type=CableTypeChoices.TYPE_CAT6).save() + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + interface_ct = ContentType.objects.get_for_model(Interface) cls.form_data = { # Changing terminations not supported when editing an existing Cable @@ -1531,7 +1543,7 @@ class CableTestCase( 'color': 'c0c0c0', 'length': 100, 'length_unit': CableLengthUnitChoices.UNIT_FOOT, - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], } cls.csv_data = ( @@ -1640,11 +1652,13 @@ class PowerPanelTestCase(ViewTestCases.PrimaryObjectViewTestCase): PowerPanel(site=sites[0], rack_group=rackgroups[0], name='Power Panel 3'), )) + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + cls.form_data = { 'site': sites[1].pk, 'rack_group': rackgroups[1].pk, 'name': 'Power Panel X', - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], } cls.csv_data = ( @@ -1686,6 +1700,8 @@ class PowerFeedTestCase(ViewTestCases.PrimaryObjectViewTestCase): PowerFeed(name='Power Feed 3', power_panel=powerpanels[0], rack=racks[0]), )) + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + cls.form_data = { 'name': 'Power Feed X', 'power_panel': powerpanels[1].pk, @@ -1698,7 +1714,7 @@ class PowerFeedTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'amperage': 100, 'max_utilization': 50, 'comments': 'New comments', - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], # Connection 'cable': None, diff --git a/netbox/ipam/tests/test_views.py b/netbox/ipam/tests/test_views.py index b704664c1..06090e768 100644 --- a/netbox/ipam/tests/test_views.py +++ b/netbox/ipam/tests/test_views.py @@ -27,13 +27,15 @@ class VRFTestCase(ViewTestCases.PrimaryObjectViewTestCase): VRF(name='VRF 3', rd='65000:3'), ]) + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + cls.form_data = { 'name': 'VRF X', 'rd': '65000:999', 'tenant': tenants[0].pk, 'enforce_unique': True, 'description': 'A new VRF', - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], } cls.csv_data = ( @@ -95,12 +97,14 @@ class AggregateTestCase(ViewTestCases.PrimaryObjectViewTestCase): Aggregate(prefix=IPNetwork('10.3.0.0/16'), rir=rirs[0]), ]) + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + cls.form_data = { 'prefix': IPNetwork('10.99.0.0/16'), 'rir': rirs[1].pk, 'date_added': datetime.date(2020, 1, 1), 'description': 'A new aggregate', - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], } cls.csv_data = ( @@ -173,6 +177,8 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase): Prefix(prefix=IPNetwork('10.3.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]), ]) + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + cls.form_data = { 'prefix': IPNetwork('192.0.2.0/24'), 'site': sites[1].pk, @@ -183,7 +189,7 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'role': roles[1].pk, 'is_pool': True, 'description': 'A new prefix', - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], } cls.csv_data = ( @@ -222,6 +228,8 @@ class IPAddressTestCase(ViewTestCases.PrimaryObjectViewTestCase): IPAddress(address=IPNetwork('192.0.2.3/24'), vrf=vrfs[0]), ]) + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + cls.form_data = { 'vrf': vrfs[1].pk, 'address': IPNetwork('192.0.2.99/24'), @@ -232,7 +240,7 @@ class IPAddressTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'nat_inside': None, 'dns_name': 'example', 'description': 'A new IP address', - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], } cls.csv_data = ( @@ -311,6 +319,8 @@ class VLANTestCase(ViewTestCases.PrimaryObjectViewTestCase): VLAN(group=vlangroups[0], vid=103, name='VLAN103', site=sites[0], role=roles[0]), ]) + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + cls.form_data = { 'site': sites[1].pk, 'group': vlangroups[1].pk, @@ -320,7 +330,7 @@ class VLANTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'status': VLANStatusChoices.STATUS_RESERVED, 'role': roles[1].pk, 'description': 'A new VLAN', - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], } cls.csv_data = ( @@ -368,6 +378,8 @@ class ServiceTestCase( Service(device=device, name='Service 3', protocol=ServiceProtocolChoices.PROTOCOL_TCP, port=103), ]) + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + cls.form_data = { 'device': device.pk, 'virtual_machine': None, @@ -376,7 +388,7 @@ class ServiceTestCase( 'port': 999, 'ipaddresses': [], 'description': 'A new service', - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], } cls.csv_data = ( diff --git a/netbox/tenancy/tests/test_views.py b/netbox/tenancy/tests/test_views.py index 4e00b648c..5b88b84cf 100644 --- a/netbox/tenancy/tests/test_views.py +++ b/netbox/tenancy/tests/test_views.py @@ -49,13 +49,15 @@ class TenantTestCase(ViewTestCases.PrimaryObjectViewTestCase): Tenant(name='Tenant 3', slug='tenant-3', group=tenant_groups[0]), ]) + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + cls.form_data = { 'name': 'Tenant X', 'slug': 'tenant-x', 'group': tenant_groups[1].pk, 'description': 'A new tenant', 'comments': 'Some comments', - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], } cls.csv_data = ( diff --git a/netbox/utilities/testing/views.py b/netbox/utilities/testing/views.py index 28eefdc70..774ceac85 100644 --- a/netbox/utilities/testing/views.py +++ b/netbox/utilities/testing/views.py @@ -1,13 +1,14 @@ from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.contrib.postgres.fields import ArrayField -from django.core.exceptions import ObjectDoesNotExist -from django.db.models import ForeignKey, ManyToManyField +from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist +from django.db.models import ManyToManyField from django.forms.models import model_to_dict from django.test import Client, TestCase as _TestCase, override_settings from django.urls import reverse, NoReverseMatch from django.utils.text import slugify from netaddr import IPNetwork +from taggit.managers import TaggableManager from extras.models import Tag from users.models import ObjectPermission @@ -55,14 +56,19 @@ class TestCase(_TestCase): model_dict[attr] = getattr(instance, attr) for key, value in list(model_dict.items()): + try: + field = instance._meta.get_field(key) + except FieldDoesNotExist: + # Attribute is not a model field + continue - # TODO: Differentiate between tags assigned to the instance and a M2M field for tags (ex: ConfigContext) - if key == 'tags': - model_dict[key] = sorted(value, key=lambda t: t.name) + # Handle ManyToManyFields + if value and type(field) in (ManyToManyField, TaggableManager): - # Convert ManyToManyField to list of instance PKs - elif model_dict[key] and type(value) in (list, tuple) and hasattr(value[0], 'pk'): - model_dict[key] = [obj.pk for obj in value] + if field.related_model is ContentType: + model_dict[key] = sorted([f'{ct.app_label}.{ct.model}' for ct in value]) + else: + model_dict[key] = sorted([obj.pk for obj in value]) if api: @@ -72,7 +78,7 @@ class TestCase(_TestCase): model_dict[key] = f'{ct.app_label}.{ct.model}' # Convert IPNetwork instances to strings - if type(value) is IPNetwork: + elif type(value) is IPNetwork: model_dict[key] = str(value) else: diff --git a/netbox/virtualization/tests/test_views.py b/netbox/virtualization/tests/test_views.py index 0ccf8a9b1..a98496f29 100644 --- a/netbox/virtualization/tests/test_views.py +++ b/netbox/virtualization/tests/test_views.py @@ -90,6 +90,8 @@ class ClusterTestCase(ViewTestCases.PrimaryObjectViewTestCase): Cluster(name='Cluster 3', group=clustergroups[0], type=clustertypes[0], site=sites[0]), ]) + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + cls.form_data = { 'name': 'Cluster X', 'group': clustergroups[1].pk, @@ -97,7 +99,7 @@ class ClusterTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'tenant': None, 'site': sites[1].pk, 'comments': 'Some comments', - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], } cls.csv_data = ( @@ -148,6 +150,8 @@ class VirtualMachineTestCase(ViewTestCases.PrimaryObjectViewTestCase): VirtualMachine(name='Virtual Machine 3', cluster=clusters[0], role=deviceroles[0], platform=platforms[0]), ]) + tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') + cls.form_data = { 'cluster': clusters[1].pk, 'tenant': None, @@ -161,7 +165,7 @@ class VirtualMachineTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'memory': 32768, 'disk': 4000, 'comments': 'Some comments', - 'tags': cls.create_tags('Alpha', 'Bravo', 'Charlie'), + 'tags': [t.pk for t in tags], 'local_context_data': None, } @@ -242,7 +246,7 @@ class InterfaceTestCase( 'mode': InterfaceModeChoices.MODE_TAGGED, 'untagged_vlan': vlans[0].pk, 'tagged_vlans': [v.pk for v in vlans[1:4]], - 'tags': tags, + 'tags': [t.pk for t in tags], } cls.bulk_create_data = { @@ -257,7 +261,7 @@ class InterfaceTestCase( 'mode': InterfaceModeChoices.MODE_TAGGED, 'untagged_vlan': vlans[0].pk, 'tagged_vlans': [v.pk for v in vlans[1:4]], - 'tags': tags, + 'tags': [t.pk for t in tags], } cls.bulk_edit_data = {