|
@@ -1,3 +1,4 @@
|
|
|
+import asyncio
|
|
|
import os
|
|
|
import sys
|
|
|
from unittest import mock, skipIf
|
|
@@ -5,9 +6,11 @@ from unittest import mock, skipIf
|
|
|
from asgiref.sync import async_to_sync
|
|
|
|
|
|
from django.core.cache import DEFAULT_CACHE_ALIAS, caches
|
|
|
-from django.core.exceptions import SynchronousOnlyOperation
|
|
|
+from django.core.exceptions import ImproperlyConfigured, SynchronousOnlyOperation
|
|
|
+from django.http import HttpResponse
|
|
|
from django.test import SimpleTestCase
|
|
|
from django.utils.asyncio import async_unsafe
|
|
|
+from django.views.generic.base import View
|
|
|
|
|
|
from .models import SimpleModel
|
|
|
|
|
@@ -72,3 +75,66 @@ class AsyncUnsafeTest(SimpleTestCase):
|
|
|
self.dangerous_method()
|
|
|
except SynchronousOnlyOperation:
|
|
|
self.fail("SynchronousOnlyOperation should not be raised.")
|
|
|
+
|
|
|
+
|
|
|
+class SyncView(View):
|
|
|
+ def get(self, request, *args, **kwargs):
|
|
|
+ return HttpResponse("Hello (sync) world!")
|
|
|
+
|
|
|
+
|
|
|
+class AsyncView(View):
|
|
|
+ async def get(self, request, *args, **kwargs):
|
|
|
+ return HttpResponse("Hello (async) world!")
|
|
|
+
|
|
|
+
|
|
|
+class ViewTests(SimpleTestCase):
|
|
|
+ def test_views_are_correctly_marked(self):
|
|
|
+ tests = [
|
|
|
+ (SyncView, False),
|
|
|
+ (AsyncView, True),
|
|
|
+ ]
|
|
|
+ for view_cls, is_async in tests:
|
|
|
+ with self.subTest(view_cls=view_cls, is_async=is_async):
|
|
|
+ self.assertIs(view_cls.view_is_async, is_async)
|
|
|
+ callback = view_cls.as_view()
|
|
|
+ self.assertIs(asyncio.iscoroutinefunction(callback), is_async)
|
|
|
+
|
|
|
+ def test_mixed_views_raise_error(self):
|
|
|
+ class MixedView(View):
|
|
|
+ def get(self, request, *args, **kwargs):
|
|
|
+ return HttpResponse("Hello (mixed) world!")
|
|
|
+
|
|
|
+ async def post(self, request, *args, **kwargs):
|
|
|
+ return HttpResponse("Hello (mixed) world!")
|
|
|
+
|
|
|
+ msg = (
|
|
|
+ f"{MixedView.__qualname__} HTTP handlers must either be all sync or all "
|
|
|
+ "async."
|
|
|
+ )
|
|
|
+ with self.assertRaisesMessage(ImproperlyConfigured, msg):
|
|
|
+ MixedView.as_view()
|
|
|
+
|
|
|
+ def test_options_handler_responds_correctly(self):
|
|
|
+ tests = [
|
|
|
+ (SyncView, False),
|
|
|
+ (AsyncView, True),
|
|
|
+ ]
|
|
|
+ for view_cls, is_coroutine in tests:
|
|
|
+ with self.subTest(view_cls=view_cls, is_coroutine=is_coroutine):
|
|
|
+ instance = view_cls()
|
|
|
+ response = instance.options(None)
|
|
|
+ self.assertIs(
|
|
|
+ asyncio.iscoroutine(response),
|
|
|
+ is_coroutine,
|
|
|
+ )
|
|
|
+ if is_coroutine:
|
|
|
+ response = asyncio.run(response)
|
|
|
+
|
|
|
+ self.assertIsInstance(response, HttpResponse)
|
|
|
+
|
|
|
+ def test_base_view_class_is_sync(self):
|
|
|
+ """
|
|
|
+ View and by extension any subclasses that don't define handlers are
|
|
|
+ sync.
|
|
|
+ """
|
|
|
+ self.assertIs(View.view_is_async, False)
|