test_models.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. from unittest.mock import patch, MagicMock
  2. from django.contrib.contenttypes.models import ContentType
  3. from django.core.exceptions import ObjectDoesNotExist
  4. from django.test import TestCase
  5. from core.models import DataSource, Job, ObjectType
  6. from core.choices import ObjectChangeActionChoices
  7. from dcim.models import Site, Location, Device
  8. from netbox.constants import CENSOR_TOKEN, CENSOR_TOKEN_CHANGED
  9. class DataSourceChangeLoggingTestCase(TestCase):
  10. def test_password_added_on_create(self):
  11. datasource = DataSource.objects.create(
  12. name='Data Source 1',
  13. type='git',
  14. source_url='http://localhost/',
  15. parameters={
  16. 'username': 'jeff',
  17. 'password': 'foobar123',
  18. }
  19. )
  20. objectchange = datasource.to_objectchange(ObjectChangeActionChoices.ACTION_CREATE)
  21. self.assertIsNone(objectchange.prechange_data)
  22. self.assertEqual(objectchange.postchange_data['parameters']['username'], 'jeff')
  23. self.assertEqual(objectchange.postchange_data['parameters']['password'], CENSOR_TOKEN_CHANGED)
  24. def test_password_added_on_update(self):
  25. datasource = DataSource.objects.create(
  26. name='Data Source 1',
  27. type='git',
  28. source_url='http://localhost/'
  29. )
  30. datasource.snapshot()
  31. # Add a blank password
  32. datasource.parameters = {
  33. 'username': 'jeff',
  34. 'password': '',
  35. }
  36. objectchange = datasource.to_objectchange(ObjectChangeActionChoices.ACTION_UPDATE)
  37. self.assertIsNone(objectchange.prechange_data['parameters'])
  38. self.assertEqual(objectchange.postchange_data['parameters']['username'], 'jeff')
  39. self.assertEqual(objectchange.postchange_data['parameters']['password'], '')
  40. # Add a password
  41. datasource.parameters = {
  42. 'username': 'jeff',
  43. 'password': 'foobar123',
  44. }
  45. objectchange = datasource.to_objectchange(ObjectChangeActionChoices.ACTION_UPDATE)
  46. self.assertEqual(objectchange.postchange_data['parameters']['username'], 'jeff')
  47. self.assertEqual(objectchange.postchange_data['parameters']['password'], CENSOR_TOKEN_CHANGED)
  48. def test_password_changed(self):
  49. datasource = DataSource.objects.create(
  50. name='Data Source 1',
  51. type='git',
  52. source_url='http://localhost/',
  53. parameters={
  54. 'username': 'jeff',
  55. 'password': 'password1',
  56. }
  57. )
  58. datasource.snapshot()
  59. # Change the password
  60. datasource.parameters['password'] = 'password2'
  61. objectchange = datasource.to_objectchange(ObjectChangeActionChoices.ACTION_UPDATE)
  62. self.assertEqual(objectchange.prechange_data['parameters']['username'], 'jeff')
  63. self.assertEqual(objectchange.prechange_data['parameters']['password'], CENSOR_TOKEN)
  64. self.assertEqual(objectchange.postchange_data['parameters']['username'], 'jeff')
  65. self.assertEqual(objectchange.postchange_data['parameters']['password'], CENSOR_TOKEN_CHANGED)
  66. def test_password_removed_on_update(self):
  67. datasource = DataSource.objects.create(
  68. name='Data Source 1',
  69. type='git',
  70. source_url='http://localhost/',
  71. parameters={
  72. 'username': 'jeff',
  73. 'password': 'foobar123',
  74. }
  75. )
  76. datasource.snapshot()
  77. objectchange = datasource.to_objectchange(ObjectChangeActionChoices.ACTION_UPDATE)
  78. self.assertEqual(objectchange.prechange_data['parameters']['username'], 'jeff')
  79. self.assertEqual(objectchange.prechange_data['parameters']['password'], CENSOR_TOKEN)
  80. self.assertEqual(objectchange.postchange_data['parameters']['username'], 'jeff')
  81. self.assertEqual(objectchange.postchange_data['parameters']['password'], CENSOR_TOKEN)
  82. # Remove the password
  83. datasource.parameters['password'] = ''
  84. objectchange = datasource.to_objectchange(ObjectChangeActionChoices.ACTION_UPDATE)
  85. self.assertEqual(objectchange.prechange_data['parameters']['username'], 'jeff')
  86. self.assertEqual(objectchange.prechange_data['parameters']['password'], CENSOR_TOKEN)
  87. self.assertEqual(objectchange.postchange_data['parameters']['username'], 'jeff')
  88. self.assertEqual(objectchange.postchange_data['parameters']['password'], '')
  89. def test_password_not_modified(self):
  90. datasource = DataSource.objects.create(
  91. name='Data Source 1',
  92. type='git',
  93. source_url='http://localhost/',
  94. parameters={
  95. 'username': 'username1',
  96. 'password': 'foobar123',
  97. }
  98. )
  99. datasource.snapshot()
  100. # Remove the password
  101. datasource.parameters['username'] = 'username2'
  102. objectchange = datasource.to_objectchange(ObjectChangeActionChoices.ACTION_UPDATE)
  103. self.assertEqual(objectchange.prechange_data['parameters']['username'], 'username1')
  104. self.assertEqual(objectchange.prechange_data['parameters']['password'], CENSOR_TOKEN)
  105. self.assertEqual(objectchange.postchange_data['parameters']['username'], 'username2')
  106. self.assertEqual(objectchange.postchange_data['parameters']['password'], CENSOR_TOKEN)
  107. class ObjectTypeTest(TestCase):
  108. def test_create(self):
  109. """
  110. Test that an ObjectType created for a given app_label & model name will be automatically assigned to
  111. the appropriate ContentType.
  112. """
  113. kwargs = {
  114. 'app_label': 'foo',
  115. 'model': 'bar',
  116. }
  117. ct = ContentType.objects.create(**kwargs)
  118. ot = ObjectType.objects.create(**kwargs)
  119. self.assertEqual(ot.contenttype_ptr, ct)
  120. def test_get_by_natural_key(self):
  121. """
  122. Test that get_by_natural_key() returns the appropriate ObjectType.
  123. """
  124. self.assertEqual(
  125. ObjectType.objects.get_by_natural_key('dcim', 'site'),
  126. ObjectType.objects.get(app_label='dcim', model='site')
  127. )
  128. with self.assertRaises(ObjectDoesNotExist):
  129. ObjectType.objects.get_by_natural_key('foo', 'bar')
  130. def test_get_for_id(self):
  131. """
  132. Test that get_by_id() returns the appropriate ObjectType.
  133. """
  134. ot = ObjectType.objects.get_by_natural_key('dcim', 'site')
  135. self.assertEqual(
  136. ObjectType.objects.get_for_id(ot.pk),
  137. ObjectType.objects.get(pk=ot.pk)
  138. )
  139. with self.assertRaises(ObjectDoesNotExist):
  140. ObjectType.objects.get_for_id(0)
  141. def test_get_for_model(self):
  142. """
  143. Test that get_by_model() returns the appropriate ObjectType.
  144. """
  145. self.assertEqual(
  146. ObjectType.objects.get_for_model(Site),
  147. ObjectType.objects.get_by_natural_key('dcim', 'site')
  148. )
  149. def test_get_for_models(self):
  150. """
  151. Test that get_by_models() returns the appropriate ObjectType mapping.
  152. """
  153. self.assertEqual(
  154. ObjectType.objects.get_for_models(Site, Location, Device),
  155. {
  156. Site: ObjectType.objects.get_by_natural_key('dcim', 'site'),
  157. Location: ObjectType.objects.get_by_natural_key('dcim', 'location'),
  158. Device: ObjectType.objects.get_by_natural_key('dcim', 'device'),
  159. }
  160. )
  161. def test_public(self):
  162. """
  163. Test that public() returns only ObjectTypes for public models.
  164. """
  165. public_ots = ObjectType.objects.public()
  166. self.assertIn(ObjectType.objects.get_by_natural_key('dcim', 'site'), public_ots)
  167. self.assertNotIn(ObjectType.objects.get_by_natural_key('extras', 'taggeditem'), public_ots)
  168. def test_with_feature(self):
  169. """
  170. Test that with_feature() returns only ObjectTypes for models which support the specified feature.
  171. """
  172. bookmarks_ots = ObjectType.objects.with_feature('bookmarks')
  173. self.assertIn(ObjectType.objects.get_by_natural_key('dcim', 'site'), bookmarks_ots)
  174. self.assertNotIn(ObjectType.objects.get_by_natural_key('dcim', 'cabletermination'), bookmarks_ots)
  175. class JobTest(TestCase):
  176. @patch('core.models.jobs.django_rq.get_queue')
  177. def test_delete_cancels_job_from_correct_queue(self, mock_get_queue):
  178. """
  179. Test that when a job is deleted, it's canceled from the correct queue.
  180. """
  181. mock_queue = MagicMock()
  182. mock_rq_job = MagicMock()
  183. mock_queue.fetch_job.return_value = mock_rq_job
  184. mock_get_queue.return_value = mock_queue
  185. def dummy_func(**kwargs):
  186. pass
  187. # Enqueue a job with a custom queue name
  188. custom_queue = 'my_custom_queue'
  189. job = Job.enqueue(
  190. func=dummy_func,
  191. name='Test Job',
  192. queue_name=custom_queue
  193. )
  194. # Reset mock to clear enqueue call
  195. mock_get_queue.reset_mock()
  196. # Delete the job
  197. job.delete()
  198. # Verify the correct queue was used for cancellation
  199. mock_get_queue.assert_called_with(custom_queue)
  200. mock_queue.fetch_job.assert_called_with(str(job.job_id))
  201. mock_rq_job.cancel.assert_called_once()