test_amqp.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. from __future__ import absolute_import, unicode_literals
  2. from datetime import datetime, timedelta
  3. import pytest
  4. from case import Mock
  5. from kombu import Exchange, Queue
  6. from celery import uuid
  7. from celery.app.amqp import Queues, utf8dict
  8. from celery.five import keys
  9. from celery.utils.time import to_utc
  10. class test_TaskConsumer:
  11. def test_accept_content(self, app):
  12. with app.pool.acquire(block=True) as con:
  13. app.conf.accept_content = ['application/json']
  14. assert app.amqp.TaskConsumer(con).accept == {
  15. 'application/json',
  16. }
  17. assert app.amqp.TaskConsumer(con, accept=['json']).accept == {
  18. 'application/json',
  19. }
  20. class test_ProducerPool:
  21. def test_setup_nolimit(self, app):
  22. app.conf.broker_pool_limit = None
  23. try:
  24. delattr(app, '_pool')
  25. except AttributeError:
  26. pass
  27. app.amqp._producer_pool = None
  28. pool = app.amqp.producer_pool
  29. assert pool.limit == app.pool.limit
  30. assert not pool._resource.queue
  31. r1 = pool.acquire()
  32. r2 = pool.acquire()
  33. r1.release()
  34. r2.release()
  35. r1 = pool.acquire()
  36. r2 = pool.acquire()
  37. def test_setup(self, app):
  38. app.conf.broker_pool_limit = 2
  39. try:
  40. delattr(app, '_pool')
  41. except AttributeError:
  42. pass
  43. app.amqp._producer_pool = None
  44. pool = app.amqp.producer_pool
  45. assert pool.limit == app.pool.limit
  46. assert pool._resource.queue
  47. p1 = r1 = pool.acquire()
  48. p2 = r2 = pool.acquire()
  49. r1.release()
  50. r2.release()
  51. r1 = pool.acquire()
  52. r2 = pool.acquire()
  53. assert p2 is r1
  54. assert p1 is r2
  55. r1.release()
  56. r2.release()
  57. class test_Queues:
  58. def test_queues_format(self):
  59. self.app.amqp.queues._consume_from = {}
  60. assert self.app.amqp.queues.format() == ''
  61. def test_with_defaults(self):
  62. assert Queues(None) == {}
  63. def test_add(self):
  64. q = Queues()
  65. q.add('foo', exchange='ex', routing_key='rk')
  66. assert 'foo' in q
  67. assert isinstance(q['foo'], Queue)
  68. assert q['foo'].routing_key == 'rk'
  69. def test_setitem_adds_default_exchange(self):
  70. q = Queues(default_exchange=Exchange('bar'))
  71. assert q.default_exchange
  72. queue = Queue('foo', exchange=None)
  73. queue.exchange = None
  74. q['foo'] = queue
  75. assert q['foo'].exchange == q.default_exchange
  76. @pytest.mark.parametrize('ha_policy,qname,q,qargs,expected', [
  77. (None, 'xyz', 'xyz', None, None),
  78. (None, 'xyz', 'xyz', {'x-foo': 'bar'}, {'x-foo': 'bar'}),
  79. ('all', 'foo', Queue('foo'), None, {'ha-mode': 'all'}),
  80. ('all', 'xyx2',
  81. Queue('xyx2', queue_arguments={'x-foo': 'bar'}),
  82. None,
  83. {'ha-mode': 'all', 'x-foo': 'bar'}),
  84. (['A', 'B', 'C'], 'foo', Queue('foo'), None, {
  85. 'ha-mode': 'nodes',
  86. 'ha-params': ['A', 'B', 'C']}),
  87. ])
  88. def test_with_ha_policy(self, ha_policy, qname, q, qargs, expected):
  89. queues = Queues(ha_policy=ha_policy, create_missing=False)
  90. queues.add(q, queue_arguments=qargs)
  91. assert queues[qname].queue_arguments == expected
  92. def test_select_add(self):
  93. q = Queues()
  94. q.select(['foo', 'bar'])
  95. q.select_add('baz')
  96. assert sorted(keys(q._consume_from)) == ['bar', 'baz', 'foo']
  97. def test_deselect(self):
  98. q = Queues()
  99. q.select(['foo', 'bar'])
  100. q.deselect('bar')
  101. assert sorted(keys(q._consume_from)) == ['foo']
  102. def test_with_ha_policy_compat(self):
  103. q = Queues(ha_policy='all')
  104. q.add('bar')
  105. assert q['bar'].queue_arguments == {'ha-mode': 'all'}
  106. def test_add_default_exchange(self):
  107. ex = Exchange('fff', 'fanout')
  108. q = Queues(default_exchange=ex)
  109. q.add(Queue('foo'))
  110. assert q['foo'].exchange.name == 'fff'
  111. def test_alias(self):
  112. q = Queues()
  113. q.add(Queue('foo', alias='barfoo'))
  114. assert q['barfoo'] is q['foo']
  115. @pytest.mark.parametrize('queues_kwargs,qname,q,expected', [
  116. ({'max_priority': 10},
  117. 'foo', 'foo', {'x-max-priority': 10}),
  118. ({'max_priority': 10},
  119. 'xyz', Queue('xyz', queue_arguments={'x-max-priority': 3}),
  120. {'x-max-priority': 3}),
  121. ({'max_priority': 10},
  122. 'moo', Queue('moo', queue_arguments=None),
  123. {'x-max-priority': 10}),
  124. ({'ha_policy': 'all', 'max_priority': 5},
  125. 'bar', 'bar',
  126. {'ha-mode': 'all', 'x-max-priority': 5}),
  127. ({'ha_policy': 'all', 'max_priority': 5},
  128. 'xyx2', Queue('xyx2', queue_arguments={'x-max-priority': 2}),
  129. {'ha-mode': 'all', 'x-max-priority': 2}),
  130. ({'max_priority': None},
  131. 'foo2', 'foo2',
  132. None),
  133. ({'max_priority': None},
  134. 'xyx3', Queue('xyx3', queue_arguments={'x-max-priority': 7}),
  135. {'x-max-priority': 7}),
  136. ])
  137. def test_with_max_priority(self, queues_kwargs, qname, q, expected):
  138. queues = Queues(**queues_kwargs)
  139. queues.add(q)
  140. assert queues[qname].queue_arguments == expected
  141. class test_default_queues:
  142. @pytest.mark.parametrize('name,exchange,rkey', [
  143. ('default', None, None),
  144. ('default', 'exchange', None),
  145. ('default', 'exchange', 'routing_key'),
  146. ('default', None, 'routing_key'),
  147. ])
  148. def test_setting_default_queue(self, name, exchange, rkey):
  149. self.app.conf.task_queues = {}
  150. self.app.conf.task_default_exchange = exchange
  151. self.app.conf.task_default_routing_key = rkey
  152. self.app.conf.task_default_queue = name
  153. assert self.app.amqp.queues.default_exchange.name == exchange or name
  154. queues = dict(self.app.amqp.queues)
  155. assert len(queues) == 1
  156. queue = queues[name]
  157. assert queue.exchange.name == exchange or name
  158. assert queue.exchange.type == 'direct'
  159. assert queue.routing_key == rkey or name
  160. class test_AMQP_proto1:
  161. def test_kwargs_must_be_mapping(self):
  162. with pytest.raises(TypeError):
  163. self.app.amqp.as_task_v1(uuid(), 'foo', kwargs=[1, 2])
  164. def test_args_must_be_list(self):
  165. with pytest.raises(TypeError):
  166. self.app.amqp.as_task_v1(uuid(), 'foo', args='abc')
  167. def test_countdown_negative(self):
  168. with pytest.raises(ValueError):
  169. self.app.amqp.as_task_v1(uuid(), 'foo', countdown=-1232132323123)
  170. def test_as_task_message_without_utc(self):
  171. self.app.amqp.utc = False
  172. self.app.amqp.as_task_v1(uuid(), 'foo', countdown=30, expires=40)
  173. class test_AMQP:
  174. def setup(self):
  175. self.simple_message = self.app.amqp.as_task_v2(
  176. uuid(), 'foo', create_sent_event=True,
  177. )
  178. self.simple_message_no_sent_event = self.app.amqp.as_task_v2(
  179. uuid(), 'foo', create_sent_event=False,
  180. )
  181. def test_kwargs_must_be_mapping(self):
  182. with pytest.raises(TypeError):
  183. self.app.amqp.as_task_v2(uuid(), 'foo', kwargs=[1, 2])
  184. def test_args_must_be_list(self):
  185. with pytest.raises(TypeError):
  186. self.app.amqp.as_task_v2(uuid(), 'foo', args='abc')
  187. def test_countdown_negative(self):
  188. with pytest.raises(ValueError):
  189. self.app.amqp.as_task_v2(uuid(), 'foo', countdown=-1232132323123)
  190. def test_Queues__with_ha_policy(self):
  191. x = self.app.amqp.Queues({}, ha_policy='all')
  192. assert x.ha_policy == 'all'
  193. def test_Queues__with_max_priority(self):
  194. x = self.app.amqp.Queues({}, max_priority=23)
  195. assert x.max_priority == 23
  196. def test_send_task_message__no_kwargs(self):
  197. self.app.amqp.send_task_message(Mock(), 'foo', self.simple_message)
  198. def test_send_task_message__properties(self):
  199. prod = Mock(name='producer')
  200. self.app.amqp.send_task_message(
  201. prod, 'foo', self.simple_message_no_sent_event,
  202. foo=1, retry=False,
  203. )
  204. assert prod.publish.call_args[1]['foo'] == 1
  205. def test_send_task_message__headers(self):
  206. prod = Mock(name='producer')
  207. self.app.amqp.send_task_message(
  208. prod, 'foo', self.simple_message_no_sent_event,
  209. headers={'x1x': 'y2x'},
  210. retry=False,
  211. )
  212. assert prod.publish.call_args[1]['headers']['x1x'] == 'y2x'
  213. def test_send_task_message__queue_string(self):
  214. prod = Mock(name='producer')
  215. self.app.amqp.send_task_message(
  216. prod, 'foo', self.simple_message_no_sent_event,
  217. queue='foo', retry=False,
  218. )
  219. kwargs = prod.publish.call_args[1]
  220. assert kwargs['routing_key'] == 'foo'
  221. assert kwargs['exchange'] == ''
  222. def test_send_task_message__broadcast_without_exchange(self):
  223. from kombu.common import Broadcast
  224. evd = Mock(name='evd')
  225. self.app.amqp.send_task_message(
  226. Mock(), 'foo', self.simple_message, retry=False,
  227. routing_key='xyz', queue=Broadcast('abc'),
  228. event_dispatcher=evd,
  229. )
  230. evd.publish.assert_called()
  231. event = evd.publish.call_args[0][1]
  232. assert event['routing_key'] == 'xyz'
  233. assert event['exchange'] == 'abc'
  234. def test_send_event_exchange_direct_with_exchange(self):
  235. prod = Mock(name='prod')
  236. self.app.amqp.send_task_message(
  237. prod, 'foo', self.simple_message_no_sent_event, queue='bar',
  238. retry=False, exchange_type='direct', exchange='xyz',
  239. )
  240. prod.publish.assert_called()
  241. pub = prod.publish.call_args[1]
  242. assert pub['routing_key'] == 'bar'
  243. assert pub['exchange'] == ''
  244. def test_send_event_exchange_direct_with_routing_key(self):
  245. prod = Mock(name='prod')
  246. self.app.amqp.send_task_message(
  247. prod, 'foo', self.simple_message_no_sent_event, queue='bar',
  248. retry=False, exchange_type='direct', routing_key='xyb',
  249. )
  250. prod.publish.assert_called()
  251. pub = prod.publish.call_args[1]
  252. assert pub['routing_key'] == 'bar'
  253. assert pub['exchange'] == ''
  254. def test_send_event_exchange_string(self):
  255. evd = Mock(name='evd')
  256. self.app.amqp.send_task_message(
  257. Mock(), 'foo', self.simple_message, retry=False,
  258. exchange='xyz', routing_key='xyb',
  259. event_dispatcher=evd,
  260. )
  261. evd.publish.assert_called()
  262. event = evd.publish.call_args[0][1]
  263. assert event['routing_key'] == 'xyb'
  264. assert event['exchange'] == 'xyz'
  265. def test_send_task_message__with_delivery_mode(self):
  266. prod = Mock(name='producer')
  267. self.app.amqp.send_task_message(
  268. prod, 'foo', self.simple_message_no_sent_event,
  269. delivery_mode=33, retry=False,
  270. )
  271. assert prod.publish.call_args[1]['delivery_mode'] == 33
  272. def test_send_task_message__with_receivers(self):
  273. from case import patch
  274. mocked_receiver = ((Mock(), Mock()), Mock())
  275. with patch('celery.signals.task_sent.receivers', [mocked_receiver]):
  276. self.app.amqp.send_task_message(Mock(), 'foo', self.simple_message)
  277. def test_routes(self):
  278. r1 = self.app.amqp.routes
  279. r2 = self.app.amqp.routes
  280. assert r1 is r2
  281. class test_as_task_v2:
  282. def test_raises_if_args_is_not_tuple(self):
  283. with pytest.raises(TypeError):
  284. self.app.amqp.as_task_v2(uuid(), 'foo', args='123')
  285. def test_raises_if_kwargs_is_not_mapping(self):
  286. with pytest.raises(TypeError):
  287. self.app.amqp.as_task_v2(uuid(), 'foo', kwargs=(1, 2, 3))
  288. def test_countdown_to_eta(self):
  289. now = to_utc(datetime.utcnow()).astimezone(self.app.timezone)
  290. m = self.app.amqp.as_task_v2(
  291. uuid(), 'foo', countdown=10, now=now,
  292. )
  293. assert m.headers['eta'] == (now + timedelta(seconds=10)).isoformat()
  294. def test_expires_to_datetime(self):
  295. now = to_utc(datetime.utcnow()).astimezone(self.app.timezone)
  296. m = self.app.amqp.as_task_v2(
  297. uuid(), 'foo', expires=30, now=now,
  298. )
  299. assert m.headers['expires'] == (
  300. now + timedelta(seconds=30)).isoformat()
  301. def test_eta_to_datetime(self):
  302. eta = datetime.utcnow()
  303. m = self.app.amqp.as_task_v2(
  304. uuid(), 'foo', eta=eta,
  305. )
  306. assert m.headers['eta'] == eta.isoformat()
  307. def test_callbacks_errbacks_chord(self):
  308. @self.app.task
  309. def t(i):
  310. pass
  311. m = self.app.amqp.as_task_v2(
  312. uuid(), 'foo',
  313. callbacks=[t.s(1), t.s(2)],
  314. errbacks=[t.s(3), t.s(4)],
  315. chord=t.s(5),
  316. )
  317. _, _, embed = m.body
  318. assert embed['callbacks'] == [utf8dict(t.s(1)), utf8dict(t.s(2))]
  319. assert embed['errbacks'] == [utf8dict(t.s(3)), utf8dict(t.s(4))]
  320. assert embed['chord'] == utf8dict(t.s(5))