test_amqp.py 11 KB

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