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.

1143 lines
46 KiB

import asyncio
import logging
from unittest import mock
from datetime import datetime, timezone, timedelta
from engineio import json
from engineio import packet as eio_packet
import pytest
from socketio import async_server
from socketio import async_namespace
from socketio import exceptions
from socketio import namespace
from socketio import packet
from socketio.msgpack_packet import MsgPackPacket
@mock.patch('socketio.server.engineio.AsyncServer', **{
'return_value.generate_id.side_effect': [str(i) for i in range(1, 10)],
'return_value.send_packet': mock.AsyncMock()})
class TestAsyncServer:
def teardown_method(self):
# restore JSON encoder, in case a test changed it
packet.Packet.json = json
def _get_mock_manager(self):
mgr = mock.MagicMock()
mgr.can_disconnect = mock.AsyncMock()
mgr.emit = mock.AsyncMock()
mgr.enter_room = mock.AsyncMock()
mgr.leave_room = mock.AsyncMock()
mgr.close_room = mock.AsyncMock()
mgr.trigger_callback = mock.AsyncMock()
return mgr
async def test_create(self, eio):
eio.return_value.handle_request = mock.AsyncMock()
mgr = self._get_mock_manager()
s = async_server.AsyncServer(
client_manager=mgr, async_handlers=True, foo='bar'
)
await s.handle_request({})
await s.handle_request({})
eio.assert_called_once_with(**{'foo': 'bar', 'async_handlers': False})
assert s.manager == mgr
assert s.eio.on.call_count == 3
assert s.async_handlers
async def test_attach(self, eio):
s = async_server.AsyncServer()
s.attach('app', 'path')
eio.return_value.attach.assert_called_once_with('app', 'path')
async def test_on_event(self, eio):
s = async_server.AsyncServer()
@s.on('connect')
def foo():
pass
def bar(reason):
pass
s.on('disconnect', bar)
s.on('disconnect', bar, namespace='/foo')
assert s.handlers['/']['connect'] == foo
assert s.handlers['/']['disconnect'] == bar
assert s.handlers['/foo']['disconnect'] == bar
async def test_emit(self, eio):
mgr = self._get_mock_manager()
s = async_server.AsyncServer(client_manager=mgr)
await s.emit(
'my event',
{'foo': 'bar'},
to='room',
skip_sid='123',
namespace='/foo',
callback='cb',
)
s.manager.emit.assert_awaited_once_with(
'my event',
{'foo': 'bar'},
'/foo',
room='room',
skip_sid='123',
callback='cb',
ignore_queue=False,
)
await s.emit(
'my event',
{'foo': 'bar'},
room='room',
skip_sid='123',
namespace='/foo',
callback='cb',
ignore_queue=True,
)
s.manager.emit.assert_awaited_with(
'my event',
{'foo': 'bar'},
'/foo',
room='room',
skip_sid='123',
callback='cb',
ignore_queue=True,
)
async def test_emit_default_namespace(self, eio):
mgr = self._get_mock_manager()
s = async_server.AsyncServer(client_manager=mgr)
await s.emit(
'my event',
{'foo': 'bar'},
to='room',
skip_sid='123',
callback='cb',
)
s.manager.emit.assert_awaited_once_with(
'my event',
{'foo': 'bar'},
'/',
room='room',
skip_sid='123',
callback='cb',
ignore_queue=False,
)
await s.emit(
'my event',
{'foo': 'bar'},
room='room',
skip_sid='123',
callback='cb',
ignore_queue=True,
)
s.manager.emit.assert_awaited_with(
'my event',
{'foo': 'bar'},
'/',
room='room',
skip_sid='123',
callback='cb',
ignore_queue=True,
)
async def test_send(self, eio):
mgr = self._get_mock_manager()
s = async_server.AsyncServer(client_manager=mgr)
await s.send(
'foo',
to='room',
skip_sid='123',
namespace='/foo',
callback='cb',
)
s.manager.emit.assert_awaited_once_with(
'message',
'foo',
'/foo',
room='room',
skip_sid='123',
callback='cb',
ignore_queue=False,
)
await s.send(
'foo',
room='room',
skip_sid='123',
namespace='/foo',
callback='cb',
ignore_queue=True,
)
s.manager.emit.assert_awaited_with(
'message',
'foo',
'/foo',
room='room',
skip_sid='123',
callback='cb',
ignore_queue=True,
)
async def test_call(self, eio):
mgr = self._get_mock_manager()
s = async_server.AsyncServer(client_manager=mgr)
async def fake_event_wait():
s.manager.emit.await_args_list[0][1]['callback']('foo', 321)
return True
s.eio.create_event.return_value.wait = fake_event_wait
assert await s.call('foo', sid='123') == ('foo', 321)
async def test_call_with_timeout(self, eio):
mgr = self._get_mock_manager()
s = async_server.AsyncServer(client_manager=mgr)
async def fake_event_wait():
await asyncio.sleep(1)
s.eio.create_event.return_value.wait = fake_event_wait
with pytest.raises(exceptions.TimeoutError):
await s.call('foo', sid='123', timeout=0.01)
async def test_call_with_broadcast(self, eio):
s = async_server.AsyncServer()
with pytest.raises(ValueError):
await s.call('foo')
async def test_call_without_async_handlers(self, eio):
mgr = self._get_mock_manager()
s = async_server.AsyncServer(
client_manager=mgr, async_handlers=False
)
with pytest.raises(RuntimeError):
await s.call('foo', sid='123', timeout=12)
async def test_enter_room(self, eio):
mgr = self._get_mock_manager()
s = async_server.AsyncServer(client_manager=mgr)
await s.enter_room('123', 'room', namespace='/foo')
s.manager.enter_room.assert_awaited_once_with('123', '/foo', 'room')
async def test_enter_room_default_namespace(self, eio):
mgr = self._get_mock_manager()
s = async_server.AsyncServer(client_manager=mgr)
await s.enter_room('123', 'room')
s.manager.enter_room.assert_awaited_once_with('123', '/', 'room')
async def test_leave_room(self, eio):
mgr = self._get_mock_manager()
s = async_server.AsyncServer(client_manager=mgr)
await s.leave_room('123', 'room', namespace='/foo')
s.manager.leave_room.assert_awaited_once_with('123', '/foo', 'room')
async def test_leave_room_default_namespace(self, eio):
mgr = self._get_mock_manager()
s = async_server.AsyncServer(client_manager=mgr)
await s.leave_room('123', 'room')
s.manager.leave_room.assert_awaited_once_with('123', '/', 'room')
async def test_close_room(self, eio):
mgr = self._get_mock_manager()
s = async_server.AsyncServer(client_manager=mgr)
await s.close_room('room', namespace='/foo')
s.manager.close_room.assert_awaited_once_with('room', '/foo')
async def test_close_room_default_namespace(self, eio):
mgr = self._get_mock_manager()
s = async_server.AsyncServer(client_manager=mgr)
await s.close_room('room')
s.manager.close_room.assert_awaited_once_with('room', '/')
async def test_rooms(self, eio):
mgr = self._get_mock_manager()
s = async_server.AsyncServer(client_manager=mgr)
s.rooms('123', namespace='/foo')
s.manager.get_rooms.assert_called_once_with('123', '/foo')
async def test_rooms_default_namespace(self, eio):
mgr = self._get_mock_manager()
s = async_server.AsyncServer(client_manager=mgr)
s.rooms('123')
s.manager.get_rooms.assert_called_once_with('123', '/')
async def test_handle_request(self, eio):
eio.return_value.handle_request = mock.AsyncMock()
s = async_server.AsyncServer()
await s.handle_request('environ')
s.eio.handle_request.assert_awaited_once_with('environ')
async def test_send_packet(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
await s._send_packet('123', packet.Packet(
packet.EVENT, ['my event', 'my data'], namespace='/foo'))
s.eio.send.assert_awaited_once_with(
'123', '2/foo,["my event","my data"]'
)
async def test_send_eio_packet(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
await s._send_eio_packet('123', eio_packet.Packet(
eio_packet.MESSAGE, 'hello'))
assert s.eio.send_packet.await_count == 1
assert s.eio.send_packet.await_args_list[0][0][0] == '123'
pkt = s.eio.send_packet.await_args_list[0][0][1]
assert pkt.encode() == '4hello'
async def test_transport(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
s.eio.transport = mock.MagicMock(return_value='polling')
sid_foo = await s.manager.connect('123', '/foo')
assert s.transport(sid_foo, '/foo') == 'polling'
s.eio.transport.assert_called_once_with('123')
async def test_handle_connect(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
s.manager.initialize = mock.MagicMock()
handler = mock.MagicMock()
s.on('connect', handler)
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
assert s.manager.is_connected('1', '/')
handler.assert_called_once_with('1', 'environ')
s.eio.send.assert_awaited_once_with('123', '0{"sid":"1"}')
assert s.manager.initialize.call_count == 1
await s._handle_eio_connect('456', 'environ')
await s._handle_eio_message('456', '0')
assert s.manager.initialize.call_count == 1
async def test_handle_connect_with_auth(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
s.manager.initialize = mock.MagicMock()
handler = mock.MagicMock()
s.on('connect', handler)
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0{"token":"abc"}')
assert s.manager.is_connected('1', '/')
handler.assert_called_once_with('1', 'environ', {'token': 'abc'})
s.eio.send.assert_awaited_once_with('123', '0{"sid":"1"}')
assert s.manager.initialize.call_count == 1
await s._handle_eio_connect('456', 'environ')
await s._handle_eio_message('456', '0')
assert s.manager.initialize.call_count == 1
async def test_handle_connect_with_auth_none(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
s.manager.initialize = mock.MagicMock()
handler = mock.MagicMock(side_effect=[TypeError, None, None])
s.on('connect', handler)
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
assert s.manager.is_connected('1', '/')
handler.assert_called_with('1', 'environ', None)
s.eio.send.assert_awaited_once_with('123', '0{"sid":"1"}')
assert s.manager.initialize.call_count == 1
await s._handle_eio_connect('456', 'environ')
await s._handle_eio_message('456', '0')
assert s.manager.initialize.call_count == 1
async def test_handle_connect_async(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
s.manager.initialize = mock.MagicMock()
handler = mock.AsyncMock()
s.on('connect', handler)
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
assert s.manager.is_connected('1', '/')
handler.assert_awaited_once_with('1', 'environ')
s.eio.send.assert_awaited_once_with('123', '0{"sid":"1"}')
assert s.manager.initialize.call_count == 1
await s._handle_eio_connect('456', 'environ')
await s._handle_eio_message('456', '0')
assert s.manager.initialize.call_count == 1
async def test_handle_connect_with_default_implied_namespaces(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
await s._handle_eio_message('123', '0/foo,')
assert s.manager.is_connected('1', '/')
assert not s.manager.is_connected('2', '/foo')
async def test_handle_connect_with_implied_namespaces(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer(namespaces=['/foo'])
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
await s._handle_eio_message('123', '0/foo,')
assert not s.manager.is_connected('1', '/')
assert s.manager.is_connected('1', '/foo')
async def test_handle_connect_with_all_implied_namespaces(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer(namespaces='*')
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
await s._handle_eio_message('123', '0/foo,')
assert s.manager.is_connected('1', '/')
assert s.manager.is_connected('2', '/foo')
async def test_handle_connect_namespace(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
handler = mock.MagicMock()
s.on('connect', handler, namespace='/foo')
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0/foo,')
assert s.manager.is_connected('1', '/foo')
handler.assert_called_once_with('1', 'environ')
s.eio.send.assert_awaited_once_with('123', '0/foo,{"sid":"1"}')
async def test_handle_connect_always_connect(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer(always_connect=True)
s.manager.initialize = mock.MagicMock()
handler = mock.MagicMock()
s.on('connect', handler)
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
assert s.manager.is_connected('1', '/')
handler.assert_called_once_with('1', 'environ')
s.eio.send.assert_awaited_once_with('123', '0{"sid":"1"}')
assert s.manager.initialize.call_count == 1
await s._handle_eio_connect('456', 'environ')
await s._handle_eio_message('456', '0')
assert s.manager.initialize.call_count == 1
async def test_handle_connect_rejected(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
handler = mock.MagicMock(return_value=False)
s.on('connect', handler)
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
assert not s.manager.is_connected('1', '/foo')
handler.assert_called_once_with('1', 'environ')
s.eio.send.assert_awaited_once_with(
'123', '4{"message":"Connection rejected by server"}')
assert s.environ == {'123': 'environ'}
async def test_handle_connect_namespace_rejected(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
handler = mock.MagicMock(return_value=False)
s.on('connect', handler, namespace='/foo')
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0/foo,')
assert not s.manager.is_connected('1', '/foo')
handler.assert_called_once_with('1', 'environ')
s.eio.send.assert_any_await(
'123', '4/foo,{"message":"Connection rejected by server"}')
assert s.environ == {'123': 'environ'}
async def test_handle_connect_rejected_always_connect(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer(always_connect=True)
handler = mock.MagicMock(return_value=False)
s.on('connect', handler)
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
assert not s.manager.is_connected('1', '/')
handler.assert_called_once_with('1', 'environ')
s.eio.send.assert_any_await('123', '0{"sid":"1"}')
s.eio.send.assert_any_await(
'123', '1{"message":"Connection rejected by server"}')
assert s.environ == {'123': 'environ'}
async def test_handle_connect_namespace_rejected_always_connect(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer(always_connect=True)
handler = mock.MagicMock(return_value=False)
s.on('connect', handler, namespace='/foo')
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0/foo,')
assert not s.manager.is_connected('1', '/foo')
handler.assert_called_once_with('1', 'environ')
s.eio.send.assert_any_await('123', '0/foo,{"sid":"1"}')
s.eio.send.assert_any_await(
'123', '1/foo,{"message":"Connection rejected by server"}')
assert s.environ == {'123': 'environ'}
async def test_handle_connect_rejected_with_exception(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
handler = mock.MagicMock(
side_effect=exceptions.ConnectionRefusedError('fail_reason')
)
s.on('connect', handler)
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
assert not s.manager.is_connected('1', '/')
handler.assert_called_once_with('1', 'environ')
s.eio.send.assert_awaited_once_with(
'123', '4{"message":"fail_reason"}')
assert s.environ == {'123': 'environ'}
async def test_handle_connect_rejected_with_python_exception(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
handler = mock.MagicMock(
side_effect=ConnectionRefusedError()
)
s.on('connect', handler)
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
assert not s.manager.is_connected('1', '/')
handler.assert_called_once_with('1', 'environ')
s.eio.send.assert_awaited_once_with(
'123', '4{"message":"Connection refused by server"}')
assert s.environ == {'123': 'environ'}
async def test_handle_connect_rejected_with_empty_exception(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
handler = mock.MagicMock(
side_effect=exceptions.ConnectionRefusedError()
)
s.on('connect', handler)
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
assert not s.manager.is_connected('1', '/')
handler.assert_called_once_with('1', 'environ')
s.eio.send.assert_awaited_once_with(
'123', '4{"message":"Connection rejected by server"}')
assert s.environ == {'123': 'environ'}
async def test_handle_connect_namespace_rejected_with_exception(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
handler = mock.MagicMock(
side_effect=exceptions.ConnectionRefusedError(
'fail_reason', 1, '2')
)
s.on('connect', handler, namespace='/foo')
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0/foo,')
assert not s.manager.is_connected('1', '/foo')
handler.assert_called_once_with('1', 'environ')
s.eio.send.assert_awaited_once_with(
'123', '4/foo,{"message":"fail_reason","data":[1,"2"]}')
assert s.environ == {'123': 'environ'}
async def test_handle_connect_namespace_rejected_with_empty_exception(
self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
handler = mock.MagicMock(
side_effect=exceptions.ConnectionRefusedError()
)
s.on('connect', handler, namespace='/foo')
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0/foo,')
assert not s.manager.is_connected('1', '/foo')
handler.assert_called_once_with('1', 'environ')
s.eio.send.assert_awaited_once_with(
'123', '4/foo,{"message":"Connection rejected by server"}')
assert s.environ == {'123': 'environ'}
async def test_handle_disconnect(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
s.manager.disconnect = mock.AsyncMock()
handler = mock.MagicMock()
s.on('disconnect', handler)
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
await s._handle_eio_disconnect('123', 'foo')
handler.assert_called_once_with('1', 'foo')
s.manager.disconnect.assert_awaited_once_with(
'1', '/', ignore_queue=True)
assert s.environ == {}
async def test_handle_legacy_disconnect(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
s.manager.disconnect = mock.AsyncMock()
handler = mock.MagicMock(side_effect=[TypeError, None])
s.on('disconnect', handler)
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
await s._handle_eio_disconnect('123', 'foo')
handler.assert_called_with('1')
s.manager.disconnect.assert_awaited_once_with(
'1', '/', ignore_queue=True)
assert s.environ == {}
async def test_handle_legacy_disconnect_async(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
s.manager.disconnect = mock.AsyncMock()
handler = mock.AsyncMock(side_effect=[TypeError, None])
s.on('disconnect', handler)
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
await s._handle_eio_disconnect('123', 'foo')
handler.assert_awaited_with('1')
s.manager.disconnect.assert_awaited_once_with(
'1', '/', ignore_queue=True)
assert s.environ == {}
async def test_handle_disconnect_namespace(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
handler = mock.MagicMock()
s.on('disconnect', handler)
handler_namespace = mock.MagicMock()
s.on('disconnect', handler_namespace, namespace='/foo')
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0/foo,')
await s._handle_eio_disconnect('123', 'foo')
handler.assert_not_called()
handler_namespace.assert_called_once_with('1', 'foo')
assert s.environ == {}
async def test_handle_disconnect_only_namespace(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
handler = mock.MagicMock()
s.on('disconnect', handler)
handler_namespace = mock.MagicMock()
s.on('disconnect', handler_namespace, namespace='/foo')
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0/foo,')
await s._handle_eio_message('123', '1/foo,')
assert handler.call_count == 0
handler_namespace.assert_called_once_with(
'1', s.reason.CLIENT_DISCONNECT)
assert s.environ == {'123': 'environ'}
async def test_handle_disconnect_unknown_client(self, eio):
mgr = self._get_mock_manager()
s = async_server.AsyncServer(client_manager=mgr)
await s._handle_eio_disconnect('123', 'foo')
async def test_handle_event(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer(async_handlers=False)
sid = await s.manager.connect('123', '/')
handler = mock.AsyncMock()
catchall_handler = mock.AsyncMock()
s.on('msg', handler)
s.on('*', catchall_handler)
await s._handle_eio_message('123', '2["msg","a","b"]')
await s._handle_eio_message('123', '2["my message","a","b","c"]')
handler.assert_awaited_once_with(sid, 'a', 'b')
catchall_handler.assert_awaited_once_with(
'my message', sid, 'a', 'b', 'c')
async def test_handle_event_with_namespace(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer(async_handlers=False)
sid = await s.manager.connect('123', '/foo')
handler = mock.MagicMock()
catchall_handler = mock.MagicMock()
s.on('msg', handler, namespace='/foo')
s.on('*', catchall_handler, namespace='/foo')
await s._handle_eio_message('123', '2/foo,["msg","a","b"]')
await s._handle_eio_message('123', '2/foo,["my message","a","b","c"]')
handler.assert_called_once_with(sid, 'a', 'b')
catchall_handler.assert_called_once_with(
'my message', sid, 'a', 'b', 'c')
async def test_handle_event_with_catchall_namespace(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer(async_handlers=False)
sid_foo = await s.manager.connect('123', '/foo')
sid_bar = await s.manager.connect('123', '/bar')
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()
s.on('connect', connect_star_handler, namespace='*')
s.on('msg', msg_foo_handler, namespace='/foo')
s.on('msg', msg_star_handler, namespace='*')
s.on('*', star_foo_handler, namespace='/foo')
s.on('*', star_star_handler, namespace='*')
await s._trigger_event('connect', '/bar', sid_bar)
await s._handle_eio_message('123', '2/foo,["msg","a","b"]')
await s._handle_eio_message('123', '2/bar,["msg","a","b"]')
await s._handle_eio_message('123', '2/foo,["my message","a","b","c"]')
await s._handle_eio_message('123', '2/bar,["my message","a","b","c"]')
await s._trigger_event('disconnect', '/bar', sid_bar,
s.reason.CLIENT_DISCONNECT)
connect_star_handler.assert_called_once_with('/bar', sid_bar)
msg_foo_handler.assert_called_once_with(sid_foo, 'a', 'b')
msg_star_handler.assert_called_once_with('/bar', sid_bar, 'a', 'b')
star_foo_handler.assert_called_once_with(
'my message', sid_foo, 'a', 'b', 'c')
star_star_handler.assert_called_once_with(
'my message', '/bar', sid_bar, 'a', 'b', 'c')
async def test_handle_event_with_disconnected_namespace(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer(async_handlers=False)
await s.manager.connect('123', '/foo')
handler = mock.MagicMock()
s.on('my message', handler, namespace='/bar')
await s._handle_eio_message('123', '2/bar,["my message","a","b","c"]')
handler.assert_not_called()
async def test_handle_event_binary(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer(async_handlers=False)
sid = await s.manager.connect('123', '/')
handler = mock.MagicMock()
s.on('my message', handler)
await s._handle_eio_message(
'123',
'52-["my message","a",'
'{"_placeholder":true,"num":1},'
'{"_placeholder":true,"num":0}]',
)
await s._handle_eio_message('123', b'foo')
await s._handle_eio_message('123', b'bar')
handler.assert_called_once_with(sid, 'a', b'bar', b'foo')
async def test_handle_event_binary_ack(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer(async_handlers=False)
s.manager.trigger_callback = mock.AsyncMock()
sid = await s.manager.connect('123', '/')
await s._handle_eio_message(
'123',
'61-321["my message","a",' '{"_placeholder":true,"num":0}]',
)
await s._handle_eio_message('123', b'foo')
s.manager.trigger_callback.assert_awaited_once_with(
sid, 321, ['my message', 'a', b'foo']
)
async def test_handle_event_binary_from_unknown(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer(async_handlers=False)
await s.manager.connect('123', '/')
handler = mock.MagicMock()
s.on('my message', handler)
with pytest.raises(ValueError):
await s._handle_eio_message(
'999',
'52-["my message","a",'
'{"_placeholder":true,"num":1},'
'{"_placeholder":true,"num":0}]',
)
async def test_handle_event_with_ack(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer(async_handlers=False)
sid = await s.manager.connect('123', '/')
handler = mock.MagicMock(return_value='foo')
s.on('my message', handler)
await s._handle_eio_message('123', '21000["my message","foo"]')
handler.assert_called_once_with(sid, 'foo')
s.eio.send.assert_awaited_once_with(
'123', '31000["foo"]'
)
async def test_handle_unknown_event_with_ack(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer(async_handlers=False)
await s.manager.connect('123', '/')
handler = mock.MagicMock(return_value='foo')
s.on('my message', handler)
await s._handle_eio_message('123', '21000["another message","foo"]')
s.eio.send.assert_not_awaited()
async def test_handle_event_with_ack_none(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer(async_handlers=False)
sid = await s.manager.connect('123', '/')
handler = mock.MagicMock(return_value=None)
s.on('my message', handler)
await s._handle_eio_message('123', '21000["my message","foo"]')
handler.assert_called_once_with(sid, 'foo')
s.eio.send.assert_awaited_once_with('123', '31000[]')
async def test_handle_event_with_ack_tuple(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer(async_handlers=False)
sid = await s.manager.connect('123', '/')
handler = mock.MagicMock(return_value=(1, '2', True))
s.on('my message', handler)
await s._handle_eio_message('123', '21000["my message","a","b","c"]')
handler.assert_called_once_with(sid, 'a', 'b', 'c')
s.eio.send.assert_awaited_once_with(
'123', '31000[1,"2",true]'
)
async def test_handle_event_with_ack_list(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer(async_handlers=False)
sid = await s.manager.connect('123', '/')
handler = mock.MagicMock(return_value=[1, '2', True])
s.on('my message', handler)
await s._handle_eio_message('123', '21000["my message","a","b","c"]')
handler.assert_called_once_with(sid, 'a', 'b', 'c')
s.eio.send.assert_awaited_once_with(
'123', '31000[[1,"2",true]]'
)
async def test_handle_event_with_ack_binary(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer(async_handlers=False)
sid = await s.manager.connect('123', '/')
handler = mock.MagicMock(return_value=b'foo')
s.on('my message', handler)
await s._handle_eio_message('123', '21000["my message","foo"]')
handler.assert_any_call(sid, 'foo')
async def test_handle_error_packet(self, eio):
s = async_server.AsyncServer()
with pytest.raises(ValueError):
await s._handle_eio_message('123', '4')
async def test_handle_invalid_packet(self, eio):
s = async_server.AsyncServer()
with pytest.raises(ValueError):
await s._handle_eio_message('123', '9')
async def test_send_with_ack(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
s.handlers['/'] = {}
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
cb = mock.MagicMock()
id1 = s.manager._generate_ack_id('1', cb)
id2 = s.manager._generate_ack_id('1', cb)
await s._send_packet('123', packet.Packet(
packet.EVENT, ['my event', 'foo'], id=id1))
await s._send_packet('123', packet.Packet(
packet.EVENT, ['my event', 'bar'], id=id2))
await s._handle_eio_message('123', '31["foo",2]')
cb.assert_called_once_with('foo', 2)
async def test_send_with_ack_namespace(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
s.handlers['/foo'] = {}
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0/foo,')
cb = mock.MagicMock()
id = s.manager._generate_ack_id('1', cb)
await s._send_packet(
'123', packet.Packet(packet.EVENT, ['my event', 'foo'],
namespace='/foo', id=id)
)
await s._handle_eio_message('123', '3/foo,1["foo",2]')
cb.assert_called_once_with('foo', 2)
async def test_session(self, eio):
fake_session = {}
async def fake_get_session(eio_sid):
assert eio_sid == '123'
return fake_session
async def fake_save_session(eio_sid, session):
global fake_session
assert eio_sid == '123'
fake_session = session
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
s.handlers['/'] = {}
s.handlers['/ns'] = {}
s.eio.get_session = fake_get_session
s.eio.save_session = fake_save_session
async def _test():
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
await s._handle_eio_message('123', '0/ns')
sid = s.manager.sid_from_eio_sid('123', '/')
sid2 = s.manager.sid_from_eio_sid('123', '/ns')
await s.save_session(sid, {'foo': 'bar'})
async with s.session(sid) as session:
assert session == {'foo': 'bar'}
session['foo'] = 'baz'
session['bar'] = 'foo'
assert await s.get_session(sid) == {'foo': 'baz', 'bar': 'foo'}
assert fake_session == {'/': {'foo': 'baz', 'bar': 'foo'}}
async with s.session(sid2, namespace='/ns') as session:
assert session == {}
session['a'] = 'b'
assert await s.get_session(sid2, namespace='/ns') == {'a': 'b'}
assert fake_session == {
'/': {'foo': 'baz', 'bar': 'foo'},
'/ns': {'a': 'b'},
}
await _test()
async def test_disconnect(self, eio):
eio.return_value.send = mock.AsyncMock()
eio.return_value.disconnect = mock.AsyncMock()
s = async_server.AsyncServer()
s.handlers['/'] = {}
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
await s.disconnect('1')
s.eio.send.assert_any_await('123', '1')
assert not s.manager.is_connected('1', '/')
async def test_disconnect_ignore_queue(self, eio):
eio.return_value.send = mock.AsyncMock()
eio.return_value.disconnect = mock.AsyncMock()
s = async_server.AsyncServer()
s.handlers['/'] = {}
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
await s.disconnect('1', ignore_queue=True)
s.eio.send.assert_any_await('123', '1')
assert not s.manager.is_connected('1', '/')
async def test_disconnect_namespace(self, eio):
eio.return_value.send = mock.AsyncMock()
eio.return_value.disconnect = mock.AsyncMock()
s = async_server.AsyncServer()
s.handlers['/foo'] = {}
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0/foo,')
await s.disconnect('1', namespace='/foo')
s.eio.send.assert_any_await('123', '1/foo,')
assert not s.manager.is_connected('1', '/foo')
async def test_disconnect_twice(self, eio):
eio.return_value.send = mock.AsyncMock()
eio.return_value.disconnect = mock.AsyncMock()
s = async_server.AsyncServer()
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
await s.disconnect('1')
calls = s.eio.send.await_count
assert not s.manager.is_connected('1', '/')
await s.disconnect('1')
assert calls == s.eio.send.await_count
async def test_disconnect_twice_namespace(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0/foo,')
await s.disconnect('1', namespace='/foo')
calls = s.eio.send.await_count
assert not s.manager.is_connected('1', '/foo')
await s.disconnect('1', namespace='/foo')
assert calls == s.eio.send.await_count
async def test_disconnect_with_partial_binary_packet(self, eio):
eio.return_value.send = mock.AsyncMock()
eio.return_value.disconnect = mock.AsyncMock()
s = async_server.AsyncServer()
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0')
await s._handle_eio_message(
'123',
'52-["my message","a",'
'{"_placeholder":true,"num":1},'
'{"_placeholder":true,"num":0}]',
)
await s._handle_eio_message('123', b'foo')
assert s._binary_packet['123'] is not None
await s.disconnect('1')
s.eio.send.assert_any_await('123', '1')
assert '123' not in s._binary_packet
async def test_namespace_handler(self, eio):
eio.return_value.send = mock.AsyncMock()
result = {}
class MyNamespace(async_namespace.AsyncNamespace):
def on_connect(self, sid, environ):
result['result'] = (sid, environ)
async def on_disconnect(self, sid, reason):
result['result'] = ('disconnect', sid, reason)
async def on_foo(self, sid, data):
result['result'] = (sid, data)
def on_bar(self, sid):
result['result'] = 'bar'
async def on_baz(self, sid, data1, data2):
result['result'] = (data1, data2)
s = async_server.AsyncServer(async_handlers=False)
s.register_namespace(MyNamespace('/foo'))
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0/foo,')
assert result['result'] == ('1', 'environ')
await s._handle_eio_message('123', '2/foo,["foo","a"]')
assert result['result'] == ('1', 'a')
await s._handle_eio_message('123', '2/foo,["bar"]')
assert result['result'] == 'bar'
await s._handle_eio_message('123', '2/foo,["baz","a","b"]')
assert result['result'] == ('a', 'b')
await s.disconnect('1', '/foo')
assert result['result'] == ('disconnect', '1',
s.reason.SERVER_DISCONNECT)
async def test_catchall_namespace_handler(self, eio):
eio.return_value.send = mock.AsyncMock()
result = {}
class MyNamespace(async_namespace.AsyncNamespace):
def on_connect(self, ns, sid, environ):
result['result'] = (sid, ns, environ)
async def on_disconnect(self, ns, sid):
result['result'] = ('disconnect', sid, ns)
async def on_foo(self, ns, sid, data):
result['result'] = (sid, ns, data)
def on_bar(self, ns, sid):
result['result'] = 'bar' + ns
async def on_baz(self, ns, sid, data1, data2):
result['result'] = (ns, data1, data2)
s = async_server.AsyncServer(async_handlers=False, namespaces='*')
s.register_namespace(MyNamespace('*'))
await s._handle_eio_connect('123', 'environ')
await s._handle_eio_message('123', '0/foo,')
assert result['result'] == ('1', '/foo', 'environ')
await s._handle_eio_message('123', '2/foo,["foo","a"]')
assert result['result'] == ('1', '/foo', 'a')
await s._handle_eio_message('123', '2/foo,["bar"]')
assert result['result'] == 'bar/foo'
await s._handle_eio_message('123', '2/foo,["baz","a","b"]')
assert result['result'] == ('/foo', 'a', 'b')
await s.disconnect('1', '/foo')
assert result['result'] == ('disconnect', '1', '/foo')
async def test_bad_namespace_handler(self, eio):
class Dummy:
pass
class SyncNS(namespace.Namespace):
pass
s = async_server.AsyncServer()
with pytest.raises(ValueError):
s.register_namespace(123)
with pytest.raises(ValueError):
s.register_namespace(Dummy)
with pytest.raises(ValueError):
s.register_namespace(Dummy())
with pytest.raises(ValueError):
s.register_namespace(namespace.Namespace)
with pytest.raises(ValueError):
s.register_namespace(SyncNS())
async def test_logger(self, eio):
s = async_server.AsyncServer(logger=False)
assert s.logger.getEffectiveLevel() == logging.ERROR
s.logger.setLevel(logging.NOTSET)
s = async_server.AsyncServer(logger=True)
assert s.logger.getEffectiveLevel() == logging.INFO
s.logger.setLevel(logging.WARNING)
s = async_server.AsyncServer(logger=True)
assert s.logger.getEffectiveLevel() == logging.WARNING
s.logger.setLevel(logging.NOTSET)
s = async_server.AsyncServer(logger='foo')
assert s.logger == 'foo'
async def test_engineio_logger(self, eio):
async_server.AsyncServer(engineio_logger='foo')
eio.assert_called_once_with(
**{'logger': 'foo', 'async_handlers': False}
)
async def test_custom_json(self, eio):
# Warning: this test cannot run in parallel with other tests, as it
# changes the JSON encoding/decoding functions
class CustomJSON:
@staticmethod
def dumps(*args, **kwargs):
return '*** encoded ***'
@staticmethod
def loads(*args, **kwargs):
return '+++ decoded +++'
async_server.AsyncServer(json=CustomJSON)
eio.assert_called_once_with(
**{'json': CustomJSON, 'async_handlers': False}
)
pkt = packet.Packet(
packet_type=packet.EVENT,
data={'foo': 'bar'},
)
assert pkt.encode() == '2*** encoded ***'
pkt2 = packet.Packet(encoded_packet=pkt.encode())
assert pkt2.data == '+++ decoded +++'
# restore the default JSON module
packet.Packet.json = json
async def test_async_handlers(self, eio):
s = async_server.AsyncServer(async_handlers=True)
await s.manager.connect('123', '/')
await s._handle_eio_message('123', '2["my message","a","b","c"]')
s.eio.start_background_task.assert_called_once_with(
s._handle_event_internal,
s,
'1',
'123',
['my message', 'a', 'b', 'c'],
'/',
None,
)
async def test_shutdown(self, eio):
s = async_server.AsyncServer()
s.eio.shutdown = mock.AsyncMock()
await s.shutdown()
s.eio.shutdown.assert_awaited_once_with()
async def test_start_background_task(self, eio):
s = async_server.AsyncServer()
s.start_background_task('foo', 'bar', baz='baz')
s.eio.start_background_task.assert_called_once_with(
'foo', 'bar', baz='baz'
)
async def test_sleep(self, eio):
eio.return_value.sleep = mock.AsyncMock()
s = async_server.AsyncServer()
await s.sleep(1.23)
s.eio.sleep.assert_awaited_once_with(1.23)
def test_serializer_args_with_msgpack(self, eio):
def default(o):
if isinstance(o, datetime):
return o.isoformat()
raise TypeError("Unknown type")
data = {"current": datetime.now(timezone(timedelta(0)))}
s = async_server.AsyncServer(
serializer=MsgPackPacket.configure(dumps_default=default))
p = s.packet_class(data=data)
p2 = s.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"]