Files
wehub-resource-sync acf542cac6
Sync SKILL.md and rules / sync (push) Failing after 1s
chore: import upstream snapshot with attribution
2026-07-13 12:07:10 +08:00

127 lines
4.9 KiB
JavaScript

#!/usr/bin/env node
// Tests for src/tools/caveman-init.js — fixture-based.
// Run: node tests/test_caveman_init.js
const fs = require('fs');
const path = require('path');
const os = require('os');
const assert = require('assert');
const { execFileSync } = require('child_process');
const ROOT = path.resolve(__dirname, '..');
const INIT = path.join(ROOT, 'src', 'tools', 'caveman-init.js');
let passed = 0;
let failed = 0;
// Point OPENCLAW_WORKSPACE at a nonexistent dir inside the fixture so the
// openclaw target reports skipped-workspace-missing instead of writing to
// the developer's real ~/.openclaw/workspace.
function runInit(tmp, ...args) {
return execFileSync(process.execPath, [INIT, tmp, ...args], {
encoding: 'utf8',
env: { ...process.env, OPENCLAW_WORKSPACE: path.join(tmp, 'no-openclaw') },
});
}
function test(name, fn) {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'caveman-init-test-'));
try {
fn(tmp);
passed++;
console.log(` ✓ ${name}`);
} catch (e) {
failed++;
console.error(` ✗ ${name}\n ${e.message}`);
} finally {
fs.rmSync(tmp, { recursive: true, force: true });
}
}
console.log('caveman-init tests\n');
test('greenfield: creates all rule files with proper frontmatter', (tmp) => {
runInit(tmp);
const cursor = fs.readFileSync(path.join(tmp, '.cursor/rules/caveman.mdc'), 'utf8');
assert.match(cursor, /alwaysApply: true/);
assert.match(cursor, /Respond terse like smart caveman/);
const windsurf = fs.readFileSync(path.join(tmp, '.windsurf/rules/caveman.md'), 'utf8');
assert.match(windsurf, /trigger: always_on/);
const cline = fs.readFileSync(path.join(tmp, '.clinerules/caveman.md'), 'utf8');
assert.match(cline, /^Respond terse/);
const copilot = fs.readFileSync(path.join(tmp, '.github/copilot-instructions.md'), 'utf8');
assert.match(copilot, /Respond terse/);
const agents = fs.readFileSync(path.join(tmp, 'AGENTS.md'), 'utf8');
assert.match(agents, /Respond terse/);
const opencode = fs.readFileSync(path.join(tmp, '.opencode/AGENTS.md'), 'utf8');
assert.match(opencode, /Respond terse/);
});
test('idempotent: re-running on a clean install skips all', (tmp) => {
runInit(tmp);
const out = runInit(tmp);
// 6 repo rule files skipped-already-installed + openclaw skipped (no workspace)
assert.match(out, /7 skipped/);
assert.doesNotMatch(out, /[1-9]\d* added/);
});
test('append mode: existing AGENTS.md gets caveman appended (not replaced)', (tmp) => {
fs.writeFileSync(path.join(tmp, 'AGENTS.md'), '# My project\n\nDo not delete me.\n');
runInit(tmp);
const agents = fs.readFileSync(path.join(tmp, 'AGENTS.md'), 'utf8');
assert.match(agents, /Do not delete me/);
assert.match(agents, /Respond terse like smart caveman/);
});
test('skip mode: existing .cursor rule is not overwritten without --force', (tmp) => {
const dir = path.join(tmp, '.cursor/rules');
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(path.join(dir, 'caveman.mdc'), '# original\nDo not delete me.\n');
const out = runInit(tmp);
assert.match(out, /\? .*\.cursor\/rules\/caveman\.mdc/);
const after = fs.readFileSync(path.join(dir, 'caveman.mdc'), 'utf8');
assert.strictEqual(after, '# original\nDo not delete me.\n');
});
test('--force overwrites existing rule files', (tmp) => {
const dir = path.join(tmp, '.cursor/rules');
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(path.join(dir, 'caveman.mdc'), '# original\n');
runInit(tmp, '--force');
const after = fs.readFileSync(path.join(dir, 'caveman.mdc'), 'utf8');
assert.match(after, /alwaysApply: true/);
assert.match(after, /Respond terse/);
});
test('--dry-run: announces but writes nothing', (tmp) => {
const out = runInit(tmp, '--dry-run');
assert.match(out, /\(dry run\)/);
assert.match(out, /6 added/);
assert.ok(!fs.existsSync(path.join(tmp, '.cursor')));
assert.ok(!fs.existsSync(path.join(tmp, '.windsurf')));
assert.ok(!fs.existsSync(path.join(tmp, '.clinerules')));
assert.ok(!fs.existsSync(path.join(tmp, '.github/copilot-instructions.md')));
assert.ok(!fs.existsSync(path.join(tmp, '.opencode')));
assert.ok(!fs.existsSync(path.join(tmp, 'AGENTS.md')));
});
test('--only filters to one target', (tmp) => {
const out = runInit(tmp, '--only', 'cline');
assert.match(out, /1 added/);
assert.ok(fs.existsSync(path.join(tmp, '.clinerules/caveman.md')));
assert.ok(!fs.existsSync(path.join(tmp, '.cursor')));
});
test('detects sentinel and skips files that already have caveman content', (tmp) => {
// Hand-write a file that already contains the rule (simulating prior install).
const dir = path.join(tmp, '.clinerules');
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(path.join(dir, 'caveman.md'),
'# Existing\n\nRespond terse like smart caveman. Hello.\n');
const out = runInit(tmp, '--only', 'cline');
assert.match(out, /skipped-already-installed/);
});
console.log(`\n${passed} passed, ${failed} failed`);
process.exit(failed ? 1 : 0);