This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
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<typeof GET>[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<typeof load>[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.' }
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user