Merge origin/main into feat/insaits-security-hook

This commit is contained in:
Affaan Mustafa
2026-03-10 20:48:59 -07:00
58 changed files with 9954 additions and 3443 deletions

View File

@@ -1927,7 +1927,7 @@ function runTests() {
PreToolUse: [{
matcher: 'Write',
hooks: [{
type: 'intercept',
type: 'command',
command: 'echo test',
async: 'yes' // Should be boolean, not string
}]
@@ -1947,7 +1947,7 @@ function runTests() {
PostToolUse: [{
matcher: 'Edit',
hooks: [{
type: 'intercept',
type: 'command',
command: 'echo test',
timeout: -5 // Must be non-negative
}]
@@ -2105,6 +2105,31 @@ function runTests() {
cleanupTestDir(testDir);
})) passed++; else failed++;
console.log('\nRound 82b: validate-hooks (current official events and hook types):');
if (test('accepts UserPromptSubmit with omitted matcher and prompt/http/agent hooks', () => {
const testDir = createTestDir();
const hooksJson = JSON.stringify({
hooks: {
UserPromptSubmit: [
{
hooks: [
{ type: 'prompt', prompt: 'Summarize the request.' },
{ type: 'agent', prompt: 'Review for security issues.', model: 'gpt-5.4' },
{ type: 'http', url: 'https://example.com/hooks', headers: { Authorization: 'Bearer token' } }
]
}
]
}
});
const hooksFile = path.join(testDir, 'hooks.json');
fs.writeFileSync(hooksFile, hooksJson);
const result = runValidatorWithDir('validate-hooks', 'HOOKS_FILE', hooksFile);
assert.strictEqual(result.code, 0, 'Should accept current official hook event/type combinations');
cleanupTestDir(testDir);
})) passed++; else failed++;
// ── Round 83: validate-agents whitespace-only field, validate-skills empty SKILL.md ──
console.log('\nRound 83: validate-agents (whitespace-only frontmatter field value):');

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,246 @@
/**
* Tests for scripts/lib/resolve-formatter.js
*
* Run with: node tests/lib/resolve-formatter.test.js
*/
const assert = require('assert');
const path = require('path');
const fs = require('fs');
const os = require('os');
const { findProjectRoot, detectFormatter, resolveFormatterBin, clearCaches } = require('../../scripts/lib/resolve-formatter');
/**
* Run a single test case, printing pass/fail.
*
* @param {string} name - Test description
* @param {() => void} fn - Test body (throws on failure)
* @returns {boolean} Whether the test passed
*/
function test(name, fn) {
try {
fn();
console.log(`${name}`);
return true;
} catch (err) {
console.log(`${name}`);
console.log(` Error: ${err.message}`);
return false;
}
}
/** Track all created tmp dirs for cleanup */
const tmpDirs = [];
/**
* Create a temporary directory and track it for cleanup.
*
* @returns {string} Absolute path to the new temp directory
*/
function makeTmpDir() {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'resolve-fmt-'));
tmpDirs.push(dir);
return dir;
}
/**
* Remove all tracked temporary directories.
*/
function cleanupTmpDirs() {
for (const dir of tmpDirs) {
try {
fs.rmSync(dir, { recursive: true, force: true });
} catch {
// Best-effort cleanup
}
}
tmpDirs.length = 0;
}
function runTests() {
console.log('\n=== Testing resolve-formatter.js ===\n');
let passed = 0;
let failed = 0;
function run(name, fn) {
clearCaches();
if (test(name, fn)) passed++;
else failed++;
}
// ── findProjectRoot ───────────────────────────────────────────
run('findProjectRoot: finds package.json in parent dir', () => {
const root = makeTmpDir();
const sub = path.join(root, 'src', 'lib');
fs.mkdirSync(sub, { recursive: true });
fs.writeFileSync(path.join(root, 'package.json'), '{}');
assert.strictEqual(findProjectRoot(sub), root);
});
run('findProjectRoot: returns startDir when no package.json', () => {
const root = makeTmpDir();
const sub = path.join(root, 'deep');
fs.mkdirSync(sub, { recursive: true });
// No package.json anywhere in tmp → falls back to startDir
assert.strictEqual(findProjectRoot(sub), sub);
});
run('findProjectRoot: caches result for same startDir', () => {
const root = makeTmpDir();
fs.writeFileSync(path.join(root, 'package.json'), '{}');
const first = findProjectRoot(root);
// Remove package.json — cache should still return the old result
fs.unlinkSync(path.join(root, 'package.json'));
const second = findProjectRoot(root);
assert.strictEqual(first, second);
});
// ── detectFormatter ───────────────────────────────────────────
run('detectFormatter: detects biome.json', () => {
const root = makeTmpDir();
fs.writeFileSync(path.join(root, 'biome.json'), '{}');
assert.strictEqual(detectFormatter(root), 'biome');
});
run('detectFormatter: detects biome.jsonc', () => {
const root = makeTmpDir();
fs.writeFileSync(path.join(root, 'biome.jsonc'), '{}');
assert.strictEqual(detectFormatter(root), 'biome');
});
run('detectFormatter: detects .prettierrc', () => {
const root = makeTmpDir();
fs.writeFileSync(path.join(root, '.prettierrc'), '{}');
assert.strictEqual(detectFormatter(root), 'prettier');
});
run('detectFormatter: detects prettier.config.js', () => {
const root = makeTmpDir();
fs.writeFileSync(path.join(root, 'prettier.config.js'), 'module.exports = {}');
assert.strictEqual(detectFormatter(root), 'prettier');
});
run('detectFormatter: detects prettier key in package.json', () => {
const root = makeTmpDir();
fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify({ name: 'test', prettier: { singleQuote: true } }));
assert.strictEqual(detectFormatter(root), 'prettier');
});
run('detectFormatter: ignores package.json without prettier key', () => {
const root = makeTmpDir();
fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify({ name: 'test' }));
assert.strictEqual(detectFormatter(root), null);
});
run('detectFormatter: biome takes priority over prettier', () => {
const root = makeTmpDir();
fs.writeFileSync(path.join(root, 'biome.json'), '{}');
fs.writeFileSync(path.join(root, '.prettierrc'), '{}');
assert.strictEqual(detectFormatter(root), 'biome');
});
run('detectFormatter: returns null when no config found', () => {
const root = makeTmpDir();
assert.strictEqual(detectFormatter(root), null);
});
// ── resolveFormatterBin ───────────────────────────────────────
run('resolveFormatterBin: uses local biome binary when available', () => {
const root = makeTmpDir();
const binDir = path.join(root, 'node_modules', '.bin');
fs.mkdirSync(binDir, { recursive: true });
const binName = process.platform === 'win32' ? 'biome.cmd' : 'biome';
fs.writeFileSync(path.join(binDir, binName), '');
const result = resolveFormatterBin(root, 'biome');
assert.strictEqual(result.bin, path.join(binDir, binName));
assert.deepStrictEqual(result.prefix, []);
});
run('resolveFormatterBin: falls back to npx for biome', () => {
const root = makeTmpDir();
const result = resolveFormatterBin(root, 'biome');
const expectedBin = process.platform === 'win32' ? 'npx.cmd' : 'npx';
assert.strictEqual(result.bin, expectedBin);
assert.deepStrictEqual(result.prefix, ['@biomejs/biome']);
});
run('resolveFormatterBin: uses local prettier binary when available', () => {
const root = makeTmpDir();
const binDir = path.join(root, 'node_modules', '.bin');
fs.mkdirSync(binDir, { recursive: true });
const binName = process.platform === 'win32' ? 'prettier.cmd' : 'prettier';
fs.writeFileSync(path.join(binDir, binName), '');
const result = resolveFormatterBin(root, 'prettier');
assert.strictEqual(result.bin, path.join(binDir, binName));
assert.deepStrictEqual(result.prefix, []);
});
run('resolveFormatterBin: falls back to npx for prettier', () => {
const root = makeTmpDir();
const result = resolveFormatterBin(root, 'prettier');
const expectedBin = process.platform === 'win32' ? 'npx.cmd' : 'npx';
assert.strictEqual(result.bin, expectedBin);
assert.deepStrictEqual(result.prefix, ['prettier']);
});
run('resolveFormatterBin: returns null for unknown formatter', () => {
const root = makeTmpDir();
const result = resolveFormatterBin(root, 'unknown');
assert.strictEqual(result, null);
});
run('resolveFormatterBin: caches resolved binary', () => {
const root = makeTmpDir();
const binDir = path.join(root, 'node_modules', '.bin');
fs.mkdirSync(binDir, { recursive: true });
const binName = process.platform === 'win32' ? 'biome.cmd' : 'biome';
fs.writeFileSync(path.join(binDir, binName), '');
const first = resolveFormatterBin(root, 'biome');
fs.unlinkSync(path.join(binDir, binName));
const second = resolveFormatterBin(root, 'biome');
assert.strictEqual(first.bin, second.bin);
});
// ── clearCaches ───────────────────────────────────────────────
run('clearCaches: clears all cached values', () => {
const root = makeTmpDir();
fs.writeFileSync(path.join(root, 'package.json'), '{}');
fs.writeFileSync(path.join(root, 'biome.json'), '{}');
findProjectRoot(root);
detectFormatter(root);
resolveFormatterBin(root, 'biome');
clearCaches();
// After clearing, removing config should change detection
fs.unlinkSync(path.join(root, 'biome.json'));
assert.strictEqual(detectFormatter(root), null);
});
// ── Summary & Cleanup ─────────────────────────────────────────
cleanupTmpDirs();
console.log('\n=== Test Results ===');
console.log(`Passed: ${passed}`);
console.log(`Failed: ${failed}`);
console.log(`Total: ${passed + failed}`);
process.exit(failed > 0 ? 1 : 0);
}
runTests();

