Validates session cookie via GET /v1/auth/me, populates event.locals with benutzer and haushalt, redirects to /login if unauthenticated. Public routes (/login, /register, /invite) bypass auth. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
97 lines
2.4 KiB
TypeScript
97 lines
2.4 KiB
TypeScript
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().mockReturnValue(cookie)
|
|
},
|
|
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('redirects unauthenticated requests on protected routes', async () => {
|
|
const { event, resolve } = createEvent('/planner');
|
|
try {
|
|
await handle({ event, resolve });
|
|
// If using SvelteKit redirect, it throws
|
|
expect.unreachable();
|
|
} catch (e: any) {
|
|
expect(e.status).toBe(302);
|
|
expect(e.location).toBe('/login');
|
|
}
|
|
});
|
|
|
|
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('redirects to /login 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');
|
|
}
|
|
});
|
|
});
|