134 lines
4.8 KiB
JavaScript
134 lines
4.8 KiB
JavaScript
|
|
const { describe, it, beforeEach, afterEach } = require('node:test');
|
||
|
|
const assert = require('node:assert/strict');
|
||
|
|
const { resolveStrategy, getStrategyNames, STRATEGIES } = require('../src/gep/strategy');
|
||
|
|
|
||
|
|
describe('STRATEGIES', function () {
|
||
|
|
it('defines all expected presets', function () {
|
||
|
|
const names = getStrategyNames();
|
||
|
|
assert.ok(names.includes('balanced'));
|
||
|
|
assert.ok(names.includes('innovate'));
|
||
|
|
assert.ok(names.includes('harden'));
|
||
|
|
assert.ok(names.includes('repair-only'));
|
||
|
|
assert.ok(names.includes('early-stabilize'));
|
||
|
|
assert.ok(names.includes('steady-state'));
|
||
|
|
});
|
||
|
|
|
||
|
|
it('all strategies have required fields', function () {
|
||
|
|
for (const [name, s] of Object.entries(STRATEGIES)) {
|
||
|
|
assert.equal(typeof s.repair, 'number', `${name}.repair`);
|
||
|
|
assert.equal(typeof s.optimize, 'number', `${name}.optimize`);
|
||
|
|
assert.equal(typeof s.innovate, 'number', `${name}.innovate`);
|
||
|
|
assert.equal(typeof s.repairLoopThreshold, 'number', `${name}.repairLoopThreshold`);
|
||
|
|
assert.equal(typeof s.label, 'string', `${name}.label`);
|
||
|
|
assert.equal(typeof s.description, 'string', `${name}.description`);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
it('all strategy ratios sum to approximately 1.0', function () {
|
||
|
|
for (const [name, s] of Object.entries(STRATEGIES)) {
|
||
|
|
const sum = s.repair + s.optimize + s.innovate;
|
||
|
|
assert.ok(Math.abs(sum - 1.0) < 0.01, `${name} ratios sum to ${sum}`);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('resolveStrategy', function () {
|
||
|
|
let origStrategy;
|
||
|
|
let origForceInnovation;
|
||
|
|
let origEvolveForceInnovation;
|
||
|
|
|
||
|
|
beforeEach(function () {
|
||
|
|
origStrategy = process.env.EVOLVE_STRATEGY;
|
||
|
|
origForceInnovation = process.env.FORCE_INNOVATION;
|
||
|
|
origEvolveForceInnovation = process.env.EVOLVE_FORCE_INNOVATION;
|
||
|
|
delete process.env.EVOLVE_STRATEGY;
|
||
|
|
delete process.env.FORCE_INNOVATION;
|
||
|
|
delete process.env.EVOLVE_FORCE_INNOVATION;
|
||
|
|
});
|
||
|
|
|
||
|
|
afterEach(function () {
|
||
|
|
if (origStrategy !== undefined) process.env.EVOLVE_STRATEGY = origStrategy;
|
||
|
|
else delete process.env.EVOLVE_STRATEGY;
|
||
|
|
if (origForceInnovation !== undefined) process.env.FORCE_INNOVATION = origForceInnovation;
|
||
|
|
else delete process.env.FORCE_INNOVATION;
|
||
|
|
if (origEvolveForceInnovation !== undefined) process.env.EVOLVE_FORCE_INNOVATION = origEvolveForceInnovation;
|
||
|
|
else delete process.env.EVOLVE_FORCE_INNOVATION;
|
||
|
|
});
|
||
|
|
|
||
|
|
it('defaults to balanced when no env var set', function () {
|
||
|
|
const s = resolveStrategy({});
|
||
|
|
assert.ok(['balanced', 'early-stabilize'].includes(s.name));
|
||
|
|
});
|
||
|
|
|
||
|
|
it('respects explicit EVOLVE_STRATEGY', function () {
|
||
|
|
process.env.EVOLVE_STRATEGY = 'harden';
|
||
|
|
const s = resolveStrategy({});
|
||
|
|
assert.equal(s.name, 'harden');
|
||
|
|
assert.equal(s.label, 'Hardening');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('respects innovate strategy', function () {
|
||
|
|
process.env.EVOLVE_STRATEGY = 'innovate';
|
||
|
|
const s = resolveStrategy({});
|
||
|
|
assert.equal(s.name, 'innovate');
|
||
|
|
assert.ok(s.innovate >= 0.8);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('respects repair-only strategy', function () {
|
||
|
|
process.env.EVOLVE_STRATEGY = 'repair-only';
|
||
|
|
const s = resolveStrategy({});
|
||
|
|
assert.equal(s.name, 'repair-only');
|
||
|
|
assert.equal(s.innovate, 0);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('FORCE_INNOVATION=true maps to innovate', function () {
|
||
|
|
process.env.FORCE_INNOVATION = 'true';
|
||
|
|
const s = resolveStrategy({});
|
||
|
|
assert.equal(s.name, 'innovate');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('EVOLVE_FORCE_INNOVATION=true maps to innovate', function () {
|
||
|
|
process.env.EVOLVE_FORCE_INNOVATION = 'true';
|
||
|
|
const s = resolveStrategy({});
|
||
|
|
assert.equal(s.name, 'innovate');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('explicit EVOLVE_STRATEGY takes precedence over FORCE_INNOVATION', function () {
|
||
|
|
process.env.EVOLVE_STRATEGY = 'harden';
|
||
|
|
process.env.FORCE_INNOVATION = 'true';
|
||
|
|
const s = resolveStrategy({});
|
||
|
|
assert.equal(s.name, 'harden');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('saturation signal triggers steady-state', function () {
|
||
|
|
const s = resolveStrategy({ signals: ['evolution_saturation'] });
|
||
|
|
assert.equal(s.name, 'steady-state');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('force_steady_state signal triggers steady-state', function () {
|
||
|
|
const s = resolveStrategy({ signals: ['force_steady_state'] });
|
||
|
|
assert.equal(s.name, 'steady-state');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('falls back to balanced for unknown strategy name', function () {
|
||
|
|
process.env.EVOLVE_STRATEGY = 'nonexistent';
|
||
|
|
const s = resolveStrategy({});
|
||
|
|
const fallback = STRATEGIES['balanced'];
|
||
|
|
assert.equal(s.repair, fallback.repair);
|
||
|
|
assert.equal(s.optimize, fallback.optimize);
|
||
|
|
assert.equal(s.innovate, fallback.innovate);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('auto maps to balanced or heuristic', function () {
|
||
|
|
process.env.EVOLVE_STRATEGY = 'auto';
|
||
|
|
const s = resolveStrategy({});
|
||
|
|
assert.ok(['balanced', 'early-stabilize'].includes(s.name));
|
||
|
|
});
|
||
|
|
|
||
|
|
it('returned strategy has name property', function () {
|
||
|
|
process.env.EVOLVE_STRATEGY = 'harden';
|
||
|
|
const s = resolveStrategy({});
|
||
|
|
assert.equal(s.name, 'harden');
|
||
|
|
});
|
||
|
|
});
|