95 lines
3.6 KiB
TypeScript
95 lines
3.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
||
import {
|
||
content,
|
||
gameConfig,
|
||
shipModels,
|
||
upgradeMaxLevel,
|
||
upgradePercentPerLevel,
|
||
upgradeSchema,
|
||
upgrades,
|
||
type ShipStat,
|
||
} from "../src/index";
|
||
|
||
const model = (id: string) => shipModels.find((m) => m.id === id)!;
|
||
const upgradeFor = (stat: ShipStat) => upgrades.find((u) => u.stat === stat)!;
|
||
/** A stat at the upgrade's max level for this model (decision 0.5.63: linear % of base). */
|
||
const maxed = (modelId: string, stat: Exclude<ShipStat, "handlingGameHoursPerTonne">) => {
|
||
const upgrade = upgradeFor(stat);
|
||
const level = upgradeMaxLevel(upgrade, modelId);
|
||
return model(modelId)[stat] * (1 + (upgradePercentPerLevel(upgrade, modelId) * level) / 100);
|
||
};
|
||
|
||
describe("ship upgrades content (CR-009)", () => {
|
||
it("has the five upgrades, all valid, three levels each", () => {
|
||
for (const upgrade of upgrades) expect(upgradeSchema.safeParse(upgrade).success).toBe(true);
|
||
expect(upgrades.map((u) => u.id)).toEqual([
|
||
"engines",
|
||
"cargo-bay",
|
||
"fuel-tanks",
|
||
"landing-thrusters",
|
||
"handling-gear",
|
||
]);
|
||
for (const upgrade of upgrades) expect(upgrade.maxLevel).toBe(3);
|
||
expect(content.upgrade("engines")?.name).toBe("Engine Tuning");
|
||
});
|
||
|
||
it("only changes stats ships have", () => {
|
||
for (const upgrade of upgrades) {
|
||
if (upgrade.stat === "handlingGameHoursPerTonne") {
|
||
// A game-wide base until upgraded.
|
||
expect(gameConfig.handlingGameHoursPerTonne).toBeGreaterThan(0);
|
||
} else {
|
||
for (const m of shipModels)
|
||
expect(m, `${m.id}.${upgrade.stat}`).toHaveProperty(upgrade.stat);
|
||
}
|
||
for (const id of Object.keys(upgrade.modelOverrides ?? {})) {
|
||
expect(content.shipModel(id), id).toBeDefined();
|
||
}
|
||
}
|
||
});
|
||
|
||
it("prices levels at 10/20/35 % of the ship and 10/20/30 minutes in the shop", () => {
|
||
expect(gameConfig.upgradeLevels).toEqual([
|
||
{ costPercentOfPrice: 10, shopRealMinutes: 10 },
|
||
{ costPercentOfPrice: 20, shopRealMinutes: 20 },
|
||
{ costPercentOfPrice: 35, shopRealMinutes: 30 },
|
||
]);
|
||
for (const upgrade of upgrades) {
|
||
for (const m of shipModels) {
|
||
expect(upgradeMaxLevel(upgrade, m.id)).toBeLessThanOrEqual(gameConfig.upgradeLevels.length);
|
||
}
|
||
}
|
||
});
|
||
|
||
it("keeps models distinct (decision 0.5.67)", () => {
|
||
expect(maxed("courier", "cargoCapacityTonnes")).toBeLessThan(
|
||
model("freighter").cargoCapacityTonnes,
|
||
);
|
||
expect(maxed("bulk-hauler", "cruiseSpeedKmPerGameHour")).toBeLessThan(
|
||
model("freighter").cruiseSpeedKmPerGameHour,
|
||
);
|
||
// Reach grows with tank ÷ burn (decision 0.5.95), so compare that.
|
||
expect(
|
||
maxed("freighter", "tankCapacityFu") / model("freighter").burnFuPerMillionKm,
|
||
).toBeLessThan(model("bulk-hauler").tankCapacityFu / model("bulk-hauler").burnFuPerMillionKm);
|
||
// Amended by CR-012 (the Lighter's Mars cap is a sim test: upgrade-caps.test.ts).
|
||
expect(maxed("clipper", "cargoCapacityTonnes")).toBeLessThan(
|
||
model("freighter").cargoCapacityTonnes,
|
||
);
|
||
expect(maxed("courier", "cruiseSpeedKmPerGameHour")).toBeLessThan(
|
||
model("clipper").cruiseSpeedKmPerGameHour,
|
||
);
|
||
});
|
||
});
|
||
|
||
describe("shop time counts as rest (Step 23.1, decision 0.5.135)", () => {
|
||
it("keeps every upgrade level in the shop for at least a full rest, in game hours", () => {
|
||
const { upgradeLevels, timeCompression, restGameHours } = gameConfig;
|
||
for (const level of upgradeLevels) {
|
||
// Real minutes × compression = game minutes.
|
||
const shopGameHours = (level.shopRealMinutes * timeCompression) / 60;
|
||
expect(shopGameHours).toBeGreaterThanOrEqual(restGameHours);
|
||
}
|
||
});
|
||
});
|