/** * ChatCore unit tests * * Tests scaffold — full implementation in Task 16. * Covers: new conversation, save/load/delete, provider switching, * localStorage bounds (MAX_CONVERSATIONS), and streaming abort. */ import { describe, it, expect, beforeEach, vi } from 'vitest'; import { ChatCore } from '../core/ChatCore.js'; import type { ChatConfig } from '../types/index.js'; // ---- localStorage mock ---- const storage: Record = {}; beforeEach(() => { Object.keys(storage).forEach(k => delete storage[k]); vi.stubGlobal('localStorage', { getItem: (k: string) => storage[k] ?? null, setItem: (k: string, v: string) => { storage[k] = v; }, removeItem: (k: string) => { delete storage[k]; }, clear: () => { Object.keys(storage).forEach(k => delete storage[k]); }, }); }); const config: ChatConfig = { gateway: 'https://api.example.com/v1', apiKey: 'sk_test', provider: 'xai', }; describe('ChatCore — initialisation', () => { it('creates a new conversation ID on construction', () => { const core = new ChatCore(config); expect(core.conversationId).toBeTruthy(); expect(typeof core.conversationId).toBe('string'); }); it('defaults to xai provider', () => { const core = new ChatCore(config); expect(core.provider).toBe('xai'); }); it('accepts provider override', () => { const core = new ChatCore({ ...config, provider: 'anthropic' }); expect(core.provider).toBe('anthropic'); }); }); describe('ChatCore — provider switching', () => { it('switches provider and updates model', () => { const core = new ChatCore(config); core.setProvider('openai'); expect(core.provider).toBe('openai'); expect(core.model).toBeTruthy(); }); it('ignores unknown providers with a warning', () => { const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}); const core = new ChatCore(config); const before = core.provider; core.setProvider('unknown-provider'); expect(core.provider).toBe(before); // unchanged warn.mockRestore(); }); }); describe('ChatCore — conversation persistence', () => { it('returns empty array when no conversations saved', () => { const core = new ChatCore(config); expect(core.getConversations()).toEqual([]); }); it('clearHistory starts fresh conversation with new ID', () => { const core = new ChatCore(config); const oldId = core.conversationId; core.clearHistory(); expect(core.conversationId).not.toBe(oldId); expect(core.messages.length).toBe(0); }); it('deleteConversation on active conversation starts new one', () => { const core = new ChatCore(config); const id = core.conversationId!; core.deleteConversation(id); expect(core.conversationId).not.toBe(id); }); }); describe('ChatCore — streaming', () => { it('isStreaming starts as false', () => { const core = new ChatCore(config); expect(core.isStreaming).toBe(false); }); it('stop() is a no-op when not streaming', () => { const core = new ChatCore(config); expect(() => core.stop()).not.toThrow(); }); });