test_api.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import urllib.parse
  2. from django.contrib.contenttypes.models import ContentType
  3. from django.test import Client, TestCase, override_settings
  4. from django.urls import reverse
  5. from rest_framework import status
  6. from dcim.models import Region, Site
  7. from extras.choices import CustomFieldTypeChoices
  8. from extras.models import CustomField
  9. from ipam.models import VLAN
  10. from netbox.config import Config
  11. from utilities.testing import APITestCase, disable_warnings
  12. class WritableNestedSerializerTest(APITestCase):
  13. """
  14. Test the operation of WritableNestedSerializer using VLANSerializer as our test subject.
  15. """
  16. def setUp(self):
  17. super().setUp()
  18. self.region_a = Region.objects.create(name='Region A', slug='region-a')
  19. self.site1 = Site.objects.create(region=self.region_a, name='Site 1', slug='site-1')
  20. self.site2 = Site.objects.create(region=self.region_a, name='Site 2', slug='site-2')
  21. def test_related_by_pk(self):
  22. data = {
  23. 'vid': 100,
  24. 'name': 'Test VLAN 100',
  25. 'site': self.site1.pk,
  26. }
  27. url = reverse('ipam-api:vlan-list')
  28. self.add_permissions('ipam.add_vlan')
  29. response = self.client.post(url, data, format='json', **self.header)
  30. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  31. self.assertEqual(response.data['site']['id'], self.site1.pk)
  32. vlan = VLAN.objects.get(pk=response.data['id'])
  33. self.assertEqual(vlan.site, self.site1)
  34. def test_related_by_pk_no_match(self):
  35. data = {
  36. 'vid': 100,
  37. 'name': 'Test VLAN 100',
  38. 'site': 999,
  39. }
  40. url = reverse('ipam-api:vlan-list')
  41. self.add_permissions('ipam.add_vlan')
  42. with disable_warnings('django.request'):
  43. response = self.client.post(url, data, format='json', **self.header)
  44. self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
  45. self.assertEqual(VLAN.objects.count(), 0)
  46. self.assertTrue(response.data['site'][0].startswith("Related object not found"))
  47. def test_related_by_attributes(self):
  48. data = {
  49. 'vid': 100,
  50. 'name': 'Test VLAN 100',
  51. 'site': {
  52. 'name': 'Site 1'
  53. },
  54. }
  55. url = reverse('ipam-api:vlan-list')
  56. self.add_permissions('ipam.add_vlan')
  57. response = self.client.post(url, data, format='json', **self.header)
  58. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  59. self.assertEqual(response.data['site']['id'], self.site1.pk)
  60. vlan = VLAN.objects.get(pk=response.data['id'])
  61. self.assertEqual(vlan.site, self.site1)
  62. def test_related_by_attributes_no_match(self):
  63. data = {
  64. 'vid': 100,
  65. 'name': 'Test VLAN 100',
  66. 'site': {
  67. 'name': 'Site X'
  68. },
  69. }
  70. url = reverse('ipam-api:vlan-list')
  71. self.add_permissions('ipam.add_vlan')
  72. with disable_warnings('django.request'):
  73. response = self.client.post(url, data, format='json', **self.header)
  74. self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
  75. self.assertEqual(VLAN.objects.count(), 0)
  76. self.assertTrue(response.data['site'][0].startswith("Related object not found"))
  77. def test_related_by_attributes_multiple_matches(self):
  78. data = {
  79. 'vid': 100,
  80. 'name': 'Test VLAN 100',
  81. 'site': {
  82. 'region': {
  83. "name": "Region A",
  84. },
  85. },
  86. }
  87. url = reverse('ipam-api:vlan-list')
  88. self.add_permissions('ipam.add_vlan')
  89. with disable_warnings('django.request'):
  90. response = self.client.post(url, data, format='json', **self.header)
  91. self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
  92. self.assertEqual(VLAN.objects.count(), 0)
  93. self.assertTrue(response.data['site'][0].startswith("Multiple objects match"))
  94. def test_related_by_invalid(self):
  95. data = {
  96. 'vid': 100,
  97. 'name': 'Test VLAN 100',
  98. 'site': 'XXX',
  99. }
  100. url = reverse('ipam-api:vlan-list')
  101. self.add_permissions('ipam.add_vlan')
  102. with disable_warnings('django.request'):
  103. response = self.client.post(url, data, format='json', **self.header)
  104. self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
  105. self.assertEqual(VLAN.objects.count(), 0)
  106. class APIPaginationTestCase(APITestCase):
  107. user_permissions = ('dcim.view_site',)
  108. @classmethod
  109. def setUpTestData(cls):
  110. cls.url = reverse('dcim-api:site-list')
  111. # Create a large number of Sites for testing
  112. Site.objects.bulk_create([
  113. Site(name=f'Site {i}', slug=f'site-{i}') for i in range(1, 101)
  114. ])
  115. def test_default_page_size(self):
  116. response = self.client.get(self.url, format='json', **self.header)
  117. page_size = Config().PAGINATE_COUNT
  118. self.assertLess(page_size, 100, "Default page size not sufficient for data set")
  119. self.assertHttpStatus(response, status.HTTP_200_OK)
  120. self.assertEqual(response.data['count'], 100)
  121. self.assertTrue(response.data['next'].endswith(f'?limit={page_size}&offset={page_size}'))
  122. self.assertIsNone(response.data['previous'])
  123. self.assertEqual(len(response.data['results']), page_size)
  124. def test_custom_page_size(self):
  125. response = self.client.get(f'{self.url}?limit=10', format='json', **self.header)
  126. self.assertHttpStatus(response, status.HTTP_200_OK)
  127. self.assertEqual(response.data['count'], 100)
  128. self.assertTrue(response.data['next'].endswith(f'?limit=10&offset=10'))
  129. self.assertIsNone(response.data['previous'])
  130. self.assertEqual(len(response.data['results']), 10)
  131. @override_settings(MAX_PAGE_SIZE=20)
  132. def test_max_page_size(self):
  133. response = self.client.get(f'{self.url}?limit=0', format='json', **self.header)
  134. self.assertHttpStatus(response, status.HTTP_200_OK)
  135. self.assertEqual(response.data['count'], 100)
  136. self.assertTrue(response.data['next'].endswith(f'?limit=20&offset=20'))
  137. self.assertIsNone(response.data['previous'])
  138. self.assertEqual(len(response.data['results']), 20)
  139. @override_settings(MAX_PAGE_SIZE=0)
  140. def test_max_page_size_disabled(self):
  141. response = self.client.get(f'{self.url}?limit=0', format='json', **self.header)
  142. self.assertHttpStatus(response, status.HTTP_200_OK)
  143. self.assertEqual(response.data['count'], 100)
  144. self.assertIsNone(response.data['next'])
  145. self.assertIsNone(response.data['previous'])
  146. self.assertEqual(len(response.data['results']), 100)
  147. class APIDocsTestCase(TestCase):
  148. def setUp(self):
  149. self.client = Client()
  150. # Populate a CustomField to activate CustomFieldSerializer
  151. content_type = ContentType.objects.get_for_model(Site)
  152. self.cf_text = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='test')
  153. self.cf_text.save()
  154. self.cf_text.content_types.set([content_type])
  155. self.cf_text.save()
  156. def test_api_docs(self):
  157. url = reverse('api_docs')
  158. params = {
  159. "format": "openapi",
  160. }
  161. response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
  162. self.assertEqual(response.status_code, 200)