test_amqp.py 11 KB

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