100 lines
3.8 KiB
TypeScript
100 lines
3.8 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 { POST } from './route';
|
|
|
|
let dir: string;
|
|
|
|
beforeAll(async () => {
|
|
// Same pattern as app/api/state/route.test.ts: point the DB singleton at a
|
|
// throwaway file (fresh module registry per test file → fresh singleton).
|
|
dir = await fs.mkdtemp(path.join(os.tmpdir(), 'flight-sim-api-'));
|
|
process.env.FLIGHT_DB_PATH = path.join(dir, 'sim.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/sim', { method: 'POST', body: JSON.stringify(body) }));
|
|
const postRaw = (text: string) => POST(new Request('http://localhost/api/sim', { method: 'POST', body: text }));
|
|
|
|
describe('POST /api/sim', () => {
|
|
it('returns the current simState for an empty body (tick + no-op)', async () => {
|
|
const res = await post({});
|
|
expect(res.status).toBe(200);
|
|
const body = (await res.json()) as { clockMs: number; timeScale: number; paused: boolean };
|
|
expect(typeof body.clockMs).toBe('number');
|
|
expect(body.timeScale).toBe(1); // seeded default
|
|
expect(body.paused).toBe(false);
|
|
});
|
|
|
|
it('sets and clamps timeScale into [1, 1000]', async () => {
|
|
const hi = await post({ timeScale: 2000 });
|
|
expect(hi.status).toBe(200);
|
|
expect(((await hi.json()) as { timeScale: number }).timeScale).toBe(1000);
|
|
|
|
const lo = await post({ timeScale: 0.1 });
|
|
expect(lo.status).toBe(200);
|
|
expect(((await lo.json()) as { timeScale: number }).timeScale).toBe(1);
|
|
|
|
// The top of the ladder passes through unclamped (v0.14.0).
|
|
const max = await post({ timeScale: 1000 });
|
|
expect(max.status).toBe(200);
|
|
expect(((await max.json()) as { timeScale: number }).timeScale).toBe(1000);
|
|
|
|
// In-range values pass through unclamped — ladder and off-ladder alike.
|
|
const ladder = await post({ timeScale: 500 });
|
|
expect(ladder.status).toBe(200);
|
|
expect(((await ladder.json()) as { timeScale: number }).timeScale).toBe(500);
|
|
|
|
const mid = await post({ timeScale: 37 });
|
|
expect(mid.status).toBe(200);
|
|
expect(((await mid.json()) as { timeScale: number }).timeScale).toBe(37);
|
|
|
|
// Restore the default for later tests.
|
|
await post({ timeScale: 1 });
|
|
});
|
|
|
|
it('toggles paused and freezes the clock across ticks', async () => {
|
|
const pause = await post({ paused: true });
|
|
expect(pause.status).toBe(200);
|
|
expect(((await pause.json()) as { paused: boolean }).paused).toBe(true);
|
|
|
|
// Two reads while paused → identical clock (tick-on-read advances nothing).
|
|
const a = await post({});
|
|
const b = await post({});
|
|
expect(((await a.json()) as { clockMs: number }).clockMs).toBe(
|
|
((await b.json()) as { clockMs: number }).clockMs,
|
|
);
|
|
|
|
const resume = await post({ paused: false });
|
|
expect(resume.status).toBe(200);
|
|
expect(((await resume.json()) as { paused: boolean }).paused).toBe(false);
|
|
});
|
|
|
|
it('applies both fields in one call', async () => {
|
|
const res = await post({ timeScale: 64, paused: true });
|
|
expect(res.status).toBe(200);
|
|
const body = (await res.json()) as { timeScale: number; paused: boolean };
|
|
expect(body.timeScale).toBe(64);
|
|
expect(body.paused).toBe(true);
|
|
await post({ timeScale: 1, paused: false }); // restore
|
|
});
|
|
|
|
it('rejects invalid input with 400', async () => {
|
|
for (const body of [{ timeScale: 'fast' }, { timeScale: Infinity }, { paused: 'yes' }, { bogus: 1 }]) {
|
|
const res = await post(body);
|
|
expect(res.status).toBe(400);
|
|
expect(((await res.json()) as { error: string }).error).toBeTruthy();
|
|
}
|
|
|
|
for (const text of ['nope{', '[1,2]', '"str"', 'null']) {
|
|
const res = await postRaw(text);
|
|
expect(res.status).toBe(400);
|
|
}
|
|
});
|
|
});
|