Browse Source

async namespaces, and more unit tests

pull/31/merge
Miguel Grinberg 8 years ago
parent
commit
6f41206f7d
  1. 2
      socketio/asyncio_manager.py
  2. 83
      socketio/asyncio_namespace.py
  3. 3
      socketio/asyncio_server.py
  4. 3
      socketio/namespace.py
  5. 5
      socketio/server.py
  6. 280
      tests/test_asyncio_manager.py
  7. 179
      tests/test_asyncio_namespace.py
  8. 594
      tests/test_asyncio_server.py
  9. 7
      tests/test_server.py

2
socketio/asyncio_manager.py

@ -33,7 +33,7 @@ class AsyncioManager(BaseManager):
else:
del self.callbacks[sid][namespace][id]
if callback is not None:
if asyncio.iscoroutinefunction(callback):
if asyncio.iscoroutinefunction(callback) is True:
try:
await callback(*data)
except asyncio.CancelledError: # pragma: no cover

83
socketio/asyncio_namespace.py

@ -0,0 +1,83 @@
import asyncio
from socketio import namespace
class AsyncNamespace(namespace.Namespace):
"""Base class for asyncio class-based namespaces.
A class-based namespace is a class that contains all the event handlers
for a Socket.IO namespace. The event handlers are methods of the class
with the prefix ``on_``, such as ``on_connect``, ``on_disconnect``,
``on_message``, ``on_json``, and so on. These can be regular functions or
coroutines.
:param namespace: The Socket.IO namespace to be used with all the event
handlers defined in this class. If this argument is
omitted, the default namespace is used.
"""
def is_asyncio_based(self):
return True
async def trigger_event(self, event, *args):
"""Dispatch an event to the proper handler method.
In the most common usage, this method is not overloaded by subclasses,
as it performs the routing of events to methods. However, this
method can be overriden if special dispatching rules are needed, or if
having a single method that catches all events is desired.
Note: this method is a coroutine.
"""
handler_name = 'on_' + event
if hasattr(self, handler_name):
handler = getattr(self, handler_name)
if asyncio.iscoroutinefunction(handler) is True:
try:
ret = await handler(*args)
except asyncio.CancelledError: # pragma: no cover
pass
else:
ret = handler(*args)
return ret
async def emit(self, event, data=None, room=None, skip_sid=None,
namespace=None, callback=None):
"""Emit a custom event to one or more connected clients.
The only difference with the :func:`socketio.Server.emit` method is
that when the ``namespace`` argument is not given the namespace
associated with the class is used.
Note: this method is a coroutine.
"""
return await self.server.emit(event, data=data, room=room,
skip_sid=skip_sid,
namespace=namespace or self.namespace,
callback=callback)
async def send(self, data, room=None, skip_sid=None, namespace=None,
callback=None):
"""Send a message to one or more connected clients.
The only difference with the :func:`socketio.Server.send` method is
that when the ``namespace`` argument is not given the namespace
associated with the class is used.
Note: this method is a coroutine.
"""
return await self.server.send(data, room=room, skip_sid=skip_sid,
namespace=namespace or self.namespace,
callback=callback)
async def disconnect(self, sid, namespace=None):
"""Disconnect a client.
The only difference with the :func:`socketio.Server.disconnect` method
is that when the ``namespace`` argument is not given the namespace
associated with the class is used.
Note: this method is a coroutine.
"""
return await self.server.disconnect(
sid, namespace=namespace or self.namespace)

3
socketio/asyncio_server.py

@ -305,7 +305,8 @@ class AsyncServer(server.Server):
"""Invoke an application event handler."""
# first see if we have an explicit handler for the event
if namespace in self.handlers and event in self.handlers[namespace]:
if asyncio.iscoroutinefunction(self.handlers[namespace][event]):
if asyncio.iscoroutinefunction(self.handlers[namespace][event]) \
is True:
try:
ret = await self.handlers[namespace][event](*args)
except asyncio.CancelledError: # pragma: no cover

3
socketio/namespace.py

