test_api.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. import datetime
  2. from django.contrib.auth.models import User
  3. from django.contrib.contenttypes.models import ContentType
  4. from django.urls import reverse
  5. from django.utils.timezone import make_aware
  6. from rest_framework import status
  7. from core.choices import ManagedFileRootPathChoices
  8. from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Rack, Location, RackRole, Site
  9. from extras.api.views import ReportViewSet, ScriptViewSet
  10. from extras.models import *
  11. from extras.reports import Report
  12. from extras.scripts import BooleanVar, IntegerVar, Script, StringVar
  13. from utilities.testing import APITestCase, APIViewTestCases
  14. class AppTest(APITestCase):
  15. def test_root(self):
  16. url = reverse('extras-api:api-root')
  17. response = self.client.get('{}?format=api'.format(url), **self.header)
  18. self.assertEqual(response.status_code, 200)
  19. class WebhookTest(APIViewTestCases.APIViewTestCase):
  20. model = Webhook
  21. brief_fields = ['display', 'id', 'name', 'url']
  22. create_data = [
  23. {
  24. 'content_types': ['dcim.device', 'dcim.devicetype'],
  25. 'name': 'Webhook 4',
  26. 'type_create': True,
  27. 'payload_url': 'http://example.com/?4',
  28. },
  29. {
  30. 'content_types': ['dcim.device', 'dcim.devicetype'],
  31. 'name': 'Webhook 5',
  32. 'type_update': True,
  33. 'payload_url': 'http://example.com/?5',
  34. },
  35. {
  36. 'content_types': ['dcim.device', 'dcim.devicetype'],
  37. 'name': 'Webhook 6',
  38. 'type_delete': True,
  39. 'payload_url': 'http://example.com/?6',
  40. },
  41. ]
  42. bulk_update_data = {
  43. 'ssl_verification': False,
  44. }
  45. @classmethod
  46. def setUpTestData(cls):
  47. site_ct = ContentType.objects.get_for_model(Site)
  48. rack_ct = ContentType.objects.get_for_model(Rack)
  49. webhooks = (
  50. Webhook(
  51. name='Webhook 1',
  52. type_create=True,
  53. payload_url='http://example.com/?1',
  54. ),
  55. Webhook(
  56. name='Webhook 2',
  57. type_update=True,
  58. payload_url='http://example.com/?1',
  59. ),
  60. Webhook(
  61. name='Webhook 3',
  62. type_delete=True,
  63. payload_url='http://example.com/?1',
  64. ),
  65. )
  66. Webhook.objects.bulk_create(webhooks)
  67. for webhook in webhooks:
  68. webhook.content_types.add(site_ct, rack_ct)
  69. class CustomFieldTest(APIViewTestCases.APIViewTestCase):
  70. model = CustomField
  71. brief_fields = ['display', 'id', 'name', 'url']
  72. create_data = [
  73. {
  74. 'content_types': ['dcim.site'],
  75. 'name': 'cf4',
  76. 'type': 'date',
  77. },
  78. {
  79. 'content_types': ['dcim.site'],
  80. 'name': 'cf5',
  81. 'type': 'url',
  82. },
  83. {
  84. 'content_types': ['dcim.site'],
  85. 'name': 'cf6',
  86. 'type': 'select',
  87. 'choices': ['A', 'B', 'C']
  88. },
  89. ]
  90. bulk_update_data = {
  91. 'description': 'New description',
  92. }
  93. @classmethod
  94. def setUpTestData(cls):
  95. site_ct = ContentType.objects.get_for_model(Site)
  96. custom_fields = (
  97. CustomField(
  98. name='cf1',
  99. type='text'
  100. ),
  101. CustomField(
  102. name='cf2',
  103. type='integer'
  104. ),
  105. CustomField(
  106. name='cf3',
  107. type='boolean'
  108. ),
  109. )
  110. CustomField.objects.bulk_create(custom_fields)
  111. for cf in custom_fields:
  112. cf.content_types.add(site_ct)
  113. class CustomLinkTest(APIViewTestCases.APIViewTestCase):
  114. model = CustomLink
  115. brief_fields = ['display', 'id', 'name', 'url']
  116. create_data = [
  117. {
  118. 'content_types': ['dcim.site'],
  119. 'name': 'Custom Link 4',
  120. 'enabled': True,
  121. 'link_text': 'Link 4',
  122. 'link_url': 'http://example.com/?4',
  123. },
  124. {
  125. 'content_types': ['dcim.site'],
  126. 'name': 'Custom Link 5',
  127. 'enabled': True,
  128. 'link_text': 'Link 5',
  129. 'link_url': 'http://example.com/?5',
  130. },
  131. {
  132. 'content_types': ['dcim.site'],
  133. 'name': 'Custom Link 6',
  134. 'enabled': False,
  135. 'link_text': 'Link 6',
  136. 'link_url': 'http://example.com/?6',
  137. },
  138. ]
  139. bulk_update_data = {
  140. 'new_window': True,
  141. 'enabled': False,
  142. }
  143. @classmethod
  144. def setUpTestData(cls):
  145. site_ct = ContentType.objects.get_for_model(Site)
  146. custom_links = (
  147. CustomLink(
  148. name='Custom Link 1',
  149. enabled=True,
  150. link_text='Link 1',
  151. link_url='http://example.com/?1',
  152. ),
  153. CustomLink(
  154. name='Custom Link 2',
  155. enabled=True,
  156. link_text='Link 2',
  157. link_url='http://example.com/?2',
  158. ),
  159. CustomLink(
  160. name='Custom Link 3',
  161. enabled=False,
  162. link_text='Link 3',
  163. link_url='http://example.com/?3',
  164. ),
  165. )
  166. CustomLink.objects.bulk_create(custom_links)
  167. for i, custom_link in enumerate(custom_links):
  168. custom_link.content_types.set([site_ct])
  169. class SavedFilterTest(APIViewTestCases.APIViewTestCase):
  170. model = SavedFilter
  171. brief_fields = ['display', 'id', 'name', 'slug', 'url']
  172. create_data = [
  173. {
  174. 'content_types': ['dcim.site'],
  175. 'name': 'Saved Filter 4',
  176. 'slug': 'saved-filter-4',
  177. 'weight': 100,
  178. 'enabled': True,
  179. 'shared': True,
  180. 'parameters': {'status': ['active']},
  181. },
  182. {
  183. 'content_types': ['dcim.site'],
  184. 'name': 'Saved Filter 5',
  185. 'slug': 'saved-filter-5',
  186. 'weight': 200,
  187. 'enabled': True,
  188. 'shared': True,
  189. 'parameters': {'status': ['planned']},
  190. },
  191. {
  192. 'content_types': ['dcim.site'],
  193. 'name': 'Saved Filter 6',
  194. 'slug': 'saved-filter-6',
  195. 'weight': 300,
  196. 'enabled': True,
  197. 'shared': True,
  198. 'parameters': {'status': ['retired']},
  199. },
  200. ]
  201. bulk_update_data = {
  202. 'weight': 1000,
  203. 'enabled': False,
  204. 'shared': False,
  205. }
  206. @classmethod
  207. def setUpTestData(cls):
  208. site_ct = ContentType.objects.get_for_model(Site)
  209. saved_filters = (
  210. SavedFilter(
  211. name='Saved Filter 1',
  212. slug='saved-filter-1',
  213. weight=100,
  214. enabled=True,
  215. shared=True,
  216. parameters={'status': ['active']}
  217. ),
  218. SavedFilter(
  219. name='Saved Filter 2',
  220. slug='saved-filter-2',
  221. weight=200,
  222. enabled=True,
  223. shared=True,
  224. parameters={'status': ['planned']}
  225. ),
  226. SavedFilter(
  227. name='Saved Filter 3',
  228. slug='saved-filter-3',
  229. weight=300,
  230. enabled=True,
  231. shared=True,
  232. parameters={'status': ['retired']}
  233. ),
  234. )
  235. SavedFilter.objects.bulk_create(saved_filters)
  236. for i, savedfilter in enumerate(saved_filters):
  237. savedfilter.content_types.set([site_ct])
  238. class ExportTemplateTest(APIViewTestCases.APIViewTestCase):
  239. model = ExportTemplate
  240. brief_fields = ['display', 'id', 'name', 'url']
  241. create_data = [
  242. {
  243. 'content_types': ['dcim.device'],
  244. 'name': 'Test Export Template 4',
  245. 'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}',
  246. },
  247. {
  248. 'content_types': ['dcim.device'],
  249. 'name': 'Test Export Template 5',
  250. 'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}',
  251. },
  252. {
  253. 'content_types': ['dcim.device'],
  254. 'name': 'Test Export Template 6',
  255. 'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}',
  256. },
  257. ]
  258. bulk_update_data = {
  259. 'description': 'New description',
  260. }
  261. @classmethod
  262. def setUpTestData(cls):
  263. export_templates = (
  264. ExportTemplate(
  265. name='Export Template 1',
  266. template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}'
  267. ),
  268. ExportTemplate(
  269. name='Export Template 2',
  270. template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}'
  271. ),
  272. ExportTemplate(
  273. name='Export Template 3',
  274. template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}'
  275. ),
  276. )
  277. ExportTemplate.objects.bulk_create(export_templates)
  278. for et in export_templates:
  279. et.content_types.set([ContentType.objects.get_for_model(Device)])
  280. class TagTest(APIViewTestCases.APIViewTestCase):
  281. model = Tag
  282. brief_fields = ['color', 'display', 'id', 'name', 'slug', 'url']
  283. create_data = [
  284. {
  285. 'name': 'Tag 4',
  286. 'slug': 'tag-4',
  287. },
  288. {
  289. 'name': 'Tag 5',
  290. 'slug': 'tag-5',
  291. },
  292. {
  293. 'name': 'Tag 6',
  294. 'slug': 'tag-6',
  295. },
  296. ]
  297. bulk_update_data = {
  298. 'description': 'New description',
  299. }
  300. @classmethod
  301. def setUpTestData(cls):
  302. tags = (
  303. Tag(name='Tag 1', slug='tag-1'),
  304. Tag(name='Tag 2', slug='tag-2'),
  305. Tag(name='Tag 3', slug='tag-3'),
  306. )
  307. Tag.objects.bulk_create(tags)
  308. # TODO: Standardize to APIViewTestCase (needs create & update tests)
  309. class ImageAttachmentTest(
  310. APIViewTestCases.GetObjectViewTestCase,
  311. APIViewTestCases.ListObjectsViewTestCase,
  312. APIViewTestCases.DeleteObjectViewTestCase,
  313. APIViewTestCases.GraphQLTestCase
  314. ):
  315. model = ImageAttachment
  316. brief_fields = ['display', 'id', 'image', 'name', 'url']
  317. @classmethod
  318. def setUpTestData(cls):
  319. ct = ContentType.objects.get_for_model(Site)
  320. site = Site.objects.create(name='Site 1', slug='site-1')
  321. image_attachments = (
  322. ImageAttachment(
  323. content_type=ct,
  324. object_id=site.pk,
  325. name='Image Attachment 1',
  326. image='http://example.com/image1.png',
  327. image_height=100,
  328. image_width=100
  329. ),
  330. ImageAttachment(
  331. content_type=ct,
  332. object_id=site.pk,
  333. name='Image Attachment 2',
  334. image='http://example.com/image2.png',
  335. image_height=100,
  336. image_width=100
  337. ),
  338. ImageAttachment(
  339. content_type=ct,
  340. object_id=site.pk,
  341. name='Image Attachment 3',
  342. image='http://example.com/image3.png',
  343. image_height=100,
  344. image_width=100
  345. )
  346. )
  347. ImageAttachment.objects.bulk_create(image_attachments)
  348. class JournalEntryTest(APIViewTestCases.APIViewTestCase):
  349. model = JournalEntry
  350. brief_fields = ['created', 'display', 'id', 'url']
  351. bulk_update_data = {
  352. 'comments': 'Overwritten',
  353. }
  354. @classmethod
  355. def setUpTestData(cls):
  356. user = User.objects.first()
  357. site = Site.objects.create(name='Site 1', slug='site-1')
  358. journal_entries = (
  359. JournalEntry(
  360. created_by=user,
  361. assigned_object=site,
  362. comments='Fourth entry',
  363. ),
  364. JournalEntry(
  365. created_by=user,
  366. assigned_object=site,
  367. comments='Fifth entry',
  368. ),
  369. JournalEntry(
  370. created_by=user,
  371. assigned_object=site,
  372. comments='Sixth entry',
  373. ),
  374. )
  375. JournalEntry.objects.bulk_create(journal_entries)
  376. cls.create_data = [
  377. {
  378. 'assigned_object_type': 'dcim.site',
  379. 'assigned_object_id': site.pk,
  380. 'comments': 'First entry',
  381. },
  382. {
  383. 'assigned_object_type': 'dcim.site',
  384. 'assigned_object_id': site.pk,
  385. 'comments': 'Second entry',
  386. },
  387. {
  388. 'assigned_object_type': 'dcim.site',
  389. 'assigned_object_id': site.pk,
  390. 'comments': 'Third entry',
  391. },
  392. ]
  393. class ConfigContextTest(APIViewTestCases.APIViewTestCase):
  394. model = ConfigContext
  395. brief_fields = ['display', 'id', 'name', 'url']
  396. create_data = [
  397. {
  398. 'name': 'Config Context 4',
  399. 'data': {'more_foo': True},
  400. },
  401. {
  402. 'name': 'Config Context 5',
  403. 'data': {'more_bar': False},
  404. },
  405. {
  406. 'name': 'Config Context 6',
  407. 'data': {'more_baz': None},
  408. },
  409. ]
  410. bulk_update_data = {
  411. 'description': 'New description',
  412. }
  413. @classmethod
  414. def setUpTestData(cls):
  415. config_contexts = (
  416. ConfigContext(name='Config Context 1', weight=100, data={'foo': 123}),
  417. ConfigContext(name='Config Context 2', weight=200, data={'bar': 456}),
  418. ConfigContext(name='Config Context 3', weight=300, data={'baz': 789}),
  419. )
  420. ConfigContext.objects.bulk_create(config_contexts)
  421. def test_render_configcontext_for_object(self):
  422. """
  423. Test rendering config context data for a device.
  424. """
  425. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  426. devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
  427. devicerole = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
  428. site = Site.objects.create(name='Site-1', slug='site-1')
  429. device = Device.objects.create(name='Device 1', device_type=devicetype, device_role=devicerole, site=site)
  430. # Test default config contexts (created at test setup)
  431. rendered_context = device.get_config_context()
  432. self.assertEqual(rendered_context['foo'], 123)
  433. self.assertEqual(rendered_context['bar'], 456)
  434. self.assertEqual(rendered_context['baz'], 789)
  435. # Add another context specific to the site
  436. configcontext4 = ConfigContext(
  437. name='Config Context 4',
  438. data={'site_data': 'ABC'}
  439. )
  440. configcontext4.save()
  441. configcontext4.sites.add(site)
  442. rendered_context = device.get_config_context()
  443. self.assertEqual(rendered_context['site_data'], 'ABC')
  444. # Override one of the default contexts
  445. configcontext5 = ConfigContext(
  446. name='Config Context 5',
  447. weight=2000,
  448. data={'foo': 999}
  449. )
  450. configcontext5.save()
  451. configcontext5.sites.add(site)
  452. rendered_context = device.get_config_context()
  453. self.assertEqual(rendered_context['foo'], 999)
  454. # Add a context which does NOT match our device and ensure it does not apply
  455. site2 = Site.objects.create(name='Site 2', slug='site-2')
  456. configcontext6 = ConfigContext(
  457. name='Config Context 6',
  458. weight=2000,
  459. data={'bar': 999}
  460. )
  461. configcontext6.save()
  462. configcontext6.sites.add(site2)
  463. rendered_context = device.get_config_context()
  464. self.assertEqual(rendered_context['bar'], 456)
  465. class ConfigTemplateTest(APIViewTestCases.APIViewTestCase):
  466. model = ConfigTemplate
  467. brief_fields = ['display', 'id', 'name', 'url']
  468. create_data = [
  469. {
  470. 'name': 'Config Template 4',
  471. 'template_code': 'Foo: {{ foo }}',
  472. },
  473. {
  474. 'name': 'Config Template 5',
  475. 'template_code': 'Bar: {{ bar }}',
  476. },
  477. {
  478. 'name': 'Config Template 6',
  479. 'template_code': 'Baz: {{ baz }}',
  480. },
  481. ]
  482. bulk_update_data = {
  483. 'description': 'New description',
  484. }
  485. @classmethod
  486. def setUpTestData(cls):
  487. config_templates = (
  488. ConfigTemplate(
  489. name='Config Template 1',
  490. template_code='Foo: {{ foo }}'
  491. ),
  492. ConfigTemplate(
  493. name='Config Template 2',
  494. template_code='Bar: {{ bar }}'
  495. ),
  496. ConfigTemplate(
  497. name='Config Template 3',
  498. template_code='Baz: {{ baz }}'
  499. ),
  500. )
  501. ConfigTemplate.objects.bulk_create(config_templates)
  502. class ReportTest(APITestCase):
  503. class TestReport(Report):
  504. def test_foo(self):
  505. self.log_success(None, "Report completed")
  506. @classmethod
  507. def setUpTestData(cls):
  508. ReportModule.objects.create(
  509. file_root=ManagedFileRootPathChoices.REPORTS,
  510. file_path='/var/tmp/report.py'
  511. )
  512. def get_test_report(self, *args):
  513. return ReportModule.objects.first(), self.TestReport()
  514. def setUp(self):
  515. super().setUp()
  516. # Monkey-patch the API viewset's _get_report() method to return our test Report above
  517. ReportViewSet._get_report = self.get_test_report
  518. def test_get_report(self):
  519. url = reverse('extras-api:report-detail', kwargs={'pk': None})
  520. response = self.client.get(url, **self.header)
  521. self.assertEqual(response.data['name'], self.TestReport.__name__)
  522. class ScriptTest(APITestCase):
  523. class TestScript(Script):
  524. class Meta:
  525. name = "Test script"
  526. var1 = StringVar()
  527. var2 = IntegerVar()
  528. var3 = BooleanVar()
  529. def run(self, data, commit=True):
  530. self.log_info(data['var1'])
  531. self.log_success(data['var2'])
  532. self.log_failure(data['var3'])
  533. return 'Script complete'
  534. @classmethod
  535. def setUpTestData(cls):
  536. ScriptModule.objects.create(
  537. file_root=ManagedFileRootPathChoices.SCRIPTS,
  538. file_path='/var/tmp/script.py'
  539. )
  540. def get_test_script(self, *args):
  541. return ScriptModule.objects.first(), self.TestScript
  542. def setUp(self):
  543. super().setUp()
  544. # Monkey-patch the API viewset's _get_script() method to return our test Script above
  545. ScriptViewSet._get_script = self.get_test_script
  546. def test_get_script(self):
  547. url = reverse('extras-api:script-detail', kwargs={'pk': None})
  548. response = self.client.get(url, **self.header)
  549. self.assertEqual(response.data['name'], self.TestScript.Meta.name)
  550. self.assertEqual(response.data['vars']['var1'], 'StringVar')
  551. self.assertEqual(response.data['vars']['var2'], 'IntegerVar')
  552. self.assertEqual(response.data['vars']['var3'], 'BooleanVar')
  553. class CreatedUpdatedFilterTest(APITestCase):
  554. @classmethod
  555. def setUpTestData(cls):
  556. site1 = Site.objects.create(name='Site 1', slug='site-1')
  557. location1 = Location.objects.create(site=site1, name='Location 1', slug='location-1')
  558. rackrole1 = RackRole.objects.create(name='Rack Role 1', slug='rack-role-1', color='ff0000')
  559. racks = (
  560. Rack(site=site1, location=location1, role=rackrole1, name='Rack 1', u_height=42),
  561. Rack(site=site1, location=location1, role=rackrole1, name='Rack 2', u_height=42)
  562. )
  563. Rack.objects.bulk_create(racks)
  564. # Change the created and last_updated of the second rack
  565. Rack.objects.filter(pk=racks[1].pk).update(
  566. last_updated=make_aware(datetime.datetime(2001, 2, 3, 1, 2, 3, 4)),
  567. created=make_aware(datetime.datetime(2001, 2, 3))
  568. )
  569. def test_get_rack_created(self):
  570. rack2 = Rack.objects.get(name='Rack 2')
  571. self.add_permissions('dcim.view_rack')
  572. url = reverse('dcim-api:rack-list')
  573. response = self.client.get('{}?created=2001-02-03'.format(url), **self.header)
  574. self.assertEqual(response.data['count'], 1)
  575. self.assertEqual(response.data['results'][0]['id'], rack2.pk)
  576. def test_get_rack_created_gte(self):
  577. rack1 = Rack.objects.get(name='Rack 1')
  578. self.add_permissions('dcim.view_rack')
  579. url = reverse('dcim-api:rack-list')
  580. response = self.client.get('{}?created__gte=2001-02-04'.format(url), **self.header)
  581. self.assertEqual(response.data['count'], 1)
  582. self.assertEqual(response.data['results'][0]['id'], rack1.pk)
  583. def test_get_rack_created_lte(self):
  584. rack2 = Rack.objects.get(name='Rack 2')
  585. self.add_permissions('dcim.view_rack')
  586. url = reverse('dcim-api:rack-list')
  587. response = self.client.get('{}?created__lte=2001-02-04'.format(url), **self.header)
  588. self.assertEqual(response.data['count'], 1)
  589. self.assertEqual(response.data['results'][0]['id'], rack2.pk)
  590. def test_get_rack_last_updated(self):
  591. rack2 = Rack.objects.get(name='Rack 2')
  592. self.add_permissions('dcim.view_rack')
  593. url = reverse('dcim-api:rack-list')
  594. response = self.client.get('{}?last_updated=2001-02-03%2001:02:03.000004'.format(url), **self.header)
  595. self.assertEqual(response.data['count'], 1)
  596. self.assertEqual(response.data['results'][0]['id'], rack2.pk)
  597. def test_get_rack_last_updated_gte(self):
  598. rack1 = Rack.objects.get(name='Rack 1')
  599. self.add_permissions('dcim.view_rack')
  600. url = reverse('dcim-api:rack-list')
  601. response = self.client.get('{}?last_updated__gte=2001-02-04%2001:02:03.000004'.format(url), **self.header)
  602. self.assertEqual(response.data['count'], 1)
  603. self.assertEqual(response.data['results'][0]['id'], rack1.pk)
  604. def test_get_rack_last_updated_lte(self):
  605. rack2 = Rack.objects.get(name='Rack 2')
  606. self.add_permissions('dcim.view_rack')
  607. url = reverse('dcim-api:rack-list')
  608. response = self.client.get('{}?last_updated__lte=2001-02-04%2001:02:03.000004'.format(url), **self.header)
  609. self.assertEqual(response.data['count'], 1)
  610. self.assertEqual(response.data['results'][0]['id'], rack2.pk)
  611. class ContentTypeTest(APITestCase):
  612. def test_list_objects(self):
  613. contenttype_count = ContentType.objects.count()
  614. response = self.client.get(reverse('extras-api:contenttype-list'), **self.header)
  615. self.assertHttpStatus(response, status.HTTP_200_OK)
  616. self.assertEqual(response.data['count'], contenttype_count)
  617. def test_get_object(self):
  618. contenttype = ContentType.objects.first()
  619. url = reverse('extras-api:contenttype-detail', kwargs={'pk': contenttype.pk})
  620. self.assertHttpStatus(self.client.get(url, **self.header), status.HTTP_200_OK)