deadhead-space-freight-tycoon/packages/engine/test/start.test.ts

127 lines
4.3 KiB
TypeScript

import { content } from "@deadhead/content";
import { describe, expect, it } from "vitest";
import { GameEngine, MemoryBackend, SaveStorage, type RandomSource } from "../src/index";
import { companyName } from "../src/game/names";
import { TEST_NOW } from "./support";
/** Deterministic ids and seeds, so the starting state can be compared exactly. */
function fixedRandom(seed: number): RandomSource {
let n = 0;
return { id: () => `id-${++n}`, seed: () => seed };
}
function engineOn(backend: MemoryBackend, seed = 1234) {
return new GameEngine({
storage: new SaveStorage(backend),
clock: { now: () => TEST_NOW },
random: fixedRandom(seed),
});
}
describe("company creation on the device (Step 14.8)", () => {
it("starts from empty storage with exactly CR-001's starting state", async () => {
const backend = new MemoryBackend();
const engine = engineOn(backend);
await expect(engine.call("getPlayerState", {})).rejects.toMatchObject({ code: "unauthorized" });
const { state } = await engine.call("createGuest", {});
expect(state).toEqual({
playerId: "id-1",
company: {
id: "id-2",
name: companyName(1234),
credits: content.config.startingCredits,
loanBalance: 0,
},
ships: [],
pilots: [
{
id: "id-3",
name: "Captain",
isAvatar: true,
isVeteran: false,
xp: 0,
level: 0,
skills: {},
skillPointsAvailable: 0,
assignedShipId: null,
status: "idle",
locationId: "earth",
dutyHours: 0,
dutyLimit: content.config.dutyLimitGameHours,
resting: false,
restEndsAt: null,
restingInShopUntil: null,
mode: "manual",
orders: {
destinationIds: null,
cargoClassIds: content.cargoTypes.map((c) => c.id),
takePriority: true,
minProfit: 0,
homeLocationId: "earth",
},
},
],
bays: [],
locations: content.locations.map((l) => ({ id: l.id, discovered: l.startsDiscovered })),
tutorial: {
loanerLocationId: "earth",
runInFlight: false,
runsCompleted: 0,
dealAvailable: false,
},
activeDispatches: [],
summaries: [],
goals: [],
welcomeSeen: false,
dispatchOffice: {
owned: false,
spendingFloor: content.config.independentPilots.defaultSpendingFloor,
},
radioCalls: [],
nextRadioCallAt: null,
fleetSleep: {
afterHours: 24,
sleepsAt: TEST_NOW + 24 * 60 * 60_000,
asleep: false,
},
});
});
it("opens the same company after a reload", async () => {
const backend = new MemoryBackend();
const created = await engineOn(backend).call("createGuest", {});
// A new engine on the same storage: the app reloaded.
const reopened = engineOn(backend, 9999);
expect(await reopened.call("getPlayerState", {})).toEqual(created.state);
expect(await reopened.call("createGuest", {})).toEqual(created);
});
it("gives each new company its own world seed", async () => {
const a = await engineOn(new MemoryBackend(), 1).call("createGuest", {});
const b = await engineOn(new MemoryBackend(), 2).call("createGuest", {});
expect(a.state.company.name).toBe(companyName(1));
expect(b.state.company.name).toBe(companyName(2));
});
});
describe("welcome cards in the save (Step 22.1, decision 0.5.131)", () => {
it("starts a new company with its welcome not yet seen", async () => {
const backend = new MemoryBackend();
const engine = engineOn(backend);
await engine.call("createGuest", {});
const save = await new SaveStorage(backend).load();
if (save.status !== "loaded") throw new Error(save.status);
expect(save.save.player.welcomeSeen).toBe(false);
});
it("records the welcome as seen with markWelcomeSeen (Step 22.2)", async () => {
const backend = new MemoryBackend();
const engine = engineOn(backend);
expect((await engine.call("createGuest", {})).state.welcomeSeen).toBe(false);
expect((await engine.call("markWelcomeSeen", {})).welcomeSeen).toBe(true);
// A reload keeps it.
expect((await engineOn(backend).call("getPlayerState", {})).welcomeSeen).toBe(true);
});
});