From 95b588a1850c4d100bbf4da6323cbb91043f889d Mon Sep 17 00:00:00 2001 From: Kurt Griffiths Date: Fri, 13 Dec 2019 01:06:02 -0700 Subject: [PATCH] test(asgi): Cover missing branches --- falcon/api_helpers.py | 4 ++-- falcon/app.py | 6 +++++- tests/test_error_handlers.py | 28 +++++++++++++++++++++++++++- tests/test_middleware.py | 26 +++++++++++++++++++++++++- 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/falcon/api_helpers.py b/falcon/api_helpers.py index 787b58ae3..02b0316d2 100644 --- a/falcon/api_helpers.py +++ b/falcon/api_helpers.py @@ -66,14 +66,14 @@ def prepare_middleware(middleware, independent_middleware=False, asgi=False): ) ) - process_resource = _wrap_non_coroutine_unsafe( + process_resource = ( util.get_bound_method(component, 'process_resource_async') or _wrap_non_coroutine_unsafe( util.get_bound_method(component, 'process_resource') ) ) - process_response = _wrap_non_coroutine_unsafe( + process_response = ( util.get_bound_method(component, 'process_response_async') or _wrap_non_coroutine_unsafe( util.get_bound_method(component, 'process_response') diff --git a/falcon/app.py b/falcon/app.py index b99ff5a2f..d4378aacf 100644 --- a/falcon/app.py +++ b/falcon/app.py @@ -633,7 +633,11 @@ def handle(req, resp, ex, params): """ def wrap_old_handler(old_handler): - if iscoroutinefunction(old_handler): + # NOTE(kgriffs): This branch *is* actually tested by + # test_error_handlers.test_handler_signature_shim_asgi() (as + # verified manually via pdb), but for some reason coverage + # tracking isn't picking it up. + if iscoroutinefunction(old_handler): # pragma: no cover @wraps(old_handler) async def handler_async(req, resp, ex, params): await old_handler(ex, req, resp, params) diff --git a/tests/test_error_handlers.py b/tests/test_error_handlers.py index ca730499a..6b0190729 100644 --- a/tests/test_error_handlers.py +++ b/tests/test_error_handlers.py @@ -3,7 +3,7 @@ import falcon from falcon import constants, PY35, testing -from _util import create_app # NOQA +from _util import create_app, disable_asgi_non_coroutine_wrapping # NOQA def capture_error(req, resp, ex, params): @@ -208,3 +208,29 @@ def legacy_handler3(err, rq, rs, prms): client.simulate_delete() client.simulate_get() client.simulate_head() + + def test_handler_signature_shim_asgi(self): + def check_args(ex, req, resp): + assert isinstance(ex, BaseException) + assert isinstance(req, falcon.Request) + assert isinstance(resp, falcon.Response) + + async def legacy_handler(err, rq, rs, prms): + check_args(err, rq, rs) + + app = create_app(True) + app.add_route('/', ErroredClassResource()) + app.add_error_handler(Exception, legacy_handler) + client = testing.TestClient(app) + + client.simulate_get() + + def test_handler_must_be_coroutine_for_asgi(self): + async def legacy_handler(err, rq, rs, prms): + pass + + app = create_app(True) + + with disable_asgi_non_coroutine_wrapping(): + with pytest.raises(ValueError): + app.add_error_handler(Exception, capture_error) diff --git a/tests/test_middleware.py b/tests/test_middleware.py index ec17e92a6..1ca6ad84e 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -4,6 +4,7 @@ import pytest import falcon +import falcon.errors import falcon.testing as testing from _util import create_app # NOQA @@ -854,9 +855,14 @@ def test_http_status_raised_from_error_handler(self, asgi): def _http_error_handler(error, req, resp, params): raise falcon.HTTPStatus(falcon.HTTP_201) + async def _http_error_handler_async(error, req, resp, params): + raise falcon.HTTPStatus(falcon.HTTP_201) + + h = _http_error_handler_async if asgi else _http_error_handler + # NOTE(kgriffs): This will take precedence over the default # handler for facon.HTTPError. - app.add_error_handler(falcon.HTTPError, _http_error_handler) + app.add_error_handler(falcon.HTTPError, h) response = client.simulate_request(path='/', method='POST') assert response.status == falcon.HTTP_201 @@ -949,3 +955,21 @@ def test_api_initialization_with_cors_enabled_and_middleware_param(self, mw, asg client = testing.TestClient(app) result = client.simulate_get() assert result.headers['Access-Control-Allow-Origin'] == '*' + + +def test_async_postfix_method_must_be_coroutine(): + class FaultyComponentA: + def process_request_async(self, req, resp): + pass + + class FaultyComponentB: + def process_resource_async(self, req, resp, resource, params): + pass + + class FaultyComponentC: + def process_response_async(self, req, resp, resource, req_succeeded): + pass + + for mw in (FaultyComponentA, FaultyComponentB, FaultyComponentC): + with pytest.raises(falcon.errors.CompatibilityError): + create_app(True, middleware=[mw()])