fix: fold session manager blockers into one candidate

This commit is contained in:
Affaan Mustafa
2026-03-24 23:08:27 -04:00
parent 7726c25e46
commit 1d0aa5ac2a
30 changed files with 1126 additions and 288 deletions

View File

@@ -7,6 +7,7 @@
const assert = require('assert');
const path = require('path');
const fs = require('fs');
const { spawnSync } = require('child_process');
// Import the module
const utils = require('../../scripts/lib/utils');
@@ -68,7 +69,13 @@ function runTests() {
const sessionsDir = utils.getSessionsDir();
const claudeDir = utils.getClaudeDir();
assert.ok(sessionsDir.startsWith(claudeDir), 'Sessions should be under Claude dir');
assert.ok(sessionsDir.includes('sessions'), 'Should contain sessions');
assert.ok(sessionsDir.endsWith(path.join('.claude', 'session-data')) || sessionsDir.endsWith('/.claude/session-data'), 'Should use canonical session-data directory');
})) passed++; else failed++;
if (test('getSessionSearchDirs includes canonical and legacy paths', () => {
const searchDirs = utils.getSessionSearchDirs();
assert.ok(searchDirs.includes(utils.getSessionsDir()), 'Should include canonical session dir');
assert.ok(searchDirs.includes(utils.getLegacySessionsDir()), 'Should include legacy session dir');
})) passed++; else failed++;
if (test('getTempDir returns valid temp directory', () => {
@@ -118,17 +125,77 @@ function runTests() {
assert.ok(name && name.length > 0);
})) passed++; else failed++;
// sanitizeSessionId tests
console.log('\nsanitizeSessionId:');
if (test('sanitizeSessionId strips leading dots', () => {
assert.strictEqual(utils.sanitizeSessionId('.claude'), 'claude');
})) passed++; else failed++;
if (test('sanitizeSessionId replaces dots and spaces', () => {
assert.strictEqual(utils.sanitizeSessionId('my.project'), 'my-project');
assert.strictEqual(utils.sanitizeSessionId('my project'), 'my-project');
})) passed++; else failed++;
if (test('sanitizeSessionId replaces special chars and collapses runs', () => {
assert.strictEqual(utils.sanitizeSessionId('project@v2'), 'project-v2');
assert.strictEqual(utils.sanitizeSessionId('a...b'), 'a-b');
})) passed++; else failed++;
if (test('sanitizeSessionId preserves valid chars', () => {
assert.strictEqual(utils.sanitizeSessionId('my-project_123'), 'my-project_123');
})) passed++; else failed++;
if (test('sanitizeSessionId returns null for empty or punctuation-only values', () => {
assert.strictEqual(utils.sanitizeSessionId(''), null);
assert.strictEqual(utils.sanitizeSessionId(null), null);
assert.strictEqual(utils.sanitizeSessionId(undefined), null);
assert.strictEqual(utils.sanitizeSessionId('...'), null);
assert.strictEqual(utils.sanitizeSessionId('…'), null);
})) passed++; else failed++;
if (test('sanitizeSessionId returns stable hashes for non-ASCII values', () => {
const chinese = utils.sanitizeSessionId('我的项目');
const cyrillic = utils.sanitizeSessionId('проект');
const emoji = utils.sanitizeSessionId('🚀🎉');
assert.ok(/^[a-f0-9]{8}$/.test(chinese), `Expected 8-char hash, got: ${chinese}`);
assert.ok(/^[a-f0-9]{8}$/.test(cyrillic), `Expected 8-char hash, got: ${cyrillic}`);
assert.ok(/^[a-f0-9]{8}$/.test(emoji), `Expected 8-char hash, got: ${emoji}`);
assert.notStrictEqual(chinese, cyrillic);
assert.notStrictEqual(chinese, emoji);
assert.strictEqual(utils.sanitizeSessionId('日本語プロジェクト'), utils.sanitizeSessionId('日本語プロジェクト'));
})) passed++; else failed++;
if (test('sanitizeSessionId disambiguates mixed-script names from pure ASCII', () => {
const mixed = utils.sanitizeSessionId('我的app');
const mixedTwo = utils.sanitizeSessionId('他的app');
const pure = utils.sanitizeSessionId('app');
assert.strictEqual(pure, 'app');
assert.ok(mixed.startsWith('app-'), `Expected mixed-script prefix, got: ${mixed}`);
assert.notStrictEqual(mixed, pure);
assert.notStrictEqual(mixed, mixedTwo);
})) passed++; else failed++;
if (test('sanitizeSessionId is idempotent', () => {
for (const input of ['.claude', 'my.project', 'project@v2', 'a...b', 'my-project_123']) {
const once = utils.sanitizeSessionId(input);
const twice = utils.sanitizeSessionId(once);
assert.strictEqual(once, twice, `Expected idempotent result for ${input}`);
}
})) passed++; else failed++;
// Session ID tests
console.log('\nSession ID Functions:');
if (test('getSessionIdShort falls back to project name', () => {
if (test('getSessionIdShort falls back to sanitized project name', () => {
const original = process.env.CLAUDE_SESSION_ID;
delete process.env.CLAUDE_SESSION_ID;
try {
const shortId = utils.getSessionIdShort();
assert.strictEqual(shortId, utils.getProjectName());
assert.strictEqual(shortId, utils.sanitizeSessionId(utils.getProjectName()));
} finally {
if (original) process.env.CLAUDE_SESSION_ID = original;
if (original !== undefined) process.env.CLAUDE_SESSION_ID = original;
else delete process.env.CLAUDE_SESSION_ID;
}
})) passed++; else failed++;
@@ -154,6 +221,28 @@ function runTests() {
}
})) passed++; else failed++;
if (test('getSessionIdShort sanitizes explicit fallback parameter', () => {
if (process.platform === 'win32') {
console.log(' (skipped — root CWD differs on Windows)');
return true;
}
const utilsPath = path.join(__dirname, '..', '..', 'scripts', 'lib', 'utils.js');
const script = `
const utils = require('${utilsPath.replace(/'/g, "\\'")}');
process.stdout.write(utils.getSessionIdShort('my.fallback'));
`;
const result = spawnSync('node', ['-e', script], {
encoding: 'utf8',
cwd: '/',
env: { ...process.env, CLAUDE_SESSION_ID: '' },
timeout: 10000
});
assert.strictEqual(result.status, 0, `Expected exit 0, got ${result.status}. stderr: ${result.stderr}`);
assert.strictEqual(result.stdout, 'my-fallback');
})) passed++; else failed++;
// File operations tests
console.log('\nFile Operations:');
@@ -1415,25 +1504,26 @@ function runTests() {
// ── Round 97: getSessionIdShort with whitespace-only CLAUDE_SESSION_ID ──
console.log('\nRound 97: getSessionIdShort (whitespace-only session ID):');
if (test('getSessionIdShort returns whitespace when CLAUDE_SESSION_ID is all spaces', () => {
// utils.js line 116: if (sessionId && sessionId.length > 0) — ' ' is truthy
// and has length > 0, so it passes the check instead of falling back.
const original = process.env.CLAUDE_SESSION_ID;
try {
process.env.CLAUDE_SESSION_ID = ' '; // 10 spaces
const result = utils.getSessionIdShort('fallback');
// slice(-8) on 10 spaces returns 8 spaces — not the expected fallback
assert.strictEqual(result, ' ',
'Whitespace-only ID should return 8 trailing spaces (no trim check)');
assert.strictEqual(result.trim().length, 0,
'Result should be entirely whitespace (demonstrating the missing trim)');
} finally {
if (original !== undefined) {
process.env.CLAUDE_SESSION_ID = original;
} else {
delete process.env.CLAUDE_SESSION_ID;
}
if (test('getSessionIdShort sanitizes whitespace-only CLAUDE_SESSION_ID to fallback', () => {
if (process.platform === 'win32') {
console.log(' (skipped — root CWD differs on Windows)');
return true;
}
const utilsPath = path.join(__dirname, '..', '..', 'scripts', 'lib', 'utils.js');
const script = `
const utils = require('${utilsPath.replace(/'/g, "\\'")}');
process.stdout.write(utils.getSessionIdShort('fallback'));
`;
const result = spawnSync('node', ['-e', script], {
encoding: 'utf8',
cwd: '/',
env: { ...process.env, CLAUDE_SESSION_ID: ' ' },
timeout: 10000
});
assert.strictEqual(result.status, 0, `Expected exit 0, got ${result.status}. stderr: ${result.stderr}`);
assert.strictEqual(result.stdout, 'fallback');
})) passed++; else failed++;
// ── Round 97: countInFile with same RegExp object called twice (lastIndex reuse) ──