Files
Flight-Ops/lib/game/catalog.test.ts
2026-09-21 10:51:22 +00:00

46 lines
2.4 KiB
TypeScript
Raw Permalink 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 { FUEL_DISCOUNT_PER_REP, MAX_FUEL_DISCOUNT, MAX_PLANE_DISCOUNT, PLANE_DISCOUNT_PER_REP, PLANE_TYPES, repRate, STARTER_MODEL } from './catalog';
import { STARTING_REPUTATION } from './seed';
// v0.28.0 (ISS-047) — catalog sanity: the progression ladder must stay well-formed as types are added.
describe('PLANE_TYPES', () => {
it('every entry has positive specs and a unique id', () => {
const ids = Object.keys(PLANE_TYPES);
expect(new Set(ids).size).toBe(ids.length);
for (const t of Object.values(PLANE_TYPES)) {
expect(t.id, `${t.name} id`).toBeTruthy();
expect(t.price, `${t.name} price`).toBeGreaterThan(0);
expect(t.maxFuel, `${t.name} maxFuel`).toBeGreaterThan(0);
expect(t.burnPerKm, `${t.name} burnPerKm`).toBeGreaterThan(0);
expect(t.emptyWeightKg, `${t.name} emptyWeightKg`).toBeGreaterThan(0);
expect(t.speedKmh, `${t.name} speedKmh`).toBeGreaterThan(0);
expect(t.maxPayloadKg, `${t.name} maxPayloadKg`).toBeGreaterThan(0);
}
});
it('atr72 is the starter model', () => {
expect(STARTER_MODEL).toBe('atr72');
expect(PLANE_TYPES[STARTER_MODEL]).toBeDefined();
});
});
// v0.38.0 (ISS-072) — the shared rep-discount shape: linear above the starting value, capped.
describe('repRate', () => {
it('is exactly 0 at and below the starting reputation', () => {
expect(repRate(STARTING_REPUTATION, PLANE_DISCOUNT_PER_REP, MAX_PLANE_DISCOUNT)).toBe(0);
expect(repRate(0, PLANE_DISCOUNT_PER_REP, MAX_PLANE_DISCOUNT)).toBe(0);
expect(repRate(-5, FUEL_DISCOUNT_PER_REP, MAX_FUEL_DISCOUNT)).toBe(0); // below-start clamps too
});
it('is linear in reputation above the starting value (exact mid-range values)', () => {
expect(repRate(STARTING_REPUTATION + 10, PLANE_DISCOUNT_PER_REP, MAX_PLANE_DISCOUNT)).toBeCloseTo(0.1, 12); // 0.01 × 10
expect(repRate(STARTING_REPUTATION + 30, PLANE_DISCOUNT_PER_REP, MAX_PLANE_DISCOUNT)).toBeCloseTo(0.3, 12); // 0.01 × 30
expect(repRate(STARTING_REPUTATION + 40, FUEL_DISCOUNT_PER_REP, MAX_FUEL_DISCOUNT)).toBeCloseTo(0.2, 12); // 0.005 × 40 = cap
});
it('clamps at the cap', () => {
expect(repRate(STARTING_REPUTATION + 50, PLANE_DISCOUNT_PER_REP, MAX_PLANE_DISCOUNT)).toBe(MAX_PLANE_DISCOUNT); // 0.5 → capped to 0.4
expect(repRate(1_000_000, FUEL_DISCOUNT_PER_REP, MAX_FUEL_DISCOUNT)).toBe(MAX_FUEL_DISCOUNT);
});
});