1111from aiohttp import web
1212from models_library .rest_error import ErrorGet
1313from servicelib .aiohttp import status
14+ from servicelib .mimetype_constants import MIMETYPE_APPLICATION_JSON , MIMETYPE_TEXT_PLAIN
1415from simcore_service_webserver .exception_handling import (
1516 ExceptionHandlersMap ,
1617 HttpErrorInfo ,
2021from simcore_service_webserver .exception_handling_base import (
2122 exception_handling_middleware ,
2223)
24+ from simcore_service_webserver .exception_handling_factory import (
25+ create_http_error_exception_handlers_map ,
26+ )
27+
28+ from services .web .server .tests .conftest import TestClient
2329
2430
2531@pytest .fixture
@@ -29,9 +35,9 @@ def exception_handlers_map(build_method: str) -> ExceptionHandlersMap:
2935 """
3036 exception_handlers_map : ExceptionHandlersMap = {}
3137
32- if build_method == "custom " :
38+ if build_method == "function " :
3339
34- async def _value_error_as_422 (
40+ async def _value_error_as_422_func (
3541 request : web .Request , exception : BaseException
3642 ) -> web .Response :
3743 # custom exception handler
@@ -40,7 +46,7 @@ async def _value_error_as_422(
4046 )
4147
4248 exception_handlers_map = {
43- ValueError : _value_error_as_422 ,
49+ ValueError : _value_error_as_422_func ,
4450 }
4551
4652 elif build_method == "http_map" :
@@ -57,7 +63,7 @@ async def _value_error_as_422(
5763 return exception_handlers_map
5864
5965
60- @pytest .mark .parametrize ("build_method" , ["custom " , "http_map" ])
66+ @pytest .mark .parametrize ("build_method" , ["function " , "http_map" ])
6167async def test_handling_exceptions_decorating_a_route (
6268 aiohttp_client : Callable ,
6369 exception_handlers_map : ExceptionHandlersMap ,
@@ -70,10 +76,11 @@ async def test_handling_exceptions_decorating_a_route(
7076 # adding new routes
7177 routes = web .RouteTableDef ()
7278
73- @routes .get ("/{what}" )
79+ @routes .post ("/{what}" )
7480 @exc_handling # < ----- 2. using decorator
7581 async def _handler (request : web .Request ):
76- match request .match_info ["what" ]:
82+ what = request .match_info ["what" ]
83+ match what :
7784 case "ValueError" :
7885 raise ValueError # handled
7986 case "IndexError" :
@@ -86,49 +93,48 @@ async def _handler(request: web.Request):
8693 # but if it is so ...
8794 raise web .HTTPOk # not-handled
8895
89- return web .Response ()
96+ return web .Response (text = what )
9097
9198 app = web .Application ()
9299 app .add_routes (routes )
93100
94101 # 3. testing from the client side
95- client = await aiohttp_client (app )
102+ client : TestClient = await aiohttp_client (app )
96103
97104 # success
98- resp = await client .get ("/ok" )
105+ resp = await client .post ("/ok" )
99106 assert resp .status == status .HTTP_200_OK
100107
101108 # handled non-HTTPException exception
102- resp = await client .get ("/ValueError" )
109+ resp = await client .post ("/ValueError" )
103110 assert resp .status == status .HTTP_422_UNPROCESSABLE_ENTITY
104111 if build_method == "http_map" :
105112 body = await resp .json ()
106113 error = ErrorGet .model_validate (body ["error" ])
107114 assert error .message == f"{ build_method = } "
108115
109116 # undhandled non-HTTPException
110- resp = await client .get ("/IndexError" )
117+ resp = await client .post ("/IndexError" )
111118 assert resp .status == status .HTTP_500_INTERNAL_SERVER_ERROR
112119
113120 # undhandled HTTPError
114- resp = await client .get ("/HTTPConflict" )
121+ resp = await client .post ("/HTTPConflict" )
115122 assert resp .status == status .HTTP_409_CONFLICT
116123
117124 # undhandled HTTPSuccess
118- resp = await client .get ("/HTTPOk" )
125+ resp = await client .post ("/HTTPOk" )
119126 assert resp .status == status .HTTP_200_OK
120127
121128
122- @pytest .mark .parametrize ("build_method" , ["custom " , "http_map" ])
129+ @pytest .mark .parametrize ("build_method" , ["function " , "http_map" ])
123130async def test_handling_exceptions_with_middelware (
124131 aiohttp_client : Callable ,
125132 exception_handlers_map : ExceptionHandlersMap ,
126133 build_method : str ,
127134):
128- # adding new routes
129135 routes = web .RouteTableDef ()
130136
131- @routes .get ("/{what}" ) # NO decorantor now
137+ @routes .post ("/{what}" ) # NO decorantor now
132138 async def _handler (request : web .Request ):
133139 match request .match_info ["what" ]:
134140 case "ValueError" :
@@ -143,16 +149,51 @@ async def _handler(request: web.Request):
143149 app .middlewares .append (exc_handling )
144150
145151 # 2. testing from the client side
146- client = await aiohttp_client (app )
152+ client : TestClient = await aiohttp_client (app )
147153
148154 # success
149- resp = await client .get ("/ok" )
155+ resp = await client .post ("/ok" )
150156 assert resp .status == status .HTTP_200_OK
151157
152158 # handled non-HTTPException exception
153- resp = await client .get ("/ValueError" )
159+ resp = await client .post ("/ValueError" )
154160 assert resp .status == status .HTTP_422_UNPROCESSABLE_ENTITY
155161 if build_method == "http_map" :
156162 body = await resp .json ()
157163 error = ErrorGet .model_validate (body ["error" ])
158164 assert error .message == f"{ build_method = } "
165+
166+
167+ @pytest .mark .parametrize ("with_middleware" , [True , False ])
168+ async def test_raising_aiohttp_http_errors (
169+ aiohttp_client : Callable , with_middleware : bool
170+ ):
171+ routes = web .RouteTableDef ()
172+
173+ @routes .post ("/raise-http-error" )
174+ async def _handler1 (request : web .Request ):
175+ # 1. raises aiohttp.web_exceptions.HttpError
176+ raise web .HTTPConflict
177+
178+ app = web .Application ()
179+ app .add_routes (routes )
180+
181+ # 2. create & install middleware handlers for ALL http (optional)
182+ if with_middleware :
183+ exc_handling = exception_handling_middleware (
184+ exception_handlers_map = create_http_error_exception_handlers_map ()
185+ )
186+ app .middlewares .append (exc_handling )
187+
188+ # 3. testing from the client side
189+ client : TestClient = await aiohttp_client (app )
190+
191+ resp = await client .post ("/raise-http-error" )
192+ assert resp .status == status .HTTP_409_CONFLICT
193+
194+ if with_middleware :
195+ assert resp .content_type == MIMETYPE_APPLICATION_JSON
196+ ErrorGet .model_construct ((await resp .json ())["error" ])
197+ else :
198+ # default
199+ assert resp .content_type == MIMETYPE_TEXT_PLAIN
0 commit comments