Improvements for test client (#613)

* Tests for httpclient sending content

* Testing ordered consumers

* Added docs

* Added testing for ordering

* Added GET params at HttpClient

* Remove blank line

* Fix py3 bites

* Test Client now support ChannelSocketException

* Fix flake and isort

* Conflict resolution
This commit is contained in:
Krukov D 2017-04-19 06:18:28 +03:00 committed by Andrew Godwin
parent b7ea0b9287
commit fa413af053
4 changed files with 148 additions and 4 deletions

View File

@ -12,6 +12,7 @@ from django.test.testcases import TestCase, TransactionTestCase
from .. import DEFAULT_CHANNEL_LAYER from .. import DEFAULT_CHANNEL_LAYER
from ..asgi import ChannelLayerWrapper, channel_layers from ..asgi import ChannelLayerWrapper, channel_layers
from ..channel import Group from ..channel import Group
from ..exceptions import ChannelSocketException
from ..message import Message from ..message import Message
from ..routing import Router, include from ..routing import Router, include
from ..signals import consumer_finished, consumer_started from ..signals import consumer_finished, consumer_started
@ -134,6 +135,8 @@ class Client(object):
try: try:
consumer_started.send(sender=self.__class__) consumer_started.send(sender=self.__class__)
return consumer(message, **kwargs) return consumer(message, **kwargs)
except ChannelSocketException as e:
e.run(message)
finally: finally:
# Copy Django's workaround so we don't actually close DB conns # Copy Django's workaround so we don't actually close DB conns
consumer_finished.disconnect(close_old_connections) consumer_finished.disconnect(close_old_connections)

View File

@ -20,11 +20,13 @@ class WSClient(Client):
""" """
def __init__(self, **kwargs): def __init__(self, **kwargs):
self._ordered = kwargs.pop('ordered', False)
super(WSClient, self).__init__(**kwargs) super(WSClient, self).__init__(**kwargs)
self._session = None self._session = None
self._headers = {} self._headers = {}
self._cookies = {} self._cookies = {}
self._session_cookie = True self._session_cookie = True
self.order = 0
def set_cookie(self, key, value): def set_cookie(self, key, value):
""" """
@ -76,18 +78,38 @@ class WSClient(Client):
Send a message to a channel. Send a message to a channel.
Adds reply_channel name and channel_session to the message. Adds reply_channel name and channel_session to the message.
""" """
if to != 'websocket.connect' and '?' in path:
path = path.split('?')[0]
self.channel_layer.send(to, self._get_content(content, text, path))
self._session_cookie = False
def _get_content(self, content={}, text=None, path='/'):
content = copy.deepcopy(content) content = copy.deepcopy(content)
content.setdefault('reply_channel', self.reply_channel) content.setdefault('reply_channel', self.reply_channel)
if '?' in path:
path, query_string = path.split('?')
content.setdefault('path', path) content.setdefault('path', path)
content.setdefault('query_string', query_string)
else:
content.setdefault('path', path)
content.setdefault('headers', self.headers) content.setdefault('headers', self.headers)
if self._ordered:
if 'order' in content:
raise ValueError('Do not use "order" manually with "ordered=True"')
content['order'] = self.order
self.order += 1
text = text or content.get('text', None) text = text or content.get('text', None)
if text is not None: if text is not None:
if not isinstance(text, six.string_types): if not isinstance(text, six.string_types):
content['text'] = json.dumps(text) content['text'] = json.dumps(text)
else: else:
content['text'] = text content['text'] = text
self.channel_layer.send(to, content) return content
self._session_cookie = False
def send_and_consume(self, channel, content={}, text=None, path='/', fail_on_none=True, check_accept=True): def send_and_consume(self, channel, content={}, text=None, path='/', fail_on_none=True, check_accept=True):
""" """

View File

@ -187,6 +187,26 @@ may call ``WSClient.force_login`` (like at django client) with the user object.
``receive`` method by default trying to deserialize json text content of a message, ``receive`` method by default trying to deserialize json text content of a message,
so if you need to pass decoding use ``receive(json=False)``, like in the example. so if you need to pass decoding use ``receive(json=False)``, like in the example.
For testing consumers with ``enforce_ordering`` initialize ``HttpClient`` with ``ordered``
flag, but if you wanna use your own order don't use it, use content::
client = HttpClient(ordered=True)
client.send_and_consume('websocket.receive', text='1', path='/ws') # order = 0
client.send_and_consume('websocket.receive', text='2', path='/ws') # order = 1
client.send_and_consume('websocket.receive', text='3', path='/ws') # order = 2
# manually
client = HttpClient()
client.send('websocket.receive', content={'order': 0}, text='1')
client.send('websocket.receive', content={'order': 2}, text='2')
client.send('websocket.receive', content={'order': 1}, text='3')
# calling consume 4 time for `waiting` message with order 1
client.consume('websocket.receive')
client.consume('websocket.receive')
client.consume('websocket.receive')
client.consume('websocket.receive')
Applying routes Applying routes
--------------- ---------------

View File

@ -2,7 +2,11 @@ from __future__ import unicode_literals
from django.http.cookie import parse_cookie from django.http.cookie import parse_cookie
from channels.test import ChannelTestCase, WSClient from channels import route
from channels.exceptions import ChannelSocketException
from channels.handler import AsgiRequest
from channels.test import ChannelTestCase, WSClient, apply_routes
from channels.sessions import enforce_ordering
class WSClientTests(ChannelTestCase): class WSClientTests(ChannelTestCase):
@ -22,3 +26,98 @@ class WSClientTests(ChannelTestCase):
'qux': 'qu;x', 'qux': 'qu;x',
'sessionid': client.get_cookies()['sessionid']}, 'sessionid': client.get_cookies()['sessionid']},
cookie_dict) cookie_dict)
def test_simple_content(self):
client = WSClient()
content = client._get_content(text={'key': 'value'}, path='/my/path')
self.assertEqual(content['text'], '{"key": "value"}')
self.assertEqual(content['path'], '/my/path')
self.assertTrue('reply_channel' in content)
self.assertTrue('headers' in content)
def test_path_in_content(self):
client = WSClient()
content = client._get_content(content={'path': '/my_path'}, text={'path': 'hi'}, path='/my/path')
self.assertEqual(content['text'], '{"path": "hi"}')
self.assertEqual(content['path'], '/my_path')
self.assertTrue('reply_channel' in content)
self.assertTrue('headers' in content)
def test_session_in_headers(self):
client = WSClient()
content = client._get_content()
self.assertTrue('path' in content)
self.assertEqual(content['path'], '/')
self.assertTrue('headers' in content)
self.assertTrue('cookie' in content['headers'])
self.assertTrue(b'sessionid' in content['headers']['cookie'])
def test_ordering_in_content(self):
client = WSClient(ordered=True)
content = client._get_content()
self.assertTrue('order' in content)
self.assertEqual(content['order'], 0)
client.order = 2
content = client._get_content()
self.assertTrue('order' in content)
self.assertEqual(content['order'], 2)
def test_ordering(self):
client = WSClient(ordered=True)
@enforce_ordering
def consumer(message):
message.reply_channel.send({'text': message['text']})
with apply_routes(route('websocket.receive', consumer)):
client.send_and_consume('websocket.receive', text='1') # order = 0
client.send_and_consume('websocket.receive', text='2') # order = 1
client.send_and_consume('websocket.receive', text='3') # order = 2
self.assertEqual(client.receive(), 1)
self.assertEqual(client.receive(), 2)
self.assertEqual(client.receive(), 3)
def test_get_params(self):
client = WSClient()
content = client._get_content(path='/my/path?test=1&token=2')
self.assertTrue('path' in content)
self.assertTrue('query_string' in content)
self.assertEqual(content['path'], '/my/path')
self.assertEqual(content['query_string'], 'test=1&token=2')
def test_get_params_with_consumer(self):
client = WSClient(ordered=True)
def consumer(message):
message.content['method'] = 'FAKE'
message.reply_channel.send({'text': dict(AsgiRequest(message).GET)})
with apply_routes([route('websocket.receive', consumer, path=r'^/test'),
route('websocket.connect', consumer, path=r'^/test')]):
path = '/test?key1=val1&key2=val2&key1=val3'
client.send_and_consume('websocket.connect', path=path, check_accept=False)
self.assertDictEqual(client.receive(), {'key2': ['val2'], 'key1': ['val1', 'val3']})
client.send_and_consume('websocket.receive', path=path)
self.assertDictEqual(client.receive(), {})
def test_channel_socket_exception(self):
class MyChannelSocketException(ChannelSocketException):
def run(self, message):
message.reply_channel.send({'text': 'error'})
def consumer(message):
raise MyChannelSocketException
client = WSClient()
with apply_routes(route('websocket.receive', consumer)):
client.send_and_consume('websocket.receive')
self.assertEqual(client.receive(json=False), 'error')