/** * Data Integrity Tests * Validates that data is preserved and not corrupted during migration * Tests data loss prevention, foreign key relationships, and indexes */ import { PrismaClient } from '@prisma/client' import { setupTestEnvironment, createTestPrismaClient, initializeTestDatabase, cleanupTestDatabase, createSampleNotes, verifyDataIntegrity } from './setup' describe('Data Integrity Tests', () => { let prisma: PrismaClient beforeAll(async () => { await setupTestEnvironment() prisma = createTestPrismaClient() await initializeTestDatabase(prisma) }) afterAll(async () => { await cleanupTestDatabase(prisma) }) describe('No Data Loss', () => { test('should preserve all notes after migration', async () => { // Create test notes const initialNotes = await createSampleNotes(prisma, 50) const initialCount = initialNotes.length // Query after migration const notesAfterMigration = await prisma.note.findMany() const finalCount = notesAfterMigration.length // Verify no data loss expect(finalCount).toBe(initialCount) }) test('should preserve note titles', async () => { const testTitles = [ 'Important Meeting Notes', 'Shopping List', 'Project Ideas', 'Recipe Collection', 'Book Reviews' ] // Create notes with specific titles for (const title of testTitles) { await prisma.note.create({ data: { title, content: `Content for ${title}`, userId: 'test-user-id' } }) } // Verify all titles are preserved const notes = await prisma.note.findMany({ where: { title: { in: testTitles } } }) const preservedTitles = notes.map(n => n.title) for (const title of testTitles) { expect(preservedTitles).toContain(title) } }) test('should preserve note content', async () => { const testContent = [ 'This is a simple text note', 'Note with **markdown** formatting', 'Note with [links](https://example.com)', 'Note with numbers: 1, 2, 3, 4, 5', 'Note with special characters: émojis 🎉 & çharacters' ] // Create notes with specific content for (const content of testContent) { await prisma.note.create({ data: { title: `Content Test`, content, userId: 'test-user-id' } }) } // Verify all content is preserved const notes = await prisma.note.findMany({ where: { title: 'Content Test' } }) const preservedContent = notes.map(n => n.content) for (const content of testContent) { expect(preservedContent).toContain(content) } }) test('should preserve note metadata', async () => { // Create note with all metadata const note = await prisma.note.create({ data: { title: 'Metadata Test Note', content: 'Test content', color: 'red', isPinned: true, isArchived: false, type: 'text', size: 'large', userId: 'test-user-id', order: 5 } }) // Verify metadata is preserved const retrieved = await prisma.note.findUnique({ where: { id: note.id } }) expect(retrieved?.color).toBe('red') expect(retrieved?.isPinned).toBe(true) expect(retrieved?.isArchived).toBe(false) expect(retrieved?.type).toBe('text') expect(retrieved?.size).toBe('large') expect(retrieved?.order).toBe(5) }) }) describe('No Data Corruption', () => { test('should preserve checkItems JSON structure', async () => { const checkItems = JSON.stringify([ { text: 'Buy groceries', done: false }, { text: 'Call dentist', done: true }, { text: 'Finish report', done: false }, { text: 'Schedule meeting', done: false } ]) const note = await prisma.note.create({ data: { title: 'Checklist Test Note', content: 'My checklist', checkItems, userId: 'test-user-id' } }) // Verify checkItems are preserved and can be parsed const retrieved = await prisma.note.findUnique({ where: { id: note.id } }) expect(retrieved?.checkItems).toBeDefined() const parsedCheckItems = JSON.parse(retrieved?.checkItems || '[]') expect(parsedCheckItems.length).toBe(4) expect(parsedCheckItems[0].text).toBe('Buy groceries') expect(parsedCheckItems[0].done).toBe(false) expect(parsedCheckItems[1].done).toBe(true) }) test('should preserve images JSON structure', async () => { const images = JSON.stringify([ { url: 'image1.jpg', caption: 'First image' }, { url: 'image2.jpg', caption: 'Second image' }, { url: 'image3.jpg', caption: 'Third image' } ]) const note = await prisma.note.create({ data: { title: 'Images Test Note', content: 'Note with images', images, userId: 'test-user-id' } }) // Verify images are preserved and can be parsed const retrieved = await prisma.note.findUnique({ where: { id: note.id } }) expect(retrieved?.images).toBeDefined() const parsedImages = JSON.parse(retrieved?.images || '[]') expect(parsedImages.length).toBe(3) expect(parsedImages[0].url).toBe('image1.jpg') expect(parsedImages[0].caption).toBe('First image') }) test('should preserve labels JSON structure', async () => { const labels = JSON.stringify(['work', 'important', 'project']) const note = await prisma.note.create({ data: { title: 'Labels Test Note', content: 'Note with labels', labels, userId: 'test-user-id' } }) // Verify labels are preserved and can be parsed const retrieved = await prisma.note.findUnique({ where: { id: note.id } }) expect(retrieved?.labels).toBeDefined() const parsedLabels = JSON.parse(retrieved?.labels || '[]') expect(parsedLabels.length).toBe(3) expect(parsedLabels).toContain('work') expect(parsedLabels).toContain('important') expect(parsedLabels).toContain('project') }) test('should preserve embedding JSON structure', async () => { const embedding = JSON.stringify({ vector: [0.1, 0.2, 0.3, 0.4, 0.5], model: 'text-embedding-ada-002', timestamp: new Date().toISOString() }) const note = await prisma.note.create({ data: { title: 'Embedding Test Note', content: 'Note with embedding', embedding, userId: 'test-user-id' } }) // Verify embedding is preserved and can be parsed const retrieved = await prisma.note.findUnique({ where: { id: note.id } }) expect(retrieved?.embedding).toBeDefined() const parsedEmbedding = JSON.parse(retrieved?.embedding || '{}') expect(parsedEmbedding.vector).toEqual([0.1, 0.2, 0.3, 0.4, 0.5]) expect(parsedEmbedding.model).toBe('text-embedding-ada-002') }) test('should preserve links JSON structure', async () => { const links = JSON.stringify([ { url: 'https://example.com', title: 'Example' }, { url: 'https://docs.example.com', title: 'Documentation' } ]) const note = await prisma.note.create({ data: { title: 'Links Test Note', content: 'Note with links', links, userId: 'test-user-id' } }) // Verify links are preserved and can be parsed const retrieved = await prisma.note.findUnique({ where: { id: note.id } }) expect(retrieved?.links).toBeDefined() const parsedLinks = JSON.parse(retrieved?.links || '[]') expect(parsedLinks.length).toBe(2) expect(parsedLinks[0].url).toBe('https://example.com') expect(parsedLinks[0].title).toBe('Example') }) }) describe('Foreign Key Relationships', () => { test('should maintain Note to User relationship', async () => { const user = await prisma.user.create({ data: { email: 'fk-integrity@test.com', name: 'FK Integrity User' } }) const note = await prisma.note.create({ data: { title: 'User Integrity Note', content: 'Test content', userId: user.id } }) // Verify relationship is maintained expect(note.userId).toBe(user.id) // Query user's notes const userNotes = await prisma.note.findMany({ where: { userId: user.id } }) expect(userNotes.length).toBeGreaterThan(0) expect(userNotes.some(n => n.id === note.id)).toBe(true) }) test('should maintain Note to Notebook relationship', async () => { const notebook = await prisma.notebook.create({ data: { name: 'Integrity Notebook', order: 0, userId: 'test-user-id' } }) const note = await prisma.note.create({ data: { title: 'Notebook Integrity Note', content: 'Test content', userId: 'test-user-id', notebookId: notebook.id } }) // Verify relationship is maintained expect(note.notebookId).toBe(notebook.id) // Query notebook's notes const notebookNotes = await prisma.note.findMany({ where: { notebookId: notebook.id } }) expect(notebookNotes.length).toBeGreaterThan(0) expect(notebookNotes.some(n => n.id === note.id)).toBe(true) }) test('should maintain AiFeedback to Note relationship', async () => { const note = await prisma.note.create({ data: { title: 'Feedback Integrity Note', content: 'Test content', userId: 'test-user-id' } }) const feedback = await prisma.aiFeedback.create({ data: { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_up', feature: 'title_suggestion', originalContent: 'Test feedback' } }) // Verify relationship is maintained expect(feedback.noteId).toBe(note.id) // Query note's feedback const noteFeedback = await prisma.aiFeedback.findMany({ where: { noteId: note.id } }) expect(noteFeedback.length).toBeGreaterThan(0) expect(noteFeedback.some(f => f.id === feedback.id)).toBe(true) }) test('should maintain AiFeedback to User relationship', async () => { const user = await prisma.user.create({ data: { email: 'feedback-user@test.com', name: 'Feedback User' } }) const note = await prisma.note.create({ data: { title: 'User Feedback Note', content: 'Test content', userId: user.id } }) const feedback = await prisma.aiFeedback.create({ data: { noteId: note.id, userId: user.id, feedbackType: 'thumbs_up', feature: 'title_suggestion', originalContent: 'Test feedback' } }) // Verify relationship is maintained expect(feedback.userId).toBe(user.id) // Query user's feedback const userFeedback = await prisma.aiFeedback.findMany({ where: { userId: user.id } }) expect(userFeedback.length).toBeGreaterThan(0) expect(userFeedback.some(f => f.id === feedback.id)).toBe(true) }) test('should maintain cascade delete correctly', async () => { // Create a note with feedback const note = await prisma.note.create({ data: { title: 'Cascade Delete Note', content: 'Test content', userId: 'test-user-id' } }) const feedback = await prisma.aiFeedback.create({ data: { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_up', feature: 'title_suggestion', originalContent: 'Test feedback' } }) // Verify feedback exists const feedbacksBefore = await prisma.aiFeedback.findMany({ where: { noteId: note.id } }) expect(feedbacksBefore.length).toBe(1) // Delete note await prisma.note.delete({ where: { id: note.id } }) // Verify feedback is cascade deleted const feedbacksAfter = await prisma.aiFeedback.findMany({ where: { noteId: note.id } }) expect(feedbacksAfter.length).toBe(0) }) }) describe('Index Integrity', () => { test('should maintain index on Note.isPinned', async () => { // Create notes with various pinned states await prisma.note.createMany({ data: [ { title: 'Pinned 1', content: 'Content 1', userId: 'test-user-id', isPinned: true }, { title: 'Not Pinned 1', content: 'Content 2', userId: 'test-user-id', isPinned: false }, { title: 'Pinned 2', content: 'Content 3', userId: 'test-user-id', isPinned: true }, { title: 'Not Pinned 2', content: 'Content 4', userId: 'test-user-id', isPinned: false } ] }) // Query by isPinned (should use index) const pinnedNotes = await prisma.note.findMany({ where: { isPinned: true } }) expect(pinnedNotes.length).toBe(2) expect(pinnedNotes.every(n => n.isPinned === true)).toBe(true) }) test('should maintain index on Note.order', async () => { // Create notes with specific order await prisma.note.createMany({ data: [ { title: 'Order 0', content: 'Content 0', userId: 'test-user-id', order: 0 }, { title: 'Order 1', content: 'Content 1', userId: 'test-user-id', order: 1 }, { title: 'Order 2', content: 'Content 2', userId: 'test-user-id', order: 2 }, { title: 'Order 3', content: 'Content 3', userId: 'test-user-id', order: 3 } ] }) // Query ordered by order (should use index) const orderedNotes = await prisma.note.findMany({ orderBy: { order: 'asc' } }) expect(orderedNotes[0].order).toBe(0) expect(orderedNotes[1].order).toBe(1) expect(orderedNotes[2].order).toBe(2) expect(orderedNotes[3].order).toBe(3) }) test('should maintain index on AiFeedback.noteId', async () => { const note = await prisma.note.create({ data: { title: 'Index Feedback Note', content: 'Test content', userId: 'test-user-id' } }) // Create multiple feedback entries await prisma.aiFeedback.createMany({ data: [ { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_up', feature: 'title_suggestion', originalContent: 'Feedback 1' }, { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_down', feature: 'semantic_search', originalContent: 'Feedback 2' }, { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_up', feature: 'paragraph_refactor', originalContent: 'Feedback 3' } ] }) // Query by noteId (should use index) const feedbacks = await prisma.aiFeedback.findMany({ where: { noteId: note.id } }) expect(feedbacks.length).toBe(3) }) test('should maintain index on AiFeedback.userId', async () => { const note = await prisma.note.create({ data: { title: 'User Index Feedback Note', content: 'Test content', userId: 'test-user-id' } }) // Create multiple feedback entries for same user await prisma.aiFeedback.createMany({ data: [ { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_up', feature: 'title_suggestion', originalContent: 'Feedback 1' }, { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_down', feature: 'semantic_search', originalContent: 'Feedback 2' } ] }) // Query by userId (should use index) const userFeedbacks = await prisma.aiFeedback.findMany({ where: { userId: 'test-user-id' } }) expect(userFeedbacks.length).toBeGreaterThanOrEqual(2) }) test('should maintain index on AiFeedback.feature', async () => { const note1 = await prisma.note.create({ data: { title: 'Feature Index Note 1', content: 'Test content 1', userId: 'test-user-id' } }) const note2 = await prisma.note.create({ data: { title: 'Feature Index Note 2', content: 'Test content 2', userId: 'test-user-id' } }) // Create feedback with same feature await prisma.aiFeedback.createMany({ data: [ { noteId: note1.id, userId: 'test-user-id', feedbackType: 'thumbs_up', feature: 'title_suggestion', originalContent: 'Feedback 1' }, { noteId: note2.id, userId: 'test-user-id', feedbackType: 'thumbs_up', feature: 'title_suggestion', originalContent: 'Feedback 2' } ] }) // Query by feature (should use index) const titleFeedbacks = await prisma.aiFeedback.findMany({ where: { feature: 'title_suggestion' } }) expect(titleFeedbacks.length).toBeGreaterThanOrEqual(2) }) }) describe('AI Fields Integrity', () => { test('should preserve AI fields correctly', async () => { const note = await prisma.note.create({ data: { title: 'AI Fields Integrity Note', content: 'Test content', userId: 'test-user-id', autoGenerated: true, aiProvider: 'openai', aiConfidence: 95, language: 'en', languageConfidence: 0.98, lastAiAnalysis: new Date() } }) // Verify all AI fields are preserved const retrieved = await prisma.note.findUnique({ where: { id: note.id } }) expect(retrieved?.autoGenerated).toBe(true) expect(retrieved?.aiProvider).toBe('openai') expect(retrieved?.aiConfidence).toBe(95) expect(retrieved?.language).toBe('en') expect(retrieved?.languageConfidence).toBeCloseTo(0.98) expect(retrieved?.lastAiAnalysis).toBeDefined() }) test('should preserve null AI fields correctly', async () => { const note = await prisma.note.create({ data: { title: 'Null AI Fields Note', content: 'Test content', userId: 'test-user-id' // All AI fields are null by default } }) // Verify all AI fields are null const retrieved = await prisma.note.findUnique({ where: { id: note.id } }) expect(retrieved?.autoGenerated).toBeNull() expect(retrieved?.aiProvider).toBeNull() expect(retrieved?.aiConfidence).toBeNull() expect(retrieved?.language).toBeNull() expect(retrieved?.languageConfidence).toBeNull() expect(retrieved?.lastAiAnalysis).toBeNull() }) }) describe('Batch Operations Integrity', () => { test('should preserve data integrity during batch insert', async () => { const notesData = Array.from({ length: 50 }, (_, i) => ({ title: `Batch Integrity Note ${i}`, content: `Content ${i}`, userId: 'test-user-id', order: i, isPinned: i % 2 === 0 })) const result = await prisma.note.createMany({ data: notesData }) expect(result.count).toBe(50) // Verify all notes are created correctly const notes = await prisma.note.findMany({ where: { title: { contains: 'Batch Integrity Note' } } }) expect(notes.length).toBe(50) // Verify data integrity for (const note of notes) { expect(note.content).toBeDefined() expect(note.userId).toBe('test-user-id') } }) }) describe('Data Type Integrity', () => { test('should preserve boolean values correctly', async () => { const note = await prisma.note.create({ data: { title: 'Boolean Test Note', content: 'Test content', userId: 'test-user-id', isPinned: true, isArchived: false } }) const retrieved = await prisma.note.findUnique({ where: { id: note.id } }) expect(retrieved?.isPinned).toBe(true) expect(retrieved?.isArchived).toBe(false) }) test('should preserve numeric values correctly', async () => { const note = await prisma.note.create({ data: { title: 'Numeric Test Note', content: 'Test content', userId: 'test-user-id', order: 42, aiConfidence: 87, languageConfidence: 0.95 } }) const retrieved = await prisma.note.findUnique({ where: { id: note.id } }) expect(retrieved?.order).toBe(42) expect(retrieved?.aiConfidence).toBe(87) expect(retrieved?.languageConfidence).toBeCloseTo(0.95) }) test('should preserve date values correctly', async () => { const testDate = new Date('2024-01-15T10:30:00Z') const note = await prisma.note.create({ data: { title: 'Date Test Note', content: 'Test content', userId: 'test-user-id', reminder: testDate, lastAiAnalysis: testDate } }) const retrieved = await prisma.note.findUnique({ where: { id: note.id } }) expect(retrieved?.reminder).toBeDefined() expect(retrieved?.lastAiAnalysis).toBeDefined() }) }) })