/** * Performance Tests * Validates that migrations complete within acceptable time limits * Tests scalability with various data sizes */ import { PrismaClient } from '@prisma/client' import { setupTestEnvironment, createTestPrismaClient, initializeTestDatabase, cleanupTestDatabase, createSampleNotes, measureExecutionTime, getDatabaseSize } from './setup' describe('Performance Tests', () => { let prisma: PrismaClient beforeAll(async () => { await setupTestEnvironment() prisma = createTestPrismaClient() await initializeTestDatabase(prisma) }) afterAll(async () => { await cleanupTestDatabase(prisma) }) describe('Empty Migration Performance', () => { test('should complete empty database migration quickly', async () => { // Clean up any existing data await prisma.note.deleteMany({}) // Measure time to "migrate" empty database const { result, duration } = await measureExecutionTime(async () => { const noteCount = await prisma.note.count() return { count: noteCount } }) // Empty migration should complete instantly (< 1 second) expect(duration).toBeLessThan(1000) expect(result.count).toBe(0) }) }) describe('Small Dataset Performance (10 notes)', () => { beforeEach(async () => { await prisma.note.deleteMany({}) }) test('should complete migration for 10 notes within 1 second', async () => { // Create 10 notes const { result: notes, duration: createDuration } = await measureExecutionTime(async () => { return await createSampleNotes(prisma, 10) }) expect(notes.length).toBe(10) expect(createDuration).toBeLessThan(1000) // Measure query performance const { result, duration: queryDuration } = await measureExecutionTime(async () => { return await prisma.note.findMany() }) expect(result.length).toBe(10) expect(queryDuration).toBeLessThan(500) }) test('should complete create operation for 10 notes within 1 second', async () => { const { result, duration } = await measureExecutionTime(async () => { const notes = [] for (let i = 0; i < 10; i++) { const note = await prisma.note.create({ data: { title: `Perf Test Note ${i}`, content: `Test content ${i}`, userId: 'test-user-id', order: i } }) notes.push(note) } return notes }) expect(result.length).toBe(10) expect(duration).toBeLessThan(1000) }) test('should complete update operation for 10 notes within 1 second', async () => { // Create notes first await createSampleNotes(prisma, 10) // Measure update performance const { result, duration } = await measureExecutionTime(async () => { return await prisma.note.updateMany({ data: { isPinned: true }, where: { title: { contains: 'Test Note' } } }) }) expect(duration).toBeLessThan(1000) }) }) describe('Medium Dataset Performance (100 notes)', () => { beforeEach(async () => { await prisma.note.deleteMany({}) }) test('should complete migration for 100 notes within 5 seconds', async () => { // Create 100 notes const { result: notes, duration: createDuration } = await measureExecutionTime(async () => { return await createSampleNotes(prisma, 100) }) expect(notes.length).toBe(100) expect(createDuration).toBeLessThan(5000) // Measure query performance const { result, duration: queryDuration } = await measureExecutionTime(async () => { return await prisma.note.findMany() }) expect(result.length).toBe(100) expect(queryDuration).toBeLessThan(1000) }) test('should complete create operation for 100 notes within 5 seconds', async () => { const { result, duration } = await measureExecutionTime(async () => { const notes = [] for (let i = 0; i < 100; i++) { const note = await prisma.note.create({ data: { title: `Perf Test Note ${i}`, content: `Test content ${i}`, userId: 'test-user-id', order: i } }) notes.push(note) } return notes }) expect(result.length).toBe(100) expect(duration).toBeLessThan(5000) }) test('should complete batch insert for 100 notes within 2 seconds', async () => { const notesData = Array.from({ length: 100 }, (_, i) => ({ title: `Batch Note ${i}`, content: `Batch content ${i}`, userId: 'test-user-id', order: i })) const { result, duration } = await measureExecutionTime(async () => { return await prisma.note.createMany({ data: notesData }) }) expect(result.count).toBe(100) expect(duration).toBeLessThan(2000) }) test('should complete filtered query for 100 notes within 500ms', async () => { await createSampleNotes(prisma, 100) const { result, duration } = await measureExecutionTime(async () => { return await prisma.note.findMany({ where: { isPinned: true } }) }) expect(duration).toBeLessThan(500) }) }) describe('Target Dataset Performance (1,000 notes)', () => { beforeEach(async () => { await prisma.note.deleteMany({}) }) test('should complete migration for 1,000 notes within 30 seconds', async () => { // Create 1,000 notes in batches for better performance const { result: notes, duration: createDuration } = await measureExecutionTime(async () => { const allNotes = [] const batchSize = 100 const totalNotes = 1000 for (let batch = 0; batch < totalNotes / batchSize; batch++) { const batchData = Array.from({ length: batchSize }, (_, i) => ({ title: `Perf Note ${batch * batchSize + i}`, content: `Test content ${batch * batchSize + i}`, userId: 'test-user-id', order: batch * batchSize + i })) await prisma.note.createMany({ data: batchData }) } return await prisma.note.findMany() }) expect(notes.length).toBe(1000) expect(createDuration).toBeLessThan(30000) }) test('should complete batch insert for 1,000 notes within 10 seconds', async () => { const notesData = Array.from({ length: 1000 }, (_, i) => ({ title: `Batch Note ${i}`, content: `Batch content ${i}`, userId: 'test-user-id', order: i })) const { result, duration } = await measureExecutionTime(async () => { return await prisma.note.createMany({ data: notesData }) }) expect(result.count).toBe(1000) expect(duration).toBeLessThan(10000) }) test('should complete query for 1,000 notes within 1 second', async () => { const notesData = Array.from({ length: 1000 }, (_, i) => ({ title: `Query Test Note ${i}`, content: `Query test content ${i}`, userId: 'test-user-id', order: i })) await prisma.note.createMany({ data: notesData }) const { result, duration } = await measureExecutionTime(async () => { return await prisma.note.findMany() }) expect(result.length).toBe(1000) expect(duration).toBeLessThan(1000) }) test('should complete filtered query for 1,000 notes within 1 second', async () => { // Create notes with various pinned states const notesData = Array.from({ length: 1000 }, (_, i) => ({ title: `Filter Test Note ${i}`, content: `Filter test content ${i}`, userId: 'test-user-id', order: i, isPinned: i % 3 === 0 // Every 3rd note is pinned })) await prisma.note.createMany({ data: notesData }) const { result, duration } = await measureExecutionTime(async () => { return await prisma.note.findMany({ where: { isPinned: true } }) }) expect(result.length).toBeGreaterThan(0) expect(duration).toBeLessThan(1000) }) test('should complete indexed query for 1,000 notes within 500ms', async () => { // Create notes const notesData = Array.from({ length: 1000 }, (_, i) => ({ title: `Index Test Note ${i}`, content: `Index test content ${i}`, userId: 'test-user-id', order: i, isPinned: i % 2 === 0 })) await prisma.note.createMany({ data: notesData }) // Query using indexed field (isPinned) const { result, duration } = await measureExecutionTime(async () => { return await prisma.note.findMany({ where: { isPinned: true }, orderBy: { order: 'asc' } }) }) expect(result.length).toBeGreaterThan(0) expect(duration).toBeLessThan(500) }) }) describe('Stress Test Performance (10,000 notes)', () => { beforeEach(async () => { await prisma.note.deleteMany({}) }) test('should complete batch insert for 10,000 notes within 30 seconds', async () => { const notesData = Array.from({ length: 10000 }, (_, i) => ({ title: `Stress Note ${i}`, content: `Stress test content ${i}`, userId: 'test-user-id', order: i })) const { result, duration } = await measureExecutionTime(async () => { return await prisma.note.createMany({ data: notesData }) }) expect(result.count).toBe(10000) expect(duration).toBeLessThan(30000) }) test('should complete query for 10,000 notes within 2 seconds', async () => { const notesData = Array.from({ length: 10000 }, (_, i) => ({ title: `Stress Query Note ${i}`, content: `Stress query content ${i}`, userId: 'test-user-id', order: i })) await prisma.note.createMany({ data: notesData }) const { result, duration } = await measureExecutionTime(async () => { return await prisma.note.findMany({ take: 100 // Limit to 100 for performance }) }) expect(result.length).toBe(100) expect(duration).toBeLessThan(2000) }) test('should handle pagination for 10,000 notes efficiently', async () => { const notesData = Array.from({ length: 10000 }, (_, i) => ({ title: `Pagination Note ${i}`, content: `Pagination content ${i}`, userId: 'test-user-id', order: i })) await prisma.note.createMany({ data: notesData }) const { result, duration } = await measureExecutionTime(async () => { return await prisma.note.findMany({ skip: 100, take: 50 }) }) expect(result.length).toBe(50) expect(duration).toBeLessThan(1000) }) }) describe('AI Features Performance', () => { beforeEach(async () => { await prisma.note.deleteMany({}) await prisma.aiFeedback.deleteMany({}) }) test('should create AI-enabled notes efficiently (100 notes)', async () => { const { result, duration } = await measureExecutionTime(async () => { const notes = [] for (let i = 0; i < 100; i++) { const note = await prisma.note.create({ data: { title: `AI Note ${i}`, content: `AI content ${i}`, userId: 'test-user-id', autoGenerated: i % 2 === 0, aiProvider: i % 3 === 0 ? 'openai' : 'ollama', aiConfidence: 70 + i, language: i % 2 === 0 ? 'en' : 'fr', languageConfidence: 0.85 + (i * 0.001), lastAiAnalysis: new Date(), order: i } }) notes.push(note) } return notes }) expect(result.length).toBe(100) expect(duration).toBeLessThan(5000) }) test('should query by AI fields efficiently (100 notes)', async () => { // Create AI notes const notes = [] for (let i = 0; i < 100; i++) { const note = await prisma.note.create({ data: { title: `AI Query Note ${i}`, content: `Content ${i}`, userId: 'test-user-id', autoGenerated: i % 2 === 0, aiProvider: i % 3 === 0 ? 'openai' : 'ollama', order: i } }) notes.push(note) } // Query by autoGenerated const { result: autoGenerated, duration: duration1 } = await measureExecutionTime(async () => { return await prisma.note.findMany({ where: { autoGenerated: true } }) }) expect(autoGenerated.length).toBeGreaterThan(0) expect(duration1).toBeLessThan(500) // Query by aiProvider const { result: openaiNotes, duration: duration2 } = await measureExecutionTime(async () => { return await prisma.note.findMany({ where: { aiProvider: 'openai' } }) }) expect(openaiNotes.length).toBeGreaterThan(0) expect(duration2).toBeLessThan(500) }) test('should create AI feedback efficiently (100 feedback entries)', async () => { // Create a note const note = await prisma.note.create({ data: { title: 'Feedback Performance Note', content: 'Test content', userId: 'test-user-id' } }) const { result, duration } = await measureExecutionTime(async () => { const feedbacks = [] for (let i = 0; i < 100; i++) { const feedback = await prisma.aiFeedback.create({ data: { noteId: note.id, userId: 'test-user-id', feedbackType: i % 3 === 0 ? 'thumbs_up' : 'thumbs_down', feature: 'title_suggestion', originalContent: `Feedback ${i}`, metadata: JSON.stringify({ aiProvider: 'openai', confidence: 70 + i, timestamp: new Date().toISOString() }) } }) feedbacks.push(feedback) } return feedbacks }) expect(result.length).toBe(100) expect(duration).toBeLessThan(5000) }) test('should query feedback by note efficiently (100 feedback entries)', async () => { const note = await prisma.note.create({ data: { title: 'Feedback Query Note', content: 'Test content', userId: 'test-user-id' } }) // Create feedback for (let i = 0; i < 100; i++) { await prisma.aiFeedback.create({ data: { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_up', feature: 'title_suggestion', originalContent: `Feedback ${i}` } }) } // Query by noteId (should use index) const { result, duration } = await measureExecutionTime(async () => { return await prisma.aiFeedback.findMany({ where: { noteId: note.id }, orderBy: { createdAt: 'asc' } }) }) expect(result.length).toBe(100) expect(duration).toBeLessThan(500) }) }) describe('Database Size Performance', () => { test('should track database size growth', async () => { // Get initial size const initialSize = await getDatabaseSize(prisma) // Add 100 notes const notesData = Array.from({ length: 100 }, (_, i) => ({ title: `Size Test Note ${i}`, content: `Size test content ${i}`.repeat(10), // Larger content userId: 'test-user-id', order: i })) await prisma.note.createMany({ data: notesData }) // Get size after adding notes const sizeAfter = await getDatabaseSize(prisma) // Database should have grown expect(sizeAfter).toBeGreaterThan(initialSize) }) test('should handle large content efficiently', async () => { const largeContent = 'A'.repeat(10000) // 10KB per note const { result, duration } = await measureExecutionTime(async () => { return await prisma.note.create({ data: { title: 'Large Content Note', content: largeContent, userId: 'test-user-id' } }) }) expect(result.content).toHaveLength(10000) expect(duration).toBeLessThan(1000) }) }) describe('Concurrent Operations Performance', () => { test('should handle multiple concurrent reads', async () => { // Create test data await createSampleNotes(prisma, 100) // Measure concurrent read performance const { duration } = await measureExecutionTime(async () => { const promises = [ prisma.note.findMany({ take: 10 }), prisma.note.findMany({ take: 10, skip: 10 }), prisma.note.findMany({ take: 10, skip: 20 }), prisma.note.findMany({ take: 10, skip: 30 }), prisma.note.findMany({ take: 10, skip: 40 }) ] await Promise.all(promises) }) // All concurrent reads should complete quickly expect(duration).toBeLessThan(2000) }) }) })