test_views.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. import json
  2. import urllib.parse
  3. import uuid
  4. from django.contrib.auth import get_user_model
  5. from django.contrib.contenttypes.models import ContentType
  6. from django.urls import reverse
  7. from dcim.models import DeviceType, Manufacturer, Site
  8. from extras.choices import *
  9. from extras.models import *
  10. from utilities.testing import ViewTestCases, TestCase
  11. User = get_user_model()
  12. class CustomFieldTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  13. model = CustomField
  14. @classmethod
  15. def setUpTestData(cls):
  16. site_ct = ContentType.objects.get_for_model(Site)
  17. CustomFieldChoiceSet.objects.create(
  18. name='Choice Set 1',
  19. extra_choices=(
  20. ('A', 'A'),
  21. ('B', 'B'),
  22. ('C', 'C'),
  23. )
  24. )
  25. custom_fields = (
  26. CustomField(name='field1', label='Field 1', type=CustomFieldTypeChoices.TYPE_TEXT),
  27. CustomField(name='field2', label='Field 2', type=CustomFieldTypeChoices.TYPE_TEXT),
  28. CustomField(name='field3', label='Field 3', type=CustomFieldTypeChoices.TYPE_TEXT),
  29. )
  30. for customfield in custom_fields:
  31. customfield.save()
  32. customfield.content_types.add(site_ct)
  33. cls.form_data = {
  34. 'name': 'field_x',
  35. 'label': 'Field X',
  36. 'type': 'text',
  37. 'content_types': [site_ct.pk],
  38. 'search_weight': 2000,
  39. 'filter_logic': CustomFieldFilterLogicChoices.FILTER_EXACT,
  40. 'default': None,
  41. 'weight': 200,
  42. 'required': True,
  43. 'ui_visible': CustomFieldUIVisibleChoices.ALWAYS,
  44. 'ui_editable': CustomFieldUIEditableChoices.YES,
  45. }
  46. cls.csv_data = (
  47. 'name,label,type,content_types,object_type,weight,search_weight,filter_logic,choice_set,validation_minimum,validation_maximum,validation_regex,ui_visible,ui_editable',
  48. 'field4,Field 4,text,dcim.site,,100,1000,exact,,,,[a-z]{3},always,yes',
  49. 'field5,Field 5,integer,dcim.site,,100,2000,exact,,1,100,,always,yes',
  50. 'field6,Field 6,select,dcim.site,,100,3000,exact,Choice Set 1,,,,always,yes',
  51. 'field7,Field 7,object,dcim.site,dcim.region,100,4000,exact,,,,,always,yes',
  52. )
  53. cls.csv_update_data = (
  54. 'id,label',
  55. f'{custom_fields[0].pk},New label 1',
  56. f'{custom_fields[1].pk},New label 2',
  57. f'{custom_fields[2].pk},New label 3',
  58. )
  59. cls.bulk_edit_data = {
  60. 'required': True,
  61. 'weight': 200,
  62. }
  63. class CustomFieldChoiceSetTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  64. model = CustomFieldChoiceSet
  65. @classmethod
  66. def setUpTestData(cls):
  67. choice_sets = (
  68. CustomFieldChoiceSet(
  69. name='Choice Set 1',
  70. extra_choices=(('A1', 'Choice 1'), ('A2', 'Choice 2'), ('A3', 'Choice 3'))
  71. ),
  72. CustomFieldChoiceSet(
  73. name='Choice Set 2',
  74. extra_choices=(('B1', 'Choice 1'), ('B2', 'Choice 2'), ('B3', 'Choice 3'))
  75. ),
  76. CustomFieldChoiceSet(
  77. name='Choice Set 3',
  78. extra_choices=(('C1', 'Choice 1'), ('C2', 'Choice 2'), ('C3', 'Choice 3'))
  79. ),
  80. )
  81. CustomFieldChoiceSet.objects.bulk_create(choice_sets)
  82. cls.form_data = {
  83. 'name': 'Choice Set X',
  84. 'extra_choices': '\n'.join(['X1,Choice 1', 'X2,Choice 2', 'X3,Choice 3'])
  85. }
  86. cls.csv_data = (
  87. 'name,extra_choices',
  88. 'Choice Set 4,"D1,D2,D3"',
  89. 'Choice Set 5,"E1,E2,E3"',
  90. 'Choice Set 6,"F1,F2,F3"',
  91. )
  92. cls.csv_update_data = (
  93. 'id,extra_choices',
  94. f'{choice_sets[0].pk},"A,B,C"',
  95. f'{choice_sets[1].pk},"A,B,C"',
  96. f'{choice_sets[2].pk},"A,B,C"',
  97. )
  98. cls.bulk_edit_data = {
  99. 'description': 'New description',
  100. }
  101. class CustomLinkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  102. model = CustomLink
  103. @classmethod
  104. def setUpTestData(cls):
  105. site_ct = ContentType.objects.get_for_model(Site)
  106. custom_links = (
  107. CustomLink(name='Custom Link 1', enabled=True, link_text='Link 1', link_url='http://example.com/?1'),
  108. CustomLink(name='Custom Link 2', enabled=True, link_text='Link 2', link_url='http://example.com/?2'),
  109. CustomLink(name='Custom Link 3', enabled=False, link_text='Link 3', link_url='http://example.com/?3'),
  110. )
  111. CustomLink.objects.bulk_create(custom_links)
  112. for i, custom_link in enumerate(custom_links):
  113. custom_link.content_types.set([site_ct])
  114. cls.form_data = {
  115. 'name': 'Custom Link X',
  116. 'content_types': [site_ct.pk],
  117. 'enabled': False,
  118. 'weight': 100,
  119. 'button_class': CustomLinkButtonClassChoices.DEFAULT,
  120. 'link_text': 'Link X',
  121. 'link_url': 'http://example.com/?x'
  122. }
  123. cls.csv_data = (
  124. "name,content_types,enabled,weight,button_class,link_text,link_url",
  125. "Custom Link 4,dcim.site,True,100,blue,Link 4,http://exmaple.com/?4",
  126. "Custom Link 5,dcim.site,True,100,blue,Link 5,http://exmaple.com/?5",
  127. "Custom Link 6,dcim.site,False,100,blue,Link 6,http://exmaple.com/?6",
  128. )
  129. cls.csv_update_data = (
  130. "id,name",
  131. f"{custom_links[0].pk},Custom Link 7",
  132. f"{custom_links[1].pk},Custom Link 8",
  133. f"{custom_links[2].pk},Custom Link 9",
  134. )
  135. cls.bulk_edit_data = {
  136. 'button_class': CustomLinkButtonClassChoices.CYAN,
  137. 'enabled': False,
  138. 'weight': 200,
  139. }
  140. class SavedFilterTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  141. model = SavedFilter
  142. @classmethod
  143. def setUpTestData(cls):
  144. site_ct = ContentType.objects.get_for_model(Site)
  145. users = (
  146. User(username='User 1'),
  147. User(username='User 2'),
  148. User(username='User 3'),
  149. )
  150. User.objects.bulk_create(users)
  151. saved_filters = (
  152. SavedFilter(
  153. name='Saved Filter 1',
  154. slug='saved-filter-1',
  155. user=users[0],
  156. weight=100,
  157. parameters={'status': ['active']}
  158. ),
  159. SavedFilter(
  160. name='Saved Filter 2',
  161. slug='saved-filter-2',
  162. user=users[1],
  163. weight=200,
  164. parameters={'status': ['planned']}
  165. ),
  166. SavedFilter(
  167. name='Saved Filter 3',
  168. slug='saved-filter-3',
  169. user=users[2],
  170. weight=300,
  171. parameters={'status': ['retired']}
  172. ),
  173. )
  174. SavedFilter.objects.bulk_create(saved_filters)
  175. for i, savedfilter in enumerate(saved_filters):
  176. savedfilter.content_types.set([site_ct])
  177. cls.form_data = {
  178. 'name': 'Saved Filter X',
  179. 'slug': 'saved-filter-x',
  180. 'content_types': [site_ct.pk],
  181. 'description': 'Foo',
  182. 'weight': 1000,
  183. 'enabled': True,
  184. 'shared': True,
  185. 'parameters': '{"foo": 123}',
  186. }
  187. cls.csv_data = (
  188. 'name,slug,content_types,weight,enabled,shared,parameters',
  189. 'Saved Filter 4,saved-filter-4,dcim.device,400,True,True,{"foo": "a"}',
  190. 'Saved Filter 5,saved-filter-5,dcim.device,500,True,True,{"foo": "b"}',
  191. 'Saved Filter 6,saved-filter-6,dcim.device,600,True,True,{"foo": "c"}',
  192. )
  193. cls.csv_update_data = (
  194. "id,name",
  195. f"{saved_filters[0].pk},Saved Filter 7",
  196. f"{saved_filters[1].pk},Saved Filter 8",
  197. f"{saved_filters[2].pk},Saved Filter 9",
  198. )
  199. cls.bulk_edit_data = {
  200. 'weight': 999,
  201. }
  202. class BookmarkTestCase(
  203. ViewTestCases.DeleteObjectViewTestCase,
  204. ViewTestCases.ListObjectsViewTestCase,
  205. ViewTestCases.BulkDeleteObjectsViewTestCase
  206. ):
  207. model = Bookmark
  208. @classmethod
  209. def setUpTestData(cls):
  210. site_ct = ContentType.objects.get_for_model(Site)
  211. sites = (
  212. Site(name='Site 1', slug='site-1'),
  213. Site(name='Site 2', slug='site-2'),
  214. Site(name='Site 3', slug='site-3'),
  215. Site(name='Site 4', slug='site-4'),
  216. )
  217. Site.objects.bulk_create(sites)
  218. cls.form_data = {
  219. 'object_type': site_ct.pk,
  220. 'object_id': sites[3].pk,
  221. }
  222. def setUp(self):
  223. super().setUp()
  224. sites = Site.objects.all()
  225. user = self.user
  226. bookmarks = (
  227. Bookmark(object=sites[0], user=user),
  228. Bookmark(object=sites[1], user=user),
  229. Bookmark(object=sites[2], user=user),
  230. )
  231. Bookmark.objects.bulk_create(bookmarks)
  232. def _get_url(self, action, instance=None):
  233. if action == 'list':
  234. return reverse('account:bookmarks')
  235. return super()._get_url(action, instance)
  236. def test_list_objects_anonymous(self):
  237. return
  238. def test_list_objects_with_constrained_permission(self):
  239. return
  240. class ExportTemplateTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  241. model = ExportTemplate
  242. @classmethod
  243. def setUpTestData(cls):
  244. site_ct = ContentType.objects.get_for_model(Site)
  245. TEMPLATE_CODE = """{% for object in queryset %}{{ object }}{% endfor %}"""
  246. export_templates = (
  247. ExportTemplate(name='Export Template 1', template_code=TEMPLATE_CODE),
  248. ExportTemplate(name='Export Template 2', template_code=TEMPLATE_CODE),
  249. ExportTemplate(name='Export Template 3', template_code=TEMPLATE_CODE),
  250. )
  251. ExportTemplate.objects.bulk_create(export_templates)
  252. for et in export_templates:
  253. et.content_types.set([site_ct])
  254. cls.form_data = {
  255. 'name': 'Export Template X',
  256. 'content_types': [site_ct.pk],
  257. 'template_code': TEMPLATE_CODE,
  258. }
  259. cls.csv_data = (
  260. "name,content_types,template_code",
  261. f"Export Template 4,dcim.site,{TEMPLATE_CODE}",
  262. f"Export Template 5,dcim.site,{TEMPLATE_CODE}",
  263. f"Export Template 6,dcim.site,{TEMPLATE_CODE}",
  264. )
  265. cls.csv_update_data = (
  266. "id,name",
  267. f"{export_templates[0].pk},Export Template 7",
  268. f"{export_templates[1].pk},Export Template 8",
  269. f"{export_templates[2].pk},Export Template 9",
  270. )
  271. cls.bulk_edit_data = {
  272. 'mime_type': 'text/html',
  273. 'file_extension': 'html',
  274. 'as_attachment': True,
  275. }
  276. class WebhookTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  277. model = Webhook
  278. @classmethod
  279. def setUpTestData(cls):
  280. site_ct = ContentType.objects.get_for_model(Site)
  281. webhooks = (
  282. Webhook(name='Webhook 1', payload_url='http://example.com/?1', type_create=True, http_method='POST'),
  283. Webhook(name='Webhook 2', payload_url='http://example.com/?2', type_create=True, http_method='POST'),
  284. Webhook(name='Webhook 3', payload_url='http://example.com/?3', type_create=True, http_method='POST'),
  285. )
  286. for webhook in webhooks:
  287. webhook.save()
  288. webhook.content_types.add(site_ct)
  289. cls.form_data = {
  290. 'name': 'Webhook X',
  291. 'content_types': [site_ct.pk],
  292. 'type_create': False,
  293. 'type_update': True,
  294. 'type_delete': True,
  295. 'payload_url': 'http://example.com/?x',
  296. 'http_method': 'GET',
  297. 'http_content_type': 'application/foo',
  298. 'conditions': None,
  299. }
  300. cls.csv_data = (
  301. "name,content_types,type_create,payload_url,http_method,http_content_type",
  302. "Webhook 4,dcim.site,True,http://example.com/?4,GET,application/json",
  303. "Webhook 5,dcim.site,True,http://example.com/?5,GET,application/json",
  304. "Webhook 6,dcim.site,True,http://example.com/?6,GET,application/json",
  305. )
  306. cls.csv_update_data = (
  307. "id,name",
  308. f"{webhooks[0].pk},Webhook 7",
  309. f"{webhooks[1].pk},Webhook 8",
  310. f"{webhooks[2].pk},Webhook 9",
  311. )
  312. cls.bulk_edit_data = {
  313. 'enabled': False,
  314. 'type_create': False,
  315. 'type_update': True,
  316. 'type_delete': True,
  317. 'http_method': 'GET',
  318. }
  319. class TagTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
  320. model = Tag
  321. @classmethod
  322. def setUpTestData(cls):
  323. tags = (
  324. Tag(name='Tag 1', slug='tag-1'),
  325. Tag(name='Tag 2', slug='tag-2'),
  326. Tag(name='Tag 3', slug='tag-3'),
  327. )
  328. Tag.objects.bulk_create(tags)
  329. cls.form_data = {
  330. 'name': 'Tag X',
  331. 'slug': 'tag-x',
  332. 'color': 'c0c0c0',
  333. 'comments': 'Some comments',
  334. }
  335. cls.csv_data = (
  336. "name,slug,color,description",
  337. "Tag 4,tag-4,ff0000,Fourth tag",
  338. "Tag 5,tag-5,00ff00,Fifth tag",
  339. "Tag 6,tag-6,0000ff,Sixth tag",
  340. )
  341. cls.csv_update_data = (
  342. "id,name,description",
  343. f"{tags[0].pk},Tag 7,Fourth tag7",
  344. f"{tags[1].pk},Tag 8,Fifth tag8",
  345. f"{tags[2].pk},Tag 9,Sixth tag9",
  346. )
  347. cls.bulk_edit_data = {
  348. 'color': '00ff00',
  349. }
  350. # TODO: Change base class to PrimaryObjectViewTestCase
  351. # Blocked by absence of standard create/edit, bulk create views
  352. class ConfigContextTestCase(
  353. ViewTestCases.GetObjectViewTestCase,
  354. ViewTestCases.GetObjectChangelogViewTestCase,
  355. ViewTestCases.DeleteObjectViewTestCase,
  356. ViewTestCases.ListObjectsViewTestCase,
  357. ViewTestCases.BulkEditObjectsViewTestCase,
  358. ViewTestCases.BulkDeleteObjectsViewTestCase
  359. ):
  360. model = ConfigContext
  361. @classmethod
  362. def setUpTestData(cls):
  363. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  364. devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
  365. # Create three ConfigContexts
  366. for i in range(1, 4):
  367. configcontext = ConfigContext(
  368. name='Config Context {}'.format(i),
  369. data={'foo': i}
  370. )
  371. configcontext.save()
  372. configcontext.device_types.add(devicetype)
  373. cls.form_data = {
  374. 'name': 'Config Context X',
  375. 'weight': 200,
  376. 'description': 'A new config context',
  377. 'is_active': True,
  378. 'regions': [],
  379. 'sites': [],
  380. 'roles': [],
  381. 'platforms': [],
  382. 'tenant_groups': [],
  383. 'tenants': [],
  384. 'device_types': [devicetype.id],
  385. 'tags': [],
  386. 'data': '{"foo": 123}',
  387. }
  388. cls.bulk_edit_data = {
  389. 'weight': 300,
  390. 'is_active': False,
  391. 'description': 'New description',
  392. }
  393. class ConfigTemplateTestCase(
  394. ViewTestCases.GetObjectViewTestCase,
  395. ViewTestCases.GetObjectChangelogViewTestCase,
  396. ViewTestCases.DeleteObjectViewTestCase,
  397. ViewTestCases.ListObjectsViewTestCase,
  398. ViewTestCases.BulkEditObjectsViewTestCase,
  399. ViewTestCases.BulkDeleteObjectsViewTestCase
  400. ):
  401. model = ConfigTemplate
  402. @classmethod
  403. def setUpTestData(cls):
  404. TEMPLATE_CODE = """Foo: {{ foo }}"""
  405. config_templates = (
  406. ConfigTemplate(name='Config Template 1', template_code=TEMPLATE_CODE),
  407. ConfigTemplate(name='Config Template 2', template_code=TEMPLATE_CODE),
  408. ConfigTemplate(name='Config Template 3', template_code=TEMPLATE_CODE),
  409. )
  410. ConfigTemplate.objects.bulk_create(config_templates)
  411. cls.form_data = {
  412. 'name': 'Config Template X',
  413. 'description': 'Config template',
  414. 'template_code': TEMPLATE_CODE,
  415. }
  416. cls.csv_update_data = (
  417. "id,name",
  418. f"{config_templates[0].pk},Config Template 7",
  419. f"{config_templates[1].pk},Config Template 8",
  420. f"{config_templates[2].pk},Config Template 9",
  421. )
  422. cls.bulk_edit_data = {
  423. 'description': 'New description',
  424. }
  425. # TODO: Convert to StandardTestCases.Views
  426. class ObjectChangeTestCase(TestCase):
  427. user_permissions = (
  428. 'extras.view_objectchange',
  429. )
  430. @classmethod
  431. def setUpTestData(cls):
  432. site = Site(name='Site 1', slug='site-1')
  433. site.save()
  434. # Create three ObjectChanges
  435. user = User.objects.create_user(username='testuser2')
  436. for i in range(1, 4):
  437. oc = site.to_objectchange(action=ObjectChangeActionChoices.ACTION_UPDATE)
  438. oc.user = user
  439. oc.request_id = uuid.uuid4()
  440. oc.save()
  441. def test_objectchange_list(self):
  442. url = reverse('extras:objectchange_list')
  443. params = {
  444. "user": User.objects.first().pk,
  445. }
  446. response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
  447. self.assertHttpStatus(response, 200)
  448. def test_objectchange(self):
  449. objectchange = ObjectChange.objects.first()
  450. response = self.client.get(objectchange.get_absolute_url())
  451. self.assertHttpStatus(response, 200)
  452. class JournalEntryTestCase(
  453. # ViewTestCases.GetObjectViewTestCase,
  454. ViewTestCases.CreateObjectViewTestCase,
  455. ViewTestCases.EditObjectViewTestCase,
  456. ViewTestCases.DeleteObjectViewTestCase,
  457. ViewTestCases.ListObjectsViewTestCase,
  458. ViewTestCases.BulkEditObjectsViewTestCase,
  459. ViewTestCases.BulkDeleteObjectsViewTestCase
  460. ):
  461. model = JournalEntry
  462. @classmethod
  463. def setUpTestData(cls):
  464. site_ct = ContentType.objects.get_for_model(Site)
  465. site = Site.objects.create(name='Site 1', slug='site-1')
  466. user = User.objects.create(username='User 1')
  467. JournalEntry.objects.bulk_create((
  468. JournalEntry(assigned_object=site, created_by=user, comments='First entry'),
  469. JournalEntry(assigned_object=site, created_by=user, comments='Second entry'),
  470. JournalEntry(assigned_object=site, created_by=user, comments='Third entry'),
  471. ))
  472. cls.form_data = {
  473. 'assigned_object_type': site_ct.pk,
  474. 'assigned_object_id': site.pk,
  475. 'kind': 'info',
  476. 'comments': 'A new entry',
  477. }
  478. cls.bulk_edit_data = {
  479. 'kind': 'success',
  480. 'comments': 'Overwritten',
  481. }
  482. class CustomLinkTest(TestCase):
  483. user_permissions = ['dcim.view_site']
  484. def test_view_object_with_custom_link(self):
  485. customlink = CustomLink(
  486. name='Test',
  487. link_text='FOO {{ object.name }} BAR',
  488. link_url='http://example.com/?site={{ object.slug }}',
  489. new_window=False
  490. )
  491. customlink.save()
  492. customlink.content_types.set([ContentType.objects.get_for_model(Site)])
  493. site = Site(name='Test Site', slug='test-site')
  494. site.save()
  495. response = self.client.get(site.get_absolute_url(), follow=True)
  496. self.assertEqual(response.status_code, 200)
  497. self.assertIn(f'FOO {site.name} BAR', str(response.content))