Keep/keep-notes/tests/migration-ai-fields.test.ts
sepehr ddb67ba9e5 fix: unify theme system - fix theme switching persistence
- Unified localStorage key to 'theme-preference' across all components
- Fixed header.tsx using wrong localStorage key ('theme' instead of 'theme-preference')
- Added localStorage hybrid persistence for instant theme changes
- Removed router.refresh() which was causing stale data revert
- Replaced Blue theme with Sepia
- Consolidated auth() calls to prevent race conditions
- Updated UserSettingsData types to include all themes
2026-01-18 22:33:41 +01:00

449 lines
13 KiB
TypeScript

/**
* 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)
}
})
})
})