1414from servicelib .aiohttp import status
1515from simcore_service_webserver .errors import WebServerBaseError
1616from simcore_service_webserver .exceptions_handlers import (
17+ ExceptionToHttpErrorMap ,
1718 HttpErrorInfo ,
1819 create_decorator_from_exception_handler ,
1920)
2930# Some custom errors in my service
3031
3132
32- class BasePluginError (WebServerBaseError ):
33+ class BaseError (WebServerBaseError ):
3334 ...
3435
3536
36- class OneError (BasePluginError ):
37+ class OneError (BaseError ):
3738 ...
3839
3940
40- class OtherError (BasePluginError ):
41+ class OtherError (BaseError ):
4142 ...
4243
4344
4445def test_sort_concrete_first ():
45- assert _sort_exceptions_by_specificity ([Exception , BasePluginError ]) == [
46- BasePluginError ,
46+ assert _sort_exceptions_by_specificity ([Exception , BaseError ]) == [
47+ BaseError ,
4748 Exception ,
4849 ]
4950
5051 assert _sort_exceptions_by_specificity (
51- [Exception , BasePluginError ], concrete_first = False
52+ [Exception , BaseError ], concrete_first = False
5253 ) == [
5354 Exception ,
54- BasePluginError ,
55+ BaseError ,
5556 ]
5657
5758
@@ -62,7 +63,7 @@ def test_sort_exceptions_by_specificity():
6263 Exception ,
6364 OtherError ,
6465 OneError ,
65- BasePluginError ,
66+ BaseError ,
6667 ValueError ,
6768 ArithmeticError ,
6869 ZeroDivisionError ,
@@ -82,7 +83,6 @@ def fake_request() -> web.Request:
8283async def test_factory__create_exception_handler_from_http_error (
8384 fake_request : web .Request ,
8485):
85-
8686 one_error_to_404 = create_exception_handler_from_http_error (
8787 OneError ,
8888 status_code = status .HTTP_404_NOT_FOUND ,
@@ -92,12 +92,14 @@ async def test_factory__create_exception_handler_from_http_error(
9292 # calling exception handler
9393 caught = OneError ()
9494 response = await one_error_to_404 (fake_request , caught )
95- assert response
9695 assert response .status == status .HTTP_404_NOT_FOUND
97- assert "one error" in response .text
96+ assert response .text is not None
97+ assert "one error message" in response .text
9898
9999
100- async def test_create_exception_handler_from_http_error_map (fake_request : web .Request ):
100+ async def test_factory__create_exception_handler_from_http_error_map (
101+ fake_request : web .Request ,
102+ ):
101103
102104 # call exception handler factory
103105 exc_handler = create_exception_handler_from_http_error_map (
@@ -109,8 +111,10 @@ async def test_create_exception_handler_from_http_error_map(fake_request: web.Re
109111 )
110112
111113 # Converts exception in map
112- got_exc = await exc_handler (fake_request , OneError ())
114+ with pytest .raises (web .HTTPBadRequest ) as exc_info :
115+ await exc_handler (fake_request , OneError ())
113116
117+ got_exc = exc_info .value
114118 assert isinstance (got_exc , web .HTTPBadRequest )
115119 assert got_exc .reason == "Error One mapped to 400"
116120
@@ -120,46 +124,53 @@ async def test_create_exception_handler_from_http_error_map(fake_request: web.Re
120124
121125
122126async def test__handled_exception_context_manager (fake_request : web .Request ):
123- async def _suppress_handler (request , exception ):
127+
128+ expected_response = web .json_response ({"error" : {"msg" : "Foo" }})
129+
130+ async def _custom_handler (request , exception ):
124131 assert request == fake_request
125132 assert isinstance (
126- exception , BasePluginError
133+ exception , BaseError
127134 ), "only BasePluginError exceptions should call this handler"
128- return None # noqa: RET501, PLR1711
135+ return expected_response
129136
130- async def _fun (raises ):
131- async with _handled_exception_context_manager (
132- BasePluginError , _suppress_handler , request = fake_request
133- ):
134- raise raises
137+ exc_handling_ctx = _handled_exception_context_manager (
138+ BaseError , _custom_handler , request = fake_request
139+ )
140+
141+ # handles any BaseError returning a response
142+ async with exc_handling_ctx as ctx :
143+ raise OneError
144+ assert ctx .response == expected_response
135145
136- # checks
137- await _fun ( raises = OneError )
138- await _fun ( raises = OtherError )
146+ async with exc_handling_ctx as ctx :
147+ raise OtherError
148+ assert ctx . response == expected_response
139149
150+ # otherwise thru
140151 with pytest .raises (ArithmeticError ):
141- await _fun (raises = ArithmeticError )
152+ async with exc_handling_ctx :
153+ raise ArithmeticError
142154
143155
144156async def test_create_decorator_from_exception_handler (
145157 caplog : pytest .LogCaptureFixture ,
146158):
159+ # Create an SINGLE exception handler that acts as umbrella for all these exceptions
160+ http_error_map : ExceptionToHttpErrorMap = {
161+ OneError : HttpErrorInfo (
162+ status .HTTP_503_SERVICE_UNAVAILABLE ,
163+ "Human readable error transmitted to the front-end" ,
164+ )
165+ }
147166
148- exc_handler = create_exception_handler_from_http_error_map (
149- {
150- OneError : HttpErrorInfo (
151- status .HTTP_503_SERVICE_UNAVAILABLE ,
152- "Human readable error transmitted to the front-end" ,
153- )
154- }
155- )
156-
157- _handle_exceptions = create_decorator_from_exception_handler (
158- exception_types = BasePluginError , # <--- FIXME" this is redundant because exception has been already passed in exc_handler!
167+ exc_handler = create_exception_handler_from_http_error_map (http_error_map )
168+ _exc_handling_ctx = create_decorator_from_exception_handler (
169+ exception_types = BaseError , # <--- FIXME" this is redundant because exception has been already passed in exc_handler!
159170 exception_handler = exc_handler ,
160171 )
161172
162- @_handle_exceptions
173+ @_exc_handling_ctx
163174 async def _rest_handler (request : web .Request ) -> web .Response :
164175 if request .query .get ("raise" ) == "OneError" :
165176 raise OneError
@@ -193,8 +204,9 @@ async def _rest_handler(request: web.Request) -> web.Response:
193204 assert resp .status == status .HTTP_503_SERVICE_UNAVAILABLE
194205 assert "front-end" in resp .reason
195206
196- assert caplog .records
207+ assert caplog .records , "Expected 5XX troubleshooting logged as error"
197208 assert caplog .records [0 ].levelno == logging .ERROR
209+
198210 # typically capture by last
199211 with pytest .raises (ArithmeticError ):
200212 resp = await _rest_handler (
0 commit comments