import { describe, it, expect, vi, beforeEach } from 'vitest'; // Mock $env/dynamic/private before importing anything vi.mock('$env/dynamic/private', () => ({ env: { BACKEND_URL: 'http://localhost:8080' } })); // Mock the apiClient const mockGet = vi.fn(); vi.mock('$lib/server/api', () => ({ apiClient: () => ({ GET: mockGet }) })); describe('auth guard (hooks.server.ts handle)', () => { let handle: any; beforeEach(async () => { mockGet.mockReset(); const mod = await import('./hooks.server'); handle = mod.handle; }); function createEvent(pathname: string, cookie?: string) { const resolve = vi.fn().mockResolvedValue(new Response('ok')); const event = { url: new URL(`http://localhost${pathname}`), cookies: { get: vi.fn().mockImplementation((name: string) => { if (name === 'JSESSIONID') return cookie; return undefined; }) }, locals: {} as any, fetch: vi.fn() }; return { event, resolve }; } it('allows public routes without auth', async () => { const { event, resolve } = createEvent('/login'); await handle({ event, resolve }); expect(resolve).toHaveBeenCalledWith(event); }); it.each(['/login', '/login/', '/register', '/invite/abc123'])( 'allows public route %s without auth', async (path) => { const { event, resolve } = createEvent(path); await handle({ event, resolve }); expect(resolve).toHaveBeenCalledWith(event); } ); it.each(['/_app/immutable/chunks/app.js', '/favicon.ico'])( 'allows static asset %s without auth', async (path) => { const { event, resolve } = createEvent(path); await handle({ event, resolve }); expect(resolve).toHaveBeenCalledWith(event); } ); it('redirects unauthenticated requests to /login with redirect param', async () => { const { event, resolve } = createEvent('/recipes/abc'); try { await handle({ event, resolve }); expect.unreachable(); } catch (e: any) { expect(e.status).toBe(302); expect(e.location).toBe('/login?redirect=%2Frecipes%2Fabc'); } }); it('populates event.locals.benutzer on valid session', async () => { mockGet.mockResolvedValue({ data: { data: { id: '123', displayName: 'Max', householdId: 'h1', householdName: 'Familie Müller', householdRole: 'planer', email: 'max@example.com', systemRole: 'user' } }, error: undefined }); const { event, resolve } = createEvent('/planner', 'valid-session'); await handle({ event, resolve }); expect(event.locals.benutzer).toEqual({ id: '123', name: 'Max', rolle: 'planer' }); expect(event.locals.haushalt).toEqual({ id: 'h1', name: 'Familie Müller' }); expect(resolve).toHaveBeenCalledWith(event); }); it('handles user without household gracefully', async () => { mockGet.mockResolvedValue({ data: { data: { id: '456', displayName: 'Neu', householdId: null, householdName: null, householdRole: null, email: 'neu@example.com', systemRole: 'user' } }, error: undefined }); const { event, resolve } = createEvent('/planner', 'valid-session'); await handle({ event, resolve }); expect(event.locals.benutzer).toEqual({ id: '456', name: 'Neu', rolle: 'mitglied' }); expect(event.locals.haushalt).toEqual({ id: undefined, name: 'Kein Haushalt' }); expect(resolve).toHaveBeenCalledWith(event); }); it('redirects to /login with redirect param when session validation fails', async () => { mockGet.mockResolvedValue({ data: undefined, error: { status: 401 } }); const { event, resolve } = createEvent('/planner', 'bad-session'); try { await handle({ event, resolve }); expect.unreachable(); } catch (e: any) { expect(e.status).toBe(302); expect(e.location).toBe('/login?redirect=%2Fplanner'); } }); });