Подготовка новых моделей тестов
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Пакет описания тестов для базовых классов
|
||||
"""
|
||||
|
||||
from .core_enum_tests import CoreEnumTests
|
||||
from .core_exception_tests import CoreExceptionTests
|
||||
from .core_response_tests import CoreResponseTests
|
||||
|
||||
from .views import *
|
||||
@@ -1,234 +0,0 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Описание класса CoreEnumTests
|
||||
"""
|
||||
|
||||
from django.test import SimpleTestCase
|
||||
|
||||
from net.xeaf.rack.core import CoreEnum
|
||||
|
||||
|
||||
class TestColorEnum(CoreEnum):
|
||||
"""
|
||||
Тестовое перечисление для проверки
|
||||
"""
|
||||
|
||||
RED = "red"
|
||||
GREEN = "green"
|
||||
BLUE = "blue"
|
||||
|
||||
|
||||
class TestStatusEnum(CoreEnum):
|
||||
"""
|
||||
Еще одно тестовое перечисление
|
||||
"""
|
||||
|
||||
ACTIVE = "active"
|
||||
INACTIVE = "inactive"
|
||||
PENDING = "pending"
|
||||
|
||||
|
||||
class TestEmptyEnum(CoreEnum):
|
||||
"""
|
||||
Пустое перечисление для граничных случаев
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class CoreEnumTests(SimpleTestCase):
|
||||
"""
|
||||
Тесты для класса CoreEnum
|
||||
"""
|
||||
|
||||
def test_choices_returns_list_of_tuples(self):
|
||||
"""
|
||||
Проверяет, что choices() возвращает список кортежей (value, name)
|
||||
"""
|
||||
|
||||
expected = [
|
||||
("red", "RED"),
|
||||
("green", "GREEN"),
|
||||
("blue", "BLUE"),
|
||||
]
|
||||
self.assertEqual(TestColorEnum.choices(), expected)
|
||||
|
||||
def test_choices_contains_all_members(self):
|
||||
"""
|
||||
Проверяет, что choices() содержит все элементы перечисления
|
||||
"""
|
||||
|
||||
choices = TestColorEnum.choices()
|
||||
self.assertEqual(len(choices), 3)
|
||||
self.assertIn(("red", "RED"), choices)
|
||||
self.assertIn(("green", "GREEN"), choices)
|
||||
self.assertIn(("blue", "BLUE"), choices)
|
||||
|
||||
def test_choices_order_matches_definition(self):
|
||||
"""
|
||||
Проверяет, что порядок элементов сохраняется
|
||||
"""
|
||||
|
||||
choices = TestColorEnum.choices()
|
||||
self.assertEqual(choices[0][0], "red")
|
||||
self.assertEqual(choices[1][0], "green")
|
||||
self.assertEqual(choices[2][0], "blue")
|
||||
|
||||
def test_keys_returns_list_of_names(self):
|
||||
"""
|
||||
Проверяет, что keys() возвращает список имен
|
||||
"""
|
||||
|
||||
expected = ["RED", "GREEN", "BLUE"]
|
||||
self.assertEqual(TestColorEnum.keys(), expected)
|
||||
|
||||
def test_keys_contains_all_names(self):
|
||||
"""
|
||||
Проверяет, что keys() содержит все имена элементов
|
||||
"""
|
||||
|
||||
keys = TestColorEnum.keys()
|
||||
self.assertEqual(len(keys), 3)
|
||||
self.assertIn("RED", keys)
|
||||
self.assertIn("GREEN", keys)
|
||||
self.assertIn("BLUE", keys)
|
||||
|
||||
def test_values_returns_list_of_values(self):
|
||||
"""
|
||||
Проверяет, что values() возвращает список значений
|
||||
"""
|
||||
|
||||
expected = ["red", "green", "blue"]
|
||||
self.assertEqual(TestColorEnum.values(), expected)
|
||||
|
||||
def test_values_contains_all_values(self):
|
||||
"""
|
||||
Проверяет, что values() содержит все значения элементов
|
||||
"""
|
||||
|
||||
values = TestColorEnum.values()
|
||||
self.assertEqual(len(values), 3)
|
||||
self.assertIn("red", values)
|
||||
self.assertIn("green", values)
|
||||
self.assertIn("blue", values)
|
||||
|
||||
def test_choices_with_different_enum(self):
|
||||
"""
|
||||
Проверяет работу choices() с другим перечислением
|
||||
"""
|
||||
|
||||
expected = [
|
||||
("active", "ACTIVE"),
|
||||
("inactive", "INACTIVE"),
|
||||
("pending", "PENDING"),
|
||||
]
|
||||
self.assertEqual(TestStatusEnum.choices(), expected)
|
||||
|
||||
def test_keys_with_different_enum(self):
|
||||
"""
|
||||
Проверяет работу keys() с другим перечислением
|
||||
"""
|
||||
|
||||
expected = ["ACTIVE", "INACTIVE", "PENDING"]
|
||||
self.assertEqual(TestStatusEnum.keys(), expected)
|
||||
|
||||
def test_values_with_different_enum(self):
|
||||
"""
|
||||
Проверяет работу values() с другим перечислением
|
||||
"""
|
||||
|
||||
expected = ["active", "inactive", "pending"]
|
||||
self.assertEqual(TestStatusEnum.values(), expected)
|
||||
|
||||
def test_empty_enum_choices(self):
|
||||
"""
|
||||
Проверяет поведение choices() для пустого перечисления
|
||||
"""
|
||||
|
||||
self.assertEqual(TestEmptyEnum.choices(), [])
|
||||
|
||||
def test_empty_enum_keys(self):
|
||||
"""
|
||||
Проверяет поведение keys() для пустого перечисления
|
||||
"""
|
||||
|
||||
self.assertEqual(TestEmptyEnum.keys(), [])
|
||||
|
||||
def test_empty_enum_values(self):
|
||||
"""
|
||||
Проверяет поведение values() для пустого перечисления
|
||||
"""
|
||||
|
||||
self.assertEqual(TestEmptyEnum.values(), [])
|
||||
|
||||
def test_choices_returns_different_type(self):
|
||||
"""
|
||||
Проверяет, что choices() возвращает list, а не tuple
|
||||
"""
|
||||
|
||||
self.assertIsInstance(TestColorEnum.choices(), list)
|
||||
self.assertIsInstance(TestColorEnum.choices()[0], tuple)
|
||||
|
||||
def test_keys_returns_list(self):
|
||||
"""
|
||||
Проверяет, что keys() возвращает list
|
||||
"""
|
||||
|
||||
self.assertIsInstance(TestColorEnum.keys(), list)
|
||||
|
||||
def test_values_returns_list(self):
|
||||
"""
|
||||
Проверяет, что values() возвращает list
|
||||
"""
|
||||
|
||||
self.assertIsInstance(TestColorEnum.values(), list)
|
||||
|
||||
def test_choices_values_match(self):
|
||||
"""
|
||||
Проверяет согласованность choices() и values()
|
||||
"""
|
||||
|
||||
choices_values = [choice[0] for choice in TestColorEnum.choices()]
|
||||
self.assertEqual(choices_values, TestColorEnum.values())
|
||||
|
||||
def test_choices_keys_match(self):
|
||||
"""
|
||||
Проверяет согласованность choices() и keys()
|
||||
"""
|
||||
|
||||
choices_keys = [choice[1] for choice in TestColorEnum.choices()]
|
||||
self.assertEqual(choices_keys, TestColorEnum.keys())
|
||||
|
||||
def test_keys_and_values_length_match(self):
|
||||
"""
|
||||
Проверяет, что keys() и values() имеют одинаковую длину
|
||||
"""
|
||||
|
||||
self.assertEqual(len(TestColorEnum.keys()), len(TestColorEnum.values()))
|
||||
|
||||
def test_choices_length_matches_members(self):
|
||||
"""
|
||||
Проверяет, что количество choices совпадает с количеством элементов
|
||||
"""
|
||||
|
||||
self.assertEqual(len(TestColorEnum.choices()), len(TestColorEnum.__members__))
|
||||
|
||||
def test_all_elements_are_strings(self):
|
||||
"""
|
||||
Проверяет, что все значения являются строками (StrEnum)
|
||||
"""
|
||||
|
||||
for value in TestColorEnum.values():
|
||||
self.assertIsInstance(value, str)
|
||||
|
||||
for key in TestColorEnum.keys():
|
||||
self.assertIsInstance(key, str)
|
||||
|
||||
for choice in TestColorEnum.choices():
|
||||
self.assertIsInstance(choice[0], str)
|
||||
self.assertIsInstance(choice[1], str)
|
||||
@@ -1,141 +0,0 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Описание класса CoreExceptionTests
|
||||
"""
|
||||
|
||||
from django.test import SimpleTestCase
|
||||
from rest_framework import status
|
||||
|
||||
from net.xeaf.rack.core import CoreException
|
||||
|
||||
|
||||
class CoreExceptionTests(SimpleTestCase):
|
||||
"""
|
||||
Тесты для класса CoreException
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Заполняем параметры для теста
|
||||
"""
|
||||
|
||||
self.default_status = status.HTTP_400_BAD_REQUEST
|
||||
self.default_detail = "Тестовая ошибка"
|
||||
self.exc_instance = CoreException(status_code=self.default_status)
|
||||
|
||||
def test_init_with_basic_params(self):
|
||||
"""
|
||||
Проверяет базовую инициализацию без метаданных
|
||||
"""
|
||||
|
||||
exc = CoreException(status_code=self.default_status, detail=self.default_detail)
|
||||
|
||||
self.assertEqual(exc.status_code, self.default_status)
|
||||
self.assertEqual(exc.detail, self.default_detail)
|
||||
self.assertIsNone(exc.meta)
|
||||
|
||||
def test_init_with_meta_only(self):
|
||||
"""
|
||||
Проверяет инициализацию, когда передан только словарь meta
|
||||
"""
|
||||
|
||||
meta_data = {"action": "REDIRECT", "url": "/login/"}
|
||||
|
||||
exc = CoreException(status_code=self.default_status, meta=meta_data)
|
||||
|
||||
self.assertEqual(exc.status_code, self.default_status)
|
||||
self.assertEqual(exc.meta, meta_data)
|
||||
self.assertIsInstance(exc.meta, dict)
|
||||
self.assertEqual(exc.meta["action"], "REDIRECT")
|
||||
|
||||
def test_init_with_kwargs_only(self):
|
||||
"""
|
||||
Проверяет инициализацию, когда meta=None, но переданы именованные аргументы (kwargs)
|
||||
"""
|
||||
|
||||
exc = CoreException(status_code=self.default_status, foo="bar", code=123)
|
||||
|
||||
self.assertEqual(exc.status_code, self.default_status)
|
||||
self.assertIsNotNone(exc.meta)
|
||||
self.assertIsInstance(exc.meta, dict)
|
||||
self.assertEqual(exc.meta["foo"], "bar")
|
||||
self.assertEqual(exc.meta["code"], 123)
|
||||
|
||||
def test_process_meta_with_none(self):
|
||||
"""
|
||||
Если meta=None и kwargs пусты, self.meta должен остаться None
|
||||
"""
|
||||
|
||||
self.exc_instance.meta = None # Сбрасываем состояние перед проверкой
|
||||
|
||||
self.exc_instance._process_meta(meta=None)
|
||||
|
||||
self.assertIsNone(self.exc_instance.meta)
|
||||
|
||||
def test_process_meta_with_dict(self):
|
||||
"""
|
||||
Если передан только словарь meta, он должен записаться в self.meta
|
||||
"""
|
||||
|
||||
self.exc_instance.meta = None
|
||||
test_meta = {"source": "auth_service"}
|
||||
|
||||
self.exc_instance._process_meta(meta=test_meta)
|
||||
|
||||
self.assertEqual(self.exc_instance.meta, test_meta)
|
||||
|
||||
def test_process_meta_with_kwargs_only(self):
|
||||
"""
|
||||
Если meta=None, но есть kwargs, должен создаться словарь с этими kwargs
|
||||
"""
|
||||
|
||||
self.exc_instance.meta = None
|
||||
|
||||
self.exc_instance._process_meta(meta=None, request_id="req_123", user_id=99)
|
||||
|
||||
self.assertIsNotNone(self.exc_instance.meta)
|
||||
self.assertIsInstance(self.exc_instance.meta, dict)
|
||||
self.assertEqual(self.exc_instance.meta["request_id"], "req_123")
|
||||
self.assertEqual(self.exc_instance.meta["user_id"], 99)
|
||||
|
||||
def test_process_meta_combined(self):
|
||||
"""
|
||||
Если переданы и meta, и kwargs, они должны успешно объединиться (update)
|
||||
"""
|
||||
|
||||
self.exc_instance.meta = None
|
||||
initial_meta = {"version": "v1"}
|
||||
|
||||
self.exc_instance._process_meta(meta=initial_meta, debug=True, env="prod")
|
||||
|
||||
self.assertIsNotNone(self.exc_instance.meta)
|
||||
self.assertIsInstance(self.exc_instance.meta, dict)
|
||||
self.assertEqual(self.exc_instance.meta["version"], "v1")
|
||||
self.assertEqual(self.exc_instance.meta["debug"], True)
|
||||
self.assertEqual(self.exc_instance.meta["env"], "prod")
|
||||
|
||||
def test_process_meta_with_empty_dict_and_kwargs(self):
|
||||
"""
|
||||
Критический случай: meta пришел пустым словарем, но kwargs заполнены
|
||||
"""
|
||||
|
||||
self.exc_instance.meta = None
|
||||
|
||||
self.exc_instance._process_meta(meta={}, trace_id="xyz")
|
||||
|
||||
self.assertIsNotNone(self.exc_instance.meta)
|
||||
self.assertIsInstance(self.exc_instance.meta, dict)
|
||||
self.assertEqual(self.exc_instance.meta["trace_id"], "xyz")
|
||||
|
||||
def test_is_pure_status_defaults_to_false(self):
|
||||
"""
|
||||
Проверяет, что по умолчанию исключение возвращает False для чистого статуса
|
||||
"""
|
||||
|
||||
exc = CoreException(status_code=400)
|
||||
self.assertFalse(exc.is_pure_status())
|
||||
@@ -1,98 +0,0 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Описание класса CoreResponseTests
|
||||
"""
|
||||
|
||||
from django.test import SimpleTestCase
|
||||
from rest_framework import status
|
||||
|
||||
from net.xeaf.rack.core import CoreResponse
|
||||
|
||||
|
||||
class CoreResponseTests(SimpleTestCase):
|
||||
"""
|
||||
Тесты для класса CoreResponse
|
||||
"""
|
||||
|
||||
def test_init_success_format(self):
|
||||
"""
|
||||
Проверяет формат успешного ответа (код < 400): данные улетают в 'data'
|
||||
"""
|
||||
|
||||
test_data = {"user_id": 42}
|
||||
internal_code = status.HTTP_200_OK
|
||||
external_code = status.HTTP_200_OK
|
||||
|
||||
response = CoreResponse(
|
||||
internal_status=internal_code,
|
||||
external_status=external_code,
|
||||
data=test_data
|
||||
)
|
||||
|
||||
# Проверяем внешний HTTP-статус в заголовках
|
||||
self.assertEqual(response.status_code, external_code)
|
||||
|
||||
# Проверяем структуру тела JSON-ответа (внутреннее свойство DRF .data)
|
||||
self.assertEqual(response.data["status"], internal_code)
|
||||
self.assertIn("timestamp", response.data)
|
||||
# Так как это успех, данные обязаны быть в ключе "data"
|
||||
self.assertEqual(response.data["data"], test_data)
|
||||
self.assertNotIn("detail", response.data)
|
||||
self.assertNotIn("meta", response.data)
|
||||
|
||||
def test_init_error_format(self):
|
||||
"""
|
||||
Проверяет формат ответа с ошибкой (код >= 400): данные улетают в 'detail'
|
||||
"""
|
||||
|
||||
error_msg = "Некорректные параметры запроса"
|
||||
internal_code = status.HTTP_400_BAD_REQUEST
|
||||
external_code = status.HTTP_200_OK # Маскируем под 200 OK для фронтенда
|
||||
|
||||
response = CoreResponse(
|
||||
internal_status=internal_code,
|
||||
external_status=external_code,
|
||||
data=error_msg
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, external_code)
|
||||
self.assertEqual(response.data["status"], internal_code)
|
||||
# Так как это ошибка, данные обязаны быть в ключе "detail"
|
||||
self.assertEqual(response.data["detail"], error_msg)
|
||||
self.assertNotIn("data", response.data)
|
||||
|
||||
def test_init_with_meta(self):
|
||||
"""
|
||||
Проверяет корректную интеграцию словаря meta в тело ответа
|
||||
"""
|
||||
|
||||
meta_data = {"page": 1, "has_next": False}
|
||||
|
||||
response = CoreResponse(
|
||||
internal_status=status.HTTP_200_OK,
|
||||
external_status=status.HTTP_200_OK,
|
||||
data={"items": []},
|
||||
meta=meta_data
|
||||
)
|
||||
|
||||
self.assertIn("meta", response.data)
|
||||
self.assertEqual(response.data["meta"], meta_data)
|
||||
|
||||
def test_init_with_empty_data(self):
|
||||
"""
|
||||
Проверяет, что если data=None или пустой, ключи 'data' и 'detail' не создаются
|
||||
"""
|
||||
|
||||
response = CoreResponse(
|
||||
internal_status=status.HTTP_204_NO_CONTENT,
|
||||
external_status=status.HTTP_204_NO_CONTENT,
|
||||
data=None
|
||||
)
|
||||
|
||||
self.assertNotIn("data", response.data)
|
||||
self.assertNotIn("detail", response.data)
|
||||
@@ -1,17 +0,0 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Пакет описания тестов для базовых классов представлений
|
||||
"""
|
||||
|
||||
from .core_view_set_get_serializer_tests import CoreViewSetGetSerializerTests
|
||||
from .core_view_set_is_partial_update import CoreViewSetIsPartialUpdateTests
|
||||
from .core_view_set_process_data_params import CoreViewSetProcessDataParamsTests
|
||||
from .core_view_set_process_pk_param_tests import CoreViewSetProcessPkParamTests
|
||||
from .core_view_set_process_query_params_tests import CoreViewSetProcessQueryParamsTests
|
||||
from .core_view_set_path_generation_tests import CoreViewSetPathGenerationTests
|
||||
from .core_view_set_request_context_tests import CoreViewSetRequestContextTests
|
||||
@@ -1,71 +0,0 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Описание класса CoreViewSetGetSerializerTest
|
||||
"""
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.test import SimpleTestCase
|
||||
from rest_framework import serializers
|
||||
|
||||
from net.xeaf.rack.core.views import CoreViewSet
|
||||
|
||||
|
||||
class MockSerializer(serializers.Serializer):
|
||||
"""
|
||||
Тестовый класс сериализатора
|
||||
"""
|
||||
|
||||
field = serializers.CharField()
|
||||
""" Поле сериализатора """
|
||||
|
||||
|
||||
class MockViewSet(CoreViewSet):
|
||||
"""
|
||||
Тестовый класс представления
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class CoreViewSetGetSerializerTests(SimpleTestCase):
|
||||
"""
|
||||
Тесты метода CoreViewSet._get_serializer
|
||||
"""
|
||||
|
||||
def test_get_serializer_with_serializer_class(self):
|
||||
"""
|
||||
Передан явный класс сериализатора
|
||||
"""
|
||||
|
||||
view_set = MockViewSet()
|
||||
custom_serializer = MockSerializer
|
||||
result = view_set._get_serializer(custom_serializer, None)
|
||||
|
||||
self.assertEqual(result, custom_serializer)
|
||||
|
||||
def test_get_serializer_with_default(self):
|
||||
"""
|
||||
Передан класс по умолчанию
|
||||
"""
|
||||
|
||||
view_set = MockViewSet()
|
||||
default_serializer = MockSerializer
|
||||
result = view_set._get_serializer(None, default_serializer)
|
||||
|
||||
self.assertEqual(result, default_serializer)
|
||||
|
||||
def test_get_serializer_no_serializer_raises_error(self):
|
||||
"""
|
||||
Не передан сериализатор - ошибка
|
||||
"""
|
||||
|
||||
view_set = MockViewSet()
|
||||
with self.assertRaises(ImproperlyConfigured) as context:
|
||||
view_set._get_serializer(None, None)
|
||||
|
||||
self.assertIn("must define 'serializer' attribute", str(context.exception))
|
||||
@@ -1,107 +0,0 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Описание класса CoreViewSetIsPartialUpdateTest
|
||||
"""
|
||||
|
||||
from net.xeaf.rack.core.tests import CoreTestCase
|
||||
from net.xeaf.rack.core.views import CoreViewSet
|
||||
|
||||
|
||||
class MockViewSetForIsPartialUpdate(CoreViewSet):
|
||||
"""
|
||||
ViewSet для тестов _is_partial_update
|
||||
"""
|
||||
|
||||
permission_classes = ()
|
||||
|
||||
|
||||
class CoreViewSetIsPartialUpdateTests(CoreTestCase):
|
||||
"""
|
||||
Тесты для CoreViewSet._is_partial_update
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Подготовка к тестированию
|
||||
"""
|
||||
|
||||
super().setUp()
|
||||
self.view_set = MockViewSetForIsPartialUpdate()
|
||||
|
||||
def test_is_partial_update_for_patch(self):
|
||||
"""
|
||||
PATCH запрос - возвращает True
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'PATCH',
|
||||
'/test',
|
||||
data={'name': 'Updated'}
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._is_partial_update()
|
||||
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_is_partial_update_for_put(self):
|
||||
"""
|
||||
PUT запрос - возвращает False
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'PUT',
|
||||
'/test',
|
||||
data={'name': 'Updated', 'age': 30}
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._is_partial_update()
|
||||
|
||||
self.assertFalse(result)
|
||||
|
||||
def test_is_partial_update_for_post(self):
|
||||
"""
|
||||
POST запрос - возвращает False
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'POST',
|
||||
'/test',
|
||||
data={'name': 'New', 'age': 25}
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._is_partial_update()
|
||||
|
||||
self.assertFalse(result)
|
||||
|
||||
def test_is_partial_update_for_delete(self):
|
||||
"""
|
||||
DELETE запрос - возвращает False
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'DELETE',
|
||||
'/test/123/'
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._is_partial_update()
|
||||
|
||||
self.assertFalse(result)
|
||||
|
||||
def test_is_partial_update_for_get(self):
|
||||
"""
|
||||
GET запрос - возвращает False
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'GET',
|
||||
'/test'
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._is_partial_update()
|
||||
|
||||
self.assertFalse(result)
|
||||
@@ -1,163 +0,0 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Описание класса CoreViewSetPathGenerationTests
|
||||
"""
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.test import SimpleTestCase
|
||||
from net.xeaf.rack.core.views import CoreViewSet
|
||||
from net.xeaf.rack.enums import ViewSetAction
|
||||
|
||||
|
||||
class CoreViewSetPathGenerationTests(SimpleTestCase):
|
||||
"""
|
||||
Тесты генерации URL паттернов
|
||||
"""
|
||||
|
||||
class FullCRUDViewSet(CoreViewSet):
|
||||
"""
|
||||
ViewSet с полным CRUD
|
||||
"""
|
||||
|
||||
item_list_pattern = "test/"
|
||||
item_detail_pattern = "test/<uuid:pk>/"
|
||||
|
||||
@classmethod
|
||||
def item_list(cls, request):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def item_create(cls, request):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def item_retrieve(cls, request, pk):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def item_update(cls, request, pk):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def item_partial_update(cls, request, pk):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def item_destroy(cls, request, pk):
|
||||
pass
|
||||
|
||||
class ListOnlyViewSet(CoreViewSet):
|
||||
"""
|
||||
ViewSet только для списка
|
||||
"""
|
||||
|
||||
item_list_pattern = "test/"
|
||||
|
||||
@classmethod
|
||||
def item_list(cls, request):
|
||||
pass
|
||||
|
||||
class DetailOnlyViewSet(CoreViewSet):
|
||||
"""
|
||||
ViewSet только для деталей
|
||||
"""
|
||||
|
||||
item_detail_pattern = "test/<uuid:pk>/"
|
||||
|
||||
@classmethod
|
||||
def item_retrieve(cls, request, pk):
|
||||
pass
|
||||
|
||||
def test_get_paths_full_crud(self):
|
||||
"""
|
||||
Полный CRUD - все пути сгенерированы
|
||||
"""
|
||||
|
||||
paths = self.FullCRUDViewSet.get_paths()
|
||||
|
||||
self.assertEqual(len(paths), 2)
|
||||
|
||||
list_path = paths[0]
|
||||
detail_path = paths[1]
|
||||
|
||||
self.assertEqual(list_path.pattern._route, "test/")
|
||||
self.assertEqual(detail_path.pattern._route, "test/<uuid:pk>/")
|
||||
|
||||
def test_get_paths_list_only(self):
|
||||
"""
|
||||
Только список - только list path
|
||||
"""
|
||||
|
||||
paths = self.ListOnlyViewSet.get_paths()
|
||||
|
||||
self.assertEqual(len(paths), 1)
|
||||
|
||||
list_path = paths[0]
|
||||
self.assertEqual(list_path.pattern._route, "test/")
|
||||
|
||||
def test_get_paths_detail_only(self):
|
||||
"""
|
||||
Только детали - только detail path
|
||||
"""
|
||||
|
||||
paths = self.DetailOnlyViewSet.get_paths()
|
||||
|
||||
self.assertEqual(len(paths), 1)
|
||||
|
||||
detail_path = paths[0]
|
||||
self.assertEqual(detail_path.pattern._route, "test/<uuid:pk>/")
|
||||
|
||||
def test_get_paths_no_patterns(self):
|
||||
"""
|
||||
Нет паттернов - пустой список
|
||||
"""
|
||||
|
||||
class EmptyViewSet(CoreViewSet):
|
||||
pass
|
||||
|
||||
paths = EmptyViewSet.get_paths()
|
||||
|
||||
self.assertEqual(paths, [])
|
||||
|
||||
def test_get_paths_list_without_methods(self):
|
||||
"""
|
||||
Есть паттерн, но нет методов - пустой список
|
||||
"""
|
||||
|
||||
class NoMethodsViewSet(CoreViewSet):
|
||||
item_list_pattern = "test/"
|
||||
|
||||
with self.assertRaises(ImproperlyConfigured):
|
||||
NoMethodsViewSet.get_paths()
|
||||
|
||||
def test_path_method_valid_action(self):
|
||||
"""
|
||||
Получение метода для валидного действия
|
||||
"""
|
||||
|
||||
result = CoreViewSet._path_method(ViewSetAction.LIST)
|
||||
self.assertEqual(result, "item_list")
|
||||
|
||||
def test_path_method_exists_true(self):
|
||||
"""
|
||||
Проверка существования метода - True
|
||||
"""
|
||||
|
||||
result = self.FullCRUDViewSet._path_method_exists(ViewSetAction.LIST)
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_path_method_exists_false(self):
|
||||
"""
|
||||
Проверка существования метода - False
|
||||
"""
|
||||
|
||||
class EmptyViewSet(CoreViewSet):
|
||||
pass
|
||||
|
||||
result = EmptyViewSet._path_method_exists(ViewSetAction.LIST)
|
||||
self.assertFalse(result)
|
||||
@@ -1,320 +0,0 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Описание класса CoreViewSetProcessDataParamsTestsTest
|
||||
"""
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from net.xeaf.rack.core.tests import CoreTestCase
|
||||
from net.xeaf.rack.core.views import CoreViewSet
|
||||
|
||||
|
||||
class DataParamsSerializer(serializers.Serializer):
|
||||
"""
|
||||
Сериализатор для тестов data params
|
||||
"""
|
||||
|
||||
name = serializers.CharField(max_length=100)
|
||||
age = serializers.IntegerField(min_value=0, max_value=150)
|
||||
email = serializers.EmailField(required=False)
|
||||
|
||||
|
||||
class MockViewSetForDataParams(CoreViewSet):
|
||||
"""
|
||||
ViewSet для тестов data params
|
||||
"""
|
||||
|
||||
permission_classes = ()
|
||||
|
||||
def _get_request_method(self):
|
||||
"""
|
||||
Метод по умолчанию
|
||||
"""
|
||||
|
||||
return 'POST' # По умолчанию POST
|
||||
|
||||
def _get_request_session(self):
|
||||
"""
|
||||
Нет сессии
|
||||
"""
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class CoreViewSetProcessDataParamsTests(CoreTestCase):
|
||||
"""
|
||||
Тесты для CoreViewSet._process_data_params
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Подготовка к тестам
|
||||
"""
|
||||
|
||||
super().setUp()
|
||||
self.view_set = MockViewSetForDataParams()
|
||||
|
||||
def test_process_data_params_no_serializer(self):
|
||||
"""
|
||||
Без сериализатора - возвращаются исходные данные
|
||||
"""
|
||||
|
||||
data = {'name': 'Test', 'age': 25}
|
||||
request = self.create_i_ivanov_request(
|
||||
'POST',
|
||||
'/test',
|
||||
data=data
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._process_data_params(None)
|
||||
|
||||
self.assertEqual(result, data)
|
||||
|
||||
def test_process_data_params_with_serializer(self):
|
||||
"""
|
||||
С сериализатором - валидирует и возвращает данные
|
||||
Проверяет, что данные проходят валидацию через сериализатор
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'POST',
|
||||
'/test',
|
||||
data={'name': 'Ivan Ivanov', 'age': 30}
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._process_data_params(DataParamsSerializer)
|
||||
|
||||
self.assertEqual(result['name'], 'Ivan Ivanov')
|
||||
self.assertEqual(result['age'], 30)
|
||||
self.assertIsInstance(result['age'], int)
|
||||
|
||||
def test_process_data_params_with_serializer_optional_fields(self):
|
||||
"""
|
||||
С сериализатором - опциональные поля
|
||||
Проверяет, что опциональные поля могут отсутствовать
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'POST',
|
||||
'/test',
|
||||
data={'name': 'Ivan Ivanov', 'age': 30}
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._process_data_params(DataParamsSerializer)
|
||||
|
||||
self.assertEqual(result['name'], 'Ivan Ivanov')
|
||||
self.assertEqual(result['age'], 30)
|
||||
self.assertNotIn('email', result)
|
||||
|
||||
def test_process_data_params_with_serializer_optional_field_present(self):
|
||||
"""
|
||||
Опциональное поле передано
|
||||
Проверяет, что опциональное поле обрабатывается корректно
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'POST',
|
||||
'/test',
|
||||
data={
|
||||
'name': 'Ivan Ivanov',
|
||||
'age': 30,
|
||||
'email': 'ivan@example.com'
|
||||
}
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._process_data_params(DataParamsSerializer)
|
||||
|
||||
self.assertEqual(result['name'], 'Ivan Ivanov')
|
||||
self.assertEqual(result['age'], 30)
|
||||
self.assertEqual(result['email'], 'ivan@example.com')
|
||||
|
||||
def test_process_data_params_with_serializer_invalid_raises_error(self):
|
||||
"""
|
||||
Невалидные данные - ошибка валидации
|
||||
Проверяет, что сериализатор выбрасывает ValidationError
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'POST',
|
||||
'/test',
|
||||
data={'name': '', 'age': 200} # name пустой, age > 150
|
||||
)
|
||||
self.view_set.request = request
|
||||
|
||||
with self.assertRaises(serializers.ValidationError):
|
||||
self.view_set._process_data_params(DataParamsSerializer)
|
||||
|
||||
def test_process_data_params_with_serializer_missing_required(self):
|
||||
"""
|
||||
Отсутствие обязательного поля
|
||||
Проверяет, что при отсутствии required поля выбрасывается ошибка
|
||||
"""
|
||||
# Arrange
|
||||
request = self.create_i_ivanov_request(
|
||||
'POST',
|
||||
'/test',
|
||||
data={'age': 30} # name отсутствует
|
||||
)
|
||||
self.view_set.request = request
|
||||
|
||||
with self.assertRaises(serializers.ValidationError):
|
||||
self.view_set._process_data_params(DataParamsSerializer)
|
||||
|
||||
def test_process_data_params_with_serializer_invalid_email(self):
|
||||
"""
|
||||
Невалидный email
|
||||
Проверяет, что невалидный email вызывает ошибку
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'POST',
|
||||
'/test',
|
||||
data={
|
||||
'name': 'Ivan Ivanov',
|
||||
'age': 30,
|
||||
'email': 'invalid-email' # невалидный email
|
||||
}
|
||||
)
|
||||
self.view_set.request = request
|
||||
|
||||
with self.assertRaises(serializers.ValidationError):
|
||||
self.view_set._process_data_params(DataParamsSerializer)
|
||||
|
||||
def test_process_data_params_with_partial_update(self):
|
||||
"""
|
||||
Частичное обновление (PATCH) - partial=True
|
||||
Проверяет, что при PATCH запросе partial=True
|
||||
"""
|
||||
|
||||
class PartialViewSet(MockViewSetForDataParams):
|
||||
def _get_request_method(self):
|
||||
return 'PATCH'
|
||||
|
||||
view_set = PartialViewSet()
|
||||
request = self.create_i_ivanov_request(
|
||||
'PATCH',
|
||||
'/test',
|
||||
data={'name': 'Updated Name'}
|
||||
)
|
||||
view_set.request = request
|
||||
result = view_set._process_data_params(DataParamsSerializer)
|
||||
|
||||
self.assertEqual(result['name'], 'Updated Name')
|
||||
self.assertNotIn('age', result)
|
||||
self.assertNotIn('email', result)
|
||||
|
||||
def test_process_data_params_required_field_missing_partial(self):
|
||||
"""
|
||||
Отсутствие обязательного поля при partial=True
|
||||
Проверяет, что при partial=True можно не указывать обязательные поля
|
||||
"""
|
||||
|
||||
class PartialViewSet(MockViewSetForDataParams):
|
||||
def _get_request_method(self):
|
||||
return 'PATCH'
|
||||
|
||||
view_set = PartialViewSet()
|
||||
request = self.create_i_ivanov_request(
|
||||
'PATCH',
|
||||
'/test',
|
||||
data={'email': 'test@example.com'} # только опциональное поле
|
||||
)
|
||||
view_set.request = request
|
||||
result = view_set._process_data_params(DataParamsSerializer)
|
||||
|
||||
self.assertEqual(result['email'], 'test@example.com')
|
||||
self.assertNotIn('name', result)
|
||||
self.assertNotIn('age', result)
|
||||
|
||||
def test_process_data_params_required_field_missing_full_update(self):
|
||||
"""
|
||||
Отсутствие обязательного поля при полном обновлении (PUT)
|
||||
Проверяет, что при PUT обязательные поля должны быть указаны
|
||||
"""
|
||||
|
||||
class PutViewSet(MockViewSetForDataParams):
|
||||
def _get_request_method(self):
|
||||
return 'PUT'
|
||||
|
||||
view_set = PutViewSet()
|
||||
request = self.create_i_ivanov_request(
|
||||
'PUT',
|
||||
'/test',
|
||||
data={'email': 'test@example.com'} # name и age отсутствуют
|
||||
)
|
||||
view_set.request = request
|
||||
|
||||
with self.assertRaises(serializers.ValidationError):
|
||||
view_set._process_data_params(DataParamsSerializer)
|
||||
|
||||
def test_process_data_params_with_extra_fields(self):
|
||||
"""
|
||||
Лишние поля в данных - сериализатор их игнорирует
|
||||
Проверяет, что сериализатор игнорирует поля, не определенные в нем
|
||||
"""
|
||||
# Arrange
|
||||
request = self.create_i_ivanov_request(
|
||||
'POST',
|
||||
'/test',
|
||||
data={
|
||||
'name': 'Ivan Ivanov',
|
||||
'age': 30,
|
||||
'extra_field': 'should_be_ignored'
|
||||
}
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._process_data_params(DataParamsSerializer)
|
||||
|
||||
self.assertEqual(result['name'], 'Ivan Ivanov')
|
||||
self.assertEqual(result['age'], 30)
|
||||
self.assertNotIn('extra_field', result)
|
||||
|
||||
def test_process_data_params_with_petrov_user(self):
|
||||
"""
|
||||
Запрос от Петрова с данными
|
||||
Проверяет, что data params работают для любого пользователя
|
||||
"""
|
||||
|
||||
request = self.create_p_petrov_request(
|
||||
'POST',
|
||||
'/test',
|
||||
data={'name': 'Petr Petrov', 'age': 25}
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._process_data_params(DataParamsSerializer)
|
||||
|
||||
self.assertEqual(result['name'], 'Petr Petrov')
|
||||
self.assertEqual(result['age'], 25)
|
||||
|
||||
def test_process_data_params_with_empty_data(self):
|
||||
"""
|
||||
Пустые данные
|
||||
Проверяет обработку пустого request.data
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request('POST', '/test', data={})
|
||||
self.view_set.request = request
|
||||
|
||||
with self.assertRaises(serializers.ValidationError):
|
||||
self.view_set._process_data_params(DataParamsSerializer)
|
||||
|
||||
def test_process_data_params_with_null_values(self):
|
||||
"""
|
||||
Null значения в данных
|
||||
Проверяет, что null значения обрабатываются корректно
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'POST',
|
||||
'/test',
|
||||
data={'name': None, 'age': 30}
|
||||
)
|
||||
self.view_set.request = request
|
||||
|
||||
with self.assertRaises(serializers.ValidationError):
|
||||
self.view_set._process_data_params(DataParamsSerializer)
|
||||
@@ -1,72 +0,0 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Описание класса CoreViewSetProcessPkParamTest
|
||||
"""
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from django.test import SimpleTestCase
|
||||
|
||||
from net.xeaf.rack.core.views import CoreViewSet
|
||||
from net.xeaf.rack.utils.exceptions import BadRequestException
|
||||
|
||||
|
||||
class MockViewSet(CoreViewSet):
|
||||
"""
|
||||
Тестовый класс представления
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class CoreViewSetProcessPkParamTests(SimpleTestCase):
|
||||
"""
|
||||
Тесты для CoreViewSet._process_pk_param
|
||||
"""
|
||||
|
||||
def test_process_pk_param_valid_uuid(self):
|
||||
"""
|
||||
Валидный UUID передан
|
||||
"""
|
||||
|
||||
view_set = MockViewSet()
|
||||
valid_uuid = "bd428358-6944-4657-8044-a3dc1c4dff8a"
|
||||
result = view_set._process_pk_param(pk=valid_uuid)
|
||||
|
||||
self.assertIsInstance(result, UUID)
|
||||
self.assertEqual(str(result), valid_uuid)
|
||||
|
||||
def test_process_pk_param_missing_raises_error(self):
|
||||
"""
|
||||
Параметр pk отсутствует - ошибка
|
||||
"""
|
||||
|
||||
view_set = MockViewSet()
|
||||
with self.assertRaises(BadRequestException):
|
||||
view_set._process_pk_param()
|
||||
|
||||
def test_process_pk_param_invalid_uuid_raises_error(self):
|
||||
"""
|
||||
Невалидный UUID - ошибка
|
||||
"""
|
||||
|
||||
view_set = MockViewSet()
|
||||
with self.assertRaises(ValueError):
|
||||
view_set._process_pk_param(pk="invalid-uuid")
|
||||
|
||||
def test_process_pk_param_ignores_extra_kwargs(self):
|
||||
"""
|
||||
Лишние параметры игнорируются
|
||||
"""
|
||||
|
||||
view_set = MockViewSet()
|
||||
valid_uuid = "bd428358-6944-4657-8044-a3dc1c4dff8a"
|
||||
result = view_set._process_pk_param(pk=valid_uuid, extra="value")
|
||||
|
||||
self.assertIsInstance(result, UUID)
|
||||
self.assertEqual(str(result), valid_uuid)
|
||||
@@ -1,278 +0,0 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Описание класса CoreViewSetProcessQueryParamsTests
|
||||
"""
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from net.xeaf.rack.core.tests import CoreTestCase
|
||||
from net.xeaf.rack.core.views import CoreViewSet
|
||||
from net.xeaf.rack.management.userstory import AccountTestData
|
||||
from net.xeaf.rack.management.userstory import SessionTestData
|
||||
|
||||
|
||||
class QueryParamsSerializer(serializers.Serializer):
|
||||
"""
|
||||
Сериализатор параметров тестового запроса
|
||||
"""
|
||||
|
||||
limit = serializers.IntegerField(required=False, default=10)
|
||||
offset = serializers.IntegerField(required=False, default=0)
|
||||
search = serializers.CharField(required=False, default='', allow_blank=True)
|
||||
|
||||
|
||||
class MockViewSetForQueryParams(CoreViewSet):
|
||||
"""
|
||||
Тестовый класс представления
|
||||
"""
|
||||
|
||||
permission_classes = ()
|
||||
""" Классы проверки прав доступа """
|
||||
|
||||
def _get_request_session(self):
|
||||
"""
|
||||
Возвращает сессию пользователя из запроса
|
||||
|
||||
:return: Сессия пользователя
|
||||
"""
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class CoreViewSetProcessQueryParamsTests(CoreTestCase):
|
||||
"""
|
||||
Тесты для CoreViewSet._process_query_params
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Подготовка к тестированию
|
||||
"""
|
||||
|
||||
super().setUp()
|
||||
self.view_set = MockViewSetForQueryParams()
|
||||
|
||||
def test_process_query_params_no_serializer(self):
|
||||
"""
|
||||
Без сериализатора - возвращает словарь с параметрами
|
||||
Проверяет, что параметры извлекаются из query_params и мержатся с kwargs
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'GET',
|
||||
'/test',
|
||||
query_params={
|
||||
'param1': 'value1',
|
||||
'param2': 'value2'
|
||||
}
|
||||
)
|
||||
self.view_set.request = request
|
||||
|
||||
result = self.view_set._process_query_params(
|
||||
None,
|
||||
custom_param='custom_value'
|
||||
)
|
||||
|
||||
self.assertEqual(result['param1'], 'value1')
|
||||
self.assertEqual(result['param2'], 'value2')
|
||||
self.assertEqual(result['custom_param'], 'custom_value')
|
||||
self.assertNotIn('request', result)
|
||||
|
||||
def test_process_query_params_with_serializer(self):
|
||||
"""
|
||||
С сериализатором - валидирует и возвращает данные
|
||||
Проверяет, что параметры проходят валидацию через сериализатор
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'GET',
|
||||
'/test',
|
||||
query_params={
|
||||
'limit': '20',
|
||||
'offset': '5',
|
||||
'search': 'test'
|
||||
}
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._process_query_params(QueryParamsSerializer)
|
||||
|
||||
self.assertEqual(result['limit'], 20)
|
||||
self.assertEqual(result['offset'], 5)
|
||||
self.assertEqual(result['search'], 'test')
|
||||
|
||||
def test_process_query_params_with_serializer_defaults(self):
|
||||
"""
|
||||
С сериализатором - значения по умолчанию
|
||||
Проверяет, что отсутствующие параметры заполняются значениями по умолчанию
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'GET',
|
||||
'/test',
|
||||
query_params={}
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._process_query_params(QueryParamsSerializer)
|
||||
|
||||
self.assertEqual(result['limit'], 10) # default
|
||||
self.assertEqual(result['offset'], 0) # default
|
||||
self.assertEqual(result['search'], '') # allow_blank=True
|
||||
|
||||
def test_process_query_params_with_serializer_invalid_raises_error(self):
|
||||
"""
|
||||
Невалидные параметры - ошибка валидации
|
||||
Проверяет, что сериализатор выбрасывает ValidationError
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'GET',
|
||||
'/test',
|
||||
query_params={
|
||||
'limit': 'invalid', # невалидное значение
|
||||
'offset': '5'
|
||||
}
|
||||
)
|
||||
self.view_set.request = request
|
||||
|
||||
with self.assertRaises(serializers.ValidationError):
|
||||
self.view_set._process_query_params(QueryParamsSerializer)
|
||||
|
||||
def test_process_query_params_merges_with_kwargs(self):
|
||||
"""
|
||||
Параметры из kwargs переопределяют параметры запроса
|
||||
Проверяет, что kwargs имеют приоритет над query_params
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'GET',
|
||||
'/test',
|
||||
query_params={'limit': '20'}
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._process_query_params(
|
||||
None,
|
||||
limit=50 # переопределяем
|
||||
)
|
||||
|
||||
self.assertEqual(result['limit'], 50)
|
||||
|
||||
def test_process_query_params_with_serializer_ignores_extra_kwargs(self):
|
||||
"""
|
||||
С сериализатором - только валидированные параметры
|
||||
Проверяет, что сериализатор игнорирует параметры, не определенные в нем
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'GET',
|
||||
'/test',
|
||||
query_params={
|
||||
'limit': '20',
|
||||
'offset': '5'
|
||||
}
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._process_query_params(
|
||||
QueryParamsSerializer,
|
||||
extra_param='extra_value' # не входит в сериализатор
|
||||
)
|
||||
|
||||
self.assertNotIn('extra_param', result)
|
||||
self.assertEqual(result['limit'], 20)
|
||||
self.assertEqual(result['offset'], 5)
|
||||
|
||||
def test_process_query_params_empty_params(self):
|
||||
"""
|
||||
Пустые параметры запроса
|
||||
Проверяет обработку пустого query_params
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request('GET', '/test')
|
||||
self.view_set.request = request
|
||||
|
||||
result = self.view_set._process_query_params(None)
|
||||
|
||||
self.assertEqual(result, {})
|
||||
|
||||
def test_process_query_params_filters_request_param(self):
|
||||
"""
|
||||
Фильтрация параметра 'request'
|
||||
Проверяет, что параметр 'request' не попадает в результат
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'GET',
|
||||
'/test',
|
||||
query_params={
|
||||
'request': 'should_be_filtered',
|
||||
'param': 'value'
|
||||
}
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._process_query_params(None)
|
||||
|
||||
self.assertNotIn('request', result)
|
||||
self.assertEqual(result['param'], 'value')
|
||||
|
||||
def test_process_query_params_with_petrov_user(self):
|
||||
"""
|
||||
Запрос от Петрова с параметрами
|
||||
Проверяет, что query_params работают для любого пользователя
|
||||
"""
|
||||
|
||||
request = self.create_p_petrov_request(
|
||||
'GET',
|
||||
'/test',
|
||||
query_params={
|
||||
'limit': '15',
|
||||
'offset': '3'
|
||||
}
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._process_query_params(QueryParamsSerializer)
|
||||
|
||||
self.assertEqual(result['limit'], 15)
|
||||
self.assertEqual(result['offset'], 3)
|
||||
|
||||
def test_process_query_params_with_non_get_method(self):
|
||||
"""
|
||||
Не-GET запрос с query_params
|
||||
Проверяет, что query_params работают и для не-GET запросов
|
||||
"""
|
||||
|
||||
request = self.create_i_ivanov_request(
|
||||
'POST',
|
||||
'/test',
|
||||
data={'name': 'Test'},
|
||||
query_params={'filter': 'active'}
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._process_query_params(None)
|
||||
|
||||
self.assertEqual(result['filter'], 'active')
|
||||
|
||||
def test_process_query_params_with_serializer_and_ivanov_session(self):
|
||||
"""
|
||||
Проверка, что сессия Ивана доступна в контексте
|
||||
Используем get_ivanov_session() для проверки
|
||||
"""
|
||||
|
||||
ivanov_session = self.get_i_ivanov_session()
|
||||
request = self.create_request(
|
||||
'GET',
|
||||
'/test',
|
||||
query_params={'limit': '20'},
|
||||
auth=ivanov_session
|
||||
)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._process_query_params(QueryParamsSerializer)
|
||||
|
||||
self.assertEqual(result['limit'], 20)
|
||||
|
||||
# Проверяем, что сессия действительно Ивана
|
||||
self.assertEqual(ivanov_session.token, SessionTestData.I_IVANOV_SESSION_TOKEN)
|
||||
self.assertEqual(str(ivanov_session.account.id), str(AccountTestData.I_IVANOV_ID))
|
||||
@@ -1,136 +0,0 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Описание класса CoreViewSetRequestContextTests
|
||||
"""
|
||||
|
||||
from net.xeaf.rack.core.tests import CoreTestCase
|
||||
from net.xeaf.rack.core.views import CoreViewSet
|
||||
from net.xeaf.rack.management.userstory import AccountTestData
|
||||
from net.xeaf.rack.management.userstory import SessionTestData
|
||||
from net.xeaf.rack.utils.exceptions import UnauthorizedException
|
||||
|
||||
|
||||
class MockViewSetWithAuth(CoreViewSet):
|
||||
"""
|
||||
ViewSet для тестов аутентификации
|
||||
"""
|
||||
|
||||
permission_classes = ()
|
||||
|
||||
|
||||
class CoreViewSetRequestContextTests(CoreTestCase):
|
||||
"""
|
||||
Тесты методов получения сессии и аккаунта
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Подготовка к тестированию
|
||||
"""
|
||||
|
||||
super().setUp()
|
||||
self.view_set = MockViewSetWithAuth()
|
||||
|
||||
def test_get_request_session_authenticated(self):
|
||||
"""
|
||||
Получение сессии для аутентифицированного запроса
|
||||
"""
|
||||
|
||||
session = self.get_i_ivanov_session()
|
||||
request = self.create_request('GET', '/test', auth=session)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._get_request_session()
|
||||
|
||||
self.assertIsNotNone(result)
|
||||
# noinspection PyUnresolvedReferences
|
||||
self.assertEqual(result.token, SessionTestData.I_IVANOV_SESSION_TOKEN)
|
||||
# noinspection PyUnresolvedReferences
|
||||
self.assertEqual(str(result.account.id), str(AccountTestData.I_IVANOV_ID))
|
||||
|
||||
def test_get_request_account_authenticated(self):
|
||||
"""
|
||||
Получение аккаунта для аутентифицированного запроса
|
||||
"""
|
||||
|
||||
session = self.get_i_ivanov_session()
|
||||
request = self.create_request('GET', '/test', auth=session)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._get_request_account()
|
||||
|
||||
self.assertIsNotNone(result)
|
||||
self.assertEqual(str(result.id), str(AccountTestData.I_IVANOV_ID))
|
||||
self.assertEqual(result.username, AccountTestData.I_IVANOV_USERNAME)
|
||||
|
||||
def test_get_request_account_unauthenticated_raises_error(self):
|
||||
"""
|
||||
Не аутентифицированный запрос - ошибка UnauthorizedException
|
||||
"""
|
||||
|
||||
request = self.create_request('GET', '/test')
|
||||
self.view_set.request = request
|
||||
|
||||
with self.assertRaises(UnauthorizedException):
|
||||
self.view_set._get_request_account()
|
||||
|
||||
def test_get_request_context_with_auth(self):
|
||||
"""
|
||||
Получение контекста запроса с аутентификацией
|
||||
"""
|
||||
|
||||
session = self.get_i_ivanov_session()
|
||||
request = self.create_request('GET', '/test', auth=session)
|
||||
self.view_set.request = request
|
||||
context = self.view_set._get_request_context()
|
||||
|
||||
self.assertIn('account', context)
|
||||
self.assertIn('request', context)
|
||||
self.assertEqual(str(context['account'].id), str(AccountTestData.I_IVANOV_ID))
|
||||
self.assertEqual(context['request'], request)
|
||||
|
||||
def test_get_request_context_without_auth(self):
|
||||
"""
|
||||
Получение контекста без аутентификации - account = None
|
||||
"""
|
||||
|
||||
request = self.create_request('GET', '/test')
|
||||
self.view_set.request = request
|
||||
context = self.view_set._get_request_context()
|
||||
|
||||
self.assertIn('account', context)
|
||||
self.assertIsNone(context['account'])
|
||||
self.assertIn('request', context)
|
||||
self.assertEqual(context['request'], request)
|
||||
|
||||
def test_get_request_session_petrov_authenticated(self):
|
||||
"""
|
||||
Получение сессии Петрова
|
||||
"""
|
||||
|
||||
session = self.get_p_petrov_session()
|
||||
request = self.create_request('GET', '/test', auth=session)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._get_request_session()
|
||||
|
||||
self.assertIsNotNone(result)
|
||||
# noinspection PyUnresolvedReferences
|
||||
self.assertEqual(result.token, SessionTestData.P_PETROV_SESSION_TOKEN)
|
||||
# noinspection PyUnresolvedReferences
|
||||
self.assertEqual(str(result.account.id), str(AccountTestData.P_PETROV_ID))
|
||||
|
||||
def test_get_request_account_petrov_authenticated(self):
|
||||
"""
|
||||
Получение аккаунта Петрова
|
||||
"""
|
||||
session = self.get_p_petrov_session()
|
||||
request = self.create_request('GET', '/test', auth=session)
|
||||
self.view_set.request = request
|
||||
result = self.view_set._get_request_account()
|
||||
|
||||
self.assertIsNotNone(result)
|
||||
self.assertEqual(str(result.id), str(AccountTestData.P_PETROV_ID))
|
||||
self.assertEqual(result.username, AccountTestData.P_PETROV_USERNAME)
|
||||
Reference in New Issue
Block a user