| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- from django.test import Client, TestCase, override_settings
- from django.urls import reverse
- from drf_spectacular.drainage import GENERATOR_STATS
- from rest_framework import status
- from core.models import ObjectType
- from dcim.models import Region, Site
- from extras.choices import CustomFieldTypeChoices
- from extras.models import CustomField
- from ipam.models import VLAN
- from netbox.config import get_config
- from utilities.testing import APITestCase, disable_warnings
- class WritableNestedSerializerTest(APITestCase):
- """
- Test the operation of WritableNestedSerializer using VLANSerializer as our test subject.
- """
- def setUp(self):
- super().setUp()
- self.region_a = Region.objects.create(name='Region A', slug='region-a')
- self.site1 = Site.objects.create(region=self.region_a, name='Site 1', slug='site-1')
- self.site2 = Site.objects.create(region=self.region_a, name='Site 2', slug='site-2')
- def test_related_by_pk(self):
- data = {
- 'vid': 100,
- 'name': 'Test VLAN 100',
- 'site': self.site1.pk,
- }
- url = reverse('ipam-api:vlan-list')
- self.add_permissions('ipam.add_vlan')
- response = self.client.post(url, data, format='json', **self.header)
- self.assertHttpStatus(response, status.HTTP_201_CREATED)
- self.assertEqual(response.data['site']['id'], self.site1.pk)
- vlan = VLAN.objects.get(pk=response.data['id'])
- self.assertEqual(vlan.site, self.site1)
- def test_related_by_pk_no_match(self):
- data = {
- 'vid': 100,
- 'name': 'Test VLAN 100',
- 'site': 999,
- }
- url = reverse('ipam-api:vlan-list')
- self.add_permissions('ipam.add_vlan')
- with disable_warnings('django.request'):
- response = self.client.post(url, data, format='json', **self.header)
- self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
- self.assertEqual(VLAN.objects.count(), 0)
- self.assertTrue(response.data['site'][0].startswith("Related object not found"))
- def test_related_by_attributes(self):
- data = {
- 'vid': 100,
- 'name': 'Test VLAN 100',
- 'site': {
- 'name': 'Site 1'
- },
- }
- url = reverse('ipam-api:vlan-list')
- self.add_permissions('ipam.add_vlan')
- response = self.client.post(url, data, format='json', **self.header)
- self.assertHttpStatus(response, status.HTTP_201_CREATED)
- self.assertEqual(response.data['site']['id'], self.site1.pk)
- vlan = VLAN.objects.get(pk=response.data['id'])
- self.assertEqual(vlan.site, self.site1)
- def test_related_by_attributes_no_match(self):
- data = {
- 'vid': 100,
- 'name': 'Test VLAN 100',
- 'site': {
- 'name': 'Site X'
- },
- }
- url = reverse('ipam-api:vlan-list')
- self.add_permissions('ipam.add_vlan')
- with disable_warnings('django.request'):
- response = self.client.post(url, data, format='json', **self.header)
- self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
- self.assertEqual(VLAN.objects.count(), 0)
- self.assertTrue(response.data['site'][0].startswith("Related object not found"))
- def test_related_by_attributes_multiple_matches(self):
- data = {
- 'vid': 100,
- 'name': 'Test VLAN 100',
- 'site': {
- 'region': {
- "name": "Region A",
- },
- },
- }
- url = reverse('ipam-api:vlan-list')
- self.add_permissions('ipam.add_vlan')
- with disable_warnings('django.request'):
- response = self.client.post(url, data, format='json', **self.header)
- self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
- self.assertEqual(VLAN.objects.count(), 0)
- self.assertTrue(response.data['site'][0].startswith("Multiple objects match"))
- def test_related_by_invalid(self):
- data = {
- 'vid': 100,
- 'name': 'Test VLAN 100',
- 'site': 'XXX',
- }
- url = reverse('ipam-api:vlan-list')
- self.add_permissions('ipam.add_vlan')
- with disable_warnings('django.request'):
- response = self.client.post(url, data, format='json', **self.header)
- self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
- self.assertEqual(VLAN.objects.count(), 0)
- class APIPaginationTestCase(APITestCase):
- user_permissions = ('dcim.view_site',)
- @classmethod
- def setUpTestData(cls):
- cls.url = reverse('dcim-api:site-list')
- # Create a large number of Sites for testing
- Site.objects.bulk_create([
- Site(name=f'Site {i}', slug=f'site-{i}') for i in range(1, 101)
- ])
- def test_default_page_size(self):
- response = self.client.get(self.url, format='json', **self.header)
- page_size = get_config().PAGINATE_COUNT
- self.assertLess(page_size, 100, "Default page size not sufficient for data set")
- self.assertHttpStatus(response, status.HTTP_200_OK)
- self.assertEqual(response.data['count'], 100)
- self.assertTrue(response.data['next'].endswith(f'?limit={page_size}&offset={page_size}'))
- self.assertIsNone(response.data['previous'])
- self.assertEqual(len(response.data['results']), page_size)
- @override_settings(MAX_PAGE_SIZE=30)
- def test_default_page_size_with_small_max_page_size(self):
- response = self.client.get(self.url, format='json', **self.header)
- page_size = get_config().MAX_PAGE_SIZE
- self.assertLess(page_size, 100, "Default page size not sufficient for data set")
- self.assertHttpStatus(response, status.HTTP_200_OK)
- self.assertEqual(response.data['count'], 100)
- self.assertTrue(response.data['next'].endswith(f'?limit={page_size}&offset={page_size}'))
- self.assertIsNone(response.data['previous'])
- self.assertEqual(len(response.data['results']), page_size)
- def test_custom_page_size(self):
- response = self.client.get(f'{self.url}?limit=10', format='json', **self.header)
- self.assertHttpStatus(response, status.HTTP_200_OK)
- self.assertEqual(response.data['count'], 100)
- self.assertTrue(response.data['next'].endswith('?limit=10&offset=10'))
- self.assertIsNone(response.data['previous'])
- self.assertEqual(len(response.data['results']), 10)
- @override_settings(MAX_PAGE_SIZE=80)
- def test_max_page_size(self):
- response = self.client.get(f'{self.url}?limit=0', format='json', **self.header)
- self.assertHttpStatus(response, status.HTTP_200_OK)
- self.assertEqual(response.data['count'], 100)
- self.assertTrue(response.data['next'].endswith('?limit=80&offset=80'))
- self.assertIsNone(response.data['previous'])
- self.assertEqual(len(response.data['results']), 80)
- @override_settings(MAX_PAGE_SIZE=0)
- def test_max_page_size_disabled(self):
- response = self.client.get(f'{self.url}?limit=0', format='json', **self.header)
- self.assertHttpStatus(response, status.HTTP_200_OK)
- self.assertEqual(response.data['count'], 100)
- self.assertIsNone(response.data['next'])
- self.assertIsNone(response.data['previous'])
- self.assertEqual(len(response.data['results']), 100)
- class APIOrderingTestCase(APITestCase):
- user_permissions = ('dcim.view_site',)
- @classmethod
- def setUpTestData(cls):
- cls.url = reverse('dcim-api:site-list')
- sites = (
- Site(name='Site 1', slug='site-1', facility='C', description='Z'),
- Site(name='Site 2', slug='site-2', facility='C', description='Y'),
- Site(name='Site 3', slug='site-3', facility='B', description='X'),
- Site(name='Site 4', slug='site-4', facility='B', description='W'),
- Site(name='Site 5', slug='site-5', facility='A', description='V'),
- Site(name='Site 6', slug='site-6', facility='A', description='U'),
- )
- Site.objects.bulk_create(sites)
- def test_default_order(self):
- response = self.client.get(self.url, format='json', **self.header)
- self.assertHttpStatus(response, status.HTTP_200_OK)
- self.assertEqual(response.data['count'], 6)
- self.assertListEqual(
- [s['name'] for s in response.data['results']],
- ['Site 1', 'Site 2', 'Site 3', 'Site 4', 'Site 5', 'Site 6']
- )
- def test_order_single_field(self):
- response = self.client.get(f'{self.url}?ordering=description', format='json', **self.header)
- self.assertHttpStatus(response, status.HTTP_200_OK)
- self.assertEqual(response.data['count'], 6)
- self.assertListEqual(
- [s['name'] for s in response.data['results']],
- ['Site 6', 'Site 5', 'Site 4', 'Site 3', 'Site 2', 'Site 1']
- )
- def test_order_reversed(self):
- response = self.client.get(f'{self.url}?ordering=-name', format='json', **self.header)
- self.assertHttpStatus(response, status.HTTP_200_OK)
- self.assertEqual(response.data['count'], 6)
- self.assertListEqual(
- [s['name'] for s in response.data['results']],
- ['Site 6', 'Site 5', 'Site 4', 'Site 3', 'Site 2', 'Site 1']
- )
- def test_order_multiple_fields(self):
- response = self.client.get(f'{self.url}?ordering=facility,name', format='json', **self.header)
- self.assertHttpStatus(response, status.HTTP_200_OK)
- self.assertEqual(response.data['count'], 6)
- self.assertListEqual(
- [s['name'] for s in response.data['results']],
- ['Site 5', 'Site 6', 'Site 3', 'Site 4', 'Site 1', 'Site 2']
- )
- class APIDocsTestCase(TestCase):
- def setUp(self):
- self.client = Client()
- # Populate a CustomField to activate CustomFieldSerializer
- object_type = ObjectType.objects.get_for_model(Site)
- self.cf_text = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='test')
- self.cf_text.save()
- self.cf_text.object_types.set([object_type])
- self.cf_text.save()
- def test_api_docs(self):
- url = reverse('api_docs')
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- url = reverse('schema')
- with GENERATOR_STATS.silence(): # Suppress schema generator warnings
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
|