81 lines
3.3 KiB
TypeScript
81 lines
3.3 KiB
TypeScript
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([]);
|
||
});
|
||
});
|