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

View File

@ -20,11 +20,13 @@ class WSClient(Client):
"""
def __init__(self, **kwargs):
self._ordered = kwargs.pop('ordered', False)
super(WSClient, self).__init__(**kwargs)
self._session = None
self._headers = {}
self._cookies = {}
self._session_cookie = True
self.order = 0
def set_cookie(self, key, value):
"""
@ -76,18 +78,38 @@ class WSClient(Client):
Send a message to a channel.
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.setdefault('reply_channel', self.reply_channel)
content.setdefault('path', path)
if '?' in path:
path, query_string = path.split('?')
content.setdefault('path', path)
content.setdefault('query_string', query_string)
else:
content.setdefault('path', path)
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)
if text is not None:
if not isinstance(text, six.string_types):
content['text'] = json.dumps(text)
else:
content['text'] = text
self.channel_layer.send(to, content)
self._session_cookie = False
return content
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,
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
---------------

View File

@ -2,7 +2,11 @@ from __future__ import unicode_literals
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):
@ -22,3 +26,98 @@ class WSClientTests(ChannelTestCase):
'qux': 'qu;x',
'sessionid': client.get_cookies()['sessionid']},
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')