mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
99 lines
3.1 KiB
JavaScript
99 lines
3.1 KiB
JavaScript
/**
|
|
* ccw cli exec --final output mode
|
|
*
|
|
* Ensures programmatic callers can get a clean final agent result without
|
|
* banners/spinner/summary noise on stdout.
|
|
*/
|
|
|
|
import { afterEach, describe, it, mock } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import http from 'node:http';
|
|
import { mkdtempSync, rmSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
|
|
const cliCommandPath = new URL('../dist/commands/cli.js', import.meta.url).href;
|
|
const cliExecutorPath = new URL('../dist/tools/cli-executor.js', import.meta.url).href;
|
|
const historyStorePath = new URL('../dist/tools/cli-history-store.js', import.meta.url).href;
|
|
|
|
function stubHttpRequest() {
|
|
mock.method(http, 'request', () => {
|
|
const req = {
|
|
on(event, handler) {
|
|
if (event === 'socket') handler({ unref() {} });
|
|
return req;
|
|
},
|
|
write() {},
|
|
end() {},
|
|
destroy() {},
|
|
};
|
|
return req;
|
|
});
|
|
}
|
|
|
|
describe('ccw cli exec --final', async () => {
|
|
afterEach(() => {
|
|
mock.restoreAll();
|
|
});
|
|
|
|
it('writes only finalOutput to stdout (no banner/summary)', async () => {
|
|
stubHttpRequest();
|
|
|
|
const testHome = mkdtempSync(join(tmpdir(), 'ccw-cli-final-only-'));
|
|
const prevHome = process.env.CCW_DATA_DIR;
|
|
process.env.CCW_DATA_DIR = testHome;
|
|
|
|
// Ensure the CLI doesn't wait for stdin in Node's test runner environment.
|
|
const prevStdinIsTty = process.stdin.isTTY;
|
|
Object.defineProperty(process.stdin, 'isTTY', { value: true, configurable: true });
|
|
|
|
const cliModule = await import(cliCommandPath);
|
|
const cliExecutorModule = await import(cliExecutorPath);
|
|
const historyStoreModule = await import(historyStorePath);
|
|
|
|
const stdoutWrites = [];
|
|
mock.method(process.stdout, 'write', (chunk) => {
|
|
stdoutWrites.push(String(chunk));
|
|
return true;
|
|
});
|
|
mock.method(console, 'log', () => {});
|
|
mock.method(console, 'error', () => {});
|
|
|
|
mock.method(cliExecutorModule.cliExecutorTool, 'execute', async () => {
|
|
return {
|
|
success: true,
|
|
stdout: 'STDOUT_SHOULD_NOT_WIN',
|
|
stderr: '',
|
|
parsedOutput: 'PARSED_SHOULD_NOT_WIN',
|
|
finalOutput: 'FINAL',
|
|
execution: { id: 'EXEC-FINAL', duration_ms: 1, status: 'success' },
|
|
conversation: { turn_count: 1, total_duration_ms: 1 },
|
|
};
|
|
});
|
|
|
|
// Prevent the command from terminating the test runner.
|
|
mock.method(process, 'exit', () => {});
|
|
|
|
// Ensure the CLI's internal delayed exit timer doesn't keep the test process alive.
|
|
const realSetTimeout = globalThis.setTimeout;
|
|
mock.method(globalThis, 'setTimeout', (fn, ms, ...args) => {
|
|
const t = realSetTimeout(fn, ms, ...args);
|
|
t?.unref?.();
|
|
return t;
|
|
});
|
|
|
|
await cliModule.cliCommand('exec', [], { prompt: 'Hello', tool: 'gemini', final: true });
|
|
|
|
assert.equal(stdoutWrites.join(''), 'FINAL');
|
|
|
|
try {
|
|
historyStoreModule?.closeAllStores?.();
|
|
} catch {
|
|
// ignore
|
|
}
|
|
Object.defineProperty(process.stdin, 'isTTY', { value: prevStdinIsTty, configurable: true });
|
|
process.env.CCW_DATA_DIR = prevHome;
|
|
rmSync(testHome, { recursive: true, force: true });
|
|
});
|
|
});
|