test_django.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. from __future__ import absolute_import, unicode_literals
  2. import pytest
  3. from contextlib import contextmanager
  4. from case import Mock, mock, patch
  5. from celery.fixups.django import (
  6. _maybe_close_fd,
  7. fixup,
  8. FixupWarning,
  9. DjangoFixup,
  10. DjangoWorkerFixup,
  11. )
  12. class FixupCase:
  13. Fixup = None
  14. @contextmanager
  15. def fixup_context(self, app):
  16. with patch('celery.fixups.django.DjangoWorkerFixup.validate_models'):
  17. with patch('celery.fixups.django.symbol_by_name') as symbyname:
  18. with patch('celery.fixups.django.import_module') as impmod:
  19. f = self.Fixup(app)
  20. yield f, impmod, symbyname
  21. class test_DjangoFixup(FixupCase):
  22. Fixup = DjangoFixup
  23. def test_setting_default_app(self):
  24. from celery import _state
  25. prev, _state.default_app = _state.default_app, None
  26. try:
  27. app = Mock(name='app')
  28. DjangoFixup(app)
  29. app.set_default.assert_called_with()
  30. finally:
  31. _state.default_app = prev
  32. @patch('celery.fixups.django.DjangoWorkerFixup')
  33. def test_worker_fixup_property(self, DjangoWorkerFixup):
  34. f = DjangoFixup(self.app)
  35. f._worker_fixup = None
  36. assert f.worker_fixup is DjangoWorkerFixup()
  37. assert f.worker_fixup is DjangoWorkerFixup()
  38. def test_on_import_modules(self):
  39. f = DjangoFixup(self.app)
  40. f.worker_fixup = Mock(name='worker_fixup')
  41. f.on_import_modules()
  42. f.worker_fixup.validate_models.assert_called_with()
  43. def test_autodiscover_tasks(self, patching):
  44. patching.modules('django.apps')
  45. from django.apps import apps
  46. f = DjangoFixup(self.app)
  47. configs = [Mock(name='c1'), Mock(name='c2')]
  48. apps.get_app_configs.return_value = configs
  49. assert f.autodiscover_tasks() == [c.name for c in configs]
  50. def test_fixup(self, patching):
  51. with patch('celery.fixups.django.DjangoFixup') as Fixup:
  52. patching.setenv('DJANGO_SETTINGS_MODULE', '')
  53. fixup(self.app)
  54. Fixup.assert_not_called()
  55. patching.setenv('DJANGO_SETTINGS_MODULE', 'settings')
  56. with mock.mask_modules('django'):
  57. with pytest.warns(FixupWarning):
  58. fixup(self.app)
  59. Fixup.assert_not_called()
  60. with mock.module_exists('django'):
  61. import django
  62. django.VERSION = (1, 10, 1)
  63. fixup(self.app)
  64. Fixup.assert_called()
  65. def test_maybe_close_fd(self):
  66. with patch('os.close'):
  67. _maybe_close_fd(Mock())
  68. _maybe_close_fd(object())
  69. def test_init(self):
  70. with self.fixup_context(self.app) as (f, importmod, sym):
  71. assert f
  72. def test_install(self, patching):
  73. self.app.loader = Mock()
  74. self.cw = patching('os.getcwd')
  75. self.p = patching('sys.path')
  76. self.sigs = patching('celery.fixups.django.signals')
  77. with self.fixup_context(self.app) as (f, _, _):
  78. self.cw.return_value = '/opt/vandelay'
  79. f.install()
  80. self.sigs.worker_init.connect.assert_called_with(f.on_worker_init)
  81. assert self.app.loader.now == f.now
  82. self.p.append.assert_called_with('/opt/vandelay')
  83. def test_now(self):
  84. with self.fixup_context(self.app) as (f, _, _):
  85. assert f.now(utc=True)
  86. f._now.assert_not_called()
  87. assert f.now(utc=False)
  88. f._now.assert_called()
  89. def test_on_worker_init(self):
  90. with self.fixup_context(self.app) as (f, _, _):
  91. with patch('celery.fixups.django.DjangoWorkerFixup') as DWF:
  92. f.on_worker_init()
  93. DWF.assert_called_with(f.app)
  94. DWF.return_value.install.assert_called_with()
  95. assert f._worker_fixup is DWF.return_value
  96. class test_DjangoWorkerFixup(FixupCase):
  97. Fixup = DjangoWorkerFixup
  98. def test_init(self):
  99. with self.fixup_context(self.app) as (f, importmod, sym):
  100. assert f
  101. def test_install(self):
  102. self.app.conf = {'CELERY_DB_REUSE_MAX': None}
  103. self.app.loader = Mock()
  104. with self.fixup_context(self.app) as (f, _, _):
  105. with patch('celery.fixups.django.signals') as sigs:
  106. f.install()
  107. sigs.beat_embedded_init.connect.assert_called_with(
  108. f.close_database,
  109. )
  110. sigs.worker_ready.connect.assert_called_with(f.on_worker_ready)
  111. sigs.task_prerun.connect.assert_called_with(f.on_task_prerun)
  112. sigs.task_postrun.connect.assert_called_with(f.on_task_postrun)
  113. sigs.worker_process_init.connect.assert_called_with(
  114. f.on_worker_process_init,
  115. )
  116. def test_on_worker_process_init(self, patching):
  117. with self.fixup_context(self.app) as (f, _, _):
  118. with patch('celery.fixups.django._maybe_close_fd') as mcf:
  119. _all = f._db.connections.all = Mock()
  120. conns = _all.return_value = [
  121. Mock(), Mock(),
  122. ]
  123. conns[0].connection = None
  124. with patch.object(f, 'close_cache'):
  125. with patch.object(f, '_close_database'):
  126. f.on_worker_process_init()
  127. mcf.assert_called_with(conns[1].connection)
  128. f.close_cache.assert_called_with()
  129. f._close_database.assert_called_with()
  130. f.validate_models = Mock(name='validate_models')
  131. patching.setenv('FORKED_BY_MULTIPROCESSING', '1')
  132. f.on_worker_process_init()
  133. f.validate_models.assert_called_with()
  134. def test_on_task_prerun(self):
  135. task = Mock()
  136. with self.fixup_context(self.app) as (f, _, _):
  137. task.request.is_eager = False
  138. with patch.object(f, 'close_database'):
  139. f.on_task_prerun(task)
  140. f.close_database.assert_called_with()
  141. task.request.is_eager = True
  142. with patch.object(f, 'close_database'):
  143. f.on_task_prerun(task)
  144. f.close_database.assert_not_called()
  145. def test_on_task_postrun(self):
  146. task = Mock()
  147. with self.fixup_context(self.app) as (f, _, _):
  148. with patch.object(f, 'close_cache'):
  149. task.request.is_eager = False
  150. with patch.object(f, 'close_database'):
  151. f.on_task_postrun(task)
  152. f.close_database.assert_called()
  153. f.close_cache.assert_called()
  154. # when a task is eager, don't close connections
  155. with patch.object(f, 'close_cache'):
  156. task.request.is_eager = True
  157. with patch.object(f, 'close_database'):
  158. f.on_task_postrun(task)
  159. f.close_database.assert_not_called()
  160. f.close_cache.assert_not_called()
  161. def test_close_database(self):
  162. with self.fixup_context(self.app) as (f, _, _):
  163. with patch.object(f, '_close_database') as _close:
  164. f.db_reuse_max = None
  165. f.close_database()
  166. _close.assert_called_with()
  167. _close.reset_mock()
  168. f.db_reuse_max = 10
  169. f._db_recycles = 3
  170. f.close_database()
  171. _close.assert_not_called()
  172. assert f._db_recycles == 4
  173. _close.reset_mock()
  174. f._db_recycles = 20
  175. f.close_database()
  176. _close.assert_called_with()
  177. assert f._db_recycles == 1
  178. def test__close_database(self):
  179. with self.fixup_context(self.app) as (f, _, _):
  180. conns = [Mock(), Mock(), Mock()]
  181. conns[1].close.side_effect = KeyError('already closed')
  182. f.DatabaseError = KeyError
  183. f.interface_errors = ()
  184. f._db.connections = Mock() # ConnectionHandler
  185. f._db.connections.all.side_effect = lambda: conns
  186. f._close_database()
  187. conns[0].close.assert_called_with()
  188. conns[1].close.assert_called_with()
  189. conns[2].close.assert_called_with()
  190. conns[1].close.side_effect = KeyError('omg')
  191. with pytest.raises(KeyError):
  192. f._close_database()
  193. def test_close_cache(self):
  194. with self.fixup_context(self.app) as (f, _, _):
  195. f.close_cache()
  196. f._cache.cache.close.assert_called_with()
  197. f._cache.cache.close.side_effect = TypeError()
  198. f.close_cache()
  199. def test_on_worker_ready(self):
  200. with self.fixup_context(self.app) as (f, _, _):
  201. f._settings.DEBUG = False
  202. f.on_worker_ready()
  203. with pytest.warns(UserWarning):
  204. f._settings.DEBUG = True
  205. f.on_worker_ready()
  206. def test_validate_models(self, patching):
  207. with mock.module('django', 'django.db', 'django.core',
  208. 'django.core.cache', 'django.conf',
  209. 'django.db.utils'):
  210. f = self.Fixup(self.app)
  211. f.django_setup = Mock(name='django.setup')
  212. patching.modules('django.core.checks')
  213. from django.core.checks import run_checks
  214. f.validate_models()
  215. f.django_setup.assert_called_with()
  216. run_checks.assert_called_with()
  217. def test_django_setup(self, patching):
  218. patching('celery.fixups.django.symbol_by_name')
  219. patching('celery.fixups.django.import_module')
  220. django, = patching.modules('django')
  221. f = self.Fixup(self.app)
  222. f.django_setup()
  223. django.setup.assert_called_with()