import { afterEach, beforeEach, expect, it, vi } from 'vitest'; import type { AuthUser } from '../../src/lib/auth.js'; import { newFixture, type Fixture } from '../helpers/fixture.js'; let fixture: Fixture; beforeEach(async () => { vi.resetModules(); fixture = await newFixture([]); const { db } = await import('../../src/lib/server/db/index.js'); for (const role of ['admin', 'moderator', 'super_viewer', 'operator', 'viewer']) { db() .prepare( `INSERT INTO auth_users (id,username,display_name,password_hash,role,created_at,updated_at) VALUES(?,?,?,'unused',?,0,0)` ) .run(role, role, role, role); } db() .prepare( `INSERT INTO moderation_cases (id,game,guid,title,status,actor_id,actor,created_at,updated_at) VALUES('case-123','t5','123','Private investigation','open','moderator','moderator',?,?)` ) .run('2026-10-17T12:00:00.000Z', '2026-10-17T12:00:00.000Z'); }); afterEach(async () => fixture.cleanup()); async function api(user: AuthUser | null, id = 'case-123') { const { GET } = await import('../../src/routes/api/moderation/cases/[id]/+server.js'); return GET({ locals: { user }, params: { id } } as Parameters[0]); } async function page(user: AuthUser | null, id = 'case-123') { const { load } = await import('../../src/routes/moderation/cases/[id]/+page.server.js'); const setHeaders = vi.fn(); const result = await load({ locals: { user }, params: { id }, setHeaders } as unknown as Parameters[0]); return { result, setHeaders }; } it.each(['admin', 'moderator', 'super_viewer'])( 'serves private case detail without caching to %s', async (role) => { const { getUser } = await import('../../src/lib/server/auth/index.js'); const user = getUser(role)!; const response = await api(user); expect(response.status).toBe(200); expect(response.headers.get('Cache-Control')).toBe('no-store'); expect(await response.json()).toMatchObject({ case: { id: 'case-123', title: 'Private investigation' } }); const { result, setHeaders } = await page(user); expect(result).toMatchObject({ moderationCase: { case: { id: 'case-123', title: 'Private investigation' } } }); expect(setHeaders).toHaveBeenCalledWith({ 'Cache-Control': 'no-store' }); } ); it.each(['operator', 'viewer'])( 'rejects %s reads before revealing case existence or content', async (role) => { const { getUser } = await import('../../src/lib/server/auth/index.js'); const user = getUser(role)!; for (const id of ['case-123', 'missing']) { const response = await api(user, id); expect(response.status).toBe(403); expect(response.headers.get('Cache-Control')).toBe('no-store'); expect(await response.json()).toEqual({ error: 'Moderation access is required.' }); await expect(page(user, id)).rejects.toMatchObject({ status: 403, body: { message: 'Moderation access is required.' } }); } } ); it.each(["role='viewer'", 'disabled=1', 'must_change_password=1'])( 'rechecks current staff access when stale session locals have %s', async (change) => { const { getUser } = await import('../../src/lib/server/auth/index.js'); const { db } = await import('../../src/lib/server/db/index.js'); const user = getUser('moderator')!; db().prepare(`UPDATE auth_users SET ${change} WHERE id='moderator'`).run(); const response = await api(user); expect(response.status).toBe(403); expect(response.headers.get('Cache-Control')).toBe('no-store'); await expect(page(user)).rejects.toMatchObject({ status: 403 }); } ); it('preserves private not-found responses in the API and page loader', async () => { const { getUser } = await import('../../src/lib/server/auth/index.js'); const user = getUser('moderator')!; const response = await api(user, 'missing'); expect(response.status).toBe(404); expect(response.headers.get('Cache-Control')).toBe('no-store'); expect(await response.json()).toEqual({ error: 'Case not found.' }); await expect(page(user, 'missing')).rejects.toMatchObject({ status: 404, body: { message: 'Case not found.' } }); }); it('requires a signed-in user before looking up a case', async () => { const response = await api(null); expect(response.status).toBe(401); expect(response.headers.get('Cache-Control')).toBe('no-store'); expect(await response.json()).toEqual({ error: 'Sign in to continue.' }); await expect(page(null)).rejects.toMatchObject({ status: 401, body: { message: 'Sign in to continue.' } }); });