deadhead-space-freight-tycoon/apps/server/test/state.test.ts

98 lines
3.2 KiB
TypeScript

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 { shipId: 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);
});
});