ADR-0008: Isolated Database Testing Architecture¶
Статус¶
Accepted
Контекст¶
При росте количества интеграционных тестов возникли проблемы с legacy cleanup подходом: - Тесты влияют друг на друга через shared database state - Race conditions между параллельными тестами - Сложная логика cleanup с риском удаления не-тестовых данных - Высокая стоимость поддержки cleanup функций - Медленная очистка БД между тестами
Для надежного тестирования финансовой системы критически важна полная изоляция тестовых данных.
Решение¶
Внедрение PostgreSQL TEMPLATE системы для полной изоляции каждого теста в отдельной базе данных.
Isolated Database Architecture:
- PostgreSQL TEMPLATE DATABASE - базовая пустая БД со схемой
- Изолированные тестовые БД - каждый тест получает свою копию
- Автоматическая очистка - DROP DATABASE при завершении теста
- Zero cleanup logic - никаких ручных cleanup функций
Техническая архитектура:
saga_empty_template (master template)
↓ CREATE DATABASE test_saga_123 TEMPLATE saga_empty_template
test_saga_123 (isolated test DB)
↓ Тест завершен
DROP DATABASE test_saga_123 (automatic cleanup)
Ключевые компоненты:
backend/tests/isolated_db_helper.go (1133+ строк)
├── CreateIsolatedTestDB(t) # Основная функция для тестов
├── IsolatedTestDB struct # 50+ helper методов
├── Automatic cleanup logic # t.Cleanup() integration
└── Template management # Setup saga_empty_template
Архитектурные принципы:
- Каждый тест = отдельная база данных
- Полная изоляция данных между тестами
- Automatic cleanup без ручного кода
- PostgreSQL TEMPLATE обеспечивает скорость создания
Альтернативы¶
Legacy Cleanup Functions¶
Заменена - сложная логика, race conditions, риск удаления production данных, медленная работа.
Transaction Rollback Pattern¶
Отклонена - не работает с commit/rollback логикой в приложении, сложность nested transactions.
Docker Test Containers¶
Отклонена - медленный startup контейнеров, избыточная сложность для локальной разработки.
In-Memory Database (SQLite)¶
Отклонена - не соответствует PostgreSQL особенностям (JSON, индексы, constraint).
Последствия¶
Положительные¶
- Полная изоляция - тесты не влияют друг на друга
- Параллельное выполнение - без race conditions
- Безопасность - невозможно удалить production данные
- Простота - нет сложной cleanup логики
- Скорость - CREATE DATABASE быстрее чем DELETE операции
- Надежность - guaranteed cleanup через PostgreSQL DROP
Отрицательные¶
- Немного больше overhead на создание БД
- Требует правильной настройки PostgreSQL template
- Необходимость миграции существующих тестов
Нейтральные¶
- Изменение привычного подхода к написанию тестов
- Необходимость обучения команды новому подходу
Implementation Details¶
Использование в тестах:
func TestUserOperations(t *testing.T) {
// Создание изолированной БД (автоматически сгенерированное имя)
isolatedDB := tests.CreateIsolatedTestDB(t)
// Автоматическая очистка через t.Cleanup()
// Создание тестового пользователя
userID := isolatedDB.CreateTestUser(t, "test@saga-test.com")
// Создание депозита
amount, _ := models.NewSafeDecimalFromString("100.00")
deposit, err := isolatedDB.CreateTestDeposit(ctx, userID, amount)
require.NoError(t, err)
// Проверка баланса
balance := isolatedDB.GetUserBalance(t, userID, "USDC")
assert.Equal(t, amount, balance)
// НИКАКИХ cleanup операций не требуется!
// PostgreSQL автоматически удалит БД через t.Cleanup()
}
Helper методы IsolatedTestDB:
// User management
func (db *IsolatedTestDB) CreateTestUser(t, email) string
func (db *IsolatedTestDB) CountUsers(t) int
func (db *IsolatedTestDB) GetUserByEmail(t, email) *models.User
// Financial operations
func (db *IsolatedTestDB) CreateTestDeposit(ctx, userID, amount) (*models.Transaction, error)
func (db *IsolatedTestDB) CreateTestWithdrawal(ctx, userID, amount) (*models.Transaction, error)
func (db *IsolatedTestDB) GetUserBalance(t, userID, currency) SafeDecimal
// Wallet management
func (db *IsolatedTestDB) CreateTestWallet(t, userID, address) string
func (db *IsolatedTestDB) GetUserWallets(t, userID) []*models.Wallet
// Investment operations
func (db *IsolatedTestDB) CreateTestInvestment(ctx, userID, strategy, amount) (*models.Investment, error)
func (db *IsolatedTestDB) CountInvestments(t) int
// Automatic cleanup through t.Cleanup() - no manual cleanup needed!
Template Database Setup:
-- Template БД создается один раз при первом использовании
CREATE DATABASE saga_empty_template;
-- Применяются все миграции для полной схемы
-- Template остается пустой но со всей структурой
Migration Strategy¶
Текущий статус (Сентябрь 2025):
- ✅ Архитектура реализована -
isolated_db_helper.go(1133+ строк) - ✅ 100+ тестов мигрированы - используют
CreateIsolatedTestDB() - ⚠️ Legacy тесты остались - 8+ интеграционных тестов все еще используют cleanup функции
- ⚠️ Legacy файлы сохранены - до завершения миграции всех тестов
План завершения:
- Мигрировать оставшиеся интеграционные тесты на isolated подход
- Удалить legacy cleanup файлы:
cleanup_test_user_fixed.go,immutable_transaction_cleanup.go,test_cleanup.go - Обновить CI/CD пайплайн для использования только isolated тестов
Файлы для миграции:
backend/tests/integration/no_gas_system_test.gobackend/tests/integration/cleanup_safety_test.gobackend/tests/integration/investment_details_api_integration_test.gobackend/tests/integration/auth_comprehensive_test.gobackend/tests/integration/real_user_deposits_verification_test.gobackend/shared/services/balance_service_test.gobackend/integration_tests/database_interface_test.go
Связанные решения¶
- Связано с: ADR-0003 (No-Mock Testing Architecture) - для реального database testing
- Связано с: ADR-0007 (Database Schema Design) - для template БД структуры
- Связано с: ADR-0006 (Centralized Test Config) - для тестовых данных
Критические принципы¶
Принципы изолированного тестирования:
- Один тест = одна БД - каждый
CreateIsolatedTestDB()создает уникальную БД - Zero cleanup code - никаких ручных cleanup функций в тестах
- Automatic lifecycle - создание через TEMPLATE, удаление через t.Cleanup()
- Full isolation - тесты НИКОГДА не влияют друг на друга
- PostgreSQL native - используем TEMPLATE возможности БД
Критическое правило:
НИКОГДА не использовать legacy cleanup функции в новых тестах. Если тест создает данные в БД - использовать CreateIsolatedTestDB().
Performance characteristics:
- CREATE DATABASE from TEMPLATE: ~50-100ms
- Legacy cleanup DELETE operations: ~200-500ms
- Isolated approach: 2-5x faster + guaranteed isolation
Безопасность:
- Невозможно случайно удалить production данные
- Каждый тест работает в изолированной среде
- Automatic cleanup исключает забытые тестовые данные
- PostgreSQL ACID guarantees для всех операций
Примечания¶
Настройка PostgreSQL для isolated testing:
-- Убедиться что template1 доступна для создания БД
GRANT CREATE ON DATABASE template1 TO saga_user;
-- Или настроить отдельную template БД для тестов
Мониторинг использования:
- Template database должна оставаться пустой
- Isolated test databases создаются и удаляются автоматически
- Логирование создания/удаления изолированных БД для debugging
Best Practices:
- Всегда используй
CreateIsolatedTestDB(t)для новых тестов - НЕ создавай manual cleanup logic
- НЕ используй shared test database state
- Используй helper methods для common operations
- Testdata generation через isolated DB helpers
Архитектурное преимущество: Isolated Database Architecture устраняет класс проблем связанных с shared mutable state в тестах, обеспечивая 100% надежность и reproducibility тестирования финансовой системы.