mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-08-28 05:25:25 +00:00
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Skill copy sync / shipped skills drift guard (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
246 lines
8.1 KiB
TypeScript
246 lines
8.1 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import {
|
|
loadSettings,
|
|
saveSettings,
|
|
setActiveProvider,
|
|
getActiveProviderConfig,
|
|
isProviderConfigured,
|
|
clearSettings,
|
|
getProviderDisplayName,
|
|
getAvailableModels,
|
|
getProviderCapabilities,
|
|
} from '../../src/core/llm/settings-service';
|
|
import {
|
|
getMiniMaxModelCapabilities,
|
|
MINIMAX_ANTHROPIC_BASE_URLS,
|
|
MINIMAX_MODEL_IDS,
|
|
} from '../../src/core/llm/types';
|
|
import { createChatModel } from '../../src/core/llm/agent';
|
|
|
|
describe('loadSettings', () => {
|
|
it('returns defaults when nothing is stored', () => {
|
|
const settings = loadSettings();
|
|
expect(settings.activeProvider).toBeDefined();
|
|
expect(settings.openai).toBeDefined();
|
|
expect(settings.ollama).toBeDefined();
|
|
expect(settings.minimax).toMatchObject({
|
|
model: MINIMAX_MODEL_IDS[0],
|
|
baseUrl: MINIMAX_ANTHROPIC_BASE_URLS.global_en,
|
|
thinkingMode: 'adaptive',
|
|
});
|
|
});
|
|
|
|
it('merges stored values with defaults', () => {
|
|
sessionStorage.setItem(
|
|
'gitnexus-llm-settings',
|
|
JSON.stringify({
|
|
activeProvider: 'ollama',
|
|
ollama: { model: 'qwen3-coder:30b' },
|
|
}),
|
|
);
|
|
|
|
const settings = loadSettings();
|
|
expect(settings.activeProvider).toBe('ollama');
|
|
expect(settings.ollama.model).toBe('qwen3-coder:30b');
|
|
// Should still have other provider defaults
|
|
expect(settings.openai).toBeDefined();
|
|
});
|
|
|
|
it('migrates unsupported legacy MiniMax models to the current default', () => {
|
|
sessionStorage.setItem(
|
|
'gitnexus-llm-settings',
|
|
JSON.stringify({
|
|
activeProvider: 'minimax',
|
|
minimax: {
|
|
apiKey: 'minimax-test-key',
|
|
model: 'MiniMax-M2.5',
|
|
temperature: 0.1,
|
|
},
|
|
}),
|
|
);
|
|
|
|
const settings = loadSettings();
|
|
expect(settings.minimax).toMatchObject({
|
|
model: MINIMAX_MODEL_IDS[0],
|
|
thinkingMode: 'adaptive',
|
|
});
|
|
|
|
const model = createChatModel(getActiveProviderConfig()!) as any;
|
|
expect(model.model).toBe(MINIMAX_MODEL_IDS[0]);
|
|
expect(model.thinking).toEqual({ type: 'adaptive' });
|
|
});
|
|
|
|
it('returns defaults on corrupted JSON', () => {
|
|
sessionStorage.setItem('gitnexus-llm-settings', 'not-json{{{');
|
|
const settings = loadSettings();
|
|
expect(settings.activeProvider).toBeDefined();
|
|
});
|
|
|
|
it('migrates legacy localStorage to sessionStorage', () => {
|
|
localStorage.setItem(
|
|
'gitnexus-llm-settings',
|
|
JSON.stringify({
|
|
activeProvider: 'ollama',
|
|
ollama: { model: 'migrated-model' },
|
|
}),
|
|
);
|
|
|
|
const settings = loadSettings();
|
|
expect(settings.ollama.model).toBe('migrated-model');
|
|
expect(sessionStorage.getItem('gitnexus-llm-settings')).not.toBeNull();
|
|
expect(localStorage.getItem('gitnexus-llm-settings')).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('saveSettings / clearSettings', () => {
|
|
it('persists settings to sessionStorage', () => {
|
|
const settings = loadSettings();
|
|
settings.activeProvider = 'anthropic';
|
|
saveSettings(settings);
|
|
expect(loadSettings().activeProvider).toBe('anthropic');
|
|
});
|
|
|
|
it('clearSettings removes settings from both storages', () => {
|
|
saveSettings({ ...loadSettings(), activeProvider: 'anthropic' });
|
|
expect(sessionStorage.getItem('gitnexus-llm-settings')).not.toBeNull();
|
|
clearSettings();
|
|
expect(sessionStorage.getItem('gitnexus-llm-settings')).toBeNull();
|
|
expect(localStorage.getItem('gitnexus-llm-settings')).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('setActiveProvider', () => {
|
|
it('changes the active provider and persists', () => {
|
|
setActiveProvider('gemini');
|
|
expect(loadSettings().activeProvider).toBe('gemini');
|
|
});
|
|
});
|
|
|
|
describe('getActiveProviderConfig', () => {
|
|
it('returns null for unconfigured providers requiring API keys', () => {
|
|
setActiveProvider('openai');
|
|
expect(getActiveProviderConfig()).toBeNull();
|
|
});
|
|
|
|
it('returns config for ollama without API key', () => {
|
|
setActiveProvider('ollama');
|
|
const config = getActiveProviderConfig();
|
|
expect(config).not.toBeNull();
|
|
expect(config!.provider).toBe('ollama');
|
|
});
|
|
|
|
it('returns config for openai when API key is set', () => {
|
|
const settings = loadSettings();
|
|
settings.activeProvider = 'openai';
|
|
settings.openai = { ...settings.openai, apiKey: 'sk-test-123' };
|
|
saveSettings(settings);
|
|
|
|
const config = getActiveProviderConfig();
|
|
expect(config).not.toBeNull();
|
|
expect(config!.provider).toBe('openai');
|
|
});
|
|
|
|
it('returns config for deepseek when API key is set', () => {
|
|
const settings = loadSettings();
|
|
settings.activeProvider = 'deepseek';
|
|
settings.deepseek = { ...settings.deepseek, apiKey: 'sk-deepseek-123' };
|
|
saveSettings(settings);
|
|
|
|
const config = getActiveProviderConfig();
|
|
expect(config).not.toBeNull();
|
|
expect(config!.provider).toBe('deepseek');
|
|
});
|
|
|
|
it('returns the regional endpoint and thinking mode for MiniMax', () => {
|
|
const settings = loadSettings();
|
|
settings.activeProvider = 'minimax';
|
|
settings.minimax = {
|
|
...settings.minimax,
|
|
apiKey: 'minimax-test-key',
|
|
model: MINIMAX_MODEL_IDS[0],
|
|
baseUrl: MINIMAX_ANTHROPIC_BASE_URLS.cn_zh,
|
|
thinkingMode: 'disabled',
|
|
};
|
|
saveSettings(settings);
|
|
|
|
expect(getActiveProviderConfig()).toMatchObject({
|
|
provider: 'minimax',
|
|
model: MINIMAX_MODEL_IDS[0],
|
|
baseUrl: MINIMAX_ANTHROPIC_BASE_URLS.cn_zh,
|
|
thinkingMode: 'disabled',
|
|
});
|
|
});
|
|
|
|
it('returns null for openrouter with empty API key', () => {
|
|
const settings = loadSettings();
|
|
settings.activeProvider = 'openrouter';
|
|
settings.openrouter = { ...settings.openrouter, apiKey: ' ' };
|
|
saveSettings(settings);
|
|
|
|
expect(getActiveProviderConfig()).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('isProviderConfigured', () => {
|
|
it('returns false when provider requires API key and none is set', () => {
|
|
// Manually build a clean openai config with no API key
|
|
saveSettings({
|
|
...loadSettings(),
|
|
activeProvider: 'openai',
|
|
openai: { apiKey: '', model: 'gpt-4o', temperature: 0.1 },
|
|
});
|
|
expect(isProviderConfigured()).toBe(false);
|
|
});
|
|
|
|
it('returns true for ollama (no key required)', () => {
|
|
setActiveProvider('ollama');
|
|
expect(isProviderConfigured()).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('getProviderDisplayName', () => {
|
|
it('returns human-readable names', () => {
|
|
expect(getProviderDisplayName('openai')).toBe('OpenAI');
|
|
expect(getProviderDisplayName('azure-openai')).toBe('Azure OpenAI');
|
|
expect(getProviderDisplayName('gemini')).toBe('Google Gemini');
|
|
expect(getProviderDisplayName('anthropic')).toBe('Anthropic');
|
|
expect(getProviderDisplayName('ollama')).toBe('Ollama (Local)');
|
|
expect(getProviderDisplayName('openrouter')).toBe('OpenRouter');
|
|
expect(getProviderDisplayName('deepseek')).toBe('DeepSeek');
|
|
});
|
|
});
|
|
|
|
describe('getAvailableModels', () => {
|
|
it('returns models for known providers', () => {
|
|
expect(getAvailableModels('openai').length).toBeGreaterThan(0);
|
|
expect(getAvailableModels('ollama').length).toBeGreaterThan(0);
|
|
expect(getAvailableModels('anthropic')).toContain('claude-sonnet-4-20250514');
|
|
expect(getAvailableModels('deepseek')).toContain('deepseek-v4-flash');
|
|
expect(getAvailableModels('minimax')).toEqual([...MINIMAX_MODEL_IDS]);
|
|
});
|
|
|
|
it('describes MiniMax model input and thinking capabilities', () => {
|
|
expect(getMiniMaxModelCapabilities(MINIMAX_MODEL_IDS[0])).toEqual({
|
|
contextWindow: 1_000_000,
|
|
inputModalities: ['text', 'image', 'video'],
|
|
thinkingModes: ['adaptive', 'disabled'],
|
|
});
|
|
expect(getMiniMaxModelCapabilities(MINIMAX_MODEL_IDS[1])).toEqual({
|
|
contextWindow: 204_800,
|
|
inputModalities: ['text'],
|
|
thinkingModes: ['always_on'],
|
|
});
|
|
});
|
|
|
|
it('returns empty array for unknown provider', () => {
|
|
expect(getAvailableModels('unknown' as any)).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('getProviderCapabilities', () => {
|
|
it('enables transcript replay only for providers that require it', () => {
|
|
expect(getProviderCapabilities('deepseek').preserveAssistantTranscript).toBe(true);
|
|
expect(getProviderCapabilities('openai').preserveAssistantTranscript).toBe(false);
|
|
expect(getProviderCapabilities('anthropic').preserveAssistantTranscript).toBe(false);
|
|
});
|
|
});
|