b1b87c8521
- custom load capped at cargo capacity (error when exceeded); full load shows the cargo capacity; zero cargo pins load to empty and disables the toggle - per-input red border + tooltip for every invalid value (blocks, techs, load, MAT, modernization target); no value may be negative; locking a speed is disabled when drive is zero - display every computed number (results + goal-seek back-solved input) rounded up to 3 decimals via a shared pkg/calc Ceil3 bridged to wasm; engine keeps its own round-to-nearest util.Fixed* - modernization total upgrade cost spans two columns (single line) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
80 lines
2.5 KiB
TypeScript
80 lines
2.5 KiB
TypeScript
// Smoke test for the Phase 30 calculator bridge against the real
|
||
// (TinyGo-built) core.wasm. The calc-model and component suites use a
|
||
// fake Core; this file boots the actual WASM module to confirm every new
|
||
// function is registered in `ui/wasm/main.go` and marshals correctly —
|
||
// including the object return of `produceShipsInTurn` and the `null`
|
||
// infeasible result of the solvers. Requires `make wasm` to have run.
|
||
|
||
import { beforeAll, describe, expect, test } from "vitest";
|
||
import type { Core } from "../src/platform/core/index";
|
||
import { loadWasmCoreForTest } from "./setup-wasm";
|
||
|
||
let core: Core;
|
||
|
||
beforeAll(async () => {
|
||
core = await loadWasmCoreForTest();
|
||
});
|
||
|
||
describe("WasmCore calculator bridge (Phase 30)", () => {
|
||
test("combat results", () => {
|
||
expect(core.effectiveAttack({ weapons: 15, weaponsTech: 1.5 })).toBeCloseTo(
|
||
22.5,
|
||
9,
|
||
);
|
||
expect(
|
||
core.effectiveDefence({ shields: 20, shieldsTech: 1, fullMass: 45 }),
|
||
).toBeCloseTo((20 / Math.cbrt(45)) * Math.cbrt(30), 6);
|
||
expect(
|
||
core.bombingPower({ weapons: 30, weaponsTech: 1, armament: 3, number: 1 }),
|
||
).toBeCloseTo(139.29503, 3);
|
||
});
|
||
|
||
test("planet build", () => {
|
||
expect(
|
||
core.shipBuildCost({ shipMass: 10, material: 3, resources: 0.5 }),
|
||
).toBeCloseTo(114, 9);
|
||
const r = core.produceShipsInTurn({
|
||
productionAvailable: 100,
|
||
material: 100,
|
||
resources: 10,
|
||
shipMass: 1,
|
||
});
|
||
expect(r).toEqual({
|
||
ships: 10,
|
||
materialLeft: 90,
|
||
productionUsed: 0,
|
||
progress: 0,
|
||
});
|
||
});
|
||
|
||
test("goal-seek solvers, including infeasible", () => {
|
||
expect(
|
||
core.weaponsForAttack({ targetAttack: 30, weaponsTech: 1.5 }),
|
||
).toBeCloseTo(20, 9);
|
||
expect(
|
||
core.cargoForEmptyMass({ targetEmptyMass: 42, restMass: 30 }),
|
||
).toBeCloseTo(12, 9);
|
||
expect(
|
||
core.loadForFullMass({ targetFullMass: 65, emptyMass: 45, cargoTech: 1 }),
|
||
).toBeCloseTo(20, 9);
|
||
const shields = core.shieldsForDefence({
|
||
targetDefence: 5,
|
||
shieldsTech: 1,
|
||
restMass: 40,
|
||
});
|
||
expect(shields).not.toBeNull();
|
||
expect(shields as number).toBeGreaterThan(0);
|
||
// Speed at/above the stripped-hull ceiling (20 × driveTech) is
|
||
// unreachable: the bridge returns null.
|
||
expect(
|
||
core.driveForSpeed({ targetSpeed: 100, driveTech: 1.2, restMass: 35 }),
|
||
).toBeNull();
|
||
});
|
||
|
||
test("ceil3 rounds up to three decimals", () => {
|
||
expect(core.ceil3({ value: 5.0003 })).toBeCloseTo(5.001, 9);
|
||
expect(core.ceil3({ value: 4.2761 })).toBeCloseTo(4.277, 9);
|
||
expect(core.ceil3({ value: 5 })).toBeCloseTo(5, 9);
|
||
});
|
||
});
|