RAHHH
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
**DO NOT ADD a __init__.py file in this directory**
|
||||
|
||||
"Why not?" you ask; read on!
|
||||
|
||||
1. If we're running asyncio tests, we can't ever call txaio.use_twisted()
|
||||
|
||||
2. If we're running twisted tests, we can't ever call txaio.use_asycnio()...
|
||||
|
||||
3. ...and these are decided/called at import time
|
||||
|
||||
4. so: we can't *import* any of the autobahn.asyncio.* modules if we're
|
||||
running twisted tests (or vice versa)
|
||||
|
||||
5. ...but test-runners (py.test and trial) import things automagically
|
||||
(to "discover" tests)
|
||||
|
||||
6. We use py.test to run asyncio tests; see "setup.cfg" where we tell
|
||||
it "norecursedirs = autobahn/twisted/*" so it doesn't ipmort twisted
|
||||
stuff (and hence call txaio.use_twisted())
|
||||
|
||||
7. We use trial to run twisted tests; the lack of __init__ in here
|
||||
stops it from trying to import this (and hence the parent
|
||||
package). (The only files matching test_*.py are in this
|
||||
directory.)
|
||||
|
||||
*Therefore*, we don't put a __init__ file in this directory.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,235 @@
|
||||
import pytest
|
||||
import os
|
||||
|
||||
from unittest.mock import Mock, call
|
||||
|
||||
from autobahn.asyncio.rawsocket import PrefixProtocol, RawSocketClientProtocol, RawSocketServerProtocol, \
|
||||
WampRawSocketClientFactory, WampRawSocketServerFactory
|
||||
from autobahn.asyncio.util import get_serializers
|
||||
from autobahn.wamp import message
|
||||
from autobahn.wamp.types import TransportDetails
|
||||
|
||||
|
||||
@pytest.mark.skipif(not os.environ.get('USE_ASYNCIO', False), reason='test runs on asyncio only')
|
||||
def test_sers(event_loop):
|
||||
serializers = get_serializers()
|
||||
assert len(serializers) > 0
|
||||
m = serializers[0]().serialize(message.Abort('close'))
|
||||
assert m
|
||||
|
||||
|
||||
@pytest.mark.skipif(not os.environ.get('USE_ASYNCIO', False), reason='test runs on asyncio only')
|
||||
def test_prefix(event_loop):
|
||||
p = PrefixProtocol()
|
||||
transport = Mock()
|
||||
receiver = Mock()
|
||||
p.stringReceived = receiver
|
||||
p.connection_made(transport)
|
||||
small_msg = b'\x00\x00\x00\x04abcd'
|
||||
p.data_received(small_msg)
|
||||
receiver.assert_called_once_with(b'abcd')
|
||||
assert len(p._buffer) == 0
|
||||
|
||||
p.sendString(b'abcd')
|
||||
|
||||
# print(transport.write.call_args_list)
|
||||
transport.write.assert_has_calls([call(b'\x00\x00\x00\x04'), call(b'abcd')])
|
||||
|
||||
transport.reset_mock()
|
||||
receiver.reset_mock()
|
||||
|
||||
big_msg = b'\x00\x00\x00\x0C' + b'0123456789AB'
|
||||
|
||||
p.data_received(big_msg[0:2])
|
||||
assert not receiver.called
|
||||
|
||||
p.data_received(big_msg[2:6])
|
||||
assert not receiver.called
|
||||
|
||||
p.data_received(big_msg[6:11])
|
||||
assert not receiver.called
|
||||
|
||||
p.data_received(big_msg[11:16])
|
||||
receiver.assert_called_once_with(b'0123456789AB')
|
||||
|
||||
transport.reset_mock()
|
||||
receiver.reset_mock()
|
||||
|
||||
two_messages = b'\x00\x00\x00\x04' + b'abcd' + b'\x00\x00\x00\x05' + b'12345' + b'\x00'
|
||||
p.data_received(two_messages)
|
||||
receiver.assert_has_calls([call(b'abcd'), call(b'12345')])
|
||||
assert p._buffer == b'\x00'
|
||||
|
||||
|
||||
@pytest.mark.skipif(not os.environ.get('USE_ASYNCIO', False), reason='test runs on asyncio only')
|
||||
def test_is_closed(event_loop):
|
||||
class CP(RawSocketClientProtocol):
|
||||
@property
|
||||
def serializer_id(self):
|
||||
return 1
|
||||
client = CP()
|
||||
|
||||
on_hs = Mock()
|
||||
transport = Mock()
|
||||
receiver = Mock()
|
||||
client.stringReceived = receiver
|
||||
client._on_handshake_complete = on_hs
|
||||
assert client.is_closed.done()
|
||||
client.connection_made(transport)
|
||||
assert not client.is_closed.done()
|
||||
client.connection_lost(None)
|
||||
|
||||
assert client.is_closed.done()
|
||||
|
||||
|
||||
@pytest.mark.skipif(not os.environ.get('USE_ASYNCIO', False), reason='test runs on asyncio only')
|
||||
def test_raw_socket_server1(event_loop):
|
||||
|
||||
server = RawSocketServerProtocol()
|
||||
ser = Mock(return_value=True)
|
||||
on_hs = Mock()
|
||||
transport = Mock()
|
||||
receiver = Mock()
|
||||
server.supports_serializer = ser
|
||||
server.stringReceived = receiver
|
||||
server._on_handshake_complete = on_hs
|
||||
server.stringReceived = receiver
|
||||
|
||||
server.connection_made(transport)
|
||||
hs = b'\x7F\xF1\x00\x00' + b'\x00\x00\x00\x04abcd'
|
||||
server.data_received(hs)
|
||||
|
||||
ser.assert_called_once_with(1)
|
||||
on_hs.assert_called_once_with()
|
||||
assert transport.write.called
|
||||
transport.write.assert_called_once_with(b'\x7F\xF1\x00\x00')
|
||||
assert not transport.close.called
|
||||
receiver.assert_called_once_with(b'abcd')
|
||||
|
||||
|
||||
@pytest.mark.skipif(not os.environ.get('USE_ASYNCIO', False), reason='test runs on asyncio only')
|
||||
def test_raw_socket_server_errors(event_loop):
|
||||
|
||||
server = RawSocketServerProtocol()
|
||||
ser = Mock(return_value=True)
|
||||
on_hs = Mock()
|
||||
transport = Mock()
|
||||
receiver = Mock()
|
||||
server.supports_serializer = ser
|
||||
server.stringReceived = receiver
|
||||
server._on_handshake_complete = on_hs
|
||||
server.stringReceived = receiver
|
||||
server.connection_made(transport)
|
||||
server.data_received(b'abcdef')
|
||||
transport.close.assert_called_once_with()
|
||||
|
||||
server = RawSocketServerProtocol()
|
||||
ser = Mock(return_value=False)
|
||||
on_hs = Mock()
|
||||
transport = Mock(spec_set=('close', 'write', 'get_extra_info'))
|
||||
receiver = Mock()
|
||||
server.supports_serializer = ser
|
||||
server.stringReceived = receiver
|
||||
server._on_handshake_complete = on_hs
|
||||
server.stringReceived = receiver
|
||||
server.connection_made(transport)
|
||||
server.data_received(b'\x7F\xF1\x00\x00')
|
||||
transport.close.assert_called_once_with()
|
||||
transport.write.assert_called_once_with(b'\x7F\x10\x00\x00')
|
||||
|
||||
|
||||
@pytest.mark.skipif(not os.environ.get('USE_ASYNCIO', False), reason='test runs on asyncio only')
|
||||
def test_raw_socket_client1(event_loop):
|
||||
class CP(RawSocketClientProtocol):
|
||||
@property
|
||||
def serializer_id(self):
|
||||
return 1
|
||||
client = CP()
|
||||
|
||||
on_hs = Mock()
|
||||
transport = Mock()
|
||||
receiver = Mock()
|
||||
client.stringReceived = receiver
|
||||
client._on_handshake_complete = on_hs
|
||||
|
||||
client.connection_made(transport)
|
||||
client.data_received(b'\x7F\xF1\x00\x00' + b'\x00\x00\x00\x04abcd')
|
||||
on_hs.assert_called_once_with()
|
||||
assert transport.write.called
|
||||
transport.write.called_one_with(b'\x7F\xF1\x00\x00')
|
||||
assert not transport.close.called
|
||||
receiver.assert_called_once_with(b'abcd')
|
||||
|
||||
|
||||
@pytest.mark.skipif(not os.environ.get('USE_ASYNCIO', False), reason='test runs on asyncio only')
|
||||
def test_raw_socket_client_error(event_loop):
|
||||
class CP(RawSocketClientProtocol):
|
||||
@property
|
||||
def serializer_id(self):
|
||||
return 1
|
||||
client = CP()
|
||||
|
||||
on_hs = Mock()
|
||||
transport = Mock(spec_set=('close', 'write', 'get_extra_info'))
|
||||
receiver = Mock()
|
||||
client.stringReceived = receiver
|
||||
client._on_handshake_complete = on_hs
|
||||
client.connection_made(transport)
|
||||
|
||||
client.data_received(b'\x7F\xF1\x00\x01')
|
||||
transport.close.assert_called_once_with()
|
||||
|
||||
|
||||
@pytest.mark.skipif(not os.environ.get('USE_ASYNCIO', False), reason='test runs on asyncio only')
|
||||
def test_wamp_server(event_loop):
|
||||
transport = Mock(spec_set=('abort', 'close', 'write', 'get_extra_info'))
|
||||
transport.write = Mock(side_effect=lambda m: messages.append(m))
|
||||
server = Mock(spec=['onOpen', 'onMessage'])
|
||||
|
||||
def fact_server():
|
||||
return server
|
||||
|
||||
messages = []
|
||||
|
||||
proto = WampRawSocketServerFactory(fact_server)()
|
||||
proto.connection_made(transport)
|
||||
assert proto.transport_details.is_server is True
|
||||
assert proto.transport_details.channel_framing == TransportDetails.CHANNEL_FRAMING_RAWSOCKET
|
||||
assert proto.factory._serializers
|
||||
s = proto.factory._serializers[1].RAWSOCKET_SERIALIZER_ID
|
||||
proto.data_received(bytes(bytearray([0x7F, 0xF0 | s, 0, 0])))
|
||||
assert proto._serializer
|
||||
server.onOpen.assert_called_once_with(proto)
|
||||
|
||||
proto.send(message.Abort('close'))
|
||||
for d in messages[1:]:
|
||||
proto.data_received(d)
|
||||
assert server.onMessage.called
|
||||
assert isinstance(server.onMessage.call_args[0][0], message.Abort)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not os.environ.get('USE_ASYNCIO', False), reason='test runs on asyncio only')
|
||||
def test_wamp_client(event_loop):
|
||||
transport = Mock(spec_set=('abort', 'close', 'write', 'get_extra_info'))
|
||||
transport.write = Mock(side_effect=lambda m: messages.append(m))
|
||||
client = Mock(spec=['onOpen', 'onMessage'])
|
||||
|
||||
def fact_client():
|
||||
return client
|
||||
|
||||
messages = []
|
||||
|
||||
proto = WampRawSocketClientFactory(fact_client)()
|
||||
proto.connection_made(transport)
|
||||
assert proto.transport_details.is_server is False
|
||||
assert proto.transport_details.channel_framing == TransportDetails.CHANNEL_FRAMING_RAWSOCKET
|
||||
assert proto._serializer
|
||||
s = proto._serializer.RAWSOCKET_SERIALIZER_ID
|
||||
proto.data_received(bytes(bytearray([0x7F, 0xF0 | s, 0, 0])))
|
||||
client.onOpen.assert_called_once_with(proto)
|
||||
|
||||
proto.send(message.Abort('close'))
|
||||
for d in messages[1:]:
|
||||
proto.data_received(d)
|
||||
assert client.onMessage.called
|
||||
assert isinstance(client.onMessage.call_args[0][0], message.Abort)
|
||||
@@ -0,0 +1,71 @@
|
||||
import os
|
||||
import asyncio
|
||||
import pytest
|
||||
|
||||
import txaio
|
||||
|
||||
# because py.test tries to collect it as a test-case
|
||||
from unittest.mock import Mock
|
||||
|
||||
from autobahn.asyncio.websocket import WebSocketServerFactory
|
||||
|
||||
|
||||
async def echo_async(what, when):
|
||||
await asyncio.sleep(when)
|
||||
return what
|
||||
|
||||
|
||||
@pytest.mark.skipif(not os.environ.get('USE_ASYNCIO', False), reason='test runs on asyncio only')
|
||||
@pytest.mark.asyncio
|
||||
async def test_echo_async():
|
||||
assert 'Hello!' == await echo_async('Hello!', 0)
|
||||
|
||||
|
||||
# @pytest.mark.asyncio(forbid_global_loop=True)
|
||||
@pytest.mark.skipif(not os.environ.get('USE_ASYNCIO', False), reason='test runs on asyncio only')
|
||||
def test_websocket_custom_loop(event_loop):
|
||||
factory = WebSocketServerFactory(loop=event_loop)
|
||||
server = factory()
|
||||
transport = Mock()
|
||||
server.connection_made(transport)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not os.environ.get('USE_ASYNCIO', False), reason='test runs on asyncio only')
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_on_connect_server(event_loop):
|
||||
|
||||
num = 42
|
||||
done = txaio.create_future()
|
||||
values = []
|
||||
|
||||
async def foo(x):
|
||||
await asyncio.sleep(1)
|
||||
return x * x
|
||||
|
||||
async def on_connect(req):
|
||||
v = await foo(num)
|
||||
values.append(v)
|
||||
txaio.resolve(done, req)
|
||||
|
||||
factory = WebSocketServerFactory()
|
||||
server = factory()
|
||||
server.onConnect = on_connect
|
||||
transport = Mock()
|
||||
|
||||
server.connection_made(transport)
|
||||
server.data = b'\r\n'.join([
|
||||
b'GET /ws HTTP/1.1',
|
||||
b'Host: www.example.com',
|
||||
b'Sec-WebSocket-Version: 13',
|
||||
b'Origin: http://www.example.com.malicious.com',
|
||||
b'Sec-WebSocket-Extensions: permessage-deflate',
|
||||
b'Sec-WebSocket-Key: tXAxWFUqnhi86Ajj7dRY5g==',
|
||||
b'Connection: keep-alive, Upgrade',
|
||||
b'Upgrade: websocket',
|
||||
b'\r\n', # last string doesn't get a \r\n from join()
|
||||
])
|
||||
server.processHandshake()
|
||||
await done
|
||||
|
||||
assert len(values) == 1
|
||||
assert values[0] == num * num
|
||||
@@ -0,0 +1,134 @@
|
||||
###############################################################################
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) typedef int GmbH
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
import unittest
|
||||
from txaio.testutil import replace_loop
|
||||
|
||||
import asyncio
|
||||
from unittest.mock import patch, Mock
|
||||
|
||||
from autobahn.asyncio.wamp import ApplicationRunner
|
||||
|
||||
|
||||
class TestApplicationRunner(unittest.TestCase):
|
||||
"""
|
||||
Test the autobahn.asyncio.wamp.ApplicationRunner class.
|
||||
"""
|
||||
def _assertRaisesRegex(self, exception, error, *args, **kw):
|
||||
try:
|
||||
self.assertRaisesRegex
|
||||
except AttributeError:
|
||||
f = self.assertRaisesRegexp
|
||||
else:
|
||||
f = self.assertRaisesRegex
|
||||
f(exception, error, *args, **kw)
|
||||
|
||||
def test_explicit_SSLContext(self):
|
||||
"""
|
||||
Ensure that loop.create_connection is called with the exact SSL
|
||||
context object that is passed (as ssl) to the __init__ method of
|
||||
ApplicationRunner.
|
||||
"""
|
||||
with replace_loop(Mock()) as loop:
|
||||
with patch.object(asyncio, 'get_event_loop', return_value=loop):
|
||||
loop.run_until_complete = Mock(return_value=(Mock(), Mock()))
|
||||
ssl = {}
|
||||
runner = ApplicationRunner('ws://127.0.0.1:8080/ws', 'realm',
|
||||
ssl=ssl)
|
||||
runner.run('_unused_')
|
||||
self.assertIs(ssl, loop.create_connection.call_args[1]['ssl'])
|
||||
|
||||
def test_omitted_SSLContext_insecure(self):
|
||||
"""
|
||||
Ensure that loop.create_connection is called with ssl=False
|
||||
if no ssl argument is passed to the __init__ method of
|
||||
ApplicationRunner and the websocket URL starts with "ws:".
|
||||
"""
|
||||
with replace_loop(Mock()) as loop:
|
||||
with patch.object(asyncio, 'get_event_loop', return_value=loop):
|
||||
loop.run_until_complete = Mock(return_value=(Mock(), Mock()))
|
||||
runner = ApplicationRunner('ws://127.0.0.1:8080/ws', 'realm')
|
||||
runner.run('_unused_')
|
||||
self.assertIs(False, loop.create_connection.call_args[1]['ssl'])
|
||||
|
||||
def test_omitted_SSLContext_secure(self):
|
||||
"""
|
||||
Ensure that loop.create_connection is called with ssl=True
|
||||
if no ssl argument is passed to the __init__ method of
|
||||
ApplicationRunner and the websocket URL starts with "wss:".
|
||||
"""
|
||||
with replace_loop(Mock()) as loop:
|
||||
with patch.object(asyncio, 'get_event_loop', return_value=loop):
|
||||
loop.run_until_complete = Mock(return_value=(Mock(), Mock()))
|
||||
runner = ApplicationRunner('wss://127.0.0.1:8080/wss', 'realm')
|
||||
runner.run(self.fail)
|
||||
self.assertIs(True, loop.create_connection.call_args[1]['ssl'])
|
||||
|
||||
def test_conflict_SSL_True_with_ws_url(self):
|
||||
"""
|
||||
ApplicationRunner must raise an exception if given an ssl value of True
|
||||
but only a "ws:" URL.
|
||||
"""
|
||||
with replace_loop(Mock()) as loop:
|
||||
loop.run_until_complete = Mock(return_value=(Mock(), Mock()))
|
||||
runner = ApplicationRunner('ws://127.0.0.1:8080/wss', 'realm',
|
||||
ssl=True)
|
||||
error = (r'^ssl argument value passed to ApplicationRunner '
|
||||
r'conflicts with the "ws:" prefix of the url '
|
||||
r'argument\. Did you mean to use "wss:"\?$')
|
||||
self._assertRaisesRegex(Exception, error, runner.run, '_unused_')
|
||||
|
||||
def test_conflict_SSLContext_with_ws_url(self):
|
||||
"""
|
||||
ApplicationRunner must raise an exception if given an ssl value that is
|
||||
an instance of SSLContext, but only a "ws:" URL.
|
||||
"""
|
||||
import ssl
|
||||
try:
|
||||
# Try to create an SSLContext, to be as rigorous as we can be
|
||||
# by avoiding making assumptions about the ApplicationRunner
|
||||
# implementation. If we happen to be on a Python that has no
|
||||
# SSLContext, we pass ssl=True, which will simply cause this
|
||||
# test to degenerate to the behavior of
|
||||
# test_conflict_SSL_True_with_ws_url (above). In fact, at the
|
||||
# moment (2015-05-10), none of this matters because the
|
||||
# ApplicationRunner implementation does not check to require
|
||||
# that its ssl argument is either a bool or an SSLContext. But
|
||||
# that may change, so we should be careful.
|
||||
ssl.create_default_context
|
||||
except AttributeError:
|
||||
context = True
|
||||
else:
|
||||
context = ssl.create_default_context()
|
||||
|
||||
with replace_loop(Mock()) as loop:
|
||||
loop.run_until_complete = Mock(return_value=(Mock(), Mock()))
|
||||
runner = ApplicationRunner('ws://127.0.0.1:8080/wss', 'realm',
|
||||
ssl=context)
|
||||
error = (r'^ssl argument value passed to ApplicationRunner '
|
||||
r'conflicts with the "ws:" prefix of the url '
|
||||
r'argument\. Did you mean to use "wss:"\?$')
|
||||
self._assertRaisesRegex(Exception, error, runner.run, '_unused_')
|
||||
Reference in New Issue
Block a user