import fs from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; import { eq } from 'drizzle-orm'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import * as schema from '@/lib/db/schema'; import { getGameDb } from '@/lib/db/client'; 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-accept-api-')); process.env.FLIGHT_DB_PATH = path.join(dir, 'accept.db'); }); afterAll(async () => { delete process.env.FLIGHT_DB_PATH; await fs.rm(dir, { recursive: true, force: true }); }); const post = (id: string) => POST(new Request(`http://localhost/api/contracts/${id}/accept`), { params: Promise.resolve({ id }) }); describe('POST /api/contracts/:id/accept', () => { it('returns the full state snapshot with the contract accepted', async () => { const { db } = await getGameDb(); const open = (await db.select().from(schema.contracts)).find((c) => c.status === 'open')!; const res = await post(open.id); expect(res.status).toBe(200); expect(res.headers.get('content-type')).toContain('application/json'); // Response convention: the FULL snapshot, same shape as GET /api/state. const body = (await res.json()) as { account: { cash: number }; simState: { clockMs: number; timeScale: number; paused: boolean }; planes: Array>; contracts: Array<{ id: string; status: string }>; }; // v0.10.0: the snapshot also carries the static market reference data. expect(Object.keys(body).sort()).toEqual([ 'account', 'catalog', 'contracts', 'fuelPricePerL', 'planes', 'sellFraction', 'simState', ]); expect(body.contracts.find((c) => c.id === open.id)?.status).toBe('accepted'); // Persisted, not just in the response. const row = (await db.select().from(schema.contracts).where(eq(schema.contracts.id, open.id)))[0]; expect(row.status).toBe('accepted'); }); it('rejects a non-open contract with 409', async () => { const { db } = await getGameDb(); // Deterministic regardless of which contract the first test accepted. await db.update(schema.contracts).set({ status: 'completed' }).where(eq(schema.contracts.id, 'contract-2')); const res = await post('contract-2'); expect(res.status).toBe(409); expect(((await res.json()) as { error: string }).error).toMatch(/completed/); }); it('rejects an unknown id with 404', async () => { const res = await post('nope'); expect(res.status).toBe(404); expect(((await res.json()) as { error: string }).error).toMatch(/not found/); }); });