summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorCarlton Gibson <carlton.gibson@noumenal.es>2022-12-13 16:15:25 +0100
committerCarlton Gibson <carlton.gibson@noumenal.es>2022-12-22 10:41:12 +0100
commit0bd2c0c9015b53c41394a1c0989afbfd94dc2830 (patch)
tree6b24758335cf10eeedfdf7dec50cda3500796305 /tests
parentae0899be0d787fbfc5f5ab2b18c5a8219d822d2b (diff)
Fixed #33735 -- Added async support to StreamingHttpResponse.
Thanks to Florian Vazelle for initial exploratory work, and to Nick Pope and Mariusz Felisiak for review.
Diffstat (limited to 'tests')
-rw-r--r--tests/asgi/tests.py11
-rw-r--r--tests/httpwrappers/tests.py36
-rw-r--r--tests/middleware/tests.py22
3 files changed, 69 insertions, 0 deletions
diff --git a/tests/asgi/tests.py b/tests/asgi/tests.py
index 4e51c2d9fe..61d040b45b 100644
--- a/tests/asgi/tests.py
+++ b/tests/asgi/tests.py
@@ -12,6 +12,7 @@ from django.db import close_old_connections
from django.test import (
AsyncRequestFactory,
SimpleTestCase,
+ ignore_warnings,
modify_settings,
override_settings,
)
@@ -58,6 +59,13 @@ class ASGITest(SimpleTestCase):
# Allow response.close() to finish.
await communicator.wait()
+ # Python's file API is not async compatible. A third-party library such
+ # as https://github.com/Tinche/aiofiles allows passing the file to
+ # FileResponse as an async interator. With a sync iterator
+ # StreamingHTTPResponse triggers a warning when iterating the file.
+ # assertWarnsMessage is not async compatible, so ignore_warnings for the
+ # test.
+ @ignore_warnings(module="django.http.response")
async def test_file_response(self):
"""
Makes sure that FileResponse works over ASGI.
@@ -91,6 +99,8 @@ class ASGITest(SimpleTestCase):
self.assertEqual(value, b"text/plain")
else:
raise
+
+ # Warning ignored here.
response_body = await communicator.receive_output()
self.assertEqual(response_body["type"], "http.response.body")
self.assertEqual(response_body["body"], test_file_contents)
@@ -106,6 +116,7 @@ class ASGITest(SimpleTestCase):
"django.contrib.staticfiles.finders.FileSystemFinder",
],
)
+ @ignore_warnings(module="django.http.response")
async def test_static_file_response(self):
application = ASGIStaticFilesHandler(get_asgi_application())
# Construct HTTP request.
diff --git a/tests/httpwrappers/tests.py b/tests/httpwrappers/tests.py
index e1920e2eda..fa2c8fd5d2 100644
--- a/tests/httpwrappers/tests.py
+++ b/tests/httpwrappers/tests.py
@@ -720,6 +720,42 @@ class StreamingHttpResponseTests(SimpleTestCase):
'<StreamingHttpResponse status_code=200, "text/html; charset=utf-8">',
)
+ async def test_async_streaming_response(self):
+ async def async_iter():
+ yield b"hello"
+ yield b"world"
+
+ r = StreamingHttpResponse(async_iter())
+
+ chunks = []
+ async for chunk in r:
+ chunks.append(chunk)
+ self.assertEqual(chunks, [b"hello", b"world"])
+
+ def test_async_streaming_response_warning(self):
+ async def async_iter():
+ yield b"hello"
+ yield b"world"
+
+ r = StreamingHttpResponse(async_iter())
+
+ msg = (
+ "StreamingHttpResponse must consume asynchronous iterators in order to "
+ "serve them synchronously. Use a synchronous iterator instead."
+ )
+ with self.assertWarnsMessage(Warning, msg):
+ self.assertEqual(list(r), [b"hello", b"world"])
+
+ async def test_sync_streaming_response_warning(self):
+ r = StreamingHttpResponse(iter(["hello", "world"]))
+
+ msg = (
+ "StreamingHttpResponse must consume synchronous iterators in order to "
+ "serve them asynchronously. Use an asynchronous iterator instead."
+ )
+ with self.assertWarnsMessage(Warning, msg):
+ self.assertEqual(b"hello", await r.__aiter__().__anext__())
+
class FileCloseTests(SimpleTestCase):
def setUp(self):
diff --git a/tests/middleware/tests.py b/tests/middleware/tests.py
index 1b8efe1a3e..e29d32ad74 100644
--- a/tests/middleware/tests.py
+++ b/tests/middleware/tests.py
@@ -899,6 +899,28 @@ class GZipMiddlewareTest(SimpleTestCase):
self.assertEqual(r.get("Content-Encoding"), "gzip")
self.assertFalse(r.has_header("Content-Length"))
+ async def test_compress_async_streaming_response(self):
+ """
+ Compression is performed on responses with async streaming content.
+ """
+
+ async def get_stream_response(request):
+ async def iterator():
+ for chunk in self.sequence:
+ yield chunk
+
+ resp = StreamingHttpResponse(iterator())
+ resp["Content-Type"] = "text/html; charset=UTF-8"
+ return resp
+
+ r = await GZipMiddleware(get_stream_response)(self.req)
+ self.assertEqual(
+ self.decompress(b"".join([chunk async for chunk in r])),
+ b"".join(self.sequence),
+ )
+ self.assertEqual(r.get("Content-Encoding"), "gzip")
+ self.assertFalse(r.has_header("Content-Length"))
+
def test_compress_streaming_response_unicode(self):
"""
Compression is performed on responses with streaming Unicode content.