File size: 5,967 Bytes
ca51841 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | 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' }); // no baseUrl
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'); // most recent first
});
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);
});
});
|