// Shared fixture suite for the game tests (save.test.ts, the 5a route tests, and the later // ship/research route tests): one deterministic world — seed '42', save stamped at the // current schema version so no backfill runs — plus the crew and task-row minters the // queueing tests use. Ids line up across worlds built in fresh files (same insert order), // so two fixture worlds are comparable row for row. import type { GameDb } from '@/lib/db/client' import * as schema from '@/lib/db/schema' import { SCHEMA_VERSION } from './save' // Fixed so a fixture's event rows carry a stable created_at (tests never assert on it, but // a wall-clock Date would make two fixture worlds differ byte-for-byte). export const T0 = '2026-10-01T00:00:00.000Z' // Origin (s0, id 1, current) — Outpost (s1, one hop: the jump target) — Farpoint (s2, two // hops: beyond one-hop range, so the unreachable pre-check has a target to 409 on; with // NAVIGATION AI researched it would be reachable). p0 'Alder' in s0, p1 'Basalt' in s1. // No crew, tasks, ship, or tech rows — the caller mints exactly what a test needs. export async function buildWorldFixture( db: GameDb, outpostDiscovered = false, ): Promise<{ sys1: number; p0: number; p1: number; farSystem: number }> { const [save] = await db.db .insert(schema.saves) .values({ seed: '42', turn: 0, createdAt: T0, schemaVersion: SCHEMA_VERSION }) .returning() const [s0] = await db.db .insert(schema.solarSystems) .values({ saveId: save.id, name: 'Origin', x: 50, y: 50, discovered: true, isCurrent: true }) .returning({ id: schema.solarSystems.id }) const [s1] = await db.db .insert(schema.solarSystems) .values({ saveId: save.id, name: 'Outpost', x: 55, y: 50, discovered: outpostDiscovered }) .returning({ id: schema.solarSystems.id }) const [s2] = await db.db .insert(schema.solarSystems) .values({ saveId: save.id, name: 'Farpoint', x: 60, y: 50, discovered: false }) .returning({ id: schema.solarSystems.id }) await db.db.insert(schema.jumpRoutes).values({ fromSystem: s0.id, toSystem: s1.id }) await db.db.insert(schema.jumpRoutes).values({ fromSystem: s1.id, toSystem: s0.id }) await db.db.insert(schema.jumpRoutes).values({ fromSystem: s1.id, toSystem: s2.id }) await db.db.insert(schema.jumpRoutes).values({ fromSystem: s2.id, toSystem: s1.id }) const [p0] = await db.db .insert(schema.planets) .values({ systemId: s0.id, parentId: null, name: 'Alder', type: 'ROCKY', hazard: false }) .returning({ id: schema.planets.id }) const [p1] = await db.db .insert(schema.planets) .values({ systemId: s1.id, parentId: null, name: 'Basalt', type: 'GAS', hazard: false }) .returning({ id: schema.planets.id }) return { sys1: s1.id, p0: p0.id, p1: p1.id, farSystem: s2.id } } // `count` identical crew members, in insertion order — the queueing tests need real crew // ids to lead and help with. export async function addCrew(db: GameDb, count: number): Promise { const [save] = await db.db.select().from(schema.saves).limit(1) const ids: number[] = [] for (let i = 0; i < count; i++) { const [row] = await db.db .insert(schema.crew) .values({ saveId: save.id, name: `CREW ${i + 1}`, role: 'PILOT', traitGood: 'QUICK HANDS', traitBad: 'OVERCAUTIOUS', origin: 'STARTING CREW' }) .returning({ id: schema.crew.id }) ids.push(row.id) } return ids } // Mint one tasks-table row with a chosen state — the queueing/tick tests seed the queue // directly instead of going through the routes (which would run the same validation). export async function insertTaskRow( db: GameDb, over: { type: string; payload: object; leadCrewId: number; helpers: number[]; duration: number; remaining: number; status: string; queuedAtTurn: number }, ): Promise { const [save] = await db.db.select().from(schema.saves).limit(1) const [row] = await db.db .insert(schema.tasks) .values({ saveId: save.id, type: over.type, payload: JSON.stringify(over.payload), leadCrewId: over.leadCrewId, helpers: JSON.stringify(over.helpers), duration: over.duration, remaining: over.remaining, status: over.status, queuedAtTurn: over.queuedAtTurn }) .returning({ id: schema.tasks.id }) return row.id }