Initial commit with translated description
This commit is contained in:
142
test/mutation.test.js
Normal file
142
test/mutation.test.js
Normal file
@@ -0,0 +1,142 @@
|
||||
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 }));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user