import { http } from 'msw' import { jsonResponse, noContent, paginate } from '../helpers' import { seedUsers, opAlice, seedPermissions } from '../../fixtures/seed_users' import { seedClasses } from '../../fixtures/seed_classes' // Default `/api/admin/*` handlers — auth round-trip, users, classes-write, // system settings. Tests override per-scenario via `server.use(...)`. const SEED_BEARER = 'test-bearer-default' export const adminHandlers = [ http.post('/api/admin/auth/login', async ({ request }) => { const body = (await request.json().catch(() => ({}))) as { email?: string; password?: string } const user = seedUsers.find((u) => u.email === body.email) ?? opAlice return new Response(JSON.stringify({ token: SEED_BEARER, user }), { status: 200, headers: { 'Content-Type': 'application/json', // AC-03 contract — refresh cookie is HttpOnly + Secure + SameSite=Strict. 'Set-Cookie': 'refreshToken=test-refresh; HttpOnly; Secure; SameSite=Strict; Path=/api/admin/auth', }, }) }), http.post('/api/admin/auth/refresh', () => { return jsonResponse({ token: SEED_BEARER }) }), http.post('/api/admin/auth/logout', () => noContent()), // AZ-510 chains GET /users/me after POST refresh during AuthProvider // bootstrap. The default user shape includes `permissions` so production // code paths (e.g., hasPermission, RBAC route gates) get a realistic // payload without each test having to override. http.get('/api/admin/users/me', () => jsonResponse({ ...opAlice, permissions: seedPermissions[opAlice.id] ?? [] }), ), http.get('/api/admin/users', () => jsonResponse(paginate(seedUsers))), http.get('/api/admin/users/:id', ({ params }) => { const user = seedUsers.find((u) => u.id === params.id) if (!user) return new Response(null, { status: 404 }) return jsonResponse(user) }), http.get('/api/admin/classes', () => jsonResponse(seedClasses)), http.post('/api/admin/classes', async ({ request }) => { const body = await request.json() return jsonResponse(body, { status: 201 }) }), http.put('/api/admin/classes/:id', async ({ request }) => { const body = await request.json() return jsonResponse(body) }), // AZ-512 — PATCH partial-merge over the seeded class. Default-handler // returns the merged shape so the UI's PATCH-then-refetch sequence sees the // updated row. Tests that need 404/5xx semantics override per-scenario. http.patch('/api/admin/classes/:id', async ({ params, request }) => { const idParam = String(params.id) const id = Number(idParam) const body = (await request.json().catch(() => ({}))) as Partial<{ name: string shortName: string color: string maxSizeM: number photoMode: number }> const existing = seedClasses.find((c) => String(c.id) === idParam) ?? ({ id: Number.isFinite(id) ? id : 0, name: '', shortName: '', color: '#FF0000', maxSizeM: 5, photoMode: 0 } as const) return jsonResponse({ ...existing, ...body, id: existing.id }) }), http.delete('/api/admin/classes/:id', () => noContent()), http.get('/api/admin/settings', () => jsonResponse({ id: 'sys-settings-1', name: 'Test System', militaryUnit: null, defaultCameraWidth: 1920, defaultCameraFoV: 60, thumbnailWidth: 256, thumbnailHeight: 256, thumbnailBorder: 2, generateAnnotatedImage: true, silentDetection: false, }), ), http.put('/api/admin/settings', async ({ request }) => { const body = await request.json() return jsonResponse(body) }), // Test-only suite endpoint — gated behind a non-production build flag in the // real `admin/` service. The fast-profile MSW just returns 204 so isolation // helpers can call it uniformly with the e2e profile. http.post('/api/admin/test-only/reset', () => noContent()), ]