/** * Test database setup and teardown utilities for migration tests * Updated for PostgreSQL */ import { PrismaClient } from '@prisma/client' /** * Create a Prisma client instance for testing * Uses DATABASE_URL from environment */ export function createTestPrismaClient(): PrismaClient { return new PrismaClient() } /** * Initialize test database schema * Runs prisma migrate deploy or db push */ export async function initializeTestDatabase(prisma: PrismaClient) { await prisma.$connect() } export async function setupTestEnvironment(): Promise { // no-op — environment is ready via Docker } /** * Cleanup test database * Disconnects Prisma client and cleans all data */ async function deleteManyIfTableExists( prisma: PrismaClient, tableName: string, deleteFn: () => Promise ) { if (await verifyTableExists(prisma, tableName)) { await deleteFn() } } export async function cleanupTestDatabase(prisma: PrismaClient) { try { await deleteManyIfTableExists(prisma, 'AiFeedback', () => prisma.aiFeedback.deleteMany()) await deleteManyIfTableExists(prisma, 'MemoryEchoInsight', () => prisma.memoryEchoInsight.deleteMany()) await deleteManyIfTableExists(prisma, 'NoteShare', () => prisma.noteShare.deleteMany()) await deleteManyIfTableExists(prisma, 'Note', () => prisma.note.deleteMany()) await deleteManyIfTableExists(prisma, 'Label', () => prisma.label.deleteMany()) await deleteManyIfTableExists(prisma, 'Notebook', () => prisma.notebook.deleteMany()) await deleteManyIfTableExists(prisma, 'UserAISettings', () => prisma.userAISettings.deleteMany()) await deleteManyIfTableExists(prisma, 'SystemConfig', () => prisma.systemConfig.deleteMany()) await deleteManyIfTableExists(prisma, 'Session', () => prisma.session.deleteMany()) await deleteManyIfTableExists(prisma, 'Account', () => prisma.account.deleteMany()) await deleteManyIfTableExists(prisma, 'VerificationToken', () => prisma.verificationToken.deleteMany()) await deleteManyIfTableExists(prisma, 'Subscription', () => prisma.subscription.deleteMany()) await deleteManyIfTableExists(prisma, 'User', () => prisma.user.deleteMany()) await prisma.$disconnect() } catch (error) { console.error('Error cleaning up test database:', error) } } /** * Ensure a test user exists with the given ID (upsert by id). * Needed because PostgreSQL enforces FK constraints on Note.userId / Notebook.userId. */ export async function ensureTestUser(prisma: PrismaClient, userId: string): Promise { await prisma.user.upsert({ where: { id: userId }, create: { id: userId, email: `${userId}@test-fixture.internal` }, update: {}, }) } /** * Create sample test data */ export async function createSampleNotes(prisma: PrismaClient, count: number = 10) { const notes = [] const userId = 'test-user-123' await ensureTestUser(prisma, userId) for (let i = 0; i < count; i++) { const note = await prisma.note.create({ data: { title: `Test Note ${i + 1}`, content: `This is test content for note ${i + 1}`, userId, color: `color-${i % 5}`, order: i, isPinned: i % 3 === 0, isArchived: false, type: 'text', size: i % 3 === 0 ? 'small' : 'medium' } }) notes.push(note) } return notes } /** * Create sample AI-enabled notes */ export async function createSampleAINotes(prisma: PrismaClient, count: number = 10) { const notes = [] const userId = 'test-user-ai' await ensureTestUser(prisma, userId) for (let i = 0; i < count; i++) { const note = await prisma.note.create({ data: { title: `AI Test Note ${i + 1}`, content: `This is AI test content for note ${i + 1}`, userId, color: 'default', order: i, autoGenerated: i % 2 === 0, aiProvider: i % 3 === 0 ? 'openai' : 'ollama', aiConfidence: 70 + i * 2, language: i % 2 === 0 ? 'en' : 'fr', languageConfidence: 0.85 + (i * 0.01), lastAiAnalysis: new Date() } }) notes.push(note) } return notes } /** * Measure execution time for a function */ export async function measureExecutionTime(fn: () => Promise): Promise<{ result: T; duration: number }> { const start = performance.now() const result = await fn() const end = performance.now() return { result, duration: end - start } } /** * Verify data integrity after migration */ export async function verifyDataIntegrity(prisma: PrismaClient, expectedNoteCount: number) { const noteCount = await prisma.note.count() if (noteCount !== expectedNoteCount) { throw new Error(`Data integrity check failed: Expected ${expectedNoteCount} notes, found ${noteCount}`) } return true } /** * Check if database table exists (PostgreSQL version) */ export async function verifyTableExists(prisma: PrismaClient, tableName: string): Promise { try { const result = await prisma.$queryRawUnsafe>( `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = $1 )`, tableName ) return result[0]?.exists ?? false } catch (error) { return false } } /** * Check if index exists on a table (PostgreSQL version) */ export async function verifyIndexExists(prisma: PrismaClient, tableName: string, indexName: string): Promise { try { const result = await prisma.$queryRawUnsafe>( `SELECT EXISTS ( SELECT FROM pg_indexes WHERE schemaname = 'public' AND tablename = $1 AND indexname = $2 )`, tableName, indexName ) return result[0]?.exists ?? false } catch (error) { return false } } /** * Check if column exists in table (PostgreSQL version) */ export async function verifyColumnExists(prisma: PrismaClient, tableName: string, columnName: string): Promise { try { const result = await prisma.$queryRawUnsafe>( `SELECT EXISTS ( SELECT FROM information_schema.columns WHERE table_schema = 'public' AND table_name = $1 AND column_name = $2 )`, tableName, columnName ) return result[0]?.exists ?? false } catch (error) { return false } } /** * Get table schema information (PostgreSQL version) */ /** Approximate DB size in bytes (PostgreSQL). */ export async function getDatabaseSize(prisma: PrismaClient): Promise { const rows = await prisma.$queryRaw>` SELECT pg_database_size(current_database()) AS size ` return Number(rows[0]?.size ?? 0) } export async function getTableSchema(prisma: PrismaClient, tableName: string) { try { const result = await prisma.$queryRawUnsafe>( `SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema = 'public' AND table_name = $1 ORDER BY ordinal_position`, tableName ) return result } catch (error) { return null } }