Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

test_pidbox.py 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. from __future__ import absolute_import
  2. import socket
  3. import warnings
  4. from kombu import Connection
  5. from kombu import pidbox
  6. from kombu.exceptions import ContentDisallowed, InconsistencyError
  7. from kombu.utils import uuid
  8. from .case import Case, Mock, patch
  9. class test_Mailbox(Case):
  10. def _handler(self, state):
  11. return self.stats['var']
  12. def setUp(self):
  13. class Mailbox(pidbox.Mailbox):
  14. def _collect(self, *args, **kwargs):
  15. return 'COLLECTED'
  16. self.mailbox = Mailbox('test_pidbox')
  17. self.connection = Connection(transport='memory')
  18. self.state = {'var': 1}
  19. self.handlers = {'mymethod': self._handler}
  20. self.bound = self.mailbox(self.connection)
  21. self.default_chan = self.connection.channel()
  22. self.node = self.bound.Node(
  23. 'test_pidbox',
  24. state=self.state, handlers=self.handlers,
  25. channel=self.default_chan,
  26. )
  27. def test_publish_reply_ignores_InconsistencyError(self):
  28. mailbox = pidbox.Mailbox('test_reply__collect')(self.connection)
  29. with patch('kombu.pidbox.Producer') as Producer:
  30. producer = Producer.return_value = Mock(name='producer')
  31. producer.publish.side_effect = InconsistencyError()
  32. mailbox._publish_reply(
  33. {'foo': 'bar'}, mailbox.reply_exchange, mailbox.oid, 'foo',
  34. )
  35. self.assertTrue(producer.publish.called)
  36. def test_reply__collect(self):
  37. mailbox = pidbox.Mailbox('test_reply__collect')(self.connection)
  38. exchange = mailbox.reply_exchange.name
  39. channel = self.connection.channel()
  40. mailbox.reply_queue(channel).declare()
  41. ticket = uuid()
  42. mailbox._publish_reply({'foo': 'bar'}, exchange, mailbox.oid, ticket)
  43. _callback_called = [False]
  44. def callback(body):
  45. _callback_called[0] = True
  46. reply = mailbox._collect(ticket, limit=1,
  47. callback=callback, channel=channel)
  48. self.assertEqual(reply, [{'foo': 'bar'}])
  49. self.assertTrue(_callback_called[0])
  50. ticket = uuid()
  51. mailbox._publish_reply({'biz': 'boz'}, exchange, mailbox.oid, ticket)
  52. reply = mailbox._collect(ticket, limit=1, channel=channel)
  53. self.assertEqual(reply, [{'biz': 'boz'}])
  54. mailbox._publish_reply({'foo': 'BAM'}, exchange, mailbox.oid, 'doom',
  55. serializer='pickle')
  56. with self.assertRaises(ContentDisallowed):
  57. reply = mailbox._collect('doom', limit=1, channel=channel)
  58. mailbox._publish_reply(
  59. {'foo': 'BAMBAM'}, exchange, mailbox.oid, 'doom',
  60. serializer='pickle',
  61. )
  62. reply = mailbox._collect('doom', limit=1, channel=channel,
  63. accept=['pickle'])
  64. self.assertEqual(reply[0]['foo'], 'BAMBAM')
  65. de = mailbox.connection.drain_events = Mock()
  66. de.side_effect = socket.timeout
  67. mailbox._collect(ticket, limit=1, channel=channel)
  68. def test_constructor(self):
  69. self.assertIsNone(self.mailbox.connection)
  70. self.assertTrue(self.mailbox.exchange.name)
  71. self.assertTrue(self.mailbox.reply_exchange.name)
  72. def test_bound(self):
  73. bound = self.mailbox(self.connection)
  74. self.assertIs(bound.connection, self.connection)
  75. def test_Node(self):
  76. self.assertTrue(self.node.hostname)
  77. self.assertTrue(self.node.state)
  78. self.assertIs(self.node.mailbox, self.bound)
  79. self.assertTrue(self.handlers)
  80. # No initial handlers
  81. node2 = self.bound.Node('test_pidbox2', state=self.state)
  82. self.assertDictEqual(node2.handlers, {})
  83. def test_Node_consumer(self):
  84. consumer1 = self.node.Consumer()
  85. self.assertIs(consumer1.channel, self.default_chan)
  86. self.assertTrue(consumer1.no_ack)
  87. chan2 = self.connection.channel()
  88. consumer2 = self.node.Consumer(channel=chan2, no_ack=False)
  89. self.assertIs(consumer2.channel, chan2)
  90. self.assertFalse(consumer2.no_ack)
  91. def test_Node_consumer_multiple_listeners(self):
  92. warnings.resetwarnings()
  93. consumer = self.node.Consumer()
  94. q = consumer.queues[0]
  95. with warnings.catch_warnings(record=True) as log:
  96. q.on_declared('foo', 1, 1)
  97. self.assertTrue(log)
  98. self.assertIn('already using this', log[0].message.args[0])
  99. with warnings.catch_warnings(record=True) as log:
  100. q.on_declared('foo', 1, 0)
  101. self.assertFalse(log)
  102. def test_handler(self):
  103. node = self.bound.Node('test_handler', state=self.state)
  104. @node.handler
  105. def my_handler_name(state):
  106. return 42
  107. self.assertIn('my_handler_name', node.handlers)
  108. def test_dispatch(self):
  109. node = self.bound.Node('test_dispatch', state=self.state)
  110. @node.handler
  111. def my_handler_name(state, x=None, y=None):
  112. return x + y
  113. self.assertEqual(node.dispatch('my_handler_name',
  114. arguments={'x': 10, 'y': 10}), 20)
  115. def test_dispatch_raising_SystemExit(self):
  116. node = self.bound.Node('test_dispatch_raising_SystemExit',
  117. state=self.state)
  118. @node.handler
  119. def my_handler_name(state):
  120. raise SystemExit
  121. with self.assertRaises(SystemExit):
  122. node.dispatch('my_handler_name')
  123. def test_dispatch_raising(self):
  124. node = self.bound.Node('test_dispatch_raising', state=self.state)
  125. @node.handler
  126. def my_handler_name(state):
  127. raise KeyError('foo')
  128. res = node.dispatch('my_handler_name')
  129. self.assertIn('error', res)
  130. self.assertIn('KeyError', res['error'])
  131. def test_dispatch_replies(self):
  132. _replied = [False]
  133. def reply(data, **options):
  134. _replied[0] = True
  135. node = self.bound.Node('test_dispatch', state=self.state)
  136. node.reply = reply
  137. @node.handler
  138. def my_handler_name(state, x=None, y=None):
  139. return x + y
  140. node.dispatch('my_handler_name',
  141. arguments={'x': 10, 'y': 10},
  142. reply_to={'exchange': 'foo', 'routing_key': 'bar'})
  143. self.assertTrue(_replied[0])
  144. def test_reply(self):
  145. _replied = [(None, None, None)]
  146. def publish_reply(data, exchange, routing_key, ticket, **kwargs):
  147. _replied[0] = (data, exchange, routing_key, ticket)
  148. mailbox = self.mailbox(self.connection)
  149. mailbox._publish_reply = publish_reply
  150. node = mailbox.Node('test_reply')
  151. @node.handler
  152. def my_handler_name(state):
  153. return 42
  154. node.dispatch('my_handler_name',
  155. reply_to={'exchange': 'exchange',
  156. 'routing_key': 'rkey'},
  157. ticket='TICKET')
  158. data, exchange, routing_key, ticket = _replied[0]
  159. self.assertEqual(data, {'test_reply': 42})
  160. self.assertEqual(exchange, 'exchange')
  161. self.assertEqual(routing_key, 'rkey')
  162. self.assertEqual(ticket, 'TICKET')
  163. def test_handle_message(self):
  164. node = self.bound.Node('test_dispatch_from_message')
  165. @node.handler
  166. def my_handler_name(state, x=None, y=None):
  167. return x * y
  168. body = {'method': 'my_handler_name',
  169. 'arguments': {'x': 64, 'y': 64}}
  170. self.assertEqual(node.handle_message(body, None), 64 * 64)
  171. # message not for me should not be processed.
  172. body['destination'] = ['some_other_node']
  173. self.assertIsNone(node.handle_message(body, None))
  174. def test_handle_message_adjusts_clock(self):
  175. node = self.bound.Node('test_adjusts_clock')
  176. @node.handler
  177. def my_handler_name(state):
  178. return 10
  179. body = {'method': 'my_handler_name',
  180. 'arguments': {}}
  181. message = Mock(name='message')
  182. message.headers = {'clock': 313}
  183. node.adjust_clock = Mock(name='adjust_clock')
  184. res = node.handle_message(body, message)
  185. node.adjust_clock.assert_called_with(313)
  186. self.assertEqual(res, 10)
  187. def test_listen(self):
  188. consumer = self.node.listen()
  189. self.assertEqual(consumer.callbacks[0],
  190. self.node.handle_message)
  191. self.assertEqual(consumer.channel, self.default_chan)
  192. def test_cast(self):
  193. self.bound.cast(['somenode'], 'mymethod')
  194. consumer = self.node.Consumer()
  195. self.assertIsCast(self.get_next(consumer))
  196. def test_abcast(self):
  197. self.bound.abcast('mymethod')
  198. consumer = self.node.Consumer()
  199. self.assertIsCast(self.get_next(consumer))
  200. def test_call_destination_must_be_sequence(self):
  201. with self.assertRaises(ValueError):
  202. self.bound.call('some_node', 'mymethod')
  203. def test_call(self):
  204. self.assertEqual(
  205. self.bound.call(['some_node'], 'mymethod'),
  206. 'COLLECTED',
  207. )
  208. consumer = self.node.Consumer()
  209. self.assertIsCall(self.get_next(consumer))
  210. def test_multi_call(self):
  211. self.assertEqual(self.bound.multi_call('mymethod'), 'COLLECTED')
  212. consumer = self.node.Consumer()
  213. self.assertIsCall(self.get_next(consumer))
  214. def get_next(self, consumer):
  215. m = consumer.queues[0].get()
  216. if m:
  217. return m.payload
  218. def assertIsCast(self, message):
  219. self.assertTrue(message['method'])
  220. def assertIsCall(self, message):
  221. self.assertTrue(message['method'])
  222. self.assertTrue(message['reply_to'])