diff --git a/api/app/clients/specs/BaseClient.test.js b/api/app/clients/specs/BaseClient.test.js index b5ae5b80b6..b93526ef13 100644 --- a/api/app/clients/specs/BaseClient.test.js +++ b/api/app/clients/specs/BaseClient.test.js @@ -2,6 +2,14 @@ const { Constants } = require('librechat-data-provider'); const { initializeFakeClient } = require('./FakeClient'); jest.mock('~/db/connect'); +jest.mock('~/server/services/Config', () => ({ + getAppConfig: jest.fn().mockResolvedValue({ + // Default app config for tests + paths: { uploads: '/tmp' }, + fileStrategy: 'local', + memory: { disabled: false }, + }), +})); jest.mock('~/models', () => ({ User: jest.fn(), Key: jest.fn(), diff --git a/api/app/clients/tools/util/handleTools.test.js b/api/app/clients/tools/util/handleTools.test.js index 1cacda8159..b01aa2f7ef 100644 --- a/api/app/clients/tools/util/handleTools.test.js +++ b/api/app/clients/tools/util/handleTools.test.js @@ -9,6 +9,27 @@ const mockPluginService = { jest.mock('~/server/services/PluginService', () => mockPluginService); +jest.mock('~/server/services/Config', () => ({ + getAppConfig: jest.fn().mockResolvedValue({ + // Default app config for tool tests + paths: { uploads: '/tmp' }, + fileStrategy: 'local', + filteredTools: [], + includedTools: [], + }), + getCachedTools: jest.fn().mockResolvedValue({ + // Default cached tools for tests + dalle: { + type: 'function', + function: { + name: 'dalle', + description: 'DALL-E image generation', + parameters: {}, + }, + }, + }), +})); + const { BaseLLM } = require('@langchain/openai'); const { Calculator } = require('@langchain/community/tools/calculator'); diff --git a/api/server/index.spec.js b/api/server/index.spec.js index 79ca1621ac..141ce24596 100644 --- a/api/server/index.spec.js +++ b/api/server/index.spec.js @@ -3,9 +3,29 @@ const request = require('supertest'); const { MongoMemoryServer } = require('mongodb-memory-server'); const mongoose = require('mongoose'); -jest.mock('~/server/services/Config/loadCustomConfig', () => { - return jest.fn(() => Promise.resolve({})); -}); +jest.mock('~/server/services/Config', () => ({ + loadCustomConfig: jest.fn(() => Promise.resolve({})), + getCustomConfig: jest.fn(() => Promise.resolve({})), + setAppConfig: jest.fn(), + getAppConfig: jest.fn().mockResolvedValue({ + paths: { + uploads: '/tmp', + dist: '/tmp/dist', + fonts: '/tmp/fonts', + assets: '/tmp/assets', + }, + fileStrategy: 'local', + imageOutputType: 'PNG', + }), + setCachedTools: jest.fn(), +})); + +jest.mock('~/app/clients/tools', () => ({ + createOpenAIImageTools: jest.fn(() => []), + createYouTubeTools: jest.fn(() => []), + manifestToolMap: {}, + toolkits: [], +})); describe('Server Configuration', () => { // Increase the default timeout to allow for Mongo cleanup @@ -31,6 +51,22 @@ describe('Server Configuration', () => { }); beforeAll(async () => { + // Create the required directories and files for the test + const fs = require('fs'); + const path = require('path'); + + const dirs = ['/tmp/dist', '/tmp/fonts', '/tmp/assets']; + dirs.forEach((dir) => { + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + }); + + fs.writeFileSync( + path.join('/tmp/dist', 'index.html'), + 'LibreChat
', + ); + mongoServer = await MongoMemoryServer.create(); process.env.MONGO_URI = mongoServer.getUri(); process.env.PORT = '0'; // Use a random available port diff --git a/api/server/services/Endpoints/azureAssistants/initialize.spec.js b/api/server/services/Endpoints/azureAssistants/initialize.spec.js index 28e1004c9f..e894a82499 100644 --- a/api/server/services/Endpoints/azureAssistants/initialize.spec.js +++ b/api/server/services/Endpoints/azureAssistants/initialize.spec.js @@ -12,6 +12,15 @@ jest.mock('~/server/services/UserService', () => ({ checkUserKeyExpiry: jest.requireActual('~/server/services/UserService').checkUserKeyExpiry, })); +jest.mock('~/server/services/Config', () => ({ + getAppConfig: jest.fn().mockResolvedValue({ + azureAssistants: { + apiKey: 'test-key', + baseURL: 'https://test.url', + }, + }), +})); + const today = new Date(); const tenDaysFromToday = new Date(today.setDate(today.getDate() + 10)); const isoString = tenDaysFromToday.toISOString(); diff --git a/api/server/services/Endpoints/custom/initialize.spec.js b/api/server/services/Endpoints/custom/initialize.spec.js index 373620d42a..8108ec8302 100644 --- a/api/server/services/Endpoints/custom/initialize.spec.js +++ b/api/server/services/Endpoints/custom/initialize.spec.js @@ -30,6 +30,12 @@ jest.mock('~/server/services/Config', () => ({ headers: { 'x-user': '{{LIBRECHAT_USER_ID}}', 'x-email': '{{LIBRECHAT_USER_EMAIL}}' }, models: { default: ['test-model'] }, }), + getAppConfig: jest.fn().mockResolvedValue({ + 'test-endpoint': { + apiKey: 'test-key', + baseURL: 'https://test.com', + }, + }), })); jest.mock('~/server/services/ModelService', () => ({ diff --git a/api/server/services/Endpoints/google/initialize.spec.js b/api/server/services/Endpoints/google/initialize.spec.js index e5391107bd..c6218f5d12 100644 --- a/api/server/services/Endpoints/google/initialize.spec.js +++ b/api/server/services/Endpoints/google/initialize.spec.js @@ -8,6 +8,14 @@ jest.mock('~/server/services/UserService', () => ({ getUserKey: jest.fn().mockImplementation(() => ({})), })); +jest.mock('~/server/services/Config', () => ({ + getAppConfig: jest.fn().mockResolvedValue({ + google: { + apiKey: 'test-key', + }, + }), +})); + const app = { locals: {} }; describe('google/initializeClient', () => { diff --git a/api/server/services/Endpoints/openAI/initialize.spec.js b/api/server/services/Endpoints/openAI/initialize.spec.js index 16563e4b26..40a6e2973c 100644 --- a/api/server/services/Endpoints/openAI/initialize.spec.js +++ b/api/server/services/Endpoints/openAI/initialize.spec.js @@ -1,4 +1,13 @@ -jest.mock('~/cache/getLogStores'); +jest.mock('~/cache/getLogStores', () => ({ + getLogStores: jest.fn().mockReturnValue({ + get: jest.fn().mockResolvedValue({ + openAI: { apiKey: 'test-key' }, + }), + set: jest.fn(), + delete: jest.fn(), + }), +})); + const { EModelEndpoint, ErrorTypes, validateAzureGroups } = require('librechat-data-provider'); const { getUserKey, getUserKeyValues } = require('~/server/services/UserService'); const initializeClient = require('./initialize'); @@ -11,6 +20,38 @@ jest.mock('~/server/services/UserService', () => ({ checkUserKeyExpiry: jest.requireActual('~/server/services/UserService').checkUserKeyExpiry, })); +jest.mock('~/server/services/Config', () => ({ + getAppConfig: jest.fn().mockResolvedValue({ + openAI: { + apiKey: 'test-key', + }, + azureOpenAI: { + apiKey: 'test-azure-key', + modelNames: ['gpt-4-vision-preview', 'gpt-3.5-turbo', 'gpt-4'], + modelGroupMap: { + 'gpt-4-vision-preview': { + group: 'librechat-westus', + deploymentName: 'gpt-4-vision-preview', + version: '2024-02-15-preview', + }, + }, + groupMap: { + 'librechat-westus': { + apiKey: 'WESTUS_API_KEY', + instanceName: 'librechat-westus', + version: '2023-12-01-preview', + models: { + 'gpt-4-vision-preview': { + deploymentName: 'gpt-4-vision-preview', + version: '2024-02-15-preview', + }, + }, + }, + }, + }, + }), +})); + describe('initializeClient', () => { // Set up environment variables const originalEnvironment = process.env; @@ -79,7 +120,7 @@ describe('initializeClient', () => { }, ]; - const { modelNames, modelGroupMap, groupMap } = validateAzureGroups(validAzureConfigs); + const { modelNames } = validateAzureGroups(validAzureConfigs); beforeEach(() => { jest.resetModules(); // Clears the cache @@ -112,25 +153,29 @@ describe('initializeClient', () => { test('should initialize client with Azure credentials when endpoint is azureOpenAI', async () => { process.env.AZURE_API_KEY = 'test-azure-api-key'; (process.env.AZURE_OPENAI_API_INSTANCE_NAME = 'some-value'), - (process.env.AZURE_OPENAI_API_DEPLOYMENT_NAME = 'some-value'), - (process.env.AZURE_OPENAI_API_VERSION = 'some-value'), - (process.env.AZURE_OPENAI_API_COMPLETIONS_DEPLOYMENT_NAME = 'some-value'), - (process.env.AZURE_OPENAI_API_EMBEDDINGS_DEPLOYMENT_NAME = 'some-value'), - (process.env.OPENAI_API_KEY = 'test-openai-api-key'); + (process.env.AZURE_OPENAI_API_DEPLOYMENT_NAME = 'some-value'), + (process.env.AZURE_OPENAI_API_VERSION = 'some-value'), + (process.env.AZURE_OPENAI_API_COMPLETIONS_DEPLOYMENT_NAME = 'some-value'), + (process.env.AZURE_OPENAI_API_EMBEDDINGS_DEPLOYMENT_NAME = 'some-value'), + (process.env.OPENAI_API_KEY = 'test-openai-api-key'); process.env.DEBUG_OPENAI = 'false'; process.env.OPENAI_SUMMARIZE = 'false'; const req = { - body: { key: null, endpoint: 'azureOpenAI' }, + body: { + key: null, + endpoint: 'azureOpenAI', + model: 'gpt-4-vision-preview', + }, user: { id: '123' }, app, }; const res = {}; - const endpointOption = { modelOptions: { model: 'test-model' } }; + const endpointOption = {}; const client = await initializeClient({ req, res, endpointOption }); - expect(client.openAIApiKey).toBe('test-azure-api-key'); + expect(client.openAIApiKey).toBe('WESTUS_API_KEY'); expect(client.client).toBeInstanceOf(OpenAIClient); }); @@ -291,7 +336,7 @@ describe('initializeClient', () => { let userValues = getUserKey(); try { userValues = JSON.parse(userValues); - } catch (e) { + } catch { throw new Error( JSON.stringify({ type: ErrorTypes.INVALID_USER_KEY, @@ -307,6 +352,9 @@ describe('initializeClient', () => { }); test('should initialize client correctly for Azure OpenAI with valid configuration', async () => { + // Set up Azure environment variables + process.env.WESTUS_API_KEY = 'test-westus-key'; + const req = { body: { key: null, @@ -314,15 +362,6 @@ describe('initializeClient', () => { model: modelNames[0], }, user: { id: '123' }, - app: { - locals: { - [EModelEndpoint.azureOpenAI]: { - modelNames, - modelGroupMap, - groupMap, - }, - }, - }, }; const res = {}; const endpointOption = {};