import { PrismaClient } from '@prisma/client'; import { execSync } from 'child_process'; import * as path from 'path'; // Database URL for testing - using file-based SQLite const TEST_DATABASE_URL = 'file:./test.db'; // Prisma client instance for tests let prisma: PrismaClient | null = null; /** * Setup test database with in-memory SQLite */ export async function setupTestDb(): Promise { // Set environment variable for test database BEFORE importing config process.env.DATABASE_URL = TEST_DATABASE_URL; process.env.NODE_ENV = 'test'; process.env.JWT_SECRET = 'test-jwt-secret-key-for-testing-only'; process.env.JWT_REFRESH_SECRET = 'test-jwt-refresh-secret-key-for-testing-only'; process.env.JWT_EXPIRES_IN = '1h'; process.env.JWT_REFRESH_EXPIRES_IN = '7d'; process.env.SMTP_HOST = 'smtp.test.com'; process.env.SMTP_USER = 'test@test.com'; process.env.SMTP_PASS = 'testpass'; // Create new Prisma client prisma = new PrismaClient({ datasources: { db: { url: TEST_DATABASE_URL, }, }, }); // Connect and run migrations await prisma.$connect(); // Use Prisma migrate deploy to create tables try { // Generate Prisma client first execSync('npx prisma generate', { cwd: path.join(__dirname, '../..'), env: { ...process.env, DATABASE_URL: TEST_DATABASE_URL }, stdio: 'pipe' }); // Run migrations execSync('npx prisma migrate deploy', { cwd: path.join(__dirname, '../..'), env: { ...process.env, DATABASE_URL: TEST_DATABASE_URL }, stdio: 'pipe' }); } catch (error) { // If migrate deploy fails, try with db push try { execSync('npx prisma db push --accept-data-loss', { cwd: path.join(__dirname, '../..'), env: { ...process.env, DATABASE_URL: TEST_DATABASE_URL }, stdio: 'pipe' }); } catch (pushError) { console.warn('⚠️ Could not run migrations, will try raw SQL approach'); } } return prisma; } /** * Teardown test database */ export async function teardownTestDb(): Promise { if (prisma) { // Delete all data from all tables try { await resetDatabase(); } catch (error) { // Ignore errors during cleanup } await prisma.$disconnect(); prisma = null; } } /** * Reset database - delete all data from tables */ export async function resetDatabase(): Promise { if (!prisma) { prisma = getPrismaClient(); } // Delete in reverse order of dependencies const tables = [ 'bonus_usages', 'user_bonuses', 'bonus_packs', 'payments', 'user_subscriptions', 'subscription_plans', 'notifications', 'user_activities', 'check_ins', 'equipment_rentals', 'orders', 'order_items', 'coach_reviews', 'student_enrollments', 'coaches', 'class_bookings', 'classes', 'league_standings', 'league_matches', 'league_team_members', 'league_teams', 'leagues', 'tournament_matches', 'tournament_participants', 'tournaments', 'user_stats', 'match_results', 'recurring_bookings', 'bookings', 'court_schedules', 'courts', 'group_members', 'groups', 'friends', 'level_history', 'users', ]; for (const table of tables) { try { // @ts-ignore - dynamic table access await prisma.$executeRawUnsafe(`DELETE FROM ${table};`); } catch (error) { // Table might not exist, ignore } } } /** * Get Prisma client instance */ export function getPrismaClient(): PrismaClient { if (!prisma) { prisma = new PrismaClient({ datasources: { db: { url: TEST_DATABASE_URL, }, }, }); } return prisma; } /** * Execute query in transaction */ export async function executeInTransaction(callback: (tx: any) => Promise): Promise { const client = getPrismaClient(); return client.$transaction(callback); }