import type { RequestEvent } from '@sveltejs/kit'; import { fail, isActionFailure, isHttpError, isRedirect, redirect } from '@sveltejs/kit'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { guarded } from '../../../src/lib/server/actions'; import { ReadOnlyError } from '../../../src/lib/server/ledger/errors'; /** The guard never touches the event, so a bare object is enough. */ const event = { request: new Request('http://localhost/'), locals: {} } as unknown as RequestEvent; describe('guarded', () => { beforeEach(() => { vi.spyOn(console, 'error').mockImplementation(() => {}); }); it('turns an unexpected throw into a 503 with a retry message', async () => { const actions = guarded({ go: async (_e: RequestEvent) => { throw new Error('boom'); } }); const e = await actions.go(event).then( () => null, (err: unknown) => err ); expect(isHttpError(e)).toBe(true); expect(isHttpError(e) && e.status).toBe(503); expect(isHttpError(e) && e.body.message).toBe( 'Could not save — the storage backend is unavailable. Please try again.' ); expect(console.error).toHaveBeenCalledWith('[action] storage failure', expect.any(Error)); }); it('maps ReadOnlyError to a 503 that says the app is read-only', async () => { const actions = guarded({ go: async (_e: RequestEvent) => { throw new ReadOnlyError(); } }); const e = await actions.go(event).then( () => null, (err: unknown) => err ); expect(isHttpError(e) && e.status).toBe(503); expect(isHttpError(e) && e.body.message).toBe( 'The app is temporarily read-only; try again in a couple of minutes.' ); }); it('rethrows redirects unchanged', async () => { const actions = guarded({ go: async (_e: RequestEvent) => { redirect(303, '/'); } }); const e = await actions.go(event).then( () => null, (err: unknown) => err ); expect(isRedirect(e)).toBe(true); expect(isRedirect(e) && e.status).toBe(303); expect(isRedirect(e) && e.location).toBe('/'); }); it('returns action failures unchanged', async () => { const failure = fail(400, { error: 'nope' }); const actions = guarded({ go: async (_e: RequestEvent) => failure }); const result = await actions.go(event); expect(result).toBe(failure); expect(isActionFailure(result)).toBe(true); }); });