Files

143 lines
4.6 KiB
JavaScript
Raw Permalink Normal View History

const { describe, it } = require('node:test');
const assert = require('node:assert/strict');
const {
buildMutation,
isValidMutation,
normalizeMutation,
isHighRiskMutationAllowed,
isHighRiskPersonality,
clamp01,
} = require('../src/gep/mutation');
describe('clamp01', () => {
it('clamps values to [0, 1]', () => {
assert.equal(clamp01(0.5), 0.5);
assert.equal(clamp01(0), 0);
assert.equal(clamp01(1), 1);
assert.equal(clamp01(-0.5), 0);
assert.equal(clamp01(1.5), 1);
});
it('returns 0 for non-finite input', () => {
assert.equal(clamp01(NaN), 0);
assert.equal(clamp01(undefined), 0);
// Note: clamp01(Infinity) returns 0 because the implementation checks
// Number.isFinite() before clamping. Mathematically clamp(Inf, 0, 1) = 1,
// but the current behavior treats all non-finite values uniformly as 0.
assert.equal(clamp01(Infinity), 0);
});
});
describe('buildMutation', () => {
it('returns a valid Mutation object', () => {
const m = buildMutation({ signals: ['log_error'], selectedGene: { id: 'gene_repair' } });
assert.ok(isValidMutation(m));
assert.equal(m.type, 'Mutation');
assert.ok(m.id.startsWith('mut_'));
});
it('selects repair category when error signals present', () => {
const m = buildMutation({ signals: ['log_error', 'errsig:something'] });
assert.equal(m.category, 'repair');
});
it('selects innovate category when drift enabled', () => {
const m = buildMutation({ signals: ['stable_success_plateau'], driftEnabled: true });
assert.equal(m.category, 'innovate');
});
it('selects innovate for opportunity signals without errors', () => {
const m = buildMutation({ signals: ['user_feature_request'] });
assert.equal(m.category, 'innovate');
});
it('downgrades innovate to optimize for high-risk personality', () => {
const highRiskPersonality = { rigor: 0.3, risk_tolerance: 0.8, creativity: 0.5 };
const m = buildMutation({
signals: ['user_feature_request'],
personalityState: highRiskPersonality,
});
assert.equal(m.category, 'optimize');
assert.ok(m.trigger_signals.some(s => s.includes('safety')));
});
it('caps risk_level to medium when personality disallows high risk', () => {
const conservativePersonality = { rigor: 0.5, risk_tolerance: 0.6, creativity: 0.5 };
const m = buildMutation({
signals: ['stable_success_plateau'],
driftEnabled: true,
allowHighRisk: true,
personalityState: conservativePersonality,
});
assert.notEqual(m.risk_level, 'high');
});
});
describe('isValidMutation', () => {
it('returns true for valid mutation', () => {
const m = buildMutation({ signals: ['log_error'] });
assert.ok(isValidMutation(m));
});
it('returns false for missing fields', () => {
assert.ok(!isValidMutation(null));
assert.ok(!isValidMutation({}));
assert.ok(!isValidMutation({ type: 'Mutation' }));
});
it('returns false for invalid category', () => {
assert.ok(!isValidMutation({
type: 'Mutation', id: 'x', category: 'destroy',
trigger_signals: [], target: 't', expected_effect: 'e', risk_level: 'low',
}));
});
});
describe('normalizeMutation', () => {
it('fills defaults for empty object', () => {
const m = normalizeMutation({});
assert.ok(isValidMutation(m));
assert.equal(m.category, 'optimize');
assert.equal(m.risk_level, 'low');
});
it('preserves valid fields', () => {
const m = normalizeMutation({
id: 'mut_custom', category: 'repair',
trigger_signals: ['log_error'], target: 'file.js',
expected_effect: 'fix bug', risk_level: 'medium',
});
assert.equal(m.id, 'mut_custom');
assert.equal(m.category, 'repair');
assert.equal(m.risk_level, 'medium');
});
});
describe('isHighRiskPersonality', () => {
it('detects low rigor as high risk', () => {
assert.ok(isHighRiskPersonality({ rigor: 0.3 }));
});
it('detects high risk_tolerance as high risk', () => {
assert.ok(isHighRiskPersonality({ risk_tolerance: 0.7 }));
});
it('returns false for conservative personality', () => {
assert.ok(!isHighRiskPersonality({ rigor: 0.8, risk_tolerance: 0.2 }));
});
});
describe('isHighRiskMutationAllowed', () => {
it('allows when rigor >= 0.6 and risk_tolerance <= 0.5', () => {
assert.ok(isHighRiskMutationAllowed({ rigor: 0.8, risk_tolerance: 0.3 }));
});
it('disallows when rigor too low', () => {
assert.ok(!isHighRiskMutationAllowed({ rigor: 0.4, risk_tolerance: 0.3 }));
});
it('disallows when risk_tolerance too high', () => {
assert.ok(!isHighRiskMutationAllowed({ rigor: 0.8, risk_tolerance: 0.6 }));
});
});