import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { processSessionList } from '../src/session-list.js'; /** * Deterministic fixture builder for processSessionList. * Returns already-enriched sessions (is_favorite, favorited_at, custom_name set). */ function makeSession(overrides = {}) { return { id: overrides.id || 'sess-1', engine: overrides.engine || 'codex', created_at: overrides.created_at || 1000, status: overrides.status || 'success', summary: overrides.summary || 'Test session', model: overrides.model || 'gpt-4', cwd: overrides.cwd || '/home/test', source: overrides.source || 'chat', custom_name: overrides.custom_name || null, is_favorite: overrides.is_favorite || false, favorited_at: overrides.favorited_at || null, ...overrides, }; } describe('Session list pipeline (processSessionList)', () => { // ── Sorting ────────────────────────────────────────────────────── it('should place favorited sessions first, ordered by favorited_at desc', () => { const list = [ makeSession({ id: 'old-fav', is_favorite: true, favorited_at: 100, created_at: 500 }), makeSession({ id: 'recent-fav', is_favorite: true, favorited_at: 200, created_at: 400 }), makeSession({ id: 'normal', is_favorite: false, created_at: 900 }), ]; const result = processSessionList(list, { limit: 10 }); assert.equal(result.sessions[0].id, 'recent-fav'); assert.equal(result.sessions[1].id, 'old-fav'); assert.equal(result.sessions[2].id, 'normal'); }); it('should apply created_at desc tie-break for equal favorited_at values', () => { const list = [ makeSession({ id: 'older', is_favorite: true, favorited_at: 100, created_at: 1000 }), makeSession({ id: 'newer', is_favorite: true, favorited_at: 100, created_at: 2000 }), ]; const result = processSessionList(list, { limit: 10 }); // Equal favorited_at → created_at desc assert.equal(result.sessions[0].id, 'newer'); assert.equal(result.sessions[1].id, 'older'); }); it('should order non-favorite sessions by created_at desc', () => { const list = [ makeSession({ id: 'old', created_at: 100 }), makeSession({ id: 'mid', created_at: 200 }), makeSession({ id: 'new', created_at: 300 }), ]; const result = processSessionList(list, { limit: 10 }); assert.equal(result.sessions[0].id, 'new'); assert.equal(result.sessions[1].id, 'mid'); assert.equal(result.sessions[2].id, 'old'); }); it('should sort before pagination (sort covers all items, pagination slices)', () => { const list = []; // 5 favorites at timestamps favoring higher i → higher favorited_at for (let i = 0; i < 5; i++) { list.push(makeSession({ id: `fav-${i}`, is_favorite: true, favorited_at: 500 - i, created_at: 1000 + i })); } // 5 non-favorites for (let i = 0; i < 5; i++) { list.push(makeSession({ id: `normal-${i}`, created_at: 100 + i })); } // fav-0 has favorited_at=500 (highest), fav-4 has 496 (lowest among favs) const result = processSessionList(list, { limit: 3, offset: 0 }); assert.equal(result.total, 10); assert.equal(result.sessions.length, 3); assert.equal(result.sessions[0].id, 'fav-0'); assert.equal(result.sessions[1].id, 'fav-1'); assert.equal(result.sessions[2].id, 'fav-2'); }); // ── Filtering ──────────────────────────────────────────────────── it('should filter by favorite-only when opts.favorite="1"', () => { const list = [ makeSession({ id: 'fav', is_favorite: true, favorited_at: 100 }), makeSession({ id: 'not-fav', is_favorite: false }), ]; const result = processSessionList(list, { favorite: '1', limit: 10 }); assert.equal(result.sessions.length, 1); assert.equal(result.sessions[0].id, 'fav'); }); it('should combine favorite-only with status filter', () => { const list = [ makeSession({ id: 'fav-ok', is_favorite: true, favorited_at: 200, status: 'success' }), makeSession({ id: 'fav-err', is_favorite: true, favorited_at: 100, status: 'error' }), makeSession({ id: 'normal-ok', status: 'success' }), ]; // Only favorited AND status=error const result = processSessionList(list, { favorite: '1', status: 'failed', limit: 10 }); assert.equal(result.sessions.length, 1); assert.equal(result.sessions[0].id, 'fav-err'); }); it('should be deterministic when favorited_at and created_at both equal', () => { const list = [ makeSession({ id: 'a', is_favorite: true, favorited_at: 100, created_at: 1000 }), makeSession({ id: 'b', is_favorite: true, favorited_at: 100, created_at: 1000 }), ]; // When both are equal, sort is stable (preserves original order) const result = processSessionList(list, { limit: 10 }); assert.equal(result.sessions[0].id, 'a'); assert.equal(result.sessions[1].id, 'b'); }); it('should combine favorite-only with query and status filter (composition test)', () => { const list = [ // Favorited, matches query, has error status makeSession({ id: 'match-all', is_favorite: true, favorited_at: 300, status: 'error', summary: 'OAuth fix', custom_name: null }), // Favorited, does NOT match query makeSession({ id: 'fav-no-query', is_favorite: true, favorited_at: 200, status: 'error', summary: 'Other thing' }), // Favorited, matches query but wrong status makeSession({ id: 'fav-ok-status', is_favorite: true, favorited_at: 100, status: 'success', summary: 'OAuth callback' }), // Not favorited, matches query and status makeSession({ id: 'not-fav', is_favorite: false, status: 'error', summary: 'OAuth failure' }), ]; // Only favorited + query matching "OAuth" + status failed const result = processSessionList(list, { favorite: '1', query: 'OAuth', status: 'failed', limit: 10 }); assert.equal(result.sessions.length, 1); assert.equal(result.sessions[0].id, 'match-all'); }); // ── Edge cases ─────────────────────────────────────────────────── it('should return empty array for empty input', () => { const result = processSessionList([], { limit: 10 }); assert.deepEqual(result, { sessions: [], total: 0 }); }); it('should respect limit and offset', () => { const list = []; for (let i = 0; i < 20; i++) { list.push(makeSession({ id: `s-${i}`, created_at: 2000 - i })); } // s-0 has created_at=2000 (highest → first after sort desc) const page1 = processSessionList(list, { limit: 5, offset: 0 }); assert.equal(page1.sessions.length, 5); assert.equal(page1.sessions[0].id, 's-0'); assert.equal(page1.total, 20); const page2 = processSessionList(list, { limit: 5, offset: 5 }); assert.equal(page2.sessions.length, 5); assert.equal(page2.sessions[0].id, 's-5'); }); it('should cap limit at 200', () => { const list = Array.from({ length: 300 }, (_, i) => makeSession({ id: `s-${i}`, created_at: 300 - i }) ); const result = processSessionList(list, { limit: 999 }); assert.equal(result.sessions.length, 200); assert.equal(result.total, 300); }); });