Some checks failed
CI/CD Pipeline / Run Tests (push) Failing after 37s
CI/CD Pipeline / Code Quality Checks (push) Failing after 43s
CI/CD Pipeline / Build & Push Images (push) Has been skipped
CI/CD Pipeline / Deploy (dev) (push) Has been skipped
CI/CD Pipeline / Deploy (prod) (push) Has been skipped
CI/CD Pipeline / Code Quality Checks (pull_request) Failing after 0s
CI/CD Pipeline / Run Tests (pull_request) Failing after 0s
CI/CD Pipeline / Build & Push Images (pull_request) Has been skipped
CI/CD Pipeline / Deploy (dev) (pull_request) Has been skipped
CI/CD Pipeline / Deploy (prod) (pull_request) Has been skipped
- Обновлены клиенты парсеров (checko, fns, minpromtorg, proverki, zakupki) - Добавлены новые миграции для моделей - Расширено покрытие тестами - Обновлены конфигурации и настройки проекта - Добавлены утилиты для тестирования Co-Authored-By: Warp <agent@warp.dev>
208 lines
7.1 KiB
Python
208 lines
7.1 KiB
Python
"""Тесты для Model Mixins."""
|
||
|
||
from django.db import connection, models
|
||
from django.test import TestCase, TransactionTestCase
|
||
from django.test.utils import isolate_apps
|
||
|
||
from apps.core.mixins import (
|
||
AuditMixin,
|
||
OrderableMixin,
|
||
SlugMixin,
|
||
SoftDeleteMixin,
|
||
StatusMixin,
|
||
TimestampMixin,
|
||
)
|
||
from tests.apps.user.factories import UserFactory
|
||
|
||
|
||
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"))
|
||
|
||
|
||
@isolate_apps("apps.core")
|
||
class MixinsBehaviorTest(TransactionTestCase):
|
||
class TestModel(
|
||
SoftDeleteMixin,
|
||
OrderableMixin,
|
||
StatusMixin,
|
||
SlugMixin,
|
||
AuditMixin,
|
||
TimestampMixin,
|
||
models.Model,
|
||
):
|
||
name = models.CharField(max_length=50)
|
||
|
||
class Meta:
|
||
app_label = "core"
|
||
|
||
@classmethod
|
||
def setUpClass(cls):
|
||
super().setUpClass()
|
||
table_name = cls.TestModel._meta.db_table
|
||
existing_tables = connection.introspection.table_names()
|
||
with connection.schema_editor() as schema_editor:
|
||
if table_name in existing_tables:
|
||
schema_editor.delete_model(cls.TestModel)
|
||
schema_editor.create_model(cls.TestModel)
|
||
|
||
@classmethod
|
||
def tearDownClass(cls):
|
||
table_name = cls.TestModel._meta.db_table
|
||
with connection.schema_editor() as schema_editor:
|
||
if table_name in connection.introspection.table_names():
|
||
schema_editor.delete_model(cls.TestModel)
|
||
super().tearDownClass()
|
||
|
||
def test_soft_delete_and_restore(self):
|
||
user = UserFactory.create_user()
|
||
obj = self.TestModel.objects.create(
|
||
name=f"item-{user.id}",
|
||
slug=f"slug-{user.id}",
|
||
created_by=user,
|
||
)
|
||
obj.delete()
|
||
self.assertTrue(obj.is_deleted)
|
||
self.assertIsNotNone(obj.deleted_at)
|
||
self.assertEqual(self.TestModel.objects.count(), 0)
|
||
self.assertEqual(self.TestModel.all_objects.count(), 1)
|
||
obj.restore()
|
||
self.assertFalse(obj.is_deleted)
|
||
self.assertEqual(self.TestModel.objects.count(), 1)
|
||
|
||
def test_soft_delete_queryset_methods(self):
|
||
obj = self.TestModel.objects.create(name="alive", slug="alive-slug")
|
||
self.TestModel.objects.filter(id=obj.id).delete()
|
||
self.assertEqual(self.TestModel.objects.count(), 0)
|
||
self.assertEqual(self.TestModel.objects.deleted_only().count(), 1)
|
||
|
||
def test_hard_delete(self):
|
||
obj = self.TestModel.objects.create(name="hard", slug="hard-slug")
|
||
obj.hard_delete()
|
||
self.assertEqual(self.TestModel.all_objects.count(), 0)
|
||
|
||
def test_orderable_moves(self):
|
||
obj = self.TestModel.objects.create(name="ord", slug="ord-slug", order=2)
|
||
obj.move_up()
|
||
obj.refresh_from_db()
|
||
self.assertEqual(obj.order, 1)
|
||
obj.move_down()
|
||
obj.refresh_from_db()
|
||
self.assertEqual(obj.order, 2)
|
||
obj.move_to(5)
|
||
obj.refresh_from_db()
|
||
self.assertEqual(obj.order, 5)
|
||
obj.move_to(-1)
|
||
obj.refresh_from_db()
|
||
self.assertEqual(obj.order, 5)
|
||
|
||
def test_status_transitions(self):
|
||
obj = self.TestModel.objects.create(name="status", slug="status-slug")
|
||
self.assertTrue(obj.is_draft)
|
||
obj.activate()
|
||
obj.refresh_from_db()
|
||
self.assertTrue(obj.is_active_status)
|
||
obj.deactivate()
|
||
obj.refresh_from_db()
|
||
self.assertEqual(obj.status, obj.Status.INACTIVE)
|
||
obj.archive()
|
||
obj.refresh_from_db()
|
||
self.assertTrue(obj.is_archived)
|