import { describe, it, expect, beforeAll, afterEach } from 'vitest'; import { SessionManager, BaseSession } from '../../src/utils/session-manager'; import { createTestUser, getTestDB } from '../setup'; // Use onboarding_sessions table for testing const manager = new SessionManager({ tableName: 'onboarding_sessions', ttlMs: 30 * 60 * 1000, // 30 minutes maxMessages: 5, }); describe('SessionManager', () => { describe('create()', () => { it('returns session with correct fields', () => { const session = manager.create('user123', 'greeting'); expect(session.user_id).toBe('user123'); expect(session.status).toBe('greeting'); expect(session.collected_info).toEqual({}); expect(session.messages).toEqual([]); expect(session.created_at).toBeTypeOf('number'); expect(session.updated_at).toBeTypeOf('number'); expect(session.expires_at).toBeGreaterThan(session.created_at); }); }); describe('save() and get() round-trip', () => { it('saves and retrieves session from DB', async () => { const db = getTestDB(); const session = manager.create('user456', 'gathering'); session.collected_info = { purpose: 'hosting' }; session.messages = [{ role: 'user', content: 'hello' }]; await manager.save(db, session); const retrieved = await manager.get(db, 'user456'); expect(retrieved).not.toBeNull(); expect(retrieved!.user_id).toBe('user456'); expect(retrieved!.status).toBe('gathering'); expect(retrieved!.collected_info).toEqual({ purpose: 'hosting' }); expect(retrieved!.messages).toEqual([{ role: 'user', content: 'hello' }]); }); }); describe('delete()', () => { it('removes session from DB', async () => { const db = getTestDB(); const session = manager.create('user789', 'greeting'); await manager.save(db, session); // Verify it exists const exists = await manager.has(db, 'user789'); expect(exists).toBe(true); // Delete await manager.delete(db, 'user789'); // Verify it's gone const afterDelete = await manager.get(db, 'user789'); expect(afterDelete).toBeNull(); }); }); describe('has()', () => { it('returns true for existing session', async () => { const db = getTestDB(); const session = manager.create('user_has_test', 'greeting'); await manager.save(db, session); expect(await manager.has(db, 'user_has_test')).toBe(true); }); it('returns false for missing session', async () => { const db = getTestDB(); expect(await manager.has(db, 'nonexistent_user')).toBe(false); }); }); describe('addMessage()', () => { it('adds message to session', () => { const session = manager.create('user_msg', 'greeting'); manager.addMessage(session, 'user', 'hello'); expect(session.messages).toHaveLength(1); expect(session.messages[0]).toEqual({ role: 'user', content: 'hello' }); }); it('trims old messages when over limit', () => { const session = manager.create('user_trim', 'greeting'); // maxMessages is 5, add 7 messages for (let i = 0; i < 7; i++) { manager.addMessage(session, 'user', `message ${i}`); } expect(session.messages).toHaveLength(5); // Should keep the last 5 messages (2..6) expect(session.messages[0].content).toBe('message 2'); expect(session.messages[4].content).toBe('message 6'); }); }); describe('expired sessions', () => { it('get() returns null for expired session', async () => { const db = getTestDB(); // Create a session that's already expired const now = Date.now(); const expiredSession: BaseSession = { user_id: 'expired_user', status: 'greeting', collected_info: {}, messages: [], created_at: now - 60000, updated_at: now - 60000, expires_at: now - 1000, // expired 1 second ago }; // Insert directly with past expires_at await db.prepare( `INSERT INTO onboarding_sessions (user_id, status, collected_info, messages, created_at, updated_at, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?)` ).bind( expiredSession.user_id, expiredSession.status, JSON.stringify(expiredSession.collected_info), JSON.stringify(expiredSession.messages), expiredSession.created_at, expiredSession.updated_at, expiredSession.expires_at ).run(); const result = await manager.get(db, 'expired_user'); expect(result).toBeNull(); }); }); });