120 lines
4.2 KiB
TypeScript
120 lines
4.2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
||
import { content, gameConfig, tripEventSchema, tripEvents } from "../src/index";
|
||
|
||
describe("random events content (Step 26.1, decisions 0.5.145–0.5.149)", () => {
|
||
it("has the catalog's fourteen events, all valid, with unique ids", () => {
|
||
for (const event of tripEvents) {
|
||
const parsed = tripEventSchema.safeParse(event);
|
||
expect(parsed.success, event.id).toBe(true);
|
||
}
|
||
expect(tripEvents.map((e) => e.id)).toEqual([
|
||
"solar-flare",
|
||
"micrometeor-strike",
|
||
"engine-hiccup",
|
||
"debris-field",
|
||
"storm-approach",
|
||
"customs-hold",
|
||
"hazmat-leak",
|
||
"load-shift",
|
||
"spoilage-risk",
|
||
"solar-tailwind",
|
||
"grateful-client",
|
||
"derelict-pod",
|
||
"distress-beacon",
|
||
"spot-freight",
|
||
]);
|
||
expect(content.tripEvent("derelict-pod")?.kind).toBe("radio");
|
||
});
|
||
|
||
it("gives every radio call two or three options and exactly one default", () => {
|
||
for (const event of tripEvents.filter((e) => e.kind === "radio")) {
|
||
expect(event.options.length, event.id).toBeGreaterThanOrEqual(2);
|
||
expect(
|
||
event.options.filter((o) => o.default),
|
||
event.id,
|
||
).toHaveLength(1);
|
||
}
|
||
const twoDefaults = {
|
||
...content.tripEvent("debris-field")!,
|
||
options: content.tripEvent("debris-field")!.options.map((o) => ({ ...o, default: true })),
|
||
};
|
||
expect(tripEventSchema.safeParse(twoDefaults).success).toBe(false);
|
||
});
|
||
|
||
it("keeps every option's chances to at most 100%", () => {
|
||
for (const event of tripEvents) {
|
||
for (const option of event.options) {
|
||
const total = option.chances.reduce((sum, c) => sum + c.chance, 0);
|
||
expect(total, `${event.id}/${option.id}`).toBeLessThanOrEqual(1);
|
||
}
|
||
}
|
||
const tooMuch = {
|
||
...content.tripEvent("debris-field")!,
|
||
options: [
|
||
content.tripEvent("debris-field")!.options[0]!,
|
||
{
|
||
...content.tripEvent("debris-field")!.options[1]!,
|
||
chances: [
|
||
{ chance: 0.7, effects: {} },
|
||
{ chance: 0.4, effects: {} },
|
||
],
|
||
},
|
||
],
|
||
};
|
||
expect(tripEventSchema.safeParse(tooMuch).success).toBe(false);
|
||
});
|
||
|
||
it("weights about one good event for every two bad (28–38% good)", () => {
|
||
const total = tripEvents.reduce((sum, e) => sum + e.weight, 0);
|
||
const good = tripEvents.filter((e) => e.good).reduce((sum, e) => sum + e.weight, 0);
|
||
expect(good / total).toBeGreaterThanOrEqual(0.28);
|
||
expect(good / total).toBeLessThanOrEqual(0.38);
|
||
});
|
||
|
||
it("only names places, goods and skills that exist", () => {
|
||
for (const event of tripEvents) {
|
||
const { arrivalIds, priorityGoods, skillHalving, cargoClassId } = event.when;
|
||
for (const id of arrivalIds ?? []) expect(content.location(id), id).toBeDefined();
|
||
for (const id of priorityGoods ?? []) expect(content.good(id), id).toBeDefined();
|
||
if (skillHalving) expect(content.skill(skillHalving), skillHalving).toBeDefined();
|
||
if (cargoClassId) expect(content.cargoTypes.some((c) => c.id === cargoClassId)).toBe(true);
|
||
}
|
||
expect(content.tripEvent(gameConfig.events.firstEventId)?.good).toBe(true);
|
||
});
|
||
|
||
it("sets the CR's pacing, hull and repair numbers, and a region for every place", () => {
|
||
expect(gameConfig.events).toMatchObject({
|
||
baseChance: 0.02,
|
||
chancePerGameHour: 0.004,
|
||
maxChance: 0.35,
|
||
shipCooldownTrips: 2,
|
||
fleetRadioGapRealMinutes: 45,
|
||
graceTripsAfterDeal: 3,
|
||
responseWindowRealMinutes: 30,
|
||
});
|
||
expect(gameConfig.hull).toMatchObject({
|
||
floorPct: 5,
|
||
noDangerousBelowPct: 50,
|
||
needsRepairBelowPct: 25,
|
||
});
|
||
expect(content.locations.map((l) => [l.id, l.region])).toEqual([
|
||
["earth", "inner"],
|
||
["moon", "inner"],
|
||
["mars", "inner"],
|
||
["ceres-station", "belt"],
|
||
["jupiter-cloud-platform", "jupiter"],
|
||
["venus-cloud-city", "inner"],
|
||
["io-sulfur-works", "jupiter"],
|
||
["europa-ice-station", "jupiter"],
|
||
]);
|
||
});
|
||
|
||
it("adds Hull Plating: +1 hull rating a level, three levels", () => {
|
||
expect(content.upgrade("hull-plating")).toMatchObject({
|
||
stat: "hullRating",
|
||
flatPerLevel: 1,
|
||
maxLevel: 3,
|
||
});
|
||
});
|
||
});
|