v0.71.0: admin panel — users table, login/sessions, generic DB CRUD modal
CI / build + unit + e2e (pull_request) Failing after 13m35s
CI / build + unit + e2e (pull_request) Failing after 13m35s
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
import fs from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||
import { getGameDb } from '@/lib/db/client';
|
||||
import { createSession } from '@/lib/auth';
|
||||
import { POST } from './route';
|
||||
|
||||
// v0.71.0 — logout route tests: deletes the session named by X-Flight-Session, idempotently.
|
||||
|
||||
let dir: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
dir = await fs.mkdtemp(path.join(os.tmpdir(), 'flight-logout-api-'));
|
||||
process.env.FLIGHT_DB_PATH = path.join(dir, 'logout.db');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
delete process.env.FLIGHT_DB_PATH;
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
const post = (sessionId?: string) =>
|
||||
POST(
|
||||
new Request('http://localhost/api/logout', {
|
||||
method: 'POST',
|
||||
headers: sessionId ? { 'X-Flight-Session': sessionId } : undefined,
|
||||
}),
|
||||
);
|
||||
|
||||
async function mintSession(): Promise<string> {
|
||||
const inst = await getGameDb();
|
||||
return createSession(inst, 'admin-kamilkrzywda'); // any user id works — logout only deletes the session row
|
||||
}
|
||||
|
||||
describe('POST /api/logout', () => {
|
||||
it('returns 401 without a session header', async () => {
|
||||
const res = await post();
|
||||
expect(res.status).toBe(401);
|
||||
});
|
||||
|
||||
it('deletes the named session and is idempotent on repeat calls', async () => {
|
||||
const sessionId = await mintSession();
|
||||
const count = (await getGameDb()).raw.exec('SELECT COUNT(*) FROM sessions WHERE id = ?', [sessionId])[0].values[0][0];
|
||||
expect(Number(count)).toBe(1);
|
||||
|
||||
const first = await post(sessionId);
|
||||
expect(first.status).toBe(200);
|
||||
expect(await first.json()).toEqual({ ok: true });
|
||||
|
||||
// The row is gone…
|
||||
const after = (await getGameDb()).raw.exec('SELECT COUNT(*) FROM sessions WHERE id = ?', [sessionId])[0].values[0][0];
|
||||
expect(Number(after)).toBe(0);
|
||||
|
||||
// …and a second logout of the same (now unknown) session is still 200.
|
||||
const second = await post(sessionId);
|
||||
expect(second.status).toBe(200);
|
||||
expect(await second.json()).toEqual({ ok: true });
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
import { getGameDb, persist } from '@/lib/db/client';
|
||||
import { SESSION_HEADER } from '@/lib/auth';
|
||||
|
||||
// v0.71.0 — POST /api/logout (no body). Deletes the session named by the X-Flight-Session
|
||||
// header; idempotent — deleting an unknown or already-deleted session is still 200. A missing
|
||||
// header has nothing to delete and gets 401 like every other unauthenticated request here.
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const sessionId = req.headers.get(SESSION_HEADER);
|
||||
if (!sessionId) return Response.json({ error: 'authentication required' }, { status: 401 });
|
||||
|
||||
const inst = await getGameDb();
|
||||
inst.raw.run('DELETE FROM sessions WHERE id = ?', [sessionId]);
|
||||
await persist(inst);
|
||||
return Response.json({ ok: true });
|
||||
}
|
||||
Reference in New Issue
Block a user