71 lines
3.3 KiB
TypeScript
71 lines
3.3 KiB
TypeScript
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 { verifyPassword } from '@/lib/auth';
|
|
import { STARTING_CASH, STARTING_REPUTATION } from '@/lib/game/seed';
|
|
import { POST } from './route';
|
|
|
|
// v1.1.0 (ISS-107) — register route tests. Same temp-DB pattern as the login route tests: the DB
|
|
// singleton points at a throwaway file, and every created user is verified through verifyPassword().
|
|
|
|
let dir: string;
|
|
|
|
beforeAll(async () => {
|
|
dir = await fs.mkdtemp(path.join(os.tmpdir(), 'flight-register-api-'));
|
|
process.env.FLIGHT_DB_PATH = path.join(dir, 'register.db');
|
|
});
|
|
|
|
afterAll(async () => {
|
|
delete process.env.FLIGHT_DB_PATH;
|
|
await fs.rm(dir, { recursive: true, force: true });
|
|
});
|
|
|
|
const post = (body: unknown) => POST(new Request('http://localhost/api/register', { method: 'POST', body: JSON.stringify(body) }));
|
|
|
|
describe('POST /api/register', () => {
|
|
it('rejects malformed bodies and bad credentials with 400', async () => {
|
|
for (const body of [
|
|
{}, // missing both fields
|
|
{ username: 'ab', password: 'longenough' }, // too short
|
|
{ username: 'a'.repeat(21), password: 'longenough' }, // too long
|
|
{ username: 'bad user!', password: 'longenough' }, // bad charset (space + !)
|
|
{ username: 'validuser', password: 'short' }, // password < 8 chars
|
|
{ username: 'validuser', password: 'longenough', bogus: 1 }, // unknown field
|
|
]) {
|
|
const res = await post(body);
|
|
expect(res.status).toBe(400);
|
|
expect(((await res.json()) as { error: string }).error).toBeTruthy();
|
|
}
|
|
});
|
|
|
|
it('creates users + players rows sharing one id, with the starter economy, and mints a session', async () => {
|
|
const res = await post({ username: 'NewUser42', password: 'correct-horse-1' });
|
|
expect(res.status).toBe(200);
|
|
const body = (await res.json()) as { sessionId: string; user: { username: string; admin: boolean } };
|
|
expect(body.user).toEqual({ username: 'newuser42', admin: false }); // input lowercased
|
|
expect(body.sessionId).toMatch(/^[0-9a-f-]{36}$/);
|
|
|
|
const inst = await getGameDb();
|
|
const userRow = inst.raw.exec('SELECT id, password_hash FROM users WHERE username = ?', ['newuser42'])[0].values[0];
|
|
const playerRow = inst.raw.exec('SELECT id, provider, cash, reputation FROM players WHERE username = ?', ['newuser42'])[0].values[0];
|
|
expect(String(playerRow[0])).toBe(String(userRow[0])); // one UUID for both rows
|
|
expect(verifyPassword('correct-horse-1', String(userRow[1]))).toBe(true);
|
|
expect(String(playerRow[1])).toBe('local');
|
|
expect(Number(playerRow[2])).toBe(STARTING_CASH);
|
|
expect(Number(playerRow[3])).toBe(STARTING_REPUTATION);
|
|
|
|
// The minted session resolves to the new user.
|
|
const sess = inst.raw.exec('SELECT user_id FROM sessions WHERE id = ?', [body.sessionId])[0].values[0];
|
|
expect(String(sess[0])).toBe(String(userRow[0]));
|
|
});
|
|
|
|
it('returns 409 when the username is taken (case-insensitively)', async () => {
|
|
await post({ username: 'taken-user', password: 'correct-horse-1' });
|
|
const res = await post({ username: 'Taken-User', password: 'different-pass-2' });
|
|
expect(res.status).toBe(409);
|
|
expect(((await res.json()) as { error: string }).error).toMatch(/already taken/i);
|
|
});
|
|
});
|