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() }, licenseKey: { findMany: vi.fn(), findFirst: vi.fn() } } })); vi.mock('$lib/server/schedule', () => ({ getScheduleForLicense: vi.fn(), setScheduleType: vi.fn(), createBlock: vi.fn(), updateBlock: vi.fn(), deleteBlock: vi.fn() })); import { prisma } from '$lib/db'; import { createBlock, deleteBlock, getScheduleForLicense, setScheduleType } from '$lib/server/schedule'; import { actions, load } from './+page.server'; const procFindMany = prisma.proclamation.findMany as ReturnType; const licenseFindMany = prisma.licenseKey.findMany as ReturnType; const licenseFindFirst = prisma.licenseKey.findFirst as ReturnType; const getScheduleMock = getScheduleForLicense as ReturnType; const setTypeMock = setScheduleType as ReturnType; const createBlockMock = createBlock as ReturnType; const deleteBlockMock = deleteBlock as ReturnType; const locals = createLocals(mockUser() as never); beforeEach(() => { vi.clearAllMocks(); (prisma.proclamationCategory.findMany as ReturnType).mockResolvedValue([ { id: 2, name: 'Physical' } ]); (prisma.proclamationGroup.findMany as ReturnType).mockResolvedValue([ { id: 1, name: 'General' } ]); procFindMany.mockResolvedValue([{ id: 'p1', name: 'Peace', categoryId: 2, groupId: 1 }]); licenseFindMany.mockResolvedValue([ { id: 'lk1', key: 'AAAA-BBBB-CCCC-DDDD', systemName: 'Living Room', active: true } ]); licenseFindFirst.mockResolvedValue({ id: 'lk1' }); getScheduleMock.mockResolvedValue(null); }); describe('schedule load', () => { it('rejects unauthenticated requests', async () => { await expect( load({ locals: createLocals(), url: new URL('http://x/') } as never) ).rejects.toMatchObject({ status: 403 }); }); it('never selects proclamation content', async () => { await load({ locals, url: new URL('http://x/') } 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 () => { getScheduleMock.mockResolvedValue({ id: 's1', type: 'daily', blocks: [ { id: 'b1', day: null, startMinutes: 360, endMinutes: 480, dynamicAudio: true, proclamations: [ { proclamation: { id: 'p1', name: 'Peace', categoryId: 2, groupId: 1, content: '

secret

' } } ] } ] }); const result = (await load({ locals, url: new URL('http://x/') } as never)) as { type: string | null; blocks: { id: string; dynamicAudio: boolean; proclamations: Record[] }[]; }; expect(result.type).toBe('daily'); 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({ licenseKeyId: 'lk1', 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({ licenseKeyId: 'lk1', day: 'mon', startMinutes: '360', endMinutes: '480', proclamationIds: JSON.stringify(['p1', 'p2']), dynamicAudio: 'true' }), locals } as never); expect(result).toEqual({ success: true }); expect(createBlockMock).toHaveBeenCalledWith('lk1', { day: 'mon', startMinutes: 360, endMinutes: 480, proclamationIds: ['p1', 'p2'], dynamicAudio: true }); }); it('setType requiring confirmation returns the confirm error', async () => { setTypeMock.mockResolvedValue({ ok: false, error: 'confirm' }); const result = await actions.setType!({ request: createFormRequest({ licenseKeyId: 'lk1', type: 'weekly' }), locals } as never); expect(isActionFailure(result)).toBe(true); if (isActionFailure(result)) { expect(result.data).toEqual({ error: 'confirm' }); } }); it('deleteBlock returns not found without an id', async () => { const result = await actions.deleteBlock!({ request: createFormRequest({ licenseKeyId: 'lk1' }), locals } as never); expect(isActionFailure(result)).toBe(true); if (isActionFailure(result)) { expect(result.data).toEqual({ error: 'Block not found.' }); } expect(deleteBlockMock).not.toHaveBeenCalled(); }); });