Files
juliusbrussee--caveman/tests/test_mode_tracker_stdin.js
wehub-resource-sync acf542cac6
Sync SKILL.md and rules / sync (push) Failing after 1s
chore: import upstream snapshot with attribution
2026-07-13 12:07:10 +08:00

86 lines
2.8 KiB
JavaScript

#!/usr/bin/env node
// Tests for the stdin 'error' handler in caveman-mode-tracker.js.
// Covers issue #538: an abnormal stdin close (broken pipe, parent crash) emits
// an 'error' event on process.stdin; without a listener Node throws it as an
// uncaught exception and the hook exits non-zero — a spurious hook failure.
//
// Run: node tests/test_mode_tracker_stdin.js
const path = require('path');
const os = require('os');
const fs = require('fs');
const assert = require('assert');
const { spawnSync } = require('child_process');
const HOOK_PATH = path.resolve(__dirname, '..', 'src', 'hooks', 'caveman-mode-tracker.js');
const CLEAN_EXIT = 0;
let passed = 0;
let failed = 0;
function test(name, fn) {
try {
fn();
passed++;
console.log(` ✓ ${name}`);
} catch (e) {
failed++;
console.error(` ✗ ${name}`);
console.error(` ${e.message}`);
}
}
console.log('caveman-mode-tracker stdin error handling\n');
// Load the REAL hook in a child, then emit an 'error' on process.stdin to
// simulate an abnormal close. stdin is left open (never closed) so the only
// event that fires is the injected 'error' — isolating the handler under test.
function runWithStdinError() {
const harness =
`require(${JSON.stringify(HOOK_PATH)});` +
`setImmediate(() => process.stdin.emit('error', new Error('EPIPE (simulated)')));`;
return spawnSync(process.execPath, ['-e', harness], {
stdio: ['pipe', 'ignore', 'pipe'],
encoding: 'utf8',
});
}
test('stdin "error" event does not crash the hook (exit 0)', () => {
const res = runWithStdinError();
assert.strictEqual(
res.status,
CLEAN_EXIT,
`expected clean exit on stdin error, got status=${res.status} signal=${res.signal}\n` +
`stderr: ${(res.stderr || '').trim()}`
);
assert.ok(
!/Unhandled 'error' event/.test(res.stderr || ''),
`hook leaked an uncaught stdin error:\n${(res.stderr || '').trim()}`
);
});
// Regression guard: the new listener must not disturb the normal path — a valid
// prompt piped on stdin, then a clean EOF, still exits 0.
test('normal stdin (valid JSON + clean EOF) still exits 0', () => {
const tmpConfig = fs.mkdtempSync(path.join(os.tmpdir(), 'caveman-tracker-stdin-'));
try {
const res = spawnSync(process.execPath, [HOOK_PATH], {
input: JSON.stringify({ prompt: 'hello there' }),
env: { ...process.env, CLAUDE_CONFIG_DIR: tmpConfig },
stdio: ['pipe', 'ignore', 'pipe'],
encoding: 'utf8',
});
assert.strictEqual(
res.status,
CLEAN_EXIT,
`expected clean exit on normal input, got status=${res.status}\n` +
`stderr: ${(res.stderr || '').trim()}`
);
} finally {
fs.rmSync(tmpConfig, { recursive: true, force: true });
}
});
console.log(`\n${passed} passed, ${failed} failed`);
process.exit(failed === 0 ? 0 : 1);