@agentled/cli
Version:
CLI for Agentled — manage workflows, apps, and knowledge from the command line. Zero context-window cost for AI agents.
98 lines • 4.4 kB
JavaScript
/**
* `agentled test <workflowId>` — run declarative test assertions.
*
* Default mode: zero-credit replay against captured fixtures (fixtures/
* step-outputs/). Use `--live` to call testAiAction / testAppAction /
* testCodeAction (costs credits for AI/app steps; code is always free).
*
* Test file format: tests/<workflowId>.test.json
* Generate one with `agentled workflows pull <workflowId>`.
*
* Assertion verbs: hasKey, isType, equals, matches, inRange, hasLength, contains
* (see utils/test-runner.ts for full schema)
*/
import { existsSync, readFileSync } from 'node:fs';
import { join, resolve } from 'node:path';
import { AgentledClient } from '../client.js';
import { printOutput, printError } from '../utils/output.js';
import { findWorkspaceDir } from '../utils/workspace-folder.js';
import { runStepFixture, runStepLive } from '../utils/test-runner.js';
export function registerTestCommand(program) {
program
.command('test <workflowId>')
.description('Run declarative test assertions against captured fixtures (zero credits) or live API calls (--live).')
.option('--live', 'Use live API calls (costs credits for AI/app steps)', false)
.option('--step <stepId>', 'Run assertions for a single step only')
.option('--format <fmt>', 'Output format', 'json')
.action(async (workflowId, opts) => {
try {
const wsDir = findWorkspaceDir();
const candidates = [];
if (wsDir)
candidates.push(join(wsDir, 'tests', `${workflowId}.test.json`));
candidates.push(resolve(`tests/${workflowId}.test.json`), resolve(`${workflowId}.test.json`));
const testFilePath = candidates.find(existsSync);
if (!testFilePath) {
printError(`Test file not found for workflow ${workflowId}. Expected: tests/${workflowId}.test.json. Run "agentled workflows pull ${workflowId}" to generate a skeleton.`);
return;
}
let testFile;
try {
testFile = JSON.parse(readFileSync(testFilePath, 'utf-8'));
}
catch (err) {
printError(`Failed to parse test file: ${err instanceof Error ? err.message : String(err)}`);
return;
}
let stepsToRun = testFile.steps ?? [];
if (opts.step) {
stepsToRun = stepsToRun.filter((s) => s.stepId === opts.step);
if (stepsToRun.length === 0) {
printError(`Step "${opts.step}" not found in test file.`);
return;
}
}
const client = opts.live ? new AgentledClient() : null;
const results = [];
for (const step of stepsToRun) {
if (step.skip) {
results.push({
stepId: step.stepId,
description: step.description,
mode: 'skipped',
passed: 0,
failed: 0,
skipped: step.assertions.length,
errors: ['Skipped (skip: true)'],
});
continue;
}
const result = opts.live && client
? await runStepLive(step, client)
: runStepFixture(step, wsDir);
results.push(result);
}
const summary = {
totalPassed: results.reduce((a, r) => a + r.passed, 0),
totalFailed: results.reduce((a, r) => a + r.failed, 0),
totalSkipped: results.reduce((a, r) => a + r.skipped, 0),
stepsFailed: results.filter((r) => r.failed > 0).length,
stepsSkipped: results.filter((r) => r.mode === 'skipped').length,
};
printOutput({
workflowId,
workflowName: testFile.workflowName,
mode: opts.live ? 'live' : 'fixture',
testFilePath,
summary,
results,
}, (opts.format ?? 'json'));
if (summary.totalFailed > 0)
process.exitCode = 1;
}
catch (e) {
printError(e instanceof Error ? e.message : String(e));
}
});
}
//# sourceMappingURL=test.js.map