import { describe, expect, it } from "vitest"; import { shipModelSchema, shipModels, gameConfig } from "../src/index"; const ship = (id: string) => shipModels.find((s) => s.id === id)!; describe("ship models", () => { it("all pass the schema", () => { for (const model of shipModels) { expect(() => shipModelSchema.parse(model)).not.toThrow(); } }); // CR-012 ยง2 replaces the Step 2.3 ordering tests. const order = ( key: "price" | "cruiseSpeedKmPerGameHour" | "cargoCapacityTonnes", ids: string[], ) => ids .slice(1) .forEach((id, i) => expect(ship(id)[key], `${ids[i]} < ${id}`).toBeGreaterThan(ship(ids[i]!)[key]), ); const purchasable = shipModels.filter((m) => m.purchasable); const handling = (id: string) => ship(id).handlingGameHoursPerTonne ?? gameConfig.handlingGameHoursPerTonne; it("has five purchasable models plus the loaner, all atmospheric", () => { expect(shipModels.map((s) => s.id)).toEqual([ "courier", "lighter", "clipper", "freighter", "bulk-hauler", "company-courier", ]); for (const model of shipModels) { expect(model.landingClass).toBe("atmospheric"); expect(model.role.length).toBeGreaterThan(10); } expect(ship("lighter").purchasable && ship("clipper").purchasable).toBe(true); }); it("orders price Courier < Lighter < Clipper < Freighter < Bulk Hauler", () => { order("price", ["courier", "lighter", "clipper", "freighter", "bulk-hauler"]); }); it("makes the Clipper fastest, the Bulk Hauler slowest, and the Lighter slower than the Freighter", () => { const speeds = shipModels.map((m) => m.cruiseSpeedKmPerGameHour); expect(ship("clipper").cruiseSpeedKmPerGameHour).toBe(Math.max(...speeds)); expect(ship("bulk-hauler").cruiseSpeedKmPerGameHour).toBe( Math.min(...purchasable.map((m) => m.cruiseSpeedKmPerGameHour)), ); order("cruiseSpeedKmPerGameHour", ["bulk-hauler", "lighter", "freighter"]); }); it("orders capacity Courier < Clipper < Lighter < Freighter < Bulk Hauler", () => { order("cargoCapacityTonnes", ["courier", "clipper", "lighter", "freighter", "bulk-hauler"]); const lighterShare = ship("lighter").cargoCapacityTonnes / ship("freighter").cargoCapacityTonnes; expect(lighterShare).toBeGreaterThanOrEqual(0.75); expect(lighterShare).toBeLessThanOrEqual(0.9); }); it("gives the Lighter the fastest loading and the Clipper a thirsty burn rate", () => { for (const m of shipModels) { if (m.id !== "lighter") expect(handling("lighter")).toBeLessThan(handling(m.id)); } expect(ship("clipper").burnFuPerMillionKm).toBeGreaterThan(ship("courier").burnFuPerMillionKm); expect(ship("clipper").burnFuPerMillionKm).toBeGreaterThan( ship("freighter").burnFuPerMillionKm, ); }); it("gives every ship model its own map sprite key (CR-003)", () => { const sprites = shipModels.map((x) => x.sprite); for (const sprite of sprites) expect(sprite).toMatch(/^[a-z][a-z-]*$/); expect(new Set(sprites).size).toBe(sprites.length); }); });