import os import sys 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.test import SimpleTestCase from django.utils.asyncio import async_unsafe from .models import SimpleModel @skipIf(sys.platform == 'win32' and (3, 8, 0) < sys.version_info < (3, 8, 1), 'https://bugs.python.org/issue38563') class CacheTest(SimpleTestCase): def test_caches_local(self): @async_to_sync async def async_cache(): return caches[DEFAULT_CACHE_ALIAS] cache_1 = async_cache() cache_2 = async_cache() self.assertIs(cache_1, cache_2) @skipIf(sys.platform == 'win32' and (3, 8, 0) < sys.version_info < (3, 8, 1), 'https://bugs.python.org/issue38563') class DatabaseConnectionTest(SimpleTestCase): """A database connection cannot be used in an async context.""" async def test_get_async_connection(self): with self.assertRaises(SynchronousOnlyOperation): list(SimpleModel.objects.all()) @skipIf(sys.platform == 'win32' and (3, 8, 0) < sys.version_info < (3, 8, 1), 'https://bugs.python.org/issue38563') class AsyncUnsafeTest(SimpleTestCase): """ async_unsafe decorator should work correctly and returns the correct message. """ @async_unsafe def dangerous_method(self): return True async def test_async_unsafe(self): # async_unsafe decorator catches bad access and returns the right # message. msg = ( 'You cannot call this from an async context - use a thread or ' 'sync_to_async.' ) with self.assertRaisesMessage(SynchronousOnlyOperation, msg): self.dangerous_method() @mock.patch.dict(os.environ, {'DJANGO_ALLOW_ASYNC_UNSAFE': 'true'}) @async_to_sync # mock.patch() is not async-aware. async def test_async_unsafe_suppressed(self): # Decorator doesn't trigger check when the environment variable to # suppress it is set. try: self.dangerous_method() except SynchronousOnlyOperation: self.fail('SynchronousOnlyOperation should not be raised.')