From 0fa416cfb55ff494d21b5aec30fc1d37e010d9ee Mon Sep 17 00:00:00 2001 From: dabankrot Date: Mon, 26 Oct 2026 10:56:38 +0300 Subject: [PATCH] fix(types): make MaxApiError.message optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Сервер иногда возвращает ошибочный кадр без поля message (например service.unavailable при временном сбое). До этого такой кадр ронял MaxApiError.model_validate в App._build_api_error, реальный код ошибки подменялся unknown_error и в лог писался лишний exception. --- src/pymax/types/domain/error.py | 6 ++--- tests/domain/test_error_models.py | 42 +++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 tests/domain/test_error_models.py diff --git a/src/pymax/types/domain/error.py b/src/pymax/types/domain/error.py index 9b5e3e0..07b85f3 100644 --- a/src/pymax/types/domain/error.py +++ b/src/pymax/types/domain/error.py @@ -6,8 +6,8 @@ class MaxApiError(CamelModel): :ivar error: Код ошибки. :vartype error: str - :ivar message: Сообщение ошибки. - :vartype message: str + :ivar message: Сообщение ошибки, если сервер его прислал. + :vartype message: str | None :ivar title: Заголовок ошибки. :vartype title: str | None :ivar localized_message: Локализованное сообщение ошибки. @@ -15,6 +15,6 @@ class MaxApiError(CamelModel): """ error: str - message: str + message: str | None = None title: str | None = None localized_message: str | None = None diff --git a/tests/domain/test_error_models.py b/tests/domain/test_error_models.py new file mode 100644 index 0000000..5e64ad9 --- /dev/null +++ b/tests/domain/test_error_models.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from pymax.types.domain import MaxApiError + + +def test_max_api_error_without_message_keeps_error_code() -> None: + """Сервер иногда возвращает ошибочный кадр без поля ``message`` + (например ``service.unavailable`` при временном сбое). Такой кадр + обязан разобраться — иначе реальный код ошибки подменяется + ``unknown_error`` в ``App._build_api_error``.""" + error = MaxApiError.model_validate({"error": "service.unavailable"}) + + assert error.error == "service.unavailable" + assert error.message is None + assert error.title is None + assert error.localized_message is None + + +def test_max_api_error_partial_message_fields() -> None: + error = MaxApiError.model_validate( + {"error": "user.not.found", "title": "Не найдено"} + ) + + assert error.error == "user.not.found" + assert error.title == "Не найдено" + assert error.message is None + + +def test_max_api_error_full_payload_unchanged() -> None: + error = MaxApiError.model_validate( + { + "error": "user.not.found", + "message": "user missing", + "title": "Not found", + "localizedMessage": "Пользователь не найден", + } + ) + + assert error.error == "user.not.found" + assert error.message == "user missing" + assert error.title == "Not found" + assert error.localized_message == "Пользователь не найден"