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

103 lines
2.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { z } from "zod";
import {
cargoTypeSchema,
gameConfigSchema,
locationSchema,
shipModelSchema,
skillSchema,
} from "../src/index";
const validLocation = {
id: "earth",
name: "Earth",
type: "atmospheric-world",
landingRequirement: "atmospheric",
body: "earth",
launchCostMultiplier: 3,
startsDiscovered: true,
bayLeasePrice: 5000,
produces: ["standard"],
demands: ["heavy"],
};
const validShipModel = {
id: "courier",
name: "Courier",
archetype: "courier",
price: 20000,
cruiseSpeedKmPerGameHour: 300000,
cargoCapacityTonnes: 10,
fuelRangeMillionKm: 5,
hullRating: 1,
landingClass: "atmospheric",
overheadGameHours: 2,
fuelCostPerMillionKm: 100,
};
const validCargoType = {
id: "standard",
name: "Standard",
basePayPerTonnePerMillionKm: 10,
tierPayMultiplier: 1,
requiredSkillId: null,
};
const validSkill = {
id: "long-haul",
name: "Long Haul",
maxLevel: 5,
description: "Access to longer routes.",
};
const validGameConfig = {
timeCompression: 60,
epoch: { realStart: "2026-01-01T00:00:00Z", ephemerisDate: "2100-01-01T00:00:00Z" },
startingCredits: 0,
tutorialPayoutShare: 0.2,
contractBoardSize: 8,
tutorialBoardSize: 50,
contractBoardPeriodRealMinutes: 60,
firstContactChance: 0.1,
firstContactPayoutMultiplier: 2,
contractMassTonnes: { min: 1, max: 100 },
longHaulCutoffsMillionKm: [100, 200, 300, 400, 500],
xpPerDelivery: { base: 10, perMillionKm: 0.1, perCargoTier: 5 },
pilotLevelXpThresholds: [100, 250, 500],
skillPointsPerLevel: 1,
pilotHireCost: 1000,
pilotPayPerDelivery: 100,
};
const cases: [string, z.ZodType, Record<string, unknown>][] = [
["Location", locationSchema, validLocation],
["ShipModel", shipModelSchema, validShipModel],
["CargoType", cargoTypeSchema, validCargoType],
["Skill", skillSchema, validSkill],
["GameConfig", gameConfigSchema, validGameConfig],
];
describe.each(cases)("%s schema", (_name, schema, valid) => {
it("accepts a valid example", () => {
expect(schema.safeParse(valid).success).toBe(true);
});
it.each(Object.keys(valid))("rejects an object missing %s", (field) => {
const missing = { ...valid };
delete missing[field];
expect(schema.safeParse(missing).success).toBe(false);
});
});
describe("GameConfig refinements", () => {
it("rejects a mass range with min above max", () => {
const config = { ...validGameConfig, contractMassTonnes: { min: 50, max: 10 } };
expect(gameConfigSchema.safeParse(config).success).toBe(false);
});
it("rejects Long Haul cut-offs that are not increasing", () => {
const config = { ...validGameConfig, longHaulCutoffsMillionKm: [100, 100, 300] };
expect(gameConfigSchema.safeParse(config).success).toBe(false);
});
});