test_api.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  1. import datetime
  2. from django.contrib.auth import get_user_model
  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.choices import *
  10. from extras.models import *
  11. from extras.reports import Report
  12. from extras.scripts import BooleanVar, IntegerVar, Script as PythonClass, StringVar
  13. from utilities.testing import APITestCase, APIViewTestCases
  14. User = get_user_model()
  15. class AppTest(APITestCase):
  16. def test_root(self):
  17. url = reverse('extras-api:api-root')
  18. response = self.client.get('{}?format=api'.format(url), **self.header)
  19. self.assertEqual(response.status_code, 200)
  20. class WebhookTest(APIViewTestCases.APIViewTestCase):
  21. model = Webhook
  22. brief_fields = ['description', 'display', 'id', 'name', 'url']
  23. create_data = [
  24. {
  25. 'name': 'Webhook 4',
  26. 'payload_url': 'http://example.com/?4',
  27. },
  28. {
  29. 'name': 'Webhook 5',
  30. 'payload_url': 'http://example.com/?5',
  31. },
  32. {
  33. 'name': 'Webhook 6',
  34. 'payload_url': 'http://example.com/?6',
  35. },
  36. ]
  37. bulk_update_data = {
  38. 'description': 'New description',
  39. 'ssl_verification': False,
  40. }
  41. @classmethod
  42. def setUpTestData(cls):
  43. webhooks = (
  44. Webhook(
  45. name='Webhook 1',
  46. payload_url='http://example.com/?1',
  47. ),
  48. Webhook(
  49. name='Webhook 2',
  50. payload_url='http://example.com/?1',
  51. ),
  52. Webhook(
  53. name='Webhook 3',
  54. payload_url='http://example.com/?1',
  55. ),
  56. )
  57. Webhook.objects.bulk_create(webhooks)
  58. class EventRuleTest(APIViewTestCases.APIViewTestCase):
  59. model = EventRule
  60. brief_fields = ['description', 'display', 'id', 'name', 'url']
  61. bulk_update_data = {
  62. 'enabled': False,
  63. 'description': 'New description',
  64. }
  65. update_data = {
  66. 'name': 'Event Rule X',
  67. 'enabled': False,
  68. 'description': 'New description',
  69. }
  70. @classmethod
  71. def setUpTestData(cls):
  72. webhooks = (
  73. Webhook(
  74. name='Webhook 1',
  75. payload_url='http://example.com/?1',
  76. ),
  77. Webhook(
  78. name='Webhook 2',
  79. payload_url='http://example.com/?1',
  80. ),
  81. Webhook(
  82. name='Webhook 3',
  83. payload_url='http://example.com/?1',
  84. ),
  85. Webhook(
  86. name='Webhook 4',
  87. payload_url='http://example.com/?1',
  88. ),
  89. Webhook(
  90. name='Webhook 5',
  91. payload_url='http://example.com/?1',
  92. ),
  93. Webhook(
  94. name='Webhook 6',
  95. payload_url='http://example.com/?1',
  96. ),
  97. )
  98. Webhook.objects.bulk_create(webhooks)
  99. event_rules = (
  100. EventRule(name='EventRule 1', type_create=True, action_object=webhooks[0]),
  101. EventRule(name='EventRule 2', type_create=True, action_object=webhooks[1]),
  102. EventRule(name='EventRule 3', type_create=True, action_object=webhooks[2]),
  103. )
  104. EventRule.objects.bulk_create(event_rules)
  105. cls.create_data = [
  106. {
  107. 'name': 'EventRule 4',
  108. 'content_types': ['dcim.device', 'dcim.devicetype'],
  109. 'type_create': True,
  110. 'action_type': EventRuleActionChoices.WEBHOOK,
  111. 'action_object_type': 'extras.webhook',
  112. 'action_object_id': webhooks[3].pk,
  113. },
  114. {
  115. 'name': 'EventRule 5',
  116. 'content_types': ['dcim.device', 'dcim.devicetype'],
  117. 'type_create': True,
  118. 'action_type': EventRuleActionChoices.WEBHOOK,
  119. 'action_object_type': 'extras.webhook',
  120. 'action_object_id': webhooks[4].pk,
  121. },
  122. {
  123. 'name': 'EventRule 6',
  124. 'content_types': ['dcim.device', 'dcim.devicetype'],
  125. 'type_create': True,
  126. 'action_type': EventRuleActionChoices.WEBHOOK,
  127. 'action_object_type': 'extras.webhook',
  128. 'action_object_id': webhooks[5].pk,
  129. },
  130. ]
  131. class CustomFieldTest(APIViewTestCases.APIViewTestCase):
  132. model = CustomField
  133. brief_fields = ['description', 'display', 'id', 'name', 'url']
  134. create_data = [
  135. {
  136. 'content_types': ['dcim.site'],
  137. 'name': 'cf4',
  138. 'type': 'date',
  139. },
  140. {
  141. 'content_types': ['dcim.site'],
  142. 'name': 'cf5',
  143. 'type': 'url',
  144. },
  145. {
  146. 'content_types': ['dcim.site'],
  147. 'name': 'cf6',
  148. 'type': 'text',
  149. },
  150. ]
  151. bulk_update_data = {
  152. 'description': 'New description',
  153. }
  154. update_data = {
  155. 'content_types': ['dcim.device'],
  156. 'name': 'New_Name',
  157. 'description': 'New description',
  158. }
  159. @classmethod
  160. def setUpTestData(cls):
  161. site_ct = ContentType.objects.get_for_model(Site)
  162. custom_fields = (
  163. CustomField(
  164. name='cf1',
  165. type='text'
  166. ),
  167. CustomField(
  168. name='cf2',
  169. type='integer'
  170. ),
  171. CustomField(
  172. name='cf3',
  173. type='boolean'
  174. ),
  175. )
  176. CustomField.objects.bulk_create(custom_fields)
  177. for cf in custom_fields:
  178. cf.content_types.add(site_ct)
  179. class CustomFieldChoiceSetTest(APIViewTestCases.APIViewTestCase):
  180. model = CustomFieldChoiceSet
  181. brief_fields = ['choices_count', 'description', 'display', 'id', 'name', 'url']
  182. create_data = [
  183. {
  184. 'name': 'Choice Set 4',
  185. 'extra_choices': [
  186. ['4A', 'Choice 1'],
  187. ['4B', 'Choice 2'],
  188. ['4C', 'Choice 3'],
  189. ],
  190. },
  191. {
  192. 'name': 'Choice Set 5',
  193. 'extra_choices': [
  194. ['5A', 'Choice 1'],
  195. ['5B', 'Choice 2'],
  196. ['5C', 'Choice 3'],
  197. ],
  198. },
  199. {
  200. 'name': 'Choice Set 6',
  201. 'extra_choices': [
  202. ['6A', 'Choice 1'],
  203. ['6B', 'Choice 2'],
  204. ['6C', 'Choice 3'],
  205. ],
  206. },
  207. ]
  208. bulk_update_data = {
  209. 'description': 'New description',
  210. }
  211. update_data = {
  212. 'name': 'Choice Set X',
  213. 'extra_choices': [
  214. ['X1', 'Choice 1'],
  215. ['X2', 'Choice 2'],
  216. ['X3', 'Choice 3'],
  217. ],
  218. 'description': 'New description',
  219. }
  220. @classmethod
  221. def setUpTestData(cls):
  222. choice_sets = (
  223. CustomFieldChoiceSet(name='Choice Set 1', extra_choices=['1A', '1B', '1C', '1D', '1E']),
  224. CustomFieldChoiceSet(name='Choice Set 2', extra_choices=['2A', '2B', '2C', '2D', '2E']),
  225. CustomFieldChoiceSet(name='Choice Set 3', extra_choices=['3A', '3B', '3C', '3D', '3E']),
  226. )
  227. CustomFieldChoiceSet.objects.bulk_create(choice_sets)
  228. def test_invalid_choice_items(self):
  229. """
  230. Attempting to define each choice as a single-item list should return a 400 error.
  231. """
  232. self.add_permissions('extras.add_customfieldchoiceset')
  233. data = {
  234. "name": "test",
  235. "extra_choices": [
  236. ["choice1"],
  237. ["choice2"],
  238. ["choice3"],
  239. ]
  240. }
  241. response = self.client.post(self._get_list_url(), data, format='json', **self.header)
  242. self.assertEqual(response.status_code, 400)
  243. class CustomLinkTest(APIViewTestCases.APIViewTestCase):
  244. model = CustomLink
  245. brief_fields = ['display', 'id', 'name', 'url']
  246. create_data = [
  247. {
  248. 'content_types': ['dcim.site'],
  249. 'name': 'Custom Link 4',
  250. 'enabled': True,
  251. 'link_text': 'Link 4',
  252. 'link_url': 'http://example.com/?4',
  253. },
  254. {
  255. 'content_types': ['dcim.site'],
  256. 'name': 'Custom Link 5',
  257. 'enabled': True,
  258. 'link_text': 'Link 5',
  259. 'link_url': 'http://example.com/?5',
  260. },
  261. {
  262. 'content_types': ['dcim.site'],
  263. 'name': 'Custom Link 6',
  264. 'enabled': False,
  265. 'link_text': 'Link 6',
  266. 'link_url': 'http://example.com/?6',
  267. },
  268. ]
  269. bulk_update_data = {
  270. 'new_window': True,
  271. 'enabled': False,
  272. }
  273. @classmethod
  274. def setUpTestData(cls):
  275. site_ct = ContentType.objects.get_for_model(Site)
  276. custom_links = (
  277. CustomLink(
  278. name='Custom Link 1',
  279. enabled=True,
  280. link_text='Link 1',
  281. link_url='http://example.com/?1',
  282. ),
  283. CustomLink(
  284. name='Custom Link 2',
  285. enabled=True,
  286. link_text='Link 2',
  287. link_url='http://example.com/?2',
  288. ),
  289. CustomLink(
  290. name='Custom Link 3',
  291. enabled=False,
  292. link_text='Link 3',
  293. link_url='http://example.com/?3',
  294. ),
  295. )
  296. CustomLink.objects.bulk_create(custom_links)
  297. for i, custom_link in enumerate(custom_links):
  298. custom_link.content_types.set([site_ct])
  299. class SavedFilterTest(APIViewTestCases.APIViewTestCase):
  300. model = SavedFilter
  301. brief_fields = ['description', 'display', 'id', 'name', 'slug', 'url']
  302. create_data = [
  303. {
  304. 'content_types': ['dcim.site'],
  305. 'name': 'Saved Filter 4',
  306. 'slug': 'saved-filter-4',
  307. 'weight': 100,
  308. 'enabled': True,
  309. 'shared': True,
  310. 'parameters': {'status': ['active']},
  311. },
  312. {
  313. 'content_types': ['dcim.site'],
  314. 'name': 'Saved Filter 5',
  315. 'slug': 'saved-filter-5',
  316. 'weight': 200,
  317. 'enabled': True,
  318. 'shared': True,
  319. 'parameters': {'status': ['planned']},
  320. },
  321. {
  322. 'content_types': ['dcim.site'],
  323. 'name': 'Saved Filter 6',
  324. 'slug': 'saved-filter-6',
  325. 'weight': 300,
  326. 'enabled': True,
  327. 'shared': True,
  328. 'parameters': {'status': ['retired']},
  329. },
  330. ]
  331. bulk_update_data = {
  332. 'weight': 1000,
  333. 'enabled': False,
  334. 'shared': False,
  335. }
  336. @classmethod
  337. def setUpTestData(cls):
  338. site_ct = ContentType.objects.get_for_model(Site)
  339. saved_filters = (
  340. SavedFilter(
  341. name='Saved Filter 1',
  342. slug='saved-filter-1',
  343. weight=100,
  344. enabled=True,
  345. shared=True,
  346. parameters={'status': ['active']}
  347. ),
  348. SavedFilter(
  349. name='Saved Filter 2',
  350. slug='saved-filter-2',
  351. weight=200,
  352. enabled=True,
  353. shared=True,
  354. parameters={'status': ['planned']}
  355. ),
  356. SavedFilter(
  357. name='Saved Filter 3',
  358. slug='saved-filter-3',
  359. weight=300,
  360. enabled=True,
  361. shared=True,
  362. parameters={'status': ['retired']}
  363. ),
  364. )
  365. SavedFilter.objects.bulk_create(saved_filters)
  366. for i, savedfilter in enumerate(saved_filters):
  367. savedfilter.content_types.set([site_ct])
  368. class BookmarkTest(
  369. APIViewTestCases.GetObjectViewTestCase,
  370. APIViewTestCases.ListObjectsViewTestCase,
  371. APIViewTestCases.CreateObjectViewTestCase,
  372. APIViewTestCases.DeleteObjectViewTestCase
  373. ):
  374. model = Bookmark
  375. brief_fields = ['display', 'id', 'object_id', 'object_type', 'url']
  376. @classmethod
  377. def setUpTestData(cls):
  378. sites = (
  379. Site(name='Site 1', slug='site-1'),
  380. Site(name='Site 2', slug='site-2'),
  381. Site(name='Site 3', slug='site-3'),
  382. Site(name='Site 4', slug='site-4'),
  383. Site(name='Site 5', slug='site-5'),
  384. Site(name='Site 6', slug='site-6'),
  385. )
  386. Site.objects.bulk_create(sites)
  387. def setUp(self):
  388. super().setUp()
  389. sites = Site.objects.all()
  390. bookmarks = (
  391. Bookmark(object=sites[0], user=self.user),
  392. Bookmark(object=sites[1], user=self.user),
  393. Bookmark(object=sites[2], user=self.user),
  394. )
  395. Bookmark.objects.bulk_create(bookmarks)
  396. self.create_data = [
  397. {
  398. 'object_type': 'dcim.site',
  399. 'object_id': sites[3].pk,
  400. 'user': self.user.pk,
  401. },
  402. {
  403. 'object_type': 'dcim.site',
  404. 'object_id': sites[4].pk,
  405. 'user': self.user.pk,
  406. },
  407. {
  408. 'object_type': 'dcim.site',
  409. 'object_id': sites[5].pk,
  410. 'user': self.user.pk,
  411. },
  412. ]
  413. class ExportTemplateTest(APIViewTestCases.APIViewTestCase):
  414. model = ExportTemplate
  415. brief_fields = ['description', 'display', 'id', 'name', 'url']
  416. create_data = [
  417. {
  418. 'content_types': ['dcim.device'],
  419. 'name': 'Test Export Template 4',
  420. 'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}',
  421. },
  422. {
  423. 'content_types': ['dcim.device'],
  424. 'name': 'Test Export Template 5',
  425. 'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}',
  426. },
  427. {
  428. 'content_types': ['dcim.device'],
  429. 'name': 'Test Export Template 6',
  430. 'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}',
  431. },
  432. ]
  433. bulk_update_data = {
  434. 'description': 'New description',
  435. }
  436. @classmethod
  437. def setUpTestData(cls):
  438. export_templates = (
  439. ExportTemplate(
  440. name='Export Template 1',
  441. template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}'
  442. ),
  443. ExportTemplate(
  444. name='Export Template 2',
  445. template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}'
  446. ),
  447. ExportTemplate(
  448. name='Export Template 3',
  449. template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}'
  450. ),
  451. )
  452. ExportTemplate.objects.bulk_create(export_templates)
  453. for et in export_templates:
  454. et.content_types.set([ContentType.objects.get_for_model(Device)])
  455. class TagTest(APIViewTestCases.APIViewTestCase):
  456. model = Tag
  457. brief_fields = ['color', 'description', 'display', 'id', 'name', 'slug', 'url']
  458. create_data = [
  459. {
  460. 'name': 'Tag 4',
  461. 'slug': 'tag-4',
  462. },
  463. {
  464. 'name': 'Tag 5',
  465. 'slug': 'tag-5',
  466. },
  467. {
  468. 'name': 'Tag 6',
  469. 'slug': 'tag-6',
  470. },
  471. ]
  472. bulk_update_data = {
  473. 'description': 'New description',
  474. }
  475. @classmethod
  476. def setUpTestData(cls):
  477. tags = (
  478. Tag(name='Tag 1', slug='tag-1'),
  479. Tag(name='Tag 2', slug='tag-2'),
  480. Tag(name='Tag 3', slug='tag-3'),
  481. )
  482. Tag.objects.bulk_create(tags)
  483. # TODO: Standardize to APIViewTestCase (needs create & update tests)
  484. class ImageAttachmentTest(
  485. APIViewTestCases.GetObjectViewTestCase,
  486. APIViewTestCases.ListObjectsViewTestCase,
  487. APIViewTestCases.DeleteObjectViewTestCase,
  488. APIViewTestCases.GraphQLTestCase
  489. ):
  490. model = ImageAttachment
  491. brief_fields = ['display', 'id', 'image', 'name', 'url']
  492. @classmethod
  493. def setUpTestData(cls):
  494. ct = ContentType.objects.get_for_model(Site)
  495. site = Site.objects.create(name='Site 1', slug='site-1')
  496. image_attachments = (
  497. ImageAttachment(
  498. content_type=ct,
  499. object_id=site.pk,
  500. name='Image Attachment 1',
  501. image='http://example.com/image1.png',
  502. image_height=100,
  503. image_width=100
  504. ),
  505. ImageAttachment(
  506. content_type=ct,
  507. object_id=site.pk,
  508. name='Image Attachment 2',
  509. image='http://example.com/image2.png',
  510. image_height=100,
  511. image_width=100
  512. ),
  513. ImageAttachment(
  514. content_type=ct,
  515. object_id=site.pk,
  516. name='Image Attachment 3',
  517. image='http://example.com/image3.png',
  518. image_height=100,
  519. image_width=100
  520. )
  521. )
  522. ImageAttachment.objects.bulk_create(image_attachments)
  523. class JournalEntryTest(APIViewTestCases.APIViewTestCase):
  524. model = JournalEntry
  525. brief_fields = ['created', 'display', 'id', 'url']
  526. bulk_update_data = {
  527. 'comments': 'Overwritten',
  528. }
  529. @classmethod
  530. def setUpTestData(cls):
  531. user = User.objects.first()
  532. site = Site.objects.create(name='Site 1', slug='site-1')
  533. journal_entries = (
  534. JournalEntry(
  535. created_by=user,
  536. assigned_object=site,
  537. comments='Fourth entry',
  538. ),
  539. JournalEntry(
  540. created_by=user,
  541. assigned_object=site,
  542. comments='Fifth entry',
  543. ),
  544. JournalEntry(
  545. created_by=user,
  546. assigned_object=site,
  547. comments='Sixth entry',
  548. ),
  549. )
  550. JournalEntry.objects.bulk_create(journal_entries)
  551. cls.create_data = [
  552. {
  553. 'assigned_object_type': 'dcim.site',
  554. 'assigned_object_id': site.pk,
  555. 'comments': 'First entry',
  556. },
  557. {
  558. 'assigned_object_type': 'dcim.site',
  559. 'assigned_object_id': site.pk,
  560. 'comments': 'Second entry',
  561. },
  562. {
  563. 'assigned_object_type': 'dcim.site',
  564. 'assigned_object_id': site.pk,
  565. 'comments': 'Third entry',
  566. },
  567. ]
  568. class ConfigContextTest(APIViewTestCases.APIViewTestCase):
  569. model = ConfigContext
  570. brief_fields = ['description', 'display', 'id', 'name', 'url']
  571. create_data = [
  572. {
  573. 'name': 'Config Context 4',
  574. 'data': {'more_foo': True},
  575. },
  576. {
  577. 'name': 'Config Context 5',
  578. 'data': {'more_bar': False},
  579. },
  580. {
  581. 'name': 'Config Context 6',
  582. 'data': {'more_baz': None},
  583. },
  584. ]
  585. bulk_update_data = {
  586. 'description': 'New description',
  587. }
  588. @classmethod
  589. def setUpTestData(cls):
  590. config_contexts = (
  591. ConfigContext(name='Config Context 1', weight=100, data={'foo': 123}),
  592. ConfigContext(name='Config Context 2', weight=200, data={'bar': 456}),
  593. ConfigContext(name='Config Context 3', weight=300, data={'baz': 789}),
  594. )
  595. ConfigContext.objects.bulk_create(config_contexts)
  596. def test_render_configcontext_for_object(self):
  597. """
  598. Test rendering config context data for a device.
  599. """
  600. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  601. devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
  602. role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
  603. site = Site.objects.create(name='Site-1', slug='site-1')
  604. device = Device.objects.create(name='Device 1', device_type=devicetype, role=role, site=site)
  605. # Test default config contexts (created at test setup)
  606. rendered_context = device.get_config_context()
  607. self.assertEqual(rendered_context['foo'], 123)
  608. self.assertEqual(rendered_context['bar'], 456)
  609. self.assertEqual(rendered_context['baz'], 789)
  610. # Add another context specific to the site
  611. configcontext4 = ConfigContext(
  612. name='Config Context 4',
  613. data={'site_data': 'ABC'}
  614. )
  615. configcontext4.save()
  616. configcontext4.sites.add(site)
  617. rendered_context = device.get_config_context()
  618. self.assertEqual(rendered_context['site_data'], 'ABC')
  619. # Override one of the default contexts
  620. configcontext5 = ConfigContext(
  621. name='Config Context 5',
  622. weight=2000,
  623. data={'foo': 999}
  624. )
  625. configcontext5.save()
  626. configcontext5.sites.add(site)
  627. rendered_context = device.get_config_context()
  628. self.assertEqual(rendered_context['foo'], 999)
  629. # Add a context which does NOT match our device and ensure it does not apply
  630. site2 = Site.objects.create(name='Site 2', slug='site-2')
  631. configcontext6 = ConfigContext(
  632. name='Config Context 6',
  633. weight=2000,
  634. data={'bar': 999}
  635. )
  636. configcontext6.save()
  637. configcontext6.sites.add(site2)
  638. rendered_context = device.get_config_context()
  639. self.assertEqual(rendered_context['bar'], 456)
  640. class ConfigTemplateTest(APIViewTestCases.APIViewTestCase):
  641. model = ConfigTemplate
  642. brief_fields = ['description', 'display', 'id', 'name', 'url']
  643. create_data = [
  644. {
  645. 'name': 'Config Template 4',
  646. 'template_code': 'Foo: {{ foo }}',
  647. },
  648. {
  649. 'name': 'Config Template 5',
  650. 'template_code': 'Bar: {{ bar }}',
  651. },
  652. {
  653. 'name': 'Config Template 6',
  654. 'template_code': 'Baz: {{ baz }}',
  655. },
  656. ]
  657. bulk_update_data = {
  658. 'description': 'New description',
  659. }
  660. @classmethod
  661. def setUpTestData(cls):
  662. config_templates = (
  663. ConfigTemplate(
  664. name='Config Template 1',
  665. template_code='Foo: {{ foo }}'
  666. ),
  667. ConfigTemplate(
  668. name='Config Template 2',
  669. template_code='Bar: {{ bar }}'
  670. ),
  671. ConfigTemplate(
  672. name='Config Template 3',
  673. template_code='Baz: {{ baz }}'
  674. ),
  675. )
  676. ConfigTemplate.objects.bulk_create(config_templates)
  677. class ScriptTest(APITestCase):
  678. class TestScriptClass(PythonClass):
  679. class Meta:
  680. name = "Test script"
  681. var1 = StringVar()
  682. var2 = IntegerVar()
  683. var3 = BooleanVar()
  684. def run(self, data, commit=True):
  685. self.log_info(data['var1'])
  686. self.log_success(data['var2'])
  687. self.log_failure(data['var3'])
  688. return 'Script complete'
  689. @classmethod
  690. def setUpTestData(cls):
  691. module = ScriptModule.objects.create(
  692. file_root=ManagedFileRootPathChoices.SCRIPTS,
  693. file_path='/var/tmp/script.py'
  694. )
  695. Script.objects.create(
  696. module=module,
  697. name="Test script",
  698. is_executable=True,
  699. )
  700. def python_class(self):
  701. return self.TestScriptClass
  702. def setUp(self):
  703. super().setUp()
  704. # Monkey-patch the Script model to return our TestScriptClass above
  705. from extras.api.views import ScriptViewSet
  706. Script.python_class = self.python_class
  707. def test_get_script(self):
  708. module = ScriptModule.objects.get(
  709. file_root=ManagedFileRootPathChoices.SCRIPTS,
  710. file_path='/var/tmp/script.py'
  711. )
  712. script = module.scripts.all().first()
  713. url = reverse('extras-api:script-detail', kwargs={'pk': script.pk})
  714. response = self.client.get(url, **self.header)
  715. self.assertEqual(response.data['name'], self.TestScriptClass.Meta.name)
  716. self.assertEqual(response.data['vars']['var1'], 'StringVar')
  717. self.assertEqual(response.data['vars']['var2'], 'IntegerVar')
  718. self.assertEqual(response.data['vars']['var3'], 'BooleanVar')
  719. class CreatedUpdatedFilterTest(APITestCase):
  720. @classmethod
  721. def setUpTestData(cls):
  722. site1 = Site.objects.create(name='Site 1', slug='site-1')
  723. location1 = Location.objects.create(site=site1, name='Location 1', slug='location-1')
  724. rackrole1 = RackRole.objects.create(name='Rack Role 1', slug='rack-role-1', color='ff0000')
  725. racks = (
  726. Rack(site=site1, location=location1, role=rackrole1, name='Rack 1', u_height=42),
  727. Rack(site=site1, location=location1, role=rackrole1, name='Rack 2', u_height=42)
  728. )
  729. Rack.objects.bulk_create(racks)
  730. # Change the created and last_updated of the second rack
  731. Rack.objects.filter(pk=racks[1].pk).update(
  732. last_updated=make_aware(datetime.datetime(2001, 2, 3, 1, 2, 3, 4)),
  733. created=make_aware(datetime.datetime(2001, 2, 3))
  734. )
  735. def test_get_rack_created(self):
  736. rack2 = Rack.objects.get(name='Rack 2')
  737. self.add_permissions('dcim.view_rack')
  738. url = reverse('dcim-api:rack-list')
  739. response = self.client.get('{}?created=2001-02-03'.format(url), **self.header)
  740. self.assertEqual(response.data['count'], 1)
  741. self.assertEqual(response.data['results'][0]['id'], rack2.pk)
  742. def test_get_rack_created_gte(self):
  743. rack1 = Rack.objects.get(name='Rack 1')
  744. self.add_permissions('dcim.view_rack')
  745. url = reverse('dcim-api:rack-list')
  746. response = self.client.get('{}?created__gte=2001-02-04'.format(url), **self.header)
  747. self.assertEqual(response.data['count'], 1)
  748. self.assertEqual(response.data['results'][0]['id'], rack1.pk)
  749. def test_get_rack_created_lte(self):
  750. rack2 = Rack.objects.get(name='Rack 2')
  751. self.add_permissions('dcim.view_rack')
  752. url = reverse('dcim-api:rack-list')
  753. response = self.client.get('{}?created__lte=2001-02-04'.format(url), **self.header)
  754. self.assertEqual(response.data['count'], 1)
  755. self.assertEqual(response.data['results'][0]['id'], rack2.pk)
  756. def test_get_rack_last_updated(self):
  757. rack2 = Rack.objects.get(name='Rack 2')
  758. self.add_permissions('dcim.view_rack')
  759. url = reverse('dcim-api:rack-list')
  760. response = self.client.get('{}?last_updated=2001-02-03%2001:02:03.000004'.format(url), **self.header)
  761. self.assertEqual(response.data['count'], 1)
  762. self.assertEqual(response.data['results'][0]['id'], rack2.pk)
  763. def test_get_rack_last_updated_gte(self):
  764. rack1 = Rack.objects.get(name='Rack 1')
  765. self.add_permissions('dcim.view_rack')
  766. url = reverse('dcim-api:rack-list')
  767. response = self.client.get('{}?last_updated__gte=2001-02-04%2001:02:03.000004'.format(url), **self.header)
  768. self.assertEqual(response.data['count'], 1)
  769. self.assertEqual(response.data['results'][0]['id'], rack1.pk)
  770. def test_get_rack_last_updated_lte(self):
  771. rack2 = Rack.objects.get(name='Rack 2')
  772. self.add_permissions('dcim.view_rack')
  773. url = reverse('dcim-api:rack-list')
  774. response = self.client.get('{}?last_updated__lte=2001-02-04%2001:02:03.000004'.format(url), **self.header)
  775. self.assertEqual(response.data['count'], 1)
  776. self.assertEqual(response.data['results'][0]['id'], rack2.pk)
  777. class ContentTypeTest(APITestCase):
  778. def test_list_objects(self):
  779. contenttype_count = ContentType.objects.count()
  780. response = self.client.get(reverse('extras-api:contenttype-list'), **self.header)
  781. self.assertHttpStatus(response, status.HTTP_200_OK)
  782. self.assertEqual(response.data['count'], contenttype_count)
  783. def test_get_object(self):
  784. contenttype = ContentType.objects.first()
  785. url = reverse('extras-api:contenttype-detail', kwargs={'pk': contenttype.pk})
  786. self.assertHttpStatus(self.client.get(url, **self.header), status.HTTP_200_OK)