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.

1408 lines
50 KiB

import logging
import time
from unittest import mock
from datetime import datetime, timezone, timedelta
from engineio import exceptions as engineio_exceptions
from engineio import json
from engineio import packet as engineio_packet
import pytest
from socketio import async_namespace
from socketio import client
from socketio import exceptions
from socketio import msgpack_packet
from socketio import namespace
from socketio import packet
from socketio.msgpack_packet import MsgPackPacket
class TestClient:
def test_is_asyncio_based(self):
c = client.Client()
assert not c.is_asyncio_based()
@mock.patch('socketio.client.Client._engineio_client_class')
def test_create(self, engineio_client_class):
c = client.Client(
reconnection=False,
reconnection_attempts=123,
reconnection_delay=5,
reconnection_delay_max=10,
randomization_factor=0.2,
handle_sigint=False,
foo='bar',
)
assert not c.reconnection
assert c.reconnection_attempts == 123
assert c.reconnection_delay == 5
assert c.reconnection_delay_max == 10
assert c.randomization_factor == 0.2
assert not c.handle_sigint
engineio_client_class().assert_called_once_with(
foo='bar', handle_sigint=False)
assert c.connection_url is None
assert c.connection_headers is None
assert c.connection_transports is None
assert c.connection_namespaces == []
assert c.socketio_path is None
assert c.sid is None
assert c.namespaces == {}
assert c.handlers == {}
assert c.namespace_handlers == {}
assert c.callbacks == {}
assert c._binary_packet is None
assert c._reconnect_task is None
assert c.packet_class == packet.Packet
def test_msgpack(self):
c = client.Client(serializer='msgpack')
assert c.packet_class == msgpack_packet.MsgPackPacket
def test_custom_serializer(self):
class CustomPacket(packet.Packet):
pass
c = client.Client(serializer=CustomPacket)
assert c.packet_class == CustomPacket
def test_custom_json(self):
client.Client()
assert packet.Packet.json == json
assert engineio_packet.Packet.json == json
client.Client(json='foo')
assert packet.Packet.json == 'foo'
assert engineio_packet.Packet.json == 'foo'
packet.Packet.json = json
def test_logger(self):
c = client.Client(logger=False)
assert c.logger.getEffectiveLevel() == logging.ERROR
c.logger.setLevel(logging.NOTSET)
c = client.Client(logger=True)
assert c.logger.getEffectiveLevel() == logging.INFO
c.logger.setLevel(logging.WARNING)
c = client.Client(logger=True)
assert c.logger.getEffectiveLevel() == logging.WARNING
c.logger.setLevel(logging.NOTSET)
my_logger = logging.Logger('foo')
c = client.Client(logger=my_logger)
assert c.logger == my_logger
@mock.patch('socketio.client.Client._engineio_client_class')
def test_engineio_logger(self, engineio_client_class):
client.Client(engineio_logger='foo')
engineio_client_class().assert_called_once_with(
handle_sigint=True, logger='foo')
def test_on_event(self):
c = client.Client()
@c.on('connect')
def foo():
pass
def bar():
pass
c.on('disconnect', bar)
c.on('disconnect', bar, namespace='/foo')
assert c.handlers['/']['connect'] == foo
assert c.handlers['/']['disconnect'] == bar
assert c.handlers['/foo']['disconnect'] == bar
def test_event(self):
c = client.Client()
@c.event
def connect():
pass
@c.event
def foo():
pass
@c.event
def bar():
pass
@c.event(namespace='/foo')
def disconnect():
pass
assert c.handlers['/']['connect'] == connect
assert c.handlers['/']['foo'] == foo
assert c.handlers['/']['bar'] == bar
assert c.handlers['/foo']['disconnect'] == disconnect
def test_namespace_handler(self):
class MyNamespace(namespace.ClientNamespace):
pass
c = client.Client()
n = MyNamespace('/foo')
c.register_namespace(n)
assert c.namespace_handlers['/foo'] == n
def test_namespace_handler_wrong_class(self):
class MyNamespace:
def __init__(self, n):
pass
c = client.Client()
n = MyNamespace('/foo')
with pytest.raises(ValueError):
c.register_namespace(n)
def test_namespace_handler_wrong_async(self):
class MyNamespace(async_namespace.AsyncClientNamespace):
pass
c = client.Client()
n = MyNamespace('/foo')
with pytest.raises(ValueError):
c.register_namespace(n)
def test_connect(self):
c = client.Client()
c.eio.connect = mock.MagicMock()
c.connect(
'url',
headers='headers',
auth='auth',
transports='transports',
namespaces=['/foo', '/', '/bar'],
socketio_path='path',
wait=False,
)
assert c.connection_url == 'url'
assert c.connection_headers == 'headers'
assert c.connection_auth == 'auth'
assert c.connection_transports == 'transports'
assert c.connection_namespaces == ['/foo', '/', '/bar']
assert c.socketio_path == 'path'
c.eio.connect.assert_called_once_with(
'url',
headers='headers',
transports='transports',
engineio_path='path',
)
def test_connect_functions(self):
c = client.Client()
c.eio.connect = mock.MagicMock()
c.connect(
lambda: 'url',
headers=lambda: 'headers',
auth='auth',
transports='transports',
namespaces=['/foo', '/', '/bar'],
socketio_path='path',
wait=False,
)
c.eio.connect.assert_called_once_with(
'url',
headers='headers',
transports='transports',
engineio_path='path',
)
def test_connect_one_namespace(self):
c = client.Client()
c.eio.connect = mock.MagicMock()
c.connect(
'url',
headers='headers',
transports='transports',
namespaces='/foo',
socketio_path='path',
wait=False,
)
assert c.connection_url == 'url'
assert c.connection_headers == 'headers'
assert c.connection_transports == 'transports'
assert c.connection_namespaces == ['/foo']
assert c.socketio_path == 'path'
c.eio.connect.assert_called_once_with(
'url',
headers='headers',
transports='transports',
engineio_path='path',
)
def test_connect_default_namespaces(self):
c = client.Client()
c.eio.connect = mock.MagicMock()
c.on('foo', mock.MagicMock(), namespace='/foo')
c.on('bar', mock.MagicMock(), namespace='/')
c.on('baz', mock.MagicMock(), namespace='*')
c.connect(
'url',
headers='headers',
transports='transports',
socketio_path='path',
wait=False,
)
assert c.connection_url == 'url'
assert c.connection_headers == 'headers'
assert c.connection_transports == 'transports'
assert c.connection_namespaces == ['/foo', '/'] or \
c.connection_namespaces == ['/', '/foo']
assert c.socketio_path == 'path'
c.eio.connect.assert_called_once_with(
'url',
headers='headers',
transports='transports',
engineio_path='path',
)
def test_connect_no_namespaces(self):
c = client.Client()
c.eio.connect = mock.MagicMock()
c.connect(
'url',
headers='headers',
transports='transports',
socketio_path='path',
wait=False,
)
assert c.connection_url == 'url'
assert c.connection_headers == 'headers'
assert c.connection_transports == 'transports'
assert c.connection_namespaces == ['/']
assert c.socketio_path == 'path'
c.eio.connect.assert_called_once_with(
'url',
headers='headers',
transports='transports',
engineio_path='path',
)
def test_connect_error(self):
c = client.Client()
c.eio.connect = mock.MagicMock(
side_effect=engineio_exceptions.ConnectionError('foo')
)
c.on('foo', mock.MagicMock(), namespace='/foo')
c.on('bar', mock.MagicMock(), namespace='/')
with pytest.raises(exceptions.ConnectionError):
c.connect(
'url',
headers='headers',
transports='transports',
socketio_path='path',
wait=False,
)
def test_connect_twice(self):
c = client.Client()
c.eio.connect = mock.MagicMock()
c.connect(
'url',
wait=False,
)
with pytest.raises(exceptions.ConnectionError):
c.connect(
'url',
wait=False,
)
def test_connect_wait_single_namespace(self):
c = client.Client()
c.eio.connect = mock.MagicMock()
c._connect_event = mock.MagicMock()
def mock_connect(timeout):
assert timeout == 0.01
c.namespaces = {'/': '123'}
return True
c._connect_event.wait = mock_connect
c.connect(
'url',
wait=True,
wait_timeout=0.01,
)
assert c.connected is True
def test_connect_wait_two_namespaces(self):
c = client.Client()
c.eio.connect = mock.MagicMock()
c._connect_event = mock.MagicMock()
def mock_connect(timeout):
assert timeout == 0.01
if c.namespaces == {}:
c.namespaces = {'/bar': '123'}
return True
elif c.namespaces == {'/bar': '123'}:
c.namespaces = {'/bar': '123', '/foo': '456'}
return True
return False
c._connect_event.wait = mock_connect
c.connect(
'url',
namespaces=['/foo', '/bar'],
wait=True,
wait_timeout=0.01,
)
assert c.connected is True
assert c.namespaces == {'/bar': '123', '/foo': '456'}
def test_connect_wait_one_namespaces_error(self):
c = client.Client()
c.eio.connect = mock.MagicMock()
c._connect_event = mock.MagicMock()
def mock_connect(timeout):
assert timeout == 0.01
if c.failed_namespaces == []:
c.failed_namespaces = ['/foo']
return True
return False
c._connect_event.wait = mock_connect
with pytest.raises(exceptions.ConnectionError,
match='failed to connect: /foo'):
c.connect(
'url',
namespaces=['/foo'],
wait=True,
wait_timeout=0.01,
)
assert c.connected is False
assert c.namespaces == {}
assert c.failed_namespaces == ['/foo']
def test_connect_wait_three_namespaces_error(self):
c = client.Client()
c.eio.connect = mock.MagicMock()
c._connect_event = mock.MagicMock()
def mock_connect(timeout):
assert timeout == 0.01
if c.namespaces == {}:
c.namespaces = {'/bar': '123'}
return True
elif c.namespaces == {'/bar': '123'} and c.failed_namespaces == []:
c.failed_namespaces = ['/baz']
return True
elif c.failed_namespaces == ['/baz']:
c.failed_namespaces = ['/baz', '/foo']
return True
return False
c._connect_event.wait = mock_connect
with pytest.raises(exceptions.ConnectionError,
match='failed to connect: /baz, /foo'):
c.connect(
'url',
namespaces=['/foo', '/bar', '/baz'],
wait=True,
wait_timeout=0.01,
)
assert c.connected is False
assert c.namespaces == {'/bar': '123'}
assert c.failed_namespaces == ['/baz', '/foo']
def test_connect_timeout(self):
c = client.Client()
c.eio.connect = mock.MagicMock()
c.disconnect = mock.MagicMock()
with pytest.raises(exceptions.ConnectionError):
c.connect(
'url',
wait=True,
wait_timeout=0.01,
)
c.disconnect.assert_called_once_with()
def test_wait_no_reconnect(self):
c = client.Client()
c.eio.wait = mock.MagicMock()
c.sleep = mock.MagicMock()
c._reconnect_task = None
c.wait()
c.eio.wait.assert_called_once_with()
c.sleep.assert_called_once_with(1)
def test_wait_reconnect_failed(self):
c = client.Client()
c.eio.wait = mock.MagicMock()
c.sleep = mock.MagicMock()
c._reconnect_task = mock.MagicMock()
states = ['disconnected']
def fake_join():
c.eio.state = states.pop(0)
c._reconnect_task.join = fake_join
c.wait()
c.eio.wait.assert_called_once_with()
c.sleep.assert_called_once_with(1)
def test_wait_reconnect_successful(self):
c = client.Client()
c.eio.wait = mock.MagicMock()
c.sleep = mock.MagicMock()
c._reconnect_task = mock.MagicMock()
states = ['connected', 'disconnected']
def fake_join():
c.eio.state = states.pop(0)
c._reconnect_task.join = fake_join
c.wait()
assert c.eio.wait.call_count == 2
assert c.sleep.call_count == 2
def test_get_sid(self):
c = client.Client()
c.namespaces = {'/': '1', '/foo': '2'}
assert c.get_sid() == '1'
assert c.get_sid('/') == '1'
assert c.get_sid('/foo') == '2'
assert c.get_sid('/bar') is None
def test_emit_no_arguments(self):
c = client.Client()
c.namespaces = {'/': '1'}
c._send_packet = mock.MagicMock()
c.emit('foo')
expected_packet = packet.Packet(
packet.EVENT, namespace='/', data=['foo'], id=None)
assert c._send_packet.call_count == 1
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
def test_emit_one_argument(self):
c = client.Client()
c.namespaces = {'/': '1'}
c._send_packet = mock.MagicMock()
c.emit('foo', 'bar')
expected_packet = packet.Packet(
packet.EVENT,
namespace='/',
data=['foo', 'bar'],
id=None,
)
assert c._send_packet.call_count == 1
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
def test_emit_one_argument_list(self):
c = client.Client()
c.namespaces = {'/': '1'}
c._send_packet = mock.MagicMock()
c.emit('foo', ['bar', 'baz'])
expected_packet = packet.Packet(
packet.EVENT,
namespace='/',
data=['foo', ['bar', 'baz']],
id=None,
)
assert c._send_packet.call_count == 1
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
def test_emit_two_arguments(self):
c = client.Client()
c.namespaces = {'/': '1'}
c._send_packet = mock.MagicMock()
c.emit('foo', ('bar', 'baz'))
expected_packet = packet.Packet(
packet.EVENT,
namespace='/',
data=['foo', 'bar', 'baz'],
id=None,
)
assert c._send_packet.call_count == 1
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
def test_emit_namespace(self):
c = client.Client()
c.namespaces = ['/foo']
c._send_packet = mock.MagicMock()
c.emit('foo', namespace='/foo')
expected_packet = packet.Packet(
packet.EVENT, namespace='/foo', data=['foo'], id=None)
assert c._send_packet.call_count == 1
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
def test_emit_unknown_namespace(self):
c = client.Client()
c.namespaces = ['/foo']
with pytest.raises(exceptions.BadNamespaceError):
c.emit('foo', namespace='/bar')
def test_emit_with_callback(self):
c = client.Client()
c.namespaces = {'/': '1'}
c._send_packet = mock.MagicMock()
c._generate_ack_id = mock.MagicMock(return_value=123)
c.emit('foo', callback='cb')
expected_packet = packet.Packet(
packet.EVENT, namespace='/', data=['foo'], id=123)
assert c._send_packet.call_count == 1
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
c._generate_ack_id.assert_called_once_with('/', 'cb')
def test_emit_namespace_with_callback(self):
c = client.Client()
c.namespaces = {'/foo': '1'}
c._send_packet = mock.MagicMock()
c._generate_ack_id = mock.MagicMock(return_value=123)
c.emit('foo', namespace='/foo', callback='cb')
expected_packet = packet.Packet(
packet.EVENT, namespace='/foo', data=['foo'], id=123)
assert c._send_packet.call_count == 1
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
c._generate_ack_id.assert_called_once_with('/foo', 'cb')
def test_emit_binary(self):
c = client.Client()
c.namespaces = {'/': '1'}
c._send_packet = mock.MagicMock()
c.emit('foo', b'bar')
expected_packet = packet.Packet(
packet.EVENT,
namespace='/',
data=['foo', b'bar'],
id=None,
)
assert c._send_packet.call_count == 1
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
def test_emit_not_binary(self):
c = client.Client()
c.namespaces = {'/': '1'}
c._send_packet = mock.MagicMock()
c.emit('foo', 'bar')
expected_packet = packet.Packet(
packet.EVENT,
namespace='/',
data=['foo', 'bar'],
id=None,
)
assert c._send_packet.call_count == 1
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
def test_send(self):
c = client.Client()
c.emit = mock.MagicMock()
c.send('data', 'namespace', 'callback')
c.emit.assert_called_once_with(
'message', data='data', namespace='namespace', callback='callback'
)
def test_send_with_defaults(self):
c = client.Client()
c.emit = mock.MagicMock()
c.send('data')
c.emit.assert_called_once_with(
'message', data='data', namespace=None, callback=None
)
def test_call(self):
c = client.Client()
c.namespaces = {'/': '1'}
def fake_event_wait(timeout=None):
assert timeout == 60
c._generate_ack_id.call_args_list[0][0][1]('foo', 321)
return True
c._send_packet = mock.MagicMock()
c._generate_ack_id = mock.MagicMock(return_value=123)
c.eio = mock.MagicMock()
c.eio.create_event.return_value.wait = fake_event_wait
assert c.call('foo') == ('foo', 321)
expected_packet = packet.Packet(
packet.EVENT, namespace='/', data=['foo'], id=123)
assert c._send_packet.call_count == 1
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
def test_call_with_timeout(self):
c = client.Client()
c.namespaces = {'/': '1'}
def fake_event_wait(timeout=None):
assert timeout == 12
return False
c._send_packet = mock.MagicMock()
c._generate_ack_id = mock.MagicMock(return_value=123)
c.eio = mock.MagicMock()
c.eio.create_event.return_value.wait = fake_event_wait
with pytest.raises(exceptions.TimeoutError):
c.call('foo', timeout=12)
expected_packet = packet.Packet(
packet.EVENT, namespace='/', data=['foo'], id=123)
assert c._send_packet.call_count == 1
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
def test_disconnect(self):
c = client.Client()
c.connected = True
c.namespaces = {'/': '1'}
c._trigger_event = mock.MagicMock()
c._send_packet = mock.MagicMock()
c.eio = mock.MagicMock()
c.eio.state = 'connected'
c.disconnect()
assert c.connected
assert c._trigger_event.call_count == 0
assert c._send_packet.call_count == 1
expected_packet = packet.Packet(packet.DISCONNECT, namespace='/')
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
c.eio.disconnect.assert_called_once_with()
def test_disconnect_namespaces(self):
c = client.Client()
c.connected = True
c.namespaces = {'/foo': '1', '/bar': '2'}
c._trigger_event = mock.MagicMock()
c._send_packet = mock.MagicMock()
c.eio = mock.MagicMock()
c.eio.state = 'connected'
c.disconnect()
assert c._trigger_event.call_count == 0
assert c._send_packet.call_count == 2
expected_packet = packet.Packet(packet.DISCONNECT, namespace='/foo')
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
expected_packet = packet.Packet(packet.DISCONNECT, namespace='/bar')
assert (
c._send_packet.call_args_list[1][0][0].encode()
== expected_packet.encode()
)
def test_transport(self):
c = client.Client()
c.eio.transport = mock.MagicMock(return_value='foo')
assert c.transport() == 'foo'
c.eio.transport.assert_called_once_with()
def test_start_background_task(self):
c = client.Client()
c.eio.start_background_task = mock.MagicMock(return_value='foo')
assert c.start_background_task('foo', 'bar', baz='baz') == 'foo'
c.eio.start_background_task.assert_called_once_with(
'foo', 'bar', baz='baz'
)
def test_sleep(self):
c = client.Client()
c.eio.sleep = mock.MagicMock()
c.sleep(1.23)
c.eio.sleep.assert_called_once_with(1.23)
def test_send_packet(self):
c = client.Client()
c.eio.send = mock.MagicMock()
c._send_packet(packet.Packet(packet.EVENT, 'foo'))
c.eio.send.assert_called_once_with('2"foo"')
def test_send_packet_binary(self):
c = client.Client()
c.eio.send = mock.MagicMock()
c._send_packet(packet.Packet(packet.EVENT, b'foo'))
assert c.eio.send.call_args_list == [
mock.call('51-{"_placeholder":true,"num":0}'),
mock.call(b'foo'),
] or c.eio.send.call_args_list == [
mock.call('51-{"num":0,"_placeholder":true}'),
mock.call(b'foo'),
]
def test_send_packet_default_binary(self):
c = client.Client()
c.eio.send = mock.MagicMock()
c._send_packet(packet.Packet(packet.EVENT, 'foo'))
c.eio.send.assert_called_once_with('2"foo"')
def test_generate_ack_id(self):
c = client.Client()
assert c._generate_ack_id('/', 'cb') == 1
assert c._generate_ack_id('/', 'cb') == 2
assert c._generate_ack_id('/', 'cb') == 3
assert c._generate_ack_id('/foo', 'cb') == 1
assert c._generate_ack_id('/bar', 'cb') == 1
assert c._generate_ack_id('/', 'cb') == 4
assert c._generate_ack_id('/bar', 'cb') == 2
def test_handle_connect(self):
c = client.Client()
c._connect_event = mock.MagicMock()
c._trigger_event = mock.MagicMock()
c._send_packet = mock.MagicMock()
c._handle_connect('/', {'sid': '123'})
assert c.namespaces == {'/': '123'}
c._connect_event.set.assert_called_once_with()
c._trigger_event.assert_called_once_with('connect', namespace='/')
c._send_packet.assert_not_called()
def test_handle_connect_with_namespaces(self):
c = client.Client()
c.namespaces = {'/foo': '1', '/bar': '2'}
c._connect_event = mock.MagicMock()
c._trigger_event = mock.MagicMock()
c._send_packet = mock.MagicMock()
c._handle_connect('/', {'sid': '3'})
c._connect_event.set.assert_called_once_with()
c._trigger_event.assert_called_once_with('connect', namespace='/')
assert c.namespaces == {'/': '3', '/foo': '1', '/bar': '2'}
def test_handle_connect_namespace(self):
c = client.Client()
c.namespaces = {'/foo': '1'}
c._connect_event = mock.MagicMock()
c._trigger_event = mock.MagicMock()
c._send_packet = mock.MagicMock()
c._handle_connect('/foo', {'sid': '123'})
c._handle_connect('/bar', {'sid': '2'})
assert c._trigger_event.call_count == 1
c._connect_event.set.assert_called_once_with()
c._trigger_event.assert_called_once_with('connect', namespace='/bar')
assert c.namespaces == {'/foo': '1', '/bar': '2'}
def test_handle_disconnect(self):
c = client.Client()
c.namespace = {'/': '1'}
c.connected = True
c._trigger_event = mock.MagicMock()
c._handle_disconnect('/')
c._trigger_event.assert_any_call('disconnect', '/',
c.reason.SERVER_DISCONNECT)
c._trigger_event.assert_any_call('__disconnect_final', '/')
assert not c.connected
c._handle_disconnect('/')
assert c._trigger_event.call_count == 2
def test_handle_disconnect_namespace(self):
c = client.Client()
c.connected = True
c.namespaces = {'/foo': '1', '/bar': '2'}
c._trigger_event = mock.MagicMock()
c._handle_disconnect('/foo')
c._trigger_event.assert_any_call('disconnect', '/foo',
c.reason.SERVER_DISCONNECT)
c._trigger_event.assert_any_call('__disconnect_final', '/foo')
assert c.namespaces == {'/bar': '2'}
assert c.connected
c._handle_disconnect('/bar')
c._trigger_event.assert_any_call('disconnect', '/bar',
c.reason.SERVER_DISCONNECT)
c._trigger_event.assert_any_call('__disconnect_final', '/bar')
assert c.namespaces == {}
assert not c.connected
def test_handle_disconnect_unknown_namespace(self):
c = client.Client()
c.connected = True
c.namespaces = {'/foo': '1', '/bar': '2'}
c._trigger_event = mock.MagicMock()
c._handle_disconnect('/baz')
c._trigger_event.assert_any_call('disconnect', '/baz',
c.reason.SERVER_DISCONNECT)
c._trigger_event.assert_any_call('__disconnect_final', '/baz')
assert c.namespaces == {'/foo': '1', '/bar': '2'}
assert c.connected
def test_handle_disconnect_default_namespace(self):
c = client.Client()
c.connected = True
c.namespaces = {'/foo': '1', '/bar': '2'}
c._trigger_event = mock.MagicMock()
c._handle_disconnect('/')
c._trigger_event.assert_any_call('disconnect', '/',
c.reason.SERVER_DISCONNECT)
c._trigger_event.assert_any_call('__disconnect_final', '/')
assert c.namespaces == {'/foo': '1', '/bar': '2'}
assert c.connected
def test_handle_event(self):
c = client.Client()
c._trigger_event = mock.MagicMock()
c._handle_event('/', None, ['foo', ('bar', 'baz')])
c._trigger_event.assert_called_once_with('foo', '/', ('bar', 'baz'))
def test_handle_event_with_id_no_arguments(self):
c = client.Client()
c._trigger_event = mock.MagicMock(return_value=None)
c._send_packet = mock.MagicMock()
c._handle_event('/', 123, ['foo', ('bar', 'baz')])
c._trigger_event.assert_called_once_with('foo', '/', ('bar', 'baz'))
assert c._send_packet.call_count == 1
expected_packet = packet.Packet(
packet.ACK, namespace='/', id=123, data=[])
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
def test_handle_event_with_id_one_argument(self):
c = client.Client()
c._trigger_event = mock.MagicMock(return_value='ret')
c._send_packet = mock.MagicMock()
c._handle_event('/', 123, ['foo', ('bar', 'baz')])
c._trigger_event.assert_called_once_with('foo', '/', ('bar', 'baz'))
assert c._send_packet.call_count == 1
expected_packet = packet.Packet(
packet.ACK, namespace='/', id=123, data=['ret'])
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
def test_handle_event_with_id_one_list_argument(self):
c = client.Client()
c._trigger_event = mock.MagicMock(return_value=['a', 'b'])
c._send_packet = mock.MagicMock()
c._handle_event('/', 123, ['foo', ('bar', 'baz')])
c._trigger_event.assert_called_once_with('foo', '/', ('bar', 'baz'))
assert c._send_packet.call_count == 1
expected_packet = packet.Packet(
packet.ACK, namespace='/', id=123, data=[['a', 'b']])
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
def test_handle_event_with_id_two_arguments(self):
c = client.Client()
c._trigger_event = mock.MagicMock(return_value=('a', 'b'))
c._send_packet = mock.MagicMock()
c._handle_event('/', 123, ['foo', ('bar', 'baz')])
c._trigger_event.assert_called_once_with('foo', '/', ('bar', 'baz'))
assert c._send_packet.call_count == 1
expected_packet = packet.Packet(
packet.ACK, namespace='/', id=123, data=['a', 'b'])
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
def test_handle_ack(self):
c = client.Client()
mock_cb = mock.MagicMock()
c.callbacks['/foo'] = {123: mock_cb}
c._handle_ack('/foo', 123, ['bar', 'baz'])
mock_cb.assert_called_once_with('bar', 'baz')
assert 123 not in c.callbacks['/foo']
def test_handle_ack_not_found(self):
c = client.Client()
mock_cb = mock.MagicMock()
c.callbacks['/foo'] = {123: mock_cb}
c._handle_ack('/foo', 124, ['bar', 'baz'])
mock_cb.assert_not_called()
assert 123 in c.callbacks['/foo']
def test_handle_error(self):
c = client.Client()
c.connected = True
c.namespaces = {'/foo': '1', '/bar': '2'}
c._connect_event = mock.MagicMock()
c._trigger_event = mock.MagicMock()
c._handle_error('/', 'error')
assert c.namespaces == {}
assert not c.connected
c._connect_event.set.assert_called_once_with()
c._trigger_event.assert_called_once_with('connect_error', '/', 'error')
def test_handle_error_with_no_arguments(self):
c = client.Client()
c.connected = True
c.namespaces = {'/foo': '1', '/bar': '2'}
c._connect_event = mock.MagicMock()
c._trigger_event = mock.MagicMock()
c._handle_error('/', None)
assert c.namespaces == {}
assert not c.connected
c._connect_event.set.assert_called_once_with()
c._trigger_event.assert_called_once_with('connect_error', '/')
def test_handle_error_namespace(self):
c = client.Client()
c.connected = True
c.namespaces = {'/foo': '1', '/bar': '2'}
c._connect_event = mock.MagicMock()
c._trigger_event = mock.MagicMock()
c._handle_error('/bar', ['error', 'message'])
assert c.namespaces == {'/foo': '1'}
assert c.connected
c._connect_event.set.assert_called_once_with()
c._trigger_event.assert_called_once_with(
'connect_error', '/bar', 'error', 'message'
)
def test_handle_error_namespace_with_no_arguments(self):
c = client.Client()
c.connected = True
c.namespaces = {'/foo': '1', '/bar': '2'}
c._connect_event = mock.MagicMock()
c._trigger_event = mock.MagicMock()
c._handle_error('/bar', None)
assert c.namespaces == {'/foo': '1'}
assert c.connected
c._connect_event.set.assert_called_once_with()
c._trigger_event.assert_called_once_with('connect_error', '/bar')
def test_handle_error_unknown_namespace(self):
c = client.Client()
c.connected = True
c.namespaces = {'/foo': '1', '/bar': '2'}
c._connect_event = mock.MagicMock()
c._handle_error('/baz', 'error')
assert c.namespaces == {'/foo': '1', '/bar': '2'}
assert c.connected
c._connect_event.set.assert_called_once_with()
def test_trigger_event(self):
c = client.Client()
handler = mock.MagicMock()
catchall_handler = mock.MagicMock()
c.on('foo', handler)
c.on('*', catchall_handler)
c._trigger_event('foo', '/', 1, '2')
c._trigger_event('bar', '/', 1, '2', 3)
c._trigger_event('connect', '/') # should not trigger
handler.assert_called_once_with(1, '2')
catchall_handler.assert_called_once_with('bar', 1, '2', 3)
def test_trigger_event_namespace(self):
c = client.Client()
handler = mock.MagicMock()
catchall_handler = mock.MagicMock()
c.on('foo', handler, namespace='/bar')
c.on('*', catchall_handler, namespace='/bar')
c._trigger_event('foo', '/bar', 1, '2')
c._trigger_event('bar', '/bar', 1, '2', 3)
handler.assert_called_once_with(1, '2')
catchall_handler.assert_called_once_with('bar', 1, '2', 3)
def test_trigger_event_with_catchall_namespace(self):
c = client.Client()
connect_star_handler = mock.MagicMock()
msg_foo_handler = mock.MagicMock()
msg_star_handler = mock.MagicMock()
star_foo_handler = mock.MagicMock()
star_star_handler = mock.MagicMock()
c.on('connect', connect_star_handler, namespace='*')
c.on('msg', msg_foo_handler, namespace='/foo')
c.on('msg', msg_star_handler, namespace='*')
c.on('*', star_foo_handler, namespace='/foo')
c.on('*', star_star_handler, namespace='*')
c._trigger_event('connect', '/bar')
c._trigger_event('msg', '/foo', 'a', 'b')
c._trigger_event('msg', '/bar', 'a', 'b')
c._trigger_event('my message', '/foo', 'a', 'b', 'c')
c._trigger_event('my message', '/bar', 'a', 'b', 'c')
c._trigger_event('disconnect', '/bar')
connect_star_handler.assert_called_once_with('/bar')
msg_foo_handler.assert_called_once_with('a', 'b')
msg_star_handler.assert_called_once_with('/bar', 'a', 'b')
star_foo_handler.assert_called_once_with(
'my message', 'a', 'b', 'c')
star_star_handler.assert_called_once_with(
'my message', '/bar', 'a', 'b', 'c')
def test_trigger_event_with_catchall_namespace_handler(self):
result = {}
class MyNamespace(namespace.ClientNamespace):
def on_connect(self, ns):
result['result'] = (ns,)
def on_disconnect(self, ns, reason):
result['result'] = ('disconnect', ns, reason)
def on_foo(self, ns, data):
result['result'] = (ns, data)
def on_bar(self, ns):
result['result'] = 'bar' + ns
def on_baz(self, ns, data1, data2):
result['result'] = (ns, data1, data2)
c = client.Client()
c.register_namespace(MyNamespace('*'))
c._trigger_event('connect', '/foo')
assert result['result'] == ('/foo',)
c._trigger_event('foo', '/foo', 'a')
assert result['result'] == ('/foo', 'a')
c._trigger_event('bar', '/foo')
assert result['result'] == 'bar/foo'
c._trigger_event('baz', '/foo', 'a', 'b')
assert result['result'] == ('/foo', 'a', 'b')
c._trigger_event('disconnect', '/foo', 'bar')
assert result['result'] == ('disconnect', '/foo', 'bar')
def test_trigger_event_class_namespace(self):
c = client.Client()
result = []
class MyNamespace(namespace.ClientNamespace):
def on_foo(self, a, b):
result.append(a)
result.append(b)
c.register_namespace(MyNamespace('/'))
c._trigger_event('foo', '/', 1, '2')
assert result == [1, '2']
def test_trigger_event_unknown_namespace(self):
c = client.Client()
result = []
class MyNamespace(namespace.ClientNamespace):
def on_foo(self, a, b):
result.append(a)
result.append(b)
c.register_namespace(MyNamespace('/'))
c._trigger_event('foo', '/bar', 1, '2')
assert result == []
@mock.patch('socketio.client.random.random', side_effect=[1, 0, 0.5])
def test_handle_reconnect(self, random):
c = client.Client()
c._reconnect_task = 'foo'
c._reconnect_abort = c.eio.create_event()
c._reconnect_abort.wait = mock.MagicMock(return_value=False)
c.connect = mock.MagicMock(
side_effect=[ValueError, exceptions.ConnectionError, None]
)
c._handle_reconnect()
assert c._reconnect_abort.wait.call_count == 3
assert c._reconnect_abort.wait.call_args_list == [
mock.call(1.5),
mock.call(1.5),
mock.call(4.0),
]
assert c._reconnect_task is None
@mock.patch('socketio.client.random.random', side_effect=[1, 0, 0.5])
def test_handle_reconnect_max_delay(self, random):
c = client.Client(reconnection_delay_max=3)
c._reconnect_task = 'foo'
c._reconnect_abort = c.eio.create_event()
c._reconnect_abort.wait = mock.MagicMock(return_value=False)
c.connect = mock.MagicMock(
side_effect=[ValueError, exceptions.ConnectionError, None]
)
c._handle_reconnect()
assert c._reconnect_abort.wait.call_count == 3
assert c._reconnect_abort.wait.call_args_list == [
mock.call(1.5),
mock.call(1.5),
mock.call(3.0),
]
assert c._reconnect_task is None
@mock.patch('socketio.client.random.random', side_effect=[1, 0, 0.5])
def test_handle_reconnect_max_attempts(self, random):
c = client.Client(reconnection_attempts=2)
c.connection_namespaces = ['/']
c._reconnect_task = 'foo'
c._reconnect_abort = c.eio.create_event()
c._reconnect_abort.wait = mock.MagicMock(return_value=False)
c._trigger_event = mock.MagicMock()
c.connect = mock.MagicMock(
side_effect=[ValueError, exceptions.ConnectionError, None]
)
c._handle_reconnect()
assert c._reconnect_abort.wait.call_count == 2
assert c._reconnect_abort.wait.call_args_list == [
mock.call(1.5),
mock.call(1.5),
]
assert c._reconnect_task == 'foo'
c._trigger_event.assert_called_once_with('__disconnect_final',
namespace='/')
@mock.patch('socketio.client.random.random', side_effect=[1, 0, 0.5])
def test_handle_reconnect_aborted(self, random):
c = client.Client()
c.connection_namespaces = ['/']
c._reconnect_task = 'foo'
c._reconnect_abort = c.eio.create_event()
c._reconnect_abort.wait = mock.MagicMock(side_effect=[False, True])
c._trigger_event = mock.MagicMock()
c.connect = mock.MagicMock(side_effect=exceptions.ConnectionError)
c._handle_reconnect()
assert c._reconnect_abort.wait.call_count == 2
assert c._reconnect_abort.wait.call_args_list == [
mock.call(1.5),
mock.call(1.5),
]
assert c._reconnect_task == 'foo'
c._trigger_event.assert_called_once_with('__disconnect_final',
namespace='/')
def test_shutdown_disconnect(self):
c = client.Client()
c.connected = True
c.namespaces = {'/': '1'}
c._trigger_event = mock.MagicMock()
c._send_packet = mock.MagicMock()
c.eio = mock.MagicMock()
c.eio.state = 'connected'
c.shutdown()
assert c._trigger_event.call_count == 0
assert c._send_packet.call_count == 1
expected_packet = packet.Packet(packet.DISCONNECT, namespace='/')
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
c.eio.disconnect.assert_called_once_with()
def test_shutdown_disconnect_namespaces(self):
c = client.Client()
c.connected = True
c.namespaces = {'/foo': '1', '/bar': '2'}
c._trigger_event = mock.MagicMock()
c._send_packet = mock.MagicMock()
c.eio = mock.MagicMock()
c.eio.state = 'connected'
c.shutdown()
assert c._trigger_event.call_count == 0
assert c._send_packet.call_count == 2
expected_packet = packet.Packet(packet.DISCONNECT, namespace='/foo')
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
expected_packet = packet.Packet(packet.DISCONNECT, namespace='/bar')
assert (
c._send_packet.call_args_list[1][0][0].encode()
== expected_packet.encode()
)
@mock.patch('socketio.client.random.random', side_effect=[1, 0, 0.5])
def test_shutdown_reconnect(self, random):
c = client.Client()
c.connection_namespaces = ['/']
c._reconnect_task = mock.MagicMock()
c._trigger_event = mock.MagicMock()
c.connect = mock.MagicMock(side_effect=exceptions.ConnectionError)
task = c.start_background_task(c._handle_reconnect)
time.sleep(0.1)
c.shutdown()
task.join()
c._trigger_event.assert_called_once_with('__disconnect_final',
namespace='/')
c._reconnect_task.join.assert_called_once_with()
def test_handle_eio_connect(self):
c = client.Client()
c.connection_namespaces = ['/', '/foo']
c.connection_auth = 'auth'
c._send_packet = mock.MagicMock()
c.eio.sid = 'foo'
assert c.sid is None
c._handle_eio_connect()
assert c.sid == 'foo'
assert c._send_packet.call_count == 2
expected_packet = packet.Packet(
packet.CONNECT, data='auth', namespace='/')
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
expected_packet = packet.Packet(
packet.CONNECT, data='auth', namespace='/foo')
assert (
c._send_packet.call_args_list[1][0][0].encode()
== expected_packet.encode()
)
def test_handle_eio_connect_function(self):
c = client.Client()
c.connection_namespaces = ['/', '/foo']
c.connection_auth = lambda: 'auth'
c._send_packet = mock.MagicMock()
c.eio.sid = 'foo'
assert c.sid is None
c._handle_eio_connect()
assert c.sid == 'foo'
assert c._send_packet.call_count == 2
expected_packet = packet.Packet(
packet.CONNECT, data='auth', namespace='/')
assert (
c._send_packet.call_args_list[0][0][0].encode()
== expected_packet.encode()
)
expected_packet = packet.Packet(
packet.CONNECT, data='auth', namespace='/foo')
assert (
c._send_packet.call_args_list[1][0][0].encode()
== expected_packet.encode()
)
def test_handle_eio_message(self):
c = client.Client()
c._handle_connect = mock.MagicMock()
c._handle_disconnect = mock.MagicMock()
c._handle_event = mock.MagicMock()
c._handle_ack = mock.MagicMock()
c._handle_error = mock.MagicMock()
c._handle_eio_message('0{"sid":"123"}')
c._handle_connect.assert_called_with(None, {'sid': '123'})
c._handle_eio_message('0/foo,{"sid":"123"}')
c._handle_connect.assert_called_with('/foo', {'sid': '123'})
c._handle_eio_message('1')
c._handle_disconnect.assert_called_with(None)
c._handle_eio_message('1/foo')
c._handle_disconnect.assert_called_with('/foo')
c._handle_eio_message('2["foo"]')
c._handle_event.assert_called_with(None, None, ['foo'])
c._handle_eio_message('3/foo,["bar"]')
c._handle_ack.assert_called_with('/foo', None, ['bar'])
c._handle_eio_message('4')
c._handle_error.assert_called_with(None, None)
c._handle_eio_message('4"foo"')
c._handle_error.assert_called_with(None, 'foo')
c._handle_eio_message('4["foo"]')
c._handle_error.assert_called_with(None, ['foo'])
c._handle_eio_message('4/foo')
c._handle_error.assert_called_with('/foo', None)
c._handle_eio_message('4/foo,["foo","bar"]')
c._handle_error.assert_called_with('/foo', ['foo', 'bar'])
c._handle_eio_message('51-{"_placeholder":true,"num":0}')
assert c._binary_packet.packet_type == packet.BINARY_EVENT
c._handle_eio_message(b'foo')
c._handle_event.assert_called_with(None, None, b'foo')
c._handle_eio_message(
'62-/foo,{"1":{"_placeholder":true,"num":1},'
'"2":{"_placeholder":true,"num":0}}'
)
assert c._binary_packet.packet_type == packet.BINARY_ACK
c._handle_eio_message(b'bar')
c._handle_eio_message(b'foo')
c._handle_ack.assert_called_with(
'/foo', None, {'1': b'foo', '2': b'bar'}
)
with pytest.raises(ValueError):
c._handle_eio_message('9')
def test_eio_disconnect(self):
c = client.Client()
c.namespaces = {'/': '1'}
c.connected = True
c._trigger_event = mock.MagicMock()
c.start_background_task = mock.MagicMock()
c.sid = 'foo'
c.eio.state = 'connected'
c._handle_eio_disconnect('foo')
c._trigger_event.assert_called_once_with('disconnect', '/', 'foo')
assert c.sid is None
assert not c.connected
def test_eio_disconnect_namespaces(self):
c = client.Client()
c.connected = True
c.namespaces = {'/': '1', '/foo': '2', '/bar': '3'}
c._trigger_event = mock.MagicMock()
c.start_background_task = mock.MagicMock()
c.sid = 'foo'
c.eio.state = 'connected'
c._handle_eio_disconnect(c.reason.CLIENT_DISCONNECT)
c._trigger_event.assert_any_call('disconnect', '/foo',
c.reason.CLIENT_DISCONNECT)
c._trigger_event.assert_any_call('disconnect', '/bar',
c.reason.CLIENT_DISCONNECT)
c._trigger_event.assert_any_call('disconnect', '/',
c.reason.CLIENT_DISCONNECT)
assert c.sid is None
assert not c.connected
def test_eio_disconnect_reconnect(self):
c = client.Client(reconnection=True)
c.start_background_task = mock.MagicMock()
c.eio.state = 'connected'
c._handle_eio_disconnect(c.reason.CLIENT_DISCONNECT)
c.start_background_task.assert_called_once_with(c._handle_reconnect)
def test_eio_disconnect_self_disconnect(self):
c = client.Client(reconnection=True)
c.start_background_task = mock.MagicMock()
c.eio.state = 'disconnected'
c._handle_eio_disconnect(c.reason.CLIENT_DISCONNECT)
c.start_background_task.assert_not_called()
def test_eio_disconnect_no_reconnect(self):
c = client.Client(reconnection=False)
c.namespaces = {'/': '1'}
c.connected = True
c._trigger_event = mock.MagicMock()
c.start_background_task = mock.MagicMock()
c.sid = 'foo'
c.eio.state = 'connected'
c._handle_eio_disconnect(c.reason.TRANSPORT_ERROR)
c._trigger_event.assert_any_call('disconnect', '/',
c.reason.TRANSPORT_ERROR)
c._trigger_event.assert_any_call('__disconnect_final', '/')
assert c.sid is None
assert not c.connected
c.start_background_task.assert_not_called()
def test_serializer_args_with_msgpack(self):
def default(o):
if isinstance(o, datetime):
return o.isoformat()
raise TypeError("Unknown type")
data = {"current": datetime.now(timezone(timedelta(0)))}
c = client.Client(
serializer=MsgPackPacket.configure(dumps_default=default))
p = c.packet_class(data=data)
p2 = c.packet_class(encoded_packet=p.encode())
assert p.data != p2.data
assert isinstance(p2.data, dict)
assert "current" in p2.data
assert isinstance(p2.data["current"], str)
assert default(data["current"]) == p2.data["current"]