Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

280 строки
11KB

  1. import os
  2. import sys
  3. import unittest
  4. import six
  5. if six.PY3:
  6. from unittest import mock
  7. else:
  8. import mock
  9. if sys.version_info >= (3, 5):
  10. import asyncio
  11. from engineio.async_drivers import asgi as async_asgi
  12. def AsyncMock(*args, **kwargs):
  13. """Return a mock asynchronous function."""
  14. m = mock.MagicMock(*args, **kwargs)
  15. async def mock_coro(*args, **kwargs):
  16. return m(*args, **kwargs)
  17. mock_coro.mock = m
  18. return mock_coro
  19. def _run(coro):
  20. """Run the given coroutine."""
  21. return asyncio.get_event_loop().run_until_complete(coro)
  22. @unittest.skipIf(sys.version_info < (3, 5), 'only for Python 3.5+')
  23. class AsgiTests(unittest.TestCase):
  24. def test_create_app(self):
  25. app = async_asgi.ASGIApp(
  26. 'eio', 'other_app', static_files='static_files',
  27. engineio_path='/foo')
  28. self.assertEqual(app.engineio_server, 'eio')
  29. self.assertEqual(app.other_asgi_app, 'other_app')
  30. self.assertEqual(app.static_files, 'static_files')
  31. self.assertEqual(app.engineio_path, 'foo'),
  32. def test_engineio_routing(self):
  33. mock_server = mock.MagicMock()
  34. mock_server.handle_request = AsyncMock()
  35. app = async_asgi.ASGIApp(mock_server)
  36. scope = {'type': 'http', 'path': '/engine.io/'}
  37. _run(app(scope, 'receive', 'send'))
  38. mock_server.handle_request.mock.assert_called_once_with(
  39. scope, 'receive', 'send')
  40. def test_other_app_routing(self):
  41. other_app = AsyncMock()
  42. app = async_asgi.ASGIApp('eio', other_app)
  43. scope = {'type': 'http', 'path': '/foo'}
  44. _run(app(scope, 'receive', 'send'))
  45. other_app.mock.assert_called_once_with(scope, 'receive', 'send')
  46. def test_static_file_routing(self):
  47. root_dir = os.path.dirname(__file__)
  48. app = async_asgi.ASGIApp('eio', static_files={
  49. '/': root_dir + '/index.html',
  50. '/foo': {'content_type': 'text/plain',
  51. 'filename': root_dir + '/index.html'},
  52. '/static': root_dir,
  53. '/static/test/': root_dir + '/',
  54. })
  55. def check_path(path, status_code, content_type, body):
  56. scope = {'type': 'http', 'path': path}
  57. receive = AsyncMock(return_value={'type': 'http.request'})
  58. send = AsyncMock()
  59. _run(app(scope, receive, send))
  60. send.mock.assert_any_call({
  61. 'type': 'http.response.start',
  62. 'status': status_code,
  63. 'headers': [(b'Content-Type', content_type.encode('utf-8'))]})
  64. send.mock.assert_any_call({
  65. 'type': 'http.response.body',
  66. 'body': body.encode('utf-8')})
  67. check_path('/', 200, 'text/html', '<html></html>\n')
  68. check_path('/foo', 200, 'text/plain', '<html></html>\n')
  69. check_path('/static/index.html', 200, 'text/html', '<html></html>\n')
  70. check_path('/static/foo.bar', 404, 'text/plain', 'Not Found')
  71. check_path('/static/test/index.html', 200, 'text/html',
  72. '<html></html>\n')
  73. check_path('/bar/foo', 404, 'text/plain', 'Not Found')
  74. check_path('', 404, 'text/plain', 'Not Found')
  75. app.static_files[''] = 'index.html'
  76. check_path('/static/test/', 200, 'text/html',
  77. '<html></html>\n')
  78. app.static_files[''] = {'filename': 'index.html'}
  79. check_path('/static/test/', 200, 'text/html',
  80. '<html></html>\n')
  81. app.static_files[''] = {'filename': 'index.html',
  82. 'content_type': 'image/gif'}
  83. check_path('/static/test/', 200, 'image/gif',
  84. '<html></html>\n')
  85. app.static_files[''] = {'filename': 'test.gif'}
  86. check_path('/static/test/', 404, 'text/plain', 'Not Found')
  87. app.static_files = {}
  88. check_path('/static/test/index.html', 404, 'text/plain', 'Not Found')
  89. def test_lifespan_startup(self):
  90. app = async_asgi.ASGIApp('eio')
  91. scope = {'type': 'lifespan'}
  92. receive = AsyncMock(return_value={'type': 'lifespan.startup'})
  93. send = AsyncMock()
  94. _run(app(scope, receive, send))
  95. send.mock.assert_called_once_with(
  96. {'type': 'lifespan.startup.complete'})
  97. def test_lifespan_shutdown(self):
  98. app = async_asgi.ASGIApp('eio')
  99. scope = {'type': 'lifespan'}
  100. receive = AsyncMock(return_value={'type': 'lifespan.shutdown'})
  101. send = AsyncMock()
  102. _run(app(scope, receive, send))
  103. send.mock.assert_called_once_with(
  104. {'type': 'lifespan.shutdown.complete'})
  105. def test_lifespan_invalid(self):
  106. app = async_asgi.ASGIApp('eio')
  107. scope = {'type': 'lifespan'}
  108. receive = AsyncMock(return_value={'type': 'lifespan.foo'})
  109. send = AsyncMock()
  110. _run(app(scope, receive, send))
  111. send.mock.assert_not_called()
  112. def test_not_found(self):
  113. app = async_asgi.ASGIApp('eio')
  114. scope = {'type': 'http', 'path': '/foo'}
  115. receive = AsyncMock(return_value={'type': 'http.request'})
  116. send = AsyncMock()
  117. _run(app(scope, receive, send))
  118. send.mock.assert_any_call(
  119. {'type': 'http.response.start', 'status': 404,
  120. 'headers': [(b'Content-Type', b'text/plain')]})
  121. send.mock.assert_any_call({'type': 'http.response.body',
  122. 'body': b'Not Found'})
  123. def test_translate_request(self):
  124. receive = AsyncMock(return_value={'type': 'http.request',
  125. 'body': b'hello world'})
  126. send = AsyncMock()
  127. environ = _run(async_asgi.translate_request({
  128. 'type': 'http',
  129. 'method': 'PUT',
  130. 'headers': [(b'a', b'b'), (b'c-c', b'd'), (b'c_c', b'e'),
  131. (b'content-type', b'application/json'),
  132. (b'content-length', b'123')],
  133. 'path': '/foo/bar',
  134. 'query_string': b'baz=1'}, receive, send))
  135. expected_environ = {
  136. 'REQUEST_METHOD': 'PUT',
  137. 'PATH_INFO': '/foo/bar',
  138. 'QUERY_STRING': 'baz=1',
  139. 'CONTENT_TYPE': 'application/json',
  140. 'CONTENT_LENGTH': '123',
  141. 'HTTP_A': 'b',
  142. # 'HTTP_C_C': 'd,e',
  143. 'RAW_URI': '/foo/bar?baz=1',
  144. 'SERVER_PROTOCOL': 'HTTP/1.1',
  145. 'asgi.receive': receive,
  146. 'asgi.send': send
  147. }
  148. for k, v in expected_environ.items():
  149. self.assertEqual(v, environ[k])
  150. self.assertTrue(
  151. environ['HTTP_C_C'] == 'd,e' or environ['HTTP_C_C'] == 'e,d')
  152. body = _run(environ['wsgi.input'].read())
  153. self.assertEqual(body, b'hello world')
  154. def test_translate_request_no_query_string(self):
  155. receive = AsyncMock(return_value={'type': 'http.request',
  156. 'body': b'hello world'})
  157. send = AsyncMock()
  158. environ = _run(async_asgi.translate_request({
  159. 'type': 'http',
  160. 'method': 'PUT',
  161. 'headers': [(b'a', b'b'), (b'c-c', b'd'), (b'c_c', b'e'),
  162. (b'content-type', b'application/json'),
  163. (b'content-length', b'123')],
  164. 'path': '/foo/bar'}, receive, send))
  165. expected_environ = {
  166. 'REQUEST_METHOD': 'PUT',
  167. 'PATH_INFO': '/foo/bar',
  168. 'QUERY_STRING': '',
  169. 'CONTENT_TYPE': 'application/json',
  170. 'CONTENT_LENGTH': '123',
  171. 'HTTP_A': 'b',
  172. # 'HTTP_C_C': 'd,e',
  173. 'RAW_URI': '/foo/bar',
  174. 'SERVER_PROTOCOL': 'HTTP/1.1',
  175. 'asgi.receive': receive,
  176. 'asgi.send': send
  177. }
  178. for k, v in expected_environ.items():
  179. self.assertEqual(v, environ[k])
  180. self.assertTrue(
  181. environ['HTTP_C_C'] == 'd,e' or environ['HTTP_C_C'] == 'e,d')
  182. body = _run(environ['wsgi.input'].read())
  183. self.assertEqual(body, b'hello world')
  184. def test_translate_request_with_large_body(self):
  185. receive = AsyncMock(side_effect=[
  186. {'type': 'http.request', 'body': b'hello ', 'more_body': True},
  187. {'type': 'http.request', 'body': b'world', 'more_body': True},
  188. {'type': 'foo.bar'}, # should stop parsing here
  189. {'type': 'http.request', 'body': b'!!!'},
  190. ])
  191. send = AsyncMock()
  192. environ = _run(async_asgi.translate_request({
  193. 'type': 'http',
  194. 'method': 'PUT',
  195. 'headers': [(b'a', b'b'), (b'c-c', b'd'), (b'c_c', b'e'),
  196. (b'content-type', b'application/json'),
  197. (b'content-length', b'123')],
  198. 'path': '/foo/bar',
  199. 'query_string': b'baz=1'}, receive, send))
  200. expected_environ = {
  201. 'REQUEST_METHOD': 'PUT',
  202. 'PATH_INFO': '/foo/bar',
  203. 'QUERY_STRING': 'baz=1',
  204. 'CONTENT_TYPE': 'application/json',
  205. 'CONTENT_LENGTH': '123',
  206. 'HTTP_A': 'b',
  207. # 'HTTP_C_C': 'd,e',
  208. 'RAW_URI': '/foo/bar?baz=1',
  209. 'SERVER_PROTOCOL': 'HTTP/1.1',
  210. 'asgi.receive': receive,
  211. 'asgi.send': send
  212. }
  213. for k, v in expected_environ.items():
  214. self.assertEqual(v, environ[k])
  215. self.assertTrue(
  216. environ['HTTP_C_C'] == 'd,e' or environ['HTTP_C_C'] == 'e,d')
  217. body = _run(environ['wsgi.input'].read())
  218. self.assertEqual(body, b'hello world')
  219. def test_translate_websocket_request(self):
  220. receive = AsyncMock(return_value={'type': 'websocket.connect'})
  221. send = AsyncMock()
  222. _run(async_asgi.translate_request({
  223. 'type': 'websocket',
  224. 'headers': [(b'a', b'b'), (b'c-c', b'd'), (b'c_c', b'e'),
  225. (b'content-type', b'application/json'),
  226. (b'content-length', b'123')],
  227. 'path': '/foo/bar',
  228. 'query_string': b'baz=1'}, receive, send))
  229. send.mock.assert_called_once_with({'type': 'websocket.accept'})
  230. def test_translate_unknown_request(self):
  231. receive = AsyncMock(return_value={'type': 'http.foo'})
  232. send = AsyncMock()
  233. environ = _run(async_asgi.translate_request({
  234. 'type': 'http',
  235. 'path': '/foo/bar',
  236. 'query_string': b'baz=1'}, receive, send))
  237. self.assertEqual(environ, {})
  238. # @mock.patch('async_aiohttp.aiohttp.web.Response')
  239. def test_make_response(self):
  240. environ = {
  241. 'asgi.send': AsyncMock()
  242. }
  243. _run(async_asgi.make_response('202 ACCEPTED', [('foo', 'bar')],
  244. b'payload', environ))
  245. environ['asgi.send'].mock.assert_any_call(
  246. {'type': 'http.response.start', 'status': 202,
  247. 'headers': [(b'foo', b'bar')]})
  248. environ['asgi.send'].mock.assert_any_call(
  249. {'type': 'http.response.body', 'body': b'payload'})