mirror of
https://github.com/EvoMap/evolver.git
synced 2026-09-18 21:47:53 +08:00
534ede263b
Co-authored-by: autogame-17 <autogame-17@users.noreply.github.com> Co-authored-by: bjw9808 <bjw9808@gmail.com> Co-authored-by: autogame-17 <166480271+autogame-17@users.noreply.github.com>
113 lines
4.6 KiB
JavaScript
113 lines
4.6 KiB
JavaScript
const { describe, it, beforeEach, afterEach } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('fs');
|
|
const os = require('os');
|
|
const path = require('path');
|
|
|
|
const { _buildGene, _buildCapsule } = require('../src/atp/atpExecute');
|
|
|
|
describe('atpExecute._buildGene', () => {
|
|
it('produces a valid Gene with deterministic asset_id for same caps', () => {
|
|
const a = _buildGene(['code_evolution', 'debugging'], ['log_error']);
|
|
const b = _buildGene(['debugging', 'code_evolution'], ['log_error']);
|
|
assert.equal(a.type, 'Gene');
|
|
assert.equal(typeof a.id, 'string');
|
|
assert.ok(Array.isArray(a.strategy) && a.strategy.length >= 2);
|
|
assert.ok(Array.isArray(a.validation) && a.validation.length >= 1);
|
|
assert.equal(typeof a.asset_id, 'string');
|
|
assert.ok(a.asset_id.startsWith('sha256:'));
|
|
// Capability order should not affect the gene id -- we sort caps before building the id.
|
|
assert.equal(a.asset_id, b.asset_id);
|
|
});
|
|
|
|
it('falls back to a default capability when none provided', () => {
|
|
const g = _buildGene([], []);
|
|
assert.equal(g.type, 'Gene');
|
|
assert.ok(g.id.length > 0);
|
|
});
|
|
|
|
it('clamps capability and signal arrays', () => {
|
|
const manyCaps = Array.from({ length: 30 }, function (_, i) { return 'cap_' + i; });
|
|
const manySigs = Array.from({ length: 30 }, function (_, i) { return 'sig_' + i; });
|
|
const g = _buildGene(manyCaps, manySigs);
|
|
assert.ok(g.signals_match.length <= 8, 'signals_match should be clamped');
|
|
});
|
|
});
|
|
|
|
describe('atpExecute._buildCapsule', () => {
|
|
it('produces a Capsule referencing its Gene and carrying the answer', () => {
|
|
const gene = _buildGene(['documentation'], ['user_feature_request']);
|
|
const capsule = _buildCapsule({
|
|
gene: gene,
|
|
answer: 'Here is a concrete outline: 1. install 2. configure 3. run',
|
|
summary: 'Answer for documentation question',
|
|
orderId: 'proof_abc123',
|
|
taskId: 'task_xyz789',
|
|
capabilities: ['documentation'],
|
|
signals: ['user_feature_request'],
|
|
});
|
|
assert.equal(capsule.type, 'Capsule');
|
|
assert.equal(capsule.gene, gene.id);
|
|
assert.equal(capsule.content.slice(0, 15), 'Here is a concr');
|
|
assert.equal(capsule.outcome.status, 'success');
|
|
// ATP provenance rides under `a2a` (Hub-allow-listed), not a top-level
|
|
// `atp` key (which the Hub payload sanitizer would strip on publish).
|
|
assert.equal(capsule.a2a.atp.order_id, 'proof_abc123');
|
|
assert.equal(capsule.a2a.atp.task_id, 'task_xyz789');
|
|
assert.equal(capsule.source_type, 'generated');
|
|
assert.equal(capsule.schema_version, '1.0.0');
|
|
assert.ok(capsule.asset_id.startsWith('sha256:'));
|
|
});
|
|
|
|
it('defaults capsule summary when caller does not provide one', () => {
|
|
const gene = _buildGene(['general'], []);
|
|
const capsule = _buildCapsule({
|
|
gene: gene,
|
|
answer: 'short answer',
|
|
orderId: 'proof_default',
|
|
});
|
|
assert.ok(capsule.summary.length > 0);
|
|
});
|
|
|
|
it('different answers yield different capsule asset_ids', () => {
|
|
const gene = _buildGene(['general'], []);
|
|
const c1 = _buildCapsule({ gene: gene, answer: 'answer one', orderId: 'p1' });
|
|
const c2 = _buildCapsule({ gene: gene, answer: 'answer two', orderId: 'p1' });
|
|
assert.notEqual(c1.asset_id, c2.asset_id);
|
|
});
|
|
});
|
|
|
|
describe('atpExecute CLI-shape invariants', () => {
|
|
// Smoke test for the input-validation branch -- does not require a live Hub.
|
|
it('rejects missing required params', async () => {
|
|
const { completeAtpTask } = require('../src/atp/atpExecute');
|
|
const r1 = await completeAtpTask({});
|
|
assert.equal(r1.ok, false);
|
|
assert.equal(r1.stage, 'input');
|
|
const r2 = await completeAtpTask({ taskId: 't', orderId: 'o' });
|
|
assert.equal(r2.ok, false);
|
|
assert.equal(r2.stage, 'input');
|
|
});
|
|
|
|
it('reports read_answer stage when answer file is missing', async () => {
|
|
const { completeAtpTask } = require('../src/atp/atpExecute');
|
|
const missing = path.join(os.tmpdir(), 'atp-missing-' + Date.now() + '.md');
|
|
const r = await completeAtpTask({
|
|
taskId: 't1', orderId: 'o1', answerFile: missing,
|
|
});
|
|
assert.equal(r.ok, false);
|
|
assert.equal(r.stage, 'read_answer');
|
|
});
|
|
|
|
it('reports read_answer stage when answer file is empty', async () => {
|
|
const { completeAtpTask } = require('../src/atp/atpExecute');
|
|
const emptyFile = fs.mkdtempSync(path.join(os.tmpdir(), 'atp-exec-')) + '/empty.md';
|
|
fs.writeFileSync(emptyFile, '');
|
|
const r = await completeAtpTask({
|
|
taskId: 't1', orderId: 'o1', answerFile: emptyFile,
|
|
});
|
|
assert.equal(r.ok, false);
|
|
assert.equal(r.stage, 'read_answer');
|
|
});
|
|
});
|