// makeFakeCore builds a complete `Core` whose calc methods mirror // `pkg/calc` exactly, for component and unit tests that must not boot the // real WASM module. The committed `core.wasm` is rebuilt out-of-band // (`make wasm`, needs TinyGo), so tests that exercise calculator math // inject this fake instead of depending on a freshly built binary. The // Go parity tests in `ui/core/calc` guarantee the real bridge agrees with // `pkg/calc`, so a fake that also mirrors `pkg/calc` stays faithful. // // Pass `overrides` to replace individual methods — e.g. `vi.fn()` spies // when a test wants to assert how the calc-model orchestrates the bridge. import type { Core } from "../src/platform/core/index"; function weaponsBlockMass(weapons: number, armament: number): number | null { if ((armament === 0 && weapons !== 0) || (armament !== 0 && weapons === 0)) { return null; } return (armament + 1) * (weapons / 2); } export function makeFakeCore(overrides: Partial = {}): Core { const base: Core = { signRequest: () => new Uint8Array(), verifyResponse: () => true, verifyEvent: () => true, verifyPayloadHash: () => true, driveEffective: ({ drive, driveTech }) => drive * driveTech, emptyMass: ({ drive, weapons, armament, shields, cargo }) => { const wb = weaponsBlockMass(weapons, armament); if (wb === null) return null; return drive + shields + cargo + wb; }, weaponsBlockMass: ({ weapons, armament }) => weaponsBlockMass(weapons, armament), fullMass: ({ emptyMass, carryingMass }) => emptyMass + carryingMass, speed: ({ driveEffective, fullMass }) => fullMass <= 0 ? 0 : (driveEffective * 20) / fullMass, cargoCapacity: ({ cargo, cargoTech }) => cargoTech * (cargo + (cargo * cargo) / 20), carryingMass: ({ load, cargoTech }) => (load <= 0 ? 0 : load / cargoTech), blockUpgradeCost: ({ blockMass, currentTech, targetTech }) => blockMass === 0 || targetTech <= currentTech ? 0 : (1 - currentTech / targetTech) * 10 * blockMass, effectiveAttack: ({ weapons, weaponsTech }) => weapons * weaponsTech, effectiveDefence: ({ shields, shieldsTech, fullMass }) => fullMass <= 0 ? 0 : ((shields * shieldsTech) / Math.cbrt(fullMass)) * Math.cbrt(30), bombingPower: ({ weapons, weaponsTech, armament, number }) => (Math.sqrt(weapons * weaponsTech) / 10 + 1) * weapons * weaponsTech * armament * number, shipBuildCost: ({ shipMass, material, resources }) => { const matNeed = Math.max(0, shipMass - material); const matFarm = resources > 0 ? matNeed / resources : 0; return shipMass * 10 + matFarm; }, produceShipsInTurn: ({ productionAvailable, material, resources, shipMass, }) => { if (productionAvailable <= 0 || shipMass <= 0) { return { ships: 0, materialLeft: material, productionUsed: 0, progress: 0, }; } let pa = productionAvailable; let mat = material; let ships = 0; for (;;) { const matNeed = Math.max(0, shipMass - mat); const cost = shipMass * 10 + (resources > 0 ? matNeed / resources : 0); if (pa < cost) { return { ships, materialLeft: mat, productionUsed: pa, progress: pa / cost, }; } pa -= cost; mat = mat - shipMass + matNeed; ships += 1; } }, weaponsForAttack: ({ targetAttack, weaponsTech }) => weaponsTech <= 0 || targetAttack < 0 ? null : targetAttack / weaponsTech, driveForSpeed: ({ targetSpeed, driveTech, restMass }) => { const ceiling = 20 * driveTech; if (driveTech <= 0 || targetSpeed <= 0 || targetSpeed >= ceiling) { return null; } return (targetSpeed * restMass) / (ceiling - targetSpeed); }, shieldsForDefence: ({ targetDefence, shieldsTech, restMass }) => { if (targetDefence <= 0 || shieldsTech <= 0) return null; const def = (s: number) => ((s * shieldsTech) / Math.cbrt(s + restMass)) * Math.cbrt(30); let lo = 0; let hi = 1; while (def(hi) < targetDefence) { hi *= 2; if (hi > 1e12) return null; } for (let i = 0; i < 100; i++) { const mid = (lo + hi) / 2; if (def(mid) < targetDefence) lo = mid; else hi = mid; } return (lo + hi) / 2; }, cargoForEmptyMass: ({ targetEmptyMass, restMass }) => targetEmptyMass - restMass < 0 ? null : targetEmptyMass - restMass, loadForFullMass: ({ targetFullMass, emptyMass, cargoTech }) => cargoTech <= 0 || targetFullMass < emptyMass ? null : (targetFullMass - emptyMass) * cargoTech, ceil3: ({ value }) => Math.ceil(Math.round(value * 1e9) / 1e6) / 1000, }; return { ...base, ...overrides }; }