test_api.py 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130
  1. import datetime
  2. from django.contrib.contenttypes.models import ContentType
  3. from django.urls import reverse
  4. from django.utils.timezone import make_aware
  5. from rest_framework import status
  6. from core.choices import ManagedFileRootPathChoices
  7. from core.events import *
  8. from core.models import ObjectType
  9. from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Rack, Location, RackRole, Site
  10. from extras.choices import *
  11. from extras.models import *
  12. from extras.scripts import BooleanVar, IntegerVar, Script as PythonClass, StringVar
  13. from users.models import Group, User
  14. from utilities.testing import APITestCase, APIViewTestCases
  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', event_types=[OBJECT_CREATED], action_object=webhooks[0]),
  101. EventRule(name='EventRule 2', event_types=[OBJECT_CREATED], action_object=webhooks[1]),
  102. EventRule(name='EventRule 3', event_types=[OBJECT_CREATED], action_object=webhooks[2]),
  103. )
  104. EventRule.objects.bulk_create(event_rules)
  105. cls.create_data = [
  106. {
  107. 'name': 'EventRule 4',
  108. 'object_types': ['dcim.device', 'dcim.devicetype'],
  109. 'event_types': [OBJECT_CREATED],
  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. 'object_types': ['dcim.device', 'dcim.devicetype'],
  117. 'event_types': [OBJECT_CREATED],
  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. 'object_types': ['dcim.device', 'dcim.devicetype'],
  125. 'event_types': [OBJECT_CREATED],
  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. 'object_types': ['dcim.site'],
  137. 'name': 'cf4',
  138. 'type': 'date',
  139. },
  140. {
  141. 'object_types': ['dcim.site'],
  142. 'name': 'cf5',
  143. 'type': 'url',
  144. },
  145. {
  146. 'object_types': ['dcim.site'],
  147. 'name': 'cf6',
  148. 'type': 'text',
  149. },
  150. ]
  151. bulk_update_data = {
  152. 'description': 'New description',
  153. }
  154. update_data = {
  155. 'object_types': ['dcim.device'],
  156. 'name': 'New_Name',
  157. 'description': 'New description',
  158. }
  159. @classmethod
  160. def setUpTestData(cls):
  161. site_ct = ObjectType.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.object_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(
  224. name='Choice Set 1',
  225. extra_choices=[['1A', '1A'], ['1B', '1B'], ['1C', '1C'], ['1D', '1D'], ['1E', '1E']],
  226. ),
  227. CustomFieldChoiceSet(
  228. name='Choice Set 2',
  229. extra_choices=[['2A', '2A'], ['2B', '2B'], ['2C', '2C'], ['2D', '2D'], ['2E', '2E']],
  230. ),
  231. CustomFieldChoiceSet(
  232. name='Choice Set 3',
  233. extra_choices=[['3A', '3A'], ['3B', '3B'], ['3C', '3C'], ['3D', '3D'], ['3E', '3E']],
  234. ),
  235. )
  236. CustomFieldChoiceSet.objects.bulk_create(choice_sets)
  237. def test_invalid_choice_items(self):
  238. """
  239. Attempting to define each choice as a single-item list should return a 400 error.
  240. """
  241. self.add_permissions('extras.add_customfieldchoiceset')
  242. data = {
  243. "name": "test",
  244. "extra_choices": [
  245. ["choice1"],
  246. ["choice2"],
  247. ["choice3"],
  248. ]
  249. }
  250. response = self.client.post(self._get_list_url(), data, format='json', **self.header)
  251. self.assertEqual(response.status_code, 400)
  252. class CustomLinkTest(APIViewTestCases.APIViewTestCase):
  253. model = CustomLink
  254. brief_fields = ['display', 'id', 'name', 'url']
  255. create_data = [
  256. {
  257. 'object_types': ['dcim.site'],
  258. 'name': 'Custom Link 4',
  259. 'enabled': True,
  260. 'link_text': 'Link 4',
  261. 'link_url': 'http://example.com/?4',
  262. },
  263. {
  264. 'object_types': ['dcim.site'],
  265. 'name': 'Custom Link 5',
  266. 'enabled': True,
  267. 'link_text': 'Link 5',
  268. 'link_url': 'http://example.com/?5',
  269. },
  270. {
  271. 'object_types': ['dcim.site'],
  272. 'name': 'Custom Link 6',
  273. 'enabled': False,
  274. 'link_text': 'Link 6',
  275. 'link_url': 'http://example.com/?6',
  276. },
  277. ]
  278. bulk_update_data = {
  279. 'new_window': True,
  280. 'enabled': False,
  281. }
  282. @classmethod
  283. def setUpTestData(cls):
  284. site_type = ObjectType.objects.get_for_model(Site)
  285. custom_links = (
  286. CustomLink(
  287. name='Custom Link 1',
  288. enabled=True,
  289. link_text='Link 1',
  290. link_url='http://example.com/?1',
  291. ),
  292. CustomLink(
  293. name='Custom Link 2',
  294. enabled=True,
  295. link_text='Link 2',
  296. link_url='http://example.com/?2',
  297. ),
  298. CustomLink(
  299. name='Custom Link 3',
  300. enabled=False,
  301. link_text='Link 3',
  302. link_url='http://example.com/?3',
  303. ),
  304. )
  305. CustomLink.objects.bulk_create(custom_links)
  306. for i, custom_link in enumerate(custom_links):
  307. custom_link.object_types.set([site_type])
  308. class SavedFilterTest(APIViewTestCases.APIViewTestCase):
  309. model = SavedFilter
  310. brief_fields = ['description', 'display', 'id', 'name', 'slug', 'url']
  311. create_data = [
  312. {
  313. 'object_types': ['dcim.site'],
  314. 'name': 'Saved Filter 4',
  315. 'slug': 'saved-filter-4',
  316. 'weight': 100,
  317. 'enabled': True,
  318. 'shared': True,
  319. 'parameters': {'status': ['active']},
  320. },
  321. {
  322. 'object_types': ['dcim.site'],
  323. 'name': 'Saved Filter 5',
  324. 'slug': 'saved-filter-5',
  325. 'weight': 200,
  326. 'enabled': True,
  327. 'shared': True,
  328. 'parameters': {'status': ['planned']},
  329. },
  330. {
  331. 'object_types': ['dcim.site'],
  332. 'name': 'Saved Filter 6',
  333. 'slug': 'saved-filter-6',
  334. 'weight': 300,
  335. 'enabled': True,
  336. 'shared': True,
  337. 'parameters': {'status': ['retired']},
  338. },
  339. ]
  340. bulk_update_data = {
  341. 'weight': 1000,
  342. 'enabled': False,
  343. 'shared': False,
  344. }
  345. @classmethod
  346. def setUpTestData(cls):
  347. site_type = ObjectType.objects.get_for_model(Site)
  348. saved_filters = (
  349. SavedFilter(
  350. name='Saved Filter 1',
  351. slug='saved-filter-1',
  352. weight=100,
  353. enabled=True,
  354. shared=True,
  355. parameters={'status': ['active']}
  356. ),
  357. SavedFilter(
  358. name='Saved Filter 2',
  359. slug='saved-filter-2',
  360. weight=200,
  361. enabled=True,
  362. shared=True,
  363. parameters={'status': ['planned']}
  364. ),
  365. SavedFilter(
  366. name='Saved Filter 3',
  367. slug='saved-filter-3',
  368. weight=300,
  369. enabled=True,
  370. shared=True,
  371. parameters={'status': ['retired']}
  372. ),
  373. )
  374. SavedFilter.objects.bulk_create(saved_filters)
  375. for i, savedfilter in enumerate(saved_filters):
  376. savedfilter.object_types.set([site_type])
  377. class BookmarkTest(
  378. APIViewTestCases.GetObjectViewTestCase,
  379. APIViewTestCases.ListObjectsViewTestCase,
  380. APIViewTestCases.CreateObjectViewTestCase,
  381. APIViewTestCases.DeleteObjectViewTestCase
  382. ):
  383. model = Bookmark
  384. brief_fields = ['display', 'id', 'object_id', 'object_type', 'url']
  385. @classmethod
  386. def setUpTestData(cls):
  387. sites = (
  388. Site(name='Site 1', slug='site-1'),
  389. Site(name='Site 2', slug='site-2'),
  390. Site(name='Site 3', slug='site-3'),
  391. Site(name='Site 4', slug='site-4'),
  392. Site(name='Site 5', slug='site-5'),
  393. Site(name='Site 6', slug='site-6'),
  394. )
  395. Site.objects.bulk_create(sites)
  396. def setUp(self):
  397. super().setUp()
  398. sites = Site.objects.all()
  399. bookmarks = (
  400. Bookmark(object=sites[0], user=self.user),
  401. Bookmark(object=sites[1], user=self.user),
  402. Bookmark(object=sites[2], user=self.user),
  403. )
  404. Bookmark.objects.bulk_create(bookmarks)
  405. self.create_data = [
  406. {
  407. 'object_type': 'dcim.site',
  408. 'object_id': sites[3].pk,
  409. 'user': self.user.pk,
  410. },
  411. {
  412. 'object_type': 'dcim.site',
  413. 'object_id': sites[4].pk,
  414. 'user': self.user.pk,
  415. },
  416. {
  417. 'object_type': 'dcim.site',
  418. 'object_id': sites[5].pk,
  419. 'user': self.user.pk,
  420. },
  421. ]
  422. class ExportTemplateTest(APIViewTestCases.APIViewTestCase):
  423. model = ExportTemplate
  424. brief_fields = ['description', 'display', 'id', 'name', 'url']
  425. create_data = [
  426. {
  427. 'object_types': ['dcim.device'],
  428. 'name': 'Test Export Template 4',
  429. 'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}',
  430. },
  431. {
  432. 'object_types': ['dcim.device'],
  433. 'name': 'Test Export Template 5',
  434. 'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}',
  435. },
  436. {
  437. 'object_types': ['dcim.device'],
  438. 'name': 'Test Export Template 6',
  439. 'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}',
  440. 'file_name': 'test_export_template_6',
  441. },
  442. ]
  443. bulk_update_data = {
  444. 'description': 'New description',
  445. }
  446. @classmethod
  447. def setUpTestData(cls):
  448. export_templates = (
  449. ExportTemplate(
  450. name='Export Template 1',
  451. template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}'
  452. ),
  453. ExportTemplate(
  454. name='Export Template 2',
  455. template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}',
  456. file_name='export_template_2',
  457. file_extension='test',
  458. ),
  459. ExportTemplate(
  460. name='Export Template 3',
  461. template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}'
  462. ),
  463. )
  464. ExportTemplate.objects.bulk_create(export_templates)
  465. device_object_type = ObjectType.objects.get_for_model(Device)
  466. for et in export_templates:
  467. et.object_types.set([device_object_type])
  468. class TagTest(APIViewTestCases.APIViewTestCase):
  469. model = Tag
  470. brief_fields = ['color', 'description', 'display', 'id', 'name', 'slug', 'url']
  471. create_data = [
  472. {
  473. 'name': 'Tag 4',
  474. 'slug': 'tag-4',
  475. 'weight': 1000,
  476. },
  477. {
  478. 'name': 'Tag 5',
  479. 'slug': 'tag-5',
  480. },
  481. {
  482. 'name': 'Tag 6',
  483. 'slug': 'tag-6',
  484. },
  485. ]
  486. bulk_update_data = {
  487. 'description': 'New description',
  488. }
  489. @classmethod
  490. def setUpTestData(cls):
  491. tags = (
  492. Tag(name='Tag 1', slug='tag-1'),
  493. Tag(name='Tag 2', slug='tag-2'),
  494. Tag(name='Tag 3', slug='tag-3', weight=26),
  495. )
  496. Tag.objects.bulk_create(tags)
  497. class TaggedItemTest(
  498. APIViewTestCases.GetObjectViewTestCase,
  499. APIViewTestCases.ListObjectsViewTestCase
  500. ):
  501. model = TaggedItem
  502. brief_fields = ['display', 'id', 'object', 'object_id', 'object_type', 'tag', 'url']
  503. @classmethod
  504. def setUpTestData(cls):
  505. tags = (
  506. Tag(name='Tag 1', slug='tag-1'),
  507. Tag(name='Tag 2', slug='tag-2'),
  508. Tag(name='Tag 3', slug='tag-3'),
  509. )
  510. Tag.objects.bulk_create(tags)
  511. sites = (
  512. Site(name='Site 1', slug='site-1'),
  513. Site(name='Site 2', slug='site-2'),
  514. Site(name='Site 3', slug='site-3'),
  515. )
  516. Site.objects.bulk_create(sites)
  517. sites[0].tags.set([tags[0], tags[1]])
  518. sites[1].tags.set([tags[1], tags[2]])
  519. sites[2].tags.set([tags[2], tags[0]])
  520. # TODO: Standardize to APIViewTestCase (needs create & update tests)
  521. class ImageAttachmentTest(
  522. APIViewTestCases.GetObjectViewTestCase,
  523. APIViewTestCases.ListObjectsViewTestCase,
  524. APIViewTestCases.DeleteObjectViewTestCase,
  525. APIViewTestCases.GraphQLTestCase
  526. ):
  527. model = ImageAttachment
  528. brief_fields = ['display', 'id', 'image', 'name', 'url']
  529. @classmethod
  530. def setUpTestData(cls):
  531. ct = ContentType.objects.get_for_model(Site)
  532. site = Site.objects.create(name='Site 1', slug='site-1')
  533. image_attachments = (
  534. ImageAttachment(
  535. object_type=ct,
  536. object_id=site.pk,
  537. name='Image Attachment 1',
  538. image='http://example.com/image1.png',
  539. image_height=100,
  540. image_width=100
  541. ),
  542. ImageAttachment(
  543. object_type=ct,
  544. object_id=site.pk,
  545. name='Image Attachment 2',
  546. image='http://example.com/image2.png',
  547. image_height=100,
  548. image_width=100
  549. ),
  550. ImageAttachment(
  551. object_type=ct,
  552. object_id=site.pk,
  553. name='Image Attachment 3',
  554. image='http://example.com/image3.png',
  555. image_height=100,
  556. image_width=100
  557. )
  558. )
  559. ImageAttachment.objects.bulk_create(image_attachments)
  560. class JournalEntryTest(APIViewTestCases.APIViewTestCase):
  561. model = JournalEntry
  562. brief_fields = ['created', 'display', 'id', 'url']
  563. bulk_update_data = {
  564. 'comments': 'Overwritten',
  565. }
  566. @classmethod
  567. def setUpTestData(cls):
  568. user = User.objects.first()
  569. site = Site.objects.create(name='Site 1', slug='site-1')
  570. journal_entries = (
  571. JournalEntry(
  572. created_by=user,
  573. assigned_object=site,
  574. comments='Fourth entry',
  575. ),
  576. JournalEntry(
  577. created_by=user,
  578. assigned_object=site,
  579. comments='Fifth entry',
  580. ),
  581. JournalEntry(
  582. created_by=user,
  583. assigned_object=site,
  584. comments='Sixth entry',
  585. ),
  586. )
  587. JournalEntry.objects.bulk_create(journal_entries)
  588. cls.create_data = [
  589. {
  590. 'assigned_object_type': 'dcim.site',
  591. 'assigned_object_id': site.pk,
  592. 'comments': 'First entry',
  593. },
  594. {
  595. 'assigned_object_type': 'dcim.site',
  596. 'assigned_object_id': site.pk,
  597. 'comments': 'Second entry',
  598. },
  599. {
  600. 'assigned_object_type': 'dcim.site',
  601. 'assigned_object_id': site.pk,
  602. 'comments': 'Third entry',
  603. },
  604. ]
  605. class ConfigContextTest(APIViewTestCases.APIViewTestCase):
  606. model = ConfigContext
  607. brief_fields = ['description', 'display', 'id', 'name', 'url']
  608. create_data = [
  609. {
  610. 'name': 'Config Context 4',
  611. 'data': {'more_foo': True},
  612. },
  613. {
  614. 'name': 'Config Context 5',
  615. 'data': {'more_bar': False},
  616. },
  617. {
  618. 'name': 'Config Context 6',
  619. 'data': {'more_baz': None},
  620. },
  621. ]
  622. bulk_update_data = {
  623. 'description': 'New description',
  624. }
  625. @classmethod
  626. def setUpTestData(cls):
  627. config_contexts = (
  628. ConfigContext(name='Config Context 1', weight=100, data={'foo': 123}),
  629. ConfigContext(name='Config Context 2', weight=200, data={'bar': 456}),
  630. ConfigContext(name='Config Context 3', weight=300, data={'baz': 789}),
  631. )
  632. ConfigContext.objects.bulk_create(config_contexts)
  633. def test_render_configcontext_for_object(self):
  634. """
  635. Test rendering config context data for a device.
  636. """
  637. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  638. devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
  639. role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
  640. site = Site.objects.create(name='Site-1', slug='site-1')
  641. device = Device.objects.create(name='Device 1', device_type=devicetype, role=role, site=site)
  642. # Test default config contexts (created at test setup)
  643. rendered_context = device.get_config_context()
  644. self.assertEqual(rendered_context['foo'], 123)
  645. self.assertEqual(rendered_context['bar'], 456)
  646. self.assertEqual(rendered_context['baz'], 789)
  647. # Add another context specific to the site
  648. configcontext4 = ConfigContext(
  649. name='Config Context 4',
  650. data={'site_data': 'ABC'}
  651. )
  652. configcontext4.save()
  653. configcontext4.sites.add(site)
  654. rendered_context = device.get_config_context()
  655. self.assertEqual(rendered_context['site_data'], 'ABC')
  656. # Override one of the default contexts
  657. configcontext5 = ConfigContext(
  658. name='Config Context 5',
  659. weight=2000,
  660. data={'foo': 999}
  661. )
  662. configcontext5.save()
  663. configcontext5.sites.add(site)
  664. rendered_context = device.get_config_context()
  665. self.assertEqual(rendered_context['foo'], 999)
  666. # Add a context which does NOT match our device and ensure it does not apply
  667. site2 = Site.objects.create(name='Site 2', slug='site-2')
  668. configcontext6 = ConfigContext(
  669. name='Config Context 6',
  670. weight=2000,
  671. data={'bar': 999}
  672. )
  673. configcontext6.save()
  674. configcontext6.sites.add(site2)
  675. rendered_context = device.get_config_context()
  676. self.assertEqual(rendered_context['bar'], 456)
  677. class ConfigTemplateTest(APIViewTestCases.APIViewTestCase):
  678. model = ConfigTemplate
  679. brief_fields = ['description', 'display', 'id', 'name', 'url']
  680. create_data = [
  681. {
  682. 'name': 'Config Template 4',
  683. 'template_code': 'Foo: {{ foo }}',
  684. 'mime_type': 'text/plain',
  685. 'file_name': 'output4',
  686. 'file_extension': 'txt',
  687. 'as_attachment': True,
  688. },
  689. {
  690. 'name': 'Config Template 5',
  691. 'template_code': 'Bar: {{ bar }}',
  692. },
  693. {
  694. 'name': 'Config Template 6',
  695. 'template_code': 'Baz: {{ baz }}',
  696. },
  697. ]
  698. bulk_update_data = {
  699. 'description': 'New description',
  700. }
  701. @classmethod
  702. def setUpTestData(cls):
  703. config_templates = (
  704. ConfigTemplate(
  705. name='Config Template 1',
  706. template_code='Foo: {{ foo }}'
  707. ),
  708. ConfigTemplate(
  709. name='Config Template 2',
  710. template_code='Bar: {{ bar }}',
  711. ),
  712. ConfigTemplate(
  713. name='Config Template 3',
  714. template_code='Baz: {{ baz }}'
  715. ),
  716. )
  717. ConfigTemplate.objects.bulk_create(config_templates)
  718. class ScriptTest(APITestCase):
  719. class TestScriptClass(PythonClass):
  720. class Meta:
  721. name = "Test script"
  722. var1 = StringVar()
  723. var2 = IntegerVar()
  724. var3 = BooleanVar()
  725. def run(self, data, commit=True):
  726. self.log_info(data['var1'])
  727. self.log_success(data['var2'])
  728. self.log_failure(data['var3'])
  729. return 'Script complete'
  730. @classmethod
  731. def setUpTestData(cls):
  732. module = ScriptModule.objects.create(
  733. file_root=ManagedFileRootPathChoices.SCRIPTS,
  734. file_path='/var/tmp/script.py'
  735. )
  736. Script.objects.create(
  737. module=module,
  738. name="Test script",
  739. is_executable=True,
  740. )
  741. def python_class(self):
  742. return self.TestScriptClass
  743. def setUp(self):
  744. super().setUp()
  745. # Monkey-patch the Script model to return our TestScriptClass above
  746. Script.python_class = self.python_class
  747. def test_get_script(self):
  748. module = ScriptModule.objects.get(
  749. file_root=ManagedFileRootPathChoices.SCRIPTS,
  750. file_path='/var/tmp/script.py'
  751. )
  752. script = module.scripts.all().first()
  753. url = reverse('extras-api:script-detail', kwargs={'pk': script.pk})
  754. response = self.client.get(url, **self.header)
  755. self.assertEqual(response.data['name'], self.TestScriptClass.Meta.name)
  756. self.assertEqual(response.data['vars']['var1'], 'StringVar')
  757. self.assertEqual(response.data['vars']['var2'], 'IntegerVar')
  758. self.assertEqual(response.data['vars']['var3'], 'BooleanVar')
  759. class CreatedUpdatedFilterTest(APITestCase):
  760. @classmethod
  761. def setUpTestData(cls):
  762. site1 = Site.objects.create(name='Site 1', slug='site-1')
  763. location1 = Location.objects.create(site=site1, name='Location 1', slug='location-1')
  764. rackrole1 = RackRole.objects.create(name='Rack Role 1', slug='rack-role-1', color='ff0000')
  765. racks = (
  766. Rack(site=site1, location=location1, role=rackrole1, name='Rack 1', u_height=42),
  767. Rack(site=site1, location=location1, role=rackrole1, name='Rack 2', u_height=42)
  768. )
  769. Rack.objects.bulk_create(racks)
  770. # Change the created and last_updated of the second rack
  771. Rack.objects.filter(pk=racks[1].pk).update(
  772. last_updated=make_aware(datetime.datetime(2001, 2, 3, 1, 2, 3, 4)),
  773. created=make_aware(datetime.datetime(2001, 2, 3))
  774. )
  775. def test_get_rack_created(self):
  776. rack2 = Rack.objects.get(name='Rack 2')
  777. self.add_permissions('dcim.view_rack')
  778. url = reverse('dcim-api:rack-list')
  779. response = self.client.get('{}?created=2001-02-03'.format(url), **self.header)
  780. self.assertEqual(response.data['count'], 1)
  781. self.assertEqual(response.data['results'][0]['id'], rack2.pk)
  782. def test_get_rack_created_gte(self):
  783. rack1 = Rack.objects.get(name='Rack 1')
  784. self.add_permissions('dcim.view_rack')
  785. url = reverse('dcim-api:rack-list')
  786. response = self.client.get('{}?created__gte=2001-02-04'.format(url), **self.header)
  787. self.assertEqual(response.data['count'], 1)
  788. self.assertEqual(response.data['results'][0]['id'], rack1.pk)
  789. def test_get_rack_created_lte(self):
  790. rack2 = Rack.objects.get(name='Rack 2')
  791. self.add_permissions('dcim.view_rack')
  792. url = reverse('dcim-api:rack-list')
  793. response = self.client.get('{}?created__lte=2001-02-04'.format(url), **self.header)
  794. self.assertEqual(response.data['count'], 1)
  795. self.assertEqual(response.data['results'][0]['id'], rack2.pk)
  796. def test_get_rack_last_updated(self):
  797. rack2 = Rack.objects.get(name='Rack 2')
  798. self.add_permissions('dcim.view_rack')
  799. url = reverse('dcim-api:rack-list')
  800. response = self.client.get('{}?last_updated=2001-02-03%2001:02:03.000004'.format(url), **self.header)
  801. self.assertEqual(response.data['count'], 1)
  802. self.assertEqual(response.data['results'][0]['id'], rack2.pk)
  803. def test_get_rack_last_updated_gte(self):
  804. rack1 = Rack.objects.get(name='Rack 1')
  805. self.add_permissions('dcim.view_rack')
  806. url = reverse('dcim-api:rack-list')
  807. response = self.client.get('{}?last_updated__gte=2001-02-04%2001:02:03.000004'.format(url), **self.header)
  808. self.assertEqual(response.data['count'], 1)
  809. self.assertEqual(response.data['results'][0]['id'], rack1.pk)
  810. def test_get_rack_last_updated_lte(self):
  811. rack2 = Rack.objects.get(name='Rack 2')
  812. self.add_permissions('dcim.view_rack')
  813. url = reverse('dcim-api:rack-list')
  814. response = self.client.get('{}?last_updated__lte=2001-02-04%2001:02:03.000004'.format(url), **self.header)
  815. self.assertEqual(response.data['count'], 1)
  816. self.assertEqual(response.data['results'][0]['id'], rack2.pk)
  817. class ObjectTypeTest(APITestCase):
  818. def test_list_objects(self):
  819. object_type_count = ObjectType.objects.count()
  820. response = self.client.get(reverse('extras-api:objecttype-list'), **self.header)
  821. self.assertHttpStatus(response, status.HTTP_200_OK)
  822. self.assertEqual(response.data['count'], object_type_count)
  823. def test_get_object(self):
  824. object_type = ObjectType.objects.first()
  825. url = reverse('extras-api:objecttype-detail', kwargs={'pk': object_type.pk})
  826. self.assertHttpStatus(self.client.get(url, **self.header), status.HTTP_200_OK)
  827. class SubscriptionTest(APIViewTestCases.APIViewTestCase):
  828. model = Subscription
  829. brief_fields = ['display', 'id', 'object_id', 'object_type', 'url', 'user']
  830. @classmethod
  831. def setUpTestData(cls):
  832. users = (
  833. User(username='User 1'),
  834. User(username='User 2'),
  835. User(username='User 3'),
  836. User(username='User 4'),
  837. )
  838. User.objects.bulk_create(users)
  839. sites = (
  840. Site(name='Site 1', slug='site-1'),
  841. Site(name='Site 2', slug='site-2'),
  842. Site(name='Site 3', slug='site-3'),
  843. )
  844. Site.objects.bulk_create(sites)
  845. subscriptions = (
  846. Subscription(
  847. object=sites[0],
  848. user=users[0],
  849. ),
  850. Subscription(
  851. object=sites[1],
  852. user=users[1],
  853. ),
  854. Subscription(
  855. object=sites[2],
  856. user=users[2],
  857. ),
  858. )
  859. Subscription.objects.bulk_create(subscriptions)
  860. cls.create_data = [
  861. {
  862. 'object_type': 'dcim.site',
  863. 'object_id': sites[0].pk,
  864. 'user': users[3].pk,
  865. },
  866. {
  867. 'object_type': 'dcim.site',
  868. 'object_id': sites[1].pk,
  869. 'user': users[3].pk,
  870. },
  871. {
  872. 'object_type': 'dcim.site',
  873. 'object_id': sites[2].pk,
  874. 'user': users[3].pk,
  875. },
  876. ]
  877. class NotificationGroupTest(APIViewTestCases.APIViewTestCase):
  878. model = NotificationGroup
  879. brief_fields = ['description', 'display', 'id', 'name', 'url']
  880. create_data = [
  881. {
  882. 'object_types': ['dcim.site'],
  883. 'name': 'Custom Link 4',
  884. 'enabled': True,
  885. 'link_text': 'Link 4',
  886. 'link_url': 'http://example.com/?4',
  887. },
  888. {
  889. 'object_types': ['dcim.site'],
  890. 'name': 'Custom Link 5',
  891. 'enabled': True,
  892. 'link_text': 'Link 5',
  893. 'link_url': 'http://example.com/?5',
  894. },
  895. {
  896. 'object_types': ['dcim.site'],
  897. 'name': 'Custom Link 6',
  898. 'enabled': False,
  899. 'link_text': 'Link 6',
  900. 'link_url': 'http://example.com/?6',
  901. },
  902. ]
  903. bulk_update_data = {
  904. 'description': 'New description',
  905. }
  906. @classmethod
  907. def setUpTestData(cls):
  908. users = (
  909. User(username='User 1'),
  910. User(username='User 2'),
  911. User(username='User 3'),
  912. )
  913. User.objects.bulk_create(users)
  914. groups = (
  915. Group(name='Group 1'),
  916. Group(name='Group 2'),
  917. Group(name='Group 3'),
  918. )
  919. Group.objects.bulk_create(groups)
  920. notification_groups = (
  921. NotificationGroup(name='Notification Group 1'),
  922. NotificationGroup(name='Notification Group 2'),
  923. NotificationGroup(name='Notification Group 3'),
  924. )
  925. NotificationGroup.objects.bulk_create(notification_groups)
  926. for i, notification_group in enumerate(notification_groups):
  927. notification_group.users.add(users[i])
  928. notification_group.groups.add(groups[i])
  929. cls.create_data = [
  930. {
  931. 'name': 'Notification Group 4',
  932. 'description': 'Foo',
  933. 'users': [users[0].pk],
  934. 'groups': [groups[0].pk],
  935. },
  936. {
  937. 'name': 'Notification Group 5',
  938. 'description': 'Bar',
  939. 'users': [users[1].pk],
  940. 'groups': [groups[1].pk],
  941. },
  942. {
  943. 'name': 'Notification Group 6',
  944. 'description': 'Baz',
  945. 'users': [users[2].pk],
  946. 'groups': [groups[2].pk],
  947. },
  948. ]
  949. class NotificationTest(APIViewTestCases.APIViewTestCase):
  950. model = Notification
  951. brief_fields = ['display', 'event_type', 'id', 'object_id', 'object_type', 'read', 'url', 'user']
  952. @classmethod
  953. def setUpTestData(cls):
  954. users = (
  955. User(username='User 1'),
  956. User(username='User 2'),
  957. User(username='User 3'),
  958. User(username='User 4'),
  959. )
  960. User.objects.bulk_create(users)
  961. sites = (
  962. Site(name='Site 1', slug='site-1'),
  963. Site(name='Site 2', slug='site-2'),
  964. Site(name='Site 3', slug='site-3'),
  965. )
  966. Site.objects.bulk_create(sites)
  967. notifications = (
  968. Notification(
  969. object=sites[0],
  970. event_type=OBJECT_CREATED,
  971. user=users[0],
  972. ),
  973. Notification(
  974. object=sites[1],
  975. event_type=OBJECT_UPDATED,
  976. user=users[1],
  977. ),
  978. Notification(
  979. object=sites[2],
  980. event_type=OBJECT_DELETED,
  981. user=users[2],
  982. ),
  983. )
  984. Notification.objects.bulk_create(notifications)
  985. cls.create_data = [
  986. {
  987. 'object_type': 'dcim.site',
  988. 'object_id': sites[0].pk,
  989. 'user': users[3].pk,
  990. 'event_type': OBJECT_CREATED,
  991. },
  992. {
  993. 'object_type': 'dcim.site',
  994. 'object_id': sites[1].pk,
  995. 'user': users[3].pk,
  996. 'event_type': OBJECT_UPDATED,
  997. },
  998. {
  999. 'object_type': 'dcim.site',
  1000. 'object_id': sites[2].pk,
  1001. 'user': users[3].pk,
  1002. 'event_type': OBJECT_DELETED,
  1003. },
  1004. ]