deadhead-space-freight-tycoon/apps/server/test/lighter-clipper.test.ts

81 lines
3.3 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 { errorResponseSchema, shipResponseSchema } from "@deadhead/schema";
import { checkRoute, effectiveStats, gameToReal, realToGame, wouldStrand } from "@deadhead/sim";
import { eq } from "drizzle-orm";
import { describe, expect, it } from "vitest";
import { discoveredLocations, pilots, ships } from "../src/db/schema";
import { setCredits, TEST_NOW, useTestDatabase } from "./db";
import { call, getState, signUpOwner, useTestApp } from "./game";
const database = useTestDatabase();
const testApp = useTestApp(database);
describe("Lighter and Clipper (Step 10L.6)", () => {
for (const modelId of ["lighter", "clipper"]) {
it(`buys a ${modelId} at the chosen HQ`, async () => {
const { app } = testApp();
const owner = await signUpOwner(app, database().db);
const price = content.shipModel(modelId)!.price;
await setCredits(database().db, owner.playerId, price);
const response = await call(app, owner, "POST", "/api/ships", {
modelId,
bayId: owner.earthBayId,
});
expect(response.statusCode).toBe(200);
const { ship } = shipResponseSchema.parse(response.json());
expect(ship).toMatchObject({ modelId, status: "idle", locationId: "earth" });
expect((await getState(app, owner)).company.credits).toBe(0);
});
}
it("refuses a deadhead that would strand the ship, changing nothing", async () => {
const { app, clock } = testApp();
const owner = await signUpOwner(app, database().db);
const db = database().db;
await setCredits(db, owner.playerId, 100_000);
// A Clipper at the Moon, 1,420 game days in: Mars is in range from the Moon
// (198 × 1.3), but from Mars (198 × 1.15) nothing is when it gets there.
const departure = realToGame(TEST_NOW, content.config) + 1420 * 24 * 3_600_000;
clock.now = gameToReal(departure, content.config);
await db
.update(ships)
.set({ modelId: "clipper", locationId: "moon" })
.where(eq(ships.id, owner.shipId));
await db.update(pilots).set({ locationId: "moon" }).where(eq(pilots.id, owner.avatarId));
await db
.insert(discoveredLocations)
.values({ playerId: owner.playerId, locationId: "mars", discoveredAt: TEST_NOW });
// The scenario as the sim sees it.
const clipper = effectiveStats(content.shipModel("clipper")!, {}, content);
const [moon, mars] = [content.location("moon")!, content.location("mars")!];
expect(checkRoute(clipper, moon, mars, realToGame(clock.now, content.config)).ok).toBe(true);
expect(
wouldStrand(
clipper,
moon,
mars,
0,
realToGame(clock.now, content.config),
undefined,
content,
),
).toBe(true);
const before = await getState(app, owner);
const response = await call(app, owner, "POST", "/api/dispatches", {
kind: "reposition",
shipId: owner.shipId,
pilotId: owner.avatarId,
destinationId: "mars",
});
expect(response.statusCode).toBe(409);
expect(errorResponseSchema.parse(response.json()).error.code).toBe("would-strand");
const after = await getState(app, owner);
expect(after.company.credits).toBe(before.company.credits);
expect(after.ships).toEqual(before.ships);
expect(after.activeDispatches).toEqual([]);
});
});