View File

@@ -10,24 +10,28 @@ const path = require('path');
const fs = require('fs');
const testsDir = __dirname;
const testFiles = [
'lib/utils.test.js',
'lib/package-manager.test.js',
'lib/session-manager.test.js',
'lib/session-aliases.test.js',
'lib/project-detect.test.js',
'hooks/hooks.test.js',
'hooks/evaluate-session.test.js',
'hooks/suggest-compact.test.js',
'integration/hooks.test.js',
'ci/validators.test.js',
'scripts/claw.test.js',
'scripts/setup-package-manager.test.js',
'scripts/skill-create-output.test.js'
];
/**
* Discover all *.test.js files under testsDir (relative paths for stable output order).
*/
function discoverTestFiles(dir, baseDir = dir, acc = []) {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const e of entries) {
const full = path.join(dir, e.name);
const rel = path.relative(baseDir, full);
if (e.isDirectory()) {
discoverTestFiles(full, baseDir, acc);
} else if (e.isFile() && e.name.endsWith('.test.js')) {
acc.push(rel);
}
}
return acc.sort();
}
const testFiles = discoverTestFiles(testsDir);
const BOX_W = 58; // inner width between ║ delimiters
const boxLine = (s) => `${s.padEnd(BOX_W)}`;
const boxLine = s => `${s.padEnd(BOX_W)}`;
console.log('╔' + '═'.repeat(BOX_W) + '╗');
console.log(boxLine(' Everything Claude Code - Test Suite'));
@@ -67,6 +71,17 @@ for (const testFile of testFiles) {
if (passedMatch) totalPassed += parseInt(passedMatch[1], 10);
if (failedMatch) totalFailed += parseInt(failedMatch[1], 10);
if (result.error) {
console.log(`${testFile} failed to start: ${result.error.message}`);
totalFailed += failedMatch ? 0 : 1;
continue;
}
if (result.status !== 0) {
console.log(`${testFile} exited with status ${result.status}`);
totalFailed += failedMatch ? 0 : 1;
}
}
totalTests = totalPassed + totalFailed;