mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-03-30 21:53:28 +08:00
fix: box alignment in test runner, update metadata counts, add 18 tests
- Fix run-all.js box alignment (hardcoded spaces 1 char short, now using dynamic padEnd) - Update .opencode/index.ts metadata (12→13 agents, 24→31 commands, 16→37 skills) - Add commandExists edge case tests (empty, spaces, path separators, metacharacters) - Add findFiles edge case tests (? wildcard, mtime sorting, maxAge filtering) - Add ensureDir race condition and return value tests - Add runCommand output trimming and failure tests - Add pre-compact session annotation and compaction log timestamp tests - Add check-console-log invalid JSON handling test - Add replaceInFile capture group test - Add readStdinJson Promise type check
This commit is contained in:
@@ -2,11 +2,11 @@
|
||||
* Everything Claude Code (ECC) Plugin for OpenCode
|
||||
*
|
||||
* This package provides a complete OpenCode plugin with:
|
||||
* - 12 specialized agents (planner, architect, code-reviewer, etc.)
|
||||
* - 24 commands (/plan, /tdd, /code-review, etc.)
|
||||
* - 13 specialized agents (planner, architect, code-reviewer, etc.)
|
||||
* - 31 commands (/plan, /tdd, /code-review, etc.)
|
||||
* - Plugin hooks (auto-format, TypeScript check, console.log warning, etc.)
|
||||
* - Custom tools (run-tests, check-coverage, security-audit)
|
||||
* - 16 skills (coding-standards, security-review, tdd-workflow, etc.)
|
||||
* - 37 skills (coding-standards, security-review, tdd-workflow, etc.)
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
@@ -48,9 +48,9 @@ export const metadata = {
|
||||
description: "Everything Claude Code plugin for OpenCode",
|
||||
author: "affaan-m",
|
||||
features: {
|
||||
agents: 12,
|
||||
commands: 24,
|
||||
skills: 16,
|
||||
agents: 13,
|
||||
commands: 31,
|
||||
skills: 37,
|
||||
hookEvents: [
|
||||
"file.edited",
|
||||
"tool.execute.before",
|
||||
|
||||
@@ -218,6 +218,13 @@ async function runTests() {
|
||||
assert.strictEqual(result.code, 0);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (await asyncTest('handles invalid JSON stdin gracefully', async () => {
|
||||
const result = await runScript(path.join(scriptsDir, 'check-console-log.js'), 'not valid json');
|
||||
assert.strictEqual(result.code, 0, 'Should exit 0 on invalid JSON');
|
||||
// Should still pass through the data
|
||||
assert.ok(result.stdout.includes('not valid json'), 'Should pass through invalid data');
|
||||
})) passed++; else failed++;
|
||||
|
||||
// session-end.js tests
|
||||
console.log('\nsession-end.js:');
|
||||
|
||||
@@ -283,6 +290,53 @@ async function runTests() {
|
||||
assert.ok(fs.existsSync(logFile), 'Compaction log should exist');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (await asyncTest('annotates active session file with compaction marker', async () => {
|
||||
const isoHome = path.join(os.tmpdir(), `ecc-compact-annotate-${Date.now()}`);
|
||||
const sessionsDir = path.join(isoHome, '.claude', 'sessions');
|
||||
fs.mkdirSync(sessionsDir, { recursive: true });
|
||||
|
||||
// Create an active .tmp session file
|
||||
const sessionFile = path.join(sessionsDir, '2026-02-11-test-session.tmp');
|
||||
fs.writeFileSync(sessionFile, '# Session: 2026-02-11\n**Started:** 10:00\n');
|
||||
|
||||
try {
|
||||
await runScript(path.join(scriptsDir, 'pre-compact.js'), '', {
|
||||
HOME: isoHome, USERPROFILE: isoHome
|
||||
});
|
||||
|
||||
const content = fs.readFileSync(sessionFile, 'utf8');
|
||||
assert.ok(
|
||||
content.includes('Compaction occurred'),
|
||||
'Should annotate the session file with compaction marker'
|
||||
);
|
||||
} finally {
|
||||
fs.rmSync(isoHome, { recursive: true, force: true });
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (await asyncTest('compaction log contains timestamp', async () => {
|
||||
const isoHome = path.join(os.tmpdir(), `ecc-compact-ts-${Date.now()}`);
|
||||
const sessionsDir = path.join(isoHome, '.claude', 'sessions');
|
||||
fs.mkdirSync(sessionsDir, { recursive: true });
|
||||
|
||||
try {
|
||||
await runScript(path.join(scriptsDir, 'pre-compact.js'), '', {
|
||||
HOME: isoHome, USERPROFILE: isoHome
|
||||
});
|
||||
|
||||
const logFile = path.join(sessionsDir, 'compaction-log.txt');
|
||||
assert.ok(fs.existsSync(logFile), 'Compaction log should exist');
|
||||
const content = fs.readFileSync(logFile, 'utf8');
|
||||
// Should have a timestamp like [2026-02-11 14:30:00]
|
||||
assert.ok(
|
||||
/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\]/.test(content),
|
||||
`Log should contain timestamped entry, got: ${content.substring(0, 100)}`
|
||||
);
|
||||
} finally {
|
||||
fs.rmSync(isoHome, { recursive: true, force: true });
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
// suggest-compact.js tests
|
||||
console.log('\nsuggest-compact.js:');
|
||||
|
||||
|
||||
@@ -715,6 +715,171 @@ function runTests() {
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
// commandExists edge cases
|
||||
console.log('\ncommandExists Edge Cases:');
|
||||
|
||||
if (test('commandExists rejects empty string', () => {
|
||||
assert.strictEqual(utils.commandExists(''), false, 'Empty string should not be a valid command');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('commandExists rejects command with spaces', () => {
|
||||
assert.strictEqual(utils.commandExists('my command'), false, 'Commands with spaces should be rejected');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('commandExists rejects command with path separators', () => {
|
||||
assert.strictEqual(utils.commandExists('/usr/bin/node'), false, 'Commands with / should be rejected');
|
||||
assert.strictEqual(utils.commandExists('..\\cmd'), false, 'Commands with \\ should be rejected');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('commandExists rejects shell metacharacters', () => {
|
||||
assert.strictEqual(utils.commandExists('cmd;ls'), false, 'Semicolons should be rejected');
|
||||
assert.strictEqual(utils.commandExists('$(whoami)'), false, 'Subshell syntax should be rejected');
|
||||
assert.strictEqual(utils.commandExists('cmd|cat'), false, 'Pipes should be rejected');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('commandExists allows dots and underscores', () => {
|
||||
// These are valid chars per the regex check — the command might not exist
|
||||
// but it shouldn't be rejected by the validator
|
||||
const dotResult = utils.commandExists('definitely.not.a.real.tool.12345');
|
||||
assert.strictEqual(typeof dotResult, 'boolean', 'Should return boolean, not throw');
|
||||
})) passed++; else failed++;
|
||||
|
||||
// findFiles edge cases
|
||||
console.log('\nfindFiles Edge Cases:');
|
||||
|
||||
if (test('findFiles with ? wildcard matches single character', () => {
|
||||
const testDir = path.join(utils.getTempDir(), `ff-qmark-${Date.now()}`);
|
||||
utils.ensureDir(testDir);
|
||||
try {
|
||||
fs.writeFileSync(path.join(testDir, 'a1.txt'), '');
|
||||
fs.writeFileSync(path.join(testDir, 'b2.txt'), '');
|
||||
fs.writeFileSync(path.join(testDir, 'abc.txt'), '');
|
||||
|
||||
const results = utils.findFiles(testDir, '??.txt');
|
||||
const names = results.map(r => path.basename(r.path)).sort();
|
||||
assert.deepStrictEqual(names, ['a1.txt', 'b2.txt'], 'Should match exactly 2-char basenames');
|
||||
} finally {
|
||||
fs.rmSync(testDir, { recursive: true, force: true });
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('findFiles sorts by mtime (newest first)', () => {
|
||||
const testDir = path.join(utils.getTempDir(), `ff-sort-${Date.now()}`);
|
||||
utils.ensureDir(testDir);
|
||||
try {
|
||||
const f1 = path.join(testDir, 'old.txt');
|
||||
const f2 = path.join(testDir, 'new.txt');
|
||||
fs.writeFileSync(f1, 'old');
|
||||
// Set older mtime on first file
|
||||
const past = new Date(Date.now() - 60000);
|
||||
fs.utimesSync(f1, past, past);
|
||||
fs.writeFileSync(f2, 'new');
|
||||
|
||||
const results = utils.findFiles(testDir, '*.txt');
|
||||
assert.strictEqual(results.length, 2);
|
||||
assert.ok(
|
||||
path.basename(results[0].path) === 'new.txt',
|
||||
`Newest file should be first, got ${path.basename(results[0].path)}`
|
||||
);
|
||||
} finally {
|
||||
fs.rmSync(testDir, { recursive: true, force: true });
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('findFiles with maxAge filters old files', () => {
|
||||
const testDir = path.join(utils.getTempDir(), `ff-age-${Date.now()}`);
|
||||
utils.ensureDir(testDir);
|
||||
try {
|
||||
const recent = path.join(testDir, 'recent.txt');
|
||||
const old = path.join(testDir, 'old.txt');
|
||||
fs.writeFileSync(recent, 'new');
|
||||
fs.writeFileSync(old, 'old');
|
||||
// Set mtime to 30 days ago
|
||||
const past = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
|
||||
fs.utimesSync(old, past, past);
|
||||
|
||||
const results = utils.findFiles(testDir, '*.txt', { maxAge: 7 });
|
||||
assert.strictEqual(results.length, 1, 'Should only return recent file');
|
||||
assert.ok(results[0].path.includes('recent.txt'), 'Should return the recent file');
|
||||
} finally {
|
||||
fs.rmSync(testDir, { recursive: true, force: true });
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
// ensureDir edge cases
|
||||
console.log('\nensureDir Edge Cases:');
|
||||
|
||||
if (test('ensureDir is safe for concurrent calls (EEXIST race)', () => {
|
||||
const testDir = path.join(utils.getTempDir(), `ensure-race-${Date.now()}`, 'nested');
|
||||
try {
|
||||
// Call concurrently — both should succeed without throwing
|
||||
const results = [utils.ensureDir(testDir), utils.ensureDir(testDir)];
|
||||
assert.strictEqual(results[0], testDir);
|
||||
assert.strictEqual(results[1], testDir);
|
||||
assert.ok(fs.existsSync(testDir));
|
||||
} finally {
|
||||
fs.rmSync(path.dirname(testDir), { recursive: true, force: true });
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('ensureDir returns the directory path', () => {
|
||||
const testDir = path.join(utils.getTempDir(), `ensure-ret-${Date.now()}`);
|
||||
try {
|
||||
const result = utils.ensureDir(testDir);
|
||||
assert.strictEqual(result, testDir, 'Should return the directory path');
|
||||
} finally {
|
||||
fs.rmSync(testDir, { recursive: true, force: true });
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
// runCommand edge cases
|
||||
console.log('\nrunCommand Edge Cases:');
|
||||
|
||||
if (test('runCommand returns trimmed output', () => {
|
||||
const result = utils.runCommand('echo " hello "');
|
||||
assert.strictEqual(result.success, true);
|
||||
assert.strictEqual(result.output, 'hello', 'Should trim leading/trailing whitespace');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('runCommand captures stderr on failure', () => {
|
||||
const result = utils.runCommand('node -e "process.exit(1)"');
|
||||
assert.strictEqual(result.success, false);
|
||||
assert.ok(typeof result.output === 'string', 'Output should be a string on failure');
|
||||
})) passed++; else failed++;
|
||||
|
||||
// getGitModifiedFiles edge cases
|
||||
console.log('\ngetGitModifiedFiles Edge Cases:');
|
||||
|
||||
if (test('getGitModifiedFiles returns array with empty patterns', () => {
|
||||
const files = utils.getGitModifiedFiles([]);
|
||||
assert.ok(Array.isArray(files), 'Should return array');
|
||||
})) passed++; else failed++;
|
||||
|
||||
// replaceInFile edge cases
|
||||
console.log('\nreplaceInFile Edge Cases:');
|
||||
|
||||
if (test('replaceInFile with regex capture groups works correctly', () => {
|
||||
const testFile = path.join(utils.getTempDir(), `replace-capture-${Date.now()}.txt`);
|
||||
try {
|
||||
utils.writeFile(testFile, 'version: 1.0.0');
|
||||
const result = utils.replaceInFile(testFile, /version: (\d+)\.(\d+)\.(\d+)/, 'version: $1.$2.99');
|
||||
assert.strictEqual(result, true);
|
||||
assert.strictEqual(utils.readFile(testFile), 'version: 1.0.99');
|
||||
} finally {
|
||||
fs.unlinkSync(testFile);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
// readStdinJson (function API, not actual stdin — more thorough edge cases)
|
||||
console.log('\nreadStdinJson Edge Cases:');
|
||||
|
||||
if (test('readStdinJson type check: returns a Promise', () => {
|
||||
// readStdinJson returns a Promise regardless of stdin state
|
||||
const result = utils.readStdinJson({ timeoutMs: 100 });
|
||||
assert.ok(result instanceof Promise, 'Should return a Promise');
|
||||
// Don't await — just verify it's a Promise type
|
||||
})) passed++; else failed++;
|
||||
|
||||
// Summary
|
||||
console.log('\n=== Test Results ===');
|
||||
console.log(`Passed: ${passed}`);
|
||||
|
||||
@@ -22,9 +22,12 @@ const testFiles = [
|
||||
'scripts/skill-create-output.test.js'
|
||||
];
|
||||
|
||||
console.log('╔══════════════════════════════════════════════════════════╗');
|
||||
console.log('║ Everything Claude Code - Test Suite ║');
|
||||
console.log('╚══════════════════════════════════════════════════════════╝');
|
||||
const BOX_W = 58; // inner width between ║ delimiters
|
||||
const boxLine = (s) => `║${s.padEnd(BOX_W)}║`;
|
||||
|
||||
console.log('╔' + '═'.repeat(BOX_W) + '╗');
|
||||
console.log(boxLine(' Everything Claude Code - Test Suite'));
|
||||
console.log('╚' + '═'.repeat(BOX_W) + '╝');
|
||||
console.log();
|
||||
|
||||
let totalPassed = 0;
|
||||
@@ -71,12 +74,12 @@ for (const testFile of testFiles) {
|
||||
|
||||
totalTests = totalPassed + totalFailed;
|
||||
|
||||
console.log('\n╔══════════════════════════════════════════════════════════╗');
|
||||
console.log('║ Final Results ║');
|
||||
console.log('╠══════════════════════════════════════════════════════════╣');
|
||||
console.log(`║ Total Tests: ${String(totalTests).padStart(4)} ║`);
|
||||
console.log(`║ Passed: ${String(totalPassed).padStart(4)} ✓ ║`);
|
||||
console.log(`║ Failed: ${String(totalFailed).padStart(4)} ${totalFailed > 0 ? '✗' : ' '} ║`);
|
||||
console.log('╚══════════════════════════════════════════════════════════╝');
|
||||
console.log('\n╔' + '═'.repeat(BOX_W) + '╗');
|
||||
console.log(boxLine(' Final Results'));
|
||||
console.log('╠' + '═'.repeat(BOX_W) + '╣');
|
||||
console.log(boxLine(` Total Tests: ${String(totalTests).padStart(4)}`));
|
||||
console.log(boxLine(` Passed: ${String(totalPassed).padStart(4)} ✓`));
|
||||
console.log(boxLine(` Failed: ${String(totalFailed).padStart(4)} ${totalFailed > 0 ? '✗' : ' '}`));
|
||||
console.log('╚' + '═'.repeat(BOX_W) + '╝');
|
||||
|
||||
process.exit(totalFailed > 0 ? 1 : 0);
|
||||
|
||||
Reference in New Issue
Block a user