@ -17,6 +17,9 @@ class Namespace(object):
def _set_server(self, server):
self.server = server
def is_asyncio_based(self):
return False
def trigger_event(self, event, *args):
"""Dispatch an event to the proper handler method.

5
socketio/server.py

@ -113,6 +113,9 @@ class Server(object):
self.async_mode = self.eio.async_mode
def is_asyncio_based(self):
return False
def on(self, event, handler=None, namespace=None):
"""Register an event handler.
@ -171,6 +174,8 @@ class Server(object):
"""
if not isinstance(namespace_handler, namespace.Namespace):
raise ValueError('Not a namespace instance')
if self.is_asyncio_based() != namespace_handler.is_asyncio_based():
raise ValueError('Not a valid namespace class for this server')
namespace_handler._set_server(self)
self.namespace_handlers[namespace_handler.namespace] = \
namespace_handler

280
tests/test_asyncio_manager.py

@ -0,0 +1,280 @@
import sys
import unittest
import six
if six.PY3:
from unittest import mock
else:
import mock
if sys.version_info >= (3, 5):
import asyncio
from asyncio import coroutine
from socketio import asyncio_manager
else:
# mock coroutine so that Python 2 doesn't complain
def coroutine(f):
return f
def AsyncMock(*args, **kwargs):
"""Return a mock asynchronous function."""
m = mock.MagicMock(*args, **kwargs)
@coroutine
def mock_coro(*args, **kwargs):
return m(*args, **kwargs)
mock_coro.mock = m
return mock_coro
def _run(coro):
"""Run the given coroutine."""
return asyncio.get_event_loop().run_until_complete(coro)
@unittest.skipIf(sys.version_info < (3, 5), 'only for Python 3.5+')
class TestAsyncioManager(unittest.TestCase):
def setUp(self):
mock_server = mock.MagicMock()
mock_server._emit_internal = AsyncMock()
self.bm = asyncio_manager.AsyncioManager()
self.bm.set_server(mock_server)
self.bm.initialize()
def test_connect(self):
self.bm.connect('123', '/foo')
self.assertIn(None, self.bm.rooms['/foo'])
self.assertIn('123', self.bm.rooms['/foo'])
self.assertIn('123', self.bm.rooms['/foo'][None])
self.assertIn('123', self.bm.rooms['/foo']['123'])
self.assertEqual(self.bm.rooms['/foo'], {None: {'123': True},
'123': {'123': True}})
def test_pre_disconnect(self):
self.bm.connect('123', '/foo')
self.bm.connect('456', '/foo')
self.bm.pre_disconnect('123', '/foo')
self.assertEqual(self.bm.pending_disconnect, {'/foo': ['123']})
self.assertFalse(self.bm.is_connected('123', '/foo'))
self.bm.pre_disconnect('456', '/foo')
self.assertEqual(self.bm.pending_disconnect, {'/foo': ['123', '456']})
self.assertFalse(self.bm.is_connected('456', '/foo'))
self.bm.disconnect('123', '/foo')
self.assertEqual(self.bm.pending_disconnect, {'/foo': ['456']})
self.bm.disconnect('456', '/foo')
self.assertEqual(self.bm.pending_disconnect, {})
def test_disconnect(self):
self.bm.connect('123', '/foo')
self.bm.connect('456', '/foo')
self.bm.enter_room('123', '/foo', 'bar')
self.bm.enter_room('456', '/foo', 'baz')
self.bm.disconnect('123', '/foo')
self.assertEqual(self.bm.rooms['/foo'], {None: {'456': True},
'456': {'456': True},
'baz': {'456': True}})
def test_disconnect_default_namespace(self):
self.bm.connect('123', '/')
self.bm.connect('123', '/foo')
self.bm.connect('456', '/')
self.bm.connect('456', '/foo')
self.assertTrue(self.bm.is_connected('123', '/'))
self.assertTrue(self.bm.is_connected('123', '/foo'))
self.bm.disconnect('123', '/')
self.assertFalse(self.bm.is_connected('123', '/'))
self.assertTrue(self.bm.is_connected('123', '/foo'))
self.bm.disconnect('123', '/foo')
self.assertFalse(self.bm.is_connected('123', '/foo'))
self.assertEqual(self.bm.rooms['/'], {None: {'456': True},
'456': {'456': True}})
self.assertEqual(self.bm.rooms['/foo'], {None: {'456': True},
'456': {'456': True}})
def test_disconnect_twice(self):
self.bm.connect('123', '/')
self.bm.connect('123', '/foo')
self.bm.connect('456', '/')
self.bm.connect('456', '/foo')
self.bm.disconnect('123', '/')
self.bm.disconnect('123', '/foo')
self.bm.disconnect('123', '/')
self.bm.disconnect('123', '/foo')
self.assertEqual(self.bm.rooms['/'], {None: {'456': True},
'456': {'456': True}})
self.assertEqual(self.bm.rooms['/foo'], {None: {'456': True},
'456': {'456': True}})
def test_disconnect_all(self):
self.bm.connect('123', '/foo')
self.bm.connect('456', '/foo')
self.bm.enter_room('123', '/foo', 'bar')
self.bm.enter_room('456', '/foo', 'baz')
self.bm.disconnect('123', '/foo')
self.bm.disconnect('456', '/foo')
self.assertEqual(self.bm.rooms, {})
def test_disconnect_with_callbacks(self):
self.bm.connect('123', '/')
self.bm.connect('123', '/foo')
self.bm._generate_ack_id('123', '/', 'f')
self.bm._generate_ack_id('123', '/foo', 'g')
self.bm.disconnect('123', '/foo')
self.assertNotIn('/foo', self.bm.callbacks['123'])
self.bm.disconnect('123', '/')
self.assertNotIn('123', self.bm.callbacks)
def test_trigger_sync_callback(self):
self.bm.connect('123', '/')
self.bm.connect('123', '/foo')
cb = mock.MagicMock()
id1 = self.bm._generate_ack_id('123', '/', cb)
id2 = self.bm._generate_ack_id('123', '/foo', cb)
_run(self.bm.trigger_callback('123', '/', id1, ['foo']))
_run(self.bm.trigger_callback('123', '/foo', id2, ['bar', 'baz']))
self.assertEqual(cb.call_count, 2)
cb.assert_any_call('foo')
cb.assert_any_call('bar', 'baz')
def test_trigger_async_callback(self):
self.bm.connect('123', '/')
self.bm.connect('123', '/foo')
cb = AsyncMock()
id1 = self.bm._generate_ack_id('123', '/', cb)
id2 = self.bm._generate_ack_id('123', '/foo', cb)
_run(self.bm.trigger_callback('123', '/', id1, ['foo']))
_run(self.bm.trigger_callback('123', '/foo', id2, ['bar', 'baz']))
self.assertEqual(cb.mock.call_count, 2)
cb.mock.assert_any_call('foo')
cb.mock.assert_any_call('bar', 'baz')
def test_invalid_callback(self):
self.bm.connect('123', '/')
cb = mock.MagicMock()
id = self.bm._generate_ack_id('123', '/', cb)
# these should not raise an exception
_run(self.bm.trigger_callback('124', '/', id, ['foo']))
_run(self.bm.trigger_callback('123', '/foo', id, ['foo']))
_run(self.bm.trigger_callback('123', '/', id + 1, ['foo']))
self.assertEqual(cb.mock.call_count, 0)
def test_get_namespaces(self):
self.assertEqual(list(self.bm.get_namespaces()), [])
self.bm.connect('123', '/')
self.bm.connect('123', '/foo')
namespaces = list(self.bm.get_namespaces())
self.assertEqual(len(namespaces), 2)
self.assertIn('/', namespaces)
self.assertIn('/foo', namespaces)
def test_get_participants(self):
self.bm.connect('123', '/')
self.bm.connect('456', '/')
self.bm.connect('789', '/')
self.bm.disconnect('789', '/')
self.assertNotIn('789', self.bm.rooms['/'][None])
participants = list(self.bm.get_participants('/', None))
self.assertEqual(len(participants), 2)
self.assertNotIn('789', participants)
def test_leave_invalid_room(self):
self.bm.connect('123', '/foo')
self.bm.leave_room('123', '/foo', 'baz')
self.bm.leave_room('123', '/bar', 'baz')
def test_no_room(self):
rooms = self.bm.get_rooms('123', '/foo')
self.assertEqual([], rooms)
def test_close_room(self):
self.bm.connect('123', '/foo')
self.bm.connect('456', '/foo')
self.bm.connect('789', '/foo')
self.bm.enter_room('123', '/foo', 'bar')
self.bm.enter_room('123', '/foo', 'bar')
self.bm.close_room('bar', '/foo')
self.assertNotIn('bar', self.bm.rooms['/foo'])
def test_close_invalid_room(self):
self.bm.close_room('bar', '/foo')
def test_rooms(self):
self.bm.connect('123', '/foo')
self.bm.enter_room('123', '/foo', 'bar')
r = self.bm.get_rooms('123', '/foo')
self.assertEqual(len(r), 2)
self.assertIn('123', r)
self.assertIn('bar', r)
def test_emit_to_sid(self):
self.bm.connect('123', '/foo')
self.bm.connect('456', '/foo')
_run(self.bm.emit('my event', {'foo': 'bar'}, namespace='/foo',
room='123'))
self.bm.server._emit_internal.mock.assert_called_once_with(
'123', 'my event', {'foo': 'bar'}, '/foo', None)
def test_emit_to_room(self):
self.bm.connect('123', '/foo')
self.bm.enter_room('123', '/foo', 'bar')
self.bm.connect('456', '/foo')
self.bm.enter_room('456', '/foo', 'bar')
self.bm.connect('789', '/foo')
_run(self.bm.emit('my event', {'foo': 'bar'}, namespace='/foo',
room='bar'))
self.assertEqual(self.bm.server._emit_internal.mock.call_count, 2)
self.bm.server._emit_internal.mock.assert_any_call(
'123', 'my event', {'foo': 'bar'}, '/foo', None)
self.bm.server._emit_internal.mock.assert_any_call(
'456', 'my event', {'foo': 'bar'}, '/foo', None)
def test_emit_to_all(self):
self.bm.connect('123', '/foo')
self.bm.enter_room('123', '/foo', 'bar')
self.bm.connect('456', '/foo')
self.bm.enter_room('456', '/foo', 'bar')
self.bm.connect('789', '/foo')
self.bm.connect('abc', '/bar')
_run(self.bm.emit('my event', {'foo': 'bar'}, namespace='/foo'))
self.assertEqual(self.bm.server._emit_internal.mock.call_count, 3)
self.bm.server._emit_internal.mock.assert_any_call(
'123', 'my event', {'foo': 'bar'}, '/foo', None)
self.bm.server._emit_internal.mock.assert_any_call(
'456', 'my event', {'foo': 'bar'}, '/foo', None)
self.bm.server._emit_internal.mock.assert_any_call(
'789', 'my event', {'foo': 'bar'}, '/foo', None)
def test_emit_to_all_skip_one(self):
self.bm.connect('123', '/foo')
self.bm.enter_room('123', '/foo', 'bar')
self.bm.connect('456', '/foo')
self.bm.enter_room('456', '/foo', 'bar')
self.bm.connect('789', '/foo')
self.bm.connect('abc', '/bar')
_run(self.bm.emit('my event', {'foo': 'bar'}, namespace='/foo',
skip_sid='456'))
self.assertEqual(self.bm.server._emit_internal.mock.call_count, 2)
self.bm.server._emit_internal.mock.assert_any_call(
'123', 'my event', {'foo': 'bar'}, '/foo', None)
self.bm.server._emit_internal.mock.assert_any_call(
'789', 'my event', {'foo': 'bar'}, '/foo', None)
def test_emit_with_callback(self):
self.bm.connect('123', '/foo')
self.bm._generate_ack_id = mock.MagicMock()
self.bm._generate_ack_id.return_value = 11
_run(self.bm.emit('my event', {'foo': 'bar'}, namespace='/foo',
callback='cb'))
self.bm._generate_ack_id.assert_called_once_with('123', '/foo', 'cb')
self.bm.server._emit_internal.mock.assert_called_once_with(
'123', 'my event', {'foo': 'bar'}, '/foo', 11)
def test_emit_to_invalid_room(self):
_run(self.bm.emit('my event', {'foo': 'bar'}, namespace='/',
room='123'))
def test_emit_to_invalid_namespace(self):
_run(self.bm.emit('my event', {'foo': 'bar'}, namespace='/foo'))

179
tests/test_asyncio_namespace.py

@ -0,0 +1,179 @@
import sys
import unittest
import six
if six.PY3:
from unittest import mock
else:
import mock
if sys.version_info >= (3, 5):
import asyncio
from asyncio import coroutine
from socketio import asyncio_namespace
else:
# mock coroutine so that Python 2 doesn't complain
def coroutine(f):
return f
def AsyncMock(*args, **kwargs):
"""Return a mock asynchronous function."""
m = mock.MagicMock(*args, **kwargs)
@coroutine
def mock_coro(*args, **kwargs):
return m(*args, **kwargs)
mock_coro.mock = m
return mock_coro
def _run(coro):
"""Run the given coroutine."""
return asyncio.get_event_loop().run_until_complete(coro)
@unittest.skipIf(sys.version_info < (3, 5), 'only for Python 3.5+')
class TestAsyncNamespace(unittest.TestCase):
def test_connect_event(self):
result = {}
class MyNamespace(asyncio_namespace.AsyncNamespace):
@coroutine
def on_connect(self, sid, environ):
result['result'] = (sid, environ)
ns = MyNamespace('/foo')
ns._set_server(mock.MagicMock())
_run(ns.trigger_event('connect', 'sid', {'foo': 'bar'}))
self.assertEqual(result['result'], ('sid', {'foo': 'bar'}))
def test_disconnect_event(self):
result = {}
class MyNamespace(asyncio_namespace.AsyncNamespace):
@coroutine
def on_disconnect(self, sid):
result['result'] = sid
ns = MyNamespace('/foo')
ns._set_server(mock.MagicMock())
_run(ns.trigger_event('disconnect', 'sid'))
self.assertEqual(result['result'], 'sid')
def test_sync_event(self):
result = {}
class MyNamespace(asyncio_namespace.AsyncNamespace):
def on_custom_message(self, sid, data):
result['result'] = (sid, data)
ns = MyNamespace('/foo')
ns._set_server(mock.MagicMock())
_run(ns.trigger_event('custom_message', 'sid', {'data': 'data'}))
self.assertEqual(result['result'], ('sid', {'data': 'data'}))
def test_async_event(self):
result = {}
class MyNamespace(asyncio_namespace.AsyncNamespace):
@coroutine
def on_custom_message(self, sid, data):
result['result'] = (sid, data)
ns = MyNamespace('/foo')
ns._set_server(mock.MagicMock())
_run(ns.trigger_event('custom_message', 'sid', {'data': 'data'}))
self.assertEqual(result['result'], ('sid', {'data': 'data'}))
def test_event_not_found(self):
result = {}
class MyNamespace(asyncio_namespace.AsyncNamespace):
@coroutine
def on_custom_message(self, sid, data):
result['result'] = (sid, data)
ns = MyNamespace('/foo')
ns._set_server(mock.MagicMock())
_run(ns.trigger_event('another_custom_message', 'sid',
{'data': 'data'}))
self.assertEqual(result, {})
def test_emit(self):
ns = asyncio_namespace.AsyncNamespace('/foo')
mock_server = mock.MagicMock()
mock_server.emit = AsyncMock()
ns._set_server(mock_server)
_run(ns.emit('ev', data='data', room='room', skip_sid='skip',
callback='cb'))
ns.server.emit.mock.assert_called_with(
'ev', data='data', room='room', skip_sid='skip', namespace='/foo',
callback='cb')
_run(ns.emit('ev', data='data', room='room', skip_sid='skip',
namespace='/bar', callback='cb'))
ns.server.emit.mock.assert_called_with(
'ev', data='data', room='room', skip_sid='skip', namespace='/bar',
callback='cb')
def test_send(self):
ns = asyncio_namespace.AsyncNamespace('/foo')
mock_server = mock.MagicMock()
mock_server.send = AsyncMock()
ns._set_server(mock_server)
_run(ns.send(data='data', room='room', skip_sid='skip', callback='cb'))
ns.server.send.mock.assert_called_with(
'data', room='room', skip_sid='skip', namespace='/foo',
callback='cb')
_run(ns.send(data='data', room='room', skip_sid='skip',
namespace='/bar', callback='cb'))
ns.server.send.mock.assert_called_with(
'data', room='room', skip_sid='skip', namespace='/bar',
callback='cb')
def test_enter_room(self):
ns = asyncio_namespace.AsyncNamespace('/foo')
ns._set_server(mock.MagicMock())
ns.enter_room('sid', 'room')
ns.server.enter_room.assert_called_with('sid', 'room',
namespace='/foo')
ns.enter_room('sid', 'room', namespace='/bar')
ns.server.enter_room.assert_called_with('sid', 'room',
namespace='/bar')
def test_leave_room(self):
ns = asyncio_namespace.AsyncNamespace('/foo')
ns._set_server(mock.MagicMock())
ns.leave_room('sid', 'room')
ns.server.leave_room.assert_called_with('sid', 'room',
namespace='/foo')
ns.leave_room('sid', 'room', namespace='/bar')
ns.server.leave_room.assert_called_with('sid', 'room',
namespace='/bar')
def test_close_room(self):
ns = asyncio_namespace.AsyncNamespace('/foo')
ns._set_server(mock.MagicMock())
ns.close_room('room')
ns.server.close_room.assert_called_with('room', namespace='/foo')
ns.close_room('room', namespace='/bar')
ns.server.close_room.assert_called_with('room', namespace='/bar')
def test_rooms(self):
ns = asyncio_namespace.AsyncNamespace('/foo')
ns._set_server(mock.MagicMock())
ns.rooms('sid')
ns.server.rooms.assert_called_with('sid', namespace='/foo')
ns.rooms('sid', namespace='/bar')
ns.server.rooms.assert_called_with('sid', namespace='/bar')
def test_disconnect(self):
ns = asyncio_namespace.AsyncNamespace('/foo')
mock_server = mock.MagicMock()
mock_server.disconnect = AsyncMock()
ns._set_server(mock_server)
_run(ns.disconnect('sid'))
ns.server.disconnect.mock.assert_called_with('sid', namespace='/foo')
_run(ns.disconnect('sid', namespace='/bar'))
ns.server.disconnect.mock.assert_called_with('sid', namespace='/bar')

594
tests/test_asyncio_server.py

@ -0,0 +1,594 @@
import json
import logging
import sys
import unittest
import six
if six.PY3:
from unittest import mock
else:
import mock
from socketio import packet
from socketio import namespace
if sys.version_info >= (3, 5):
import asyncio
from asyncio import coroutine
from socketio import asyncio_server
from socketio import asyncio_namespace
else:
# mock coroutine so that Python 2 doesn't complain
def coroutine(f):
return f
def AsyncMock(*args, **kwargs):
"""Return a mock asynchronous function."""
m = mock.MagicMock(*args, **kwargs)
@coroutine
def mock_coro(*args, **kwargs):
return m(*args, **kwargs)
mock_coro.mock = m
return mock_coro
def _run(coro):
"""Run the given coroutine."""
return asyncio.get_event_loop().run_until_complete(coro)
@unittest.skipIf(sys.version_info < (3, 5), 'only for Python 3.5+')
@mock.patch('socketio.server.engineio.AsyncServer')
class TestAsyncServer(unittest.TestCase):
def tearDown(self):
# restore JSON encoder, in case a test changed it
packet.Packet.json = json
def _get_mock_manager(self):
mgr = mock.MagicMock()
mgr.emit = AsyncMock()
mgr.trigger_callback = AsyncMock()
return mgr
def test_create(self, eio):
eio.return_value.handle_request = AsyncMock()
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr, foo='bar')
_run(s.handle_request({}))
_run(s.handle_request({}))
eio.assert_called_once_with(**{'foo': 'bar', 'async_handlers': False})
self.assertEqual(s.manager, mgr)
self.assertEqual(s.eio.on.call_count, 3)
self.assertEqual(s.binary, False)
self.assertEqual(s.async_handlers, False)
def test_attach(self, eio):
s = asyncio_server.AsyncServer()
s.attach('app', 'path')
eio.return_value.attach.assert_called_once_with('app', 'path')
def test_on_event(self, eio):
s = asyncio_server.AsyncServer()
@s.on('connect')
def foo():
pass
def bar():
pass
s.on('disconnect', bar)
s.on('disconnect', bar, namespace='/foo')
self.assertEqual(s.handlers['/']['connect'], foo)
self.assertEqual(s.handlers['/']['disconnect'], bar)
self.assertEqual(s.handlers['/foo']['disconnect'], bar)
def test_emit(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
_run(s.emit('my event', {'foo': 'bar'}, 'room', '123',
namespace='/foo', callback='cb'))
s.manager.emit.mock.assert_called_once_with(
'my event', {'foo': 'bar'}, '/foo', 'room', '123', 'cb')
def test_emit_default_namespace(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
_run(s.emit('my event', {'foo': 'bar'}, 'room', '123', callback='cb'))
s.manager.emit.mock.assert_called_once_with('my event', {'foo': 'bar'},
'/', 'room', '123', 'cb')
def test_send(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
_run(s.send('foo', 'room', '123', namespace='/foo', callback='cb'))
s.manager.emit.mock.assert_called_once_with('message', 'foo', '/foo',
'room', '123', 'cb')
def test_enter_room(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s.enter_room('123', 'room', namespace='/foo')
s.manager.enter_room.assert_called_once_with('123', '/foo', 'room')
def test_enter_room_default_namespace(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s.enter_room('123', 'room')
s.manager.enter_room.assert_called_once_with('123', '/', 'room')
def test_leave_room(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s.leave_room('123', 'room', namespace='/foo')
s.manager.leave_room.assert_called_once_with('123', '/foo', 'room')
def test_leave_room_default_namespace(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s.leave_room('123', 'room')
s.manager.leave_room.assert_called_once_with('123', '/', 'room')
def test_close_room(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s.close_room('room', namespace='/foo')
s.manager.close_room.assert_called_once_with('room', '/foo')
def test_close_room_default_namespace(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s.close_room('room')
s.manager.close_room.assert_called_once_with('room', '/')
def test_rooms(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s.rooms('123', namespace='/foo')
s.manager.get_rooms.assert_called_once_with('123', '/foo')
def test_rooms_default_namespace(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s.rooms('123')
s.manager.get_rooms.assert_called_once_with('123', '/')
def test_handle_request(self, eio):
eio.return_value.handle_request = AsyncMock()
s = asyncio_server.AsyncServer()
_run(s.handle_request('environ'))
s.eio.handle_request.mock.assert_called_once_with('environ')
def test_emit_internal(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
_run(s._emit_internal('123', 'my event', 'my data', namespace='/foo'))
s.eio.send.mock.assert_called_once_with(
'123', '2/foo,["my event","my data"]', binary=False)
def test_emit_internal_with_tuple(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
_run(s._emit_internal('123', 'my event', ('foo', 'bar'),
namespace='/foo'))
s.eio.send.mock.assert_called_once_with(
'123', '2/foo,["my event","foo","bar"]', binary=False)
def test_emit_internal_with_list(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
_run(s._emit_internal('123', 'my event', ['foo', 'bar'],
namespace='/foo'))
s.eio.send.mock.assert_called_once_with(
'123', '2/foo,["my event",["foo","bar"]]', binary=False)
def test_emit_internal_with_callback(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
id = s.manager._generate_ack_id('123', '/foo', 'cb')
_run(s._emit_internal('123', 'my event', 'my data', namespace='/foo',
id=id))
s.eio.send.mock.assert_called_once_with(
'123', '2/foo,1["my event","my data"]', binary=False)
def test_emit_internal_default_namespace(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
_run(s._emit_internal('123', 'my event', 'my data'))
s.eio.send.mock.assert_called_once_with(
'123', '2["my event","my data"]', binary=False)
def test_emit_internal_binary(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer(binary=True)
_run(s._emit_internal('123', u'my event', b'my binary data'))
self.assertEqual(s.eio.send.mock.call_count, 2)
def test_transport(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s.eio.transport = mock.MagicMock(return_value='polling')
_run(s._handle_eio_connect('foo', 'environ'))
self.assertEqual(s.transport('foo'), 'polling')
s.eio.transport.assert_called_once_with('foo')
def test_handle_connect(self, eio):
eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
handler = mock.MagicMock()
s.on('connect', handler)
_run(s._handle_eio_connect('123', 'environ'))
handler.assert_called_once_with('123', 'environ')
s.manager.connect.assert_called_once_with('123', '/')
s.eio.send.mock.assert_called_once_with('123', '0', binary=False)
self.assertEqual(mgr.initialize.call_count, 1)
_run(s._handle_eio_connect('456', 'environ'))
self.assertEqual(mgr.initialize.call_count, 1)
def test_handle_connect_async(self, eio):
eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
handler = AsyncMock()
s.on('connect', handler)
_run(s._handle_eio_connect('123', 'environ'))
handler.mock.assert_called_once_with('123', 'environ')
s.manager.connect.assert_called_once_with('123', '/')
s.eio.send.mock.assert_called_once_with('123', '0', binary=False)
self.assertEqual(mgr.initialize.call_count, 1)
_run(s._handle_eio_connect('456', 'environ'))
self.assertEqual(mgr.initialize.call_count, 1)
def test_handle_connect_namespace(self, eio):
eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
handler = mock.MagicMock()
s.on('connect', handler, namespace='/foo')
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0/foo'))
handler.assert_called_once_with('123', 'environ')
s.manager.connect.assert_any_call('123', '/')
s.manager.connect.assert_any_call('123', '/foo')
s.eio.send.mock.assert_any_call('123', '0/foo', binary=False)
def test_handle_connect_rejected(self, eio):
eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
handler = mock.MagicMock(return_value=False)
s.on('connect', handler)
_run(s._handle_eio_connect('123', 'environ'))
handler.assert_called_once_with('123', 'environ')
self.assertEqual(s.manager.connect.call_count, 1)
self.assertEqual(s.manager.disconnect.call_count, 1)
s.eio.send.mock.assert_called_once_with('123', '4', binary=False)
def test_handle_connect_namespace_rejected(self, eio):
eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
handler = mock.MagicMock(return_value=False)
s.on('connect', handler, namespace='/foo')
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0/foo'))
self.assertEqual(s.manager.connect.call_count, 2)
self.assertEqual(s.manager.disconnect.call_count, 1)
s.eio.send.mock.assert_any_call('123', '4/foo', binary=False)
def test_handle_disconnect(self, eio):
eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
handler = mock.MagicMock()
s.on('disconnect', handler)
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_disconnect('123'))
handler.assert_called_once_with('123')
s.manager.disconnect.assert_called_once_with('123', '/')
self.assertEqual(s.environ, {})
def test_handle_disconnect_namespace(self, eio):
eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s.manager.get_namespaces = mock.MagicMock(return_value=['/', '/foo'])
handler = mock.MagicMock()
s.on('disconnect', handler)
handler_namespace = mock.MagicMock()
s.on('disconnect', handler_namespace, namespace='/foo')
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0/foo'))
_run(s._handle_eio_disconnect('123'))
handler.assert_called_once_with('123')
handler_namespace.assert_called_once_with('123')
self.assertEqual(s.environ, {})
def test_handle_disconnect_only_namespace(self, eio):
eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s.manager.get_namespaces = mock.MagicMock(return_value=['/', '/foo'])
handler = mock.MagicMock()
s.on('disconnect', handler)
handler_namespace = mock.MagicMock()
s.on('disconnect', handler_namespace, namespace='/foo')
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0/foo'))
_run(s._handle_eio_message('123', '1/foo'))
self.assertEqual(handler.call_count, 0)
handler_namespace.assert_called_once_with('123')
self.assertEqual(s.environ, {'123': 'environ'})
def test_handle_disconnect_unknown_client(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
_run(s._handle_eio_disconnect('123'))
def test_handle_event(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
handler = AsyncMock()
s.on('my message', handler)
_run(s._handle_eio_message('123', '2["my message","a","b","c"]'))
handler.mock.assert_called_once_with('123', 'a', 'b', 'c')
def test_handle_event_with_namespace(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
handler = mock.MagicMock()
s.on('my message', handler, namespace='/foo')
_run(s._handle_eio_message('123', '2/foo,["my message","a","b","c"]'))
handler.assert_called_once_with('123', 'a', 'b', 'c')
def test_handle_event_binary(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
handler = mock.MagicMock()
s.on('my message', handler)
_run(s._handle_eio_message('123', '52-["my message","a",'
'{"_placeholder":true,"num":1},'
'{"_placeholder":true,"num":0}]'))
_run(s._handle_eio_message('123', b'foo'))
_run(s._handle_eio_message('123', b'bar'))
handler.assert_called_once_with('123', 'a', b'bar', b'foo')
def test_handle_event_binary_ack(self, eio):
eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s.manager.initialize(s)
_run(s._handle_eio_message('123', '61-321["my message","a",'
'{"_placeholder":true,"num":0}]'))
_run(s._handle_eio_message('123', b'foo'))
mgr.trigger_callback.mock.assert_called_once_with(
'123', '/', 321, ['my message', 'a', b'foo'])
def test_handle_event_with_ack(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
handler = mock.MagicMock(return_value='foo')
s.on('my message', handler)
_run(s._handle_eio_message('123', '21000["my message","foo"]'))
handler.assert_called_once_with('123', 'foo')
s.eio.send.mock.assert_called_once_with('123', '31000["foo"]',
binary=False)
def test_handle_event_with_ack_none(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
handler = mock.MagicMock(return_value=None)
s.on('my message', handler)
_run(s._handle_eio_message('123', '21000["my message","foo"]'))
handler.assert_called_once_with('123', 'foo')
s.eio.send.mock.assert_called_once_with('123', '31000[]',
binary=False)
def test_handle_event_with_ack_tuple(self, eio):
eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
handler = mock.MagicMock(return_value=(1, '2', True))
s.on('my message', handler)
_run(s._handle_eio_message('123', '21000["my message","a","b","c"]'))
handler.assert_called_once_with('123', 'a', 'b', 'c')
s.eio.send.mock.assert_called_once_with('123', '31000[1,"2",true]',
binary=False)
def test_handle_event_with_ack_list(self, eio):
eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
handler = mock.MagicMock(return_value=[1, '2', True])
s.on('my message', handler)
_run(s._handle_eio_message('123', '21000["my message","a","b","c"]'))
handler.assert_called_once_with('123', 'a', 'b', 'c')
s.eio.send.mock.assert_called_once_with('123', '31000[[1,"2",true]]',
binary=False)
def test_handle_event_with_ack_binary(self, eio):
eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr, binary=True)
handler = mock.MagicMock(return_value=b'foo')
s.on('my message', handler)
_run(s._handle_eio_message('123', '21000["my message","foo"]'))
handler.assert_any_call('123', 'foo')
def test_handle_error_packet(self, eio):
s = asyncio_server.AsyncServer()
self.assertRaises(ValueError, _run, s._handle_eio_message('123', '4'))
def test_handle_invalid_packet(self, eio):
s = asyncio_server.AsyncServer()
self.assertRaises(ValueError, _run, s._handle_eio_message('123', '9'))
def test_send_with_ack(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
_run(s._handle_eio_connect('123', 'environ'))
cb = mock.MagicMock()
id1 = s.manager._generate_ack_id('123', '/', cb)
id2 = s.manager._generate_ack_id('123', '/', cb)
_run(s._emit_internal('123', 'my event', ['foo'], id=id1))
_run(s._emit_internal('123', 'my event', ['bar'], id=id2))
_run(s._handle_eio_message('123', '31["foo",2]'))
cb.assert_called_once_with('foo', 2)
def test_send_with_ack_namespace(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0/foo'))
cb = mock.MagicMock()
id = s.manager._generate_ack_id('123', '/foo', cb)
_run(s._emit_internal('123', 'my event', ['foo'], namespace='/foo',
id=id))
_run(s._handle_eio_message('123', '3/foo,1["foo",2]'))
cb.assert_called_once_with('foo', 2)
def test_disconnect(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
_run(s._handle_eio_connect('123', 'environ'))
_run(s.disconnect('123'))
s.eio.send.mock.assert_any_call('123', '1', binary=False)
def test_disconnect_namespace(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0/foo'))
_run(s.disconnect('123', namespace='/foo'))
s.eio.send.mock.assert_any_call('123', '1/foo', binary=False)
def test_disconnect_twice(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
_run(s._handle_eio_connect('123', 'environ'))
_run(s.disconnect('123'))
calls = s.eio.send.mock.call_count
_run(s.disconnect('123'))
self.assertEqual(calls, s.eio.send.mock.call_count)
def test_disconnect_twice_namespace(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0/foo'))
_run(s.disconnect('123', namespace='/foo'))
calls = s.eio.send.mock.call_count
_run(s.disconnect('123', namespace='/foo'))
self.assertEqual(calls, s.eio.send.mock.call_count)
def test_namespace_handler(self, eio):
eio.return_value.send = AsyncMock()
result = {}
class MyNamespace(asyncio_namespace.AsyncNamespace):
def on_connect(self, sid, environ):
result['result'] = (sid, environ)
@coroutine
def on_disconnect(self, sid):
result['result'] = ('disconnect', sid)
@coroutine
def on_foo(self, sid, data):
result['result'] = (sid, data)
def on_bar(self, sid):
result['result'] = 'bar'
@coroutine
def on_baz(self, sid, data1, data2):
result['result'] = (data1, data2)
s = asyncio_server.AsyncServer()
s.register_namespace(MyNamespace('/foo'))
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0/foo'))
self.assertEqual(result['result'], ('123', 'environ'))
_run(s._handle_eio_message('123', '2/foo,["foo","a"]'))
self.assertEqual(result['result'], ('123', 'a'))
_run(s._handle_eio_message('123', '2/foo,["bar"]'))
self.assertEqual(result['result'], 'bar')
_run(s._handle_eio_message('123', '2/foo,["baz","a","b"]'))
self.assertEqual(result['result'], ('a', 'b'))
_run(s.disconnect('123', '/foo'))
self.assertEqual(result['result'], ('disconnect', '123'))
def test_bad_namespace_handler(self, eio):
class Dummy(object):
pass
class SyncNS(namespace.Namespace):
pass
s = asyncio_server.AsyncServer()
self.assertRaises(ValueError, s.register_namespace, 123)
self.assertRaises(ValueError, s.register_namespace, Dummy)
self.assertRaises(ValueError, s.register_namespace, Dummy())
self.assertRaises(ValueError, s.register_namespace,
namespace.Namespace)
self.assertRaises(ValueError, s.register_namespace, SyncNS())
def test_logger(self, eio):
s = asyncio_server.AsyncServer(logger=False)
self.assertEqual(s.logger.getEffectiveLevel(), logging.ERROR)
s.logger.setLevel(logging.NOTSET)
s = asyncio_server.AsyncServer(logger=True)
self.assertEqual(s.logger.getEffectiveLevel(), logging.INFO)
s.logger.setLevel(logging.WARNING)
s = asyncio_server.AsyncServer(logger=True)
self.assertEqual(s.logger.getEffectiveLevel(), logging.WARNING)
s.logger.setLevel(logging.NOTSET)
s = asyncio_server.AsyncServer(logger='foo')
self.assertEqual(s.logger, 'foo')
def test_engineio_logger(self, eio):
asyncio_server.AsyncServer(engineio_logger='foo')
eio.assert_called_once_with(**{'logger': 'foo',
'async_handlers': False})
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(object):
@staticmethod
def dumps(*args, **kwargs):
return '*** encoded ***'
@staticmethod
def loads(*args, **kwargs):
return '+++ decoded +++'
asyncio_server.AsyncServer(json=CustomJSON)
eio.assert_called_once_with(**{'json': CustomJSON,
'async_handlers': False})
pkt = packet.Packet(packet_type=packet.EVENT,
data={six.text_type('foo'): six.text_type('bar')})
self.assertEqual(pkt.encode(), '2*** encoded ***')
pkt2 = packet.Packet(encoded_packet=pkt.encode())
self.assertEqual(pkt2.data, '+++ decoded +++')
# restore the default JSON module
packet.Packet.json = json
def test_start_background_task(self, eio):
eio.return_value.start_background_task = AsyncMock()
s = asyncio_server.AsyncServer()
_run(s.start_background_task('foo', 'bar', baz='baz'))
s.eio.start_background_task.mock.assert_called_once_with('foo', 'bar',
baz='baz')
def test_sleep(self, eio):
eio.return_value.sleep = AsyncMock()
s = asyncio_server.AsyncServer()
_run(s.sleep(1.23))
s.eio.sleep.mock.assert_called_once_with(1.23)

7
tests/test_server.py

@ -180,6 +180,8 @@ class TestServer(unittest.TestCase):
s.manager.connect.assert_called_once_with('123', '/')
s.eio.send.assert_called_once_with('123', '0', binary=False)
self.assertEqual(mgr.initialize.call_count, 1)
s._handle_eio_connect('456', 'environ')
self.assertEqual(mgr.initialize.call_count, 1)
def test_handle_connect_namespace(self, eio):
mgr = mock.MagicMock()
@ -439,12 +441,17 @@ class TestServer(unittest.TestCase):
class Dummy(object):
pass
class AsyncNS(namespace.Namespace):
def is_asyncio_based(self):
return True
s = server.Server()
self.assertRaises(ValueError, s.register_namespace, 123)
self.assertRaises(ValueError, s.register_namespace, Dummy)
self.assertRaises(ValueError, s.register_namespace, Dummy())
self.assertRaises(ValueError, s.register_namespace,
namespace.Namespace)
self.assertRaises(ValueError, s.register_namespace, AsyncNS())
def test_logger(self, eio):
s = server.Server(logger=False)

Loading…
Cancel
Save