- Import superValidate from sveltekit-superforms/server instead of default path to avoid pulling SuperDebug.svelte into SSR - Upgrade @prisma/client to match prisma CLI version (7.2.0 -> 7.8.0) so generated client's query_compiler_fast_bg files exist at runtime - Add regression test verifying superValidate works in server context - Add regression test verifying generated Prisma client compiler imports reference real files
170 lines
5.2 KiB
TypeScript
170 lines
5.2 KiB
TypeScript
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', () => ({
|
|
prisma: {
|
|
userRole: {
|
|
findMany: vi.fn(),
|
|
create: vi.fn(),
|
|
update: vi.fn(),
|
|
delete: vi.fn(),
|
|
findUnique: vi.fn(),
|
|
findFirst: vi.fn()
|
|
}
|
|
}
|
|
}));
|
|
|
|
import { prisma } from '$lib/db';
|
|
import { loadRecords, upsertRecord, deleteRecord } from './crud';
|
|
|
|
const findMany = prisma.userRole.findMany as ReturnType<typeof vi.fn>;
|
|
const create = prisma.userRole.create as ReturnType<typeof vi.fn>;
|
|
const update = prisma.userRole.update as ReturnType<typeof vi.fn>;
|
|
const deletes = prisma.userRole.delete as ReturnType<typeof vi.fn>;
|
|
const findUnique = prisma.userRole.findUnique as ReturnType<typeof vi.fn>;
|
|
const findFirst = prisma.userRole.findFirst as ReturnType<typeof vi.fn>;
|
|
|
|
const config = getModelConfig('user-roles')!;
|
|
|
|
describe('crud loadRecords', () => {
|
|
beforeEach(() => {
|
|
findMany.mockReset();
|
|
});
|
|
|
|
it('returns records', async () => {
|
|
findMany.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 () => {
|
|
findMany.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('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);
|
|
});
|
|
});
|