mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-03-30 13:43:26 +08:00
feat: add C++ language support and hook tests (#539)
- agents: cpp-build-resolver, cpp-reviewer - commands: cpp-build, cpp-review, cpp-test - rules: cpp/ (coding-style, hooks, patterns, security, testing) - tests: 9 new hook test files with comprehensive coverage Cherry-picked from PR #436.
This commit is contained in:
145
tests/hooks/auto-tmux-dev.test.js
Normal file
145
tests/hooks/auto-tmux-dev.test.js
Normal file
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* Tests for scripts/hooks/auto-tmux-dev.js
|
||||
*
|
||||
* Tests dev server command transformation for tmux wrapping.
|
||||
*
|
||||
* Run with: node tests/hooks/auto-tmux-dev.test.js
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
const { spawnSync } = require('child_process');
|
||||
|
||||
const script = path.join(__dirname, '..', '..', 'scripts', 'hooks', 'auto-tmux-dev.js');
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` \u2713 ${name}`);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.log(` \u2717 ${name}`);
|
||||
console.log(` Error: ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function runScript(input) {
|
||||
const result = spawnSync('node', [script], {
|
||||
encoding: 'utf8',
|
||||
input: typeof input === 'string' ? input : JSON.stringify(input),
|
||||
timeout: 10000,
|
||||
});
|
||||
return {
|
||||
code: result.status || 0,
|
||||
stdout: result.stdout || '',
|
||||
stderr: result.stderr || '',
|
||||
};
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
console.log('\n=== Testing auto-tmux-dev.js ===\n');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
// Check if tmux is available for conditional tests
|
||||
const tmuxAvailable = spawnSync('which', ['tmux'], { encoding: 'utf8' }).status === 0;
|
||||
|
||||
console.log('Dev server detection:');
|
||||
|
||||
if (test('transforms npm run dev command', () => {
|
||||
const result = runScript({ tool_input: { command: 'npm run dev' } });
|
||||
assert.strictEqual(result.code, 0);
|
||||
const output = JSON.parse(result.stdout);
|
||||
if (process.platform !== 'win32' && tmuxAvailable) {
|
||||
assert.ok(output.tool_input.command.includes('tmux'), 'Should contain tmux');
|
||||
assert.ok(output.tool_input.command.includes('npm run dev'), 'Should contain original command');
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('transforms pnpm dev command', () => {
|
||||
const result = runScript({ tool_input: { command: 'pnpm dev' } });
|
||||
assert.strictEqual(result.code, 0);
|
||||
const output = JSON.parse(result.stdout);
|
||||
if (process.platform !== 'win32' && tmuxAvailable) {
|
||||
assert.ok(output.tool_input.command.includes('tmux'));
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('transforms yarn dev command', () => {
|
||||
const result = runScript({ tool_input: { command: 'yarn dev' } });
|
||||
assert.strictEqual(result.code, 0);
|
||||
const output = JSON.parse(result.stdout);
|
||||
if (process.platform !== 'win32' && tmuxAvailable) {
|
||||
assert.ok(output.tool_input.command.includes('tmux'));
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('transforms bun run dev command', () => {
|
||||
const result = runScript({ tool_input: { command: 'bun run dev' } });
|
||||
assert.strictEqual(result.code, 0);
|
||||
const output = JSON.parse(result.stdout);
|
||||
if (process.platform !== 'win32' && tmuxAvailable) {
|
||||
assert.ok(output.tool_input.command.includes('tmux'));
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log('\nNon-dev commands (pass-through):');
|
||||
|
||||
if (test('does not transform npm install', () => {
|
||||
const input = { tool_input: { command: 'npm install' } };
|
||||
const result = runScript(input);
|
||||
assert.strictEqual(result.code, 0);
|
||||
const output = JSON.parse(result.stdout);
|
||||
assert.strictEqual(output.tool_input.command, 'npm install');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('does not transform npm test', () => {
|
||||
const input = { tool_input: { command: 'npm test' } };
|
||||
const result = runScript(input);
|
||||
assert.strictEqual(result.code, 0);
|
||||
const output = JSON.parse(result.stdout);
|
||||
assert.strictEqual(output.tool_input.command, 'npm test');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('does not transform npm run build', () => {
|
||||
const input = { tool_input: { command: 'npm run build' } };
|
||||
const result = runScript(input);
|
||||
assert.strictEqual(result.code, 0);
|
||||
const output = JSON.parse(result.stdout);
|
||||
assert.strictEqual(output.tool_input.command, 'npm run build');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('does not transform npm run develop (partial match)', () => {
|
||||
const input = { tool_input: { command: 'npm run develop' } };
|
||||
const result = runScript(input);
|
||||
assert.strictEqual(result.code, 0);
|
||||
const output = JSON.parse(result.stdout);
|
||||
assert.strictEqual(output.tool_input.command, 'npm run develop');
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log('\nEdge cases:');
|
||||
|
||||
if (test('handles empty input gracefully', () => {
|
||||
const result = runScript('{}');
|
||||
assert.strictEqual(result.code, 0);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('handles invalid JSON gracefully', () => {
|
||||
const result = runScript('not json');
|
||||
assert.strictEqual(result.code, 0);
|
||||
assert.strictEqual(result.stdout, 'not json');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('passes through missing command field', () => {
|
||||
const input = { tool_input: {} };
|
||||
const result = runScript(input);
|
||||
assert.strictEqual(result.code, 0);
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
runTests();
|
||||
108
tests/hooks/check-hook-enabled.test.js
Normal file
108
tests/hooks/check-hook-enabled.test.js
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Tests for scripts/hooks/check-hook-enabled.js
|
||||
*
|
||||
* Tests the CLI wrapper around isHookEnabled.
|
||||
*
|
||||
* Run with: node tests/hooks/check-hook-enabled.test.js
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
const { spawnSync } = require('child_process');
|
||||
|
||||
const script = path.join(__dirname, '..', '..', 'scripts', 'hooks', 'check-hook-enabled.js');
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` \u2713 ${name}`);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.log(` \u2717 ${name}`);
|
||||
console.log(` Error: ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function runScript(args = [], envOverrides = {}) {
|
||||
const env = { ...process.env, ...envOverrides };
|
||||
// Remove potentially interfering env vars unless explicitly set
|
||||
if (!envOverrides.ECC_HOOK_PROFILE) delete env.ECC_HOOK_PROFILE;
|
||||
if (!envOverrides.ECC_DISABLED_HOOKS) delete env.ECC_DISABLED_HOOKS;
|
||||
|
||||
const result = spawnSync('node', [script, ...args], {
|
||||
encoding: 'utf8',
|
||||
timeout: 10000,
|
||||
env,
|
||||
});
|
||||
return {
|
||||
code: result.status || 0,
|
||||
stdout: result.stdout || '',
|
||||
stderr: result.stderr || '',
|
||||
};
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
console.log('\n=== Testing check-hook-enabled.js ===\n');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
console.log('No arguments:');
|
||||
|
||||
if (test('returns yes when no hookId provided', () => {
|
||||
const result = runScript([]);
|
||||
assert.strictEqual(result.stdout, 'yes');
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log('\nDefault profile (standard):');
|
||||
|
||||
if (test('returns yes for hook with default profiles', () => {
|
||||
const result = runScript(['my-hook']);
|
||||
assert.strictEqual(result.stdout, 'yes');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns yes for hook with standard,strict profiles', () => {
|
||||
const result = runScript(['my-hook', 'standard,strict']);
|
||||
assert.strictEqual(result.stdout, 'yes');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns no for hook with only strict profile', () => {
|
||||
const result = runScript(['my-hook', 'strict']);
|
||||
assert.strictEqual(result.stdout, 'no');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns no for hook with only minimal profile', () => {
|
||||
const result = runScript(['my-hook', 'minimal']);
|
||||
assert.strictEqual(result.stdout, 'no');
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log('\nDisabled hooks:');
|
||||
|
||||
if (test('returns no when hook is disabled via env', () => {
|
||||
const result = runScript(['my-hook'], { ECC_DISABLED_HOOKS: 'my-hook' });
|
||||
assert.strictEqual(result.stdout, 'no');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns yes when different hook is disabled', () => {
|
||||
const result = runScript(['my-hook'], { ECC_DISABLED_HOOKS: 'other-hook' });
|
||||
assert.strictEqual(result.stdout, 'yes');
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log('\nProfile overrides:');
|
||||
|
||||
if (test('returns yes for strict profile with strict-only hook', () => {
|
||||
const result = runScript(['my-hook', 'strict'], { ECC_HOOK_PROFILE: 'strict' });
|
||||
assert.strictEqual(result.stdout, 'yes');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns yes for minimal profile with minimal-only hook', () => {
|
||||
const result = runScript(['my-hook', 'minimal'], { ECC_HOOK_PROFILE: 'minimal' });
|
||||
assert.strictEqual(result.stdout, 'yes');
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
runTests();
|
||||
131
tests/hooks/cost-tracker.test.js
Normal file
131
tests/hooks/cost-tracker.test.js
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Tests for cost-tracker.js hook
|
||||
*
|
||||
* Run with: node tests/hooks/cost-tracker.test.js
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const { spawnSync } = require('child_process');
|
||||
|
||||
const script = path.join(__dirname, '..', '..', 'scripts', 'hooks', 'cost-tracker.js');
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` ✓ ${name}`);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.log(` ✗ ${name}`);
|
||||
console.log(` Error: ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function makeTempDir() {
|
||||
return fs.mkdtempSync(path.join(os.tmpdir(), 'cost-tracker-test-'));
|
||||
}
|
||||
|
||||
function runScript(input, envOverrides = {}) {
|
||||
const inputStr = typeof input === 'string' ? input : JSON.stringify(input);
|
||||
const result = spawnSync('node', [script], {
|
||||
encoding: 'utf8',
|
||||
input: inputStr,
|
||||
timeout: 10000,
|
||||
env: { ...process.env, ...envOverrides },
|
||||
});
|
||||
return { code: result.status || 0, stdout: result.stdout || '', stderr: result.stderr || '' };
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
console.log('\n=== Testing cost-tracker.js ===\n');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
// 1. Passes through input on stdout
|
||||
(test('passes through input on stdout', () => {
|
||||
const input = {
|
||||
model: 'claude-sonnet-4-20250514',
|
||||
usage: { input_tokens: 100, output_tokens: 50 },
|
||||
};
|
||||
const inputStr = JSON.stringify(input);
|
||||
const result = runScript(input);
|
||||
assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
|
||||
assert.strictEqual(result.stdout, inputStr, 'Expected stdout to match original input');
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
// 2. Creates metrics file when given valid usage data
|
||||
(test('creates metrics file when given valid usage data', () => {
|
||||
const tmpHome = makeTempDir();
|
||||
const input = {
|
||||
model: 'claude-sonnet-4-20250514',
|
||||
usage: { input_tokens: 1000, output_tokens: 500 },
|
||||
};
|
||||
const result = runScript(input, { HOME: tmpHome });
|
||||
assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
|
||||
|
||||
const metricsFile = path.join(tmpHome, '.claude', 'metrics', 'costs.jsonl');
|
||||
assert.ok(fs.existsSync(metricsFile), `Expected metrics file to exist at ${metricsFile}`);
|
||||
|
||||
const content = fs.readFileSync(metricsFile, 'utf8').trim();
|
||||
const row = JSON.parse(content);
|
||||
assert.strictEqual(row.input_tokens, 1000, 'Expected input_tokens to be 1000');
|
||||
assert.strictEqual(row.output_tokens, 500, 'Expected output_tokens to be 500');
|
||||
assert.ok(row.timestamp, 'Expected timestamp to be present');
|
||||
assert.ok(typeof row.estimated_cost_usd === 'number', 'Expected estimated_cost_usd to be a number');
|
||||
assert.ok(row.estimated_cost_usd > 0, 'Expected estimated_cost_usd to be positive');
|
||||
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
// 3. Handles empty input gracefully
|
||||
(test('handles empty input gracefully', () => {
|
||||
const tmpHome = makeTempDir();
|
||||
const result = runScript('', { HOME: tmpHome });
|
||||
assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
|
||||
// stdout should be empty since input was empty
|
||||
assert.strictEqual(result.stdout, '', 'Expected empty stdout for empty input');
|
||||
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
// 4. Handles invalid JSON gracefully
|
||||
(test('handles invalid JSON gracefully', () => {
|
||||
const tmpHome = makeTempDir();
|
||||
const invalidInput = 'not valid json {{{';
|
||||
const result = runScript(invalidInput, { HOME: tmpHome });
|
||||
assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
|
||||
// Should still pass through the raw input on stdout
|
||||
assert.strictEqual(result.stdout, invalidInput, 'Expected stdout to contain original invalid input');
|
||||
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
// 5. Handles missing usage fields gracefully
|
||||
(test('handles missing usage fields gracefully', () => {
|
||||
const tmpHome = makeTempDir();
|
||||
const input = { model: 'claude-sonnet-4-20250514' };
|
||||
const inputStr = JSON.stringify(input);
|
||||
const result = runScript(input, { HOME: tmpHome });
|
||||
assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
|
||||
assert.strictEqual(result.stdout, inputStr, 'Expected stdout to match original input');
|
||||
|
||||
const metricsFile = path.join(tmpHome, '.claude', 'metrics', 'costs.jsonl');
|
||||
assert.ok(fs.existsSync(metricsFile), 'Expected metrics file to exist even with missing usage');
|
||||
|
||||
const row = JSON.parse(fs.readFileSync(metricsFile, 'utf8').trim());
|
||||
assert.strictEqual(row.input_tokens, 0, 'Expected input_tokens to be 0 when missing');
|
||||
assert.strictEqual(row.output_tokens, 0, 'Expected output_tokens to be 0 when missing');
|
||||
assert.strictEqual(row.estimated_cost_usd, 0, 'Expected estimated_cost_usd to be 0 when no tokens');
|
||||
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
runTests();
|
||||
152
tests/hooks/doc-file-warning.test.js
Normal file
152
tests/hooks/doc-file-warning.test.js
Normal file
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
const { spawnSync } = require('child_process');
|
||||
|
||||
const script = path.join(__dirname, '..', '..', 'scripts', 'hooks', 'doc-file-warning.js');
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` ✓ ${name}`);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.log(` ✗ ${name}`);
|
||||
console.log(` Error: ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function runScript(input) {
|
||||
const result = spawnSync('node', [script], {
|
||||
encoding: 'utf8',
|
||||
input: JSON.stringify(input),
|
||||
timeout: 10000,
|
||||
});
|
||||
return { code: result.status || 0, stdout: result.stdout || '', stderr: result.stderr || '' };
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
console.log('\n=== Testing doc-file-warning.js ===\n');
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
// 1. Allowed standard doc files - no warning in stderr
|
||||
const standardFiles = [
|
||||
'README.md',
|
||||
'CLAUDE.md',
|
||||
'AGENTS.md',
|
||||
'CONTRIBUTING.md',
|
||||
'CHANGELOG.md',
|
||||
'LICENSE.md',
|
||||
'SKILL.md',
|
||||
'MEMORY.md',
|
||||
'WORKLOG.md',
|
||||
];
|
||||
for (const file of standardFiles) {
|
||||
(test(`allows standard doc file: ${file}`, () => {
|
||||
const { code, stderr } = runScript({ tool_input: { file_path: file } });
|
||||
assert.strictEqual(code, 0, `expected exit code 0, got ${code}`);
|
||||
assert.strictEqual(stderr, '', `expected no warning for ${file}, got: ${stderr}`);
|
||||
}) ? passed++ : failed++);
|
||||
}
|
||||
|
||||
// 2. Allowed directory paths - no warning
|
||||
const allowedDirPaths = [
|
||||
'docs/foo.md',
|
||||
'docs/guide/setup.md',
|
||||
'skills/bar.md',
|
||||
'skills/testing/tdd.md',
|
||||
'.history/session.md',
|
||||
'memory/patterns.md',
|
||||
'.claude/commands/deploy.md',
|
||||
'.claude/plans/roadmap.md',
|
||||
'.claude/projects/myproject.md',
|
||||
];
|
||||
for (const file of allowedDirPaths) {
|
||||
(test(`allows directory path: ${file}`, () => {
|
||||
const { code, stderr } = runScript({ tool_input: { file_path: file } });
|
||||
assert.strictEqual(code, 0, `expected exit code 0, got ${code}`);
|
||||
assert.strictEqual(stderr, '', `expected no warning for ${file}, got: ${stderr}`);
|
||||
}) ? passed++ : failed++);
|
||||
}
|
||||
|
||||
// 3. Allowed .plan.md files - no warning
|
||||
(test('allows .plan.md files', () => {
|
||||
const { code, stderr } = runScript({ tool_input: { file_path: 'feature.plan.md' } });
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(stderr, '', `expected no warning for .plan.md, got: ${stderr}`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
(test('allows nested .plan.md files', () => {
|
||||
const { code, stderr } = runScript({ tool_input: { file_path: 'src/refactor.plan.md' } });
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(stderr, '', `expected no warning for nested .plan.md, got: ${stderr}`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
// 4. Non-md/txt files always pass - no warning
|
||||
const nonDocFiles = ['foo.js', 'app.py', 'styles.css', 'data.json', 'image.png'];
|
||||
for (const file of nonDocFiles) {
|
||||
(test(`allows non-doc file: ${file}`, () => {
|
||||
const { code, stderr } = runScript({ tool_input: { file_path: file } });
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(stderr, '', `expected no warning for ${file}, got: ${stderr}`);
|
||||
}) ? passed++ : failed++);
|
||||
}
|
||||
|
||||
// 5. Non-standard doc files - warning in stderr
|
||||
const nonStandardFiles = ['random-notes.md', 'TODO.md', 'notes.txt', 'scratch.md', 'ideas.txt'];
|
||||
for (const file of nonStandardFiles) {
|
||||
(test(`warns on non-standard doc file: ${file}`, () => {
|
||||
const { code, stderr } = runScript({ tool_input: { file_path: file } });
|
||||
assert.strictEqual(code, 0, 'should still exit 0 (warn only)');
|
||||
assert.ok(stderr.includes('WARNING'), `expected warning in stderr for ${file}, got: ${stderr}`);
|
||||
assert.ok(stderr.includes(file), `expected file path in stderr for ${file}`);
|
||||
}) ? passed++ : failed++);
|
||||
}
|
||||
|
||||
// 6. Invalid/empty input - passes through without error
|
||||
(test('handles empty object input without error', () => {
|
||||
const { code, stderr } = runScript({});
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(stderr, '', `expected no warning for empty input, got: ${stderr}`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
(test('handles missing file_path without error', () => {
|
||||
const { code, stderr } = runScript({ tool_input: {} });
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(stderr, '', `expected no warning for missing file_path, got: ${stderr}`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
(test('handles empty file_path without error', () => {
|
||||
const { code, stderr } = runScript({ tool_input: { file_path: '' } });
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(stderr, '', `expected no warning for empty file_path, got: ${stderr}`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
// 7. Stdout always contains the original input (pass-through)
|
||||
(test('passes through input to stdout for allowed file', () => {
|
||||
const input = { tool_input: { file_path: 'README.md' } };
|
||||
const { stdout } = runScript(input);
|
||||
assert.strictEqual(stdout, JSON.stringify(input));
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
(test('passes through input to stdout for warned file', () => {
|
||||
const input = { tool_input: { file_path: 'random-notes.md' } };
|
||||
const { stdout } = runScript(input);
|
||||
assert.strictEqual(stdout, JSON.stringify(input));
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
(test('passes through input to stdout for empty input', () => {
|
||||
const input = {};
|
||||
const { stdout } = runScript(input);
|
||||
assert.strictEqual(stdout, JSON.stringify(input));
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
runTests();
|
||||
397
tests/hooks/hook-flags.test.js
Normal file
397
tests/hooks/hook-flags.test.js
Normal file
@@ -0,0 +1,397 @@
|
||||
/**
|
||||
* Tests for scripts/lib/hook-flags.js
|
||||
*
|
||||
* Run with: node tests/hooks/hook-flags.test.js
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
// Import the module
|
||||
const {
|
||||
VALID_PROFILES,
|
||||
normalizeId,
|
||||
getHookProfile,
|
||||
getDisabledHookIds,
|
||||
parseProfiles,
|
||||
isHookEnabled,
|
||||
} = require('../../scripts/lib/hook-flags');
|
||||
|
||||
// Test helper
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` ✓ ${name}`);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.log(` ✗ ${name}`);
|
||||
console.log(` Error: ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to save and restore env vars
|
||||
function withEnv(vars, fn) {
|
||||
const saved = {};
|
||||
for (const key of Object.keys(vars)) {
|
||||
saved[key] = process.env[key];
|
||||
if (vars[key] === undefined) {
|
||||
delete process.env[key];
|
||||
} else {
|
||||
process.env[key] = vars[key];
|
||||
}
|
||||
}
|
||||
try {
|
||||
fn();
|
||||
} finally {
|
||||
for (const key of Object.keys(saved)) {
|
||||
if (saved[key] === undefined) {
|
||||
delete process.env[key];
|
||||
} else {
|
||||
process.env[key] = saved[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test suite
|
||||
function runTests() {
|
||||
console.log('\n=== Testing hook-flags.js ===\n');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
// VALID_PROFILES tests
|
||||
console.log('VALID_PROFILES:');
|
||||
|
||||
if (test('is a Set', () => {
|
||||
assert.ok(VALID_PROFILES instanceof Set);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('contains minimal, standard, strict', () => {
|
||||
assert.ok(VALID_PROFILES.has('minimal'));
|
||||
assert.ok(VALID_PROFILES.has('standard'));
|
||||
assert.ok(VALID_PROFILES.has('strict'));
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('contains exactly 3 profiles', () => {
|
||||
assert.strictEqual(VALID_PROFILES.size, 3);
|
||||
})) passed++; else failed++;
|
||||
|
||||
// normalizeId tests
|
||||
console.log('\nnormalizeId:');
|
||||
|
||||
if (test('returns empty string for null', () => {
|
||||
assert.strictEqual(normalizeId(null), '');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns empty string for undefined', () => {
|
||||
assert.strictEqual(normalizeId(undefined), '');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns empty string for empty string', () => {
|
||||
assert.strictEqual(normalizeId(''), '');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('trims whitespace', () => {
|
||||
assert.strictEqual(normalizeId(' hello '), 'hello');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('converts to lowercase', () => {
|
||||
assert.strictEqual(normalizeId('MyHook'), 'myhook');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('handles mixed case with whitespace', () => {
|
||||
assert.strictEqual(normalizeId(' My-Hook-ID '), 'my-hook-id');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('converts numbers to string', () => {
|
||||
assert.strictEqual(normalizeId(123), '123');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns empty string for whitespace-only input', () => {
|
||||
assert.strictEqual(normalizeId(' '), '');
|
||||
})) passed++; else failed++;
|
||||
|
||||
// getHookProfile tests
|
||||
console.log('\ngetHookProfile:');
|
||||
|
||||
if (test('defaults to standard when env var not set', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: undefined }, () => {
|
||||
assert.strictEqual(getHookProfile(), 'standard');
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns minimal when set to minimal', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: 'minimal' }, () => {
|
||||
assert.strictEqual(getHookProfile(), 'minimal');
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns standard when set to standard', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: 'standard' }, () => {
|
||||
assert.strictEqual(getHookProfile(), 'standard');
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns strict when set to strict', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: 'strict' }, () => {
|
||||
assert.strictEqual(getHookProfile(), 'strict');
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('is case-insensitive', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: 'STRICT' }, () => {
|
||||
assert.strictEqual(getHookProfile(), 'strict');
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('trims whitespace from env var', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: ' minimal ' }, () => {
|
||||
assert.strictEqual(getHookProfile(), 'minimal');
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('defaults to standard for invalid value', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: 'invalid' }, () => {
|
||||
assert.strictEqual(getHookProfile(), 'standard');
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('defaults to standard for empty string', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: '' }, () => {
|
||||
assert.strictEqual(getHookProfile(), 'standard');
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
// getDisabledHookIds tests
|
||||
console.log('\ngetDisabledHookIds:');
|
||||
|
||||
if (test('returns empty Set when env var not set', () => {
|
||||
withEnv({ ECC_DISABLED_HOOKS: undefined }, () => {
|
||||
const result = getDisabledHookIds();
|
||||
assert.ok(result instanceof Set);
|
||||
assert.strictEqual(result.size, 0);
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns empty Set for empty string', () => {
|
||||
withEnv({ ECC_DISABLED_HOOKS: '' }, () => {
|
||||
assert.strictEqual(getDisabledHookIds().size, 0);
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns empty Set for whitespace-only string', () => {
|
||||
withEnv({ ECC_DISABLED_HOOKS: ' ' }, () => {
|
||||
assert.strictEqual(getDisabledHookIds().size, 0);
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('parses single hook id', () => {
|
||||
withEnv({ ECC_DISABLED_HOOKS: 'my-hook' }, () => {
|
||||
const result = getDisabledHookIds();
|
||||
assert.strictEqual(result.size, 1);
|
||||
assert.ok(result.has('my-hook'));
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('parses multiple comma-separated hook ids', () => {
|
||||
withEnv({ ECC_DISABLED_HOOKS: 'hook-a,hook-b,hook-c' }, () => {
|
||||
const result = getDisabledHookIds();
|
||||
assert.strictEqual(result.size, 3);
|
||||
assert.ok(result.has('hook-a'));
|
||||
assert.ok(result.has('hook-b'));
|
||||
assert.ok(result.has('hook-c'));
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('trims whitespace around hook ids', () => {
|
||||
withEnv({ ECC_DISABLED_HOOKS: ' hook-a , hook-b ' }, () => {
|
||||
const result = getDisabledHookIds();
|
||||
assert.strictEqual(result.size, 2);
|
||||
assert.ok(result.has('hook-a'));
|
||||
assert.ok(result.has('hook-b'));
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('normalizes hook ids to lowercase', () => {
|
||||
withEnv({ ECC_DISABLED_HOOKS: 'MyHook,ANOTHER' }, () => {
|
||||
const result = getDisabledHookIds();
|
||||
assert.ok(result.has('myhook'));
|
||||
assert.ok(result.has('another'));
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('filters out empty entries from trailing commas', () => {
|
||||
withEnv({ ECC_DISABLED_HOOKS: 'hook-a,,hook-b,' }, () => {
|
||||
const result = getDisabledHookIds();
|
||||
assert.strictEqual(result.size, 2);
|
||||
assert.ok(result.has('hook-a'));
|
||||
assert.ok(result.has('hook-b'));
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
// parseProfiles tests
|
||||
console.log('\nparseProfiles:');
|
||||
|
||||
if (test('returns fallback for null input', () => {
|
||||
const result = parseProfiles(null);
|
||||
assert.deepStrictEqual(result, ['standard', 'strict']);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns fallback for undefined input', () => {
|
||||
const result = parseProfiles(undefined);
|
||||
assert.deepStrictEqual(result, ['standard', 'strict']);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('uses custom fallback when provided', () => {
|
||||
const result = parseProfiles(null, ['minimal']);
|
||||
assert.deepStrictEqual(result, ['minimal']);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('parses comma-separated string', () => {
|
||||
const result = parseProfiles('minimal,strict');
|
||||
assert.deepStrictEqual(result, ['minimal', 'strict']);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('parses single string value', () => {
|
||||
const result = parseProfiles('strict');
|
||||
assert.deepStrictEqual(result, ['strict']);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('parses array of profiles', () => {
|
||||
const result = parseProfiles(['minimal', 'standard']);
|
||||
assert.deepStrictEqual(result, ['minimal', 'standard']);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('filters invalid profiles from string', () => {
|
||||
const result = parseProfiles('minimal,invalid,strict');
|
||||
assert.deepStrictEqual(result, ['minimal', 'strict']);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('filters invalid profiles from array', () => {
|
||||
const result = parseProfiles(['minimal', 'bogus', 'strict']);
|
||||
assert.deepStrictEqual(result, ['minimal', 'strict']);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns fallback when all string values are invalid', () => {
|
||||
const result = parseProfiles('invalid,bogus');
|
||||
assert.deepStrictEqual(result, ['standard', 'strict']);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns fallback when all array values are invalid', () => {
|
||||
const result = parseProfiles(['invalid', 'bogus']);
|
||||
assert.deepStrictEqual(result, ['standard', 'strict']);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('is case-insensitive for string input', () => {
|
||||
const result = parseProfiles('MINIMAL,STRICT');
|
||||
assert.deepStrictEqual(result, ['minimal', 'strict']);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('is case-insensitive for array input', () => {
|
||||
const result = parseProfiles(['MINIMAL', 'STRICT']);
|
||||
assert.deepStrictEqual(result, ['minimal', 'strict']);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('trims whitespace in string input', () => {
|
||||
const result = parseProfiles(' minimal , strict ');
|
||||
assert.deepStrictEqual(result, ['minimal', 'strict']);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('handles null values in array', () => {
|
||||
const result = parseProfiles([null, 'strict']);
|
||||
assert.deepStrictEqual(result, ['strict']);
|
||||
})) passed++; else failed++;
|
||||
|
||||
// isHookEnabled tests
|
||||
console.log('\nisHookEnabled:');
|
||||
|
||||
if (test('returns true by default for a hook (standard profile)', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: undefined, ECC_DISABLED_HOOKS: undefined }, () => {
|
||||
assert.strictEqual(isHookEnabled('my-hook'), true);
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns true for empty hookId', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: undefined, ECC_DISABLED_HOOKS: undefined }, () => {
|
||||
assert.strictEqual(isHookEnabled(''), true);
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns true for null hookId', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: undefined, ECC_DISABLED_HOOKS: undefined }, () => {
|
||||
assert.strictEqual(isHookEnabled(null), true);
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns false when hook is in disabled list', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: undefined, ECC_DISABLED_HOOKS: 'my-hook' }, () => {
|
||||
assert.strictEqual(isHookEnabled('my-hook'), false);
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('disabled check is case-insensitive', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: undefined, ECC_DISABLED_HOOKS: 'MY-HOOK' }, () => {
|
||||
assert.strictEqual(isHookEnabled('my-hook'), false);
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns true when hook is not in disabled list', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: undefined, ECC_DISABLED_HOOKS: 'other-hook' }, () => {
|
||||
assert.strictEqual(isHookEnabled('my-hook'), true);
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns false when current profile is not in allowed profiles', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: 'minimal', ECC_DISABLED_HOOKS: undefined }, () => {
|
||||
assert.strictEqual(isHookEnabled('my-hook', { profiles: 'strict' }), false);
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns true when current profile is in allowed profiles', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: 'strict', ECC_DISABLED_HOOKS: undefined }, () => {
|
||||
assert.strictEqual(isHookEnabled('my-hook', { profiles: 'standard,strict' }), true);
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns true when current profile matches single allowed profile', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: 'minimal', ECC_DISABLED_HOOKS: undefined }, () => {
|
||||
assert.strictEqual(isHookEnabled('my-hook', { profiles: 'minimal' }), true);
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('disabled hooks take precedence over profile match', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: 'strict', ECC_DISABLED_HOOKS: 'my-hook' }, () => {
|
||||
assert.strictEqual(isHookEnabled('my-hook', { profiles: 'strict' }), false);
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('uses default profiles (standard, strict) when none specified', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: 'minimal', ECC_DISABLED_HOOKS: undefined }, () => {
|
||||
assert.strictEqual(isHookEnabled('my-hook'), false);
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('allows standard profile by default', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: 'standard', ECC_DISABLED_HOOKS: undefined }, () => {
|
||||
assert.strictEqual(isHookEnabled('my-hook'), true);
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('allows strict profile by default', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: 'strict', ECC_DISABLED_HOOKS: undefined }, () => {
|
||||
assert.strictEqual(isHookEnabled('my-hook'), true);
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('accepts array profiles option', () => {
|
||||
withEnv({ ECC_HOOK_PROFILE: 'minimal', ECC_DISABLED_HOOKS: undefined }, () => {
|
||||
assert.strictEqual(isHookEnabled('my-hook', { profiles: ['minimal', 'standard'] }), true);
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
runTests();
|
||||
207
tests/hooks/post-bash-hooks.test.js
Normal file
207
tests/hooks/post-bash-hooks.test.js
Normal file
@@ -0,0 +1,207 @@
|
||||
/**
|
||||
* Tests for post-bash-build-complete.js and post-bash-pr-created.js
|
||||
*
|
||||
* Run with: node tests/hooks/post-bash-hooks.test.js
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
const { spawnSync } = require('child_process');
|
||||
|
||||
const buildCompleteScript = path.join(__dirname, '..', '..', 'scripts', 'hooks', 'post-bash-build-complete.js');
|
||||
const prCreatedScript = path.join(__dirname, '..', '..', 'scripts', 'hooks', 'post-bash-pr-created.js');
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` ✓ ${name}`);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.log(` ✗ ${name}`);
|
||||
console.log(` Error: ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function runScript(scriptPath, input) {
|
||||
return spawnSync('node', [scriptPath], {
|
||||
encoding: 'utf8',
|
||||
input,
|
||||
stdio: ['pipe', 'pipe', 'pipe']
|
||||
});
|
||||
}
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
// ── post-bash-build-complete.js ──────────────────────────────────
|
||||
|
||||
console.log('\nPost-Bash Build Complete Hook Tests');
|
||||
console.log('====================================\n');
|
||||
|
||||
console.log('Build command detection:');
|
||||
|
||||
if (test('stderr contains "Build completed" for npm run build command', () => {
|
||||
const input = JSON.stringify({ tool_input: { command: 'npm run build' } });
|
||||
const result = runScript(buildCompleteScript, input);
|
||||
assert.strictEqual(result.status, 0, 'Should exit with code 0');
|
||||
assert.ok(result.stderr.includes('Build completed'), `stderr should contain "Build completed", got: ${result.stderr}`);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('stderr contains "Build completed" for pnpm build command', () => {
|
||||
const input = JSON.stringify({ tool_input: { command: 'pnpm build' } });
|
||||
const result = runScript(buildCompleteScript, input);
|
||||
assert.strictEqual(result.status, 0, 'Should exit with code 0');
|
||||
assert.ok(result.stderr.includes('Build completed'), `stderr should contain "Build completed", got: ${result.stderr}`);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('stderr contains "Build completed" for yarn build command', () => {
|
||||
const input = JSON.stringify({ tool_input: { command: 'yarn build' } });
|
||||
const result = runScript(buildCompleteScript, input);
|
||||
assert.strictEqual(result.status, 0, 'Should exit with code 0');
|
||||
assert.ok(result.stderr.includes('Build completed'), `stderr should contain "Build completed", got: ${result.stderr}`);
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log('\nNon-build command detection:');
|
||||
|
||||
if (test('no stderr message for npm test command', () => {
|
||||
const input = JSON.stringify({ tool_input: { command: 'npm test' } });
|
||||
const result = runScript(buildCompleteScript, input);
|
||||
assert.strictEqual(result.status, 0, 'Should exit with code 0');
|
||||
assert.strictEqual(result.stderr, '', 'stderr should be empty for non-build command');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('no stderr message for ls command', () => {
|
||||
const input = JSON.stringify({ tool_input: { command: 'ls -la' } });
|
||||
const result = runScript(buildCompleteScript, input);
|
||||
assert.strictEqual(result.status, 0, 'Should exit with code 0');
|
||||
assert.strictEqual(result.stderr, '', 'stderr should be empty for non-build command');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('no stderr message for git status command', () => {
|
||||
const input = JSON.stringify({ tool_input: { command: 'git status' } });
|
||||
const result = runScript(buildCompleteScript, input);
|
||||
assert.strictEqual(result.status, 0, 'Should exit with code 0');
|
||||
assert.strictEqual(result.stderr, '', 'stderr should be empty for non-build command');
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log('\nStdout pass-through:');
|
||||
|
||||
if (test('stdout passes through input for build command', () => {
|
||||
const input = JSON.stringify({ tool_input: { command: 'npm run build' } });
|
||||
const result = runScript(buildCompleteScript, input);
|
||||
assert.strictEqual(result.stdout, input, 'stdout should be the original input');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('stdout passes through input for non-build command', () => {
|
||||
const input = JSON.stringify({ tool_input: { command: 'npm test' } });
|
||||
const result = runScript(buildCompleteScript, input);
|
||||
assert.strictEqual(result.stdout, input, 'stdout should be the original input');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('stdout passes through input for invalid JSON', () => {
|
||||
const input = 'not valid json';
|
||||
const result = runScript(buildCompleteScript, input);
|
||||
assert.strictEqual(result.stdout, input, 'stdout should be the original input');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('stdout passes through empty input', () => {
|
||||
const input = '';
|
||||
const result = runScript(buildCompleteScript, input);
|
||||
assert.strictEqual(result.stdout, input, 'stdout should be the original input');
|
||||
})) passed++; else failed++;
|
||||
|
||||
// ── post-bash-pr-created.js ──────────────────────────────────────
|
||||
|
||||
console.log('\n\nPost-Bash PR Created Hook Tests');
|
||||
console.log('================================\n');
|
||||
|
||||
console.log('PR creation detection:');
|
||||
|
||||
if (test('stderr contains PR URL when gh pr create output has PR URL', () => {
|
||||
const input = JSON.stringify({
|
||||
tool_input: { command: 'gh pr create --title "Fix bug" --body "desc"' },
|
||||
tool_output: { output: 'https://github.com/owner/repo/pull/42\n' }
|
||||
});
|
||||
const result = runScript(prCreatedScript, input);
|
||||
assert.strictEqual(result.status, 0, 'Should exit with code 0');
|
||||
assert.ok(result.stderr.includes('https://github.com/owner/repo/pull/42'), `stderr should contain PR URL, got: ${result.stderr}`);
|
||||
assert.ok(result.stderr.includes('[Hook] PR created:'), 'stderr should contain PR created message');
|
||||
assert.ok(result.stderr.includes('gh pr review 42'), 'stderr should contain review command');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('stderr contains correct repo in review command', () => {
|
||||
const input = JSON.stringify({
|
||||
tool_input: { command: 'gh pr create' },
|
||||
tool_output: { output: 'Created PR\nhttps://github.com/my-org/my-repo/pull/123\nDone' }
|
||||
});
|
||||
const result = runScript(prCreatedScript, input);
|
||||
assert.strictEqual(result.status, 0, 'Should exit with code 0');
|
||||
assert.ok(result.stderr.includes('--repo my-org/my-repo'), `stderr should contain correct repo, got: ${result.stderr}`);
|
||||
assert.ok(result.stderr.includes('gh pr review 123'), 'stderr should contain correct PR number');
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log('\nNon-PR command detection:');
|
||||
|
||||
if (test('no stderr about PR for non-gh command', () => {
|
||||
const input = JSON.stringify({ tool_input: { command: 'npm test' } });
|
||||
const result = runScript(prCreatedScript, input);
|
||||
assert.strictEqual(result.status, 0, 'Should exit with code 0');
|
||||
assert.strictEqual(result.stderr, '', 'stderr should be empty for non-PR command');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('no stderr about PR for gh issue command', () => {
|
||||
const input = JSON.stringify({ tool_input: { command: 'gh issue list' } });
|
||||
const result = runScript(prCreatedScript, input);
|
||||
assert.strictEqual(result.status, 0, 'Should exit with code 0');
|
||||
assert.strictEqual(result.stderr, '', 'stderr should be empty for non-PR create command');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('no stderr about PR for gh pr create without PR URL in output', () => {
|
||||
const input = JSON.stringify({
|
||||
tool_input: { command: 'gh pr create' },
|
||||
tool_output: { output: 'Error: could not create PR' }
|
||||
});
|
||||
const result = runScript(prCreatedScript, input);
|
||||
assert.strictEqual(result.status, 0, 'Should exit with code 0');
|
||||
assert.strictEqual(result.stderr, '', 'stderr should be empty when no PR URL in output');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('no stderr about PR for gh pr list command', () => {
|
||||
const input = JSON.stringify({ tool_input: { command: 'gh pr list' } });
|
||||
const result = runScript(prCreatedScript, input);
|
||||
assert.strictEqual(result.status, 0, 'Should exit with code 0');
|
||||
assert.strictEqual(result.stderr, '', 'stderr should be empty for gh pr list');
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log('\nStdout pass-through:');
|
||||
|
||||
if (test('stdout passes through input for PR create command', () => {
|
||||
const input = JSON.stringify({
|
||||
tool_input: { command: 'gh pr create' },
|
||||
tool_output: { output: 'https://github.com/owner/repo/pull/1' }
|
||||
});
|
||||
const result = runScript(prCreatedScript, input);
|
||||
assert.strictEqual(result.stdout, input, 'stdout should be the original input');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('stdout passes through input for non-PR command', () => {
|
||||
const input = JSON.stringify({ tool_input: { command: 'echo hello' } });
|
||||
const result = runScript(prCreatedScript, input);
|
||||
assert.strictEqual(result.stdout, input, 'stdout should be the original input');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('stdout passes through input for invalid JSON', () => {
|
||||
const input = 'not valid json';
|
||||
const result = runScript(prCreatedScript, input);
|
||||
assert.strictEqual(result.stdout, input, 'stdout should be the original input');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('stdout passes through empty input', () => {
|
||||
const input = '';
|
||||
const result = runScript(prCreatedScript, input);
|
||||
assert.strictEqual(result.stdout, input, 'stdout should be the original input');
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
121
tests/hooks/pre-bash-dev-server-block.test.js
Normal file
121
tests/hooks/pre-bash-dev-server-block.test.js
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Tests for pre-bash-dev-server-block.js hook
|
||||
*
|
||||
* Run with: node tests/hooks/pre-bash-dev-server-block.test.js
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
const { spawnSync } = require('child_process');
|
||||
|
||||
const script = path.join(__dirname, '..', '..', 'scripts', 'hooks', 'pre-bash-dev-server-block.js');
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` ✓ ${name}`);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.log(` ✗ ${name}`);
|
||||
console.log(` Error: ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function runScript(command) {
|
||||
const input = { tool_input: { command } };
|
||||
const result = spawnSync('node', [script], {
|
||||
encoding: 'utf8',
|
||||
input: JSON.stringify(input),
|
||||
timeout: 10000,
|
||||
});
|
||||
return { code: result.status || 0, stdout: result.stdout || '', stderr: result.stderr || '' };
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
console.log('\n=== Testing pre-bash-dev-server-block.js ===\n');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
// --- Blocking tests (non-Windows only) ---
|
||||
|
||||
if (!isWindows) {
|
||||
(test('blocks npm run dev (exit code 2, stderr contains BLOCKED)', () => {
|
||||
const result = runScript('npm run dev');
|
||||
assert.strictEqual(result.code, 2, `Expected exit code 2, got ${result.code}`);
|
||||
assert.ok(result.stderr.includes('BLOCKED'), `Expected stderr to contain BLOCKED, got: ${result.stderr}`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
(test('blocks pnpm dev (exit code 2)', () => {
|
||||
const result = runScript('pnpm dev');
|
||||
assert.strictEqual(result.code, 2, `Expected exit code 2, got ${result.code}`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
(test('blocks yarn dev (exit code 2)', () => {
|
||||
const result = runScript('yarn dev');
|
||||
assert.strictEqual(result.code, 2, `Expected exit code 2, got ${result.code}`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
(test('blocks bun run dev (exit code 2)', () => {
|
||||
const result = runScript('bun run dev');
|
||||
assert.strictEqual(result.code, 2, `Expected exit code 2, got ${result.code}`);
|
||||
}) ? passed++ : failed++);
|
||||
} else {
|
||||
console.log(' (skipping blocking tests on Windows)\n');
|
||||
}
|
||||
|
||||
// --- Allow tests ---
|
||||
|
||||
(test('allows tmux-wrapped npm run dev (exit code 0)', () => {
|
||||
const result = runScript('tmux new-session -d -s dev "npm run dev"');
|
||||
assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
(test('allows npm install (exit code 0)', () => {
|
||||
const result = runScript('npm install');
|
||||
assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
(test('allows npm test (exit code 0)', () => {
|
||||
const result = runScript('npm test');
|
||||
assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
(test('allows npm run build (exit code 0)', () => {
|
||||
const result = runScript('npm run build');
|
||||
assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
// --- Edge cases ---
|
||||
|
||||
(test('empty/invalid input passes through (exit code 0)', () => {
|
||||
const result = spawnSync('node', [script], {
|
||||
encoding: 'utf8',
|
||||
input: '',
|
||||
timeout: 10000,
|
||||
});
|
||||
assert.strictEqual(result.status || 0, 0, `Expected exit code 0, got ${result.status}`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
(test('stdout contains original input on pass-through', () => {
|
||||
const input = { tool_input: { command: 'npm install' } };
|
||||
const inputStr = JSON.stringify(input);
|
||||
const result = spawnSync('node', [script], {
|
||||
encoding: 'utf8',
|
||||
input: inputStr,
|
||||
timeout: 10000,
|
||||
});
|
||||
assert.strictEqual(result.status || 0, 0);
|
||||
assert.strictEqual(result.stdout.trim(), inputStr, `Expected stdout to contain original input`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
// --- Summary ---
|
||||
|
||||
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
runTests();
|
||||
104
tests/hooks/pre-bash-reminders.test.js
Normal file
104
tests/hooks/pre-bash-reminders.test.js
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Tests for pre-bash-git-push-reminder.js and pre-bash-tmux-reminder.js hooks
|
||||
*
|
||||
* Run with: node tests/hooks/pre-bash-reminders.test.js
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
const { spawnSync } = require('child_process');
|
||||
|
||||
const gitPushScript = path.join(__dirname, '..', '..', 'scripts', 'hooks', 'pre-bash-git-push-reminder.js');
|
||||
const tmuxScript = path.join(__dirname, '..', '..', 'scripts', 'hooks', 'pre-bash-tmux-reminder.js');
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` ✓ ${name}`);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.log(` ✗ ${name}`);
|
||||
console.log(` Error: ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function runScript(scriptPath, command, envOverrides = {}) {
|
||||
const input = { tool_input: { command } };
|
||||
const inputStr = JSON.stringify(input);
|
||||
const result = spawnSync('node', [scriptPath], {
|
||||
encoding: 'utf8',
|
||||
input: inputStr,
|
||||
timeout: 10000,
|
||||
env: { ...process.env, ...envOverrides },
|
||||
});
|
||||
return { code: result.status || 0, stdout: result.stdout || '', stderr: result.stderr || '', inputStr };
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
console.log('\n=== Testing pre-bash-git-push-reminder.js & pre-bash-tmux-reminder.js ===\n');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
// --- git-push-reminder tests ---
|
||||
|
||||
console.log(' git-push-reminder:');
|
||||
|
||||
(test('git push triggers stderr warning', () => {
|
||||
const result = runScript(gitPushScript, 'git push origin main');
|
||||
assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
|
||||
assert.ok(result.stderr.includes('[Hook]'), `Expected stderr to contain [Hook], got: ${result.stderr}`);
|
||||
assert.ok(result.stderr.includes('Review changes before push'), `Expected stderr to mention review`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
(test('git status has no warning', () => {
|
||||
const result = runScript(gitPushScript, 'git status');
|
||||
assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
|
||||
assert.strictEqual(result.stderr, '', `Expected no stderr, got: ${result.stderr}`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
(test('git push always passes through input on stdout', () => {
|
||||
const result = runScript(gitPushScript, 'git push');
|
||||
assert.strictEqual(result.stdout, result.inputStr, 'Expected stdout to match original input');
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
// --- tmux-reminder tests (non-Windows only) ---
|
||||
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
if (!isWindows) {
|
||||
console.log('\n tmux-reminder:');
|
||||
|
||||
(test('npm install triggers tmux suggestion', () => {
|
||||
const result = runScript(tmuxScript, 'npm install', { TMUX: '' });
|
||||
assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
|
||||
assert.ok(result.stderr.includes('[Hook]'), `Expected stderr to contain [Hook], got: ${result.stderr}`);
|
||||
assert.ok(result.stderr.includes('tmux'), `Expected stderr to mention tmux`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
(test('npm test triggers tmux suggestion', () => {
|
||||
const result = runScript(tmuxScript, 'npm test', { TMUX: '' });
|
||||
assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
|
||||
assert.ok(result.stderr.includes('tmux'), `Expected stderr to mention tmux`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
(test('regular command like ls has no tmux suggestion', () => {
|
||||
const result = runScript(tmuxScript, 'ls -la', { TMUX: '' });
|
||||
assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
|
||||
assert.strictEqual(result.stderr, '', `Expected no stderr for ls, got: ${result.stderr}`);
|
||||
}) ? passed++ : failed++);
|
||||
|
||||
(test('tmux reminder always passes through input on stdout', () => {
|
||||
const result = runScript(tmuxScript, 'npm install', { TMUX: '' });
|
||||
assert.strictEqual(result.stdout, result.inputStr, 'Expected stdout to match original input');
|
||||
}) ? passed++ : failed++);
|
||||
} else {
|
||||
console.log('\n (skipping tmux-reminder tests on Windows)\n');
|
||||
}
|
||||
|
||||
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
runTests();
|
||||
159
tests/hooks/quality-gate.test.js
Normal file
159
tests/hooks/quality-gate.test.js
Normal file
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* Tests for scripts/hooks/quality-gate.js
|
||||
*
|
||||
* Run with: node tests/hooks/quality-gate.test.js
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
|
||||
const qualityGate = require('../../scripts/hooks/quality-gate');
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` ✓ ${name}`);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.log(` ✗ ${name}`);
|
||||
console.log(` Error: ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
console.log('\nQuality Gate Hook Tests');
|
||||
console.log('========================\n');
|
||||
|
||||
// --- run() returns original input for valid JSON ---
|
||||
|
||||
console.log('run() pass-through behavior:');
|
||||
|
||||
if (test('returns original input for valid JSON with file_path', () => {
|
||||
const input = JSON.stringify({ tool_input: { file_path: '/tmp/nonexistent-file.js' } });
|
||||
const result = qualityGate.run(input);
|
||||
assert.strictEqual(result, input, 'Should return original input unchanged');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns original input for valid JSON without file_path', () => {
|
||||
const input = JSON.stringify({ tool_input: { command: 'ls' } });
|
||||
const result = qualityGate.run(input);
|
||||
assert.strictEqual(result, input, 'Should return original input unchanged');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns original input for valid JSON with nested structure', () => {
|
||||
const input = JSON.stringify({ tool_input: { file_path: '/some/path.ts', content: 'hello' }, other: [1, 2, 3] });
|
||||
const result = qualityGate.run(input);
|
||||
assert.strictEqual(result, input, 'Should return original input unchanged');
|
||||
})) passed++; else failed++;
|
||||
|
||||
// --- run() returns original input for invalid JSON ---
|
||||
|
||||
console.log('\nInvalid JSON handling:');
|
||||
|
||||
if (test('returns original input for invalid JSON (no crash)', () => {
|
||||
const input = 'this is not json at all {{{';
|
||||
const result = qualityGate.run(input);
|
||||
assert.strictEqual(result, input, 'Should return original input unchanged');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns original input for partial JSON', () => {
|
||||
const input = '{"tool_input": {';
|
||||
const result = qualityGate.run(input);
|
||||
assert.strictEqual(result, input, 'Should return original input unchanged');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns original input for JSON with trailing garbage', () => {
|
||||
const input = '{"tool_input": {}}extra';
|
||||
const result = qualityGate.run(input);
|
||||
assert.strictEqual(result, input, 'Should return original input unchanged');
|
||||
})) passed++; else failed++;
|
||||
|
||||
// --- run() returns original input when file does not exist ---
|
||||
|
||||
console.log('\nNon-existent file handling:');
|
||||
|
||||
if (test('returns original input when file_path points to non-existent file', () => {
|
||||
const input = JSON.stringify({ tool_input: { file_path: '/tmp/does-not-exist-12345.js' } });
|
||||
const result = qualityGate.run(input);
|
||||
assert.strictEqual(result, input, 'Should return original input unchanged');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns original input when file_path is a non-existent .py file', () => {
|
||||
const input = JSON.stringify({ tool_input: { file_path: '/tmp/does-not-exist-12345.py' } });
|
||||
const result = qualityGate.run(input);
|
||||
assert.strictEqual(result, input, 'Should return original input unchanged');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns original input when file_path is a non-existent .go file', () => {
|
||||
const input = JSON.stringify({ tool_input: { file_path: '/tmp/does-not-exist-12345.go' } });
|
||||
const result = qualityGate.run(input);
|
||||
assert.strictEqual(result, input, 'Should return original input unchanged');
|
||||
})) passed++; else failed++;
|
||||
|
||||
// --- run() returns original input for empty input ---
|
||||
|
||||
console.log('\nEmpty input handling:');
|
||||
|
||||
if (test('returns original input for empty string', () => {
|
||||
const input = '';
|
||||
const result = qualityGate.run(input);
|
||||
assert.strictEqual(result, input, 'Should return empty string unchanged');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('returns original input for whitespace-only string', () => {
|
||||
const input = ' ';
|
||||
const result = qualityGate.run(input);
|
||||
assert.strictEqual(result, input, 'Should return whitespace string unchanged');
|
||||
})) passed++; else failed++;
|
||||
|
||||
// --- run() handles missing tool_input gracefully ---
|
||||
|
||||
console.log('\nMissing tool_input handling:');
|
||||
|
||||
if (test('handles missing tool_input gracefully', () => {
|
||||
const input = JSON.stringify({ something_else: 'value' });
|
||||
const result = qualityGate.run(input);
|
||||
assert.strictEqual(result, input, 'Should return original input unchanged');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('handles null tool_input gracefully', () => {
|
||||
const input = JSON.stringify({ tool_input: null });
|
||||
const result = qualityGate.run(input);
|
||||
assert.strictEqual(result, input, 'Should return original input unchanged');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('handles tool_input with empty file_path', () => {
|
||||
const input = JSON.stringify({ tool_input: { file_path: '' } });
|
||||
const result = qualityGate.run(input);
|
||||
assert.strictEqual(result, input, 'Should return original input unchanged');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('handles empty JSON object', () => {
|
||||
const input = JSON.stringify({});
|
||||
const result = qualityGate.run(input);
|
||||
assert.strictEqual(result, input, 'Should return original input unchanged');
|
||||
})) passed++; else failed++;
|
||||
|
||||
// --- run() with a real file (but no formatter installed) ---
|
||||
|
||||
console.log('\nReal file without formatter:');
|
||||
|
||||
if (test('returns original input for existing file with no formatter configured', () => {
|
||||
const tmpFile = path.join(os.tmpdir(), `quality-gate-test-${Date.now()}.js`);
|
||||
fs.writeFileSync(tmpFile, 'const x = 1;\n');
|
||||
try {
|
||||
const input = JSON.stringify({ tool_input: { file_path: tmpFile } });
|
||||
const result = qualityGate.run(input);
|
||||
assert.strictEqual(result, input, 'Should return original input unchanged');
|
||||
} finally {
|
||||
fs.unlinkSync(tmpFile);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
Reference in New Issue
Block a user