diff --git a/questionpy_server/web/_middlewares.py b/questionpy_server/web/_middlewares.py index 0af036a..709a0ce 100644 --- a/questionpy_server/web/_middlewares.py +++ b/questionpy_server/web/_middlewares.py @@ -14,7 +14,7 @@ InvalidPackageError, OutOfMemoryError, PackageError, - QPyWebBaseError, + QpyWebError, ServerError, WorkerTimeoutError, ) @@ -27,7 +27,7 @@ ) from questionpy_server.worker.runtime.messages import WorkerMemoryLimitExceededError, WorkerUnknownError -exception_map: dict[type[QPyBaseError], type[QPyWebBaseError]] = { +exception_map: dict[type[QPyBaseError], type[QpyWebError]] = { InvalidQuestionStateError: InvalidPackageError, StaticFileSizeMismatchError: InvalidPackageError, WorkerNotRunningError: InvalidPackageError, @@ -49,7 +49,6 @@ async def error_middleware(request: Request, handler: Handler) -> StreamResponse Returns: The response. - """ try: return await handler(request) diff --git a/questionpy_server/web/errors.py b/questionpy_server/web/errors.py index 9874817..a1afbcc 100644 --- a/questionpy_server/web/errors.py +++ b/questionpy_server/web/errors.py @@ -1,8 +1,6 @@ # This file is part of the QuestionPy Server. (https://questionpy.org) # The QuestionPy Server is free software released under terms of the MIT license. See LICENSE.md. # (c) Technische Universität Berlin, innoCampus -from abc import abstractmethod - from aiohttp import web from aiohttp.log import web_logger @@ -27,9 +25,8 @@ def __init__(self, msg: str, body: RequestError | None = None) -> None: web_logger.info(msg) - class WorkerTimeoutError(web.HTTPBadRequest, _ExceptionMixin): - def __init__(self, *, reason: str, temporary: bool) -> None: + def __init__(self, *, reason: str | None, temporary: bool) -> None: super().__init__( msg="Question package did not answer in a reasonable amount of time", body=RequestError( @@ -41,7 +38,7 @@ def __init__(self, *, reason: str, temporary: bool) -> None: class OutOfMemoryError(web.HTTPBadRequest, _ExceptionMixin): - def __init__(self, *, reason: str, temporary: bool) -> None: + def __init__(self, *, reason: str | None, temporary: bool) -> None: super().__init__( "Question package reached its memory limit", RequestError( @@ -53,7 +50,7 @@ def __init__(self, *, reason: str, temporary: bool) -> None: class InvalidPackageError(web.HTTPBadRequest, _ExceptionMixin): - def __init__(self, *, reason: str, temporary: bool) -> None: + def __init__(self, *, reason: str | None, temporary: bool) -> None: super().__init__( "Invalid package was provided", RequestError( @@ -65,7 +62,7 @@ def __init__(self, *, reason: str, temporary: bool) -> None: class InvalidRequestError(web.HTTPBadRequest, _ExceptionMixin): - def __init__(self, *, reason: str, temporary: bool) -> None: + def __init__(self, *, reason: str | None, temporary: bool) -> None: super().__init__( "Invalid request body was provided", RequestError( @@ -77,7 +74,7 @@ def __init__(self, *, reason: str, temporary: bool) -> None: class PackageError(web.HTTPBadRequest, _ExceptionMixin): - def __init__(self, *, reason: str, temporary: bool) -> None: + def __init__(self, *, reason: str | None, temporary: bool) -> None: super().__init__( "An error occurred within the package", RequestError( @@ -89,7 +86,7 @@ def __init__(self, *, reason: str, temporary: bool) -> None: class ServerError(web.HTTPInternalServerError, _ExceptionMixin): - def __init__(self, *, reason: str, temporary: bool) -> None: + def __init__(self, *, reason: str | None, temporary: bool) -> None: super().__init__( "There was an internal server error", RequestError( @@ -98,3 +95,8 @@ def __init__(self, *, reason: str, temporary: bool) -> None: reason=reason, ), ) + + +QpyWebError = ( + WorkerTimeoutError | OutOfMemoryError | InvalidPackageError | InvalidRequestError | PackageError | ServerError +) diff --git a/tests/questionpy_common/test_elements.py b/tests/questionpy_common/test_elements.py index dd684a8..9163fbe 100644 --- a/tests/questionpy_common/test_elements.py +++ b/tests/questionpy_common/test_elements.py @@ -58,8 +58,8 @@ async def test_should_validate_main_body_when_question_state_is_not_given(client # Even though the question state is optional, the body is still required to be valid JSON. res = await client.request(_METHOD, _URL, data=b"{not_valid!}", headers={"Content-Type": "application/json"}) assert res.status == 400 - assert res.reason == "InvalidRequestError" - assert (await res.json())["reason"] == "Invalid JSON Body" + res_data = await res.json() + assert res_data == {"error_code": RequestErrorCode.INVALID_REQUEST.value, "temporary": False, "reason": None} async def test_no_package(client: TestClient) -> None: @@ -72,7 +72,7 @@ async def test_no_package(client: TestClient) -> None: assert res.status == 400 res_data = await res.json() - assert res_data == {'error_code': RequestErrorCode.INVALID_REQUEST.value, 'temporary': False, 'reason': None} + assert res_data == {"error_code": RequestErrorCode.INVALID_REQUEST.value, "temporary": False, "reason": None} async def test_data_gets_cached(client: TestClient) -> None: diff --git a/tests/questionpy_server/web/routes/test_packages.py b/tests/questionpy_server/web/routes/test_packages.py index 1e2cdf4..cad3714 100644 --- a/tests/questionpy_server/web/routes/test_packages.py +++ b/tests/questionpy_server/web/routes/test_packages.py @@ -12,7 +12,7 @@ from pydantic import TypeAdapter from questionpy_server.collector.local_collector import LocalCollector -from questionpy_server.models import PackageVersionInfo, PackageVersionsInfo +from questionpy_server.models import PackageVersionInfo, PackageVersionsInfo, RequestErrorCode from questionpy_server.utils.manifest import ComparableManifest from questionpy_server.web.app import QPyServer from tests.conftest import PACKAGE @@ -97,5 +97,5 @@ async def test_extract_info_faulty(client: TestClient) -> None: res = await client.request("POST", "/package-extract-info", data=payload) assert res.status == 400 - assert res.reason == "InvalidRequestError" - assert (await res.json())["reason"] == "PackageMissingWithoutHashError" + res_data = await res.json() + assert res_data == {"error_code": RequestErrorCode.INVALID_REQUEST.value, "temporary": False, "reason": None} diff --git a/tests/questionpy_server/web/test_error_middleware.py b/tests/questionpy_server/web/test_error_middleware.py index 20e75a1..25f7977 100644 --- a/tests/questionpy_server/web/test_error_middleware.py +++ b/tests/questionpy_server/web/test_error_middleware.py @@ -18,7 +18,7 @@ InvalidRequestError, OutOfMemoryError, PackageError, - QPyWebBaseError, + QpyWebError, ServerError, WorkerTimeoutError, ) @@ -70,7 +70,7 @@ async def test_http_exception_should_be_returned_as_is(aiohttp_client: AiohttpCl ], ) async def test_request_error_should_be_returned_as_is( - aiohttp_client: AiohttpClient, error_type: type[QPyWebBaseError] + aiohttp_client: AiohttpClient, error_type: type[QpyWebError] ) -> None: error = error_type(reason="reason", temporary=False) server = error_server(error) @@ -112,7 +112,9 @@ async def test_worker_error_should_be_transformed_to_web_error( WorkerRealTimeLimitExceededError, ], ) -async def test_known_qpy_exception_should_be_transformed_to_web_error(aiohttp_client: AiohttpClient, error_type: type[WorkerCPUTimeLimitExceededError | WorkerCPUTimeLimitExceededError]) -> None: +async def test_known_qpy_exception_should_be_transformed_to_web_error( + aiohttp_client: AiohttpClient, error_type: type[WorkerCPUTimeLimitExceededError | WorkerRealTimeLimitExceededError] +) -> None: error = error_type(3) server = error_server(error) client = await aiohttp_client(server)