| import { describe, it, expect, beforeEach } from 'vitest'; |
| import { store } from '../src/store.js'; |
|
|
| describe('store – default settings', () => { |
| it('default apiKey is empty string', () => { |
| expect(store.getSettings().apiKey).toBe(''); |
| }); |
|
|
| it('default baseUrl points to localhost:8000', () => { |
| expect(store.getSettings().baseUrl).toBe('http://127.0.0.1:8000'); |
| }); |
|
|
| it('default theme is dark', () => { |
| expect(store.getSettings().theme).toBe('dark'); |
| }); |
|
|
| it('default contextLimitTokens is 4096', () => { |
| expect(store.getSettings().contextLimitTokens).toBe(4096); |
| }); |
|
|
| it('default contextResetThresholdPercent is 85', () => { |
| expect(store.getSettings().contextResetThresholdPercent).toBe(85); |
| }); |
| }); |
|
|
| describe('store – saveSettings', () => { |
| it('persists and retrieves settings', () => { |
| store.saveSettings({ apiKey: 'sk-abc', baseUrl: 'http://example.com', theme: 'dark' }); |
| const s = store.getSettings(); |
| expect(s.apiKey).toBe('sk-abc'); |
| expect(s.baseUrl).toBe('http://example.com'); |
| }); |
|
|
| it('missing keys fall back to defaults', () => { |
| store.saveSettings({ apiKey: 'sk-test' }); |
| expect(store.getSettings().baseUrl).toBe('http://127.0.0.1:8000'); |
| }); |
|
|
| it('invalid context settings are normalized back to safe defaults', () => { |
| store.saveSettings({ contextLimitTokens: null, contextResetThresholdPercent: 999 }); |
| const settings = store.getSettings(); |
| expect(settings.contextLimitTokens).toBe(4096); |
| expect(settings.contextResetThresholdPercent).toBe(95); |
| }); |
| }); |
|
|
| describe('store – conversations', () => { |
| it('starts with empty conversation list', () => { |
| expect(store.getConversations()).toEqual([]); |
| }); |
|
|
| it('createConversation uses provided model', () => { |
| const conv = store.createConversation('my-model'); |
| expect(conv.model).toBe('my-model'); |
| }); |
|
|
| it('createConversation defaults to empty string model (not hardcoded gpt-4o)', () => { |
| const conv = store.createConversation(); |
| expect(conv.model).toBe(''); |
| }); |
|
|
| it('createConversation prepends to list', () => { |
| store.createConversation('a'); |
| store.createConversation('b'); |
| const convs = store.getConversations(); |
| expect(convs[0].model).toBe('b'); |
| }); |
|
|
| it('deleteConversation removes correct conversation', () => { |
| const c1 = store.createConversation('m1'); |
| const c2 = store.createConversation('m2'); |
| store.deleteConversation(c1.id); |
| const ids = store.getConversations().map(c => c.id); |
| expect(ids).not.toContain(c1.id); |
| expect(ids).toContain(c2.id); |
| }); |
|
|
| it('addMessage appends to conversation', () => { |
| const conv = store.createConversation('m'); |
| store.addMessage(conv.id, { role: 'user', content: 'hello', timestamp: new Date().toISOString() }); |
| const updated = store.getCurrentConversation() ?? store.getConversations().find(c => c.id === conv.id); |
| expect(updated.messages).toHaveLength(1); |
| expect(updated.messages[0].content).toBe('hello'); |
| }); |
|
|
| it('updateConversationTitle updates correctly', () => { |
| const conv = store.createConversation('m'); |
| store.updateConversationTitle(conv.id, 'My Title'); |
| const updated = store.getConversations().find(c => c.id === conv.id); |
| expect(updated.title).toBe('My Title'); |
| }); |
| }); |
|
|
| describe('store – clearMessages', () => { |
| it('clears all messages from a conversation', () => { |
| const conv = store.createConversation('m'); |
| store.addMessage(conv.id, { role: 'user', content: 'hello', timestamp: 't1' }); |
| store.addMessage(conv.id, { role: 'assistant', content: 'hi', timestamp: 't2' }); |
| store.clearMessages(conv.id); |
| const updated = store.getConversations().find(c => c.id === conv.id); |
| expect(updated.messages).toHaveLength(0); |
| }); |
|
|
| it('preserves conversation metadata (title, model) after clearMessages', () => { |
| const conv = store.createConversation('my-model'); |
| store.updateConversationTitle(conv.id, 'My Chat'); |
| store.addMessage(conv.id, { role: 'user', content: 'msg', timestamp: 't1' }); |
| store.clearMessages(conv.id); |
| const updated = store.getConversations().find(c => c.id === conv.id); |
| expect(updated.title).toBe('My Chat'); |
| expect(updated.model).toBe('my-model'); |
| expect(updated.messages).toHaveLength(0); |
| }); |
|
|
| it('does nothing for non-existent conversation id', () => { |
| expect(() => store.clearMessages('no-such-id')).not.toThrow(); |
| }); |
| }); |
|
|
| describe('store – model capabilities', () => { |
| it('starts empty', () => { |
| expect(store.getModelCapabilities()).toEqual({}); |
| }); |
|
|
| it('saves and retrieves capabilities', () => { |
| store.saveModelCapabilities({ 'my-model': { text: true, image: true, audio: false } }); |
| expect(store.getModelCapabilities()['my-model'].image).toBe(true); |
| }); |
|
|
| it('scopes available models by baseUrl', () => { |
| store.saveAvailableModels('http://a.local', ['model-a', 'model-b']); |
| store.saveAvailableModels('http://b.local', ['model-c']); |
| expect(store.getAvailableModels('http://a.local')).toEqual(['model-a', 'model-b']); |
| expect(store.getAvailableModels('http://b.local')).toEqual(['model-c']); |
| }); |
|
|
| it('scopes current model selection by baseUrl', () => { |
| store.setCurrentModel('http://a.local', 'model-a'); |
| store.setCurrentModel('http://b.local', 'model-b'); |
| expect(store.getCurrentModel('http://a.local')).toBe('model-a'); |
| expect(store.getCurrentModel('http://b.local')).toBe('model-b'); |
| }); |
|
|
| it('scopes capability overrides by baseUrl', () => { |
| store.saveModelCapabilities('http://a.local', { 'model-a': { text: true, image: true, audio: false } }); |
| store.saveModelCapabilities('http://b.local', { 'model-a': { text: true, image: false, audio: false } }); |
| expect(store.getModelCapabilities('http://a.local')['model-a'].image).toBe(true); |
| expect(store.getModelCapabilities('http://b.local')['model-a'].image).toBe(false); |
| }); |
| }); |
|
|