import { describe, expect, it } from "vitest"; import { cargoTypes, goods, goodSchema, goodsDemandedAt, goodsProducedAt, locations, shipModels, } from "../src/index"; import { gameConfig } from "../src/data/config"; const classIds = cargoTypes.map((c) => c.id); const locationIds = locations.map((l) => l.id); const standardOn = (from: string, to: string) => goods.filter( (g) => g.cargoClassId === "standard" && g.producedAt.includes(from) && g.demandedAt.includes(to), ); describe("goods catalog (CR-007)", () => { it("has unique ids and every good passes the schema", () => { expect(new Set(goods.map((g) => g.id)).size).toBe(goods.length); for (const good of goods) expect(() => goodSchema.parse(good)).not.toThrow(); }); it("references existing classes and locations, with min ≤ max", () => { for (const good of goods) { expect(classIds).toContain(good.cargoClassId); for (const id of [...good.producedAt, ...good.demandedAt]) expect(locationIds).toContain(id); expect(good.massTonnes.min).toBeLessThanOrEqual(good.massTonnes.max); } }); it("gives High-Value and Dangerous goods a tier 1–5, and Standard and Heavy goods none", () => { for (const good of goods) { if (good.cargoClassId === "high-value" || good.cargoClassId === "dangerous") { expect(good.tier).toBeGreaterThanOrEqual(1); expect(good.tier).toBeLessThanOrEqual(5); } else { expect(good.tier).toBeUndefined(); } } }); it("has every location produce at least one Standard good", () => { for (const id of locationIds) { expect(goods.some((g) => g.cargoClassId === "standard" && g.producedAt.includes(id))).toBe( true, ); } }); it("has at least two Standard goods each way between Earth and the Moon (tutorial)", () => { expect(standardOn("earth", "moon").length).toBeGreaterThanOrEqual(2); expect(standardOn("moon", "earth").length).toBeGreaterThanOrEqual(2); }); it("offers variety on the early route: ≥ 5 Standard goods Earth→Moon, ≥ 4 back (CR-008)", () => { expect(standardOn("earth", "moon").length).toBeGreaterThanOrEqual(5); expect(standardOn("moon", "earth").length).toBeGreaterThanOrEqual(4); expect(gameConfig.contractPayVariance).toBeGreaterThan(0); expect(gameConfig.contractPayVariance).toBeLessThan(1); }); it("keeps each location's produces and demands in sync with the catalog", () => { for (const location of locations) { expect(location.produces).toEqual(goodsProducedAt(location.id)); expect(location.demands).toEqual(goodsDemandedAt(location.id)); } }); it("sets Priority to a real ship model and sensible numbers", () => { expect(shipModels.map((s) => s.id)).toContain(gameConfig.priorityReferenceShipModelId); expect(gameConfig.priorityChance).toBe(0.15); expect(gameConfig.priorityPayoutMultiplier).toBeGreaterThan(1); expect(goods.some((g) => g.priorityEligible)).toBe(true); }); }); describe("goods for Venus, Io and Europa (Step 16.2, decision 0.5.103)", () => { const newPlaces = ["venus-cloud-city", "io-sulfur-works", "europa-ice-station"]; it("adds the five new goods with the CR's classes, tiers and routes", () => { const good = (id: string) => goods.find((g) => g.id === id)!; expect(good("cloud-botanicals")).toMatchObject({ cargoClassId: "standard", priorityEligible: true, producedAt: ["venus-cloud-city"], }); expect(good("atmo-data-cores")).toMatchObject({ cargoClassId: "high-value", tier: 2 }); expect(good("sulfur")).toMatchObject({ cargoClassId: "heavy", producedAt: ["io-sulfur-works"], }); expect(good("sulfur").tier).toBeUndefined(); expect(good("volcanic-isotopes")).toMatchObject({ cargoClassId: "high-value", tier: 3 }); expect(good("ocean-samples")).toMatchObject({ cargoClassId: "high-value", tier: 3, priorityEligible: true, producedAt: ["europa-ice-station"], }); }); it("has every location produce a Standard good and demand at least one good", () => { for (const location of locations) { const standard = goods.filter( (g) => g.cargoClassId === "standard" && g.producedAt.includes(location.id), ); expect(standard.length, location.id).toBeGreaterThan(0); expect( goods.some((g) => g.demandedAt.includes(location.id)), location.id, ).toBe(true); } }); it("makes each new place a destination for at least five goods", () => { for (const id of newPlaces) { expect(goods.filter((g) => g.demandedAt.includes(id)).length, id).toBeGreaterThanOrEqual(5); } }); it("adds the new producers to existing goods", () => { const at = (id: string) => goods.find((g) => g.id === id)!; expect(at("water-ice").producedAt).toContain("europa-ice-station"); expect(at("liquid-oxygen").producedAt).toContain("europa-ice-station"); expect(at("industrial-chemicals").producedAt).toContain("venus-cloud-city"); expect(at("mail").producedAt).toEqual( expect.arrayContaining(["io-sulfur-works", "europa-ice-station"]), ); }); });