mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-10 11:23:32 +08:00
fix: clamp getAllSessions pagination params, add cleanupAliases success field, add 10 tests
- session-manager: clamp offset/limit to safe non-negative integers to prevent negative offset counting from end and NaN returning empty results - session-aliases: add success field to cleanupAliases return value for API contract consistency with setAlias/deleteAlias/renameAlias
This commit is contained in:
@@ -446,9 +446,17 @@ function cleanupAliases(sessionExists) {
|
|||||||
|
|
||||||
if (removed.length > 0 && !saveAliases(data)) {
|
if (removed.length > 0 && !saveAliases(data)) {
|
||||||
log('[Aliases] Failed to save after cleanup');
|
log('[Aliases] Failed to save after cleanup');
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
totalChecked: Object.keys(data.aliases).length + removed.length,
|
||||||
|
removed: removed.length,
|
||||||
|
removedAliases: removed,
|
||||||
|
error: 'Failed to save after cleanup'
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
success: true,
|
||||||
totalChecked: Object.keys(data.aliases).length + removed.length,
|
totalChecked: Object.keys(data.aliases).length + removed.length,
|
||||||
removed: removed.length,
|
removed: removed.length,
|
||||||
removedAliases: removed
|
removedAliases: removed
|
||||||
|
|||||||
@@ -189,12 +189,21 @@ function getSessionStats(sessionPathOrContent) {
|
|||||||
*/
|
*/
|
||||||
function getAllSessions(options = {}) {
|
function getAllSessions(options = {}) {
|
||||||
const {
|
const {
|
||||||
limit = 50,
|
limit: rawLimit = 50,
|
||||||
offset = 0,
|
offset: rawOffset = 0,
|
||||||
date = null,
|
date = null,
|
||||||
search = null
|
search = null
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
|
// Clamp offset and limit to safe non-negative integers.
|
||||||
|
// Without this, negative offset causes slice() to count from the end,
|
||||||
|
// and NaN values cause slice() to return empty or unexpected results.
|
||||||
|
// Note: cannot use `|| default` because 0 is falsy — use isNaN instead.
|
||||||
|
const offsetNum = Number(rawOffset);
|
||||||
|
const offset = Number.isNaN(offsetNum) ? 0 : Math.max(0, Math.floor(offsetNum));
|
||||||
|
const limitNum = Number(rawLimit);
|
||||||
|
const limit = Number.isNaN(limitNum) ? 50 : Math.max(1, Math.floor(limitNum));
|
||||||
|
|
||||||
const sessionsDir = getSessionsDir();
|
const sessionsDir = getSessionsDir();
|
||||||
|
|
||||||
if (!fs.existsSync(sessionsDir)) {
|
if (!fs.existsSync(sessionsDir)) {
|
||||||
|
|||||||
@@ -561,9 +561,26 @@ function runTests() {
|
|||||||
assert.strictEqual(remaining.length, 0);
|
assert.strictEqual(remaining.length, 0);
|
||||||
})) passed++; else failed++;
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('cleanupAliases returns success:true when aliases removed', () => {
|
||||||
|
resetAliases();
|
||||||
|
aliases.setAlias('dead', '/sessions/dead');
|
||||||
|
const result = aliases.cleanupAliases(() => false);
|
||||||
|
assert.strictEqual(result.success, true);
|
||||||
|
assert.strictEqual(result.removed, 1);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('cleanupAliases returns success:true when no cleanup needed', () => {
|
||||||
|
resetAliases();
|
||||||
|
aliases.setAlias('alive', '/sessions/alive');
|
||||||
|
const result = aliases.cleanupAliases(() => true);
|
||||||
|
assert.strictEqual(result.success, true);
|
||||||
|
assert.strictEqual(result.removed, 0);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
if (test('cleanupAliases with empty aliases file does nothing', () => {
|
if (test('cleanupAliases with empty aliases file does nothing', () => {
|
||||||
resetAliases();
|
resetAliases();
|
||||||
const result = aliases.cleanupAliases(() => true);
|
const result = aliases.cleanupAliases(() => true);
|
||||||
|
assert.strictEqual(result.success, true);
|
||||||
assert.strictEqual(result.removed, 0);
|
assert.strictEqual(result.removed, 0);
|
||||||
assert.strictEqual(result.totalChecked, 0);
|
assert.strictEqual(result.totalChecked, 0);
|
||||||
assert.strictEqual(result.removedAliases.length, 0);
|
assert.strictEqual(result.removedAliases.length, 0);
|
||||||
|
|||||||
@@ -754,6 +754,67 @@ src/main.ts
|
|||||||
}
|
}
|
||||||
})) passed++; else failed++;
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
// getAllSessions pagination edge cases (offset/limit clamping)
|
||||||
|
console.log('\ngetAllSessions (pagination edge cases):');
|
||||||
|
|
||||||
|
if (test('getAllSessions clamps negative offset to 0', () => {
|
||||||
|
const result = sessionManager.getAllSessions({ offset: -5, limit: 2 });
|
||||||
|
// Negative offset should be clamped to 0, returning the first 2 sessions
|
||||||
|
assert.strictEqual(result.sessions.length, 2);
|
||||||
|
assert.strictEqual(result.offset, 0);
|
||||||
|
assert.strictEqual(result.total, 5);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('getAllSessions clamps NaN offset to 0', () => {
|
||||||
|
const result = sessionManager.getAllSessions({ offset: NaN, limit: 3 });
|
||||||
|
assert.strictEqual(result.sessions.length, 3);
|
||||||
|
assert.strictEqual(result.offset, 0);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('getAllSessions clamps NaN limit to default', () => {
|
||||||
|
const result = sessionManager.getAllSessions({ offset: 0, limit: NaN });
|
||||||
|
// NaN limit should be clamped to default (50), returning all 5 sessions
|
||||||
|
assert.ok(result.sessions.length > 0);
|
||||||
|
assert.strictEqual(result.total, 5);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('getAllSessions clamps negative limit to 1', () => {
|
||||||
|
const result = sessionManager.getAllSessions({ offset: 0, limit: -10 });
|
||||||
|
// Negative limit should be clamped to 1
|
||||||
|
assert.strictEqual(result.sessions.length, 1);
|
||||||
|
assert.strictEqual(result.limit, 1);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('getAllSessions clamps zero limit to 1', () => {
|
||||||
|
const result = sessionManager.getAllSessions({ offset: 0, limit: 0 });
|
||||||
|
assert.strictEqual(result.sessions.length, 1);
|
||||||
|
assert.strictEqual(result.limit, 1);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('getAllSessions handles string offset/limit gracefully', () => {
|
||||||
|
const result = sessionManager.getAllSessions({ offset: 'abc', limit: 'xyz' });
|
||||||
|
// String non-numeric should be treated as 0/default
|
||||||
|
assert.strictEqual(result.offset, 0);
|
||||||
|
assert.ok(result.sessions.length > 0);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('getAllSessions handles fractional offset (floors to integer)', () => {
|
||||||
|
const result = sessionManager.getAllSessions({ offset: 1.7, limit: 2 });
|
||||||
|
// 1.7 should floor to 1, skip first session, return next 2
|
||||||
|
assert.strictEqual(result.offset, 1);
|
||||||
|
assert.strictEqual(result.sessions.length, 2);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('getAllSessions handles Infinity offset', () => {
|
||||||
|
// Infinity should clamp to 0 since Number(Infinity) is Infinity but
|
||||||
|
// Math.floor(Infinity) is Infinity — however slice(Infinity) returns []
|
||||||
|
// Actually: Number(Infinity) || 0 = Infinity, Math.floor(Infinity) = Infinity
|
||||||
|
// Math.max(0, Infinity) = Infinity, so slice(Infinity) = []
|
||||||
|
const result = sessionManager.getAllSessions({ offset: Infinity, limit: 2 });
|
||||||
|
assert.strictEqual(result.sessions.length, 0);
|
||||||
|
assert.strictEqual(result.total, 5);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
// getSessionStats with code blocks and special characters
|
// getSessionStats with code blocks and special characters
|
||||||
console.log('\ngetSessionStats (code blocks & special chars):');
|
console.log('\ngetSessionStats (code blocks & special chars):');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user