Files
octopus/myproject/tenants/tests/test_tenant_creation.py
Andrey Smakotin b59ad725cb Рефакторинг: вынос логики онбординга тенанта в сервисный слой
Создан TenantOnboardingService как единый источник истины для:
- Активации заявки на регистрацию тенанта
- Создания Client, Domain, Subscription
- Инициализации системных данных (Customer, статусы, способы оплаты, склад, витрина)

Новые сервисы:
- TenantOnboardingService (tenants/services/onboarding.py)
- WarehouseService (inventory/services/warehouse_service.py)
- ShowcaseService (inventory/services/showcase_service.py)
- PaymentMethodService (orders/services/payment_method_service.py)

Рефакторинг:
- admin.py: 220 строк → 5 строк (делегирование сервису)
- init_tenant_data.py: 259 строк → 68 строк
- activate_registration.py: использует сервис
- Тесты обновлены для вызова сервиса напрямую

При создании тенанта автоматически создаются склад и витрина по умолчанию.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 14:52:55 +03:00

467 lines
22 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""
Интеграционные тесты создания тенантов.
Проверяем полный процесс онбординга нового тенанта:
1. Создание схемы БД
2. Создание домена
3. Создание суперпользователя
4. Создание способов оплаты
5. Создание системных статусов заказов
6. Создание системного клиента
7. Создание склада по умолчанию
8. Создание витрины по умолчанию
"""
from django.test import TestCase, TransactionTestCase
from django.db import connection
from django.contrib.auth import get_user_model
from django.conf import settings
from django_tenants.utils import schema_context
from tenants.models import Client, Domain, TenantRegistration
from orders.models import PaymentMethod, OrderStatus
from customers.models import Customer
from inventory.models import Warehouse, Showcase
User = get_user_model()
class TenantCreationIntegrationTest(TransactionTestCase):
"""
Интеграционный тест полного процесса создания тенанта.
TransactionTestCase используется потому что:
1. Нужно создавать реальные схемы БД
2. Нужно переключаться между схемами
3. Нужно очищать созданные схемы после теста
"""
def setUp(self):
"""Подготовка к каждому тесту"""
# Создаём администратора для каждого теста
self.admin_user = User.objects.create_superuser(
email='test_admin_per_test@example.com',
password='testpass123',
name='Test Admin'
)
def tearDown(self):
"""Очистка после каждого теста"""
# Удаляем все тестовые тенанты и их схемы
test_tenants = Client.objects.filter(schema_name__startswith='test_shop_')
for tenant in test_tenants:
schema_name = tenant.schema_name
# Удаляем схему вручную
with connection.cursor() as cursor:
cursor.execute(f'DROP SCHEMA IF EXISTS {schema_name} CASCADE')
# Удаляем запись тенанта
tenant.delete()
# Удаляем тестовые заявки
TenantRegistration.objects.filter(schema_name__startswith='test_shop_').delete()
def test_new_tenant_gets_all_5_payment_methods(self):
"""
КРИТИЧЕСКИЙ ТЕСТ: Новый тенант автоматически получает все 5 способов оплаты.
Это главный тест для проверки исправления бага с отсутствием account_balance.
"""
# 1. Создаём заявку на регистрацию
registration = TenantRegistration.objects.create(
shop_name='Тестовый магазин',
schema_name='test_shop_payment',
owner_email='owner@test.com',
owner_name='Тест Владелец',
phone='+375291111111',
status=TenantRegistration.STATUS_PENDING
)
# 2. Активируем заявку через сервис
from tenants.services import TenantOnboardingService
tenant = TenantOnboardingService.activate_registration(registration, self.admin_user)
# 3. Проверяем что тенант создан
self.assertIsNotNone(tenant)
self.assertEqual(tenant.schema_name, 'test_shop_payment')
# 4. Переключаемся на схему нового тенанта
with schema_context('test_shop_payment'):
# 5. ГЛАВНАЯ ПРОВЕРКА: Все 5 способов оплаты созданы
payment_methods = PaymentMethod.objects.all().order_by('order')
self.assertEqual(
payment_methods.count(),
5,
f"Ожидалось 5 способов оплаты, получено {payment_methods.count()}"
)
# 6. Проверяем каждый способ оплаты по отдельности
expected_methods = [
('account_balance', 'С баланса счёта', 0),
('cash', 'Наличными', 1),
('card', 'Картой', 2),
('online', 'Онлайн', 3),
('legal_entity', 'Безнал от ЮРЛИЦ', 4),
]
for code, name, order in expected_methods:
method = PaymentMethod.objects.filter(code=code).first()
self.assertIsNotNone(
method,
f"Способ оплаты '{code}' не создан!"
)
self.assertEqual(method.name, name)
self.assertEqual(method.order, order)
self.assertTrue(method.is_system)
self.assertTrue(method.is_active)
# 7. Проверяем что account_balance на первом месте
first_method = payment_methods.first()
self.assertEqual(
first_method.code,
'account_balance',
f"Первый способ оплаты должен быть 'account_balance', но получен '{first_method.code}'"
)
def test_new_tenant_gets_order_statuses(self):
"""
Тест: Новый тенант получает системные статусы заказов.
КРИТИЧЕСКИ ВАЖНО: Должен быть минимум ОДИН позитивный и ОДИН негативный финальный статус.
"""
# Создаём и активируем тенант
registration = TenantRegistration.objects.create(
shop_name='Тестовый магазин статусы',
schema_name='test_shop_statuses',
owner_email='owner2@test.com',
owner_name='Тест Владелец 2',
phone='+375291111112',
status=TenantRegistration.STATUS_PENDING
)
from tenants.services import TenantOnboardingService
tenant = TenantOnboardingService.activate_registration(registration, self.admin_user)
# Проверяем статусы заказов
with schema_context('test_shop_statuses'):
statuses = OrderStatus.objects.all()
# Должно быть минимум 3 основных статуса
self.assertGreaterEqual(
statuses.count(),
3,
"Должно быть создано минимум 3 системных статуса"
)
# Проверяем наличие ключевых статусов
draft_status = OrderStatus.objects.filter(code='draft').first()
self.assertIsNotNone(draft_status, "Статус 'draft' не создан")
self.assertTrue(draft_status.is_system)
# КРИТИЧЕСКАЯ ПРОВЕРКА #1: Позитивный финальный статус
positive_statuses = OrderStatus.objects.filter(is_positive_end=True)
self.assertGreaterEqual(
positive_statuses.count(),
1,
"Должен быть минимум ОДИН позитивный финальный статус (is_positive_end=True)"
)
# Проверяем что есть 'completed' (основной позитивный статус)
completed_status = OrderStatus.objects.filter(code='completed').first()
self.assertIsNotNone(
completed_status,
"Статус 'completed' не создан (основной позитивный статус)"
)
self.assertTrue(
completed_status.is_system,
"'completed' должен быть системным"
)
self.assertTrue(
completed_status.is_positive_end,
"'completed' должен быть позитивным финальным статусом"
)
# КРИТИЧЕСКАЯ ПРОВЕРКА #2: Негативный финальный статус
negative_statuses = OrderStatus.objects.filter(is_negative_end=True)
self.assertGreaterEqual(
negative_statuses.count(),
1,
"Должен быть минимум ОДИН негативный финальный статус (is_negative_end=True)"
)
# Проверяем что есть 'cancelled' или другой негативный статус
# Проверяем первый найденный негативный статус
first_negative_status = negative_statuses.first()
self.assertTrue(
first_negative_status.is_system,
f"Негативный статус '{first_negative_status.code}' должен быть системным"
)
self.assertTrue(
first_negative_status.is_negative_end,
f"Статус '{first_negative_status.code}' должен быть негативным финальным"
)
def test_new_tenant_gets_system_customer(self):
"""
Тест: Новый тенант получает системного клиента для анонимных продаж.
"""
# Создаём и активируем тенант
registration = TenantRegistration.objects.create(
shop_name='Тестовый магазин клиенты',
schema_name='test_shop_customers',
owner_email='owner3@test.com',
owner_name='Тест Владелец 3',
phone='+375291111113',
status=TenantRegistration.STATUS_PENDING
)
from tenants.services import TenantOnboardingService
tenant = TenantOnboardingService.activate_registration(registration, self.admin_user)
# Проверяем системного клиента
with schema_context('test_shop_customers'):
system_customer = Customer.objects.filter(is_system_customer=True).first()
self.assertIsNotNone(
system_customer,
"Системный клиент не создан"
)
self.assertTrue(system_customer.is_system_customer)
self.assertEqual(system_customer.name, 'Анонимный клиент')
def test_new_tenant_gets_default_warehouse(self):
"""
Тест: Новый тенант получает склад по умолчанию.
"""
# Создаём и активируем тенант
registration = TenantRegistration.objects.create(
shop_name='Тестовый магазин склад',
schema_name='test_shop_warehouse',
owner_email='owner_warehouse@test.com',
owner_name='Тест Владелец Склад',
phone='+375291111118',
status=TenantRegistration.STATUS_PENDING
)
from tenants.services import TenantOnboardingService
tenant = TenantOnboardingService.activate_registration(registration, self.admin_user)
# Проверяем склад по умолчанию
with schema_context('test_shop_warehouse'):
default_warehouse = Warehouse.objects.filter(is_default=True).first()
self.assertIsNotNone(
default_warehouse,
"Склад по умолчанию не создан"
)
self.assertTrue(default_warehouse.is_default)
self.assertTrue(default_warehouse.is_active)
self.assertTrue(default_warehouse.is_pickup_point)
self.assertEqual(default_warehouse.name, 'Основной склад')
def test_new_tenant_gets_default_showcase(self):
"""
Тест: Новый тенант получает витрину по умолчанию.
"""
# Создаём и активируем тенант
registration = TenantRegistration.objects.create(
shop_name='Тестовый магазин витрина',
schema_name='test_shop_showcase',
owner_email='owner_showcase@test.com',
owner_name='Тест Владелец Витрина',
phone='+375291111119',
status=TenantRegistration.STATUS_PENDING
)
from tenants.services import TenantOnboardingService
tenant = TenantOnboardingService.activate_registration(registration, self.admin_user)
# Проверяем витрину по умолчанию
with schema_context('test_shop_showcase'):
default_showcase = Showcase.objects.filter(is_default=True).first()
self.assertIsNotNone(
default_showcase,
"Витрина по умолчанию не создана"
)
self.assertTrue(default_showcase.is_default)
self.assertTrue(default_showcase.is_active)
self.assertEqual(default_showcase.name, 'Основная витрина')
# Проверяем что витрина привязана к складу по умолчанию
self.assertIsNotNone(default_showcase.warehouse)
self.assertTrue(default_showcase.warehouse.is_default)
def test_new_tenant_gets_superuser(self):
"""
Тест: Новый тенант получает суперпользователя для доступа к админке.
"""
# Создаём и активируем тенант
registration = TenantRegistration.objects.create(
shop_name='Тестовый магазин админ',
schema_name='test_shop_admin',
owner_email='owner4@test.com',
owner_name='Тест Владелец 4',
phone='+375291111114',
status=TenantRegistration.STATUS_PENDING
)
from tenants.services import TenantOnboardingService
tenant = TenantOnboardingService.activate_registration(registration, self.admin_user)
# Проверяем суперпользователя
with schema_context('test_shop_admin'):
superuser = User.objects.filter(
email=settings.TENANT_ADMIN_EMAIL
).first()
self.assertIsNotNone(
superuser,
f"Суперпользователь с email {settings.TENANT_ADMIN_EMAIL} не создан"
)
self.assertTrue(superuser.is_superuser)
self.assertTrue(superuser.is_staff)
def test_new_tenant_gets_domain(self):
"""
Тест: Новый тенант получает домен для доступа.
"""
# Создаём и активируем тенант
registration = TenantRegistration.objects.create(
shop_name='Тестовый магазин домен',
schema_name='test_shop_domain',
owner_email='owner5@test.com',
owner_name='Тест Владелец 5',
phone='+375291111115',
status=TenantRegistration.STATUS_PENDING
)
from tenants.services import TenantOnboardingService
tenant = TenantOnboardingService.activate_registration(registration, self.admin_user)
# Проверяем домен
domain = Domain.objects.filter(tenant=tenant).first()
self.assertIsNotNone(domain, "Домен не создан")
self.assertEqual(
domain.domain,
'test_shop_domain.localhost',
f"Ожидался домен 'test_shop_domain.localhost', получен '{domain.domain}'"
)
self.assertTrue(domain.is_primary)
def test_registration_status_changes_to_approved(self):
"""
Тест: После активации статус заявки меняется на APPROVED.
"""
# Создаём заявку
registration = TenantRegistration.objects.create(
shop_name='Тестовый магазин статус',
schema_name='test_shop_status',
owner_email='owner6@test.com',
owner_name='Тест Владелец 6',
phone='+375291111116',
status=TenantRegistration.STATUS_PENDING
)
# Проверяем начальный статус
self.assertEqual(registration.status, TenantRegistration.STATUS_PENDING)
# Активируем
from tenants.services import TenantOnboardingService
tenant = TenantOnboardingService.activate_registration(registration, self.admin_user)
# Обновляем объект из БД
registration.refresh_from_db()
# Проверяем изменённый статус
self.assertEqual(registration.status, TenantRegistration.STATUS_APPROVED)
self.assertIsNotNone(registration.processed_at)
self.assertEqual(registration.processed_by, self.admin_user)
self.assertEqual(registration.tenant, tenant)
def test_complete_tenant_onboarding(self):
"""
КОМПЛЕКСНЫЙ ТЕСТ: Проверяем весь процесс онбординга тенанта.
Это E2E тест, который проверяет что при создании нового тенанта
создаются ВСЕ необходимые сущности.
"""
# Создаём и активируем тенант
registration = TenantRegistration.objects.create(
shop_name='Полный тестовый магазин',
schema_name='test_shop_complete',
owner_email='owner_complete@test.com',
owner_name='Тест Владелец Полный',
phone='+375291111117',
status=TenantRegistration.STATUS_PENDING
)
from tenants.services import TenantOnboardingService
tenant = TenantOnboardingService.activate_registration(registration, self.admin_user)
# Проверяем тенант
self.assertIsNotNone(tenant)
self.assertTrue(tenant.is_active)
# Проверяем домен
domain = Domain.objects.filter(tenant=tenant).first()
self.assertIsNotNone(domain)
# Переключаемся на схему тенанта для проверки
with schema_context('test_shop_complete'):
# 1. Способы оплаты
payment_methods_count = PaymentMethod.objects.count()
self.assertEqual(payment_methods_count, 5, "Должно быть 5 способов оплаты")
# 2. Статусы заказов
order_statuses_count = OrderStatus.objects.count()
self.assertGreaterEqual(order_statuses_count, 3, "Должно быть минимум 3 статуса")
# 3. Системный клиент
system_customer = Customer.objects.filter(is_system_customer=True).first()
self.assertIsNotNone(system_customer, "Должен быть системный клиент")
# 4. Суперпользователь
superuser = User.objects.filter(email=settings.TENANT_ADMIN_EMAIL).first()
self.assertIsNotNone(superuser, "Должен быть суперпользователь")
# 5. Склад по умолчанию
default_warehouse = Warehouse.objects.filter(is_default=True).first()
self.assertIsNotNone(default_warehouse, "Должен быть склад по умолчанию")
self.assertTrue(default_warehouse.is_active)
# 6. Витрина по умолчанию
default_showcase = Showcase.objects.filter(is_default=True).first()
self.assertIsNotNone(default_showcase, "Должна быть витрина по умолчанию")
self.assertTrue(default_showcase.is_active)
self.assertEqual(default_showcase.warehouse, default_warehouse)
# Проверяем заявку
registration.refresh_from_db()
self.assertEqual(registration.status, TenantRegistration.STATUS_APPROVED)
print("\n" + "=" * 70)
print("✓ ПОЛНЫЙ ОНБОРДИНГ ТЕНАНТА ПРОШЁЛ УСПЕШНО")
print("=" * 70)
print(f"Тенант: {tenant.name}")
print(f"Схема: {tenant.schema_name}")
print(f"Домен: {domain.domain}")
print(f"Способов оплаты: {payment_methods_count}")
print(f"Статусов заказов: {order_statuses_count}")
print(f"Системный клиент: ✓")
print(f"Суперпользователь: ✓")
print(f"Склад по умолчанию: ✓")
print(f"Витрина по умолчанию: ✓")
print("=" * 70)