Files
wehub-resource-sync d48cda4081
CI / Test (ubuntu-latest, Node 18.x, bun) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 18.x, npm) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 18.x, pnpm) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 18.x, yarn) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 20.x, bun) (push) Failing after 17m13s
CI / Test (ubuntu-latest, Node 20.x, npm) (push) Failing after 18m42s
CI / Test (ubuntu-latest, Node 20.x, pnpm) (push) Failing after 15m0s
CI / Test (ubuntu-latest, Node 20.x, yarn) (push) Failing after 49m44s
CI / Test (ubuntu-latest, Node 22.x, bun) (push) Failing after 51m55s
CI / Test (ubuntu-latest, Node 22.x, pnpm) (push) Failing after 21m57s
CI / Test (ubuntu-latest, Node 22.x, npm) (push) Failing after 37m39s
CI / Test (ubuntu-latest, Node 22.x, yarn) (push) Failing after 34m7s
CI / Validate Components (push) Failing after 37m15s
CI / Python Tests (push) Failing after 10m1s
CI / Security Scan (push) Failing after 10m1s
CI / Lint (push) Failing after 17m12s
CI / Coverage (push) Failing after 20m19s
CI / Test (macos-latest, Node 18.x, bun) (push) Has been cancelled
CI / Test (macos-latest, Node 18.x, npm) (push) Has been cancelled
CI / Test (macos-latest, Node 18.x, pnpm) (push) Has been cancelled
CI / Test (macos-latest, Node 18.x, yarn) (push) Has been cancelled
CI / Test (windows-latest, Node 18.x, npm) (push) Has been cancelled
CI / Test (windows-latest, Node 18.x, pnpm) (push) Has been cancelled
CI / Test (windows-latest, Node 18.x, yarn) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, bun) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, npm) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, pnpm) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, yarn) (push) Has been cancelled
CI / Test (windows-latest, Node 20.x, npm) (push) Has been cancelled
CI / Test (windows-latest, Node 20.x, pnpm) (push) Has been cancelled
CI / Test (windows-latest, Node 20.x, yarn) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, bun) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, npm) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, pnpm) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, yarn) (push) Has been cancelled
CI / Test (windows-latest, Node 22.x, npm) (push) Has been cancelled
CI / Test (windows-latest, Node 22.x, pnpm) (push) Has been cancelled
CI / Test (windows-latest, Node 22.x, yarn) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 11:55:55 +08:00

148 lines
5.4 KiB
JavaScript

