import { describe, it, expect, vi, beforeEach } from 'vitest'; import { createLocals, mockUser } from '$lib/test-utils'; import { getModelConfig } from './registry'; // Ensure superValidate can be imported from server path in Node.js/SSR context import { superValidate } from 'sveltekit-superforms/server'; import { zod4 } from 'sveltekit-superforms/adapters'; vi.mock('$lib/db', () => { const mkdel = () => ({ findMany: vi.fn(), create: vi.fn(), update: vi.fn(), delete: vi.fn(), findUnique: vi.fn(), findFirst: vi.fn() }); return { prisma: { userRole: mkdel(), proclamation: mkdel(), proclamationCategory: mkdel(), proclamationGroup: mkdel(), solfeggioFrequency: mkdel() } }; }); import { prisma } from '$lib/db'; import { loadRecords, upsertRecord, deleteRecord } from './crud'; const userRoleFindMany = prisma.userRole.findMany as ReturnType; const create = prisma.userRole.create as ReturnType; const update = prisma.userRole.update as ReturnType; const deletes = prisma.userRole.delete as ReturnType; const findUnique = prisma.userRole.findUnique as ReturnType; const findFirst = prisma.userRole.findFirst as ReturnType; const config = getModelConfig('user-roles')!; describe('crud loadRecords', () => { beforeEach(() => { userRoleFindMany.mockReset(); }); it('returns records', async () => { userRoleFindMany.mockResolvedValue([{ id: 1, name: 'Admin' }]); const locals = createLocals(mockUser() as any); const result = await loadRecords(locals, config); expect(result.records).toEqual([{ id: 1, name: 'Admin' }]); }); it('returns empty records array', async () => { userRoleFindMany.mockResolvedValue([]); const locals = createLocals(mockUser() as any); const result = await loadRecords(locals, config); expect(result.records).toEqual([]); }); }); describe('crud upsertRecord', () => { beforeEach(() => { create.mockReset(); update.mockReset(); findUnique.mockReset(); findFirst.mockReset(); }); it('creates a new record when no id', async () => { create.mockResolvedValue({ id: 2, name: 'Editor' }); const fd = new FormData(); fd.set('name', 'Editor'); const locals = createLocals(mockUser() as any); const result = await upsertRecord(fd, config, locals); expect(create).toHaveBeenCalledWith({ data: { name: 'Editor' } }); expect(result).toEqual({ success: true }); }); it('updates an existing record when id is present', async () => { findUnique.mockResolvedValue({ id: 1, name: 'Old Name' }); const fd = new FormData(); fd.set('id', '1'); fd.set('name', 'New Name'); const locals = createLocals(mockUser() as any); const result = await upsertRecord(fd, config, locals); expect(update).toHaveBeenCalledWith({ where: { id: 1 }, data: { name: 'New Name' } }); expect(result).toEqual({ success: true }); }); it('returns error when updating non-existent record', async () => { findUnique.mockResolvedValue(null); const fd = new FormData(); fd.set('id', '999'); fd.set('name', 'Ghost'); const locals = createLocals(mockUser() as any); const result = await upsertRecord(fd, config, locals); expect(result).toEqual({ success: false, error: 'Record not found' }); }); }); describe('crud deleteRecord', () => { beforeEach(() => { deletes.mockReset(); }); it('deletes a record by id', async () => { deletes.mockResolvedValue({ id: 1, name: 'Admin' }); const fd = new FormData(); fd.set('id', '1'); const locals = createLocals(mockUser() as any); const result = await deleteRecord(fd, config, locals); expect(deletes).toHaveBeenCalledWith({ where: { id: 1 } }); expect(result).toEqual({ success: true }); }); it('returns error when id is missing', async () => { const fd = new FormData(); const locals = createLocals(mockUser() as any); const result = await deleteRecord(fd, config, locals); expect(result).toEqual({ success: false, error: 'Missing id' }); }); }); describe('P2002 unique constraint handling', () => { beforeEach(() => { create.mockReset(); }); it('returns error with field names when meta.target is populated', async () => { const p2002 = new Error('Unique constraint failed'); (p2002 as any).code = 'P2002'; (p2002 as any).meta = { target: ['name'], modelName: 'UserRole' }; create.mockRejectedValue(p2002); const fd = new FormData(); fd.set('name', 'Admin'); const locals = createLocals(mockUser() as any); const result = await upsertRecord(fd, config, locals); expect(result).toEqual({ success: false, error: 'A record with this name already exists.', fieldErrors: { name: 'Already taken' } }); }); it('returns generic message when meta.target is empty', async () => { const p2002 = new Error('Unique constraint failed'); (p2002 as any).code = 'P2002'; (p2002 as any).meta = { target: [], modelName: 'UserRole' }; create.mockRejectedValue(p2002); const fd = new FormData(); fd.set('name', 'Admin'); const locals = createLocals(mockUser() as any); const result = await upsertRecord(fd, config, locals); expect(result).toEqual({ success: false, error: 'A database constraint violation occurred. The record could not be created.' }); }); it('returns generic message when meta.target is missing', async () => { const p2002 = new Error('Unique constraint failed'); (p2002 as any).code = 'P2002'; (p2002 as any).meta = { modelName: 'UserRole' }; create.mockRejectedValue(p2002); const fd = new FormData(); fd.set('name', 'Admin'); const locals = createLocals(mockUser() as any); const result = await upsertRecord(fd, config, locals); expect(result).toEqual({ success: false, error: 'A database constraint violation occurred. The record could not be created.' }); }); }); describe('sveltekit-superforms import compatibility', () => { it('imports superValidate from server path without pulling in SuperDebug.svelte', async () => { expect(superValidate).toBeDefined(); }); it('calls superValidate with a Request and validates against a Zod schema', async () => { const { z } = await import('zod'); const schema = z.object({ name: z.string().min(1) }); const body = new URLSearchParams({ name: 'test' }); const request = new Request('http://localhost', { method: 'POST', body, headers: { 'content-type': 'application/x-www-form-urlencoded' } }); const form = await superValidate(request, zod4(schema)); expect(form.valid).toBe(true); expect(form.data).toEqual({ name: 'test' }); }); }); describe('prisma client version consistency', () => { it('generated client references existing query compiler files', async () => { const fs = await import('fs'); const path = await import('path'); const runtimeDir = path.resolve('node_modules/@prisma/client/runtime'); const generatedFile = path.resolve('src/lib/generated/prisma/internal/class.ts'); const content = fs.readFileSync(generatedFile, 'utf-8'); const compilerMatch = content.match(/import\(["']@prisma\/client\/runtime\/([^"']+)["']\)/); expect(compilerMatch).not.toBeNull(); const compilerPath = path.join(runtimeDir, compilerMatch![1]); expect(fs.existsSync(compilerPath)).toBe(true); }); });