135 lines
5.1 KiB
TypeScript
135 lines
5.1 KiB
TypeScript
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"]),
|
||
);
|
||
});
|
||
});
|