async def test_request_send_push_promise() -> None: push_promise: Tuple[str, Headers] = None async def _push(path: str, headers: Headers) -> None: nonlocal push_promise push_promise = (path, headers) request = Request( 'GET', 'http', '/', b'a=b&a=c', CIMultiDict({ 'host': 'quart.com', 'Content-Type': 'application/x-www-form-urlencoded', "Accept": "*/*", "Accept-Encoding": "gzip", "User-Agent": "quart", }), "", "2", send_push_promise=_push, ) await request.send_push_promise("/") assert push_promise[0] == "/" valid_headers = { "Accept": "*/*", "Accept-Encoding": "gzip", "User-Agent": "quart", } assert len(push_promise[1]) == len(valid_headers) for name, value in valid_headers.items(): assert push_promise[1][name] == value
def test_query_string() -> None: base_request_websocket = BaseRequestWebsocket( "GET", "http", "/", b"a=b&a=c&f", CIMultiDict({"host": "localhost"}), "", "1.1") assert base_request_websocket.query_string == b"a=b&a=c&f" assert base_request_websocket.args.getlist("a") == ["b", "c"] assert base_request_websocket.args["f"] == ""
def test_basic_authorization() -> None: headers = CIMultiDict() headers['Authorization'] = "Basic {}".format(b64encode(b'identity:secret').decode('ascii')) request = BaseRequestWebsocket('GET', 'http', '/', headers) auth = request.authorization assert auth.username == 'identity' assert auth.password == 'secret'
def test_access_log_standard_atoms() -> None: request_headers = CIMultiDict({ 'Referer': 'stet.io', 'Remote-Addr': '127.0.0.1', 'User-Agent': 'quart', }) request = Request('GET', '/?x=y', request_headers) response = Response('Hello', 202) atoms = AccessLogAtoms(request, response, 'h2', 0.000023) assert atoms['h'] == '127.0.0.1' assert atoms['l'] == '-' assert time.strptime(atoms['t'], '[%d/%b/%Y:%H:%M:%S %z]') assert int(atoms['s']) == 202 assert atoms['m'] == 'GET' assert atoms['U'] == '/' assert atoms['q'] == 'x=y' assert atoms['H'] == 'h2' assert int(atoms['b']) == len('Hello') assert int(atoms['B']) == len('Hello') assert atoms['f'] == 'stet.io' assert atoms['a'] == 'quart' assert atoms['p'] == f"<{os.getpid()}>" assert atoms['not-atom'] == '-' assert atoms['T'] == 0 assert atoms['D'] == 23 assert atoms['L'] == '0.000023'
def test_access_log_header_atoms() -> None: request_headers = CIMultiDict({ 'Random': 'Request', 'Remote-Addr': '127.0.0.1', }) request = Request('GET', '/', request_headers) response_headers = CIMultiDict({ 'Random': 'Response', }) response = Response('Hello', 200, response_headers) atoms = AccessLogAtoms(request, response, 'h2', 0) assert atoms['{random}i'] == 'Request' assert atoms['{RANDOM}i'] == 'Request' assert atoms['{not-atom}i'] == '-' assert atoms['{random}o'] == 'Response' assert atoms['{RANDOM}o'] == 'Response'
def test_url_structure( method: str, scheme: str, host: str, path: str, query_string: bytes, expected_path: str, expected_full_path: str, expected_url: str, expected_base_url: str, expected_url_root: str, expected_host_url: str, ) -> None: base_request_websocket = BaseRequestWebsocket( method, scheme, path, query_string, CIMultiDict({'host': host}), "", "1.1", ) assert base_request_websocket.path == expected_path assert base_request_websocket.query_string == query_string assert base_request_websocket.full_path == expected_full_path assert base_request_websocket.url == expected_url assert base_request_websocket.base_url == expected_base_url assert base_request_websocket.url_root == expected_url_root assert base_request_websocket.host_url == expected_host_url assert base_request_websocket.host == host assert base_request_websocket.method == method assert base_request_websocket.scheme == scheme assert base_request_websocket.is_secure == scheme.endswith('s')
async def test_after_this_request() -> None: async with RequestContext( Quart(__name__), Request('GET', 'http', '/', CIMultiDict()), ) as context: after_this_request(lambda: 'hello') assert context._after_request_functions[0]() == 'hello'
def test_multidict_type_conversion( dict_class: Union[CIMultiDict, MultiDict]) -> None: data = CIMultiDict() data['x'] = '2' data['y'] = 'b' assert data.get('x', type=int) == 2 assert data.get('y', default=None, type=int) is None
async def test_has_request_context() -> None: async with RequestContext(Quart(__name__), Request('GET', 'http', '/', CIMultiDict())): assert has_request_context() is True assert has_app_context() is True assert has_request_context() is False assert has_app_context() is False
def test_bad_request_if_websocket_route() -> None: app = Quart(__name__) url_adapter = Mock() url_adapter.match.return_value = Rule('/', {'GET'}, 'index', is_websocket=True), {} app.create_url_adapter = lambda *_: url_adapter # type: ignore request = Request('GET', 'http', '/', b'', CIMultiDict()) RequestContext(app, request) assert isinstance(request.routing_exception, BadRequest)
def test_basic_authorization() -> None: headers = CIMultiDict() headers["Authorization"] = "Basic {}".format( b64encode(b"identity:secret").decode("ascii")) request = BaseRequestWebsocket("GET", "http", "/", b"", headers, "", "1.1") auth = request.authorization assert auth.username == "identity" assert auth.password == "secret"
def test_access_log_environ_atoms() -> None: os.environ['Random'] = 'Environ' request_headers = CIMultiDict({ 'Remote-Addr': '127.0.0.1', }) request = Request('GET', '/', request_headers) response = Response('Hello', 200) atoms = AccessLogAtoms(request, response, 'h2', 0) assert atoms['{random}e'] == 'Environ'
def test_request_context_match() -> None: app = Quart(__name__) url_adapter = Mock() url_adapter.match.return_value = ('Rule', {'arg': 'value'}) app.create_url_adapter = lambda *_: url_adapter # type: ignore request = Request('GET', '/', CIMultiDict(), None) RequestContext(app, request) assert request.url_rule == 'Rule' assert request.view_args == {'arg': 'value'}
def test_bad_request_if_websocket_route() -> None: app = Quart(__name__) url_adapter = Mock() url_adapter.match.side_effect = BadRequest() app.create_url_adapter = lambda *_: url_adapter # type: ignore request = Request( "GET", "http", "/", b"", CIMultiDict(), "", "1.1", send_push_promise=no_op_push ) RequestContext(app, request) assert isinstance(request.routing_exception, BadRequest)
def test_request_exceeds_max_content_length() -> None: max_content_length = 5 headers = CIMultiDict() headers['Content-Length'] = str(max_content_length + 1) with pytest.raises(RequestEntityTooLarge): Request('POST', 'http', '/', headers, max_content_length=max_content_length)
def test_request_context_match() -> None: app = Quart(__name__) url_adapter = Mock() rule = Rule('/', ['GET'], 'index') url_adapter.match.return_value = (rule, {'arg': 'value'}) app.create_url_adapter = lambda *_: url_adapter # type: ignore request = Request('GET', 'http', '/', CIMultiDict()) RequestContext(app, request) assert request.url_rule == rule assert request.view_args == {'arg': 'value'}
def test_bad_request_if_websocket_missmatch( request_factory: object, context_class: object, is_websocket: bool, ) -> None: app = Quart(__name__) url_adapter = Mock() url_adapter.match.return_value = Rule('/', {'GET'}, 'index', is_websocket=is_websocket), {} app.create_url_adapter = lambda *_: url_adapter # type: ignore request_websocket = request_factory('GET', '/', CIMultiDict()) # type: ignore context_class(app, request_websocket) # type: ignore assert isinstance(request_websocket.routing_exception, BadRequest)
def test_request_context_matching_error( exception_type: Exception, exception_instance: Exception, ) -> None: app = Quart(__name__) url_adapter = Mock() url_adapter.match.side_effect = exception_instance app.create_url_adapter = lambda *_: url_adapter # type: ignore request = Request('GET', 'http', '/', b'', CIMultiDict()) RequestContext(app, request) assert isinstance(request.routing_exception, exception_type) # type: ignore
async def test_overlapping_request_ctx() -> None: app = Quart(__name__) request = Request('GET', 'http', '/', b'', CIMultiDict()) ctx1 = app.request_context(request) await ctx1.__aenter__() ctx2 = app.request_context(request) await ctx2.__aenter__() await ctx1.__aexit__(None, None, None) assert has_app_context() # Ensure the app context still exists for ctx2 await ctx2.__aexit__(None, None, None)
async def test_app_handle_websocket_asyncio_cancelled_error() -> None: app = Quart(__name__) @app.websocket("/") async def index() -> NoReturn: raise asyncio.CancelledError() websocket = app.websocket_class("/", b"", "wss", CIMultiDict(), "", "1.1", None, None, None, None) with pytest.raises(asyncio.CancelledError): await app.handle_websocket(websocket)
async def test_overlapping_websocket_ctx() -> None: app = Quart(__name__) websocket = Websocket("/", b"", "ws", CIMultiDict(), "", "1.1", [], None, None, None) ctx1 = app.websocket_context(websocket) await ctx1.__aenter__() ctx2 = app.websocket_context(websocket) await ctx2.__aenter__() await ctx1.__aexit__(None, None, None) assert has_app_context() # Ensure the app context still exists for ctx2 await ctx2.__aexit__(None, None, None)
def test_query_string() -> None: base_request_websocket = BaseRequestWebsocket( 'GET', 'http', '/', b'a=b&a=c&f', CIMultiDict({'host': 'localhost'}), ) assert base_request_websocket.query_string == b'a=b&a=c&f' assert base_request_websocket.args.getlist('a') == ['b', 'c'] assert base_request_websocket.args['f'] == ''
def test_secure_cookie_session_interface_open_session() -> None: session = SecureCookieSession() session['something'] = 'else' interface = SecureCookieSessionInterface() app = Quart(__name__) app.secret_key = 'secret' response = Response('') interface.save_session(app, session, response) request = Request('GET', 'http', '/', b'', CIMultiDict()) request.headers['Cookie'] = response.headers['Set-Cookie'] new_session = interface.open_session(app, request) assert new_session == session
async def test_request_get_data_timeout() -> None: request = Request( 'POST', 'http', '/', b'', CIMultiDict(), body_timeout=1, send_push_promise=no_op_push, ) with pytest.raises(RequestTimeout): await request.get_data()
def test_request_context_matching_error( exception_type: Exception, exception_instance: Exception ) -> None: app = Quart(__name__) url_adapter = Mock() url_adapter.match.side_effect = exception_instance app.create_url_adapter = lambda *_: url_adapter # type: ignore request = Request( "GET", "http", "/", b"", CIMultiDict(), "", "1.1", send_push_promise=no_op_push ) RequestContext(app, request) assert isinstance(request.routing_exception, exception_type) # type: ignore
def test_request_context_match() -> None: app = Quart(__name__) url_adapter = Mock() rule = Rule("/", {"GET"}, "index") url_adapter.match.return_value = (rule, {"arg": "value"}) app.create_url_adapter = lambda *_: url_adapter # type: ignore request = Request( "GET", "http", "/", b"", CIMultiDict(), "", "1.1", send_push_promise=no_op_push ) RequestContext(app, request) assert request.url_rule == rule assert request.view_args == {"arg": "value"}
async def test_overlapping_request_ctx() -> None: app = Quart(__name__) request = Request( "GET", "http", "/", b"", CIMultiDict(), "", "1.1", send_push_promise=no_op_push ) ctx1 = app.request_context(request) await ctx1.__aenter__() ctx2 = app.request_context(request) await ctx2.__aenter__() await ctx1.__aexit__(None, None, None) assert has_app_context() # Ensure the app context still exists for ctx2 await ctx2.__aexit__(None, None, None)
async def test_request_values() -> None: request = Request( 'GET', 'http', '/?a=b&a=c', CIMultiDict({ 'host': 'quart.com', 'Content-Type': 'application/x-www-form-urlencoded' }), ) request.body.append(urlencode({'a': 'd'}).encode()) request.body.set_complete() assert (await request.values).getlist('a') == ['b', 'c', 'd']
def make_test_headers_path_and_query_string( app: Quart, path: str, headers: Union[dict, CIMultiDict] = None, query_string: dict = None, ) -> Tuple[CIMultiDict, str, bytes]: """Make the headers and path with defaults for testing. Arguments: app: The application to test against. path: The path to request. If the query_string argument is not defined this argument will be partitioned on a '?' with the following part being considered the query_string. headers: Initial headers to send. query_string: To send as a dictionary, alternatively the query_string can be determined from the path. """ if headers is None: headers = CIMultiDict() elif isinstance(headers, CIMultiDict): headers = headers elif headers is not None: headers = CIMultiDict(headers) headers.setdefault('Remote-Addr', '127.0.0.1') headers.setdefault('User-Agent', 'Quart') headers.setdefault('host', app.config['SERVER_NAME'] or 'localhost') if '?' in path and query_string is not None: raise ValueError( 'Query string is defined in the path and as an argument') if query_string is None: path, _, query_string_raw = path.partition('?') if query_string_raw: query_string_raw = urlencode(parse_qs(query_string_raw), doseq=True) else: query_string_raw = urlencode(query_string, doseq=True) query_string_bytes = query_string_raw.encode() return headers, path, query_string_bytes # type: ignore
async def test_request_exceeds_max_content_length() -> None: max_content_length = 5 headers = CIMultiDict() headers['Content-Length'] = str(max_content_length + 1) request = Request( 'POST', 'http', '/', b'', headers, max_content_length=max_content_length, send_push_promise=no_op_push, ) with pytest.raises(RequestEntityTooLarge): await request.get_data()