daphne/channels/tests/test_handler.py
Andrew Godwin 2ced4ee2e9 Remove consumer_finished from tests that flushed
No longer needed now messages aren't buffered outside consumers.
2017-01-11 15:40:24 -08:00

356 lines
13 KiB
Python

from __future__ import unicode_literals
import os
from datetime import datetime
from itertools import islice
from django.http import FileResponse, HttpResponse, HttpResponseRedirect, JsonResponse, StreamingHttpResponse
from six import BytesIO
from channels import Channel
from channels.handler import AsgiHandler
from channels.tests import ChannelTestCase
class FakeAsgiHandler(AsgiHandler):
"""
Handler subclass that just returns a premade response rather than
go into the view subsystem.
"""
chunk_size = 30
def __init__(self, response):
assert isinstance(response, (HttpResponse, StreamingHttpResponse))
self._response = response
super(FakeAsgiHandler, self).__init__()
def get_response(self, request):
return self._response
class HandlerTests(ChannelTestCase):
"""
Tests that the handler works correctly and round-trips things into a
correct response.
"""
def test_basic(self):
"""
Tests a simple request
"""
# Make stub request and desired response
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "GET",
"path": b"/test/",
})
response = HttpResponse(b"Hi there!", content_type="text/plain")
# Run the handler
handler = FakeAsgiHandler(response)
reply_messages = list(
handler(self.get_next_message("test", require=True)))
# Make sure we got the right number of messages
self.assertEqual(len(reply_messages), 1)
reply_message = reply_messages[0]
# Make sure the message looks correct
self.assertEqual(reply_message["content"], b"Hi there!")
self.assertEqual(reply_message["status"], 200)
self.assertEqual(reply_message.get("more_content", False), False)
self.assertEqual(
reply_message["headers"],
[
(b"Content-Type", b"text/plain"),
],
)
def test_cookies(self):
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "GET",
"path": b"/test/",
})
response = HttpResponse(b"Hi there!", content_type="text/plain")
response.set_signed_cookie('foo', '1', expires=datetime.now())
# Run the handler
handler = FakeAsgiHandler(response)
reply_messages = list(
handler(self.get_next_message("test", require=True)))
# Make sure we got the right number of messages
self.assertEqual(len(reply_messages), 1)
reply_message = reply_messages[0]
# Make sure the message looks correct
self.assertEqual(reply_message["content"], b"Hi there!")
self.assertEqual(reply_message["status"], 200)
self.assertEqual(reply_message.get("more_content", False), False)
self.assertEqual(reply_message["headers"][0], (b'Content-Type', b'text/plain'))
self.assertIn('foo=', reply_message["headers"][1][1].decode())
def test_headers(self):
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "GET",
"path": b"/test/",
})
response = HttpResponse(b"Hi there!", content_type="text/plain")
response['foo'] = 1
response['bar'] = 1
del response['bar']
del response['nonexistant_key']
handler = FakeAsgiHandler(response)
reply_messages = list(
handler(self.get_next_message("test", require=True)))
# Make sure we got the right number of messages
self.assertEqual(len(reply_messages), 1)
reply_message = reply_messages[0]
# Make sure the message looks correct
self.assertEqual(reply_message["content"], b"Hi there!")
header_dict = dict(reply_messages[0]['headers'])
self.assertEqual(header_dict[b'foo'].decode(), '1')
self.assertNotIn('bar', header_dict)
def test_large(self):
"""
Tests a large response (will need chunking)
"""
# Make stub request and desired response
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "GET",
"path": b"/test/",
})
response = HttpResponse(
b"Thefirstthirtybytesisrighthereandhereistherest")
# Run the handler
handler = FakeAsgiHandler(response)
reply_messages = list(
handler(self.get_next_message("test", require=True)))
# Make sure we got the right number of messages
self.assertEqual(len(reply_messages), 2)
# Make sure the messages look correct
self.assertEqual(reply_messages[0][
"content"], b"Thefirstthirtybytesisrighthere")
self.assertEqual(reply_messages[0]["status"], 200)
self.assertEqual(reply_messages[0]["more_content"], True)
self.assertEqual(reply_messages[1]["content"], b"andhereistherest")
self.assertEqual(reply_messages[1].get("more_content", False), False)
def test_empty(self):
"""
Tests an empty response
"""
# Make stub request and desired response
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "GET",
"path": b"/test/",
})
response = HttpResponse(b"", status=304)
# Run the handler
handler = FakeAsgiHandler(response)
reply_messages = list(
handler(self.get_next_message("test", require=True))
)
# Make sure we got the right number of messages
self.assertEqual(len(reply_messages), 1)
# Make sure the messages look correct
self.assertEqual(reply_messages[0].get("content", b""), b"")
self.assertEqual(reply_messages[0]["status"], 304)
self.assertEqual(reply_messages[0]["more_content"], False)
def test_empty_streaming(self):
"""
Tests an empty streaming response
"""
# Make stub request and desired response
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "GET",
"path": b"/test/",
})
response = StreamingHttpResponse([], status=304)
# Run the handler
handler = FakeAsgiHandler(response)
reply_messages = list(
handler(self.get_next_message("test", require=True))
)
# Make sure we got the right number of messages
self.assertEqual(len(reply_messages), 1)
# Make sure the messages look correct
self.assertEqual(reply_messages[0].get("content", b""), b"")
self.assertEqual(reply_messages[0]["status"], 304)
self.assertEqual(reply_messages[0]["more_content"], False)
def test_chunk_bytes(self):
"""
Makes sure chunk_bytes works correctly
"""
# Empty string should still return one chunk
result = list(FakeAsgiHandler.chunk_bytes(b""))
self.assertEqual(len(result), 1)
self.assertEqual(result[0][0], b"")
self.assertEqual(result[0][1], True)
# Below chunk size
result = list(FakeAsgiHandler.chunk_bytes(
b"12345678901234567890123456789"))
self.assertEqual(len(result), 1)
self.assertEqual(result[0][0], b"12345678901234567890123456789")
self.assertEqual(result[0][1], True)
# Exactly chunk size
result = list(FakeAsgiHandler.chunk_bytes(
b"123456789012345678901234567890"))
self.assertEqual(len(result), 1)
self.assertEqual(result[0][0], b"123456789012345678901234567890")
self.assertEqual(result[0][1], True)
# Just above chunk size
result = list(FakeAsgiHandler.chunk_bytes(
b"123456789012345678901234567890a"))
self.assertEqual(len(result), 2)
self.assertEqual(result[0][0], b"123456789012345678901234567890")
self.assertEqual(result[0][1], False)
self.assertEqual(result[1][0], b"a")
self.assertEqual(result[1][1], True)
def test_iterator(self):
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "GET",
"path": b"/test/",
})
response = HttpResponse(range(10))
handler = FakeAsgiHandler(response)
reply_messages = list(
handler(self.get_next_message("test", require=True)))
self.assertEqual(len(reply_messages), 1)
self.assertEqual(reply_messages[0]["content"], b"0123456789")
def test_streaming_data(self):
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "GET",
"path": b"/test/",
})
response = StreamingHttpResponse('Line: %s' % i for i in range(10))
handler = FakeAsgiHandler(response)
reply_messages = list(
handler(self.get_next_message("test", require=True)))
self.assertEqual(len(reply_messages), 11)
self.assertEqual(reply_messages[0]["content"], b"Line: 0")
self.assertEqual(reply_messages[9]["content"], b"Line: 9")
def test_real_file_response(self):
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "GET",
"path": b"/test/",
})
current_dir = os.path.realpath(os.path.join(
os.getcwd(), os.path.dirname(__file__)))
response = FileResponse(
open(os.path.join(current_dir, 'a_file'), 'rb'))
handler = FakeAsgiHandler(response)
reply_messages = list(
handler(self.get_next_message("test", require=True)))
self.assertEqual(len(reply_messages), 2)
self.assertEqual(response.getvalue(), b'')
def test_bytes_file_response(self):
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "GET",
"path": b"/test/",
})
response = FileResponse(BytesIO(b'sadfdasfsdfsadf'))
handler = FakeAsgiHandler(response)
reply_messages = list(
handler(self.get_next_message("test", require=True)))
self.assertEqual(len(reply_messages), 2)
def test_string_file_response(self):
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "GET",
"path": b"/test/",
})
response = FileResponse('abcd')
handler = FakeAsgiHandler(response)
reply_messages = list(
handler(self.get_next_message("test", require=True)))
self.assertEqual(len(reply_messages), 5)
def test_non_streaming_file_response(self):
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "GET",
"path": b"/test/",
})
response = FileResponse(BytesIO(b'sadfdasfsdfsadf'))
# This is to test the exception handling. This would only happening if
# the StreamingHttpResponse was incorrectly subclassed.
response.streaming = False
handler = FakeAsgiHandler(response)
with self.assertRaises(AttributeError):
list(handler(self.get_next_message("test", require=True)))
def test_unclosable_filelike_object(self):
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "GET",
"path": b"/test/",
})
# This is a readable object that cannot be closed.
class Unclosable:
def read(self, n=-1):
# Nothing to see here
return b""
response = FileResponse(Unclosable())
handler = FakeAsgiHandler(response)
reply_messages = list(islice(handler(self.get_next_message("test", require=True)), 5))
self.assertEqual(len(reply_messages), 1)
response.close()
def test_json_response(self):
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "GET",
"path": b"/test/",
})
response = JsonResponse({'foo': (1, 2)})
handler = FakeAsgiHandler(response)
reply_messages = list(handler(self.get_next_message("test", require=True)))
self.assertEqual(len(reply_messages), 1)
self.assertEqual(reply_messages[0]['content'], b'{"foo": [1, 2]}')
def test_redirect(self):
for redirect_to in ['/', '..', 'https://example.com']:
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "GET",
"path": b"/test/",
})
response = HttpResponseRedirect(redirect_to)
handler = FakeAsgiHandler(response)
reply_messages = list(handler(self.get_next_message("test", require=True)))
self.assertEqual(reply_messages[0]['status'], 302)
header_dict = dict(reply_messages[0]['headers'])
self.assertEqual(header_dict[b'Location'].decode(), redirect_to)