const { loadConfig } = require('../src/config'); const { resolveMode } = require('../src/resolveMode'); const { MemorySessionStore, MysqlSessionStore } = require('../src/sessions'); const { BookCache } = require('../src/bookCache'); const { estimateCost } = require('../src/cost'); const { memoryStorage } = require('./fixture'); describe('resolveMode', () => { const base = { tokenCount: 1000, threshold: 60000, premiumPlans: ['premium'], plan: '', style: 'default' }; it('follows the auto policy', () => { expect(resolveMode({ ...base, override: 'full' })).toEqual({ mode: 'FULL_CONTEXT', reason: 'override' }); expect(resolveMode({ ...base, override: 'hybrid', tokenCount: 10 })).toEqual({ mode: 'HYBRID_AGENTIC', reason: 'override' }); expect(resolveMode({ ...base, tokenCount: 60001 })).toEqual({ mode: 'HYBRID_AGENTIC', reason: 'over_token_threshold' }); expect(resolveMode({ ...base, tokenCount: 60000, plan: 'premium' })).toEqual({ mode: 'FULL_CONTEXT', reason: 'premium_plan' }); expect(resolveMode({ ...base, style: 'study' })).toEqual({ mode: 'FULL_CONTEXT', reason: 'study_style' }); expect(resolveMode(base)).toEqual({ mode: 'HYBRID_AGENTIC', reason: 'default_hybrid' }); }); }); describe('chat config', () => { it('defaults cache settings for a local run', () => { const config = loadConfig({}, { local: true }); expect(config.cacheTtl).toBe('5m'); expect(config.cacheMinTokens).toBe(4096); expect(config.autoFullContextMaxTokens).toBe(60000); expect(config.agentMaxIterations).toBe(4); expect(config.priceTable).toEqual({}); }); }); describe('sessions and book cache', () => { it('stores a session in memory', async () => { const store = new MemorySessionStore(); const created = await store.create({ bookId: 3, mode: 'HYBRID_AGENTIC', channel: 'chat' }); created.history = [{ role: 'user', text: 'Hi' }]; await store.save(created); const loaded = await store.get(created.sessionId); expect(loaded.history).toHaveLength(1); expect(loaded.mode).toBe('HYBRID_AGENTIC'); }); it('maps a mysql session row', async () => { const pool = { async query(sql) { if (String(sql).includes('SELECT')) { return [[{ session_id: 's1', book_id: '3', user_id: 4, mode: 'FULL_CONTEXT', channel: 'voice', style: 'study', plan: 'premium', history: [{ role: 'user', text: 'Hello' }], rolling_summary: '', last_cache_touch_at: null, cumulative_usage: { inputTokens: 1, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, toolCalls: 0, llmCalls: 0, estimatedCostUsd: 0 }, keepalive_count: 0, created_at: new Date('2026-01-01T00:00:00Z'), }]]; } return [{ affectedRows: 1 }]; }, }; const store = new MysqlSessionStore(pool); await store.ensure(); const session = await store.get('s1'); expect(session.bookId).toBe('3'); expect(session.history[0].text).toBe('Hello'); }); it('loads each book once', async () => { const storage = memoryStorage('3'); const cache = new BookCache(storage, 2); const first = await cache.load('3', 'v1'); const reads = storage.reads(); const second = await cache.load('3', 'v1'); expect(second).toBe(first); expect(storage.reads()).toBe(reads); }); }); describe('cost', () => { it('prices tokens from the table', () => { const cost = estimateCost('m', { inputTokens: 1000, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0 }, { m: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 }, }); expect(cost).toBe(3); }); });