test_views.py 19 KB

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