librechat-data-provider 0.7.83 → 0.7.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.es.js +1 -1
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react-query/index.es.js +1 -1
- package/dist/react-query/index.es.js.map +1 -1
- package/package.json +3 -1
- package/specs/bedrock.spec.ts +114 -0
- package/specs/web.spec.ts +903 -0
- package/src/api-endpoints.ts +2 -0
- package/src/bedrock.ts +4 -1
- package/src/config.ts +76 -4
- package/src/data-service.ts +8 -0
- package/src/file-config.ts +6 -0
- package/src/generate.ts +3 -3
- package/src/index.ts +4 -0
- package/src/keys.ts +1 -0
- package/src/mcp.ts +13 -6
- package/src/parameterSettings.ts +726 -0
- package/src/permissions.ts +10 -0
- package/src/roles.ts +8 -0
- package/src/schemas.ts +11 -2
- package/src/types/assistants.ts +2 -0
- package/src/types/files.ts +1 -0
- package/src/types/mutations.ts +21 -1
- package/src/types/queries.ts +5 -1
- package/src/types/web.ts +593 -0
- package/src/types.ts +8 -2
- package/src/utils.ts +10 -0
- package/src/web.ts +271 -0
|
@@ -0,0 +1,903 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ScraperTypes,
|
|
3
|
+
TCustomConfig,
|
|
4
|
+
RerankerTypes,
|
|
5
|
+
SearchProviders,
|
|
6
|
+
TWebSearchConfig,
|
|
7
|
+
} from '../src/config';
|
|
8
|
+
import { webSearchAuth, loadWebSearchAuth, extractWebSearchEnvVars } from '../src/web';
|
|
9
|
+
import { SafeSearchTypes } from '../src/config';
|
|
10
|
+
import { AuthType } from '../src/schemas';
|
|
11
|
+
|
|
12
|
+
// Mock the extractVariableName function
|
|
13
|
+
jest.mock('../src/utils', () => ({
|
|
14
|
+
extractVariableName: (value: string) => {
|
|
15
|
+
if (!value || typeof value !== 'string') return null;
|
|
16
|
+
const match = value.match(/^\${(.+)}$/);
|
|
17
|
+
return match ? match[1] : null;
|
|
18
|
+
},
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
describe('web.ts', () => {
|
|
22
|
+
describe('extractWebSearchEnvVars', () => {
|
|
23
|
+
it('should return empty array if config is undefined', () => {
|
|
24
|
+
const result = extractWebSearchEnvVars({
|
|
25
|
+
keys: ['serperApiKey', 'jinaApiKey'],
|
|
26
|
+
config: undefined,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
expect(result).toEqual([]);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should extract environment variable names from config values', () => {
|
|
33
|
+
const config: Partial<TWebSearchConfig> = {
|
|
34
|
+
serperApiKey: '${SERPER_API_KEY}',
|
|
35
|
+
jinaApiKey: '${JINA_API_KEY}',
|
|
36
|
+
cohereApiKey: 'actual-api-key', // Not in env var format
|
|
37
|
+
safeSearch: SafeSearchTypes.MODERATE,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const result = extractWebSearchEnvVars({
|
|
41
|
+
keys: ['serperApiKey', 'jinaApiKey', 'cohereApiKey'],
|
|
42
|
+
config: config as TWebSearchConfig,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
expect(result).toEqual(['SERPER_API_KEY', 'JINA_API_KEY']);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should only extract variables for keys that exist in the config', () => {
|
|
49
|
+
const config: Partial<TWebSearchConfig> = {
|
|
50
|
+
serperApiKey: '${SERPER_API_KEY}',
|
|
51
|
+
// firecrawlApiKey is missing
|
|
52
|
+
safeSearch: SafeSearchTypes.MODERATE,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const result = extractWebSearchEnvVars({
|
|
56
|
+
keys: ['serperApiKey', 'firecrawlApiKey'],
|
|
57
|
+
config: config as TWebSearchConfig,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(result).toEqual(['SERPER_API_KEY']);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('loadWebSearchAuth', () => {
|
|
65
|
+
// Common test variables
|
|
66
|
+
const userId = 'test-user-id';
|
|
67
|
+
let mockLoadAuthValues: jest.Mock;
|
|
68
|
+
let webSearchConfig: TCustomConfig['webSearch'];
|
|
69
|
+
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
// Reset mocks before each test
|
|
72
|
+
jest.clearAllMocks();
|
|
73
|
+
|
|
74
|
+
// Initialize the mock function
|
|
75
|
+
mockLoadAuthValues = jest.fn();
|
|
76
|
+
|
|
77
|
+
// Initialize a basic webSearchConfig
|
|
78
|
+
webSearchConfig = {
|
|
79
|
+
serperApiKey: '${SERPER_API_KEY}',
|
|
80
|
+
firecrawlApiKey: '${FIRECRAWL_API_KEY}',
|
|
81
|
+
firecrawlApiUrl: '${FIRECRAWL_API_URL}',
|
|
82
|
+
jinaApiKey: '${JINA_API_KEY}',
|
|
83
|
+
cohereApiKey: '${COHERE_API_KEY}',
|
|
84
|
+
safeSearch: SafeSearchTypes.MODERATE,
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should return authenticated=true when all required categories are authenticated', async () => {
|
|
89
|
+
// Mock successful authentication for all services
|
|
90
|
+
mockLoadAuthValues.mockImplementation(({ authFields }) => {
|
|
91
|
+
const result: Record<string, string> = {};
|
|
92
|
+
authFields.forEach((field) => {
|
|
93
|
+
result[field] =
|
|
94
|
+
field === 'FIRECRAWL_API_URL' ? 'https://api.firecrawl.dev' : 'test-api-key';
|
|
95
|
+
});
|
|
96
|
+
return Promise.resolve(result);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const result = await loadWebSearchAuth({
|
|
100
|
+
userId,
|
|
101
|
+
webSearchConfig,
|
|
102
|
+
loadAuthValues: mockLoadAuthValues,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
expect(result.authenticated).toBe(true);
|
|
106
|
+
expect(result.authTypes).toHaveLength(3); // providers, scrapers, rerankers
|
|
107
|
+
expect(result.authResult).toHaveProperty('serperApiKey', 'test-api-key');
|
|
108
|
+
expect(result.authResult).toHaveProperty('firecrawlApiKey', 'test-api-key');
|
|
109
|
+
|
|
110
|
+
// The implementation only includes one reranker in the result
|
|
111
|
+
// It will be either jina or cohere, but not both
|
|
112
|
+
if (result.authResult.rerankerType === 'jina') {
|
|
113
|
+
expect(result.authResult).toHaveProperty('jinaApiKey', 'test-api-key');
|
|
114
|
+
} else {
|
|
115
|
+
expect(result.authResult).toHaveProperty('cohereApiKey', 'test-api-key');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
expect(result.authResult).toHaveProperty('searchProvider', 'serper');
|
|
119
|
+
expect(result.authResult).toHaveProperty('scraperType', 'firecrawl');
|
|
120
|
+
expect(['jina', 'cohere']).toContain(result.authResult.rerankerType as string);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should return authenticated=false when a required category is not authenticated', async () => {
|
|
124
|
+
// Mock authentication failure for the providers category
|
|
125
|
+
mockLoadAuthValues.mockImplementation(({ authFields }) => {
|
|
126
|
+
const result: Record<string, string> = {};
|
|
127
|
+
authFields.forEach((field) => {
|
|
128
|
+
// Only provide values for scrapers and rerankers, not for providers
|
|
129
|
+
if (field !== 'SERPER_API_KEY') {
|
|
130
|
+
result[field] =
|
|
131
|
+
field === 'FIRECRAWL_API_URL' ? 'https://api.firecrawl.dev' : 'test-api-key';
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
return Promise.resolve(result);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const result = await loadWebSearchAuth({
|
|
138
|
+
userId,
|
|
139
|
+
webSearchConfig,
|
|
140
|
+
loadAuthValues: mockLoadAuthValues,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
expect(result.authenticated).toBe(false);
|
|
144
|
+
// We should still have authTypes for the categories we checked
|
|
145
|
+
expect(result.authTypes.some(([category]) => category === 'providers')).toBe(true);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should handle exceptions from loadAuthValues', async () => {
|
|
149
|
+
// Mock loadAuthValues to throw an error
|
|
150
|
+
mockLoadAuthValues.mockImplementation(() => {
|
|
151
|
+
throw new Error('Authentication failed');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const result = await loadWebSearchAuth({
|
|
155
|
+
userId,
|
|
156
|
+
webSearchConfig,
|
|
157
|
+
loadAuthValues: mockLoadAuthValues,
|
|
158
|
+
throwError: false, // Don't throw errors
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
expect(result.authenticated).toBe(false);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should correctly identify user-provided vs system-defined auth', async () => {
|
|
165
|
+
// Mock environment variables
|
|
166
|
+
const originalEnv = process.env;
|
|
167
|
+
process.env = {
|
|
168
|
+
...originalEnv,
|
|
169
|
+
SERPER_API_KEY: 'system-api-key',
|
|
170
|
+
FIRECRAWL_API_KEY: 'system-api-key',
|
|
171
|
+
JINA_API_KEY: 'system-api-key',
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// Mock loadAuthValues to return different values for some keys
|
|
175
|
+
mockLoadAuthValues.mockImplementation(({ authFields }) => {
|
|
176
|
+
const result: Record<string, string> = {};
|
|
177
|
+
authFields.forEach((field) => {
|
|
178
|
+
if (field === 'SERPER_API_KEY') {
|
|
179
|
+
// This matches the system env var
|
|
180
|
+
result[field] = 'system-api-key';
|
|
181
|
+
} else if (field === 'FIRECRAWL_API_KEY') {
|
|
182
|
+
// This is different from the system env var (user provided)
|
|
183
|
+
result[field] = 'user-api-key';
|
|
184
|
+
} else if (field === 'FIRECRAWL_API_URL') {
|
|
185
|
+
result[field] = 'https://api.firecrawl.dev';
|
|
186
|
+
} else if (field === 'JINA_API_KEY') {
|
|
187
|
+
// This matches the system env var
|
|
188
|
+
result[field] = 'system-api-key';
|
|
189
|
+
} else {
|
|
190
|
+
result[field] = 'test-api-key';
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
return Promise.resolve(result);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const result = await loadWebSearchAuth({
|
|
197
|
+
userId,
|
|
198
|
+
webSearchConfig,
|
|
199
|
+
loadAuthValues: mockLoadAuthValues,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
expect(result.authenticated).toBe(true);
|
|
203
|
+
// Check for providers (system-defined) and scrapers (user-provided)
|
|
204
|
+
const providersAuthType = result.authTypes.find(
|
|
205
|
+
([category]) => category === 'providers',
|
|
206
|
+
)?.[1];
|
|
207
|
+
const scrapersAuthType = result.authTypes.find(([category]) => category === 'scrapers')?.[1];
|
|
208
|
+
|
|
209
|
+
expect(providersAuthType).toBe(AuthType.SYSTEM_DEFINED);
|
|
210
|
+
expect(scrapersAuthType).toBe(AuthType.USER_PROVIDED);
|
|
211
|
+
|
|
212
|
+
// Restore original env
|
|
213
|
+
process.env = originalEnv;
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should handle optional fields correctly', async () => {
|
|
217
|
+
// Create a config without the optional firecrawlApiUrl
|
|
218
|
+
const configWithoutOptional = { ...webSearchConfig } as Partial<TWebSearchConfig>;
|
|
219
|
+
delete configWithoutOptional.firecrawlApiUrl;
|
|
220
|
+
|
|
221
|
+
mockLoadAuthValues.mockImplementation(({ authFields, optional }) => {
|
|
222
|
+
const result: Record<string, string> = {};
|
|
223
|
+
authFields.forEach((field) => {
|
|
224
|
+
// Don't provide values for optional fields
|
|
225
|
+
if (!optional?.has(field)) {
|
|
226
|
+
result[field] = 'test-api-key';
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
return Promise.resolve(result);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const result = await loadWebSearchAuth({
|
|
233
|
+
userId,
|
|
234
|
+
webSearchConfig: configWithoutOptional as TWebSearchConfig,
|
|
235
|
+
loadAuthValues: mockLoadAuthValues,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
expect(result.authenticated).toBe(true);
|
|
239
|
+
expect(result.authResult).toHaveProperty('firecrawlApiKey', 'test-api-key');
|
|
240
|
+
// Optional URL should not be in the result
|
|
241
|
+
expect(result.authResult.firecrawlApiUrl).toBeUndefined();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('should preserve safeSearch setting from webSearchConfig', async () => {
|
|
245
|
+
// Mock successful authentication
|
|
246
|
+
mockLoadAuthValues.mockImplementation(({ authFields }) => {
|
|
247
|
+
const result: Record<string, string> = {};
|
|
248
|
+
authFields.forEach((field) => {
|
|
249
|
+
result[field] = 'test-api-key';
|
|
250
|
+
});
|
|
251
|
+
return Promise.resolve(result);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Test with safeSearch: OFF
|
|
255
|
+
const configWithSafeSearchOff = {
|
|
256
|
+
...webSearchConfig,
|
|
257
|
+
safeSearch: SafeSearchTypes.OFF,
|
|
258
|
+
} as TWebSearchConfig;
|
|
259
|
+
|
|
260
|
+
const result = await loadWebSearchAuth({
|
|
261
|
+
userId,
|
|
262
|
+
webSearchConfig: configWithSafeSearchOff,
|
|
263
|
+
loadAuthValues: mockLoadAuthValues,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
expect(result.authResult).toHaveProperty('safeSearch', SafeSearchTypes.OFF);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('should set the correct service types in authResult', async () => {
|
|
270
|
+
// Mock successful authentication
|
|
271
|
+
mockLoadAuthValues.mockImplementation(({ authFields }) => {
|
|
272
|
+
const result: Record<string, string> = {};
|
|
273
|
+
authFields.forEach((field) => {
|
|
274
|
+
result[field] =
|
|
275
|
+
field === 'FIRECRAWL_API_URL' ? 'https://api.firecrawl.dev' : 'test-api-key';
|
|
276
|
+
});
|
|
277
|
+
return Promise.resolve(result);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const result = await loadWebSearchAuth({
|
|
281
|
+
userId,
|
|
282
|
+
webSearchConfig,
|
|
283
|
+
loadAuthValues: mockLoadAuthValues,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// Check that the correct service types are set
|
|
287
|
+
expect(result.authResult.searchProvider).toBe('serper' as SearchProviders);
|
|
288
|
+
expect(result.authResult.scraperType).toBe('firecrawl' as ScraperTypes);
|
|
289
|
+
// One of the rerankers should be set
|
|
290
|
+
expect(['jina', 'cohere']).toContain(result.authResult.rerankerType as string);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should check all services if none are specified', async () => {
|
|
294
|
+
// Initialize a webSearchConfig without specific services
|
|
295
|
+
const webSearchConfig: TCustomConfig['webSearch'] = {
|
|
296
|
+
serperApiKey: '${SERPER_API_KEY}',
|
|
297
|
+
firecrawlApiKey: '${FIRECRAWL_API_KEY}',
|
|
298
|
+
firecrawlApiUrl: '${FIRECRAWL_API_URL}',
|
|
299
|
+
jinaApiKey: '${JINA_API_KEY}',
|
|
300
|
+
cohereApiKey: '${COHERE_API_KEY}',
|
|
301
|
+
safeSearch: SafeSearchTypes.MODERATE,
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
// Mock successful authentication
|
|
305
|
+
mockLoadAuthValues.mockImplementation(({ authFields }) => {
|
|
306
|
+
const result: Record<string, string> = {};
|
|
307
|
+
authFields.forEach((field) => {
|
|
308
|
+
result[field] =
|
|
309
|
+
field === 'FIRECRAWL_API_URL' ? 'https://api.firecrawl.dev' : 'test-api-key';
|
|
310
|
+
});
|
|
311
|
+
return Promise.resolve(result);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
const result = await loadWebSearchAuth({
|
|
315
|
+
userId,
|
|
316
|
+
webSearchConfig,
|
|
317
|
+
loadAuthValues: mockLoadAuthValues,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
expect(result.authenticated).toBe(true);
|
|
321
|
+
|
|
322
|
+
// Should have checked all categories
|
|
323
|
+
expect(result.authTypes).toHaveLength(3);
|
|
324
|
+
|
|
325
|
+
// Should have set values for all categories
|
|
326
|
+
expect(result.authResult.searchProvider).toBeDefined();
|
|
327
|
+
expect(result.authResult.scraperType).toBeDefined();
|
|
328
|
+
expect(result.authResult.rerankerType).toBeDefined();
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('should correctly identify authTypes based on specific configurations', async () => {
|
|
332
|
+
// Set up environment variables for system-defined auth
|
|
333
|
+
const originalEnv = process.env;
|
|
334
|
+
process.env = {
|
|
335
|
+
...originalEnv,
|
|
336
|
+
SERPER_API_KEY: 'system-serper-key',
|
|
337
|
+
FIRECRAWL_API_KEY: 'system-firecrawl-key',
|
|
338
|
+
FIRECRAWL_API_URL: 'https://api.firecrawl.dev',
|
|
339
|
+
JINA_API_KEY: 'system-jina-key',
|
|
340
|
+
COHERE_API_KEY: 'system-cohere-key',
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
// Initialize webSearchConfig with environment variable references
|
|
344
|
+
const webSearchConfig: TCustomConfig['webSearch'] = {
|
|
345
|
+
serperApiKey: '${SERPER_API_KEY}',
|
|
346
|
+
firecrawlApiKey: '${FIRECRAWL_API_KEY}',
|
|
347
|
+
firecrawlApiUrl: '${FIRECRAWL_API_URL}',
|
|
348
|
+
jinaApiKey: '${JINA_API_KEY}',
|
|
349
|
+
cohereApiKey: '${COHERE_API_KEY}',
|
|
350
|
+
safeSearch: SafeSearchTypes.MODERATE,
|
|
351
|
+
// Specify which services to use
|
|
352
|
+
searchProvider: 'serper' as SearchProviders,
|
|
353
|
+
scraperType: 'firecrawl' as ScraperTypes,
|
|
354
|
+
rerankerType: 'jina' as RerankerTypes,
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
// Mock loadAuthValues to return the actual values
|
|
358
|
+
mockLoadAuthValues.mockImplementation(({ authFields }) => {
|
|
359
|
+
const result: Record<string, string> = {};
|
|
360
|
+
authFields.forEach((field) => {
|
|
361
|
+
if (field === 'SERPER_API_KEY') {
|
|
362
|
+
result[field] = 'system-serper-key';
|
|
363
|
+
} else if (field === 'FIRECRAWL_API_KEY') {
|
|
364
|
+
result[field] = 'system-firecrawl-key';
|
|
365
|
+
} else if (field === 'FIRECRAWL_API_URL') {
|
|
366
|
+
result[field] = 'https://api.firecrawl.dev';
|
|
367
|
+
} else if (field === 'JINA_API_KEY') {
|
|
368
|
+
result[field] = 'system-jina-key';
|
|
369
|
+
} else if (field === 'COHERE_API_KEY') {
|
|
370
|
+
result[field] = 'system-cohere-key';
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
return Promise.resolve(result);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const result = await loadWebSearchAuth({
|
|
377
|
+
userId,
|
|
378
|
+
webSearchConfig,
|
|
379
|
+
loadAuthValues: mockLoadAuthValues,
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// Verify that all required fields are present in the authResult
|
|
383
|
+
expect(result.authResult).toHaveProperty('serperApiKey');
|
|
384
|
+
expect(result.authResult).toHaveProperty('firecrawlApiKey');
|
|
385
|
+
expect(result.authResult).toHaveProperty('firecrawlApiUrl');
|
|
386
|
+
expect(result.authResult).toHaveProperty('jinaApiKey');
|
|
387
|
+
expect(result.authResult).toHaveProperty('searchProvider');
|
|
388
|
+
expect(result.authResult).toHaveProperty('scraperType');
|
|
389
|
+
expect(result.authResult).toHaveProperty('rerankerType');
|
|
390
|
+
|
|
391
|
+
expect(result.authenticated).toBe(true);
|
|
392
|
+
|
|
393
|
+
// Verify authTypes for each category
|
|
394
|
+
const providersAuthType = result.authTypes.find(
|
|
395
|
+
([category]) => category === 'providers',
|
|
396
|
+
)?.[1];
|
|
397
|
+
const scrapersAuthType = result.authTypes.find(([category]) => category === 'scrapers')?.[1];
|
|
398
|
+
const rerankersAuthType = result.authTypes.find(
|
|
399
|
+
([category]) => category === 'rerankers',
|
|
400
|
+
)?.[1];
|
|
401
|
+
|
|
402
|
+
// All should be system-defined since we're using environment variables
|
|
403
|
+
expect(providersAuthType).toBe(AuthType.SYSTEM_DEFINED);
|
|
404
|
+
expect(scrapersAuthType).toBe(AuthType.SYSTEM_DEFINED);
|
|
405
|
+
expect(rerankersAuthType).toBe(AuthType.SYSTEM_DEFINED);
|
|
406
|
+
|
|
407
|
+
// Verify the authResult contains the correct values
|
|
408
|
+
expect(result.authResult).toHaveProperty('serperApiKey', 'system-serper-key');
|
|
409
|
+
expect(result.authResult).toHaveProperty('firecrawlApiKey', 'system-firecrawl-key');
|
|
410
|
+
expect(result.authResult).toHaveProperty('firecrawlApiUrl', 'https://api.firecrawl.dev');
|
|
411
|
+
expect(result.authResult).toHaveProperty('jinaApiKey', 'system-jina-key');
|
|
412
|
+
expect(result.authResult).toHaveProperty('searchProvider', 'serper');
|
|
413
|
+
expect(result.authResult).toHaveProperty('scraperType', 'firecrawl');
|
|
414
|
+
expect(result.authResult).toHaveProperty('rerankerType', 'jina');
|
|
415
|
+
|
|
416
|
+
// Restore original env
|
|
417
|
+
process.env = originalEnv;
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it('should handle custom variable names in environment variables', async () => {
|
|
421
|
+
// Set up environment variables with custom names
|
|
422
|
+
const originalEnv = process.env;
|
|
423
|
+
process.env = {
|
|
424
|
+
...originalEnv,
|
|
425
|
+
CUSTOM_SERPER_KEY: 'custom-serper-key',
|
|
426
|
+
CUSTOM_FIRECRAWL_KEY: 'custom-firecrawl-key',
|
|
427
|
+
CUSTOM_FIRECRAWL_URL: 'https://custom.firecrawl.dev',
|
|
428
|
+
CUSTOM_JINA_KEY: 'custom-jina-key',
|
|
429
|
+
CUSTOM_COHERE_KEY: 'custom-cohere-key',
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
// Initialize webSearchConfig with custom variable names
|
|
433
|
+
const webSearchConfig: TCustomConfig['webSearch'] = {
|
|
434
|
+
serperApiKey: '${CUSTOM_SERPER_KEY}',
|
|
435
|
+
firecrawlApiKey: '${CUSTOM_FIRECRAWL_KEY}',
|
|
436
|
+
firecrawlApiUrl: '${CUSTOM_FIRECRAWL_URL}',
|
|
437
|
+
jinaApiKey: '${CUSTOM_JINA_KEY}',
|
|
438
|
+
cohereApiKey: '${CUSTOM_COHERE_KEY}',
|
|
439
|
+
safeSearch: SafeSearchTypes.MODERATE,
|
|
440
|
+
// Specify which services to use
|
|
441
|
+
searchProvider: 'serper' as SearchProviders,
|
|
442
|
+
scraperType: 'firecrawl' as ScraperTypes,
|
|
443
|
+
rerankerType: 'jina' as RerankerTypes, // Only Jina will be checked
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
// Mock loadAuthValues to return the actual values
|
|
447
|
+
mockLoadAuthValues.mockImplementation(({ authFields }) => {
|
|
448
|
+
const result: Record<string, string> = {};
|
|
449
|
+
authFields.forEach((field) => {
|
|
450
|
+
if (field === 'CUSTOM_SERPER_KEY') {
|
|
451
|
+
result[field] = 'custom-serper-key';
|
|
452
|
+
} else if (field === 'CUSTOM_FIRECRAWL_KEY') {
|
|
453
|
+
result[field] = 'custom-firecrawl-key';
|
|
454
|
+
} else if (field === 'CUSTOM_FIRECRAWL_URL') {
|
|
455
|
+
result[field] = 'https://custom.firecrawl.dev';
|
|
456
|
+
} else if (field === 'CUSTOM_JINA_KEY') {
|
|
457
|
+
result[field] = 'custom-jina-key';
|
|
458
|
+
}
|
|
459
|
+
// Note: CUSTOM_COHERE_KEY is not checked because we specified jina as rerankerType
|
|
460
|
+
});
|
|
461
|
+
return Promise.resolve(result);
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
const result = await loadWebSearchAuth({
|
|
465
|
+
userId,
|
|
466
|
+
webSearchConfig,
|
|
467
|
+
loadAuthValues: mockLoadAuthValues,
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
expect(result.authenticated).toBe(true);
|
|
471
|
+
|
|
472
|
+
// Verify the authResult contains the correct values from custom variables
|
|
473
|
+
expect(result.authResult).toHaveProperty('serperApiKey', 'custom-serper-key');
|
|
474
|
+
expect(result.authResult).toHaveProperty('firecrawlApiKey', 'custom-firecrawl-key');
|
|
475
|
+
expect(result.authResult).toHaveProperty('firecrawlApiUrl', 'https://custom.firecrawl.dev');
|
|
476
|
+
expect(result.authResult).toHaveProperty('jinaApiKey', 'custom-jina-key');
|
|
477
|
+
// cohereApiKey should not be in the result since we specified jina as rerankerType
|
|
478
|
+
expect(result.authResult).not.toHaveProperty('cohereApiKey');
|
|
479
|
+
|
|
480
|
+
// Verify the service types are set correctly
|
|
481
|
+
expect(result.authResult).toHaveProperty('searchProvider', 'serper');
|
|
482
|
+
expect(result.authResult).toHaveProperty('scraperType', 'firecrawl');
|
|
483
|
+
expect(result.authResult).toHaveProperty('rerankerType', 'jina');
|
|
484
|
+
|
|
485
|
+
// Restore original env
|
|
486
|
+
process.env = originalEnv;
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it('should always return authTypes array with exactly 3 categories', async () => {
|
|
490
|
+
// Set up environment variables
|
|
491
|
+
const originalEnv = process.env;
|
|
492
|
+
process.env = {
|
|
493
|
+
...originalEnv,
|
|
494
|
+
SERPER_API_KEY: 'test-key',
|
|
495
|
+
FIRECRAWL_API_KEY: 'test-key',
|
|
496
|
+
FIRECRAWL_API_URL: 'https://api.firecrawl.dev',
|
|
497
|
+
JINA_API_KEY: 'test-key',
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
// Initialize webSearchConfig with environment variable references
|
|
501
|
+
const webSearchConfig: TCustomConfig['webSearch'] = {
|
|
502
|
+
serperApiKey: '${SERPER_API_KEY}',
|
|
503
|
+
firecrawlApiKey: '${FIRECRAWL_API_KEY}',
|
|
504
|
+
firecrawlApiUrl: '${FIRECRAWL_API_URL}',
|
|
505
|
+
jinaApiKey: '${JINA_API_KEY}',
|
|
506
|
+
cohereApiKey: '${COHERE_API_KEY}',
|
|
507
|
+
safeSearch: SafeSearchTypes.MODERATE,
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
// Mock loadAuthValues to return values
|
|
511
|
+
mockLoadAuthValues.mockImplementation(({ authFields }) => {
|
|
512
|
+
const result: Record<string, string> = {};
|
|
513
|
+
authFields.forEach((field) => {
|
|
514
|
+
result[field] = field === 'FIRECRAWL_API_URL' ? 'https://api.firecrawl.dev' : 'test-key';
|
|
515
|
+
});
|
|
516
|
+
return Promise.resolve(result);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
const result = await loadWebSearchAuth({
|
|
520
|
+
userId,
|
|
521
|
+
webSearchConfig,
|
|
522
|
+
loadAuthValues: mockLoadAuthValues,
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
// Get the number of categories from webSearchAuth
|
|
526
|
+
const expectedCategoryCount = Object.keys(webSearchAuth).length;
|
|
527
|
+
|
|
528
|
+
// Verify authTypes array structure
|
|
529
|
+
expect(result.authTypes).toHaveLength(expectedCategoryCount);
|
|
530
|
+
|
|
531
|
+
// Verify each category exists exactly once
|
|
532
|
+
const categories = result.authTypes.map(([category]) => category);
|
|
533
|
+
Object.keys(webSearchAuth).forEach((category) => {
|
|
534
|
+
expect(categories).toContain(category);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
// Verify no duplicate categories
|
|
538
|
+
expect(new Set(categories).size).toBe(expectedCategoryCount);
|
|
539
|
+
|
|
540
|
+
// Verify each entry has the correct format [category, AuthType]
|
|
541
|
+
result.authTypes.forEach(([category, authType]) => {
|
|
542
|
+
expect(typeof category).toBe('string');
|
|
543
|
+
expect([AuthType.SYSTEM_DEFINED, AuthType.USER_PROVIDED]).toContain(authType);
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
// Restore original env
|
|
547
|
+
process.env = originalEnv;
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
it('should maintain authTypes array structure even when authentication fails', async () => {
|
|
551
|
+
// Set up environment variables
|
|
552
|
+
const originalEnv = process.env;
|
|
553
|
+
process.env = {
|
|
554
|
+
...originalEnv,
|
|
555
|
+
SERPER_API_KEY: 'test-key',
|
|
556
|
+
// Missing other keys to force authentication failure
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
// Initialize webSearchConfig with environment variable references
|
|
560
|
+
const webSearchConfig: TCustomConfig['webSearch'] = {
|
|
561
|
+
serperApiKey: '${SERPER_API_KEY}',
|
|
562
|
+
firecrawlApiKey: '${FIRECRAWL_API_KEY}',
|
|
563
|
+
firecrawlApiUrl: '${FIRECRAWL_API_URL}',
|
|
564
|
+
jinaApiKey: '${JINA_API_KEY}',
|
|
565
|
+
cohereApiKey: '${COHERE_API_KEY}',
|
|
566
|
+
safeSearch: SafeSearchTypes.MODERATE,
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
// Mock loadAuthValues to return partial values
|
|
570
|
+
mockLoadAuthValues.mockImplementation(({ authFields }) => {
|
|
571
|
+
const result: Record<string, string> = {};
|
|
572
|
+
authFields.forEach((field) => {
|
|
573
|
+
if (field === 'SERPER_API_KEY') {
|
|
574
|
+
result[field] = 'test-key';
|
|
575
|
+
}
|
|
576
|
+
// Other fields are intentionally missing
|
|
577
|
+
});
|
|
578
|
+
return Promise.resolve(result);
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
const result = await loadWebSearchAuth({
|
|
582
|
+
userId,
|
|
583
|
+
webSearchConfig,
|
|
584
|
+
loadAuthValues: mockLoadAuthValues,
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
// Get the number of categories from webSearchAuth
|
|
588
|
+
const expectedCategoryCount = Object.keys(webSearchAuth).length;
|
|
589
|
+
|
|
590
|
+
// Verify authentication failed
|
|
591
|
+
expect(result.authenticated).toBe(false);
|
|
592
|
+
|
|
593
|
+
// Verify authTypes array structure is maintained
|
|
594
|
+
expect(result.authTypes).toHaveLength(expectedCategoryCount);
|
|
595
|
+
|
|
596
|
+
// Verify each category exists exactly once
|
|
597
|
+
const categories = result.authTypes.map(([category]) => category);
|
|
598
|
+
Object.keys(webSearchAuth).forEach((category) => {
|
|
599
|
+
expect(categories).toContain(category);
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
// Verify no duplicate categories
|
|
603
|
+
expect(new Set(categories).size).toBe(expectedCategoryCount);
|
|
604
|
+
|
|
605
|
+
// Verify each entry has the correct format [category, AuthType]
|
|
606
|
+
result.authTypes.forEach(([category, authType]) => {
|
|
607
|
+
expect(typeof category).toBe('string');
|
|
608
|
+
expect([AuthType.SYSTEM_DEFINED, AuthType.USER_PROVIDED]).toContain(authType);
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
// Restore original env
|
|
612
|
+
process.env = originalEnv;
|
|
613
|
+
});
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
describe('webSearchAuth', () => {
|
|
617
|
+
it('should have the expected structure', () => {
|
|
618
|
+
// Check that all expected categories exist
|
|
619
|
+
expect(webSearchAuth).toHaveProperty('providers');
|
|
620
|
+
expect(webSearchAuth).toHaveProperty('scrapers');
|
|
621
|
+
expect(webSearchAuth).toHaveProperty('rerankers');
|
|
622
|
+
|
|
623
|
+
// Check providers
|
|
624
|
+
expect(webSearchAuth.providers).toHaveProperty('serper');
|
|
625
|
+
expect(webSearchAuth.providers.serper).toHaveProperty('serperApiKey', 1);
|
|
626
|
+
|
|
627
|
+
// Check scrapers
|
|
628
|
+
expect(webSearchAuth.scrapers).toHaveProperty('firecrawl');
|
|
629
|
+
expect(webSearchAuth.scrapers.firecrawl).toHaveProperty('firecrawlApiKey', 1);
|
|
630
|
+
expect(webSearchAuth.scrapers.firecrawl).toHaveProperty('firecrawlApiUrl', 0);
|
|
631
|
+
|
|
632
|
+
// Check rerankers
|
|
633
|
+
expect(webSearchAuth.rerankers).toHaveProperty('jina');
|
|
634
|
+
expect(webSearchAuth.rerankers.jina).toHaveProperty('jinaApiKey', 1);
|
|
635
|
+
expect(webSearchAuth.rerankers).toHaveProperty('cohere');
|
|
636
|
+
expect(webSearchAuth.rerankers.cohere).toHaveProperty('cohereApiKey', 1);
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
it('should mark required keys with value 1', () => {
|
|
640
|
+
// All keys with value 1 are required
|
|
641
|
+
expect(webSearchAuth.providers.serper.serperApiKey).toBe(1);
|
|
642
|
+
expect(webSearchAuth.scrapers.firecrawl.firecrawlApiKey).toBe(1);
|
|
643
|
+
expect(webSearchAuth.rerankers.jina.jinaApiKey).toBe(1);
|
|
644
|
+
expect(webSearchAuth.rerankers.cohere.cohereApiKey).toBe(1);
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
it('should mark optional keys with value 0', () => {
|
|
648
|
+
// Keys with value 0 are optional
|
|
649
|
+
expect(webSearchAuth.scrapers.firecrawl.firecrawlApiUrl).toBe(0);
|
|
650
|
+
});
|
|
651
|
+
});
|
|
652
|
+
describe('loadWebSearchAuth with specific services', () => {
|
|
653
|
+
// Common test variables
|
|
654
|
+
const userId = 'test-user-id';
|
|
655
|
+
let mockLoadAuthValues: jest.Mock;
|
|
656
|
+
|
|
657
|
+
beforeEach(() => {
|
|
658
|
+
// Reset mocks before each test
|
|
659
|
+
jest.clearAllMocks();
|
|
660
|
+
|
|
661
|
+
// Initialize the mock function
|
|
662
|
+
mockLoadAuthValues = jest.fn();
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
it('should only check the specified searchProvider', async () => {
|
|
666
|
+
// Initialize a webSearchConfig with a specific searchProvider
|
|
667
|
+
const webSearchConfig: TCustomConfig['webSearch'] = {
|
|
668
|
+
serperApiKey: '${SERPER_API_KEY}',
|
|
669
|
+
firecrawlApiKey: '${FIRECRAWL_API_KEY}',
|
|
670
|
+
firecrawlApiUrl: '${FIRECRAWL_API_URL}',
|
|
671
|
+
jinaApiKey: '${JINA_API_KEY}',
|
|
672
|
+
cohereApiKey: '${COHERE_API_KEY}',
|
|
673
|
+
safeSearch: SafeSearchTypes.MODERATE,
|
|
674
|
+
searchProvider: 'serper' as SearchProviders,
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
// Mock successful authentication
|
|
678
|
+
mockLoadAuthValues.mockImplementation(({ authFields }) => {
|
|
679
|
+
const result: Record<string, string> = {};
|
|
680
|
+
authFields.forEach((field) => {
|
|
681
|
+
result[field] =
|
|
682
|
+
field === 'FIRECRAWL_API_URL' ? 'https://api.firecrawl.dev' : 'test-api-key';
|
|
683
|
+
});
|
|
684
|
+
return Promise.resolve(result);
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
const result = await loadWebSearchAuth({
|
|
688
|
+
userId,
|
|
689
|
+
webSearchConfig,
|
|
690
|
+
loadAuthValues: mockLoadAuthValues,
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
expect(result.authenticated).toBe(true);
|
|
694
|
+
expect(result.authResult.searchProvider).toBe('serper');
|
|
695
|
+
|
|
696
|
+
// Verify that only SERPER_API_KEY was requested for the providers category
|
|
697
|
+
const providerCalls = mockLoadAuthValues.mock.calls.filter((call) =>
|
|
698
|
+
call[0].authFields.includes('SERPER_API_KEY'),
|
|
699
|
+
);
|
|
700
|
+
expect(providerCalls.length).toBe(1);
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
it('should only check the specified scraperType', async () => {
|
|
704
|
+
// Initialize a webSearchConfig with a specific scraperType
|
|
705
|
+
const webSearchConfig: TCustomConfig['webSearch'] = {
|
|
706
|
+
serperApiKey: '${SERPER_API_KEY}',
|
|
707
|
+
firecrawlApiKey: '${FIRECRAWL_API_KEY}',
|
|
708
|
+
firecrawlApiUrl: '${FIRECRAWL_API_URL}',
|
|
709
|
+
jinaApiKey: '${JINA_API_KEY}',
|
|
710
|
+
cohereApiKey: '${COHERE_API_KEY}',
|
|
711
|
+
safeSearch: SafeSearchTypes.MODERATE,
|
|
712
|
+
scraperType: 'firecrawl' as ScraperTypes,
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
// Mock successful authentication
|
|
716
|
+
mockLoadAuthValues.mockImplementation(({ authFields }) => {
|
|
717
|
+
const result: Record<string, string> = {};
|
|
718
|
+
authFields.forEach((field) => {
|
|
719
|
+
result[field] =
|
|
720
|
+
field === 'FIRECRAWL_API_URL' ? 'https://api.firecrawl.dev' : 'test-api-key';
|
|
721
|
+
});
|
|
722
|
+
return Promise.resolve(result);
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
const result = await loadWebSearchAuth({
|
|
726
|
+
userId,
|
|
727
|
+
webSearchConfig,
|
|
728
|
+
loadAuthValues: mockLoadAuthValues,
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
expect(result.authenticated).toBe(true);
|
|
732
|
+
expect(result.authResult.scraperType).toBe('firecrawl');
|
|
733
|
+
|
|
734
|
+
// Verify that only FIRECRAWL_API_KEY and FIRECRAWL_API_URL were requested for the scrapers category
|
|
735
|
+
const scraperCalls = mockLoadAuthValues.mock.calls.filter((call) =>
|
|
736
|
+
call[0].authFields.includes('FIRECRAWL_API_KEY'),
|
|
737
|
+
);
|
|
738
|
+
expect(scraperCalls.length).toBe(1);
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
it('should only check the specified rerankerType', async () => {
|
|
742
|
+
// Initialize a webSearchConfig with a specific rerankerType
|
|
743
|
+
const webSearchConfig: TCustomConfig['webSearch'] = {
|
|
744
|
+
serperApiKey: '${SERPER_API_KEY}',
|
|
745
|
+
firecrawlApiKey: '${FIRECRAWL_API_KEY}',
|
|
746
|
+
firecrawlApiUrl: '${FIRECRAWL_API_URL}',
|
|
747
|
+
jinaApiKey: '${JINA_API_KEY}',
|
|
748
|
+
cohereApiKey: '${COHERE_API_KEY}',
|
|
749
|
+
safeSearch: SafeSearchTypes.MODERATE,
|
|
750
|
+
rerankerType: 'jina' as RerankerTypes,
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
// Mock successful authentication
|
|
754
|
+
mockLoadAuthValues.mockImplementation(({ authFields }) => {
|
|
755
|
+
const result: Record<string, string> = {};
|
|
756
|
+
authFields.forEach((field) => {
|
|
757
|
+
result[field] =
|
|
758
|
+
field === 'FIRECRAWL_API_URL' ? 'https://api.firecrawl.dev' : 'test-api-key';
|
|
759
|
+
});
|
|
760
|
+
return Promise.resolve(result);
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
const result = await loadWebSearchAuth({
|
|
764
|
+
userId,
|
|
765
|
+
webSearchConfig,
|
|
766
|
+
loadAuthValues: mockLoadAuthValues,
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
expect(result.authenticated).toBe(true);
|
|
770
|
+
expect(result.authResult.rerankerType).toBe('jina');
|
|
771
|
+
|
|
772
|
+
// Verify that only JINA_API_KEY was requested for the rerankers category
|
|
773
|
+
const rerankerCalls = mockLoadAuthValues.mock.calls.filter((call) =>
|
|
774
|
+
call[0].authFields.includes('JINA_API_KEY'),
|
|
775
|
+
);
|
|
776
|
+
expect(rerankerCalls.length).toBe(1);
|
|
777
|
+
|
|
778
|
+
// Verify that COHERE_API_KEY was not requested
|
|
779
|
+
const cohereCalls = mockLoadAuthValues.mock.calls.filter((call) =>
|
|
780
|
+
call[0].authFields.includes('COHERE_API_KEY'),
|
|
781
|
+
);
|
|
782
|
+
expect(cohereCalls.length).toBe(0);
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
it('should handle invalid specified service gracefully', async () => {
|
|
786
|
+
// Initialize a webSearchConfig with an invalid searchProvider
|
|
787
|
+
const webSearchConfig: TCustomConfig['webSearch'] = {
|
|
788
|
+
serperApiKey: '${SERPER_API_KEY}',
|
|
789
|
+
firecrawlApiKey: '${FIRECRAWL_API_KEY}',
|
|
790
|
+
firecrawlApiUrl: '${FIRECRAWL_API_URL}',
|
|
791
|
+
jinaApiKey: '${JINA_API_KEY}',
|
|
792
|
+
cohereApiKey: '${COHERE_API_KEY}',
|
|
793
|
+
safeSearch: SafeSearchTypes.MODERATE,
|
|
794
|
+
searchProvider: 'invalid-provider' as SearchProviders,
|
|
795
|
+
};
|
|
796
|
+
|
|
797
|
+
// Mock successful authentication
|
|
798
|
+
mockLoadAuthValues.mockImplementation(({ authFields }) => {
|
|
799
|
+
const result: Record<string, string> = {};
|
|
800
|
+
authFields.forEach((field) => {
|
|
801
|
+
result[field] =
|
|
802
|
+
field === 'FIRECRAWL_API_URL' ? 'https://api.firecrawl.dev' : 'test-api-key';
|
|
803
|
+
});
|
|
804
|
+
return Promise.resolve(result);
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
const result = await loadWebSearchAuth({
|
|
808
|
+
userId,
|
|
809
|
+
webSearchConfig,
|
|
810
|
+
loadAuthValues: mockLoadAuthValues,
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
// Should fail because the specified provider doesn't exist
|
|
814
|
+
expect(result.authenticated).toBe(false);
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
it('should fail authentication when specified service is not authenticated but others are', async () => {
|
|
818
|
+
// Initialize a webSearchConfig with a specific rerankerType (jina)
|
|
819
|
+
const webSearchConfig: TCustomConfig['webSearch'] = {
|
|
820
|
+
serperApiKey: '${SERPER_API_KEY}',
|
|
821
|
+
firecrawlApiKey: '${FIRECRAWL_API_KEY}',
|
|
822
|
+
firecrawlApiUrl: '${FIRECRAWL_API_URL}',
|
|
823
|
+
jinaApiKey: '${JINA_API_KEY}',
|
|
824
|
+
cohereApiKey: '${COHERE_API_KEY}',
|
|
825
|
+
safeSearch: SafeSearchTypes.MODERATE,
|
|
826
|
+
rerankerType: 'jina' as RerankerTypes,
|
|
827
|
+
};
|
|
828
|
+
|
|
829
|
+
// Mock authentication where cohere is authenticated but jina is not
|
|
830
|
+
mockLoadAuthValues.mockImplementation(({ authFields }) => {
|
|
831
|
+
const result: Record<string, string> = {};
|
|
832
|
+
authFields.forEach((field) => {
|
|
833
|
+
// Authenticate all fields except JINA_API_KEY
|
|
834
|
+
if (field !== 'JINA_API_KEY') {
|
|
835
|
+
result[field] =
|
|
836
|
+
field === 'FIRECRAWL_API_URL' ? 'https://api.firecrawl.dev' : 'test-api-key';
|
|
837
|
+
}
|
|
838
|
+
});
|
|
839
|
+
return Promise.resolve(result);
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
const result = await loadWebSearchAuth({
|
|
843
|
+
userId,
|
|
844
|
+
webSearchConfig,
|
|
845
|
+
loadAuthValues: mockLoadAuthValues,
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
// Should fail because the specified reranker (jina) is not authenticated
|
|
849
|
+
// even though another reranker (cohere) might be authenticated
|
|
850
|
+
expect(result.authenticated).toBe(false);
|
|
851
|
+
|
|
852
|
+
// Verify that JINA_API_KEY was requested
|
|
853
|
+
const jinaApiKeyCalls = mockLoadAuthValues.mock.calls.filter((call) =>
|
|
854
|
+
call[0].authFields.includes('JINA_API_KEY'),
|
|
855
|
+
);
|
|
856
|
+
expect(jinaApiKeyCalls.length).toBe(1);
|
|
857
|
+
|
|
858
|
+
// Verify that COHERE_API_KEY was not requested since we specified jina
|
|
859
|
+
const cohereApiKeyCalls = mockLoadAuthValues.mock.calls.filter((call) =>
|
|
860
|
+
call[0].authFields.includes('COHERE_API_KEY'),
|
|
861
|
+
);
|
|
862
|
+
expect(cohereApiKeyCalls.length).toBe(0);
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
it('should check all services if none are specified', async () => {
|
|
866
|
+
// Initialize a webSearchConfig without specific services
|
|
867
|
+
const webSearchConfig: TCustomConfig['webSearch'] = {
|
|
868
|
+
serperApiKey: '${SERPER_API_KEY}',
|
|
869
|
+
firecrawlApiKey: '${FIRECRAWL_API_KEY}',
|
|
870
|
+
firecrawlApiUrl: '${FIRECRAWL_API_URL}',
|
|
871
|
+
jinaApiKey: '${JINA_API_KEY}',
|
|
872
|
+
cohereApiKey: '${COHERE_API_KEY}',
|
|
873
|
+
safeSearch: SafeSearchTypes.MODERATE,
|
|
874
|
+
};
|
|
875
|
+
|
|
876
|
+
// Mock successful authentication
|
|
877
|
+
mockLoadAuthValues.mockImplementation(({ authFields }) => {
|
|
878
|
+
const result: Record<string, string> = {};
|
|
879
|
+
authFields.forEach((field) => {
|
|
880
|
+
result[field] =
|
|
881
|
+
field === 'FIRECRAWL_API_URL' ? 'https://api.firecrawl.dev' : 'test-api-key';
|
|
882
|
+
});
|
|
883
|
+
return Promise.resolve(result);
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
const result = await loadWebSearchAuth({
|
|
887
|
+
userId,
|
|
888
|
+
webSearchConfig,
|
|
889
|
+
loadAuthValues: mockLoadAuthValues,
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
expect(result.authenticated).toBe(true);
|
|
893
|
+
|
|
894
|
+
// Should have checked all categories
|
|
895
|
+
expect(result.authTypes).toHaveLength(3);
|
|
896
|
+
|
|
897
|
+
// Should have set values for all categories
|
|
898
|
+
expect(result.authResult.searchProvider).toBeDefined();
|
|
899
|
+
expect(result.authResult.scraperType).toBeDefined();
|
|
900
|
+
expect(result.authResult.rerankerType).toBeDefined();
|
|
901
|
+
});
|
|
902
|
+
});
|
|
903
|
+
});
|