import { content } from "@deadhead/content"; import { contractBoardResponseSchema, errorResponseSchema } from "@deadhead/schema"; import { boardPeriodMs } from "@deadhead/sim"; import { describe, expect, it } from "vitest"; import { discoveredLocations } from "../src/db/schema"; import { makeOwner, TEST_NOW, useTestDatabase } from "./db"; import { call, signUp, useTestApp, type Guest } from "./game"; const database = useTestDatabase(); const testApp = useTestApp(database); async function owner(): Promise { const guest = await signUp(testApp().app); await makeOwner(database().db, guest.playerId, guest.avatarId); return guest; } async function earthBoard(guest: Guest) { const response = await call(testApp().app, guest, "GET", "/api/bays/earth/board"); expect(response.statusCode).toBe(200); return contractBoardResponseSchema.parse(response.json()); } describe("bay contract board", () => { it("returns a board of the configured size for an owned bay", async () => { const guest = await owner(); const { contracts, periodEndsAt } = await earthBoard(guest); expect(contracts).toHaveLength(content.config.contractBoardSize); for (const c of contracts) expect(c.originId).toBe("earth"); expect(periodEndsAt).toBeGreaterThan(testApp().clock.now); }); it("refuses a location where the player has no bay", async () => { const { app } = testApp(); const guest = await owner(); const response = await call(app, guest, "GET", "/api/bays/moon/board"); expect(response.statusCode).toBe(409); expect(errorResponseSchema.parse(response.json()).error.code).toBe("no-bay-at-location"); }); it("returns the same stored board within a period, even after a discovery", async () => { const { clock } = testApp(); const guest = await owner(); const first = await earthBoard(guest); await database() .db.insert(discoveredLocations) .values( ["mars", "ceres-station", "jupiter-cloud-platform"].map((locationId) => ({ playerId: guest.playerId, locationId, discoveredAt: TEST_NOW, })), ); clock.advance(boardPeriodMs(content.config) / 2 - 1); expect((await earthBoard(guest)).contracts).toEqual(first.contracts); }); it("returns a new board after the period rolls over", async () => { const { clock } = testApp(); const guest = await owner(); const first = await earthBoard(guest); clock.now = first.periodEndsAt; const next = await earthBoard(guest); expect(next.periodEndsAt).toBe(first.periodEndsAt + boardPeriodMs(content.config)); expect(next.contracts.map((c) => c.id)).not.toEqual(first.contracts.map((c) => c.id)); for (const c of next.contracts) expect(c.expiresAt).toBe(next.periodEndsAt); }); });