182 lines
4.8 KiB
TypeScript
182 lines
4.8 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { isActionFailure } from '@sveltejs/kit';
|
|
import { createFormRequest, createLocals, mockUser } from '$lib/test-utils';
|
|
|
|
vi.mock('$lib/db', () => ({
|
|
prisma: {
|
|
proclamationCategory: { findMany: vi.fn() },
|
|
proclamationGroup: { findMany: vi.fn() },
|
|
proclamation: { findMany: vi.fn() }
|
|
}
|
|
}));
|
|
|
|
vi.mock('$lib/server/schedule', () => ({
|
|
listBlocks: vi.fn(),
|
|
createBlock: vi.fn(),
|
|
updateBlock: vi.fn(),
|
|
deleteBlock: vi.fn()
|
|
}));
|
|
|
|
import { prisma } from '$lib/db';
|
|
import { createBlock, deleteBlock, listBlocks } from '$lib/server/schedule';
|
|
import { actions, load } from './+page.server';
|
|
|
|
const procFindMany = prisma.proclamation.findMany as ReturnType<typeof vi.fn>;
|
|
const listBlocksMock = listBlocks as ReturnType<typeof vi.fn>;
|
|
const createBlockMock = createBlock as ReturnType<typeof vi.fn>;
|
|
const deleteBlockMock = deleteBlock as ReturnType<typeof vi.fn>;
|
|
|
|
const locals = createLocals(mockUser() as never);
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
(prisma.proclamationCategory.findMany as ReturnType<typeof vi.fn>).mockResolvedValue([
|
|
{ id: 2, name: 'Physical' }
|
|
]);
|
|
(prisma.proclamationGroup.findMany as ReturnType<typeof vi.fn>).mockResolvedValue([
|
|
{ id: 1, name: 'General' }
|
|
]);
|
|
procFindMany.mockResolvedValue([{ id: 'p1', name: 'Peace', categoryId: 2, groupId: 1 }]);
|
|
listBlocksMock.mockResolvedValue([]);
|
|
});
|
|
|
|
describe('schedule load', () => {
|
|
it('rejects unauthenticated requests', async () => {
|
|
await expect(load({ locals: createLocals() } as never)).rejects.toMatchObject({
|
|
status: 403
|
|
});
|
|
});
|
|
|
|
it('never selects proclamation content', async () => {
|
|
await load({ locals } as never);
|
|
|
|
expect(procFindMany).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
select: { id: true, name: true, categoryId: true, groupId: true }
|
|
})
|
|
);
|
|
});
|
|
|
|
it('maps blocks to title/category/group only', async () => {
|
|
listBlocksMock.mockResolvedValue([
|
|
{
|
|
id: 'b1',
|
|
startMinutes: 360,
|
|
endMinutes: 480,
|
|
dynamicAudio: true,
|
|
proclamations: [
|
|
{
|
|
proclamation: {
|
|
id: 'p1',
|
|
name: 'Peace',
|
|
categoryId: 2,
|
|
groupId: 1,
|
|
content: '<p>secret</p>'
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]);
|
|
|
|
const result = (await load({ locals } as never)) as {
|
|
blocks: {
|
|
id: string;
|
|
dynamicAudio: boolean;
|
|
proclamations: Record<string, unknown>[];
|
|
}[];
|
|
};
|
|
|
|
expect(result.blocks[0].dynamicAudio).toBe(true);
|
|
expect(result.blocks[0].proclamations[0]).toEqual({
|
|
id: 'p1',
|
|
name: 'Peace',
|
|
categoryId: 2,
|
|
groupId: 1
|
|
});
|
|
expect(Object.keys(result.blocks[0].proclamations[0])).not.toContain('content');
|
|
});
|
|
});
|
|
|
|
describe('schedule actions', () => {
|
|
it('createBlock rejects unauthenticated requests', async () => {
|
|
await expect(
|
|
actions.createBlock!({
|
|
request: createFormRequest({}),
|
|
locals: createLocals()
|
|
} as never)
|
|
).rejects.toMatchObject({ status: 403 });
|
|
});
|
|
|
|
it('createBlock surfaces the service error', async () => {
|
|
createBlockMock.mockResolvedValue({ ok: false, error: 'Select at least one proclamation.' });
|
|
|
|
const result = await actions.createBlock!({
|
|
request: createFormRequest({
|
|
startMinutes: '360',
|
|
endMinutes: '480',
|
|
proclamationIds: '[]'
|
|
}),
|
|
locals
|
|
} as never);
|
|
|
|
expect(isActionFailure(result)).toBe(true);
|
|
if (isActionFailure(result)) {
|
|
expect(result.status).toBe(400);
|
|
expect(result.data).toEqual({ error: 'Select at least one proclamation.' });
|
|
}
|
|
});
|
|
|
|
it('createBlock parses the payload and succeeds', async () => {
|
|
createBlockMock.mockResolvedValue({ ok: true, id: 'b1' });
|
|
|
|
const result = await actions.createBlock!({
|
|
request: createFormRequest({
|
|
startMinutes: '360',
|
|
endMinutes: '480',
|
|
proclamationIds: JSON.stringify(['p1', 'p2']),
|
|
dynamicAudio: 'true'
|
|
}),
|
|
locals
|
|
} as never);
|
|
|
|
expect(result).toEqual({ success: true });
|
|
expect(createBlockMock).toHaveBeenCalledWith('user-1', {
|
|
startMinutes: 360,
|
|
endMinutes: 480,
|
|
proclamationIds: ['p1', 'p2'],
|
|
dynamicAudio: true
|
|
});
|
|
});
|
|
|
|
it('createBlock defaults dynamic audio to false when the field is absent', async () => {
|
|
createBlockMock.mockResolvedValue({ ok: true, id: 'b1' });
|
|
|
|
await actions.createBlock!({
|
|
request: createFormRequest({
|
|
startMinutes: '360',
|
|
endMinutes: '480',
|
|
proclamationIds: JSON.stringify(['p1'])
|
|
}),
|
|
locals
|
|
} as never);
|
|
|
|
expect(createBlockMock).toHaveBeenCalledWith(
|
|
'user-1',
|
|
expect.objectContaining({ dynamicAudio: false })
|
|
);
|
|
});
|
|
|
|
it('deleteBlock returns not found without an id', async () => {
|
|
const result = await actions.deleteBlock!({
|
|
request: createFormRequest({}),
|
|
locals
|
|
} as never);
|
|
|
|
expect(isActionFailure(result)).toBe(true);
|
|
if (isActionFailure(result)) {
|
|
expect(result.data).toEqual({ error: 'Block not found.' });
|
|
}
|
|
expect(deleteBlockMock).not.toHaveBeenCalled();
|
|
});
|
|
});
|