brainstorming
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or planning anything new, in software or out of it (a talk, a business, a renovation).
test-cli-applicationWrite integration tests for a Node.js CLI application using the built-in node:test module. Covers the exec helper pattern, output assertions, filesystem state verification, cleanup hooks, JSON output parsing, error case testing, and state restoration after destructive tests. Use when adding tests to an existing CLI, testing a new command, verifying adapter …
Independent GitHub snapshot · Automated classification · Original documentation governs
以內建 node:test 模組與 execSync 為 Node.js CLI 寫整合測試。
cli/index.js)import { describe, it, before, after } from 'node:test';
import assert from 'node:assert/strict';
import { execSync } from 'child_process';
import { existsSync, rmSync } from 'fs';
import { resolve } from 'path';
const CLI = 'node cli/index.js';
const ROOT = process.cwd();
function run(args) {
return execSync(`${CLI} ${args}`, {
cwd: ROOT,
encoding: 'utf8',
timeout: 10000,
});
}
關鍵設計決定:
node:test 內建——無需測試執行器依賴execSync 將 CLI 作子程序執行——測實際二進位,非內部函式encoding: 'utf8' 給字串輸出以供正則匹配ROOT 以求可重現預期: 一測試文件,自 node:test 引入並有可運作之 run() 助手。
失敗時: 若 node:test 不可用,你之 Node.js 版本低於 18。升級或用墊片。
煙霧測試驗 CLI 啟動、解析參數並產預期輸出形:
describe('meta', () => {
it('shows version', () => {
const out = run('--version');
assert.match(out, /\d+\.\d+\.\d+/);
});
it('shows help with all commands', () => {
const out = run('--help');
assert.match(out, /install/);
assert.match(out, /list/);
assert.match(out, /detect/);
});
});
describe('registry', () => {
it('list shows expected counts', () => {
const out = run('list --domains');
assert.match(out, /\d+ domains/);
});
it('search finds known items', () => {
const out = run('search "docker"');
assert.match(out, /result\(s\) for "docker"/);
});
it('search returns 0 for nonsense', () => {
const out = run('search "xyzzy-nonexistent"');
assert.match(out, /0 result/);
});
});
煙霧測試模式:
--version 與 --help 永遠運作預期: 煙霧測試確認 CLI 功能且資料已載。
失敗時: 若註冊表計數常變,用 \d+ 而非硬編數。
生命週期測試驗建 → 驗 → 刪序列含清理:
describe('install', () => {
const testPath = resolve(ROOT, '.agents/skills/commit-changes');
after(() => {
// Always clean up, even if tests fail
try { rmSync(testPath); } catch {}
try { rmSync(resolve(ROOT, '.agents/skills'), { recursive: true }); } catch {}
try { rmSync(resolve(ROOT, '.agents'), { recursive: true }); } catch {}
});
it('dry-run does not create files', () => {
const out = run('install commit-changes --dry-run');
assert.match(out, /DRY RUN/);
assert.ok(!existsSync(testPath));
});
it('installs creates the target', () => {
run('install commit-changes');
assert.ok(existsSync(testPath));
});
it('skips already installed', () => {
const out = run('install commit-changes');
assert.match(out, /skipped/);
});
it('uninstall removes the target', () => {
run('uninstall commit-changes');
assert.ok(!existsSync(testPath));
});
});
清理規則:
after() 鉤,非 afterEach()——生命週期測試彼此建於上try/catch——清理勿致測試套件失敗預期: 測試於 describe 區塊內順序執行,清理即使失敗亦執。
失敗時: 若測試並行執行(node:test 中非預設),以 { concurrency: 1 } 強制順序。
無變更地測每適配器之目標路徑:
describe('adapter: cursor (dry-run)', () => {
it('targets .cursor/skills/ path', () => {
const out = run('install commit-changes --framework cursor --dry-run');
assert.match(out, /\.cursor\/skills/i);
});
});
describe('adapter: copilot (dry-run)', () => {
it('targets .github/ path', () => {
const out = run('install commit-changes --framework copilot --dry-run');
assert.match(out, /\.github/i);
});
});
此模式可規模至任意數之適配器。每測試:
--framework 繞自動偵測--dry-run 故無檔建立預期: 每適配器一 describe 區塊,每塊至少含路徑斷言。
失敗時: 若適配器於專案中不存,測試將以「Unknown framework」失敗。此正確——適配器測試應僅為已實適配器存在。
describe('errors', () => {
it('rejects unknown items', () => {
assert.throws(
() => run('install nonexistent-skill-xyz'),
/No matching items|Unknown/,
);
});
it('rejects unknown framework', () => {
assert.throws(
() => run('install commit-changes --framework nonexistent'),
/Unknown framework/,
);
});
it('handles missing state gracefully', () => {
assert.throws(
() => run('scatter nonexistent-team'),
/not burning|Unknown/,
);
});
});
錯誤測試模式:
assert.throws 捕 execSync 之非零退出碼預期: 所有錯誤路徑產非零退出碼與有用訊息。
失敗時: execSync 於非零退出時拋。錯誤之 stderr 或 stdout 含訊息。若 assert.throws 正則不符,檢 error.stdout。
describe('json output', () => {
it('campfire --json outputs valid JSON', () => {
const out = run('campfire --json');
const data = JSON.parse(out);
assert.ok(typeof data.totalTeams === 'number');
assert.ok(Array.isArray(data.fires));
});
it('gather --dry-run --json outputs structured data', () => {
const out = run('gather tending --dry-run --json');
// JSON may follow a DRY RUN header — extract from first '{'
const jsonStart = out.indexOf('{');
assert.ok(jsonStart >= 0, 'Should contain JSON');
const data = JSON.parse(out.slice(jsonStart));
assert.equal(data.team, 'tending');
});
});
JSON 測試陷阱:
{ 字符提取 JSON預期: JSON 輸出可解析且含預期鍵。
失敗時: 若 JSON.parse 失敗,命令或將人讀文本與 JSON 混。要麼修命令於 --json 模式下出純 JSON,要麼提取 JSON 子串。
describe('stateful commands', () => {
const stateDir = resolve(ROOT, '.agent-almanac');
after(() => {
// Remove state file created by tests
try { rmSync(stateDir, { recursive: true }); } catch {}
});
// Tests that create/modify state...
});
// Restore symlinks that destructive tests may remove
describe('destructive tests', () => {
after(() => {
// Restore symlinks that scatter/uninstall removed
const skills = ['heal', 'meditate', 'remote-viewing'];
for (const skill of skills) {
const link = resolve(ROOT, `.claude/skills/${skill}`);
if (!existsSync(link)) {
try {
execSync(`ln -s ../../skills/${skill} ${link}`, { cwd: ROOT });
} catch {}
}
}
});
});
狀態復原規則:
.agent-almanac/state.json)必須於測後清scatter/uninstall 移除之符號連結必須復init 建之資訊清單檔(agent-almanac.yml)必須移除after() 鉤以反宣告順序執——將復鉤宣告於最後預期: 測試套件離專案於其所遇之同狀態。
失敗時: 若 CI 報告測試執行後有遺留檔,將清理加至 after()。測後用 git status 偵測洩之狀態。
node --test cli/test/cli.test.js 執行--version、--help 與註冊表載入\d+ 正則或動態讀計數,而非斷言 329 skills。node:test 預設按宣告順序執套件,但套件內測試或不然。於單一 describe 內用生命週期套件(建 → 驗 → 刪)以保證順序。after() 仍執。但若於 before() 中拋,後續測試與 after() 或不執。before() 保持最小。execSync。或用 echo y | 管道,或確保測試中永遠傳 --yes。.claude/skills/ 或 .agents/skills/ 建檔之測試修工作樹。CI 或於「dirty working directory」檢查上失敗。永遠清理。scaffold-cli-command — 建此等測試所驗之命令build-cli-plugin — 建步驟四所測之適配器design-cli-output — 測試所斷言之輸出模式