// Ported from apps/server/test (CR-014 Step 14.5): now runs on the engine. import { content, upgradeMaxLevel } from "@deadhead/content"; import { companyResponseSchema, errorResponseSchema, playerStateSchema } from "@deadhead/schema"; import { describe, expect, it } from "vitest"; import { newTestApp, useTestApp, signUp, call, getState, edit, testContract, insertDispatch, signUpOwner, } from "../support"; const testApp = useTestApp(); describe("developer tools (Step 10K.11)", () => { it("advances the server clock", async () => { const guest = await signUp(testApp().app); const before = ( (await call(testApp().app, guest, "GET", "/api/time")).json() as { realTime: number } ).realTime; const response = await call(testApp().app, guest, "POST", "/api/test/advance-clock", { advanceMs: 600_000, }); expect(response.statusCode).toBe(200); const after = ( (await call(testApp().app, guest, "GET", "/api/time")).json() as { realTime: number } ).realTime; expect(after - before).toBeGreaterThanOrEqual(600_000); }); it("adds credits", async () => { const guest = await signUp(testApp().app); const start = (await getState(testApp().app, guest)).company.credits; const response = await call(testApp().app, guest, "POST", "/api/test/add-credits", { amount: 10_000, }); expect(companyResponseSchema.parse(response.json()).company.credits).toBe(start + 10_000); }); it("lands every flight when advanced to the last arrival", async () => { const { app, clock } = testApp(); const owner = await signUpOwner(app); const flight = await insertDispatch(testApp().app, { shipId: owner.shipId, pilotId: owner.avatarId, departAt: clock.now, contract: testContract(), }); expect((await getState(app, owner)).activeDispatches).toHaveLength(1); await call(app, owner, "POST", "/api/test/advance-clock", { advanceMs: flight.arrivalAt - clock.now + 1000, }); expect((await getState(app, owner)).activeDispatches).toHaveLength(0); }); it("does not exist in production", async () => { // A production build: developer commands are off. const { app } = newTestApp({ devTools: false }); await signUp(app); for (const [url, payload] of [ ["/api/test/advance-clock", { advanceMs: 1000 }], ["/api/test/add-credits", { amount: 10_000 }], ["/api/test/pilot", { pilotId: "p", action: "level-up" }], ["/api/test/ship", { shipId: "s", action: "max-upgrades" }], ["/api/test/discover-all", {}], ] as const) { const response = await call(app, null, "POST", url, payload); expect(response.statusCode, url).toBe(404); } }); it("levels a pilot up to the next level's XP, and clears duty and rest", async () => { const { app, clock } = testApp(); const owner = await signUpOwner(app); await edit(app, (save) => { Object.assign( save.pilots.find((p) => p.id === owner.avatarId)!, { xp: 40, dutyHours: 9, restEndsAt: clock.now + 3_600_000, }, ); }); const pilotAfter = async (action: string) => { const response = await call(app, owner, "POST", "/api/test/pilot", { pilotId: owner.avatarId, action, }); expect(response.statusCode).toBe(200); return playerStateSchema.parse(response.json()).pilots.find((p) => p.id === owner.avatarId)!; }; const levelled = await pilotAfter("level-up"); expect(levelled.xp).toBe(content.config.pilotLevelXpThresholds[0]); expect(levelled.level).toBe(1); expect(levelled.skillPointsAvailable).toBe(content.config.skillPointsPerLevel); expect((await pilotAfter("level-up")).level).toBe(2); const rested = await pilotAfter("clear-rest"); expect(rested).toMatchObject({ dutyHours: 0, resting: false, restEndsAt: null }); }); it("maxes and resets a ship's upgrades, and finishes shop work", async () => { const { app, clock } = testApp(); const owner = await signUpOwner(app); const ship = async (input: Record) => { const response = await call(app, owner, "POST", "/api/test/ship", { shipId: owner.shipId, ...input, }); expect(response.statusCode).toBe(200); return playerStateSchema.parse(response.json()).ships.find((s) => s.id === owner.shipId)!; }; const maxed = await ship({ action: "max-upgrades" }); for (const upgrade of content.upgrades) { expect(maxed.upgrades[upgrade.id], upgrade.id).toBe(upgradeMaxLevel(upgrade, "courier")); } expect(maxed.stats.tankCapacityFu).toBeGreaterThan( content.shipModel("courier")!.tankCapacityFu, ); expect((await ship({ action: "reset-upgrades" })).upgrades).toEqual({}); await edit(app, (save) => { Object.assign( save.ships.find((s) => s.id === owner.shipId)!, { inShopUntil: clock.now + 600_000, shopUpgradeId: "engines", shopLevel: 1, }, ); }); expect((await ship({ action: "finish-shop" })).inShopUntil).toBeNull(); }); it("moves an idle ship to a discovered place only", async () => { const { app } = testApp(); const owner = await signUpOwner(app); const move = (locationId: string) => call(app, owner, "POST", "/api/test/ship", { shipId: owner.shipId, action: "move", locationId, }); const moved = await move("moon"); expect(moved.statusCode).toBe(200); expect(playerStateSchema.parse(moved.json()).ships[0]!.locationId).toBe("moon"); const refused = await move("mars"); expect(refused.statusCode).toBe(409); expect(errorResponseSchema.parse(refused.json()).error.code).toBe("location-undiscovered"); }); it("discovers every location", async () => { const { app } = testApp(); const owner = await signUpOwner(app); const response = await call(app, owner, "POST", "/api/test/discover-all", {}); const { locations } = playerStateSchema.parse(response.json()); expect(locations.every((l) => l.discovered)).toBe(true); expect(locations).toHaveLength(content.locations.length); }); });