/tests/asyncio/test_asyncio_client.py

https://github.com/miguelgrinberg/python-socketio · Python · 940 lines · 848 code · 90 blank · 2 comment · 11 complexity · be7ecf8039bef09da62b31a9186d0390 MD5 · raw file

  1. import asyncio
  2. from contextlib import contextmanager
  3. import sys
  4. import unittest
  5. import six
  6. if six.PY3:
  7. from unittest import mock
  8. else:
  9. import mock
  10. from socketio import asyncio_client
  11. from socketio import asyncio_namespace
  12. from engineio import exceptions as engineio_exceptions
  13. from socketio import exceptions
  14. from socketio import packet
  15. import pytest
  16. def AsyncMock(*args, **kwargs):
  17. """Return a mock asynchronous function."""
  18. m = mock.MagicMock(*args, **kwargs)
  19. async def mock_coro(*args, **kwargs):
  20. return m(*args, **kwargs)
  21. mock_coro.mock = m
  22. return mock_coro
  23. @contextmanager
  24. def mock_wait_for():
  25. async def fake_wait_for(coro, timeout):
  26. await coro
  27. await fake_wait_for._mock(timeout)
  28. original_wait_for = asyncio.wait_for
  29. asyncio.wait_for = fake_wait_for
  30. fake_wait_for._mock = AsyncMock()
  31. yield
  32. asyncio.wait_for = original_wait_for
  33. def _run(coro):
  34. """Run the given coroutine."""
  35. return asyncio.get_event_loop().run_until_complete(coro)
  36. @unittest.skipIf(sys.version_info < (3, 5), 'only for Python 3.5+')
  37. class TestAsyncClient(unittest.TestCase):
  38. def test_is_asyncio_based(self):
  39. c = asyncio_client.AsyncClient()
  40. assert c.is_asyncio_based()
  41. def test_connect(self):
  42. c = asyncio_client.AsyncClient()
  43. c.eio.connect = AsyncMock()
  44. _run(
  45. c.connect(
  46. 'url',
  47. headers='headers',
  48. transports='transports',
  49. namespaces=['/foo', '/', '/bar'],
  50. socketio_path='path',
  51. )
  52. )
  53. assert c.connection_url == 'url'
  54. assert c.connection_headers == 'headers'
  55. assert c.connection_transports == 'transports'
  56. assert c.connection_namespaces == ['/foo', '/', '/bar']
  57. assert c.socketio_path == 'path'
  58. assert c.namespaces == ['/foo', '/bar']
  59. c.eio.connect.mock.assert_called_once_with(
  60. 'url',
  61. headers='headers',
  62. transports='transports',
  63. engineio_path='path',
  64. )
  65. def test_connect_one_namespace(self):
  66. c = asyncio_client.AsyncClient()
  67. c.eio.connect = AsyncMock()
  68. _run(
  69. c.connect(
  70. 'url',
  71. headers='headers',
  72. transports='transports',
  73. namespaces='/foo',
  74. socketio_path='path',
  75. )
  76. )
  77. assert c.connection_url == 'url'
  78. assert c.connection_headers == 'headers'
  79. assert c.connection_transports == 'transports'
  80. assert c.connection_namespaces == ['/foo']
  81. assert c.socketio_path == 'path'
  82. assert c.namespaces == ['/foo']
  83. c.eio.connect.mock.assert_called_once_with(
  84. 'url',
  85. headers='headers',
  86. transports='transports',
  87. engineio_path='path',
  88. )
  89. def test_connect_default_namespaces(self):
  90. c = asyncio_client.AsyncClient()
  91. c.eio.connect = AsyncMock()
  92. c.on('foo', mock.MagicMock(), namespace='/foo')
  93. c.on('bar', mock.MagicMock(), namespace='/')
  94. _run(
  95. c.connect(
  96. 'url',
  97. headers='headers',
  98. transports='transports',
  99. socketio_path='path',
  100. )
  101. )
  102. assert c.connection_url == 'url'
  103. assert c.connection_headers == 'headers'
  104. assert c.connection_transports == 'transports'
  105. assert c.connection_namespaces is None
  106. assert c.socketio_path == 'path'
  107. assert c.namespaces == ['/foo']
  108. c.eio.connect.mock.assert_called_once_with(
  109. 'url',
  110. headers='headers',
  111. transports='transports',
  112. engineio_path='path',
  113. )
  114. def test_connect_error(self):
  115. c = asyncio_client.AsyncClient()
  116. c.eio.connect = AsyncMock(
  117. side_effect=engineio_exceptions.ConnectionError('foo')
  118. )
  119. c.on('foo', mock.MagicMock(), namespace='/foo')
  120. c.on('bar', mock.MagicMock(), namespace='/')
  121. with pytest.raises(exceptions.ConnectionError):
  122. _run(
  123. c.connect(
  124. 'url',
  125. headers='headers',
  126. transports='transports',
  127. socketio_path='path',
  128. )
  129. )
  130. def test_wait_no_reconnect(self):
  131. c = asyncio_client.AsyncClient()
  132. c.eio.wait = AsyncMock()
  133. c.sleep = AsyncMock()
  134. c._reconnect_task = None
  135. _run(c.wait())
  136. c.eio.wait.mock.assert_called_once_with()
  137. c.sleep.mock.assert_called_once_with(1)
  138. def test_wait_reconnect_failed(self):
  139. c = asyncio_client.AsyncClient()
  140. c.eio.wait = AsyncMock()
  141. c.sleep = AsyncMock()
  142. states = ['disconnected']
  143. async def fake_wait():
  144. c.eio.state = states.pop(0)
  145. c._reconnect_task = fake_wait()
  146. _run(c.wait())
  147. c.eio.wait.mock.assert_called_once_with()
  148. c.sleep.mock.assert_called_once_with(1)
  149. def test_wait_reconnect_successful(self):
  150. c = asyncio_client.AsyncClient()
  151. c.eio.wait = AsyncMock()
  152. c.sleep = AsyncMock()
  153. states = ['connected', 'disconnected']
  154. async def fake_wait():
  155. c.eio.state = states.pop(0)
  156. c._reconnect_task = fake_wait()
  157. c._reconnect_task = fake_wait()
  158. _run(c.wait())
  159. assert c.eio.wait.mock.call_count == 2
  160. assert c.sleep.mock.call_count == 2
  161. def test_emit_no_arguments(self):
  162. c = asyncio_client.AsyncClient()
  163. c._send_packet = AsyncMock()
  164. _run(c.emit('foo'))
  165. expected_packet = packet.Packet(
  166. packet.EVENT, namespace='/', data=['foo'], id=None, binary=False
  167. )
  168. assert c._send_packet.mock.call_count == 1
  169. assert (
  170. c._send_packet.mock.call_args_list[0][0][0].encode()
  171. == expected_packet.encode()
  172. )
  173. def test_emit_one_argument(self):
  174. c = asyncio_client.AsyncClient()
  175. c._send_packet = AsyncMock()
  176. _run(c.emit('foo', 'bar'))
  177. expected_packet = packet.Packet(
  178. packet.EVENT,
  179. namespace='/',
  180. data=['foo', 'bar'],
  181. id=None,
  182. binary=False,
  183. )
  184. assert c._send_packet.mock.call_count == 1
  185. assert (
  186. c._send_packet.mock.call_args_list[0][0][0].encode()
  187. == expected_packet.encode()
  188. )
  189. def test_emit_one_argument_list(self):
  190. c = asyncio_client.AsyncClient()
  191. c._send_packet = AsyncMock()
  192. _run(c.emit('foo', ['bar', 'baz']))
  193. expected_packet = packet.Packet(
  194. packet.EVENT,
  195. namespace='/',
  196. data=['foo', ['bar', 'baz']],
  197. id=None,
  198. binary=False,
  199. )
  200. assert c._send_packet.mock.call_count == 1
  201. assert (
  202. c._send_packet.mock.call_args_list[0][0][0].encode()
  203. == expected_packet.encode()
  204. )
  205. def test_emit_two_arguments(self):
  206. c = asyncio_client.AsyncClient()
  207. c._send_packet = AsyncMock()
  208. _run(c.emit('foo', ('bar', 'baz')))
  209. expected_packet = packet.Packet(
  210. packet.EVENT,
  211. namespace='/',
  212. data=['foo', 'bar', 'baz'],
  213. id=None,
  214. binary=False,
  215. )
  216. assert c._send_packet.mock.call_count == 1
  217. assert (
  218. c._send_packet.mock.call_args_list[0][0][0].encode()
  219. == expected_packet.encode()
  220. )
  221. def test_emit_namespace(self):
  222. c = asyncio_client.AsyncClient()
  223. c.namespaces = ['/foo']
  224. c._send_packet = AsyncMock()
  225. _run(c.emit('foo', namespace='/foo'))
  226. expected_packet = packet.Packet(
  227. packet.EVENT, namespace='/foo', data=['foo'], id=None, binary=False
  228. )
  229. assert c._send_packet.mock.call_count == 1
  230. assert (
  231. c._send_packet.mock.call_args_list[0][0][0].encode()
  232. == expected_packet.encode()
  233. )
  234. def test_emit_unknown_namespace(self):
  235. c = asyncio_client.AsyncClient()
  236. c.namespaces = ['/foo']
  237. with pytest.raises(exceptions.BadNamespaceError):
  238. _run(c.emit('foo', namespace='/bar'))
  239. def test_emit_with_callback(self):
  240. c = asyncio_client.AsyncClient()
  241. c._send_packet = AsyncMock()
  242. c._generate_ack_id = mock.MagicMock(return_value=123)
  243. _run(c.emit('foo', callback='cb'))
  244. expected_packet = packet.Packet(
  245. packet.EVENT, namespace='/', data=['foo'], id=123, binary=False
  246. )
  247. assert c._send_packet.mock.call_count == 1
  248. assert (
  249. c._send_packet.mock.call_args_list[0][0][0].encode()
  250. == expected_packet.encode()
  251. )
  252. c._generate_ack_id.assert_called_once_with('/', 'cb')
  253. def test_emit_namespace_with_callback(self):
  254. c = asyncio_client.AsyncClient()
  255. c.namespaces = ['/foo']
  256. c._send_packet = AsyncMock()
  257. c._generate_ack_id = mock.MagicMock(return_value=123)
  258. _run(c.emit('foo', namespace='/foo', callback='cb'))
  259. expected_packet = packet.Packet(
  260. packet.EVENT, namespace='/foo', data=['foo'], id=123, binary=False
  261. )
  262. assert c._send_packet.mock.call_count == 1
  263. assert (
  264. c._send_packet.mock.call_args_list[0][0][0].encode()
  265. == expected_packet.encode()
  266. )
  267. c._generate_ack_id.assert_called_once_with('/foo', 'cb')
  268. def test_emit_binary(self):
  269. c = asyncio_client.AsyncClient(binary=True)
  270. c._send_packet = AsyncMock()
  271. _run(c.emit('foo', b'bar'))
  272. expected_packet = packet.Packet(
  273. packet.EVENT,
  274. namespace='/',
  275. data=['foo', b'bar'],
  276. id=None,
  277. binary=True,
  278. )
  279. assert c._send_packet.mock.call_count == 1
  280. assert (
  281. c._send_packet.mock.call_args_list[0][0][0].encode()
  282. == expected_packet.encode()
  283. )
  284. def test_emit_not_binary(self):
  285. c = asyncio_client.AsyncClient(binary=False)
  286. c._send_packet = AsyncMock()
  287. _run(c.emit('foo', 'bar'))
  288. expected_packet = packet.Packet(
  289. packet.EVENT,
  290. namespace='/',
  291. data=['foo', 'bar'],
  292. id=None,
  293. binary=False,
  294. )
  295. assert c._send_packet.mock.call_count == 1
  296. assert (
  297. c._send_packet.mock.call_args_list[0][0][0].encode()
  298. == expected_packet.encode()
  299. )
  300. def test_send(self):
  301. c = asyncio_client.AsyncClient()
  302. c.emit = AsyncMock()
  303. _run(c.send('data', 'namespace', 'callback'))
  304. c.emit.mock.assert_called_once_with(
  305. 'message', data='data', namespace='namespace', callback='callback'
  306. )
  307. def test_send_with_defaults(self):
  308. c = asyncio_client.AsyncClient()
  309. c.emit = AsyncMock()
  310. _run(c.send('data'))
  311. c.emit.mock.assert_called_once_with(
  312. 'message', data='data', namespace=None, callback=None
  313. )
  314. def test_call(self):
  315. c = asyncio_client.AsyncClient()
  316. async def fake_event_wait():
  317. c._generate_ack_id.call_args_list[0][0][1]('foo', 321)
  318. c._send_packet = AsyncMock()
  319. c._generate_ack_id = mock.MagicMock(return_value=123)
  320. c.eio = mock.MagicMock()
  321. c.eio.create_event.return_value.wait = fake_event_wait
  322. assert _run(c.call('foo')) == ('foo', 321)
  323. expected_packet = packet.Packet(
  324. packet.EVENT, namespace='/', data=['foo'], id=123, binary=False
  325. )
  326. assert c._send_packet.mock.call_count == 1
  327. assert (
  328. c._send_packet.mock.call_args_list[0][0][0].encode()
  329. == expected_packet.encode()
  330. )
  331. def test_call_with_timeout(self):
  332. c = asyncio_client.AsyncClient()
  333. async def fake_event_wait():
  334. await asyncio.sleep(1)
  335. c._send_packet = AsyncMock()
  336. c._generate_ack_id = mock.MagicMock(return_value=123)
  337. c.eio = mock.MagicMock()
  338. c.eio.create_event.return_value.wait = fake_event_wait
  339. with pytest.raises(exceptions.TimeoutError):
  340. _run(c.call('foo', timeout=0.01))
  341. expected_packet = packet.Packet(
  342. packet.EVENT, namespace='/', data=['foo'], id=123, binary=False
  343. )
  344. assert c._send_packet.mock.call_count == 1
  345. assert (
  346. c._send_packet.mock.call_args_list[0][0][0].encode()
  347. == expected_packet.encode()
  348. )
  349. def test_disconnect(self):
  350. c = asyncio_client.AsyncClient()
  351. c._trigger_event = AsyncMock()
  352. c._send_packet = AsyncMock()
  353. c.eio = mock.MagicMock()
  354. c.eio.disconnect = AsyncMock()
  355. c.eio.state = 'connected'
  356. _run(c.disconnect())
  357. assert c._trigger_event.mock.call_count == 0
  358. assert c._send_packet.mock.call_count == 1
  359. expected_packet = packet.Packet(packet.DISCONNECT, namespace='/')
  360. assert (
  361. c._send_packet.mock.call_args_list[0][0][0].encode()
  362. == expected_packet.encode()
  363. )
  364. c.eio.disconnect.mock.assert_called_once_with(abort=True)
  365. def test_disconnect_namespaces(self):
  366. c = asyncio_client.AsyncClient()
  367. c.namespaces = ['/foo', '/bar']
  368. c._trigger_event = AsyncMock()
  369. c._send_packet = AsyncMock()
  370. c.eio = mock.MagicMock()
  371. c.eio.disconnect = AsyncMock()
  372. c.eio.state = 'connected'
  373. _run(c.disconnect())
  374. assert c._trigger_event.mock.call_count == 0
  375. assert c._send_packet.mock.call_count == 3
  376. expected_packet = packet.Packet(packet.DISCONNECT, namespace='/foo')
  377. assert (
  378. c._send_packet.mock.call_args_list[0][0][0].encode()
  379. == expected_packet.encode()
  380. )
  381. expected_packet = packet.Packet(packet.DISCONNECT, namespace='/bar')
  382. assert (
  383. c._send_packet.mock.call_args_list[1][0][0].encode()
  384. == expected_packet.encode()
  385. )
  386. expected_packet = packet.Packet(packet.DISCONNECT, namespace='/')
  387. assert (
  388. c._send_packet.mock.call_args_list[2][0][0].encode()
  389. == expected_packet.encode()
  390. )
  391. def test_start_background_task(self):
  392. c = asyncio_client.AsyncClient()
  393. c.eio.start_background_task = mock.MagicMock(return_value='foo')
  394. assert c.start_background_task('foo', 'bar', baz='baz') == 'foo'
  395. c.eio.start_background_task.assert_called_once_with(
  396. 'foo', 'bar', baz='baz'
  397. )
  398. def test_sleep(self):
  399. c = asyncio_client.AsyncClient()
  400. c.eio.sleep = AsyncMock()
  401. _run(c.sleep(1.23))
  402. c.eio.sleep.mock.assert_called_once_with(1.23)
  403. def test_send_packet(self):
  404. c = asyncio_client.AsyncClient()
  405. c.eio.send = AsyncMock()
  406. _run(c._send_packet(packet.Packet(packet.EVENT, 'foo', binary=False)))
  407. c.eio.send.mock.assert_called_once_with('2"foo"', binary=False)
  408. def test_send_packet_binary(self):
  409. c = asyncio_client.AsyncClient()
  410. c.eio.send = AsyncMock()
  411. _run(c._send_packet(packet.Packet(packet.EVENT, b'foo', binary=True)))
  412. assert c.eio.send.mock.call_args_list == [
  413. mock.call('51-{"_placeholder":true,"num":0}', binary=False),
  414. mock.call(b'foo', binary=True),
  415. ] or c.eio.send.mock.call_args_list == [
  416. mock.call('51-{"num":0,"_placeholder":true}', binary=False),
  417. mock.call(b'foo', binary=True),
  418. ]
  419. def test_send_packet_default_binary_py3(self):
  420. c = asyncio_client.AsyncClient()
  421. c.eio.send = AsyncMock()
  422. _run(c._send_packet(packet.Packet(packet.EVENT, 'foo')))
  423. c.eio.send.mock.assert_called_once_with('2"foo"', binary=False)
  424. def test_handle_connect(self):
  425. c = asyncio_client.AsyncClient()
  426. c._trigger_event = AsyncMock()
  427. c._send_packet = AsyncMock()
  428. _run(c._handle_connect('/'))
  429. c._trigger_event.mock.assert_called_once_with('connect', namespace='/')
  430. c._send_packet.mock.assert_not_called()
  431. def test_handle_connect_with_namespaces(self):
  432. c = asyncio_client.AsyncClient()
  433. c.namespaces = ['/foo', '/bar']
  434. c._trigger_event = AsyncMock()
  435. c._send_packet = AsyncMock()
  436. _run(c._handle_connect('/'))
  437. c._trigger_event.mock.assert_called_once_with('connect', namespace='/')
  438. assert c._send_packet.mock.call_count == 2
  439. expected_packet = packet.Packet(packet.CONNECT, namespace='/foo')
  440. assert (
  441. c._send_packet.mock.call_args_list[0][0][0].encode()
  442. == expected_packet.encode()
  443. )
  444. expected_packet = packet.Packet(packet.CONNECT, namespace='/bar')
  445. assert (
  446. c._send_packet.mock.call_args_list[1][0][0].encode()
  447. == expected_packet.encode()
  448. )
  449. def test_handle_connect_namespace(self):
  450. c = asyncio_client.AsyncClient()
  451. c.namespaces = ['/foo']
  452. c._trigger_event = AsyncMock()
  453. c._send_packet = AsyncMock()
  454. _run(c._handle_connect('/foo'))
  455. _run(c._handle_connect('/bar'))
  456. assert c._trigger_event.mock.call_args_list == [
  457. mock.call('connect', namespace='/foo'),
  458. mock.call('connect', namespace='/bar'),
  459. ]
  460. c._send_packet.mock.assert_not_called()
  461. assert c.namespaces == ['/foo', '/bar']
  462. def test_handle_disconnect(self):
  463. c = asyncio_client.AsyncClient()
  464. c.connected = True
  465. c._trigger_event = AsyncMock()
  466. _run(c._handle_disconnect('/'))
  467. c._trigger_event.mock.assert_called_once_with(
  468. 'disconnect', namespace='/'
  469. )
  470. assert not c.connected
  471. _run(c._handle_disconnect('/'))
  472. assert c._trigger_event.mock.call_count == 1
  473. def test_handle_disconnect_namespace(self):
  474. c = asyncio_client.AsyncClient()
  475. c.connected = True
  476. c.namespaces = ['/foo', '/bar']
  477. c._trigger_event = AsyncMock()
  478. _run(c._handle_disconnect('/foo'))
  479. c._trigger_event.mock.assert_called_once_with(
  480. 'disconnect', namespace='/foo'
  481. )
  482. assert c.namespaces == ['/bar']
  483. assert c.connected
  484. def test_handle_disconnect_unknown_namespace(self):
  485. c = asyncio_client.AsyncClient()
  486. c.connected = True
  487. c.namespaces = ['/foo', '/bar']
  488. c._trigger_event = AsyncMock()
  489. _run(c._handle_disconnect('/baz'))
  490. c._trigger_event.mock.assert_called_once_with(
  491. 'disconnect', namespace='/baz'
  492. )
  493. assert c.namespaces == ['/foo', '/bar']
  494. assert c.connected
  495. def test_handle_disconnect_all_namespaces(self):
  496. c = asyncio_client.AsyncClient()
  497. c.connected = True
  498. c.namespaces = ['/foo', '/bar']
  499. c._trigger_event = AsyncMock()
  500. _run(c._handle_disconnect('/'))
  501. c._trigger_event.mock.assert_any_call('disconnect', namespace='/')
  502. c._trigger_event.mock.assert_any_call('disconnect', namespace='/foo')
  503. c._trigger_event.mock.assert_any_call('disconnect', namespace='/bar')
  504. assert c.namespaces == []
  505. assert not c.connected
  506. def test_handle_event(self):
  507. c = asyncio_client.AsyncClient()
  508. c._trigger_event = AsyncMock()
  509. _run(c._handle_event('/', None, ['foo', ('bar', 'baz')]))
  510. c._trigger_event.mock.assert_called_once_with(
  511. 'foo', '/', ('bar', 'baz')
  512. )
  513. def test_handle_event_with_id_no_arguments(self):
  514. c = asyncio_client.AsyncClient(binary=True)
  515. c._trigger_event = AsyncMock(return_value=None)
  516. c._send_packet = AsyncMock()
  517. _run(c._handle_event('/', 123, ['foo', ('bar', 'baz')]))
  518. c._trigger_event.mock.assert_called_once_with(
  519. 'foo', '/', ('bar', 'baz')
  520. )
  521. assert c._send_packet.mock.call_count == 1
  522. expected_packet = packet.Packet(
  523. packet.ACK, namespace='/', id=123, data=[], binary=None
  524. )
  525. assert (
  526. c._send_packet.mock.call_args_list[0][0][0].encode()
  527. == expected_packet.encode()
  528. )
  529. def test_handle_event_with_id_one_argument(self):
  530. c = asyncio_client.AsyncClient(binary=True)
  531. c._trigger_event = AsyncMock(return_value='ret')
  532. c._send_packet = AsyncMock()
  533. _run(c._handle_event('/', 123, ['foo', ('bar', 'baz')]))
  534. c._trigger_event.mock.assert_called_once_with(
  535. 'foo', '/', ('bar', 'baz')
  536. )
  537. assert c._send_packet.mock.call_count == 1
  538. expected_packet = packet.Packet(
  539. packet.ACK, namespace='/', id=123, data=['ret'], binary=None
  540. )
  541. assert (
  542. c._send_packet.mock.call_args_list[0][0][0].encode()
  543. == expected_packet.encode()
  544. )
  545. def test_handle_event_with_id_one_list_argument(self):
  546. c = asyncio_client.AsyncClient(binary=True)
  547. c._trigger_event = AsyncMock(return_value=['a', 'b'])
  548. c._send_packet = AsyncMock()
  549. _run(c._handle_event('/', 123, ['foo', ('bar', 'baz')]))
  550. c._trigger_event.mock.assert_called_once_with(
  551. 'foo', '/', ('bar', 'baz')
  552. )
  553. assert c._send_packet.mock.call_count == 1
  554. expected_packet = packet.Packet(
  555. packet.ACK, namespace='/', id=123, data=[['a', 'b']], binary=None
  556. )
  557. assert (
  558. c._send_packet.mock.call_args_list[0][0][0].encode()
  559. == expected_packet.encode()
  560. )
  561. def test_handle_event_with_id_two_arguments(self):
  562. c = asyncio_client.AsyncClient(binary=True)
  563. c._trigger_event = AsyncMock(return_value=('a', 'b'))
  564. c._send_packet = AsyncMock()
  565. _run(c._handle_event('/', 123, ['foo', ('bar', 'baz')]))
  566. c._trigger_event.mock.assert_called_once_with(
  567. 'foo', '/', ('bar', 'baz')
  568. )
  569. assert c._send_packet.mock.call_count == 1
  570. expected_packet = packet.Packet(
  571. packet.ACK, namespace='/', id=123, data=['a', 'b'], binary=None
  572. )
  573. assert (
  574. c._send_packet.mock.call_args_list[0][0][0].encode()
  575. == expected_packet.encode()
  576. )
  577. def test_handle_ack(self):
  578. c = asyncio_client.AsyncClient()
  579. mock_cb = mock.MagicMock()
  580. c.callbacks['/foo'] = {123: mock_cb}
  581. _run(c._handle_ack('/foo', 123, ['bar', 'baz']))
  582. mock_cb.assert_called_once_with('bar', 'baz')
  583. assert 123 not in c.callbacks['/foo']
  584. def test_handle_ack_async(self):
  585. c = asyncio_client.AsyncClient()
  586. mock_cb = AsyncMock()
  587. c.callbacks['/foo'] = {123: mock_cb}
  588. _run(c._handle_ack('/foo', 123, ['bar', 'baz']))
  589. mock_cb.mock.assert_called_once_with('bar', 'baz')
  590. assert 123 not in c.callbacks['/foo']
  591. def test_handle_ack_not_found(self):
  592. c = asyncio_client.AsyncClient()
  593. mock_cb = mock.MagicMock()
  594. c.callbacks['/foo'] = {123: mock_cb}
  595. _run(c._handle_ack('/foo', 124, ['bar', 'baz']))
  596. mock_cb.assert_not_called()
  597. assert 123 in c.callbacks['/foo']
  598. def test_handle_error(self):
  599. c = asyncio_client.AsyncClient()
  600. c.connected = True
  601. c._trigger_event = AsyncMock()
  602. c.namespaces = ['/foo', '/bar']
  603. _run(c._handle_error('/', 'error'))
  604. assert c.namespaces == []
  605. assert not c.connected
  606. c._trigger_event.mock.assert_called_once_with(
  607. 'connect_error', '/', 'error'
  608. )
  609. def test_handle_error_with_no_arguments(self):
  610. c = asyncio_client.AsyncClient()
  611. c.connected = True
  612. c._trigger_event = AsyncMock()
  613. c.namespaces = ['/foo', '/bar']
  614. _run(c._handle_error('/', None))
  615. assert c.namespaces == []
  616. assert not c.connected
  617. c._trigger_event.mock.assert_called_once_with('connect_error', '/')
  618. def test_handle_error_namespace(self):
  619. c = asyncio_client.AsyncClient()
  620. c.connected = True
  621. c.namespaces = ['/foo', '/bar']
  622. c._trigger_event = AsyncMock()
  623. _run(c._handle_error('/bar', ['error', 'message']))
  624. assert c.namespaces == ['/foo']
  625. assert c.connected
  626. c._trigger_event.mock.assert_called_once_with(
  627. 'connect_error', '/bar', 'error', 'message'
  628. )
  629. def test_handle_error_namespace_with_no_arguments(self):
  630. c = asyncio_client.AsyncClient()
  631. c.connected = True
  632. c.namespaces = ['/foo', '/bar']
  633. c._trigger_event = AsyncMock()
  634. _run(c._handle_error('/bar', None))
  635. assert c.namespaces == ['/foo']
  636. assert c.connected
  637. c._trigger_event.mock.assert_called_once_with('connect_error', '/bar')
  638. def test_handle_error_unknown_namespace(self):
  639. c = asyncio_client.AsyncClient()
  640. c.connected = True
  641. c.namespaces = ['/foo', '/bar']
  642. _run(c._handle_error('/baz', 'error'))
  643. assert c.namespaces == ['/foo', '/bar']
  644. assert c.connected
  645. def test_trigger_event(self):
  646. c = asyncio_client.AsyncClient()
  647. handler = mock.MagicMock()
  648. c.on('foo', handler)
  649. _run(c._trigger_event('foo', '/', 1, '2'))
  650. handler.assert_called_once_with(1, '2')
  651. def test_trigger_event_namespace(self):
  652. c = asyncio_client.AsyncClient()
  653. handler = AsyncMock()
  654. c.on('foo', handler, namespace='/bar')
  655. _run(c._trigger_event('foo', '/bar', 1, '2'))
  656. handler.mock.assert_called_once_with(1, '2')
  657. def test_trigger_event_class_namespace(self):
  658. c = asyncio_client.AsyncClient()
  659. result = []
  660. class MyNamespace(asyncio_namespace.AsyncClientNamespace):
  661. def on_foo(self, a, b):
  662. result.append(a)
  663. result.append(b)
  664. c.register_namespace(MyNamespace('/'))
  665. _run(c._trigger_event('foo', '/', 1, '2'))
  666. assert result == [1, '2']
  667. def test_trigger_event_unknown_namespace(self):
  668. c = asyncio_client.AsyncClient()
  669. result = []
  670. class MyNamespace(asyncio_namespace.AsyncClientNamespace):
  671. def on_foo(self, a, b):
  672. result.append(a)
  673. result.append(b)
  674. c.register_namespace(MyNamespace('/'))
  675. _run(c._trigger_event('foo', '/bar', 1, '2'))
  676. assert result == []
  677. @mock.patch(
  678. 'asyncio.wait_for',
  679. new_callable=AsyncMock,
  680. side_effect=asyncio.TimeoutError,
  681. )
  682. @mock.patch('socketio.client.random.random', side_effect=[1, 0, 0.5])
  683. def test_handle_reconnect(self, random, wait_for):
  684. c = asyncio_client.AsyncClient()
  685. c._reconnect_task = 'foo'
  686. c.connect = AsyncMock(
  687. side_effect=[ValueError, exceptions.ConnectionError, None]
  688. )
  689. _run(c._handle_reconnect())
  690. assert wait_for.mock.call_count == 3
  691. assert [x[0][1] for x in asyncio.wait_for.mock.call_args_list] == [
  692. 1.5,
  693. 1.5,
  694. 4.0,
  695. ]
  696. assert c._reconnect_task is None
  697. @mock.patch(
  698. 'asyncio.wait_for',
  699. new_callable=AsyncMock,
  700. side_effect=asyncio.TimeoutError,
  701. )
  702. @mock.patch('socketio.client.random.random', side_effect=[1, 0, 0.5])
  703. def test_handle_reconnect_max_delay(self, random, wait_for):
  704. c = asyncio_client.AsyncClient(reconnection_delay_max=3)
  705. c._reconnect_task = 'foo'
  706. c.connect = AsyncMock(
  707. side_effect=[ValueError, exceptions.ConnectionError, None]
  708. )
  709. _run(c._handle_reconnect())
  710. assert wait_for.mock.call_count == 3
  711. assert [x[0][1] for x in asyncio.wait_for.mock.call_args_list] == [
  712. 1.5,
  713. 1.5,
  714. 3.0,
  715. ]
  716. assert c._reconnect_task is None
  717. @mock.patch(
  718. 'asyncio.wait_for',
  719. new_callable=AsyncMock,
  720. side_effect=asyncio.TimeoutError,
  721. )
  722. @mock.patch('socketio.client.random.random', side_effect=[1, 0, 0.5])
  723. def test_handle_reconnect_max_attempts(self, random, wait_for):
  724. c = asyncio_client.AsyncClient(reconnection_attempts=2)
  725. c._reconnect_task = 'foo'
  726. c.connect = AsyncMock(
  727. side_effect=[ValueError, exceptions.ConnectionError, None]
  728. )
  729. _run(c._handle_reconnect())
  730. assert wait_for.mock.call_count == 2
  731. assert [x[0][1] for x in asyncio.wait_for.mock.call_args_list] == [
  732. 1.5,
  733. 1.5,
  734. ]
  735. assert c._reconnect_task == 'foo'
  736. @mock.patch(
  737. 'asyncio.wait_for',
  738. new_callable=AsyncMock,
  739. side_effect=[asyncio.TimeoutError, None],
  740. )
  741. @mock.patch('socketio.client.random.random', side_effect=[1, 0, 0.5])
  742. def test_handle_reconnect_aborted(self, random, wait_for):
  743. c = asyncio_client.AsyncClient()
  744. c._reconnect_task = 'foo'
  745. c.connect = AsyncMock(
  746. side_effect=[ValueError, exceptions.ConnectionError, None]
  747. )
  748. _run(c._handle_reconnect())
  749. assert wait_for.mock.call_count == 2
  750. assert [x[0][1] for x in asyncio.wait_for.mock.call_args_list] == [
  751. 1.5,
  752. 1.5,
  753. ]
  754. assert c._reconnect_task == 'foo'
  755. def test_eio_connect(self):
  756. c = asyncio_client.AsyncClient()
  757. c.eio.sid = 'foo'
  758. assert c.sid is None
  759. c._handle_eio_connect()
  760. assert c.sid == 'foo'
  761. def test_handle_eio_message(self):
  762. c = asyncio_client.AsyncClient()
  763. c._handle_connect = AsyncMock()
  764. c._handle_disconnect = AsyncMock()
  765. c._handle_event = AsyncMock()
  766. c._handle_ack = AsyncMock()
  767. c._handle_error = AsyncMock()
  768. _run(c._handle_eio_message('0'))
  769. c._handle_connect.mock.assert_called_with(None)
  770. _run(c._handle_eio_message('0/foo'))
  771. c._handle_connect.mock.assert_called_with('/foo')
  772. _run(c._handle_eio_message('1'))
  773. c._handle_disconnect.mock.assert_called_with(None)
  774. _run(c._handle_eio_message('1/foo'))
  775. c._handle_disconnect.mock.assert_called_with('/foo')
  776. _run(c._handle_eio_message('2["foo"]'))
  777. c._handle_event.mock.assert_called_with(None, None, ['foo'])
  778. _run(c._handle_eio_message('3/foo,["bar"]'))
  779. c._handle_ack.mock.assert_called_with('/foo', None, ['bar'])
  780. _run(c._handle_eio_message('4'))
  781. c._handle_error.mock.assert_called_with(None, None)
  782. _run(c._handle_eio_message('4"foo"'))
  783. c._handle_error.mock.assert_called_with(None, 'foo')
  784. _run(c._handle_eio_message('4["foo"]'))
  785. c._handle_error.mock.assert_called_with(None, ['foo'])
  786. _run(c._handle_eio_message('4/foo'))
  787. c._handle_error.mock.assert_called_with('/foo', None)
  788. _run(c._handle_eio_message('4/foo,["foo","bar"]'))
  789. c._handle_error.mock.assert_called_with('/foo', ['foo', 'bar'])
  790. _run(c._handle_eio_message('51-{"_placeholder":true,"num":0}'))
  791. assert c._binary_packet.packet_type == packet.BINARY_EVENT
  792. _run(c._handle_eio_message(b'foo'))
  793. c._handle_event.mock.assert_called_with(None, None, b'foo')
  794. _run(
  795. c._handle_eio_message(
  796. '62-/foo,{"1":{"_placeholder":true,"num":1},'
  797. '"2":{"_placeholder":true,"num":0}}'
  798. )
  799. )
  800. assert c._binary_packet.packet_type == packet.BINARY_ACK
  801. _run(c._handle_eio_message(b'bar'))
  802. _run(c._handle_eio_message(b'foo'))
  803. c._handle_ack.mock.assert_called_with(
  804. '/foo', None, {'1': b'foo', '2': b'bar'}
  805. )
  806. with pytest.raises(ValueError):
  807. _run(c._handle_eio_message('9'))
  808. def test_eio_disconnect(self):
  809. c = asyncio_client.AsyncClient()
  810. c.connected = True
  811. c._trigger_event = AsyncMock()
  812. c.sid = 'foo'
  813. c.eio.state = 'connected'
  814. _run(c._handle_eio_disconnect())
  815. c._trigger_event.mock.assert_called_once_with(
  816. 'disconnect', namespace='/'
  817. )
  818. assert c.sid is None
  819. assert not c.connected
  820. def test_eio_disconnect_namespaces(self):
  821. c = asyncio_client.AsyncClient()
  822. c.connected = True
  823. c.namespaces = ['/foo', '/bar']
  824. c._trigger_event = AsyncMock()
  825. c.sid = 'foo'
  826. c.eio.state = 'connected'
  827. _run(c._handle_eio_disconnect())
  828. c._trigger_event.mock.assert_any_call('disconnect', namespace='/foo')
  829. c._trigger_event.mock.assert_any_call('disconnect', namespace='/bar')
  830. c._trigger_event.mock.assert_any_call('disconnect', namespace='/')
  831. assert c.sid is None
  832. assert not c.connected
  833. def test_eio_disconnect_reconnect(self):
  834. c = asyncio_client.AsyncClient(reconnection=True)
  835. c.start_background_task = mock.MagicMock()
  836. c.eio.state = 'connected'
  837. _run(c._handle_eio_disconnect())
  838. c.start_background_task.assert_called_once_with(c._handle_reconnect)
  839. def test_eio_disconnect_self_disconnect(self):
  840. c = asyncio_client.AsyncClient(reconnection=True)
  841. c.start_background_task = mock.MagicMock()
  842. c.eio.state = 'disconnected'
  843. _run(c._handle_eio_disconnect())
  844. c.start_background_task.assert_not_called()
  845. def test_eio_disconnect_no_reconnect(self):
  846. c = asyncio_client.AsyncClient(reconnection=False)
  847. c.start_background_task = mock.MagicMock()
  848. c.eio.state = 'connected'
  849. _run(c._handle_eio_disconnect())
  850. c.start_background_task.assert_not_called()