lite_webui / tests /store.test.js
blyon1995's picture
init this repo
ca51841
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);
});
});