mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-02-19 04:33:11 +08:00
test: add 6 tests for command validation and session content verification
- validate-commands: creates: line skipping, valid cross-refs, unclosed code blocks, valid workflow diagrams - session-end: backtick escaping in session files, tools/files in output
This commit is contained in:
@@ -575,6 +575,68 @@ function runTests() {
|
||||
cleanupTestDir(testDir); cleanupTestDir(agentsDir); cleanupTestDir(skillsDir);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('skips command references on creates: lines', () => {
|
||||
const testDir = createTestDir();
|
||||
const agentsDir = createTestDir();
|
||||
const skillsDir = createTestDir();
|
||||
// "Creates: `/new-table`" should NOT flag /new-table as a broken ref
|
||||
fs.writeFileSync(path.join(testDir, 'gen.md'),
|
||||
'# Generator\n\n→ Creates: `/new-table`\nWould create: `/new-endpoint`');
|
||||
|
||||
const result = runValidatorWithDirs('validate-commands', {
|
||||
COMMANDS_DIR: testDir, AGENTS_DIR: agentsDir, SKILLS_DIR: skillsDir
|
||||
});
|
||||
assert.strictEqual(result.code, 0, 'Should skip creates: lines');
|
||||
cleanupTestDir(testDir); cleanupTestDir(agentsDir); cleanupTestDir(skillsDir);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('accepts valid cross-reference between commands', () => {
|
||||
const testDir = createTestDir();
|
||||
const agentsDir = createTestDir();
|
||||
const skillsDir = createTestDir();
|
||||
fs.writeFileSync(path.join(testDir, 'build.md'), '# Build\nSee also `/deploy` for deployment.');
|
||||
fs.writeFileSync(path.join(testDir, 'deploy.md'), '# Deploy\nRun `/build` first.');
|
||||
|
||||
const result = runValidatorWithDirs('validate-commands', {
|
||||
COMMANDS_DIR: testDir, AGENTS_DIR: agentsDir, SKILLS_DIR: skillsDir
|
||||
});
|
||||
assert.strictEqual(result.code, 0, 'Should accept valid cross-refs');
|
||||
assert.ok(result.stdout.includes('Validated 2'), 'Should validate both');
|
||||
cleanupTestDir(testDir); cleanupTestDir(agentsDir); cleanupTestDir(skillsDir);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('checks references in unclosed code blocks', () => {
|
||||
const testDir = createTestDir();
|
||||
const agentsDir = createTestDir();
|
||||
const skillsDir = createTestDir();
|
||||
// Unclosed code block: the ``` regex won't strip it, so refs inside are checked
|
||||
fs.writeFileSync(path.join(testDir, 'bad.md'),
|
||||
'# Command\n\n```\n`/phantom-cmd`\nno closing block');
|
||||
|
||||
const result = runValidatorWithDirs('validate-commands', {
|
||||
COMMANDS_DIR: testDir, AGENTS_DIR: agentsDir, SKILLS_DIR: skillsDir
|
||||
});
|
||||
// Unclosed code blocks are NOT stripped, so refs inside are validated
|
||||
assert.strictEqual(result.code, 1, 'Should check refs in unclosed code blocks');
|
||||
assert.ok(result.stderr.includes('phantom-cmd'), 'Should report broken ref from unclosed block');
|
||||
cleanupTestDir(testDir); cleanupTestDir(agentsDir); cleanupTestDir(skillsDir);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('validates valid workflow diagram with known agents', () => {
|
||||
const testDir = createTestDir();
|
||||
const agentsDir = createTestDir();
|
||||
const skillsDir = createTestDir();
|
||||
fs.writeFileSync(path.join(agentsDir, 'planner.md'), '---\nmodel: sonnet\ntools: Read\n---\n# P');
|
||||
fs.writeFileSync(path.join(agentsDir, 'reviewer.md'), '---\nmodel: sonnet\ntools: Read\n---\n# R');
|
||||
fs.writeFileSync(path.join(testDir, 'flow.md'), '# Workflow\n\nplanner -> reviewer');
|
||||
|
||||
const result = runValidatorWithDirs('validate-commands', {
|
||||
COMMANDS_DIR: testDir, AGENTS_DIR: agentsDir, SKILLS_DIR: skillsDir
|
||||
});
|
||||
assert.strictEqual(result.code, 0, 'Should pass on valid workflow');
|
||||
cleanupTestDir(testDir); cleanupTestDir(agentsDir); cleanupTestDir(skillsDir);
|
||||
})) passed++; else failed++;
|
||||
|
||||
// ==========================================
|
||||
// validate-rules.js
|
||||
// ==========================================
|
||||
|
||||
@@ -749,6 +749,71 @@ async function runTests() {
|
||||
cleanupTestDir(testDir);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (await asyncTest('escapes backticks in user messages in session file', async () => {
|
||||
const testDir = createTestDir();
|
||||
const transcriptPath = path.join(testDir, 'transcript.jsonl');
|
||||
|
||||
// User messages with backticks that could break markdown
|
||||
const lines = [
|
||||
'{"type":"user","content":"Fix the `handleAuth` function in `auth.ts`"}',
|
||||
'{"type":"user","content":"Run `npm test` to verify"}',
|
||||
];
|
||||
fs.writeFileSync(transcriptPath, lines.join('\n'));
|
||||
|
||||
const stdinJson = JSON.stringify({ transcript_path: transcriptPath });
|
||||
const result = await runScript(path.join(scriptsDir, 'session-end.js'), stdinJson, {
|
||||
HOME: testDir
|
||||
});
|
||||
assert.strictEqual(result.code, 0, 'Should handle backticks without crash');
|
||||
|
||||
// Find the session file in the temp HOME
|
||||
const claudeDir = path.join(testDir, '.claude', 'sessions');
|
||||
if (fs.existsSync(claudeDir)) {
|
||||
const files = fs.readdirSync(claudeDir).filter(f => f.endsWith('.tmp'));
|
||||
if (files.length > 0) {
|
||||
const content = fs.readFileSync(path.join(claudeDir, files[0]), 'utf8');
|
||||
// Backticks should be escaped in the output
|
||||
assert.ok(content.includes('\\`'), 'Should escape backticks in session file');
|
||||
assert.ok(!content.includes('`handleAuth`'), 'Raw backticks should be escaped');
|
||||
}
|
||||
}
|
||||
cleanupTestDir(testDir);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (await asyncTest('session file contains tools used and files modified', async () => {
|
||||
const testDir = createTestDir();
|
||||
const transcriptPath = path.join(testDir, 'transcript.jsonl');
|
||||
|
||||
const lines = [
|
||||
'{"type":"user","content":"Edit the config"}',
|
||||
'{"type":"tool_use","tool_name":"Edit","tool_input":{"file_path":"/src/config.ts"}}',
|
||||
'{"type":"tool_use","tool_name":"Read","tool_input":{"file_path":"/src/utils.ts"}}',
|
||||
'{"type":"tool_use","tool_name":"Write","tool_input":{"file_path":"/src/new-file.ts"}}',
|
||||
];
|
||||
fs.writeFileSync(transcriptPath, lines.join('\n'));
|
||||
|
||||
const stdinJson = JSON.stringify({ transcript_path: transcriptPath });
|
||||
const result = await runScript(path.join(scriptsDir, 'session-end.js'), stdinJson, {
|
||||
HOME: testDir
|
||||
});
|
||||
assert.strictEqual(result.code, 0);
|
||||
|
||||
const claudeDir = path.join(testDir, '.claude', 'sessions');
|
||||
if (fs.existsSync(claudeDir)) {
|
||||
const files = fs.readdirSync(claudeDir).filter(f => f.endsWith('.tmp'));
|
||||
if (files.length > 0) {
|
||||
const content = fs.readFileSync(path.join(claudeDir, files[0]), 'utf8');
|
||||
// Should contain files modified (Edit and Write, not Read)
|
||||
assert.ok(content.includes('/src/config.ts'), 'Should list edited file');
|
||||
assert.ok(content.includes('/src/new-file.ts'), 'Should list written file');
|
||||
// Should contain tools used
|
||||
assert.ok(content.includes('Edit'), 'Should list Edit tool');
|
||||
assert.ok(content.includes('Read'), 'Should list Read tool');
|
||||
}
|
||||
}
|
||||
cleanupTestDir(testDir);
|
||||
})) passed++; else failed++;
|
||||
|
||||
// hooks.json validation
|
||||
console.log('\nhooks.json Validation:');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user