File size: 4,727 Bytes
a0ebf39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { describe, it, expect } from 'vitest';
import {
  isProviderUsable,
  validateProvider,
  validateModel,
  type ProviderCfgLike,
} from '@/lib/store/settings-validation';

describe('isProviderUsable', () => {
  it('returns true when provider has client API key', () => {
    expect(isProviderUsable({ apiKey: 'sk-xxx' })).toBe(true);
  });

  it('returns true when provider is server-configured', () => {
    expect(isProviderUsable({ isServerConfigured: true })).toBe(true);
  });

  it('returns true when provider has both client key and server config', () => {
    expect(isProviderUsable({ apiKey: 'sk-xxx', isServerConfigured: true })).toBe(true);
  });

  it('returns false when has neither client key nor server config', () => {
    expect(isProviderUsable({ apiKey: '', isServerConfigured: false })).toBe(false);
  });

  it('returns false when apiKey is empty and not server-configured', () => {
    expect(isProviderUsable({ apiKey: '' })).toBe(false);
  });

  it('returns false for undefined config', () => {
    expect(isProviderUsable(undefined)).toBe(false);
  });

  it('returns false for empty object', () => {
    expect(isProviderUsable({})).toBe(false);
  });

  it('returns true for keyless provider with explicit baseUrl', () => {
    expect(isProviderUsable({ requiresApiKey: false, baseUrl: 'http://localhost:11434/v1' })).toBe(
      true,
    );
  });

  it('returns false for keyless provider without baseUrl', () => {
    expect(isProviderUsable({ requiresApiKey: false })).toBe(false);
  });

  it('returns false for keyless provider with empty baseUrl', () => {
    expect(isProviderUsable({ requiresApiKey: false, baseUrl: '' })).toBe(false);
  });

  it('returns true for keyless provider when server-configured', () => {
    expect(isProviderUsable({ requiresApiKey: false, isServerConfigured: true })).toBe(true);
  });

  it('returns false for keyless provider with apiKey but no baseUrl', () => {
    expect(isProviderUsable({ requiresApiKey: false, apiKey: 'some-key' })).toBe(false);
  });
});

describe('validateProvider', () => {
  const cfg = (overrides: Partial<ProviderCfgLike> = {}): ProviderCfgLike => ({
    apiKey: '',
    isServerConfigured: false,
    ...overrides,
  });

  it('keeps current provider when it is server-configured', () => {
    const configMap = {
      'provider-a': cfg({ isServerConfigured: true }),
      'provider-b': cfg(),
    };
    expect(validateProvider('provider-a', configMap, ['provider-b'])).toBe('provider-a');
  });

  it('keeps current provider when it has client API key', () => {
    const configMap = {
      'provider-a': cfg({ apiKey: 'sk-xxx' }),
      'provider-b': cfg(),
    };
    expect(validateProvider('provider-a', configMap, ['provider-b'])).toBe('provider-a');
  });

  it('falls back to first usable provider when current is unusable', () => {
    const configMap = {
      'provider-a': cfg(),
      'provider-b': cfg({ isServerConfigured: true }),
    };
    expect(validateProvider('provider-a', configMap, ['provider-b'])).toBe('provider-b');
  });

  it('returns empty string when no fallback is usable and no default', () => {
    const configMap = {
      'provider-a': cfg(),
      'provider-b': cfg(),
    };
    expect(validateProvider('provider-a', configMap, ['provider-b'])).toBe('');
  });

  it('falls back to defaultId when no fallback is usable', () => {
    const configMap = {
      'provider-a': cfg(),
      'provider-b': cfg(),
    };
    expect(validateProvider('provider-a', configMap, ['provider-b'], 'browser-native')).toBe(
      'browser-native',
    );
  });

  it('prefers usable fallback over defaultId', () => {
    const configMap = {
      'provider-a': cfg(),
      'provider-b': cfg({ isServerConfigured: true }),
    };
    expect(validateProvider('provider-a', configMap, ['provider-b'], 'browser-native')).toBe(
      'provider-b',
    );
  });

  it('returns current id unchanged when it is empty', () => {
    const configMap = { 'provider-a': cfg({ isServerConfigured: true }) };
    expect(validateProvider('', configMap, ['provider-a'])).toBe('');
  });
});

describe('validateModel', () => {
  it('keeps model when still in available list', () => {
    expect(validateModel('gpt-4o', [{ id: 'gpt-4o' }, { id: 'gpt-4o-mini' }])).toBe('gpt-4o');
  });

  it('falls back to first model when current is not in list', () => {
    expect(validateModel('gpt-4-turbo', [{ id: 'gpt-4o' }, { id: 'gpt-4o-mini' }])).toBe('gpt-4o');
  });

  it('returns empty string when list is empty', () => {
    expect(validateModel('gpt-4o', [])).toBe('');
  });

  it('returns current id unchanged when it is empty', () => {
    expect(validateModel('', [{ id: 'gpt-4o' }])).toBe('');
  });
});