226 lines
6.7 KiB
JavaScript
Executable File
226 lines
6.7 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
/**
|
|
* test-monorepo-scenarios.cjs - Integration tests for monorepo patterns
|
|
*
|
|
* THIS IS THE CRITICAL TEST FILE FOR THE BUG FIX!
|
|
* Tests that subfolder blocked directories (node_modules, dist, etc.)
|
|
* are properly blocked in monorepo structures.
|
|
*/
|
|
|
|
const { execSync } = require('child_process');
|
|
const path = require('path');
|
|
|
|
const hookPath = path.join(__dirname, '..', '..', 'scout-block.cjs');
|
|
|
|
const scenarios = [
|
|
// === THE BUG CASES - These MUST be BLOCKED ===
|
|
{
|
|
input: { tool_name: 'Bash', tool_input: { command: 'ls packages/web/node_modules' } },
|
|
expected: 'BLOCKED',
|
|
desc: '[BUG FIX] ls subfolder node_modules'
|
|
},
|
|
{
|
|
input: { tool_name: 'Bash', tool_input: { command: 'cd apps/api/node_modules' } },
|
|
expected: 'BLOCKED',
|
|
desc: '[BUG FIX] cd subfolder node_modules'
|
|
},
|
|
{
|
|
input: { tool_name: 'Bash', tool_input: { command: 'cat packages/shared/node_modules/lodash/index.js' } },
|
|
expected: 'BLOCKED',
|
|
desc: '[BUG FIX] cat file in subfolder node_modules'
|
|
},
|
|
{
|
|
input: { tool_name: 'Read', tool_input: { file_path: 'packages/web/node_modules/react/package.json' } },
|
|
expected: 'BLOCKED',
|
|
desc: '[BUG FIX] Read subfolder node_modules'
|
|
},
|
|
{
|
|
input: { tool_name: 'Grep', tool_input: { pattern: 'export', path: 'packages/web/node_modules' } },
|
|
expected: 'BLOCKED',
|
|
desc: '[BUG FIX] Grep in subfolder node_modules'
|
|
},
|
|
{
|
|
input: { tool_name: 'Glob', tool_input: { pattern: 'packages/web/node_modules/**/*.js' } },
|
|
expected: 'BLOCKED',
|
|
desc: '[BUG FIX] Glob subfolder node_modules'
|
|
},
|
|
|
|
// === Deep nesting (also bug cases) ===
|
|
{
|
|
input: { tool_name: 'Read', tool_input: { file_path: 'a/b/c/d/node_modules/pkg/index.js' } },
|
|
expected: 'BLOCKED',
|
|
desc: '[BUG FIX] Deep nested node_modules'
|
|
},
|
|
{
|
|
input: { tool_name: 'Bash', tool_input: { command: 'ls packages/web/dist' } },
|
|
expected: 'BLOCKED',
|
|
desc: '[BUG FIX] ls subfolder dist'
|
|
},
|
|
{
|
|
input: { tool_name: 'Bash', tool_input: { command: 'cat apps/api/build/server.js' } },
|
|
expected: 'BLOCKED',
|
|
desc: '[BUG FIX] cat subfolder build'
|
|
},
|
|
|
|
// === Root level blocking (should still work) ===
|
|
{
|
|
input: { tool_name: 'Bash', tool_input: { command: 'ls node_modules' } },
|
|
expected: 'BLOCKED',
|
|
desc: 'ls root node_modules'
|
|
},
|
|
{
|
|
input: { tool_name: 'Read', tool_input: { file_path: 'node_modules/lodash/index.js' } },
|
|
expected: 'BLOCKED',
|
|
desc: 'Read root node_modules'
|
|
},
|
|
{
|
|
input: { tool_name: 'Bash', tool_input: { command: 'cat .git/config' } },
|
|
expected: 'BLOCKED',
|
|
desc: 'cat .git file'
|
|
},
|
|
|
|
// === Build commands - MUST be ALLOWED ===
|
|
{
|
|
input: { tool_name: 'Bash', tool_input: { command: 'npm run build' } },
|
|
expected: 'ALLOWED',
|
|
desc: 'npm run build'
|
|
},
|
|
{
|
|
input: { tool_name: 'Bash', tool_input: { command: 'pnpm build' } },
|
|
expected: 'ALLOWED',
|
|
desc: 'pnpm build'
|
|
},
|
|
{
|
|
input: { tool_name: 'Bash', tool_input: { command: 'yarn build' } },
|
|
expected: 'ALLOWED',
|
|
desc: 'yarn build'
|
|
},
|
|
{
|
|
input: { tool_name: 'Bash', tool_input: { command: 'npm test' } },
|
|
expected: 'ALLOWED',
|
|
desc: 'npm test'
|
|
},
|
|
{
|
|
input: { tool_name: 'Bash', tool_input: { command: 'npm install' } },
|
|
expected: 'ALLOWED',
|
|
desc: 'npm install'
|
|
},
|
|
{
|
|
input: { tool_name: 'Bash', tool_input: { command: 'pnpm --filter web run build' } },
|
|
expected: 'ALLOWED',
|
|
desc: 'pnpm filter build'
|
|
},
|
|
{
|
|
input: { tool_name: 'Bash', tool_input: { command: 'npx tsc' } },
|
|
expected: 'ALLOWED',
|
|
desc: 'npx tsc'
|
|
},
|
|
{
|
|
input: { tool_name: 'Bash', tool_input: { command: 'jest --coverage' } },
|
|
expected: 'ALLOWED',
|
|
desc: 'jest with flags'
|
|
},
|
|
|
|
// === Safe operations - MUST be ALLOWED ===
|
|
{
|
|
input: { tool_name: 'Read', tool_input: { file_path: 'packages/web/src/App.tsx' } },
|
|
expected: 'ALLOWED',
|
|
desc: 'Read safe path'
|
|
},
|
|
{
|
|
input: { tool_name: 'Bash', tool_input: { command: 'ls packages/web/src' } },
|
|
expected: 'ALLOWED',
|
|
desc: 'ls safe path'
|
|
},
|
|
{
|
|
input: { tool_name: 'Grep', tool_input: { pattern: 'import', path: 'src' } },
|
|
expected: 'ALLOWED',
|
|
desc: 'Grep in src'
|
|
},
|
|
{
|
|
input: { tool_name: 'Glob', tool_input: { pattern: '**/*.ts' } },
|
|
expected: 'ALLOWED',
|
|
desc: 'Glob all .ts files'
|
|
},
|
|
{
|
|
input: { tool_name: 'Bash', tool_input: { command: 'find packages -name "*.json" | head' } },
|
|
expected: 'ALLOWED',
|
|
desc: 'find without blocked dirs'
|
|
},
|
|
|
|
// === Edge cases - names containing blocked words but NOT the dirs ===
|
|
{
|
|
input: { tool_name: 'Read', tool_input: { file_path: 'my-node_modules-project/file.js' } },
|
|
expected: 'ALLOWED',
|
|
desc: 'node_modules in project name'
|
|
},
|
|
{
|
|
input: { tool_name: 'Bash', tool_input: { command: 'ls build-tools' } },
|
|
expected: 'ALLOWED',
|
|
desc: 'build- prefix directory'
|
|
},
|
|
];
|
|
|
|
console.log('Testing monorepo scenarios (scout-block integration)...\n');
|
|
console.log('Hook path:', hookPath, '\n');
|
|
|
|
let passed = 0;
|
|
let failed = 0;
|
|
|
|
for (const scenario of scenarios) {
|
|
try {
|
|
execSync(`node "${hookPath}"`, {
|
|
input: JSON.stringify(scenario.input),
|
|
encoding: 'utf-8',
|
|
stdio: ['pipe', 'pipe', 'pipe']
|
|
});
|
|
// Exit 0 = ALLOWED
|
|
const actual = 'ALLOWED';
|
|
const success = actual === scenario.expected;
|
|
if (success) {
|
|
console.log(`\x1b[32m✓\x1b[0m ${scenario.desc}: ${actual}`);
|
|
passed++;
|
|
} else {
|
|
console.log(`\x1b[31m✗\x1b[0m ${scenario.desc}: expected ${scenario.expected}, got ${actual}`);
|
|
failed++;
|
|
}
|
|
} catch (error) {
|
|
// Exit 2 = BLOCKED
|
|
const actual = error.status === 2 ? 'BLOCKED' : `ERROR(${error.status})`;
|
|
const success = actual === scenario.expected;
|
|
if (success) {
|
|
console.log(`\x1b[32m✓\x1b[0m ${scenario.desc}: ${actual}`);
|
|
passed++;
|
|
} else {
|
|
console.log(`\x1b[31m✗\x1b[0m ${scenario.desc}: expected ${scenario.expected}, got ${actual}`);
|
|
if (error.stderr) {
|
|
console.log(` stderr: ${error.stderr.toString().trim().split('\n')[0]}`);
|
|
}
|
|
failed++;
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(`\nResults: ${passed} passed, ${failed} failed`);
|
|
|
|
// Highlight if any bug fix cases failed
|
|
const bugFixFailed = scenarios.filter(s => s.desc.includes('[BUG FIX]')).some(s => {
|
|
try {
|
|
execSync(`node "${hookPath}"`, {
|
|
input: JSON.stringify(s.input),
|
|
encoding: 'utf-8',
|
|
stdio: ['pipe', 'pipe', 'pipe']
|
|
});
|
|
return s.expected === 'BLOCKED'; // Should have been blocked but wasn't
|
|
} catch (error) {
|
|
return error.status !== 2 && s.expected === 'BLOCKED';
|
|
}
|
|
});
|
|
|
|
if (bugFixFailed) {
|
|
console.log('\n\x1b[31mWARNING: Some bug fix test cases failed!\x1b[0m');
|
|
console.log('The subfolder blocking bug has NOT been fixed properly.');
|
|
}
|
|
|
|
process.exit(failed > 0 ? 1 : 0);
|