test: add 3 edge-case tests for findFiles dotfiles, getAllSessions date format, parseSessionMetadata title regex

Round 124: Tests for findFiles matching dotfiles (unlike shell glob where *
excludes hidden files), getAllSessions strict date equality filter (wrong format
silently returns empty), and parseSessionMetadata title regex edge cases
(no space after #, ## heading, multiple H1, greedy \s+ crossing newlines).
Total: 932 tests, all passing.
This commit is contained in:
Affaan Mustafa
2026-02-13 18:40:07 -08:00
parent 88fa1bdbbc
commit 0250de793a
2 changed files with 150 additions and 0 deletions

View File

@@ -2442,6 +2442,113 @@ file.ts
'CRLF notes >= LF notes length (CRLF may bleed past blank line)');
})) passed++; else failed++;
// ── Round 124: getAllSessions with invalid date format (strict equality, no normalization) ──
console.log('\nRound 124: getAllSessions (invalid date format — strict !== comparison):');
if (test('getAllSessions date filter uses strict equality so wrong format returns empty', () => {
// session-manager.js line 228: `if (date && metadata.date !== date)` — strict inequality.
// metadata.date is always "YYYY-MM-DD" format. Passing a different format like
// "2026/01/15" or "Jan 15 2026" will never match, silently returning empty.
// No validation or normalization occurs on the date parameter.
const origHome = process.env.HOME;
const origDir = process.env.CLAUDE_DIR;
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'r124-date-format-'));
const homeDir = path.join(tmpDir, 'home');
fs.mkdirSync(path.join(homeDir, '.claude', 'sessions'), { recursive: true });
try {
process.env.HOME = homeDir;
delete process.env.CLAUDE_DIR;
delete require.cache[require.resolve('../../scripts/lib/utils')];
delete require.cache[require.resolve('../../scripts/lib/session-manager')];
const freshSM = require('../../scripts/lib/session-manager');
// Create a session file with valid date
const sessionsDir = path.join(homeDir, '.claude', 'sessions');
fs.writeFileSync(
path.join(sessionsDir, '2026-01-15-abcd1234-session.tmp'),
'# Test Session'
);
// Correct format — should find 1 session
const correct = freshSM.getAllSessions({ date: '2026-01-15' });
assert.strictEqual(correct.sessions.length, 1,
'Correct YYYY-MM-DD format should match');
// Wrong separator — strict !== means no match
const wrongSep = freshSM.getAllSessions({ date: '2026/01/15' });
assert.strictEqual(wrongSep.sessions.length, 0,
'Slash-separated date does not match (strict string equality)');
// US format — no match
const usFormat = freshSM.getAllSessions({ date: '01-15-2026' });
assert.strictEqual(usFormat.sessions.length, 0,
'MM-DD-YYYY format does not match YYYY-MM-DD');
// Partial date — no match
const partial = freshSM.getAllSessions({ date: '2026-01' });
assert.strictEqual(partial.sessions.length, 0,
'Partial YYYY-MM does not match full YYYY-MM-DD');
// null date — skips filter, returns all
const nullDate = freshSM.getAllSessions({ date: null });
assert.strictEqual(nullDate.sessions.length, 1,
'null date skips filter and returns all sessions');
} finally {
process.env.HOME = origHome;
if (origDir) process.env.CLAUDE_DIR = origDir;
delete require.cache[require.resolve('../../scripts/lib/utils')];
delete require.cache[require.resolve('../../scripts/lib/session-manager')];
fs.rmSync(tmpDir, { recursive: true, force: true });
}
})) passed++; else failed++;
// ── Round 124: parseSessionMetadata title edge cases (no space, wrong level, multiple, empty) ──
console.log('\nRound 124: parseSessionMetadata (title regex edge cases — /^#\\s+(.+)$/m):');
if (test('parseSessionMetadata title: no space after # fails, ## fails, multiple picks first, empty trims', () => {
// session-manager.js line 95: /^#\s+(.+)$/m
// \s+ requires at least one whitespace after #, (.+) captures rest of line
// No space after # — \s+ fails to match
const noSpace = '#NoSpaceTitle\n\nSome content';
const meta1 = sessionManager.parseSessionMetadata(noSpace);
assert.strictEqual(meta1.title, null,
'#NoSpaceTitle has no whitespace after # → title is null');
// ## (H2) heading — ^ anchors to line start, but # matches first char only
// /^#\s+/ matches the first # then \s+ would need whitespace, but ## has another #
// Actually: /^#\s+(.+)$/ → "##" → # then \s+ → # is not whitespace → no match
const h2 = '## Subtitle\n\nContent';
const meta2 = sessionManager.parseSessionMetadata(h2);
assert.strictEqual(meta2.title, null,
'## heading does not match /^#\\s+/ because second # is not whitespace');
// Multiple # headings — first match wins (regex .match returns first)
const multiple = '# First Title\n\n# Second Title\n\nContent';
const meta3 = sessionManager.parseSessionMetadata(multiple);
assert.strictEqual(meta3.title, 'First Title',
'Multiple H1 headings: .match() returns first occurrence');
// # followed by spaces then text — leading spaces in capture are trimmed
const padded = '# Padded Title \n\nContent';
const meta4 = sessionManager.parseSessionMetadata(padded);
assert.strictEqual(meta4.title, 'Padded Title',
'Extra spaces: \\s+ matches multiple spaces, (.+) captures, .trim() cleans');
// # followed by just spaces (no actual title text)
// Surprising: \s+ is greedy and includes \n, so it matches " \n\n" (spaces + newlines)
// Then (.+) captures "Content" from the next non-empty line!
const spacesOnly = '# \n\nContent';
const meta5 = sessionManager.parseSessionMetadata(spacesOnly);
assert.strictEqual(meta5.title, 'Content',
'Spaces-only after # → \\s+ greedily matches spaces+newlines, (.+) captures next line text');
// Tab after # — \s includes tab
const tabTitle = '#\tTab Title\n\nContent';
const meta6 = sessionManager.parseSessionMetadata(tabTitle);
assert.strictEqual(meta6.title, 'Tab Title',
'Tab after # matches \\s+ (\\s includes \\t)');
})) passed++; else failed++;
// Summary
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
process.exit(failed > 0 ? 1 : 0);

