import { describe, expect, it } from "vitest"; import { cargoTypes, gameConfig, gameConfigSchema, locations, shipModels, skills, } from "../src/index"; // The Moon sits a fixed 0.384M km from Earth (Step 3.3). const EARTH_MOON_MILLION_KM = 0.384; describe("game config", () => { it("passes the schema", () => { expect(() => gameConfigSchema.parse(gameConfig)).not.toThrow(); }); it("uses 1 real minute = 1 game hour", () => { expect(gameConfig.timeCompression).toBe(60); }); it("has one Long Haul cut-off per Long Haul level", () => { const longHaul = skills.find((s) => s.id === "long-haul")!; expect(gameConfig.longHaulCutoffsMillionKm).toHaveLength(longHaul.maxLevel); }); it("covers fuel for at least three Courier runs from Earth after the deal (no soft-lock)", () => { // Amended Step 2.5 (CR-001). Assume the player always flies the lowest-paying // (light) tutorial contract; Phase 12 replaces these with measured numbers. const courier = shipModels.find((s) => s.id === "courier")!; const earth = locations.find((l) => l.id === "earth")!; const standard = cargoTypes.find((c) => c.id === "standard")!; const lightTutorialPayout = standard.basePayPerTonnePerMillionKm * gameConfig.tutorialMassTonnes.light * EARTH_MOON_MILLION_KM * gameConfig.tutorialPayoutShare; const earned = lightTutorialPayout * gameConfig.tutorialRunsBeforeDeal + gameConfig.startingCredits; const fuelFromEarth = EARTH_MOON_MILLION_KM * courier.fuelCostPerMillionKm * earth.launchCostMultiplier; expect(earned).toBeGreaterThanOrEqual(3 * fuelFromEarth); }); it("sizes the tutorial board and loaner for the three mass tiers", () => { expect(gameConfig.tutorialBoardSize).toBe(3); const loaner = shipModels.find((s) => s.id === gameConfig.tutorialLoanerShipModelId)!; expect(loaner.purchasable).toBe(false); expect(gameConfig.tutorialMassTonnes.full).toBeLessThanOrEqual(loaner.cargoCapacityTonnes); }); it("lends the Courier price plus an Earth bay lease in the deal", () => { const courier = shipModels.find((s) => s.id === "courier")!; const earth = locations.find((l) => l.id === "earth")!; expect(gameConfig.dealLoanAmount).toBeGreaterThan(0); expect(gameConfig.dealLoanAmount).toBe(courier.price + earth.bayLeasePrice); }); it("has a contract mass band each purchasable ship can carry", () => { for (const ship of shipModels.filter((s) => s.purchasable)) { expect( gameConfig.contractMassBands.some((band) => band.max <= ship.cargoCapacityTonnes), ).toBe(true); } const total = gameConfig.contractMassBands.reduce((sum, b) => sum + b.count, 0); expect(total).toBe(gameConfig.contractBoardSize); }); it("rests a pilot for at least one contract board period of real time (CR-004)", () => { const restRealMinutes = (gameConfig.restGameHours * 60) / gameConfig.timeCompression; expect(restRealMinutes).toBeGreaterThanOrEqual(gameConfig.contractBoardPeriodRealMinutes); }); it("has positive duty numbers (CR-004)", () => { expect(gameConfig.dutyLimitGameHours).toBeGreaterThan(0); expect(gameConfig.longHaulDutyBonusGameHours).toBeGreaterThan(0); }); });