import { randomUUID } from "node:crypto"; import { content } from "@deadhead/content"; import { playerStateSchema } from "@deadhead/schema"; import { pilotLevel } from "@deadhead/sim"; import { describe, expect, it } from "vitest"; import { pilots, ships } from "../src/db/schema"; import { makeOwner, setCredits, TEST_NOW, useTestDatabase } from "./db"; import { call, insertDispatch, signUp, useTestApp } from "./game"; const database = useTestDatabase(); const testApp = useTestApp(database); describe("full player state", () => { it("reflects one ship in flight and one idle, and validates against its schema", async () => { const { app, clock } = testApp(); const { db } = database(); const guest = await signUp(app); const flyingShipId = await makeOwner(db, guest.playerId, guest.avatarId); await setCredits(db, guest.playerId, 9000); // A second ship at the Moon crewed by a hired pilot with some XP and a skill. const idleShipId = randomUUID(); const hiredId = randomUUID(); await db.insert(ships).values({ id: idleShipId, playerId: guest.playerId, modelId: "freighter", name: "Slow Ox", locationId: "moon", createdAt: TEST_NOW, }); await db.insert(pilots).values({ id: hiredId, playerId: guest.playerId, name: "Rae Okafor", xp: 260, skills: { "heavy-cargo": 1 }, skillPointsSpent: 1, assignedShipId: idleShipId, createdAt: TEST_NOW, }); const record = await insertDispatch(db, { playerId: guest.playerId, shipId: flyingShipId, pilotId: guest.avatarId, departAt: clock.now, }); const response = await call(app, guest, "GET", "/api/state"); expect(response.statusCode).toBe(200); const state = playerStateSchema.parse(response.json()); expect(state.company.credits).toBe(9000); expect(state.tutorial).toBeNull(); expect(state.bays.map((b) => b.locationId)).toEqual(["earth"]); const flying = state.ships.find((s) => s.id === flyingShipId)!; const idle = state.ships.find((s) => s.id === idleShipId)!; expect(flying).toMatchObject({ status: "in-flight", locationId: null, dispatchId: record.id, pilotId: guest.avatarId, }); expect(idle).toMatchObject({ status: "idle", locationId: "moon", dispatchId: null, pilotId: hiredId, modelId: "freighter", }); const avatar = state.pilots.find((p) => p.id === guest.avatarId)!; const hired = state.pilots.find((p) => p.id === hiredId)!; expect(avatar).toMatchObject({ status: "in-flight", locationId: null, assignedShipId: flyingShipId, }); expect(hired).toMatchObject({ status: "idle", locationId: "moon", assignedShipId: idleShipId, xp: 260, level: pilotLevel(260, content.config), skills: { "heavy-cargo": 1 }, }); expect(hired.level).toBe(2); expect(hired.skillPointsAvailable).toBe(2 * content.config.skillPointsPerLevel - 1); expect(state.activeDispatches).toEqual([ expect.objectContaining({ id: record.id, status: "in-flight", arrivalAt: record.arrivalAt }), ]); expect(state.summaries).toEqual([]); expect(state.locations).toHaveLength(content.locations.length); }); });