From 723e69a62159a45055737f69e7f7c3fcba14c325 Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Fri, 13 Feb 2026 09:59:48 -0800 Subject: [PATCH] test: cover deleteSession catch, pre-compact and session-end main().catch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - session-manager: deleteSession returns false when dir is read-only (EACCES) - pre-compact: main().catch handler when HOME is non-directory (ENOTDIR) - session-end: main().catch handler when HOME is non-directory (ENOTDIR) Total tests: 828 → 831 --- tests/hooks/hooks.test.js | 40 +++++++++++++++++++++++++++++++ tests/lib/session-manager.test.js | 23 ++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/tests/hooks/hooks.test.js b/tests/hooks/hooks.test.js index cc44d17d..193bb480 100644 --- a/tests/hooks/hooks.test.js +++ b/tests/hooks/hooks.test.js @@ -3173,6 +3173,46 @@ async function runTests() { `stderr should contain [SessionStart] Error:, got: ${result.stderr}`); })) passed++; else failed++; + // ── Round 75: pre-compact.js main().catch handler ── + console.log('\nRound 75: pre-compact.js (main catch — unrecoverable error):'); + + if (await asyncTest('pre-compact 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(sessionsDir) throw ENOTDIR, + // which propagates to main().catch — the top-level error boundary + const result = await runScript(path.join(scriptsDir, 'pre-compact.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('[PreCompact] Error:'), + `stderr should contain [PreCompact] Error:, got: ${result.stderr}`); + })) passed++; else failed++; + + // ── Round 75: session-end.js main().catch handler ── + console.log('\nRound 75: session-end.js (main catch — unrecoverable error):'); + + if (await asyncTest('session-end 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(sessionsDir) throw ENOTDIR inside main(), + // which propagates to runMain().catch — the top-level error boundary + const result = await runScript(path.join(scriptsDir, 'session-end.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('[SessionEnd] Error:'), + `stderr should contain [SessionEnd] Error:, got: ${result.stderr}`); + })) passed++; else failed++; + // Summary console.log('\n=== Test Results ==='); console.log(`Passed: ${passed}`); diff --git a/tests/lib/session-manager.test.js b/tests/lib/session-manager.test.js index 93fcc44d..102d252b 100644 --- a/tests/lib/session-manager.test.js +++ b/tests/lib/session-manager.test.js @@ -1207,6 +1207,29 @@ src/main.ts } })) passed++; else failed++; + // ── Round 75: deleteSession catch — unlinkSync throws on read-only dir ── + console.log('\nRound 75: deleteSession (unlink failure in read-only dir):'); + + if (test('deleteSession returns false when file exists but directory is read-only', () => { + if (process.platform === 'win32' || process.getuid?.() === 0) { + console.log(' (skipped — chmod ineffective on Windows/root)'); + return; + } + const tmpDir = path.join(os.tmpdir(), `sm-del-ro-${Date.now()}`); + fs.mkdirSync(tmpDir, { recursive: true }); + const sessionFile = path.join(tmpDir, 'test-session.tmp'); + fs.writeFileSync(sessionFile, 'session content'); + try { + // Make directory read-only so unlinkSync throws EACCES + fs.chmodSync(tmpDir, 0o555); + const result = sessionManager.deleteSession(sessionFile); + assert.strictEqual(result, false, 'Should return false when unlinkSync fails'); + } finally { + try { fs.chmodSync(tmpDir, 0o755); } catch { /* best-effort */ } + fs.rmSync(tmpDir, { recursive: true, force: true }); + } + })) passed++; else failed++; + // Summary console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`); process.exit(failed > 0 ? 1 : 0);