From 2d58667e495222dc27b9f833ef8307ff595ccc21 Mon Sep 17 00:00:00 2001 From: "Nick V. Anokhin" Date: Mon, 20 Jul 2026 02:26:49 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20CoreException?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- net/xeaf/rack/core/core_exception.py | 1 - net/xeaf/rack/tests/core/__init__.py | 3 + .../core/core_enum_check_is_empty_tests.py | 2 +- .../rack/tests/core/core_enum_values_tests.py | 2 +- .../core_exception_is_pure_status_tests.py | 50 ++++ .../core/core_exception_process_meta_tests.py | 130 +++++++++++ .../rack/tests/core/core_exception_tests.py | 213 ++++++++++++++++++ .../rack/tests/core/core_response_tests.py | 2 +- 8 files changed, 399 insertions(+), 4 deletions(-) create mode 100644 net/xeaf/rack/tests/core/core_exception_is_pure_status_tests.py create mode 100644 net/xeaf/rack/tests/core/core_exception_process_meta_tests.py create mode 100644 net/xeaf/rack/tests/core/core_exception_tests.py diff --git a/net/xeaf/rack/core/core_exception.py b/net/xeaf/rack/core/core_exception.py index f45c3e3..83c4b6a 100644 --- a/net/xeaf/rack/core/core_exception.py +++ b/net/xeaf/rack/core/core_exception.py @@ -40,7 +40,6 @@ class CoreException(Exception): self._process_meta(meta, **kwargs) super().__init__() - # noinspection PyMethodMayBeStatic def is_pure_status(self) -> bool: """ Возвращает признак необходимости возвращать в ответе реальный статус HTTP diff --git a/net/xeaf/rack/tests/core/__init__.py b/net/xeaf/rack/tests/core/__init__.py index 8645f2d..0e77f7f 100644 --- a/net/xeaf/rack/tests/core/__init__.py +++ b/net/xeaf/rack/tests/core/__init__.py @@ -13,4 +13,7 @@ from .core_enum_choices_tests import CoreEnumChoicesTests from .core_enum_from_string_tests import CoreEnumFromStringTests from .core_enum_keys_tests import CoreEnumKeysTests from .core_enum_values_tests import CoreEnumValuesTests +from .core_exception_is_pure_status_tests import CoreExceptionIsPureStatusTests +from .core_exception_process_meta_tests import CoreExceptionProcessMetaTests +from .core_exception_tests import CoreExceptionTests from .core_response_tests import CoreResponseTests diff --git a/net/xeaf/rack/tests/core/core_enum_check_is_empty_tests.py b/net/xeaf/rack/tests/core/core_enum_check_is_empty_tests.py index ddc5759..350af7a 100644 --- a/net/xeaf/rack/tests/core/core_enum_check_is_empty_tests.py +++ b/net/xeaf/rack/tests/core/core_enum_check_is_empty_tests.py @@ -15,7 +15,7 @@ from ._core_enum_mocks import TestEnum class CoreEnumCheckIsEmptyTests(CoreSimpleTestCase): """ - Тестовый класс для метода CoreEnum._check_is_empty() + Тестовый класс для CoreEnum._check_is_empty() """ def test_check_is_empty_with_non_empty_enum_does_not_raise_error(self): diff --git a/net/xeaf/rack/tests/core/core_enum_values_tests.py b/net/xeaf/rack/tests/core/core_enum_values_tests.py index f0995ba..609965c 100644 --- a/net/xeaf/rack/tests/core/core_enum_values_tests.py +++ b/net/xeaf/rack/tests/core/core_enum_values_tests.py @@ -18,7 +18,7 @@ from ._core_enum_mocks import TestSingleEnum class CoreEnumValuesTests(CoreSimpleTestCase): """ - Тестовый класс CoreEnum.values() + Тестовый класс для CoreEnum.values() """ def test_values_returns_list_of_enum_member_values(self): diff --git a/net/xeaf/rack/tests/core/core_exception_is_pure_status_tests.py b/net/xeaf/rack/tests/core/core_exception_is_pure_status_tests.py new file mode 100644 index 0000000..c2ef46e --- /dev/null +++ b/net/xeaf/rack/tests/core/core_exception_is_pure_status_tests.py @@ -0,0 +1,50 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса CoreExceptionIsPureStatusTests +""" + +from rest_framework import status + +from net.xeaf.rack.core import CoreException +from net.xeaf.rack.core.testing import CoreSimpleTestCase + + +class CoreExceptionIsPureStatusTests(CoreSimpleTestCase): + """ + Тестовый класс для CoreException.is_pure_status() + """ + + def test_is_pure_status_returns_false_by_default(self): + """ + Тест проверяет, что по умолчанию is_pure_status возвращает False + """ + + # Arrange + exception = CoreException(status.HTTP_404_NOT_FOUND) + + # Act + result = exception.is_pure_status() + + # Assert + self.assertFalse(result) + self.assertIsInstance(result, bool) + + def test_is_pure_status_always_returns_false_for_base_exception(self): + """ + Тест проверяет, что для базового класса исключения метод всегда возвращает False + """ + + # Arrange + exception1 = CoreException(status.HTTP_400_BAD_REQUEST) + exception2 = CoreException(status.HTTP_500_INTERNAL_SERVER_ERROR) + exception3 = CoreException(status.HTTP_200_OK) + + # Act & Assert + self.assertFalse(exception1.is_pure_status()) + self.assertFalse(exception2.is_pure_status()) + self.assertFalse(exception3.is_pure_status()) diff --git a/net/xeaf/rack/tests/core/core_exception_process_meta_tests.py b/net/xeaf/rack/tests/core/core_exception_process_meta_tests.py new file mode 100644 index 0000000..272f72e --- /dev/null +++ b/net/xeaf/rack/tests/core/core_exception_process_meta_tests.py @@ -0,0 +1,130 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса CoreExceptionProcessMetaTests +""" + +from rest_framework import status + +from net.xeaf.rack.core import CoreException +from net.xeaf.rack.core.testing import CoreSimpleTestCase + + +class CoreExceptionProcessMetaTests(CoreSimpleTestCase): + """ + Тестовый класс для CoreException._process_meta() + """ + + def test_process_meta_with_meta_parameter(self): + """ + Тест проверяет обработку мета-данных при передаче параметра meta + """ + + # Arrange + exception = CoreException(status.HTTP_400_BAD_REQUEST) + meta = {"key": "value", "count": 10} + + # Act + exception._process_meta(meta) + + # Assert + self.assertEqual(exception.meta, meta) + + def test_process_meta_with_kwargs_only(self): + """ + Тест проверяет обработку дополнительных параметров при отсутствии meta + """ + + # Arrange + exception = CoreException(status.HTTP_400_BAD_REQUEST) + kwargs = {"param1": "value1", "param2": "value2"} + + # Act + exception._process_meta(None, **kwargs) + + # Assert + self.assertEqual(exception.meta, kwargs) + + def test_process_meta_with_meta_and_kwargs(self): + """ + Тест проверяет обработку мета-данных и дополнительных параметров вместе + """ + + # Arrange + exception = CoreException(status.HTTP_400_BAD_REQUEST) + meta = {"existing": "data"} + kwargs = {"new": "value", "additional": 123} + + # Act + exception._process_meta(meta, **kwargs) + + # Assert + self.assertEqual(exception.meta, {"existing": "data", "new": "value", "additional": 123}) + + def test_process_meta_with_empty_meta_and_kwargs(self): + """ + Тест проверяет обработку пустых мета-данных и дополнительных параметров + """ + + # Arrange + exception = CoreException(status.HTTP_400_BAD_REQUEST) + meta = {} + kwargs = {"param": "value"} + + # Act + exception._process_meta(meta, **kwargs) + + # Assert + self.assertEqual(exception.meta, {"param": "value"}) + + def test_process_meta_with_meta_none_and_no_kwargs(self): + """ + Тест проверяет обработку, когда meta равен None и kwargs отсутствуют + """ + + # Arrange + exception = CoreException(status.HTTP_400_BAD_REQUEST) + + # Act + exception._process_meta(None) + + # Assert + self.assertIsNone(exception.meta) + + def test_process_meta_with_meta_and_kwargs_overwrites_duplicate_keys(self): + """ + Тест проверяет, что kwargs перезаписывают ключи из meta при совпадении + """ + + # Arrange + exception = CoreException(status.HTTP_400_BAD_REQUEST) + meta = {"key": "from_meta", "keep": "this"} + kwargs = {"key": "from_kwargs", "added": "new"} + + # Act + exception._process_meta(meta, **kwargs) + + # Assert + self.assertEqual(exception.meta, {"key": "from_kwargs", "keep": "this", "added": "new"}) + + def test_process_meta_preserves_meta_structure(self): + """ + Тест проверяет сохранение структуры мета-данных при обновлении + """ + + # Arrange + exception = CoreException(status.HTTP_400_BAD_REQUEST) + meta = {"nested": {"field": "value"}, "list": [1, 2, 3]} + kwargs = {"extra": "data"} + + # Act + exception._process_meta(meta, **kwargs) + + # Assert + self.assertEqual(exception.meta["nested"], {"field": "value"}) + self.assertEqual(exception.meta["list"], [1, 2, 3]) + self.assertEqual(exception.meta["extra"], "data") diff --git a/net/xeaf/rack/tests/core/core_exception_tests.py b/net/xeaf/rack/tests/core/core_exception_tests.py new file mode 100644 index 0000000..cecd7e7 --- /dev/null +++ b/net/xeaf/rack/tests/core/core_exception_tests.py @@ -0,0 +1,213 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса CoreExceptionTests +""" + +from rest_framework import status + +from net.xeaf.rack.core import CoreException +from net.xeaf.rack.core.testing import CoreSimpleTestCase + + +class CoreExceptionTests(CoreSimpleTestCase): + """ + Тестовый класс для CoreException.__init__() + """ + + def test_init_with_required_parameters_only(self): + """ + Тест проверяет инициализацию исключения только с обязательным параметром status_code + """ + + # Arrange + status_code = status.HTTP_404_NOT_FOUND + + # Act + exception = CoreException(status_code) + + # Assert + self.assertEqual(exception.status_code, status_code) + self.assertIsNone(exception.detail) + self.assertIsNone(exception.meta) + self.assertIsInstance(exception, Exception) + + def test_init_with_status_code_and_detail_string(self): + """ + Тест проверяет инициализацию исключения с кодом статуса и строковым описанием + """ + + # Arrange + status_code = status.HTTP_400_BAD_REQUEST + detail = "Invalid request parameters" + + # Act + exception = CoreException(status_code, detail) + + # Assert + self.assertEqual(exception.status_code, status_code) + self.assertEqual(exception.detail, detail) + self.assertIsNone(exception.meta) + + def test_init_with_status_code_and_detail_dict(self): + """ + Тест проверяет инициализацию исключения с кодом статуса и деталями в виде словаря + """ + + # Arrange + status_code = status.HTTP_400_BAD_REQUEST + detail = {"field": "email", "error": "Invalid email format"} + + # Act + exception = CoreException(status_code, detail) + + # Assert + self.assertEqual(exception.status_code, status_code) + self.assertEqual(exception.detail, detail) + self.assertIsNone(exception.meta) + + def test_init_with_status_code_and_detail_list(self): + """ + Тест проверяет инициализацию исключения с кодом статуса и деталями в виде списка + """ + + # Arrange + status_code = status.HTTP_400_BAD_REQUEST + detail = ["Field 'name' is required", "Field 'email' is required"] + + # Act + exception = CoreException(status_code, detail) + + # Assert + self.assertEqual(exception.status_code, status_code) + self.assertEqual(exception.detail, detail) + self.assertIsNone(exception.meta) + + def test_init_with_meta_parameter(self): + """ + Тест проверяет инициализацию исключения с мета-данными + """ + + # Arrange + status_code = status.HTTP_403_FORBIDDEN + detail = "Access denied" + meta = {"resource": "user_profile", "required_permission": "edit"} + + # Act + exception = CoreException(status_code, detail, meta) + + # Assert + self.assertEqual(exception.status_code, status_code) + self.assertEqual(exception.detail, detail) + self.assertEqual(exception.meta, meta) + + def test_init_with_empty_meta(self): + """ + Тест проверяет инициализацию исключения с пустыми мета-данными + """ + + # Arrange + status_code = status.HTTP_500_INTERNAL_SERVER_ERROR + detail = "Internal server error" + meta = {} + + # Act + exception = CoreException(status_code, detail, meta) + + # Assert + self.assertEqual(exception.status_code, status_code) + self.assertEqual(exception.detail, detail) + self.assertEqual(exception.meta, {}) + + def test_init_with_kwargs_only(self): + """ + Тест проверяет инициализацию исключения с дополнительными параметрами через kwargs + """ + + # Arrange + status_code = status.HTTP_409_CONFLICT + detail = "Conflict" + kwargs = {"conflict_id": 123, "conflict_type": "duplicate"} + + # Act + exception = CoreException(status_code, detail, **kwargs) + + # Assert + self.assertEqual(exception.status_code, status_code) + self.assertEqual(exception.detail, detail) + self.assertEqual(exception.meta, kwargs) + + def test_init_with_meta_and_kwargs(self): + """ + Тест проверяет инициализацию исключения с мета-данными и дополнительными параметрами + """ + + # Arrange + status_code = status.HTTP_429_TOO_MANY_REQUESTS + detail = "Too many requests" + meta = {"retry_after": 60} + kwargs = {"limit": 100, "window": 60} + + # Act + exception = CoreException(status_code, detail, meta, **kwargs) + + # Assert + self.assertEqual(exception.status_code, status_code) + self.assertEqual(exception.detail, detail) + self.assertEqual(exception.meta, {"retry_after": 60, "limit": 100, "window": 60}) + + def test_init_with_meta_none_and_kwargs(self): + """ + Тест проверяет инициализацию исключения с None в качестве мета и дополнительными параметрами + """ + + # Arrange + status_code = status.HTTP_401_UNAUTHORIZED + detail = "Unauthorized" + kwargs = {"auth_type": "Bearer", "expired": True} + + # Act + exception = CoreException(status_code, detail, None, **kwargs) + + # Assert + self.assertEqual(exception.status_code, status_code) + self.assertEqual(exception.detail, detail) + self.assertEqual(exception.meta, kwargs) + + def test_init_with_meta_empty_and_kwargs(self): + """ + Тест проверяет инициализацию исключения с пустыми мета-данными и дополнительными параметрами + """ + + # Arrange + status_code = status.HTTP_402_PAYMENT_REQUIRED + detail = "Payment required" + meta = {} + kwargs = {"payment_id": 456, "amount": 99.99} + + # Act + exception = CoreException(status_code, detail, meta, **kwargs) + + # Assert + self.assertEqual(exception.status_code, status_code) + self.assertEqual(exception.detail, detail) + self.assertEqual(exception.meta, {"payment_id": 456, "amount": 99.99}) + + def test_init_creates_exception_subclass(self): + """ + Тест проверяет, что CoreException является подклассом Exception + """ + + # Arrange + status_code = status.HTTP_500_INTERNAL_SERVER_ERROR + + # Act + exception = CoreException(status_code) + + # Assert + self.assertIsInstance(exception, Exception) + self.assertTrue(issubclass(CoreException, Exception)) diff --git a/net/xeaf/rack/tests/core/core_response_tests.py b/net/xeaf/rack/tests/core/core_response_tests.py index 26150a0..c0bdd3c 100644 --- a/net/xeaf/rack/tests/core/core_response_tests.py +++ b/net/xeaf/rack/tests/core/core_response_tests.py @@ -17,7 +17,7 @@ from net.xeaf.rack.core.testing import CoreSimpleTestCase class CoreResponseTests(CoreSimpleTestCase): """ - Тестовый класс для метода CoreResponse.__init__() + Тестовый класс для CoreResponse.__init__() """ def test_init_with_success_status_and_data(self):