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,110 @@
"""Тесты для Model Mixins."""
from apps.core.mixins import (
OrderableMixin,
SoftDeleteMixin,
StatusMixin,
)
from django.test import TestCase
class TimestampMixinTest(TestCase):
"""Тесты для TimestampMixin."""
def test_created_at_auto_set(self):
"""Проверка автоматической установки created_at."""
# Используем BackgroundJob как пример модели с TimestampMixin
from apps.core.models import BackgroundJob
job = BackgroundJob.objects.create(
task_id="test-task-1",
task_name="test.task",
)
self.assertIsNotNone(job.created_at)
self.assertIsNotNone(job.updated_at)
def test_updated_at_auto_update(self):
"""Проверка автоматического обновления updated_at."""
from apps.core.models import BackgroundJob
job = BackgroundJob.objects.create(
task_id="test-task-2",
task_name="test.task",
)
original_updated = job.updated_at
# Обновляем запись
job.progress = 50
job.save()
job.refresh_from_db()
self.assertGreaterEqual(job.updated_at, original_updated)
class UUIDPrimaryKeyMixinTest(TestCase):
"""Тесты для UUIDPrimaryKeyMixin."""
def test_uuid_auto_generated(self):
"""Проверка автоматической генерации UUID."""
from apps.core.models import BackgroundJob
job = BackgroundJob.objects.create(
task_id="test-task-3",
task_name="test.task",
)
self.assertIsNotNone(job.id)
# Проверяем что ID похож на UUID (строка 32+ символа с дефисами)
self.assertEqual(len(str(job.id)), 36)
def test_uuid_unique(self):
"""Проверка уникальности UUID."""
from apps.core.models import BackgroundJob
job1 = BackgroundJob.objects.create(
task_id="test-task-4a",
task_name="test.task",
)
job2 = BackgroundJob.objects.create(
task_id="test-task-4b",
task_name="test.task",
)
self.assertNotEqual(job1.id, job2.id)
class SoftDeleteMixinTest(TestCase):
"""Тесты для SoftDeleteMixin."""
def test_soft_delete_mixin_fields(self):
"""Проверка наличия полей is_deleted и deleted_at."""
# Проверяем что поля определены в миксине
field_names = [f.name for f in SoftDeleteMixin._meta.get_fields()]
self.assertIn("is_deleted", field_names)
self.assertIn("deleted_at", field_names)
def test_soft_delete_queryset_methods(self):
"""Проверка методов SoftDeleteQuerySet."""
from apps.core.mixins import SoftDeleteQuerySet
# Проверяем что методы определены
self.assertTrue(hasattr(SoftDeleteQuerySet, "alive"))
self.assertTrue(hasattr(SoftDeleteQuerySet, "dead"))
self.assertTrue(hasattr(SoftDeleteQuerySet, "hard_delete"))
class StatusMixinTest(TestCase):
"""Тесты для StatusMixin."""
def test_status_choices(self):
"""Проверка наличия статусов."""
self.assertEqual(StatusMixin.Status.DRAFT, "draft")
self.assertEqual(StatusMixin.Status.ACTIVE, "active")
self.assertEqual(StatusMixin.Status.INACTIVE, "inactive")
self.assertEqual(StatusMixin.Status.ARCHIVED, "archived")
class OrderableMixinTest(TestCase):
"""Тесты для OrderableMixin."""
def test_orderable_mixin_has_order_field(self):
"""Проверка наличия поля order."""
self.assertTrue(hasattr(OrderableMixin, "order"))