From 27ae5ea29966a153f24466f050cdd08a5cbd9834 Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Fri, 13 Feb 2026 10:03:48 -0800 Subject: [PATCH] test: cover evaluate-session/suggest-compact main().catch and validate-hooks JSON parse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - evaluate-session: main().catch when HOME is non-directory (ENOTDIR) - suggest-compact: main().catch double-failure when TMPDIR is non-directory - validate-hooks: invalid JSON in hooks.json triggers error exit Total tests: 831 → 834 --- tests/ci/validators.test.js | 16 +++++++++++++++ tests/hooks/hooks.test.js | 39 +++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/tests/ci/validators.test.js b/tests/ci/validators.test.js index 2654235d..fd35d5b7 100644 --- a/tests/ci/validators.test.js +++ b/tests/ci/validators.test.js @@ -1991,6 +1991,22 @@ function runTests() { fs.rmSync(skillsDir, { recursive: true, force: true }); })) passed++; else failed++; + // ── Round 76: validate-hooks.js invalid JSON in hooks.json ── + console.log('\nRound 76: validate-hooks.js (invalid JSON in hooks.json):'); + + if (test('reports error for invalid JSON in hooks.json', () => { + const testDir = createTestDir(); + const hooksFile = path.join(testDir, 'hooks.json'); + fs.writeFileSync(hooksFile, '{not valid json!!!'); + + const result = runValidatorWithDir('validate-hooks', 'HOOKS_FILE', hooksFile); + assert.strictEqual(result.code, 1, + `Expected exit 1 for invalid JSON, got ${result.code}`); + assert.ok(result.stderr.includes('Invalid JSON'), + `stderr should mention Invalid JSON, got: ${result.stderr}`); + cleanupTestDir(testDir); + })) passed++; else failed++; + // Summary console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`); process.exit(failed > 0 ? 1 : 0); diff --git a/tests/hooks/hooks.test.js b/tests/hooks/hooks.test.js index 193bb480..cc4a9cd5 100644 --- a/tests/hooks/hooks.test.js +++ b/tests/hooks/hooks.test.js @@ -3213,6 +3213,45 @@ async function runTests() { `stderr should contain [SessionEnd] Error:, got: ${result.stderr}`); })) passed++; else failed++; + // ── Round 76: evaluate-session.js main().catch handler ── + console.log('\nRound 76: evaluate-session.js (main catch — unrecoverable error):'); + + if (await asyncTest('evaluate-session exits 0 with error message when HOME is non-directory', async () => { + if (process.platform === 'win32') { + console.log(' (skipped — /dev/null not available on Windows)'); + return; + } + // HOME=/dev/null makes ensureDir(learnedSkillsPath) throw ENOTDIR, + // which propagates to main().catch — the top-level error boundary + const result = await runScript(path.join(scriptsDir, 'evaluate-session.js'), '{}', { + HOME: '/dev/null', + USERPROFILE: '/dev/null' + }); + assert.strictEqual(result.code, 0, + `Should exit 0 (don't block on errors), got ${result.code}`); + assert.ok(result.stderr.includes('[ContinuousLearning] Error:'), + `stderr should contain [ContinuousLearning] Error:, got: ${result.stderr}`); + })) passed++; else failed++; + + // ── Round 76: suggest-compact.js main().catch handler ── + console.log('\nRound 76: suggest-compact.js (main catch — double-failure):'); + + if (await asyncTest('suggest-compact exits 0 with error when TMPDIR is non-directory', async () => { + if (process.platform === 'win32') { + console.log(' (skipped — /dev/null not available on Windows)'); + return; + } + // TMPDIR=/dev/null causes openSync to fail (ENOTDIR), then the catch + // fallback writeFile also fails, propagating to main().catch + const result = await runScript(path.join(scriptsDir, 'suggest-compact.js'), '', { + TMPDIR: '/dev/null' + }); + assert.strictEqual(result.code, 0, + `Should exit 0 (don't block on errors), got ${result.code}`); + assert.ok(result.stderr.includes('[StrategicCompact] Error:'), + `stderr should contain [StrategicCompact] Error:, got: ${result.stderr}`); + })) passed++; else failed++; + // Summary console.log('\n=== Test Results ==='); console.log(`Passed: ${passed}`);