deadhead-space-freight-tycoon/packages/engine/test/ported/stranded.test.ts

97 lines
3.1 KiB
TypeScript

// Ported from apps/server/test (CR-014 Step 14.5): now runs on the engine.
import {
contractBoardResponseSchema,
dispatchResponseSchema,
type Contract,
} from "@deadhead/schema";
import { describe, expect, it } from "vitest";
import {
useTestApp,
call,
getState,
setCredits,
testContract,
insertDispatch,
signUpOwner,
type Guest,
} from "../support";
const testApp = useTestApp();
/** An owner whose Courier has just delivered Earth→Moon; there is no Moon bay. */
async function landedAtMoon(): Promise<Guest & { shipId: string }> {
const { app, clock } = testApp();
const owner = await signUpOwner(app);
await setCredits(testApp().app, 5000);
const record = await insertDispatch(testApp().app, {
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",
});
});
});