163 lines
6.0 KiB
JavaScript
163 lines
6.0 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {
|
|
bestAffordableMarketUpgrade,
|
|
buyAndInstallModule,
|
|
collectSalvage,
|
|
createStarterProgression,
|
|
deriveShipStats,
|
|
describeInventoryEntry,
|
|
installInventoryModule,
|
|
installModule,
|
|
marketForPort,
|
|
salvageForPirate,
|
|
scrapInventoryModule,
|
|
upgradePathForSlot,
|
|
} from '../src/progression/ship-loadout.js';
|
|
|
|
test('starter loadout preserves current ship combat and handling values', () => {
|
|
const progression = createStarterProgression();
|
|
const stats = deriveShipStats(progression.loadout);
|
|
assert.equal(stats.maxHull, 100);
|
|
assert.equal(stats.maxSpeed, 180);
|
|
assert.equal(stats.turnRate, 76);
|
|
assert.equal(stats.reloadMs, 850);
|
|
assert.equal(stats.cannonDamage, 16);
|
|
assert.equal(stats.cargoCapacity, 4);
|
|
});
|
|
|
|
test('engine upgrades advance one tier at a time', () => {
|
|
const starter = { ...createStarterProgression(), credits: 10000 };
|
|
const mk1 = buyAndInstallModule(starter, 'tuned-propellers');
|
|
assert.equal(mk1.purchased, true);
|
|
assert.equal(mk1.loadout.engine, 'tuned-propellers');
|
|
|
|
const skip = buyAndInstallModule(starter, 'storm-turbines');
|
|
assert.equal(skip.purchased, false);
|
|
assert.equal(skip.reason, 'tier-locked');
|
|
|
|
const mk2 = buyAndInstallModule(mk1, 'corsair-overdrive');
|
|
assert.equal(mk2.purchased, true);
|
|
assert.equal(mk2.loadout.engine, 'corsair-overdrive');
|
|
});
|
|
|
|
test('upgrade path exposes current and next tier with price and effect', () => {
|
|
const starter = createStarterProgression();
|
|
const stock = marketForPort({ type: 'trade' });
|
|
const path = upgradePathForSlot(starter.loadout, 'engine', stock);
|
|
assert.equal(path.current.tierLabel, 'Stock');
|
|
assert.equal(path.next.tierLabel, 'Mk I');
|
|
assert.equal(path.next.price, 800);
|
|
assert.match(path.next.effectSummary, /speed/);
|
|
assert.equal(path.next.stocked, true);
|
|
});
|
|
|
|
test('installed modules derive meaningful ship stat changes', () => {
|
|
const progression = installModule(createStarterProgression(), 'corsair-overdrive');
|
|
const stats = deriveShipStats(progression.loadout);
|
|
assert.equal(stats.maxSpeed, 218);
|
|
assert.equal(stats.acceleration, 112);
|
|
assert.equal(stats.turnRate, 74);
|
|
});
|
|
|
|
test('port purchase deducts credits and installs the module', () => {
|
|
const starter = createStarterProgression();
|
|
const result = buyAndInstallModule(starter, 'tuned-propellers');
|
|
assert.equal(result.purchased, false);
|
|
|
|
const funded = { ...starter, credits: 1000 };
|
|
const fundedResult = buyAndInstallModule(funded, 'tuned-propellers');
|
|
assert.equal(fundedResult.purchased, true);
|
|
assert.equal(fundedResult.credits, funded.credits - 800);
|
|
assert.equal(fundedResult.loadout.engine, 'tuned-propellers');
|
|
});
|
|
|
|
test('pirate salvage includes deterministic field repair and collectible module condition', () => {
|
|
const salvageA = salvageForPirate({ seed: 'WORLD', classKey: 'captain', wave: 4, sequence: 2 });
|
|
const salvageB = salvageForPirate({ seed: 'WORLD', classKey: 'captain', wave: 4, sequence: 2 });
|
|
assert.deepEqual(salvageA, salvageB);
|
|
assert.ok(salvageA.repair > 0);
|
|
|
|
const starter = createStarterProgression();
|
|
const collected = collectSalvage(starter, salvageA);
|
|
assert.ok(collected.credits >= starter.credits);
|
|
assert.ok(collected.scrap >= starter.scrap);
|
|
|
|
if (salvageA.moduleId) {
|
|
const details = describeInventoryEntry(collected.inventory[0]);
|
|
assert.ok(details);
|
|
assert.ok(details.conditionPercent >= 28);
|
|
assert.ok(details.scrapValue >= 1);
|
|
}
|
|
});
|
|
|
|
test('recovered modules can be converted into scrap', () => {
|
|
const starter = createStarterProgression();
|
|
const collected = collectSalvage(starter, {
|
|
moduleId: 'reinforced-rudder',
|
|
condition: 0.5,
|
|
credits: 0,
|
|
scrap: 0,
|
|
repair: 6,
|
|
});
|
|
const result = scrapInventoryModule(collected, 0);
|
|
assert.equal(result.scrapped, true);
|
|
assert.equal(result.inventory.length, 0);
|
|
assert.ok(result.scrap > collected.scrap);
|
|
});
|
|
|
|
test('market selects only a valid next-tier affordable upgrade', () => {
|
|
const starter = createStarterProgression();
|
|
const upgrade = bestAffordableMarketUpgrade(starter, { type: 'military' });
|
|
assert.ok(upgrade);
|
|
assert.ok(upgrade.price <= starter.credits);
|
|
if (Number.isInteger(upgrade.tier)) assert.equal(upgrade.tier, 1);
|
|
});
|
|
|
|
test('rudder is stocked at every port type so its upgrade path remains actionable', () => {
|
|
const portTypes = ['industrial', 'military', 'fuel', 'trade'];
|
|
for (const type of portTypes) {
|
|
const stock = marketForPort({ type });
|
|
assert.ok(stock.some(({ id }) => id === 'reinforced-rudder'));
|
|
assert.ok(stock.some(({ id }) => id === 'balanced-rudder'));
|
|
}
|
|
});
|
|
|
|
test('rudder tier improves handling', () => {
|
|
const starter = { ...createStarterProgression(), credits: 650 };
|
|
const result = buyAndInstallModule(starter, 'reinforced-rudder');
|
|
const stats = deriveShipStats(result.loadout);
|
|
assert.equal(result.purchased, true);
|
|
assert.equal(result.credits, 250);
|
|
assert.equal(stats.turnRate, 88);
|
|
});
|
|
|
|
|
|
test('salvaged module condition scales installed effectiveness', () => {
|
|
const starter = createStarterProgression();
|
|
const salvaged = {
|
|
...starter,
|
|
inventory: [{ moduleId: 'reinforced-rudder', condition: 0.5, source: 'salvage' }],
|
|
};
|
|
const installed = installInventoryModule(salvaged, 0);
|
|
const stats = deriveShipStats(installed.loadout, installed.installedConditions);
|
|
assert.equal(installed.installedCondition, 0.5);
|
|
assert.ok(stats.turnRate > 76);
|
|
assert.ok(stats.turnRate < 88);
|
|
});
|
|
|
|
test('installing a selected salvage entry removes that exact inventory item', () => {
|
|
const starter = createStarterProgression();
|
|
const progression = {
|
|
...starter,
|
|
inventory: [
|
|
{ moduleId: 'reinforced-rudder', condition: 0.4, source: 'salvage' },
|
|
{ moduleId: 'tuned-propellers', condition: 0.9, source: 'salvage' },
|
|
],
|
|
};
|
|
const installed = installInventoryModule(progression, 1);
|
|
assert.equal(installed.loadout.engine, 'tuned-propellers');
|
|
assert.equal(installed.inventory.length, 1);
|
|
assert.equal(installed.inventory[0].moduleId, 'reinforced-rudder');
|
|
});
|