/**
* Tests for .cursor/hooks/before-shell-execution-block-no-verify.js
*
* Issue #2107: previously .cursor/hooks.json wired `npx block-no-verify@1.1.2`,
* which over-matches and blocks legitimate commits whose message body
* mentions `--no-verify` or `-n`. The wrapper added in this PR delegates
* to the local scripts/hooks/block-no-verify.js so Cursor users get the
* same flag-position-aware matcher Claude Code already uses.
*/
'use strict';
const assert = require('assert');
const path = require('path');
const { spawnSync } = require('child_process');
const wrapper = path.join(
__dirname, '..', '..',
'.cursor', 'hooks', 'before-shell-execution-block-no-verify.js'
);
function runWrapper(input, env = {}) {
const rawInput = typeof input === 'string' ? input : JSON.stringify(input);
const result = spawnSync('node', [wrapper], {
input: rawInput,
encoding: 'utf8',
env: { ...process.env, ECC_HOOK_PROFILE: 'standard', ...env },
timeout: 15000,
stdio: ['pipe', 'pipe', 'pipe'],
});
return {
code: Number.isInteger(result.status) ? result.status : 1,
stdout: result.stdout || '',
stderr: result.stderr || '',
};
}
function test(name, fn) {
try {
fn();
console.log(` ✓ ${name}`);
return true;
} catch (error) {
console.log(` ✗ ${name}`);
console.log(` Error: ${error.message}`);
return false;
}
}
let passed = 0;
let failed = 0;
console.log('\ncursor block-no-verify wrapper tests');
console.log('─'.repeat(50));
// --- Cursor input shapes ---
if (test('reads Cursor top-level command field', () => {
const r = runWrapper({ command: 'git commit -m "hello"' });
assert.strictEqual(r.code, 0, `expected 0, got ${r.code}: ${r.stderr}`);
})) passed++; else failed++;
if (test('reads Cursor args.command field', () => {
const r = runWrapper({ args: { command: 'git commit -m "hello"' } });
assert.strictEqual(r.code, 0, `expected 0, got ${r.code}: ${r.stderr}`);
})) passed++; else failed++;
// --- Issue #2107 false positives now allowed ---
if (test('#2107: allows --no-verify mentioned in double-quoted message body', () => {
const r = runWrapper({ command: 'git commit -m "docs: explain why we never pass --no-verify"' });
assert.strictEqual(r.code, 0, `expected 0, got ${r.code}: ${r.stderr}`);
})) passed++; else failed++;
if (test('#2107: allows --no-verify mentioned in single-quoted message body', () => {
const r = runWrapper({ command: "git commit -m 'docs: discuss --no-verify risk'" });
assert.strictEqual(r.code, 0, `expected 0, got ${r.code}: ${r.stderr}`);
})) passed++; else failed++;
if (test('#2107: allows -n mentioned in quoted message body', () => {
const r = runWrapper({ command: 'git commit -m "fix: handle -n flag in parser"' });
assert.strictEqual(r.code, 0, `expected 0, got ${r.code}: ${r.stderr}`);
})) passed++; else failed++;
if (test('#2107: allows commit message containing the literal string "block-no-verify"', () => {
const r = runWrapper({ command: 'git commit -m "feat: add block-no-verify hook"' });
assert.strictEqual(r.code, 0, `expected 0, got ${r.code}: ${r.stderr}`);
})) passed++; else failed++;
// --- Real bypass attempts still blocked ---
if (test('still blocks real --no-verify flag', () => {
const r = runWrapper({ command: 'git commit --no-verify -m "msg"' });
assert.strictEqual(r.code, 2, `expected 2, got ${r.code}`);
assert.ok(r.stderr.includes('BLOCKED'), `stderr should contain BLOCKED: ${r.stderr}`);
})) passed++; else failed++;
if (test('still blocks -n shorthand on git commit', () => {
const r = runWrapper({ command: 'git commit -n -m "msg"' });
assert.strictEqual(r.code, 2, `expected 2, got ${r.code}`);
})) passed++; else failed++;
if (test('still blocks core.hooksPath override', () => {
const r = runWrapper({ command: 'git -c core.hooksPath=/dev/null commit -m "msg"' });
assert.strictEqual(r.code, 2, `expected 2, got ${r.code}`);
assert.ok(r.stderr.includes('core.hooksPath'), `stderr should mention core.hooksPath: ${r.stderr}`);
})) passed++; else failed++;
if (test('still blocks --no-verify on git push', () => {
const r = runWrapper({ command: 'git push --no-verify' });
assert.strictEqual(r.code, 2, `expected 2, got ${r.code}`);
})) passed++; else failed++;
// --- Pass-through cases ---
if (test('allows non-git commands', () => {
const r = runWrapper({ command: 'npm test' });
assert.strictEqual(r.code, 0, `expected 0, got ${r.code}: ${r.stderr}`);
})) passed++; else failed++;
if (test('handles empty stdin gracefully', () => {
const r = runWrapper('');
assert.strictEqual(r.code, 0, `expected 0, got ${r.code}: ${r.stderr}`);
})) passed++; else failed++;
if (test('handles malformed JSON gracefully (treats raw as command string)', () => {
const r = runWrapper('git commit -m "hello"');
assert.strictEqual(r.code, 0, `expected 0, got ${r.code}: ${r.stderr}`);
})) passed++; else failed++;
// --- Disable via ECC_DISABLED_HOOKS ---
if (test('respects ECC_DISABLED_HOOKS=pre:bash:block-no-verify', () => {
const r = runWrapper(
{ command: 'git commit --no-verify -m "msg"' },
{ ECC_DISABLED_HOOKS: 'pre:bash:block-no-verify' }
);
// When the hook is disabled, the wrapper should pass through (exit 0)
// even on a real bypass attempt.
assert.strictEqual(r.code, 0, `expected 0 when disabled, got ${r.code}: ${r.stderr}`);
})) passed++; else failed++;
console.log('─'.repeat(50));
console.log(`Passed: ${passed} Failed: ${failed}`);
process.exit(failed > 0 ? 1 : 0);