mirror of
https://github.com/EvoMap/evolver.git
synced 2026-09-18 21:47:53 +08:00
193 lines
6.7 KiB
JavaScript
193 lines
6.7 KiB
JavaScript
'use strict';
|
|
|
|
const { describe, it } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const { createCapsule, validateCapsule, VALID_OUTCOME_STATUSES } = require('../src/gep/schemas/capsule');
|
|
|
|
describe('createCapsule', () => {
|
|
it('returns a fully-formed Capsule with all defaults when called with empty object', () => {
|
|
const c = createCapsule({});
|
|
assert.equal(c.type, 'Capsule');
|
|
assert.equal(c.id, null);
|
|
assert.deepEqual(c.trigger, []);
|
|
assert.deepEqual(c.strategy, []);
|
|
assert.deepEqual(c.execution_trace, []);
|
|
assert.equal(c.summary, '');
|
|
assert.equal(c.confidence, 0);
|
|
assert.deepEqual(c.blast_radius, { files: 0, lines: 0 });
|
|
assert.deepEqual(c.outcome, { status: 'failed', score: 0 });
|
|
assert.deepEqual(c.a2a, { eligible_to_broadcast: false });
|
|
assert.equal(c.success_streak, 0);
|
|
assert.equal(c.asset_id, null);
|
|
assert.equal(typeof c.schema_version, 'string');
|
|
});
|
|
|
|
it('preserves provided fields', () => {
|
|
const c = createCapsule({
|
|
id: 'cap1',
|
|
summary: 'Fixed it',
|
|
confidence: 0.9,
|
|
outcome: { status: 'success', score: 0.9 },
|
|
});
|
|
assert.equal(c.id, 'cap1');
|
|
assert.equal(c.summary, 'Fixed it');
|
|
assert.equal(c.confidence, 0.9);
|
|
assert.equal(c.outcome.status, 'success');
|
|
assert.equal(c.outcome.score, 0.9);
|
|
});
|
|
|
|
it('is idempotent — createCapsule(createCapsule(x)) equals createCapsule(x)', () => {
|
|
const input = { id: 'cap2', summary: 'ok', outcome: { status: 'success', score: 0.8 }, trigger: ['err'] };
|
|
const once = createCapsule(input);
|
|
const twice = createCapsule(once);
|
|
assert.deepEqual(once, twice);
|
|
});
|
|
|
|
it('normalizes null/missing outcome to default', () => {
|
|
const c = createCapsule({ outcome: null });
|
|
assert.deepEqual(c.outcome, { status: 'failed', score: 0 });
|
|
});
|
|
|
|
it('normalizes invalid outcome.status to "failed"', () => {
|
|
const c = createCapsule({ outcome: { status: 'unknown', score: 0.5 } });
|
|
assert.equal(c.outcome.status, 'failed');
|
|
});
|
|
|
|
it('merges partial outcome with defaults', () => {
|
|
const c = createCapsule({ outcome: { status: 'success' } });
|
|
assert.equal(c.outcome.status, 'success');
|
|
assert.equal(c.outcome.score, 0);
|
|
});
|
|
|
|
it('normalizes non-array trigger to []', () => {
|
|
const c = createCapsule({ trigger: 'not-an-array' });
|
|
assert.deepEqual(c.trigger, []);
|
|
});
|
|
|
|
it('normalizes null blast_radius to default', () => {
|
|
const c = createCapsule({ blast_radius: null });
|
|
assert.deepEqual(c.blast_radius, { files: 0, lines: 0 });
|
|
});
|
|
|
|
it('merges partial blast_radius with defaults', () => {
|
|
const c = createCapsule({ blast_radius: { files: 5 } });
|
|
assert.equal(c.blast_radius.files, 5);
|
|
assert.equal(c.blast_radius.lines, 0);
|
|
});
|
|
|
|
it('normalizes null a2a to default', () => {
|
|
const c = createCapsule({ a2a: null });
|
|
assert.deepEqual(c.a2a, { eligible_to_broadcast: false });
|
|
});
|
|
|
|
it('merges partial a2a with defaults', () => {
|
|
const c = createCapsule({ a2a: { eligible_to_broadcast: true } });
|
|
assert.equal(c.a2a.eligible_to_broadcast, true);
|
|
});
|
|
|
|
it('normalizes non-string summary to empty string', () => {
|
|
const c = createCapsule({ summary: 42 });
|
|
assert.equal(c.summary, '');
|
|
});
|
|
|
|
it('normalizes non-number confidence to 0', () => {
|
|
const c = createCapsule({ confidence: 'high' });
|
|
assert.equal(c.confidence, 0);
|
|
});
|
|
|
|
it('accepts all valid outcome statuses', () => {
|
|
for (const status of VALID_OUTCOME_STATUSES) {
|
|
const c = createCapsule({ outcome: { status, score: 0.5 } });
|
|
assert.equal(c.outcome.status, status);
|
|
}
|
|
});
|
|
|
|
it('returns independent array instances — mutation does not contaminate other capsules', () => {
|
|
const c1 = createCapsule({ id: 'c1' });
|
|
const c2 = createCapsule({ id: 'c2' });
|
|
c1.trigger.push('signal_a');
|
|
c1.execution_trace.push({ step: 'run' });
|
|
assert.deepEqual(c2.trigger, [], 'trigger should be independent');
|
|
assert.deepEqual(c2.execution_trace, [], 'execution_trace should be independent');
|
|
});
|
|
|
|
it('returns independent array instances even when partial provides arrays', () => {
|
|
const shared = ['log_error'];
|
|
const c1 = createCapsule({ trigger: shared });
|
|
const c2 = createCapsule({ trigger: shared });
|
|
c1.trigger.push('new_signal');
|
|
assert.equal(c2.trigger.length, 1, 'trigger arrays should not share references');
|
|
assert.equal(shared.length, 1, 'original partial array should not be mutated');
|
|
});
|
|
|
|
it('passes through extra fields not in defaults (e.g. diff_snapshot, failure_reason)', () => {
|
|
const c = createCapsule({ id: 'f1', diff_snapshot: 'diff', failure_reason: 'timeout' });
|
|
assert.equal(c.diff_snapshot, 'diff');
|
|
assert.equal(c.failure_reason, 'timeout');
|
|
});
|
|
});
|
|
|
|
describe('validateCapsule', () => {
|
|
function validCapsule(overrides) {
|
|
return createCapsule({
|
|
id: 'cap-valid',
|
|
outcome: { status: 'success', score: 0.8 },
|
|
trigger: ['error'],
|
|
execution_trace: [{ step: 'apply' }],
|
|
...overrides,
|
|
});
|
|
}
|
|
|
|
it('passes for a valid Capsule', () => {
|
|
assert.doesNotThrow(() => validateCapsule(validCapsule()));
|
|
});
|
|
|
|
it('throws when capsule is null', () => {
|
|
assert.throws(() => validateCapsule(null), /must be an object/);
|
|
});
|
|
|
|
it('throws when type is not "Capsule"', () => {
|
|
assert.throws(() => validateCapsule(validCapsule({ type: 'Gene' })), /type must be "Capsule"/);
|
|
});
|
|
|
|
it('throws when id is missing', () => {
|
|
assert.throws(() => validateCapsule(validCapsule({ id: null })), /id is required/);
|
|
});
|
|
|
|
it('throws when id is empty string', () => {
|
|
assert.throws(() => validateCapsule(validCapsule({ id: '' })), /id is required/);
|
|
});
|
|
|
|
it('throws when outcome is missing', () => {
|
|
assert.throws(
|
|
() => validateCapsule({ type: 'Capsule', id: 'c1', outcome: null, trigger: [], execution_trace: [] }),
|
|
/outcome must be an object/,
|
|
);
|
|
});
|
|
|
|
it('throws when outcome.status is invalid', () => {
|
|
assert.throws(
|
|
() => validateCapsule({ type: 'Capsule', id: 'c1', outcome: { status: 'pending', score: 0 }, trigger: [], execution_trace: [] }),
|
|
/outcome\.status must be one of/,
|
|
);
|
|
});
|
|
|
|
it('throws when trigger is not an array', () => {
|
|
assert.throws(
|
|
() => validateCapsule({ type: 'Capsule', id: 'c1', outcome: { status: 'success', score: 0.8 }, trigger: 'oops', execution_trace: [] }),
|
|
/trigger must be an array/,
|
|
);
|
|
});
|
|
|
|
it('throws when execution_trace is not an array', () => {
|
|
assert.throws(
|
|
() => validateCapsule({ type: 'Capsule', id: 'c1', outcome: { status: 'success', score: 0.8 }, trigger: [], execution_trace: 'oops' }),
|
|
/execution_trace must be an array/,
|
|
);
|
|
});
|
|
|
|
it('returns true on success', () => {
|
|
assert.equal(validateCapsule(validCapsule()), true);
|
|
});
|
|
});
|