feat(core): add core module with mixins, services, and background jobs

- Add Model Mixins: TimestampMixin, SoftDeleteMixin, AuditMixin, etc.
- Add Base Services: BaseService, BulkOperationsMixin, QueryOptimizerMixin
- Add Base ViewSets with bulk operations
- Add BackgroundJob model for Celery task tracking
- Add BaseAppCommand for management commands
- Add permissions, pagination, filters, cache, logging
- Migrate tests to factory_boy + faker
- Add CHANGELOG.md
- 297 tests passing
This commit is contained in:
2026-01-21 11:47:26 +01:00
parent 06b30fca02
commit f121445313
72 changed files with 9258 additions and 594 deletions

View File

@@ -0,0 +1,133 @@
"""Tests for core OpenAPI utilities"""
from apps.core.openapi import (
CommonParameters,
CommonResponses,
_get_status_description,
api_docs,
paginated_response,
)
from django.test import TestCase
from drf_yasg import openapi
from rest_framework import serializers
class DummySerializer(serializers.Serializer):
"""Dummy serializer for testing"""
id = serializers.IntegerField()
name = serializers.CharField()
class ApiDocsDecoratorTest(TestCase):
"""Tests for @api_docs decorator"""
def test_decorator_returns_function(self):
"""Test decorator returns wrapped function"""
@api_docs(summary="Test endpoint")
def my_view(request):
pass
self.assertTrue(callable(my_view))
def test_decorator_preserves_function_name(self):
"""Test decorator preserves original function name"""
@api_docs(summary="Test endpoint")
def my_view(request):
pass
self.assertEqual(my_view.__name__, "my_view")
class GetStatusDescriptionTest(TestCase):
"""Tests for _get_status_description function"""
def test_known_status_codes(self):
"""Test known status codes return Russian descriptions"""
self.assertEqual(_get_status_description(200), "Успешный запрос")
self.assertEqual(_get_status_description(201), "Ресурс создан")
self.assertEqual(_get_status_description(400), "Некорректный запрос")
self.assertEqual(_get_status_description(401), "Не авторизован")
self.assertEqual(_get_status_description(403), "Доступ запрещён")
self.assertEqual(_get_status_description(404), "Ресурс не найден")
self.assertEqual(_get_status_description(500), "Внутренняя ошибка сервера")
def test_unknown_status_code(self):
"""Test unknown status code returns generic description"""
result = _get_status_description(418)
self.assertEqual(result, "HTTP 418")
class CommonResponsesTest(TestCase):
"""Tests for CommonResponses class"""
def test_success_response_type(self):
"""Test SUCCESS is an openapi.Response"""
self.assertIsInstance(CommonResponses.SUCCESS, openapi.Response)
def test_created_response_type(self):
"""Test CREATED is an openapi.Response"""
self.assertIsInstance(CommonResponses.CREATED, openapi.Response)
def test_not_found_response_type(self):
"""Test NOT_FOUND is an openapi.Response"""
self.assertIsInstance(CommonResponses.NOT_FOUND, openapi.Response)
def test_unauthorized_response_type(self):
"""Test UNAUTHORIZED is an openapi.Response"""
self.assertIsInstance(CommonResponses.UNAUTHORIZED, openapi.Response)
def test_validation_error_response_type(self):
"""Test VALIDATION_ERROR is an openapi.Response"""
self.assertIsInstance(CommonResponses.VALIDATION_ERROR, openapi.Response)
def test_server_error_response_type(self):
"""Test SERVER_ERROR is an openapi.Response"""
self.assertIsInstance(CommonResponses.SERVER_ERROR, openapi.Response)
class CommonParametersTest(TestCase):
"""Tests for CommonParameters class"""
def test_page_parameter(self):
"""Test PAGE parameter configuration"""
self.assertEqual(CommonParameters.PAGE.name, "page")
self.assertEqual(CommonParameters.PAGE.in_, openapi.IN_QUERY)
self.assertEqual(CommonParameters.PAGE.type, openapi.TYPE_INTEGER)
def test_page_size_parameter(self):
"""Test PAGE_SIZE parameter configuration"""
self.assertEqual(CommonParameters.PAGE_SIZE.name, "page_size")
self.assertEqual(CommonParameters.PAGE_SIZE.in_, openapi.IN_QUERY)
def test_search_parameter(self):
"""Test SEARCH parameter configuration"""
self.assertEqual(CommonParameters.SEARCH.name, "search")
self.assertEqual(CommonParameters.SEARCH.type, openapi.TYPE_STRING)
def test_ordering_parameter(self):
"""Test ORDERING parameter configuration"""
self.assertEqual(CommonParameters.ORDERING.name, "ordering")
self.assertEqual(CommonParameters.ORDERING.type, openapi.TYPE_STRING)
def test_id_parameter(self):
"""Test ID parameter configuration"""
self.assertEqual(CommonParameters.ID.name, "id")
self.assertEqual(CommonParameters.ID.in_, openapi.IN_PATH)
self.assertTrue(CommonParameters.ID.required)
class PaginatedResponseTest(TestCase):
"""Tests for paginated_response function"""
def test_returns_response_object(self):
"""Test function returns openapi.Response"""
result = paginated_response(DummySerializer)
self.assertIsInstance(result, openapi.Response)
def test_response_has_description(self):
"""Test response has description"""
result = paginated_response(DummySerializer)
self.assertEqual(result.description, "Пагинированный список")