Files
koboldcpp/tools/ui/tests/client/ui-settings-sync.svelte.test.ts
T
Pascal 77acca437f ui: read persisted settings before the API key probe (#27365)
The route loads run ahead of the root layout script, so validateApiKey
read the settings store while it still held factory defaults and probed
/props without the stored key. initStores() now hands the same startup
promise to every caller and the chat loads await it before probing.

The one-time admin baseline no longer overwrites a key the user has
already set: on a first visit the config carries factory values only, so
a diverging key comes from the user and wins.
2026-08-19 14:02:08 +02:00

88 lines
3.1 KiB
TypeScript

import { CONFIG_LOCALSTORAGE_KEY } from '$lib/constants';
import { serverStore } from '$lib/stores/server.svelte';
import { settingsStore } from '$lib/stores/settings.svelte';
import { beforeEach, describe, expect, it } from 'vitest';
function mockProps(uiSettings: Record<string, string | number | boolean>) {
Object.defineProperty(serverStore, 'props', {
configurable: true,
get: () =>
({
default_generation_settings: { params: { temperature: 0.8 } },
ui_settings: uiSettings
}) as unknown as typeof serverStore.props
});
}
describe('server ui_settings application semantics', () => {
beforeEach(() => {
localStorage.removeItem(CONFIG_LOCALSTORAGE_KEY);
});
it('applies the admin defaults once for a new user', () => {
settingsStore.initialize();
mockProps({ apiKey: '', theme: 'dark' });
settingsStore.syncWithServerDefaults();
expect(settingsStore.config.theme).toBe('dark');
});
it('never reapplies on later loads: the user config diverges freely', () => {
settingsStore.initialize();
settingsStore.updateConfig('theme', 'light');
settingsStore.updateConfig('apiKey', 'sk-user-key');
// simulated F5: config now exists in localStorage
settingsStore.initialize();
mockProps({ apiKey: '', theme: 'dark' });
settingsStore.syncWithServerDefaults();
settingsStore.syncWithServerDefaults();
expect(settingsStore.config.theme).toBe('light');
expect(settingsStore.config.apiKey).toBe('sk-user-key');
const stored = JSON.parse(localStorage.getItem(CONFIG_LOCALSTORAGE_KEY) ?? '{}');
expect(stored.apiKey).toBe('sk-user-key');
});
it('keeps a value the user sets before the baseline is reachable', () => {
settingsStore.initialize();
// the splash is the only way in when the server runs with --api-key,
// so the first user write lands before the first successful /props
settingsStore.updateConfig('apiKey', 'sk-user-key');
mockProps({ apiKey: 'admin-placeholder', theme: 'dark' });
settingsStore.syncWithServerDefaults();
expect(settingsStore.config.apiKey).toBe('sk-user-key');
expect(settingsStore.config.theme).toBe('dark');
});
it('Reset to Default reapplies the full baseline, api key included', () => {
settingsStore.initialize();
settingsStore.updateConfig('theme', 'light');
settingsStore.updateConfig('apiKey', 'sk-user-key');
mockProps({ apiKey: '', theme: 'dark' });
settingsStore.forceSyncWithServerDefaults();
expect(settingsStore.config.theme).toBe('dark');
expect(settingsStore.config.apiKey).toBe('');
});
});
describe('syncable scope (spec section 4)', () => {
it('sampling params keep their live server twin, ui settings carry none', async () => {
const { ParameterSyncService } = await import('$lib/services/parameter-sync.service');
expect(ParameterSyncService.canSyncParameter('temperature')).toBe(true);
expect(ParameterSyncService.canSyncParameter('samplers')).toBe(true);
expect(ParameterSyncService.canSyncParameter('theme')).toBe(false);
expect(ParameterSyncService.canSyncParameter('systemMessage')).toBe(false);
expect(ParameterSyncService.canSyncParameter('apiKey')).toBe(false);
expect(ParameterSyncService.canSyncParameter('customCss')).toBe(false);
});
});