/** * Test suite for AI field migrations * Validates that Note and AiFeedback models work correctly with new AI fields */ import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient() describe('AI Fields Migration Tests', () => { beforeAll(async () => { // Ensure clean test environment await prisma.aiFeedback.deleteMany({}) await prisma.note.deleteMany({ where: { title: { contains: 'TEST_AI' } } }) }) afterAll(async () => { // Cleanup await prisma.aiFeedback.deleteMany({}) await prisma.note.deleteMany({ where: { title: { contains: 'TEST_AI' } } }) await prisma.$disconnect() }) describe('Note Model - AI Fields', () => { test('should create note without AI fields (backward compatibility)', async () => { const note = await prisma.note.create({ data: { title: 'TEST_AI: Note without AI', content: 'This is a test note without AI fields', userId: 'test-user-id' } }) expect(note).toBeDefined() expect(note.id).toBeDefined() expect(note.title).toBe('TEST_AI: Note without AI') expect(note.autoGenerated).toBeNull() expect(note.aiProvider).toBeNull() expect(note.aiConfidence).toBeNull() expect(note.language).toBeNull() expect(note.languageConfidence).toBeNull() expect(note.lastAiAnalysis).toBeNull() }) test('should create note with all AI fields populated', async () => { const testDate = new Date() const note = await prisma.note.create({ data: { title: 'TEST_AI: Note with AI fields', content: 'This is a test note with AI fields', userId: 'test-user-id', autoGenerated: true, aiProvider: 'openai', aiConfidence: 95, language: 'fr', languageConfidence: 0.98, lastAiAnalysis: testDate } }) expect(note).toBeDefined() expect(note.autoGenerated).toBe(true) expect(note.aiProvider).toBe('openai') expect(note.aiConfidence).toBe(95) expect(note.language).toBe('fr') expect(note.languageConfidence).toBe(0.98) expect(note.lastAiAnalysis).toEqual(testDate) }) test('should update note with AI fields', async () => { const note = await prisma.note.create({ data: { title: 'TEST_AI: Note for update test', content: 'Initial content', userId: 'test-user-id' } }) const updatedNote = await prisma.note.update({ where: { id: note.id }, data: { autoGenerated: true, aiProvider: 'ollama', aiConfidence: 87 } }) expect(updatedNote.autoGenerated).toBe(true) expect(updatedNote.aiProvider).toBe('ollama') expect(updatedNote.aiConfidence).toBe(87) }) test('should query notes filtered by AI fields', async () => { await prisma.note.create({ data: { title: 'TEST_AI: Auto-generated note 1', content: 'Test content', userId: 'test-user-id', autoGenerated: true, aiProvider: 'openai' } }) await prisma.note.create({ data: { title: 'TEST_AI: Auto-generated note 2', content: 'Test content', userId: 'test-user-id', autoGenerated: true, aiProvider: 'ollama' } }) const autoGeneratedNotes = await prisma.note.findMany({ where: { title: { contains: 'TEST_AI' }, autoGenerated: true } }) expect(autoGeneratedNotes.length).toBeGreaterThanOrEqual(2) expect(autoGeneratedNotes.every(n => n.autoGenerated === true)).toBe(true) }) }) describe('AiFeedback Model', () => { test('should create feedback entry', async () => { const note = await prisma.note.create({ data: { title: 'TEST_AI: Note for feedback', content: 'Test note', 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: 'AI suggested title', metadata: JSON.stringify({ aiProvider: 'openai', confidence: 95, model: 'gpt-4', timestamp: new Date().toISOString() }) } }) expect(feedback).toBeDefined() expect(feedback.id).toBeDefined() expect(feedback.feedbackType).toBe('thumbs_up') expect(feedback.feature).toBe('title_suggestion') expect(feedback.originalContent).toBe('AI suggested title') expect(feedback.noteId).toBe(note.id) }) test('should handle thumbs_down feedback', async () => { const note = await prisma.note.create({ data: { title: 'TEST_AI: Note for thumbs down', content: 'Test note', userId: 'test-user-id' } }) const feedback = await prisma.aiFeedback.create({ data: { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_down', feature: 'title_suggestion', originalContent: 'Bad suggestion' } }) expect(feedback.feedbackType).toBe('thumbs_down') }) test('should handle correction feedback', async () => { const note = await prisma.note.create({ data: { title: 'TEST_AI: Note for correction', content: 'Test note', userId: 'test-user-id' } }) const feedback = await prisma.aiFeedback.create({ data: { noteId: note.id, userId: 'test-user-id', feedbackType: 'correction', feature: 'title_suggestion', originalContent: 'Wrong suggestion', correctedContent: 'Corrected version' } }) expect(feedback.feedbackType).toBe('correction') expect(feedback.correctedContent).toBe('Corrected version') }) test('should query feedback by note', async () => { const note = await prisma.note.create({ data: { title: 'TEST_AI: Note for feedback query', content: 'Test note', userId: 'test-user-id' } }) await prisma.aiFeedback.create({ data: { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_up', feature: 'title_suggestion', originalContent: 'Feedback 1' } }) await prisma.aiFeedback.create({ data: { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_down', feature: 'title_suggestion', originalContent: 'Feedback 2' } }) const feedbacks = await prisma.aiFeedback.findMany({ where: { noteId: note.id }, orderBy: { createdAt: 'asc' } }) expect(feedbacks.length).toBe(2) }) test('should query feedback by feature', async () => { const note = await prisma.note.create({ data: { title: 'TEST_AI: Note for feature query', content: 'Test note', userId: 'test-user-id' } }) await prisma.aiFeedback.create({ data: { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_up', feature: 'title_suggestion', originalContent: 'Feedback 1' } }) await prisma.aiFeedback.create({ data: { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_up', feature: 'semantic_search', originalContent: 'Feedback 2' } }) const titleFeedbacks = await prisma.aiFeedback.findMany({ where: { feature: 'title_suggestion' } }) expect(titleFeedbacks.length).toBeGreaterThanOrEqual(1) expect(titleFeedbacks.every(f => f.feature === 'title_suggestion')).toBe(true) }) }) describe('Cascade Deletion', () => { test('should cascade delete feedback when note is deleted', async () => { const note = await prisma.note.create({ data: { title: 'TEST_AI: Note for cascade test', content: 'Test note', userId: 'test-user-id' } }) await prisma.aiFeedback.create({ data: { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_up', feature: 'title_suggestion', originalContent: 'Feedback to be deleted' } }) // Verify feedback exists const feedbacksBefore = await prisma.aiFeedback.findMany({ where: { noteId: note.id } }) expect(feedbacksBefore.length).toBe(1) // Delete the note await prisma.note.delete({ where: { id: note.id } }) // Verify feedback was cascade deleted const feedbacksAfter = await prisma.aiFeedback.findMany({ where: { noteId: note.id } }) expect(feedbacksAfter.length).toBe(0) }) test('should cascade delete feedback when user is deleted', async () => { // This test would require a User model with proper setup // For now, we'll skip as user deletion is a more complex operation // that may involve authentication and authorization }) }) describe('Index Performance', () => { test('should have indexes on critical fields', async () => { // Verify indexes exist by checking query plan or performance // For SQLite, indexes are created in the migration // This is more of a documentation test than a runtime test const note = await prisma.note.create({ data: { title: 'TEST_AI: Note for index test', content: 'Test note', userId: 'test-user-id' } }) 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 noteId (should use index) const byNoteId = await prisma.aiFeedback.findMany({ where: { noteId: note.id } }) expect(byNoteId.length).toBe(2) // Query by userId (should use index) const byUserId = await prisma.aiFeedback.findMany({ where: { userId: 'test-user-id' } }) expect(byUserId.length).toBeGreaterThanOrEqual(2) // Query by feature (should use index) const byFeature = await prisma.aiFeedback.findMany({ where: { feature: 'title_suggestion' } }) expect(byFeature.length).toBeGreaterThanOrEqual(1) }) }) describe('Data Types Validation', () => { test('should accept valid aiProvider values', async () => { const providers = ['openai', 'ollama', null] for (const provider of providers) { const note = await prisma.note.create({ data: { title: `TEST_AI: Note with provider ${provider}`, content: 'Test note', userId: 'test-user-id', aiProvider: provider } }) expect(note.aiProvider).toBe(provider) } }) test('should accept valid aiConfidence range (0-100)', async () => { const confidences = [0, 50, 100] for (const conf of confidences) { const note = await prisma.note.create({ data: { title: `TEST_AI: Note with confidence ${conf}`, content: 'Test note', userId: 'test-user-id', aiConfidence: conf } }) expect(note.aiConfidence).toBe(conf) } }) test('should accept valid languageConfidence range (0.0-1.0)', async () => { const confidences = [0.0, 0.5, 0.99, 1.0] for (const conf of confidences) { const note = await prisma.note.create({ data: { title: `TEST_AI: Note with lang confidence ${conf}`, content: 'Test note', userId: 'test-user-id', languageConfidence: conf } }) expect(note.languageConfidence).toBe(conf) } }) test('should accept valid ISO 639-1 language codes', async () => { const languages = ['en', 'fr', 'es', 'de', 'fa', null] for (const lang of languages) { const note = await prisma.note.create({ data: { title: `TEST_AI: Note in language ${lang}`, content: 'Test note', userId: 'test-user-id', language: lang } }) expect(note.language).toBe(lang) } }) }) })