mirror of
https://github.com/django/daphne.git
synced 2025-07-30 00:49:44 +03:00
Add Hypothesis tests for HTTP responses
Similarly to what I did for HTTP requests, this commit adds a couple test that try to check different parts of the ASGI spec. Because going from message to HTTP response is more straightforward than going from HTTP request to channel message, there's not a whole lot going on here.
This commit is contained in:
parent
f35ad20748
commit
e7490dcda6
|
@ -7,9 +7,84 @@ from __future__ import unicode_literals
|
|||
from unittest import TestCase
|
||||
|
||||
from asgiref.inmemory import ChannelLayer
|
||||
from hypothesis import given
|
||||
from twisted.test import proto_helpers
|
||||
|
||||
from ..http_protocol import HTTPFactory
|
||||
from daphne.http_protocol import HTTPFactory
|
||||
from . import factories, http_strategies, testcases
|
||||
|
||||
|
||||
class TestHTTPResponseSpec(testcases.ASGITestCase):
|
||||
|
||||
def test_minimal_response(self):
|
||||
"""
|
||||
Smallest viable example. Mostly verifies that our response building works.
|
||||
"""
|
||||
message = {'status': 200}
|
||||
response = factories.response_for_message(message)
|
||||
self.assert_valid_http_response_message(message, response)
|
||||
self.assertIn(b'200 OK', response)
|
||||
# Assert that the response is the last of the chunks.
|
||||
# N.b. at the time of writing, Daphne did not support multiple response chunks,
|
||||
# but still sends with Transfer-Encoding: chunked if no Content-Length header
|
||||
# is specified (and maybe even if specified).
|
||||
self.assertTrue(response.endswith(b'0\r\n\r\n'))
|
||||
|
||||
def test_status_code_required(self):
|
||||
"""
|
||||
Asserts that passing in the 'status' key is required.
|
||||
|
||||
Previous versions of Daphne did not enforce this, so this test is here
|
||||
to make sure it stays required.
|
||||
"""
|
||||
with self.assertRaises(ValueError):
|
||||
factories.response_for_message({})
|
||||
|
||||
def test_status_code_is_transmitted(self):
|
||||
"""
|
||||
Tests that a custom status code is present in the response.
|
||||
|
||||
We can't really use hypothesis to test all sorts of status codes, because a lot
|
||||
of them have meaning that is respected by Twisted. E.g. setting 204 (No Content)
|
||||
as a status code results in Twisted discarding the body.
|
||||
"""
|
||||
message = {'status': 201} # 'Created'
|
||||
response = factories.response_for_message(message)
|
||||
self.assert_valid_http_response_message(message, response)
|
||||
self.assertIn(b'201 Created', response)
|
||||
|
||||
@given(body=http_strategies.http_body())
|
||||
def test_body_is_transmitted(self, body):
|
||||
message = {'status': 200, 'content': body.encode('ascii')}
|
||||
response = factories.response_for_message(message)
|
||||
self.assert_valid_http_response_message(message, response)
|
||||
|
||||
@given(headers=http_strategies.headers())
|
||||
def test_headers(self, headers):
|
||||
# The ASGI spec requires us to lowercase our header names
|
||||
message = {'status': 200, 'headers': [(name.lower(), value) for name, value in headers]}
|
||||
response = factories.response_for_message(message)
|
||||
# The assert_ method does the heavy lifting of checking that headers are
|
||||
# as expected.
|
||||
self.assert_valid_http_response_message(message, response)
|
||||
|
||||
@given(
|
||||
headers=http_strategies.headers(),
|
||||
body=http_strategies.http_body()
|
||||
)
|
||||
def test_kitchen_sink(self, headers, body):
|
||||
"""
|
||||
This tests tries to let Hypothesis find combinations of variables that result
|
||||
in breaking our assumptions. But responses are less exciting than responses,
|
||||
so there's not a lot going on here.
|
||||
"""
|
||||
message = {
|
||||
'status': 202, # 'Accepted'
|
||||
'headers': [(name.lower(), value) for name, value in headers],
|
||||
'content': body.encode('ascii')
|
||||
}
|
||||
response = factories.response_for_message(message)
|
||||
self.assert_valid_http_response_message(message, response)
|
||||
|
||||
|
||||
class TestHTTPResponse(TestCase):
|
||||
|
@ -24,39 +99,6 @@ class TestHTTPResponse(TestCase):
|
|||
self.tr = proto_helpers.StringTransport()
|
||||
self.proto.makeConnection(self.tr)
|
||||
|
||||
def test_basic(self):
|
||||
"""
|
||||
Tests basic HTTP parsing
|
||||
"""
|
||||
# Send a simple request to the protocol
|
||||
self.proto.dataReceived(
|
||||
b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" +
|
||||
b"Host: somewhere.com\r\n" +
|
||||
b"\r\n"
|
||||
)
|
||||
# Get the resulting message off of the channel layer
|
||||
_, message = self.channel_layer.receive(["http.request"])
|
||||
self.assertEqual(message['http_version'], "1.1")
|
||||
self.assertEqual(message['method'], "GET")
|
||||
self.assertEqual(message['scheme'], "http")
|
||||
self.assertEqual(message['path'], "/te st-à/")
|
||||
self.assertEqual(message['query_string'], b"foo=+bar")
|
||||
self.assertEqual(message['headers'], [(b"host", b"somewhere.com")])
|
||||
self.assertFalse(message.get("body", None))
|
||||
self.assertTrue(message['reply_channel'])
|
||||
# Send back an example response
|
||||
self.factory.dispatch_reply(
|
||||
message['reply_channel'],
|
||||
{
|
||||
"status": 201,
|
||||
"status_text": b"Created",
|
||||
"content": b"OH HAI",
|
||||
"headers": [[b"X-Test", b"Boom!"]],
|
||||
}
|
||||
)
|
||||
# Make sure that comes back right on the protocol
|
||||
self.assertEqual(self.tr.value(), b"HTTP/1.1 201 Created\r\nTransfer-Encoding: chunked\r\nX-Test: Boom!\r\n\r\n6\r\nOH HAI\r\n0\r\n\r\n")
|
||||
|
||||
def test_http_disconnect_sets_path_key(self):
|
||||
"""
|
||||
Tests http disconnect has the path key set, see https://channels.readthedocs.io/en/latest/asgi.html#disconnect
|
||||
|
|
Loading…
Reference in New Issue
Block a user