Добавлены тесты защиты системного клиента и рефакторинг структуры тестов

- Создан новый класс SystemCustomerProtectionTestCase с 5 критичными тестами
- Тест создания системного клиента с правильными атрибутами
- Тест защиты от удаления системного клиента (ValidationError)
- Тест защиты email системного клиента от изменения
- Тест защиты флага is_system_customer от изменения
- Тест что обычные клиенты не затронуты защитой

- Исправлена логика в Customer.save(): проверка теперь использует original.is_system_customer
- Добавлен импорт ValidationError из django.core.exceptions

- Рефакторинг структуры тестов customers:
  - Разделены тесты по отдельным модулям в папке customers/tests/
  - test_search_strategies.py - тесты стратегий поиска
  - test_system_customer.py - тесты защиты системного клиента
  - test_wallet_balance.py - тесты баланса кошелька
  - test_wallet_service.py - тесты WalletService
  - test_wallet_model.py - тесты модели WalletTransaction

- Обновлён анализ тестов: 50 тестов (было 45), все проходят успешно
- Критичная функциональность POS системы теперь покрыта тестами
- Учтена tenant-система (используется TenantTestCase)
This commit is contained in:
2025-12-28 00:32:45 +03:00
parent b1855cc9f0
commit dbbac933af
9 changed files with 754 additions and 507 deletions

View File

@@ -0,0 +1,135 @@
# -*- coding: utf-8 -*-
"""
Тесты защиты системного клиента от изменений и удаления.
Системный клиент используется для анонимных продаж в POS системе
и должен быть защищён от случайного изменения или удаления.
Используем TenantTestCase для корректной работы с tenant-системой.
"""
from django.core.exceptions import ValidationError
from django_tenants.test.cases import TenantTestCase
from customers.models import Customer
class SystemCustomerProtectionTestCase(TenantTestCase):
"""
Тесты защиты системного клиента от изменений и удаления.
Системный клиент используется для анонимных продаж в POS системе
и должен быть защищён от случайного изменения или удаления.
"""
def setUp(self):
"""Создаём системного клиента для тестов."""
self.system_customer, created = Customer.get_or_create_system_customer()
self.regular_customer = Customer.objects.create(
name="Обычный клиент",
email="regular@test.com"
)
def test_get_or_create_system_customer_creates_with_correct_attributes(self):
"""
Метод get_or_create_system_customer() создаёт клиента с правильными атрибутами.
Проверяем:
- Фиксированный email: system@pos.customer
- Флаг is_system_customer = True
- Правильное имя и заметки
"""
# Удаляем существующего системного клиента для чистоты теста
Customer.objects.filter(is_system_customer=True).delete()
# Создаём через метод класса
customer, created = Customer.get_or_create_system_customer()
# Проверяем, что клиент действительно создан
self.assertTrue(created, "Системный клиент должен быть создан")
# Проверяем атрибуты
self.assertEqual(customer.email, "system@pos.customer")
self.assertTrue(customer.is_system_customer)
self.assertEqual(customer.name, "АНОНИМНЫЙ ПОКУПАТЕЛЬ (POS)")
self.assertIn("SYSTEM_CUSTOMER", customer.notes)
# Проверяем идемпотентность - повторный вызов возвращает того же клиента
customer2, created2 = Customer.get_or_create_system_customer()
self.assertFalse(created2, "Системный клиент не должен создаваться повторно")
self.assertEqual(customer.pk, customer2.pk, "Должен вернуться тот же клиент")
def test_system_customer_cannot_be_deleted(self):
"""
Системный клиент защищён от удаления через метод delete().
При попытке удаления должен подниматься ValidationError.
Это критично для работы POS системы.
"""
with self.assertRaises(ValidationError) as context:
self.system_customer.delete()
self.assertIn("Нельзя удалить системного клиента", str(context.exception))
# Проверяем, что клиент действительно не удалён
self.assertTrue(
Customer.objects.filter(pk=self.system_customer.pk).exists(),
"Системный клиент не должен быть удалён"
)
def test_system_customer_email_cannot_be_changed(self):
"""
Email системного клиента защищён от изменения.
Фиксированный email "system@pos.customer" используется для поиска
системного клиента в POS системе. Изменение приведёт к сбоям.
"""
original_email = self.system_customer.email
# Пытаемся изменить email
self.system_customer.email = "hacker@evil.com"
with self.assertRaises(ValidationError) as context:
self.system_customer.save()
self.assertIn("Нельзя изменить email системного клиента", str(context.exception))
# Проверяем, что email остался прежним в БД
self.system_customer.refresh_from_db()
self.assertEqual(self.system_customer.email, original_email)
def test_system_customer_flag_cannot_be_removed(self):
"""
Флаг is_system_customer защищён от изменения.
Нельзя "превратить" системного клиента в обычного,
это нарушит логику POS системы.
"""
# Пытаемся снять флаг системного клиента
self.system_customer.is_system_customer = False
with self.assertRaises(ValidationError) as context:
self.system_customer.save()
self.assertIn("Нельзя изменить флаг системного клиента", str(context.exception))
# Проверяем, что флаг остался True в БД
self.system_customer.refresh_from_db()
self.assertTrue(self.system_customer.is_system_customer)
def test_regular_customer_can_be_deleted_normally(self):
"""
Обычный клиент (не системный) может быть удалён без ограничений.
Защита применяется ТОЛЬКО к системному клиенту.
Это гарантирует, что мы не сломали обычный функционал удаления.
"""
customer_pk = self.regular_customer.pk
# Удаление должно пройти успешно
self.regular_customer.delete()
# Проверяем, что клиент действительно удалён
self.assertFalse(
Customer.objects.filter(pk=customer_pk).exists(),
"Обычный клиент должен быть удалён"
)