From 882157ac091590d475f5da98ebde944affb8df6d Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Fri, 13 Feb 2026 17:11:32 -0800 Subject: [PATCH] test: add 3 tests for Round 107 (881 total) - grepFile with ^$ pattern verifies empty line matching including trailing newline phantom - replaceInFile with self-reintroducing replacement confirms single-pass behavior - setAlias with whitespace-only title exposes missing trim validation vs sessionPath --- tests/lib/session-aliases.test.js | 20 +++++++++++++++++ tests/lib/utils.test.js | 37 +++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/tests/lib/session-aliases.test.js b/tests/lib/session-aliases.test.js index daadfb92..d6c9eefb 100644 --- a/tests/lib/session-aliases.test.js +++ b/tests/lib/session-aliases.test.js @@ -1375,6 +1375,26 @@ function runTests() { 'Another path-traversal pattern also returned unchanged'); })) passed++; else failed++; + // ── Round 107: setAlias with whitespace-only title (not trimmed unlike sessionPath) ── + console.log('\nRound 107: setAlias (whitespace-only title — truthy string stored as-is, unlike sessionPath which is trim-checked):'); + if (test('setAlias stores whitespace-only title as-is (no trim validation, unlike sessionPath)', () => { + resetAliases(); + // sessionPath with whitespace is rejected (line 195: sessionPath.trim().length === 0) + const pathResult = aliases.setAlias('ws-path', ' '); + assert.strictEqual(pathResult.success, false, + 'Whitespace-only sessionPath is rejected by trim check'); + // But title with whitespace is stored as-is (line 221: title || null — whitespace is truthy) + const titleResult = aliases.setAlias('ws-title', '/valid/path', ' '); + assert.strictEqual(titleResult.success, true, + 'Whitespace-only title is accepted (no trim check on title)'); + assert.strictEqual(titleResult.title, ' ', + 'Title stored as whitespace string (truthy, so title || null returns the whitespace)'); + // Verify persisted correctly + const loaded = aliases.loadAliases(); + assert.strictEqual(loaded.aliases['ws-title'].title, ' ', + 'Whitespace title persists in JSON as-is'); + })) passed++; else failed++; + // Summary console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`); process.exit(failed > 0 ? 1 : 0); diff --git a/tests/lib/utils.test.js b/tests/lib/utils.test.js index 4b775de9..a6b25ffd 100644 --- a/tests/lib/utils.test.js +++ b/tests/lib/utils.test.js @@ -1548,6 +1548,43 @@ function runTests() { } })) passed++; else failed++; + // ── Round 107: grepFile with ^$ pattern — empty line matching after split ── + console.log('\nRound 107: grepFile (empty line matching — ^$ on split lines, trailing \\n creates extra empty element):'); + if (test('grepFile matches empty lines with ^$ pattern including trailing newline phantom line', () => { + const tmpDir = fs.mkdtempSync(path.join(utils.getTempDir(), 'r107-grep-empty-')); + const testFile = path.join(tmpDir, 'test.txt'); + try { + // 'line1\n\nline3\n\n'.split('\n') → ['line1','','line3','',''] (5 elements, 3 empty) + fs.writeFileSync(testFile, 'line1\n\nline3\n\n'); + const results = utils.grepFile(testFile, /^$/); + assert.strictEqual(results.length, 3, + 'Should match 3 empty lines: line 2, line 4, and trailing phantom line 5'); + assert.strictEqual(results[0].lineNumber, 2, 'First empty line at position 2'); + assert.strictEqual(results[1].lineNumber, 4, 'Second empty line at position 4'); + assert.strictEqual(results[2].lineNumber, 5, 'Third empty line is the trailing phantom from split'); + } finally { + fs.rmSync(tmpDir, { recursive: true, force: true }); + } + })) passed++; else failed++; + + // ── Round 107: replaceInFile where replacement re-introduces search pattern (single-pass) ── + console.log('\nRound 107: replaceInFile (replacement contains search pattern — String.replace is single-pass):'); + if (test('replaceInFile does not re-scan replacement text (single-pass, no infinite loop)', () => { + const tmpDir = fs.mkdtempSync(path.join(utils.getTempDir(), 'r107-replace-reintr-')); + const testFile = path.join(tmpDir, 'test.txt'); + try { + fs.writeFileSync(testFile, 'foo bar baz'); + // Replace "foo" with "foo extra foo" — should only replace the first occurrence + const result = utils.replaceInFile(testFile, 'foo', 'foo extra foo'); + assert.strictEqual(result, true, 'replaceInFile should return true'); + const content = utils.readFile(testFile); + assert.strictEqual(content, 'foo extra foo bar baz', + 'Only the original "foo" is replaced — replacement text is not re-scanned'); + } finally { + fs.rmSync(tmpDir, { recursive: true, force: true }); + } + })) passed++; else failed++; + // ── Round 106: countInFile with named capture groups — match(g) ignores group details ── console.log('\nRound 106: countInFile (named capture groups — String.match(g) returns full matches only):'); if (test('countInFile with named capture groups counts matches not groups', () => {