tests.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import asyncio
  2. import sys
  3. from unittest import skipIf
  4. from asgiref.testing import ApplicationCommunicator
  5. from django.core.asgi import get_asgi_application
  6. from django.core.signals import request_started
  7. from django.db import close_old_connections
  8. from django.test import AsyncRequestFactory, SimpleTestCase, override_settings
  9. from .urls import test_filename
  10. @skipIf(sys.platform == 'win32' and (3, 8, 0) < sys.version_info < (3, 8, 1), 'https://bugs.python.org/issue38563')
  11. @override_settings(ROOT_URLCONF='asgi.urls')
  12. class ASGITest(SimpleTestCase):
  13. async_request_factory = AsyncRequestFactory()
  14. def setUp(self):
  15. request_started.disconnect(close_old_connections)
  16. def tearDown(self):
  17. request_started.connect(close_old_connections)
  18. async def test_get_asgi_application(self):
  19. """
  20. get_asgi_application() returns a functioning ASGI callable.
  21. """
  22. application = get_asgi_application()
  23. # Construct HTTP request.
  24. scope = self.async_request_factory._base_scope(path='/')
  25. communicator = ApplicationCommunicator(application, scope)
  26. await communicator.send_input({'type': 'http.request'})
  27. # Read the response.
  28. response_start = await communicator.receive_output()
  29. self.assertEqual(response_start['type'], 'http.response.start')
  30. self.assertEqual(response_start['status'], 200)
  31. self.assertEqual(
  32. set(response_start['headers']),
  33. {
  34. (b'Content-Length', b'12'),
  35. (b'Content-Type', b'text/html; charset=utf-8'),
  36. },
  37. )
  38. response_body = await communicator.receive_output()
  39. self.assertEqual(response_body['type'], 'http.response.body')
  40. self.assertEqual(response_body['body'], b'Hello World!')
  41. async def test_file_response(self):
  42. """
  43. Makes sure that FileResponse works over ASGI.
  44. """
  45. application = get_asgi_application()
  46. # Construct HTTP request.
  47. scope = self.async_request_factory._base_scope(path='/file/')
  48. communicator = ApplicationCommunicator(application, scope)
  49. await communicator.send_input({'type': 'http.request'})
  50. # Get the file content.
  51. with open(test_filename, 'rb') as test_file:
  52. test_file_contents = test_file.read()
  53. # Read the response.
  54. response_start = await communicator.receive_output()
  55. self.assertEqual(response_start['type'], 'http.response.start')
  56. self.assertEqual(response_start['status'], 200)
  57. self.assertEqual(
  58. set(response_start['headers']),
  59. {
  60. (b'Content-Length', str(len(test_file_contents)).encode('ascii')),
  61. (b'Content-Type', b'text/plain' if sys.platform == 'win32' else b'text/x-python'),
  62. (b'Content-Disposition', b'inline; filename="urls.py"'),
  63. },
  64. )
  65. response_body = await communicator.receive_output()
  66. self.assertEqual(response_body['type'], 'http.response.body')
  67. self.assertEqual(response_body['body'], test_file_contents)
  68. # Allow response.close() to finish.
  69. await communicator.wait()
  70. async def test_headers(self):
  71. application = get_asgi_application()
  72. communicator = ApplicationCommunicator(
  73. application,
  74. self.async_request_factory._base_scope(
  75. path='/meta/',
  76. headers=[
  77. [b'content-type', b'text/plain; charset=utf-8'],
  78. [b'content-length', b'77'],
  79. [b'referer', b'Scotland'],
  80. [b'referer', b'Wales'],
  81. ],
  82. ),
  83. )
  84. await communicator.send_input({'type': 'http.request'})
  85. response_start = await communicator.receive_output()
  86. self.assertEqual(response_start['type'], 'http.response.start')
  87. self.assertEqual(response_start['status'], 200)
  88. self.assertEqual(
  89. set(response_start['headers']),
  90. {
  91. (b'Content-Length', b'19'),
  92. (b'Content-Type', b'text/plain; charset=utf-8'),
  93. },
  94. )
  95. response_body = await communicator.receive_output()
  96. self.assertEqual(response_body['type'], 'http.response.body')
  97. self.assertEqual(response_body['body'], b'From Scotland,Wales')
  98. async def test_get_query_string(self):
  99. application = get_asgi_application()
  100. for query_string in (b'name=Andrew', 'name=Andrew'):
  101. with self.subTest(query_string=query_string):
  102. scope = self.async_request_factory._base_scope(
  103. path='/',
  104. query_string=query_string,
  105. )
  106. communicator = ApplicationCommunicator(application, scope)
  107. await communicator.send_input({'type': 'http.request'})
  108. response_start = await communicator.receive_output()
  109. self.assertEqual(response_start['type'], 'http.response.start')
  110. self.assertEqual(response_start['status'], 200)
  111. response_body = await communicator.receive_output()
  112. self.assertEqual(response_body['type'], 'http.response.body')
  113. self.assertEqual(response_body['body'], b'Hello Andrew!')
  114. async def test_disconnect(self):
  115. application = get_asgi_application()
  116. scope = self.async_request_factory._base_scope(path='/')
  117. communicator = ApplicationCommunicator(application, scope)
  118. await communicator.send_input({'type': 'http.disconnect'})
  119. with self.assertRaises(asyncio.TimeoutError):
  120. await communicator.receive_output()
  121. async def test_wrong_connection_type(self):
  122. application = get_asgi_application()
  123. scope = self.async_request_factory._base_scope(path='/', type='other')
  124. communicator = ApplicationCommunicator(application, scope)
  125. await communicator.send_input({'type': 'http.request'})
  126. msg = 'Django can only handle ASGI/HTTP connections, not other.'
  127. with self.assertRaisesMessage(ValueError, msg):
  128. await communicator.receive_output()
  129. async def test_non_unicode_query_string(self):
  130. application = get_asgi_application()
  131. scope = self.async_request_factory._base_scope(path='/', query_string=b'\xff')
  132. communicator = ApplicationCommunicator(application, scope)
  133. await communicator.send_input({'type': 'http.request'})
  134. response_start = await communicator.receive_output()
  135. self.assertEqual(response_start['type'], 'http.response.start')
  136. self.assertEqual(response_start['status'], 400)
  137. response_body = await communicator.receive_output()
  138. self.assertEqual(response_body['type'], 'http.response.body')
  139. self.assertEqual(response_body['body'], b'')