diff --git a/netbox/circuits/tests/test_api.py b/netbox/circuits/tests/test_api.py index 82fede862..42d534274 100644 --- a/netbox/circuits/tests/test_api.py +++ b/netbox/circuits/tests/test_api.py @@ -6,7 +6,23 @@ from circuits.choices import * from circuits.models import Circuit, CircuitTermination, CircuitType, Provider from dcim.models import Site from extras.models import Graph -from utilities.testing import APITestCase +from utilities.testing import APITestCase, choices_to_dict + + +class ChoicesTest(APITestCase): + + def test_choices(self): + + url = reverse('circuits-api:field-choice-list') + response = self.client.get(url, **self.header) + + self.assertEqual(response.status_code, 200) + + # Circuit + self.assertEqual(choices_to_dict(response.data.get('circuit:status')), CircuitStatusChoices.as_dict()) + + # CircuitTermination + self.assertEqual(choices_to_dict(response.data.get('circuit-termination:term_side')), CircuitTerminationSideChoices.as_dict()) class ProviderTest(APITestCase): diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py index 6fa202994..0b1292e44 100644 --- a/netbox/dcim/api/views.py +++ b/netbox/dcim/api/views.py @@ -52,6 +52,7 @@ class DCIMFieldChoicesViewSet(FieldChoicesViewSet): (FrontPortTemplate, ['type']), (Interface, ['type', 'mode']), (InterfaceTemplate, ['type']), + (PowerFeed, ['phase', 'status', 'supply', 'type']), (PowerOutlet, ['type', 'feed_leg']), (PowerOutletTemplate, ['type', 'feed_leg']), (PowerPort, ['type', 'connection_status']), diff --git a/netbox/dcim/tests/test_api.py b/netbox/dcim/tests/test_api.py index 3f336a3e6..2aa087043 100644 --- a/netbox/dcim/tests/test_api.py +++ b/netbox/dcim/tests/test_api.py @@ -14,10 +14,82 @@ from dcim.models import ( ) from ipam.models import IPAddress, VLAN from extras.models import Graph -from utilities.testing import APITestCase +from utilities.testing import APITestCase, choices_to_dict from virtualization.models import Cluster, ClusterType +class ChoicesTest(APITestCase): + + def test_choices(self): + + url = reverse('dcim-api:field-choice-list') + response = self.client.get(url, **self.header) + + self.assertEqual(response.status_code, 200) + + # Cable + self.assertEqual(choices_to_dict(response.data.get('cable:length_unit')), CableLengthUnitChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('cable:status')), CableStatusChoices.as_dict()) + # self.assertEqual(choices_to_dict(response.data.get('cable:termination_a_type')), ) + # self.assertEqual(choices_to_dict(response.data.get('cable:termination_b_type')), ) + self.assertEqual(choices_to_dict(response.data.get('cable:type')), CableTypeChoices.as_dict()) + + # Console ports + self.assertEqual(choices_to_dict(response.data.get('console-port:type')), ConsolePortTypeChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('console-port:connection_status')), dict(CONNECTION_STATUS_CHOICES)) + self.assertEqual(choices_to_dict(response.data.get('console-port-template:type')), ConsolePortTypeChoices.as_dict()) + + # Console server ports + self.assertEqual(choices_to_dict(response.data.get('console-server-port:type')), ConsolePortTypeChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('console-server-port-template:type')), ConsolePortTypeChoices.as_dict()) + + # Device + self.assertEqual(choices_to_dict(response.data.get('device:face')), DeviceFaceChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('device:status')), DeviceStatusChoices.as_dict()) + + # Device type + self.assertEqual(choices_to_dict(response.data.get('device-type:subdevice_role')), SubdeviceRoleChoices.as_dict()) + + # Front ports + self.assertEqual(choices_to_dict(response.data.get('front-port:type')), PortTypeChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('front-port-template:type')), PortTypeChoices.as_dict()) + + # Interfaces + self.assertEqual(choices_to_dict(response.data.get('interface:type')), InterfaceTypeChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('interface:mode')), InterfaceModeChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('interface-template:type')), InterfaceTypeChoices.as_dict()) + + # Power feed + self.assertEqual(choices_to_dict(response.data.get('power-feed:phase')), PowerFeedPhaseChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('power-feed:status')), PowerFeedStatusChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('power-feed:supply')), PowerFeedSupplyChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('power-feed:type')), PowerFeedTypeChoices.as_dict()) + + # Power outlets + self.assertEqual(choices_to_dict(response.data.get('power-outlet:type')), PowerOutletTypeChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('power-outlet:feed_leg')), PowerOutletFeedLegChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('power-outlet-template:type')), PowerOutletTypeChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('power-outlet-template:feed_leg')), PowerOutletFeedLegChoices.as_dict()) + + # Power ports + self.assertEqual(choices_to_dict(response.data.get('power-port:type')), PowerPortTypeChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('power-port:connection_status')), dict(CONNECTION_STATUS_CHOICES)) + self.assertEqual(choices_to_dict(response.data.get('power-port-template:type')), PowerPortTypeChoices.as_dict()) + + # Rack + self.assertEqual(choices_to_dict(response.data.get('rack:type')), RackTypeChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('rack:width')), RackWidthChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('rack:status')), RackStatusChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('rack:outer_unit')), RackDimensionUnitChoices.as_dict()) + + # Rear ports + self.assertEqual(choices_to_dict(response.data.get('rear-port:type')), PortTypeChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('rear-port-template:type')), PortTypeChoices.as_dict()) + + # Site + self.assertEqual(choices_to_dict(response.data.get('site:status')), SiteStatusChoices.as_dict()) + + class RegionTest(APITestCase): def setUp(self): diff --git a/netbox/extras/tests/test_api.py b/netbox/extras/tests/test_api.py index 0c419a12f..aff1761c6 100644 --- a/netbox/extras/tests/test_api.py +++ b/netbox/extras/tests/test_api.py @@ -7,10 +7,31 @@ from rest_framework import status from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Platform, Rack, RackGroup, RackRole, Region, Site from extras.api.views import ScriptViewSet +from extras.choices import * from extras.models import ConfigContext, Graph, ExportTemplate, Tag from extras.scripts import BooleanVar, IntegerVar, Script, StringVar from tenancy.models import Tenant, TenantGroup -from utilities.testing import APITestCase +from utilities.testing import APITestCase, choices_to_dict + + +class ChoicesTest(APITestCase): + + def test_choices(self): + + url = reverse('extras-api:field-choice-list') + response = self.client.get(url, **self.header) + + self.assertEqual(response.status_code, 200) + + # ExportTemplate + self.assertEqual(choices_to_dict(response.data.get('export-template:template_language')), ExportTemplateLanguageChoices.as_dict()) + + # Graph + # self.assertEqual(choices_to_dict(response.data.get('graph:type')), ) + self.assertEqual(choices_to_dict(response.data.get('graph:template_language')), ExportTemplateLanguageChoices.as_dict()) + + # ObjectChange + self.assertEqual(choices_to_dict(response.data.get('object-change:action')), ObjectChangeActionChoices.as_dict()) class GraphTest(APITestCase): diff --git a/netbox/ipam/choices.py b/netbox/ipam/choices.py index 543608b33..fab1e42aa 100644 --- a/netbox/ipam/choices.py +++ b/netbox/ipam/choices.py @@ -111,7 +111,7 @@ class VLANStatusChoices(ChoiceSet): # -# VLANs +# Services # class ServiceProtocolChoices(ChoiceSet): diff --git a/netbox/ipam/tests/test_api.py b/netbox/ipam/tests/test_api.py index 988eea0f3..450f2ad5f 100644 --- a/netbox/ipam/tests/test_api.py +++ b/netbox/ipam/tests/test_api.py @@ -5,9 +5,37 @@ from netaddr import IPNetwork from rest_framework import status from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site -from ipam.choices import ServiceProtocolChoices +from ipam.choices import * from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF -from utilities.testing import APITestCase +from utilities.testing import APITestCase, choices_to_dict + + +class ChoicesTest(APITestCase): + + def test_choices(self): + + url = reverse('ipam-api:field-choice-list') + response = self.client.get(url, **self.header) + + self.assertEqual(response.status_code, 200) + + # Aggregate + # self.assertEqual(choices_to_dict(response.data.get('aggregate:family')), ) + + # Prefix + # self.assertEqual(choices_to_dict(response.data.get('prefix:family')), ) + self.assertEqual(choices_to_dict(response.data.get('prefix:status')), PrefixStatusChoices.as_dict()) + + # IPAddress + # self.assertEqual(choices_to_dict(response.data.get('ip-address:family')), ) + self.assertEqual(choices_to_dict(response.data.get('ip-address:role')), IPAddressRoleChoices.as_dict()) + self.assertEqual(choices_to_dict(response.data.get('ip-address:status')), IPAddressStatusChoices.as_dict()) + + # VLAN + self.assertEqual(choices_to_dict(response.data.get('vlan:status')), VLANStatusChoices.as_dict()) + + # Service + self.assertEqual(choices_to_dict(response.data.get('service:protocol')), ServiceProtocolChoices.as_dict()) class VRFTest(APITestCase): diff --git a/netbox/secrets/tests/test_api.py b/netbox/secrets/tests/test_api.py index cbcec9ca9..a8721ac87 100644 --- a/netbox/secrets/tests/test_api.py +++ b/netbox/secrets/tests/test_api.py @@ -9,6 +9,16 @@ from utilities.testing import APITestCase from .constants import PRIVATE_KEY, PUBLIC_KEY +class ChoicesTest(APITestCase): + + def test_choices(self): + + url = reverse('secrets-api:field-choice-list') + response = self.client.get(url, **self.header) + + self.assertEqual(response.status_code, 200) + + class SecretRoleTest(APITestCase): def setUp(self): diff --git a/netbox/tenancy/tests/test_api.py b/netbox/tenancy/tests/test_api.py index 121898019..9d4b83484 100644 --- a/netbox/tenancy/tests/test_api.py +++ b/netbox/tenancy/tests/test_api.py @@ -5,6 +5,16 @@ from tenancy.models import Tenant, TenantGroup from utilities.testing import APITestCase +class ChoicesTest(APITestCase): + + def test_choices(self): + + url = reverse('tenancy-api:field-choice-list') + response = self.client.get(url, **self.header) + + self.assertEqual(response.status_code, 200) + + class TenantGroupTest(APITestCase): def setUp(self): diff --git a/netbox/utilities/choices.py b/netbox/utilities/choices.py index e49b3a365..2151ff4a9 100644 --- a/netbox/utilities/choices.py +++ b/netbox/utilities/choices.py @@ -1,3 +1,6 @@ +from utilities.forms import unpack_grouped_choices + + class ChoiceSetMeta(type): """ Metaclass for ChoiceSet @@ -20,6 +23,11 @@ class ChoiceSet(metaclass=ChoiceSetMeta): def values(cls): return [c[0] for c in cls.CHOICES] + @classmethod + def as_dict(cls): + # Unpack grouped choices before casting as a dict + return dict(unpack_grouped_choices(cls.CHOICES)) + @classmethod def slug_to_id(cls, slug): """ diff --git a/netbox/utilities/testing.py b/netbox/utilities/testing.py index 0534a340b..3d0bf1a05 100644 --- a/netbox/utilities/testing.py +++ b/netbox/utilities/testing.py @@ -35,3 +35,30 @@ def create_test_user(username='testuser', permissions=list()): user.user_permissions.add(perm) return user + + +def choices_to_dict(choices_list): + """ + Convert a list of field choices to a dictionary suitable for direct comparison with a ChoiceSet. For example: + + [ + { + "value": "choice-1", + "label": "First Choice" + }, + { + "value": "choice-2", + "label": "Second Choice" + } + ] + + Becomes: + + { + "choice-1": "First Choice", + "choice-2": "Second Choice + } + """ + return { + choice['value']: choice['label'] for choice in choices_list + } diff --git a/netbox/virtualization/tests/test_api.py b/netbox/virtualization/tests/test_api.py index 683a65a2b..a8a2f4340 100644 --- a/netbox/virtualization/tests/test_api.py +++ b/netbox/virtualization/tests/test_api.py @@ -5,10 +5,24 @@ from rest_framework import status from dcim.choices import InterfaceModeChoices, InterfaceTypeChoices from dcim.models import Interface from ipam.models import IPAddress, VLAN -from utilities.testing import APITestCase +from utilities.testing import APITestCase, choices_to_dict +from virtualization.choices import * from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine +class ChoicesTest(APITestCase): + + def test_choices(self): + + url = reverse('virtualization-api:field-choice-list') + response = self.client.get(url, **self.header) + + self.assertEqual(response.status_code, 200) + + # VirtualMachine + self.assertEqual(choices_to_dict(response.data.get('virtual-machine:status')), VirtualMachineStatusChoices.as_dict()) + + class ClusterTypeTest(APITestCase): def setUp(self):