blob: 26c14254dcf0689338984e56fd899ca5be27c198 [file]
import assert from 'node:assert/strict';
import { readFile, readdir, stat } from 'node:fs/promises';
import { join } from 'node:path';
import { describe, test } from 'node:test';
import {
experimentRunLayout,
runExperiment,
type ExperimentRunLayout,
} from '../experiment-engine.js';
import { withDir } from './helpers/temp-dir.js';
describe('runExperiment', () => {
test('resolves the standard controller/jobs/prompts layout', () => {
const layout = experimentRunLayout('/runs/abc');
assert.deepEqual(layout, {
runRoot: '/runs/abc',
controllerDir: '/runs/abc/controller',
jobsDir: '/runs/abc/jobs',
promptsDir: '/runs/abc/prompts',
resultsJsonlPath: '/runs/abc/controller/results.jsonl',
});
});
test('materializes dirs and prompts, runs, then persists artifacts in order', async () => {
await withDir(async (dir) => {
const runRoot = join(dir, 'run-1');
const events: string[] = [];
let runLayout: ExperimentRunLayout | undefined;
const summary = await runExperiment<{ decision: string }>({
runRoot,
prompts: (layout) => {
events.push('prompts');
return [{ path: join(layout.promptsDir, 'system.md'), content: 'hello prompt' }];
},
run: async (layout) => {
runLayout = layout;
// Directories and the prompt file must already exist before the run.
assert.ok((await stat(layout.controllerDir)).isDirectory());
assert.ok((await stat(layout.jobsDir)).isDirectory());
assert.equal(
await readFile(join(layout.promptsDir, 'system.md'), 'utf8'),
'hello prompt',
);
// Artifacts must not be written yet.
assert.deepEqual((await readdir(runRoot)).sort(), ['controller', 'jobs', 'prompts']);
events.push('run');
return { decision: 'keep' };
},
artifacts: (result, layout) => {
events.push('artifacts');
assert.equal(layout, runLayout);
return [
{
path: join(runRoot, 'result.json'),
content: `${JSON.stringify(result)}\n`,
},
];
},
});
assert.deepEqual(summary, { decision: 'keep' });
assert.deepEqual(events, ['prompts', 'run', 'artifacts']);
assert.equal(await readFile(join(runRoot, 'result.json'), 'utf8'), '{"decision":"keep"}\n');
});
});
test('awaits an async artifacts builder', async () => {
await withDir(async (dir) => {
const runRoot = join(dir, 'run-2');
await runExperiment<number>({
runRoot,
prompts: () => [],
run: async () => 7,
artifacts: async (value) => [{ path: join(runRoot, 'value.txt'), content: String(value) }],
});
assert.equal(await readFile(join(runRoot, 'value.txt'), 'utf8'), '7');
});
});
});