View File

@@ -2189,6 +2189,49 @@ function runTests() {
}
})) passed++; else failed++;
// ── Round 124: findFiles matches dotfiles (unlike shell glob where * excludes hidden files) ──
console.log('\nRound 124: findFiles (* glob matches dotfiles — unlike shell globbing):');
if (test('findFiles with * pattern matches dotfiles because .* regex includes hidden files', () => {
// In shell: `ls *` excludes .hidden files. In findFiles, `*` → `.*` regex which
// matches ANY filename including those starting with `.`. This is a behavioral
// difference from shell globbing that could surprise users.
const tmpDir = fs.mkdtempSync(path.join(utils.getTempDir(), 'r124-dotfiles-'));
try {
// Create normal and hidden files
fs.writeFileSync(path.join(tmpDir, 'normal.txt'), 'visible');
fs.writeFileSync(path.join(tmpDir, '.hidden'), 'hidden');
fs.writeFileSync(path.join(tmpDir, '.gitignore'), 'ignore');
fs.writeFileSync(path.join(tmpDir, 'README.md'), 'readme');
// * matches ALL files including dotfiles
const allResults = utils.findFiles(tmpDir, '*');
const names = allResults.map(r => path.basename(r.path)).sort();
assert.ok(names.includes('.hidden'),
'* should match .hidden (unlike shell glob)');
assert.ok(names.includes('.gitignore'),
'* should match .gitignore');
assert.ok(names.includes('normal.txt'),
'* should match normal.txt');
assert.strictEqual(names.length, 4,
'Should find all 4 files including 2 dotfiles');
// *.txt does NOT match dotfiles (because they don't end with .txt)
const txtResults = utils.findFiles(tmpDir, '*.txt');
assert.strictEqual(txtResults.length, 1,
'*.txt should only match normal.txt, not dotfiles');
// .* pattern specifically matches only dotfiles
const dotResults = utils.findFiles(tmpDir, '.*');
const dotNames = dotResults.map(r => path.basename(r.path)).sort();
assert.ok(dotNames.includes('.hidden'), '.* matches .hidden');
assert.ok(dotNames.includes('.gitignore'), '.* matches .gitignore');
assert.ok(!dotNames.includes('normal.txt'),
'.* should NOT match normal.txt (needs leading dot)');
} finally {
fs.rmSync(tmpDir, { recursive: true, force: true });
}
})) passed++; else failed++;
// Summary
console.log('\n=== Test Results ===');
console.log(`Passed: ${passed}`);