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_redis.py 40KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269
  1. from __future__ import absolute_import
  2. import socket
  3. import types
  4. from anyjson import dumps, loads
  5. from collections import defaultdict
  6. from contextlib import contextmanager
  7. from itertools import count
  8. from kombu import Connection, Exchange, Queue, Consumer, Producer
  9. from kombu.exceptions import InconsistencyError, VersionMismatch
  10. from kombu.five import Empty, Queue as _Queue
  11. from kombu.transport import virtual
  12. from kombu.utils import eventio # patch poll
  13. from kombu.tests.case import (
  14. Case, ContextMock, Mock, call, module_exists, skip_if_not_module, patch,
  15. )
  16. class JSONEqual(object):
  17. # The order in which a dict is serialized to json depends on the hashseed
  18. # so we have this to support json in .assert_has_call*.
  19. def __init__(self, expected):
  20. self.expected = expected
  21. def __eq__(self, other):
  22. return loads(other) == loads(self.expected)
  23. def __str__(self):
  24. return self.expected
  25. def __repr__(self):
  26. return '(json)%r' % (self.expected,)
  27. class _poll(eventio._select):
  28. def register(self, fd, flags):
  29. if flags & eventio.READ:
  30. self._rfd.add(fd)
  31. def poll(self, timeout):
  32. events = []
  33. for fd in self._rfd:
  34. if fd.data:
  35. events.append((fd.fileno(), eventio.READ))
  36. return events
  37. eventio.poll = _poll
  38. # must import after poller patch
  39. from kombu.transport import redis # noqa
  40. class ResponseError(Exception):
  41. pass
  42. class Client(object):
  43. queues = {}
  44. sets = defaultdict(set)
  45. hashes = defaultdict(dict)
  46. shard_hint = None
  47. def __init__(self, db=None, port=None, connection_pool=None, **kwargs):
  48. self._called = []
  49. self._connection = None
  50. self.bgsave_raises_ResponseError = False
  51. self.connection = self._sconnection(self)
  52. def bgsave(self):
  53. self._called.append('BGSAVE')
  54. if self.bgsave_raises_ResponseError:
  55. raise ResponseError()
  56. def delete(self, key):
  57. self.queues.pop(key, None)
  58. def exists(self, key):
  59. return key in self.queues or key in self.sets
  60. def hset(self, key, k, v):
  61. self.hashes[key][k] = v
  62. def hget(self, key, k):
  63. return self.hashes[key].get(k)
  64. def hdel(self, key, k):
  65. self.hashes[key].pop(k, None)
  66. def sadd(self, key, member, *args):
  67. self.sets[key].add(member)
  68. zadd = sadd
  69. def smembers(self, key):
  70. return self.sets.get(key, set())
  71. def srem(self, key, *args):
  72. self.sets.pop(key, None)
  73. zrem = srem
  74. def llen(self, key):
  75. try:
  76. return self.queues[key].qsize()
  77. except KeyError:
  78. return 0
  79. def lpush(self, key, value):
  80. self.queues[key].put_nowait(value)
  81. def parse_response(self, connection, type, **options):
  82. cmd, queues = self.connection._sock.data.pop()
  83. assert cmd == type
  84. self.connection._sock.data = []
  85. if type == 'BRPOP':
  86. item = self.brpop(queues, 0.001)
  87. if item:
  88. return item
  89. raise Empty()
  90. def brpop(self, keys, timeout=None):
  91. key = keys[0]
  92. try:
  93. item = self.queues[key].get(timeout=timeout)
  94. except Empty:
  95. pass
  96. else:
  97. return key, item
  98. def rpop(self, key):
  99. try:
  100. return self.queues[key].get_nowait()
  101. except KeyError:
  102. pass
  103. def __contains__(self, k):
  104. return k in self._called
  105. def pipeline(self):
  106. return Pipeline(self)
  107. def encode(self, value):
  108. return str(value)
  109. def _new_queue(self, key):
  110. self.queues[key] = _Queue()
  111. class _sconnection(object):
  112. disconnected = False
  113. class _socket(object):
  114. blocking = True
  115. filenos = count(30)
  116. def __init__(self, *args):
  117. self._fileno = next(self.filenos)
  118. self.data = []
  119. def fileno(self):
  120. return self._fileno
  121. def setblocking(self, blocking):
  122. self.blocking = blocking
  123. def __init__(self, client):
  124. self.client = client
  125. self._sock = self._socket()
  126. def disconnect(self):
  127. self.disconnected = True
  128. def send_command(self, cmd, *args):
  129. self._sock.data.append((cmd, args))
  130. def info(self):
  131. return {'foo': 1}
  132. def pubsub(self, *args, **kwargs):
  133. connection = self.connection
  134. class ConnectionPool(object):
  135. def get_connection(self, *args, **kwargs):
  136. return connection
  137. self.connection_pool = ConnectionPool()
  138. return self
  139. def __repr__(self):
  140. return '<MockClient: %r' % (id(self),)
  141. class Pipeline(object):
  142. def __init__(self, client):
  143. self.client = client
  144. self.stack = []
  145. def __enter__(self):
  146. return self
  147. def __exit__(self, *exc_info):
  148. pass
  149. def __getattr__(self, key):
  150. if key not in self.__dict__:
  151. def _add(*args, **kwargs):
  152. self.stack.append((getattr(self.client, key), args, kwargs))
  153. return self
  154. return _add
  155. return self.__dict__[key]
  156. def execute(self):
  157. stack = list(self.stack)
  158. self.stack[:] = []
  159. return [fun(*args, **kwargs) for fun, args, kwargs in stack]
  160. class Channel(redis.Channel):
  161. Client = Client
  162. def _get_async_client(self):
  163. return Client
  164. def _create_client(self, async=False):
  165. return Client()
  166. def _get_pool(self, async=False):
  167. return Mock()
  168. @contextmanager
  169. def conn_or_acquire(self, client=None):
  170. yield client if client is not None else self._create_client()
  171. def _get_response_error(self):
  172. return ResponseError
  173. def _new_queue(self, queue, **kwargs):
  174. self.client._new_queue(queue)
  175. def pipeline(self):
  176. return Pipeline(Client())
  177. class Transport(redis.Transport):
  178. Channel = Channel
  179. def _get_errors(self):
  180. return ((KeyError, ), (IndexError, ))
  181. class test_Channel(Case):
  182. @skip_if_not_module('redis')
  183. def setUp(self):
  184. self.connection = self.create_connection()
  185. self.channel = self.connection.default_channel
  186. def create_connection(self, **kwargs):
  187. kwargs.setdefault('transport_options', {'fanout_patterns': True})
  188. return Connection(transport=Transport, **kwargs)
  189. def _get_one_delivery_tag(self, n='test_uniq_tag'):
  190. with self.create_connection() as conn1:
  191. chan = conn1.default_channel
  192. chan.exchange_declare(n)
  193. chan.queue_declare(n)
  194. chan.queue_bind(n, n, n)
  195. msg = chan.prepare_message('quick brown fox')
  196. chan.basic_publish(msg, n, n)
  197. q, payload = chan.client.brpop([n])
  198. self.assertEqual(q, n)
  199. self.assertTrue(payload)
  200. pymsg = chan.message_to_python(loads(payload))
  201. return pymsg.delivery_tag
  202. def test_delivery_tag_is_uuid(self):
  203. seen = set()
  204. for i in range(100):
  205. tag = self._get_one_delivery_tag()
  206. self.assertNotIn(tag, seen)
  207. seen.add(tag)
  208. with self.assertRaises(ValueError):
  209. int(tag)
  210. self.assertEqual(len(tag), 36)
  211. def test_disable_ack_emulation(self):
  212. conn = Connection(transport=Transport, transport_options={
  213. 'ack_emulation': False,
  214. })
  215. chan = conn.channel()
  216. self.assertFalse(chan.ack_emulation)
  217. self.assertEqual(chan.QoS, virtual.QoS)
  218. def test_redis_info_raises(self):
  219. pool = Mock(name='pool')
  220. pool_at_init = [pool]
  221. client = Mock(name='client')
  222. class XChannel(Channel):
  223. def __init__(self, *args, **kwargs):
  224. self._pool = pool_at_init[0]
  225. super(XChannel, self).__init__(*args, **kwargs)
  226. def _create_client(self, async=False):
  227. return client
  228. class XTransport(Transport):
  229. Channel = XChannel
  230. conn = Connection(transport=XTransport)
  231. client.info.side_effect = RuntimeError()
  232. with self.assertRaises(RuntimeError):
  233. conn.channel()
  234. pool.disconnect.assert_called_with()
  235. pool.disconnect.reset_mock()
  236. pool_at_init = [None]
  237. with self.assertRaises(RuntimeError):
  238. conn.channel()
  239. self.assertFalse(pool.disconnect.called)
  240. def test_after_fork(self):
  241. self.channel._pool = None
  242. self.channel._after_fork()
  243. pool = self.channel._pool = Mock(name='pool')
  244. self.channel._after_fork()
  245. pool.disconnect.assert_called_with()
  246. def test_next_delivery_tag(self):
  247. self.assertNotEqual(
  248. self.channel._next_delivery_tag(),
  249. self.channel._next_delivery_tag(),
  250. )
  251. def test_do_restore_message(self):
  252. client = Mock(name='client')
  253. pl1 = {'body': 'BODY'}
  254. spl1 = dumps(pl1)
  255. lookup = self.channel._lookup = Mock(name='_lookup')
  256. lookup.return_value = ['george', 'elaine']
  257. self.channel._do_restore_message(
  258. pl1, 'ex', 'rkey', client,
  259. )
  260. client.rpush.assert_has_calls([
  261. call('george', spl1), call('elaine', spl1),
  262. ])
  263. client.rpush.reset_mock()
  264. pl2 = {'body': 'BODY2', 'headers': {'x-funny': 1}}
  265. headers_after = dict(pl2['headers'], redelivered=True)
  266. spl2 = dumps(dict(pl2, headers=headers_after))
  267. self.channel._do_restore_message(
  268. pl2, 'ex', 'rkey', client,
  269. )
  270. client.rpush.assert_has_calls([
  271. call('george', JSONEqual(spl2)),
  272. call('elaine', JSONEqual(spl2)),
  273. ])
  274. client.rpush.side_effect = KeyError()
  275. with patch('kombu.transport.redis.crit') as crit:
  276. self.channel._do_restore_message(
  277. pl2, 'ex', 'rkey', client,
  278. )
  279. self.assertTrue(crit.called)
  280. def test_restore(self):
  281. message = Mock(name='message')
  282. with patch('kombu.transport.redis.loads') as loads:
  283. loads.return_value = 'M', 'EX', 'RK'
  284. client = self.channel._create_client = Mock(name='client')
  285. client = client()
  286. client.pipeline = ContextMock()
  287. restore = self.channel._do_restore_message = Mock(
  288. name='_do_restore_message',
  289. )
  290. pipe = client.pipeline.return_value
  291. pipe_hget = Mock(name='pipe.hget')
  292. pipe.hget.return_value = pipe_hget
  293. pipe_hget_hdel = Mock(name='pipe.hget.hdel')
  294. pipe_hget.hdel.return_value = pipe_hget_hdel
  295. result = Mock(name='result')
  296. pipe_hget_hdel.execute.return_value = None, None
  297. self.channel._restore(message)
  298. client.pipeline.assert_called_with()
  299. unacked_key = self.channel.unacked_key
  300. self.assertFalse(loads.called)
  301. tag = message.delivery_tag
  302. pipe.hget.assert_called_with(unacked_key, tag)
  303. pipe_hget.hdel.assert_called_with(unacked_key, tag)
  304. pipe_hget_hdel.execute.assert_called_with()
  305. pipe_hget_hdel.execute.return_value = result, None
  306. self.channel._restore(message)
  307. loads.assert_called_with(result)
  308. restore.assert_called_with('M', 'EX', 'RK', client, False)
  309. def test_qos_restore_visible(self):
  310. client = self.channel._create_client = Mock(name='client')
  311. client = client()
  312. def pipe(*args, **kwargs):
  313. return Pipeline(client)
  314. client.pipeline = pipe
  315. client.zrevrangebyscore.return_value = [
  316. (1, 10),
  317. (2, 20),
  318. (3, 30),
  319. ]
  320. qos = redis.QoS(self.channel)
  321. restore = qos.restore_by_tag = Mock(name='restore_by_tag')
  322. qos._vrestore_count = 1
  323. qos.restore_visible()
  324. self.assertFalse(client.zrevrangebyscore.called)
  325. self.assertEqual(qos._vrestore_count, 2)
  326. qos._vrestore_count = 0
  327. qos.restore_visible()
  328. restore.assert_has_calls([
  329. call(1, client), call(2, client), call(3, client),
  330. ])
  331. self.assertEqual(qos._vrestore_count, 1)
  332. qos._vrestore_count = 0
  333. restore.reset_mock()
  334. client.zrevrangebyscore.return_value = []
  335. qos.restore_visible()
  336. self.assertFalse(restore.called)
  337. self.assertEqual(qos._vrestore_count, 1)
  338. qos._vrestore_count = 0
  339. client.setnx.side_effect = redis.MutexHeld()
  340. qos.restore_visible()
  341. def test_basic_consume_when_fanout_queue(self):
  342. self.channel.exchange_declare(exchange='txconfan', type='fanout')
  343. self.channel.queue_declare(queue='txconfanq')
  344. self.channel.queue_bind(queue='txconfanq', exchange='txconfan')
  345. self.assertIn('txconfanq', self.channel._fanout_queues)
  346. self.channel.basic_consume('txconfanq', False, None, 1)
  347. self.assertIn('txconfanq', self.channel.active_fanout_queues)
  348. self.assertEqual(self.channel._fanout_to_queue.get('txconfan'),
  349. 'txconfanq')
  350. def test_basic_cancel_unknown_delivery_tag(self):
  351. self.assertIsNone(self.channel.basic_cancel('txaseqwewq'))
  352. def test_subscribe_no_queues(self):
  353. self.channel.subclient = Mock()
  354. self.channel.active_fanout_queues.clear()
  355. self.channel._subscribe()
  356. self.assertFalse(self.channel.subclient.subscribe.called)
  357. def test_subscribe(self):
  358. self.channel.subclient = Mock()
  359. self.channel.active_fanout_queues.add('a')
  360. self.channel.active_fanout_queues.add('b')
  361. self.channel._fanout_queues.update(a=('a', ''), b=('b', ''))
  362. self.channel._subscribe()
  363. self.assertTrue(self.channel.subclient.psubscribe.called)
  364. s_args, _ = self.channel.subclient.psubscribe.call_args
  365. self.assertItemsEqual(s_args[0], ['a', 'b'])
  366. self.channel.subclient.connection._sock = None
  367. self.channel._subscribe()
  368. self.channel.subclient.connection.connect.assert_called_with()
  369. def test_handle_unsubscribe_message(self):
  370. s = self.channel.subclient
  371. s.subscribed = True
  372. self.channel._handle_message(s, ['unsubscribe', 'a', 0])
  373. self.assertFalse(s.subscribed)
  374. def test_handle_pmessage_message(self):
  375. self.assertDictEqual(
  376. self.channel._handle_message(
  377. self.channel.subclient,
  378. ['pmessage', 'pattern', 'channel', 'data'],
  379. ),
  380. {
  381. 'type': 'pmessage',
  382. 'pattern': 'pattern',
  383. 'channel': 'channel',
  384. 'data': 'data',
  385. },
  386. )
  387. def test_handle_message(self):
  388. self.assertDictEqual(
  389. self.channel._handle_message(
  390. self.channel.subclient,
  391. ['type', 'channel', 'data'],
  392. ),
  393. {
  394. 'type': 'type',
  395. 'pattern': None,
  396. 'channel': 'channel',
  397. 'data': 'data',
  398. },
  399. )
  400. def test_brpop_start_but_no_queues(self):
  401. self.assertIsNone(self.channel._brpop_start())
  402. def test_receive(self):
  403. s = self.channel.subclient = Mock()
  404. self.channel._fanout_to_queue['a'] = 'b'
  405. s.parse_response.return_value = ['message', 'a',
  406. dumps({'hello': 'world'})]
  407. payload, queue = self.channel._receive()
  408. self.assertDictEqual(payload, {'hello': 'world'})
  409. self.assertEqual(queue, 'b')
  410. def test_receive_raises(self):
  411. self.channel._in_listen = True
  412. s = self.channel.subclient = Mock()
  413. s.parse_response.side_effect = KeyError('foo')
  414. with self.assertRaises(redis.Empty):
  415. self.channel._receive()
  416. self.assertFalse(self.channel._in_listen)
  417. def test_receive_empty(self):
  418. s = self.channel.subclient = Mock()
  419. s.parse_response.return_value = None
  420. with self.assertRaises(redis.Empty):
  421. self.channel._receive()
  422. def test_receive_different_message_Type(self):
  423. s = self.channel.subclient = Mock()
  424. s.parse_response.return_value = ['message', '/foo/', 0, 'data']
  425. with self.assertRaises(redis.Empty):
  426. self.channel._receive()
  427. def test_brpop_read_raises(self):
  428. c = self.channel.client = Mock()
  429. c.parse_response.side_effect = KeyError('foo')
  430. with self.assertRaises(redis.Empty):
  431. self.channel._brpop_read()
  432. c.connection.disconnect.assert_called_with()
  433. def test_brpop_read_gives_None(self):
  434. c = self.channel.client = Mock()
  435. c.parse_response.return_value = None
  436. with self.assertRaises(redis.Empty):
  437. self.channel._brpop_read()
  438. def test_poll_error(self):
  439. c = self.channel.client = Mock()
  440. c.parse_response = Mock()
  441. self.channel._poll_error('BRPOP')
  442. c.parse_response.assert_called_with(c.connection, 'BRPOP')
  443. c.parse_response.side_effect = KeyError('foo')
  444. with self.assertRaises(KeyError):
  445. self.channel._poll_error('BRPOP')
  446. def test_poll_error_on_type_LISTEN(self):
  447. c = self.channel.subclient = Mock()
  448. c.parse_response = Mock()
  449. self.channel._poll_error('LISTEN')
  450. c.parse_response.assert_called_with()
  451. c.parse_response.side_effect = KeyError('foo')
  452. with self.assertRaises(KeyError):
  453. self.channel._poll_error('LISTEN')
  454. def test_put_fanout(self):
  455. self.channel._in_poll = False
  456. c = self.channel._create_client = Mock()
  457. body = {'hello': 'world'}
  458. self.channel._put_fanout('exchange', body, '')
  459. c().publish.assert_called_with('exchange', JSONEqual(dumps(body)))
  460. def test_put_priority(self):
  461. client = self.channel._create_client = Mock(name='client')
  462. msg1 = {'properties': {'delivery_info': {'priority': 3}}}
  463. self.channel._put('george', msg1)
  464. client().lpush.assert_called_with(
  465. self.channel._q_for_pri('george', 3), JSONEqual(dumps(msg1)),
  466. )
  467. msg2 = {'properties': {'delivery_info': {'priority': 313}}}
  468. self.channel._put('george', msg2)
  469. client().lpush.assert_called_with(
  470. self.channel._q_for_pri('george', 9), JSONEqual(dumps(msg2)),
  471. )
  472. msg3 = {'properties': {'delivery_info': {}}}
  473. self.channel._put('george', msg3)
  474. client().lpush.assert_called_with(
  475. self.channel._q_for_pri('george', 0), JSONEqual(dumps(msg3)),
  476. )
  477. def test_delete(self):
  478. x = self.channel
  479. x._create_client = Mock()
  480. x._create_client.return_value = x.client
  481. delete = x.client.delete = Mock()
  482. srem = x.client.srem = Mock()
  483. x._delete('queue', 'exchange', 'routing_key', None)
  484. delete.assert_any_call('queue')
  485. srem.assert_called_once_with(
  486. x.keyprefix_queue % ('exchange', ),
  487. x.sep.join(['routing_key', '', 'queue'])
  488. )
  489. def test_has_queue(self):
  490. self.channel._create_client = Mock()
  491. self.channel._create_client.return_value = self.channel.client
  492. exists = self.channel.client.exists = Mock()
  493. exists.return_value = True
  494. self.assertTrue(self.channel._has_queue('foo'))
  495. exists.assert_any_call('foo')
  496. exists.return_value = False
  497. self.assertFalse(self.channel._has_queue('foo'))
  498. def test_close_when_closed(self):
  499. self.channel.closed = True
  500. self.channel.close()
  501. def test_close_deletes_autodelete_fanout_queues(self):
  502. self.channel._fanout_queues = {'foo': ('foo', ''), 'bar': ('bar', '')}
  503. self.channel.auto_delete_queues = ['foo']
  504. self.channel.queue_delete = Mock(name='queue_delete')
  505. self.channel.close()
  506. self.channel.queue_delete.assert_has_calls([call('foo')])
  507. def test_close_client_close_raises(self):
  508. c = self.channel.client = Mock()
  509. c.connection.disconnect.side_effect = self.channel.ResponseError()
  510. self.channel.close()
  511. c.connection.disconnect.assert_called_with()
  512. def test_invalid_database_raises_ValueError(self):
  513. with self.assertRaises(ValueError):
  514. self.channel.connection.client.virtual_host = 'dwqeq'
  515. self.channel._connparams()
  516. @skip_if_not_module('redis')
  517. def test_connparams_allows_slash_in_db(self):
  518. self.channel.connection.client.virtual_host = '/123'
  519. self.assertEqual(self.channel._connparams()['db'], 123)
  520. @skip_if_not_module('redis')
  521. def test_connparams_db_can_be_int(self):
  522. self.channel.connection.client.virtual_host = 124
  523. self.assertEqual(self.channel._connparams()['db'], 124)
  524. def test_new_queue_with_auto_delete(self):
  525. redis.Channel._new_queue(self.channel, 'george', auto_delete=False)
  526. self.assertNotIn('george', self.channel.auto_delete_queues)
  527. redis.Channel._new_queue(self.channel, 'elaine', auto_delete=True)
  528. self.assertIn('elaine', self.channel.auto_delete_queues)
  529. @skip_if_not_module('redis')
  530. def test_connparams_regular_hostname(self):
  531. self.channel.connection.client.hostname = 'george.vandelay.com'
  532. self.assertEqual(
  533. self.channel._connparams()['host'],
  534. 'george.vandelay.com',
  535. )
  536. def test_rotate_cycle_ValueError(self):
  537. cycle = self.channel._queue_cycle = ['kramer', 'jerry']
  538. self.channel._rotate_cycle('kramer')
  539. self.assertEqual(cycle, ['jerry', 'kramer'])
  540. self.channel._rotate_cycle('elaine')
  541. @skip_if_not_module('redis')
  542. def test_get_async_client(self):
  543. import redis as R
  544. KombuRedis = redis.Channel._get_async_client(self.channel)
  545. self.assertTrue(KombuRedis)
  546. Rv = getattr(R, 'VERSION', None)
  547. try:
  548. R.VERSION = (2, 4, 0)
  549. with self.assertRaises(VersionMismatch):
  550. redis.Channel._get_async_client(self.channel)
  551. finally:
  552. if Rv is not None:
  553. R.VERSION = Rv
  554. @skip_if_not_module('redis')
  555. def test_get_response_error(self):
  556. from redis.exceptions import ResponseError
  557. self.assertIs(redis.Channel._get_response_error(self.channel),
  558. ResponseError)
  559. def test_register_with_event_loop(self):
  560. transport = self.connection.transport
  561. transport.cycle = Mock(name='cycle')
  562. transport.cycle.fds = {12: 'LISTEN', 13: 'BRPOP'}
  563. conn = Mock(name='conn')
  564. loop = Mock(name='loop')
  565. redis.Transport.register_with_event_loop(transport, conn, loop)
  566. transport.cycle.on_poll_init.assert_called_with(loop.poller)
  567. loop.call_repeatedly.assert_called_with(
  568. 10, transport.cycle.maybe_restore_messages,
  569. )
  570. self.assertTrue(loop.on_tick.add.called)
  571. on_poll_start = loop.on_tick.add.call_args[0][0]
  572. on_poll_start()
  573. transport.cycle.on_poll_start.assert_called_with()
  574. loop.add_reader.assert_has_calls([
  575. call(12, transport.on_readable, 12),
  576. call(13, transport.on_readable, 13),
  577. ])
  578. def test_transport_on_readable(self):
  579. transport = self.connection.transport
  580. cycle = transport.cycle = Mock(name='cyle')
  581. cycle.on_readable.return_value = None
  582. redis.Transport.on_readable(transport, 13)
  583. cycle.on_readable.assert_called_with(13)
  584. cycle.on_readable.reset_mock()
  585. queue = Mock(name='queue')
  586. ret = (Mock(name='message'), queue)
  587. cycle.on_readable.return_value = ret
  588. with self.assertRaises(KeyError):
  589. redis.Transport.on_readable(transport, 14)
  590. cb = transport._callbacks[queue] = Mock(name='callback')
  591. redis.Transport.on_readable(transport, 14)
  592. cb.assert_called_with(ret[0])
  593. @skip_if_not_module('redis')
  594. def test_transport_get_errors(self):
  595. self.assertTrue(redis.Transport._get_errors(self.connection.transport))
  596. @skip_if_not_module('redis')
  597. def test_transport_driver_version(self):
  598. self.assertTrue(
  599. redis.Transport.driver_version(self.connection.transport),
  600. )
  601. @skip_if_not_module('redis')
  602. def test_transport_get_errors_when_InvalidData_used(self):
  603. from redis import exceptions
  604. class ID(Exception):
  605. pass
  606. DataError = getattr(exceptions, 'DataError', None)
  607. InvalidData = getattr(exceptions, 'InvalidData', None)
  608. exceptions.InvalidData = ID
  609. exceptions.DataError = None
  610. try:
  611. errors = redis.Transport._get_errors(self.connection.transport)
  612. self.assertTrue(errors)
  613. self.assertIn(ID, errors[1])
  614. finally:
  615. if DataError is not None:
  616. exceptions.DataError = DataError
  617. if InvalidData is not None:
  618. exceptions.InvalidData = InvalidData
  619. def test_empty_queues_key(self):
  620. channel = self.channel
  621. channel._in_poll = False
  622. key = channel.keyprefix_queue % 'celery'
  623. # Everything is fine, there is a list of queues.
  624. channel.client.sadd(key, 'celery\x06\x16\x06\x16celery')
  625. self.assertListEqual(channel.get_table('celery'),
  626. [('celery', '', 'celery')])
  627. # ... then for some reason, the _kombu.binding.celery key gets lost
  628. channel.client.srem(key)
  629. # which raises a channel error so that the consumer/publisher
  630. # can recover by redeclaring the required entities.
  631. with self.assertRaises(InconsistencyError):
  632. self.channel.get_table('celery')
  633. @skip_if_not_module('redis')
  634. def test_socket_connection(self):
  635. with patch('kombu.transport.redis.Channel._create_client'):
  636. with Connection('redis+socket:///tmp/redis.sock') as conn:
  637. connparams = conn.default_channel._connparams()
  638. self.assertTrue(issubclass(
  639. connparams['connection_class'],
  640. redis.redis.UnixDomainSocketConnection,
  641. ))
  642. self.assertEqual(connparams['path'], '/tmp/redis.sock')
  643. class test_Redis(Case):
  644. @skip_if_not_module('redis')
  645. def setUp(self):
  646. self.connection = Connection(transport=Transport)
  647. self.exchange = Exchange('test_Redis', type='direct')
  648. self.queue = Queue('test_Redis', self.exchange, 'test_Redis')
  649. def tearDown(self):
  650. self.connection.close()
  651. def test_publish__get(self):
  652. channel = self.connection.channel()
  653. producer = Producer(channel, self.exchange, routing_key='test_Redis')
  654. self.queue(channel).declare()
  655. producer.publish({'hello': 'world'})
  656. self.assertDictEqual(self.queue(channel).get().payload,
  657. {'hello': 'world'})
  658. self.assertIsNone(self.queue(channel).get())
  659. self.assertIsNone(self.queue(channel).get())
  660. self.assertIsNone(self.queue(channel).get())
  661. def test_publish__consume(self):
  662. connection = Connection(transport=Transport)
  663. channel = connection.channel()
  664. producer = Producer(channel, self.exchange, routing_key='test_Redis')
  665. consumer = Consumer(channel, queues=[self.queue])
  666. producer.publish({'hello2': 'world2'})
  667. _received = []
  668. def callback(message_data, message):
  669. _received.append(message_data)
  670. message.ack()
  671. consumer.register_callback(callback)
  672. consumer.consume()
  673. self.assertIn(channel, channel.connection.cycle._channels)
  674. try:
  675. connection.drain_events(timeout=1)
  676. self.assertTrue(_received)
  677. with self.assertRaises(socket.timeout):
  678. connection.drain_events(timeout=0.01)
  679. finally:
  680. channel.close()
  681. def test_purge(self):
  682. channel = self.connection.channel()
  683. producer = Producer(channel, self.exchange, routing_key='test_Redis')
  684. self.queue(channel).declare()
  685. for i in range(10):
  686. producer.publish({'hello': 'world-%s' % (i, )})
  687. self.assertEqual(channel._size('test_Redis'), 10)
  688. self.assertEqual(self.queue(channel).purge(), 10)
  689. channel.close()
  690. def test_db_values(self):
  691. Connection(virtual_host=1,
  692. transport=Transport).channel()
  693. Connection(virtual_host='1',
  694. transport=Transport).channel()
  695. Connection(virtual_host='/1',
  696. transport=Transport).channel()
  697. with self.assertRaises(Exception):
  698. Connection('redis:///foo').channel()
  699. def test_db_port(self):
  700. c1 = Connection(port=None, transport=Transport).channel()
  701. c1.close()
  702. c2 = Connection(port=9999, transport=Transport).channel()
  703. c2.close()
  704. def test_close_poller_not_active(self):
  705. c = Connection(transport=Transport).channel()
  706. cycle = c.connection.cycle
  707. c.client.connection
  708. c.close()
  709. self.assertNotIn(c, cycle._channels)
  710. def test_close_ResponseError(self):
  711. c = Connection(transport=Transport).channel()
  712. c.client.bgsave_raises_ResponseError = True
  713. c.close()
  714. def test_close_disconnects(self):
  715. c = Connection(transport=Transport).channel()
  716. conn1 = c.client.connection
  717. conn2 = c.subclient.connection
  718. c.close()
  719. self.assertTrue(conn1.disconnected)
  720. self.assertTrue(conn2.disconnected)
  721. def test_get__Empty(self):
  722. channel = self.connection.channel()
  723. with self.assertRaises(Empty):
  724. channel._get('does-not-exist')
  725. channel.close()
  726. def test_get_async_client(self):
  727. myredis, exceptions = _redis_modules()
  728. @module_exists(myredis, exceptions)
  729. def _do_test():
  730. conn = Connection(transport=Transport)
  731. chan = conn.channel()
  732. self.assertTrue(chan.Client)
  733. self.assertTrue(chan.ResponseError)
  734. self.assertTrue(conn.transport.connection_errors)
  735. self.assertTrue(conn.transport.channel_errors)
  736. _do_test()
  737. def _redis_modules():
  738. class ConnectionError(Exception):
  739. pass
  740. class AuthenticationError(Exception):
  741. pass
  742. class InvalidData(Exception):
  743. pass
  744. class InvalidResponse(Exception):
  745. pass
  746. class ResponseError(Exception):
  747. pass
  748. exceptions = types.ModuleType('redis.exceptions')
  749. exceptions.ConnectionError = ConnectionError
  750. exceptions.AuthenticationError = AuthenticationError
  751. exceptions.InvalidData = InvalidData
  752. exceptions.InvalidResponse = InvalidResponse
  753. exceptions.ResponseError = ResponseError
  754. class Redis(object):
  755. pass
  756. myredis = types.ModuleType('redis')
  757. myredis.exceptions = exceptions
  758. myredis.Redis = Redis
  759. return myredis, exceptions
  760. class test_MultiChannelPoller(Case):
  761. @skip_if_not_module('redis')
  762. def setUp(self):
  763. self.Poller = redis.MultiChannelPoller
  764. def test_on_poll_start(self):
  765. p = self.Poller()
  766. p._channels = []
  767. p.on_poll_start()
  768. p._register_BRPOP = Mock(name='_register_BRPOP')
  769. p._register_LISTEN = Mock(name='_register_LISTEN')
  770. chan1 = Mock(name='chan1')
  771. p._channels = [chan1]
  772. chan1.active_queues = []
  773. chan1.active_fanout_queues = []
  774. p.on_poll_start()
  775. chan1.active_queues = ['q1']
  776. chan1.active_fanout_queues = ['q2']
  777. chan1.qos.can_consume.return_value = False
  778. p.on_poll_start()
  779. p._register_LISTEN.assert_called_with(chan1)
  780. self.assertFalse(p._register_BRPOP.called)
  781. chan1.qos.can_consume.return_value = True
  782. p._register_LISTEN.reset_mock()
  783. p.on_poll_start()
  784. p._register_BRPOP.assert_called_with(chan1)
  785. p._register_LISTEN.assert_called_with(chan1)
  786. def test_on_poll_init(self):
  787. p = self.Poller()
  788. chan1 = Mock(name='chan1')
  789. p._channels = []
  790. poller = Mock(name='poller')
  791. p.on_poll_init(poller)
  792. self.assertIs(p.poller, poller)
  793. p._channels = [chan1]
  794. p.on_poll_init(poller)
  795. chan1.qos.restore_visible.assert_called_with(
  796. num=chan1.unacked_restore_limit,
  797. )
  798. def test_handle_event(self):
  799. p = self.Poller()
  800. chan = Mock(name='chan')
  801. p._fd_to_chan[13] = chan, 'BRPOP'
  802. chan.handlers = {'BRPOP': Mock(name='BRPOP')}
  803. chan.qos.can_consume.return_value = False
  804. p.handle_event(13, redis.READ)
  805. self.assertFalse(chan.handlers['BRPOP'].called)
  806. chan.qos.can_consume.return_value = True
  807. p.handle_event(13, redis.READ)
  808. chan.handlers['BRPOP'].assert_called_with()
  809. p.handle_event(13, redis.ERR)
  810. chan._poll_error.assert_called_with('BRPOP')
  811. p.handle_event(13, ~(redis.READ | redis.ERR))
  812. def test_fds(self):
  813. p = self.Poller()
  814. p._fd_to_chan = {1: 2}
  815. self.assertDictEqual(p.fds, p._fd_to_chan)
  816. def test_close_unregisters_fds(self):
  817. p = self.Poller()
  818. poller = p.poller = Mock()
  819. p._chan_to_sock.update({1: 1, 2: 2, 3: 3})
  820. p.close()
  821. self.assertEqual(poller.unregister.call_count, 3)
  822. u_args = poller.unregister.call_args_list
  823. self.assertItemsEqual(u_args, [((1, ), {}),
  824. ((2, ), {}),
  825. ((3, ), {})])
  826. def test_close_when_unregister_raises_KeyError(self):
  827. p = self.Poller()
  828. p.poller = Mock()
  829. p._chan_to_sock.update({1: 1})
  830. p.poller.unregister.side_effect = KeyError(1)
  831. p.close()
  832. def test_close_resets_state(self):
  833. p = self.Poller()
  834. p.poller = Mock()
  835. p._channels = Mock()
  836. p._fd_to_chan = Mock()
  837. p._chan_to_sock = Mock()
  838. p._chan_to_sock.itervalues.return_value = []
  839. p._chan_to_sock.values.return_value = [] # py3k
  840. p.close()
  841. p._channels.clear.assert_called_with()
  842. p._fd_to_chan.clear.assert_called_with()
  843. p._chan_to_sock.clear.assert_called_with()
  844. def test_register_when_registered_reregisters(self):
  845. p = self.Poller()
  846. p.poller = Mock()
  847. channel, client, type = Mock(), Mock(), Mock()
  848. sock = client.connection._sock = Mock()
  849. sock.fileno.return_value = 10
  850. p._chan_to_sock = {(channel, client, type): 6}
  851. p._register(channel, client, type)
  852. p.poller.unregister.assert_called_with(6)
  853. self.assertTupleEqual(p._fd_to_chan[10], (channel, type))
  854. self.assertEqual(p._chan_to_sock[(channel, client, type)], sock)
  855. p.poller.register.assert_called_with(sock, p.eventflags)
  856. # when client not connected yet
  857. client.connection._sock = None
  858. def after_connected():
  859. client.connection._sock = Mock()
  860. client.connection.connect.side_effect = after_connected
  861. p._register(channel, client, type)
  862. client.connection.connect.assert_called_with()
  863. def test_register_BRPOP(self):
  864. p = self.Poller()
  865. channel = Mock()
  866. channel.client.connection._sock = None
  867. p._register = Mock()
  868. channel._in_poll = False
  869. p._register_BRPOP(channel)
  870. self.assertEqual(channel._brpop_start.call_count, 1)
  871. self.assertEqual(p._register.call_count, 1)
  872. channel.client.connection._sock = Mock()
  873. p._chan_to_sock[(channel, channel.client, 'BRPOP')] = True
  874. channel._in_poll = True
  875. p._register_BRPOP(channel)
  876. self.assertEqual(channel._brpop_start.call_count, 1)
  877. self.assertEqual(p._register.call_count, 1)
  878. def test_register_LISTEN(self):
  879. p = self.Poller()
  880. channel = Mock()
  881. channel.subclient.connection._sock = None
  882. channel._in_listen = False
  883. p._register = Mock()
  884. p._register_LISTEN(channel)
  885. p._register.assert_called_with(channel, channel.subclient, 'LISTEN')
  886. self.assertEqual(p._register.call_count, 1)
  887. self.assertEqual(channel._subscribe.call_count, 1)
  888. channel._in_listen = True
  889. channel.subclient.connection._sock = Mock()
  890. p._register_LISTEN(channel)
  891. self.assertEqual(p._register.call_count, 1)
  892. self.assertEqual(channel._subscribe.call_count, 1)
  893. def create_get(self, events=None, queues=None, fanouts=None):
  894. _pr = [] if events is None else events
  895. _aq = [] if queues is None else queues
  896. _af = [] if fanouts is None else fanouts
  897. p = self.Poller()
  898. p.poller = Mock()
  899. p.poller.poll.return_value = _pr
  900. p._register_BRPOP = Mock()
  901. p._register_LISTEN = Mock()
  902. channel = Mock()
  903. p._channels = [channel]
  904. channel.active_queues = _aq
  905. channel.active_fanout_queues = _af
  906. return p, channel
  907. def test_get_no_actions(self):
  908. p, channel = self.create_get()
  909. with self.assertRaises(redis.Empty):
  910. p.get()
  911. def test_qos_reject(self):
  912. p, channel = self.create_get()
  913. qos = redis.QoS(channel)
  914. qos.ack = Mock(name='Qos.ack')
  915. qos.reject(1234)
  916. qos.ack.assert_called_with(1234)
  917. def test_get_brpop_qos_allow(self):
  918. p, channel = self.create_get(queues=['a_queue'])
  919. channel.qos.can_consume.return_value = True
  920. with self.assertRaises(redis.Empty):
  921. p.get()
  922. p._register_BRPOP.assert_called_with(channel)
  923. def test_get_brpop_qos_disallow(self):
  924. p, channel = self.create_get(queues=['a_queue'])
  925. channel.qos.can_consume.return_value = False
  926. with self.assertRaises(redis.Empty):
  927. p.get()
  928. self.assertFalse(p._register_BRPOP.called)
  929. def test_get_listen(self):
  930. p, channel = self.create_get(fanouts=['f_queue'])
  931. with self.assertRaises(redis.Empty):
  932. p.get()
  933. p._register_LISTEN.assert_called_with(channel)
  934. def test_get_receives_ERR(self):
  935. p, channel = self.create_get(events=[(1, eventio.ERR)])
  936. p._fd_to_chan[1] = (channel, 'BRPOP')
  937. with self.assertRaises(redis.Empty):
  938. p.get()
  939. channel._poll_error.assert_called_with('BRPOP')
  940. def test_get_receives_multiple(self):
  941. p, channel = self.create_get(events=[(1, eventio.ERR),
  942. (1, eventio.ERR)])
  943. p._fd_to_chan[1] = (channel, 'BRPOP')
  944. with self.assertRaises(redis.Empty):
  945. p.get()
  946. channel._poll_error.assert_called_with('BRPOP')
  947. class test_Mutex(Case):
  948. @skip_if_not_module('redis')
  949. def test_mutex(self, lock_id='xxx'):
  950. client = Mock(name='client')
  951. with patch('kombu.transport.redis.uuid') as uuid:
  952. # Won
  953. uuid.return_value = lock_id
  954. client.setnx.return_value = True
  955. client.pipeline = ContextMock()
  956. pipe = client.pipeline.return_value
  957. pipe.get.return_value = lock_id
  958. held = False
  959. with redis.Mutex(client, 'foo1', 100):
  960. held = True
  961. self.assertTrue(held)
  962. client.setnx.assert_called_with('foo1', lock_id)
  963. pipe.get.return_value = 'yyy'
  964. held = False
  965. with redis.Mutex(client, 'foo1', 100):
  966. held = True
  967. self.assertTrue(held)
  968. # Did not win
  969. client.expire.reset_mock()
  970. pipe.get.return_value = lock_id
  971. client.setnx.return_value = False
  972. with self.assertRaises(redis.MutexHeld):
  973. held = False
  974. with redis.Mutex(client, 'foo1', '100'):
  975. held = True
  976. self.assertFalse(held)
  977. client.ttl.return_value = 0
  978. with self.assertRaises(redis.MutexHeld):
  979. held = False
  980. with redis.Mutex(client, 'foo1', '100'):
  981. held = True
  982. self.assertFalse(held)
  983. self.assertTrue(client.expire.called)
  984. # Wins but raises WatchError (and that is ignored)
  985. client.setnx.return_value = True
  986. pipe.watch.side_effect = redis.redis.WatchError()
  987. held = False
  988. with redis.Mutex(client, 'foo1', 100):
  989. held = True
  990. self.assertTrue(held)