deadhead-space-freight-tycoon/packages/engine/test/independent-commands.test.ts

156 lines
5.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { content } from "@deadhead/content";
import { describe, expect, it } from "vitest";
import { encodeSave } from "../src/index";
import {
addPilot,
edit,
getState,
readSave,
setCredits,
signUp,
signUpOwner,
useTestApp,
} from "./support";
const testApp = useTestApp();
const PRICE = content.config.independentPilots.dispatchOfficePrice;
/** An owner with a hired pilot (unassigned, at Earth). */
async function ownerWithPilot() {
const { app } = testApp();
const owner = await signUpOwner(app);
await addPilot(app, { id: "p1", name: "Ada", locationId: "earth" });
return { app, owner };
}
describe("Dispatch Office, pilot modes and orders (Step 21.3, decisions 0.5.122–0.5.124)", () => {
it("refuses Independent before the Dispatch Office, and allows it after", async () => {
const { app } = await ownerWithPilot();
await expect(
app.call("setPilotMode", { pilotId: "p1", mode: "independent" }),
).rejects.toMatchObject({ code: "dispatch-office-required" });
await setCredits(app, PRICE + 10);
const bought = await app.call("buyDispatchOffice", {});
expect(bought.dispatchOffice.owned).toBe(true);
expect(bought.company.credits).toBe(10);
const { pilot } = await app.call("setPilotMode", { pilotId: "p1", mode: "independent" });
expect(pilot.mode).toBe("independent");
expect((await getState(app)).pilots.find((p) => p.id === "p1")!.mode).toBe("independent");
// And back to Manual.
expect((await app.call("setPilotMode", { pilotId: "p1", mode: "manual" })).pilot.mode).toBe(
"manual",
);
});
it("sells the Dispatch Office once, for its price, and not during the tutorial", async () => {
const { app } = testApp();
await signUp(app);
await setCredits(app, PRICE);
await expect(app.call("buyDispatchOffice", {})).rejects.toMatchObject({
code: "tutorial-active",
});
const owner = await ownerWithPilot();
await setCredits(owner.app, PRICE - 1);
await expect(owner.app.call("buyDispatchOffice", {})).rejects.toMatchObject({
code: "insufficient-funds",
});
await setCredits(owner.app, PRICE * 2);
await owner.app.call("buyDispatchOffice", {});
await expect(owner.app.call("buyDispatchOffice", {})).rejects.toMatchObject({
code: "dispatch-office-owned",
});
expect((await getState(owner.app)).company.credits).toBe(PRICE);
});
it("never makes the avatar Independent", async () => {
const { app, owner } = await ownerWithPilot();
await setCredits(app, PRICE);
await app.call("buyDispatchOffice", {});
await expect(
app.call("setPilotMode", { pilotId: owner.avatarId, mode: "independent" }),
).rejects.toMatchObject({ code: "avatar-not-independent" });
});
it("saves and loads a pilot's orders, checking them against content and bays", async () => {
const { app } = await ownerWithPilot();
const orders = {
destinationIds: ["moon", "mars"],
cargoClassIds: ["standard", "heavy"],
takePriority: false,
minProfit: 250,
homeLocationId: "earth",
};
expect((await app.call("setPilotOrders", { pilotId: "p1", orders })).pilot.orders).toEqual(
orders,
);
expect((await getState(app)).pilots.find((p) => p.id === "p1")!.orders).toEqual(orders);
expect((await readSave(app)).pilots.find((p) => p.id === "p1")!.orders).toEqual(orders);
await expect(
app.call("setPilotOrders", {
pilotId: "p1",
orders: { ...orders, destinationIds: ["pluto"] },
}),
).rejects.toMatchObject({ code: "unknown-location" });
await expect(
app.call("setPilotOrders", { pilotId: "p1", orders: { ...orders, cargoClassIds: ["gas"] } }),
).rejects.toMatchObject({ code: "unknown-cargo-class" });
await expect(
app.call("setPilotOrders", { pilotId: "p1", orders: { ...orders, homeLocationId: "mars" } }),
).rejects.toMatchObject({ code: "no-bay-at-location" });
});
it("starts pilots on Manual with default orders, home where they were hired", async () => {
const { app } = testApp();
const owner = await signUpOwner(app);
await edit(app, (save) => save.bays.push({ id: "b2", locationId: "moon", createdAt: 0 }));
await setCredits(app, content.config.pilotHireCost);
const { pilot } = await app.call("hirePilot", { locationId: "moon" });
expect(pilot.mode).toBe("manual");
expect(pilot.orders).toEqual({
destinationIds: null,
cargoClassIds: content.cargoTypes.map((c) => c.id),
takePriority: true,
minProfit: 0,
homeLocationId: "moon",
});
const avatar = (await getState(app)).pilots.find((p) => p.id === owner.avatarId)!;
expect(avatar.orders.homeLocationId).toBe("earth");
});
it("sets the spending floor", async () => {
const { app } = await ownerWithPilot();
expect((await getState(app)).dispatchOffice).toEqual({
owned: false,
spendingFloor: content.config.independentPilots.defaultSpendingFloor,
});
const result = await app.call("setSpendingFloor", { credits: 2_000 });
expect(result.dispatchOffice.spendingFloor).toBe(2_000);
expect((await getState(app)).dispatchOffice.spendingFloor).toBe(2_000);
});
it("opens a save from before CR-021 with every pilot Manual and no Dispatch Office", async () => {
const { app } = await ownerWithPilot();
const save = await readSave(app);
const old = {
...save,
saveVersion: 4,
company: { id: save.company.id, name: save.company.name, credits: 5, loanBalance: 0 },
pilots: save.pilots.map(({ mode: _mode, orders: _orders, ...rest }) => rest),
};
const device = testApp().app;
const { state } = await device.call("importSave", { code: await encodeSave(old as never) });
expect(state.dispatchOffice).toEqual({
owned: false,
spendingFloor: content.config.independentPilots.defaultSpendingFloor,
});
for (const pilot of state.pilots) {
expect(pilot.mode).toBe("manual");
expect(pilot.orders.homeLocationId).toBe("earth");
}
});
});