import { contractBoardResponseSchema, dispatchResponseSchema, type Contract, } from "@deadhead/schema"; import { describe, expect, it } from "vitest"; import { setCredits, useTestDatabase } from "./db"; import { call, getState, insertDispatch, signUpOwner, testContract, useTestApp, type Guest, } from "./game"; const database = useTestDatabase(); const testApp = useTestApp(database); /** An owner whose Courier has just delivered Earth→Moon; there is no Moon bay. */ async function landedAtMoon(): Promise { const { app, clock } = testApp(); const owner = await signUpOwner(app, database().db); await setCredits(database().db, owner.playerId, 5000); const record = await insertDispatch(database().db, { playerId: owner.playerId, shipId: owner.shipId, pilotId: owner.avatarId, departAt: clock.now, contract: testContract({ payout: 1000 }), }); clock.now = record.arrivalAt; const state = await getState(app, owner); expect(state.ships[0]).toMatchObject({ status: "idle", locationId: "moon" }); expect(state.bays.map((b) => b.locationId)).toEqual(["earth"]); return owner; } /** A Moon→Earth job the Courier can fly with no skills. */ function courierJobHome(contracts: Contract[]): Contract { const job = contracts.find( (c) => c.destinationId === "earth" && c.massTonnes <= 20 && c.requiredSkill === null && c.requiredLongHaulLevel === 0, ); if (!job) throw new Error("no Courier job home on the Moon board"); return job; } describe("no more stranded ships (the original bug)", () => { it("lets a ship that delivered to the Moon, with no Moon bay, take a Moon→Earth job home", async () => { const { app, clock } = testApp(); const owner = await landedAtMoon(); const board = contractBoardResponseSchema.parse( (await call(app, owner, "GET", "/api/locations/moon/board")).json(), ); const job = courierJobHome(board.contracts); const response = await call(app, owner, "POST", "/api/dispatches", { kind: "contract", contractId: job.id, shipId: owner.shipId, pilotId: owner.avatarId, }); expect(response.statusCode).toBe(200); const { dispatch } = dispatchResponseSchema.parse(response.json()); clock.now = dispatch.arrivalAt; const state = await getState(app, owner); expect(state.ships[0]).toMatchObject({ status: "idle", locationId: "earth" }); expect(state.summaries.find((s) => s.dispatchId === dispatch.id)).toMatchObject({ originId: "moon", destinationId: "earth", }); }); it("lets the same ship deadhead Moon→Earth instead", async () => { const { app, clock } = testApp(); const owner = await landedAtMoon(); const response = await call(app, owner, "POST", "/api/dispatches", { kind: "reposition", shipId: owner.shipId, pilotId: owner.avatarId, destinationId: "earth", }); expect(response.statusCode).toBe(200); const { dispatch } = dispatchResponseSchema.parse(response.json()); clock.now = dispatch.arrivalAt; expect((await getState(app, owner)).ships[0]).toMatchObject({ status: "idle", locationId: "earth", }); }); });