deadhead-space-freight-tycoon/packages/content/test/goods.test.ts

135 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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"]),
);
});
});