| import { |
| OCRStrategy, |
| FileSources, |
| EModelEndpoint, |
| EImageOutputType, |
| AgentCapabilities, |
| defaultSocialLogins, |
| validateAzureGroups, |
| defaultAgentCapabilities, |
| } from 'librechat-data-provider'; |
| import type { TCustomConfig } from 'librechat-data-provider'; |
|
|
| jest.mock('@librechat/data-schemas', () => ({ |
| ...jest.requireActual('@librechat/data-schemas'), |
| logger: { |
| info: jest.fn(), |
| warn: jest.fn(), |
| error: jest.fn(), |
| debug: jest.fn(), |
| }, |
| })); |
|
|
| import { AppService } from '@librechat/data-schemas'; |
|
|
| const azureGroups = [ |
| { |
| group: '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', |
| }, |
| 'gpt-3.5-turbo': { |
| deploymentName: 'gpt-35-turbo', |
| }, |
| 'gpt-3.5-turbo-1106': { |
| deploymentName: 'gpt-35-turbo-1106', |
| }, |
| 'gpt-4': { |
| deploymentName: 'gpt-4', |
| }, |
| 'gpt-4-1106-preview': { |
| deploymentName: 'gpt-4-1106-preview', |
| }, |
| }, |
| }, |
| { |
| group: 'librechat-eastus', |
| apiKey: '${EASTUS_API_KEY}', |
| instanceName: 'librechat-eastus', |
| deploymentName: 'gpt-4-turbo', |
| version: '2024-02-15-preview', |
| models: { |
| 'gpt-4-turbo': true, |
| }, |
| } as const, |
| ]; |
|
|
| describe('AppService', () => { |
| const mockSystemTools = { |
| ExampleTool: { |
| type: 'function', |
| function: { |
| description: 'Example tool function', |
| name: 'exampleFunction', |
| parameters: { |
| type: 'object', |
| properties: { |
| param1: { type: 'string', description: 'An example parameter' }, |
| }, |
| required: ['param1'], |
| }, |
| }, |
| }, |
| }; |
|
|
| beforeEach(() => { |
| process.env.CDN_PROVIDER = undefined; |
| jest.clearAllMocks(); |
| }); |
|
|
| it('should correctly assign process.env and initialize app config based on custom config', async () => { |
| const config: Partial<TCustomConfig> = { |
| registration: { socialLogins: ['testLogin'] }, |
| fileStrategy: 'testStrategy' as FileSources, |
| balance: { |
| enabled: true, |
| }, |
| }; |
|
|
| const result = await AppService({ config, systemTools: mockSystemTools }); |
|
|
| expect(process.env.CDN_PROVIDER).toEqual('testStrategy'); |
|
|
| expect(result).toEqual( |
| expect.objectContaining({ |
| config: expect.objectContaining({ |
| fileStrategy: 'testStrategy', |
| }), |
| registration: expect.objectContaining({ |
| socialLogins: ['testLogin'], |
| }), |
| fileStrategy: 'testStrategy', |
| interfaceConfig: expect.objectContaining({ |
| endpointsMenu: true, |
| modelSelect: true, |
| parameters: true, |
| sidePanel: true, |
| presets: true, |
| }), |
| mcpConfig: null, |
| imageOutputType: expect.any(String), |
| fileConfig: undefined, |
| secureImageLinks: undefined, |
| balance: { enabled: true }, |
| filteredTools: undefined, |
| includedTools: undefined, |
| webSearch: expect.objectContaining({ |
| safeSearch: 1, |
| jinaApiKey: '${JINA_API_KEY}', |
| jinaApiUrl: '${JINA_API_URL}', |
| cohereApiKey: '${COHERE_API_KEY}', |
| serperApiKey: '${SERPER_API_KEY}', |
| searxngApiKey: '${SEARXNG_API_KEY}', |
| firecrawlApiKey: '${FIRECRAWL_API_KEY}', |
| firecrawlApiUrl: '${FIRECRAWL_API_URL}', |
| searxngInstanceUrl: '${SEARXNG_INSTANCE_URL}', |
| }), |
| memory: undefined, |
| endpoints: expect.objectContaining({ |
| agents: expect.objectContaining({ |
| disableBuilder: false, |
| capabilities: expect.arrayContaining([...defaultAgentCapabilities]), |
| maxCitations: 30, |
| maxCitationsPerFile: 7, |
| minRelevanceScore: 0.45, |
| }), |
| }), |
| }), |
| ); |
| }); |
|
|
| it('should change the `imageOutputType` based on config value', async () => { |
| const config = { |
| version: '0.10.0', |
| imageOutputType: EImageOutputType.WEBP, |
| }; |
|
|
| const result = await AppService({ config }); |
| expect(result).toEqual( |
| expect.objectContaining({ |
| imageOutputType: EImageOutputType.WEBP, |
| }), |
| ); |
| }); |
|
|
| it('should default to `PNG` `imageOutputType` with no provided type', async () => { |
| const config = { |
| version: '0.10.0', |
| }; |
|
|
| const result = await AppService({ config }); |
| expect(result).toEqual( |
| expect.objectContaining({ |
| imageOutputType: EImageOutputType.PNG, |
| }), |
| ); |
| }); |
|
|
| it('should default to `PNG` `imageOutputType` with no provided config', async () => { |
| const config = {}; |
|
|
| const result = await AppService({ config }); |
| expect(result).toEqual( |
| expect.objectContaining({ |
| imageOutputType: EImageOutputType.PNG, |
| }), |
| ); |
| }); |
|
|
| it('should load and format tools accurately with defined structure', async () => { |
| const config = {}; |
|
|
| const result = await AppService({ config, systemTools: mockSystemTools }); |
|
|
| |
| expect(result.availableTools).toBeDefined(); |
| expect(result.availableTools?.ExampleTool).toEqual({ |
| type: 'function', |
| function: { |
| description: 'Example tool function', |
| name: 'exampleFunction', |
| parameters: { |
| type: 'object', |
| properties: { |
| param1: { type: 'string', description: 'An example parameter' }, |
| }, |
| required: ['param1'], |
| }, |
| }, |
| }); |
| }); |
|
|
| it('should correctly configure Assistants endpoint based on custom config', async () => { |
| const config: Partial<TCustomConfig> = { |
| endpoints: { |
| [EModelEndpoint.assistants]: { |
| disableBuilder: true, |
| pollIntervalMs: 5000, |
| timeoutMs: 30000, |
| supportedIds: ['id1', 'id2'], |
| privateAssistants: false, |
| }, |
| }, |
| }; |
|
|
| const result = await AppService({ config }); |
|
|
| expect(result).toEqual( |
| expect.objectContaining({ |
| endpoints: expect.objectContaining({ |
| [EModelEndpoint.assistants]: expect.objectContaining({ |
| disableBuilder: true, |
| pollIntervalMs: 5000, |
| timeoutMs: 30000, |
| supportedIds: expect.arrayContaining(['id1', 'id2']), |
| privateAssistants: false, |
| }), |
| }), |
| }), |
| ); |
| }); |
|
|
| it('should correctly configure Agents endpoint based on custom config', async () => { |
| const config: Partial<TCustomConfig> = { |
| endpoints: { |
| [EModelEndpoint.agents]: { |
| disableBuilder: true, |
| recursionLimit: 10, |
| maxRecursionLimit: 20, |
| allowedProviders: ['openai', 'anthropic'], |
| capabilities: [AgentCapabilities.tools, AgentCapabilities.actions], |
| }, |
| }, |
| }; |
|
|
| const result = await AppService({ config }); |
|
|
| expect(result).toEqual( |
| expect.objectContaining({ |
| endpoints: expect.objectContaining({ |
| [EModelEndpoint.agents]: expect.objectContaining({ |
| disableBuilder: true, |
| recursionLimit: 10, |
| maxRecursionLimit: 20, |
| allowedProviders: expect.arrayContaining(['openai', 'anthropic']), |
| capabilities: expect.arrayContaining([ |
| AgentCapabilities.tools, |
| AgentCapabilities.actions, |
| ]), |
| }), |
| }), |
| }), |
| ); |
| }); |
|
|
| it('should configure Agents endpoint with defaults when no config is provided', async () => { |
| const config = {}; |
|
|
| const result = await AppService({ config }); |
|
|
| expect(result).toEqual( |
| expect.objectContaining({ |
| endpoints: expect.objectContaining({ |
| [EModelEndpoint.agents]: expect.objectContaining({ |
| disableBuilder: false, |
| capabilities: expect.arrayContaining([...defaultAgentCapabilities]), |
| }), |
| }), |
| }), |
| ); |
| }); |
|
|
| it('should configure Agents endpoint with defaults when endpoints exist but agents is not defined', async () => { |
| const config = { |
| endpoints: { |
| [EModelEndpoint.openAI]: { |
| titleConvo: true, |
| }, |
| }, |
| }; |
|
|
| const result = await AppService({ config }); |
|
|
| expect(result).toEqual( |
| expect.objectContaining({ |
| endpoints: expect.objectContaining({ |
| [EModelEndpoint.agents]: expect.objectContaining({ |
| disableBuilder: false, |
| capabilities: expect.arrayContaining([...defaultAgentCapabilities]), |
| }), |
| [EModelEndpoint.openAI]: expect.objectContaining({ |
| titleConvo: true, |
| }), |
| }), |
| }), |
| ); |
| }); |
|
|
| it('should correctly configure minimum Azure OpenAI Assistant values', async () => { |
| const assistantGroups = [azureGroups[0], { ...azureGroups[1], assistants: true }]; |
| const config = { |
| endpoints: { |
| [EModelEndpoint.azureOpenAI]: { |
| groups: assistantGroups, |
| assistants: true, |
| }, |
| }, |
| }; |
|
|
| process.env.WESTUS_API_KEY = 'westus-key'; |
| process.env.EASTUS_API_KEY = 'eastus-key'; |
|
|
| const result = await AppService({ config }); |
| expect(result).toEqual( |
| expect.objectContaining({ |
| endpoints: expect.objectContaining({ |
| [EModelEndpoint.azureAssistants]: expect.objectContaining({ |
| capabilities: expect.arrayContaining([ |
| expect.any(String), |
| expect.any(String), |
| expect.any(String), |
| ]), |
| }), |
| }), |
| }), |
| ); |
| }); |
|
|
| it('should correctly configure Azure OpenAI endpoint based on custom config', async () => { |
| const config: Partial<TCustomConfig> = { |
| endpoints: { |
| [EModelEndpoint.azureOpenAI]: { |
| groups: azureGroups, |
| }, |
| }, |
| }; |
|
|
| process.env.WESTUS_API_KEY = 'westus-key'; |
| process.env.EASTUS_API_KEY = 'eastus-key'; |
|
|
| const result = await AppService({ config }); |
|
|
| const { modelNames, modelGroupMap, groupMap } = validateAzureGroups(azureGroups); |
| expect(result).toEqual( |
| expect.objectContaining({ |
| endpoints: expect.objectContaining({ |
| [EModelEndpoint.azureOpenAI]: expect.objectContaining({ |
| modelNames, |
| modelGroupMap, |
| groupMap, |
| }), |
| }), |
| }), |
| ); |
| }); |
|
|
| it('should not modify FILE_UPLOAD environment variables without rate limits', async () => { |
| |
| process.env.FILE_UPLOAD_IP_MAX = '10'; |
| process.env.FILE_UPLOAD_IP_WINDOW = '15'; |
| process.env.FILE_UPLOAD_USER_MAX = '5'; |
| process.env.FILE_UPLOAD_USER_WINDOW = '20'; |
|
|
| const initialEnv = { ...process.env }; |
| const config = {}; |
|
|
| await AppService({ config }); |
|
|
| |
| expect(process.env.FILE_UPLOAD_IP_MAX).toEqual(initialEnv.FILE_UPLOAD_IP_MAX); |
| expect(process.env.FILE_UPLOAD_IP_WINDOW).toEqual(initialEnv.FILE_UPLOAD_IP_WINDOW); |
| expect(process.env.FILE_UPLOAD_USER_MAX).toEqual(initialEnv.FILE_UPLOAD_USER_MAX); |
| expect(process.env.FILE_UPLOAD_USER_WINDOW).toEqual(initialEnv.FILE_UPLOAD_USER_WINDOW); |
| }); |
|
|
| it('should fallback to default FILE_UPLOAD environment variables when rate limits are unspecified', async () => { |
| |
| process.env.FILE_UPLOAD_IP_MAX = 'initialMax'; |
| process.env.FILE_UPLOAD_IP_WINDOW = 'initialWindow'; |
| process.env.FILE_UPLOAD_USER_MAX = 'initialUserMax'; |
| process.env.FILE_UPLOAD_USER_WINDOW = 'initialUserWindow'; |
| const config = {}; |
|
|
| await AppService({ config }); |
|
|
| |
| expect(process.env.FILE_UPLOAD_IP_MAX).toEqual('initialMax'); |
| expect(process.env.FILE_UPLOAD_IP_WINDOW).toEqual('initialWindow'); |
| expect(process.env.FILE_UPLOAD_USER_MAX).toEqual('initialUserMax'); |
| expect(process.env.FILE_UPLOAD_USER_WINDOW).toEqual('initialUserWindow'); |
| }); |
|
|
| it('should not modify IMPORT environment variables without rate limits', async () => { |
| |
| process.env.IMPORT_IP_MAX = '10'; |
| process.env.IMPORT_IP_WINDOW = '15'; |
| process.env.IMPORT_USER_MAX = '5'; |
| process.env.IMPORT_USER_WINDOW = '20'; |
|
|
| const initialEnv = { ...process.env }; |
| const config = {}; |
|
|
| await AppService({ config }); |
|
|
| |
| expect(process.env.IMPORT_IP_MAX).toEqual(initialEnv.IMPORT_IP_MAX); |
| expect(process.env.IMPORT_IP_WINDOW).toEqual(initialEnv.IMPORT_IP_WINDOW); |
| expect(process.env.IMPORT_USER_MAX).toEqual(initialEnv.IMPORT_USER_MAX); |
| expect(process.env.IMPORT_USER_WINDOW).toEqual(initialEnv.IMPORT_USER_WINDOW); |
| }); |
|
|
| it('should fallback to default IMPORT environment variables when rate limits are unspecified', async () => { |
| |
| process.env.IMPORT_IP_MAX = 'initialMax'; |
| process.env.IMPORT_IP_WINDOW = 'initialWindow'; |
| process.env.IMPORT_USER_MAX = 'initialUserMax'; |
| process.env.IMPORT_USER_WINDOW = 'initialUserWindow'; |
| const config = {}; |
|
|
| await AppService({ config }); |
|
|
| |
| expect(process.env.IMPORT_IP_MAX).toEqual('initialMax'); |
| expect(process.env.IMPORT_IP_WINDOW).toEqual('initialWindow'); |
| expect(process.env.IMPORT_USER_MAX).toEqual('initialUserMax'); |
| expect(process.env.IMPORT_USER_WINDOW).toEqual('initialUserWindow'); |
| }); |
|
|
| it('should correctly configure endpoint with titlePrompt, titleMethod, and titlePromptTemplate', async () => { |
| const config: Partial<TCustomConfig> = { |
| endpoints: { |
| [EModelEndpoint.openAI]: { |
| titleConvo: true, |
| titleModel: 'gpt-3.5-turbo', |
| titleMethod: 'structured', |
| titlePrompt: 'Custom title prompt for conversation', |
| titlePromptTemplate: 'Summarize this conversation: {{conversation}}', |
| }, |
| [EModelEndpoint.assistants]: { |
| titleMethod: 'functions', |
| titlePrompt: 'Generate a title for this assistant conversation', |
| titlePromptTemplate: 'Assistant conversation template: {{messages}}', |
| }, |
| [EModelEndpoint.azureOpenAI]: { |
| groups: azureGroups, |
| titleConvo: true, |
| titleMethod: 'completion', |
| titleModel: 'gpt-4', |
| titlePrompt: 'Azure title prompt', |
| titlePromptTemplate: 'Azure conversation: {{context}}', |
| }, |
| }, |
| }; |
|
|
| const result = await AppService({ config }); |
|
|
| expect(result).toEqual( |
| expect.objectContaining({ |
| endpoints: expect.objectContaining({ |
| |
| [EModelEndpoint.openAI]: expect.objectContaining({ |
| titleConvo: true, |
| titleModel: 'gpt-3.5-turbo', |
| titleMethod: 'structured', |
| titlePrompt: 'Custom title prompt for conversation', |
| titlePromptTemplate: 'Summarize this conversation: {{conversation}}', |
| }), |
| |
| [EModelEndpoint.assistants]: expect.objectContaining({ |
| titleMethod: 'functions', |
| titlePrompt: 'Generate a title for this assistant conversation', |
| titlePromptTemplate: 'Assistant conversation template: {{messages}}', |
| }), |
| |
| [EModelEndpoint.azureOpenAI]: expect.objectContaining({ |
| titleConvo: true, |
| titleMethod: 'completion', |
| titleModel: 'gpt-4', |
| titlePrompt: 'Azure title prompt', |
| titlePromptTemplate: 'Azure conversation: {{context}}', |
| }), |
| }), |
| }), |
| ); |
| }); |
|
|
| it('should configure Agent endpoint with title generation settings', async () => { |
| const config: Partial<TCustomConfig> = { |
| endpoints: { |
| [EModelEndpoint.agents]: { |
| disableBuilder: false, |
| titleConvo: true, |
| titleModel: 'gpt-4', |
| titleMethod: 'structured', |
| titlePrompt: 'Generate a descriptive title for this agent conversation', |
| titlePromptTemplate: 'Agent conversation summary: {{content}}', |
| recursionLimit: 15, |
| capabilities: [AgentCapabilities.tools, AgentCapabilities.actions], |
| maxCitations: 30, |
| maxCitationsPerFile: 7, |
| minRelevanceScore: 0.45, |
| }, |
| }, |
| }; |
|
|
| const result = await AppService({ config }); |
|
|
| expect(result).toEqual( |
| expect.objectContaining({ |
| endpoints: expect.objectContaining({ |
| [EModelEndpoint.agents]: expect.objectContaining({ |
| disableBuilder: false, |
| titleConvo: true, |
| titleModel: 'gpt-4', |
| titleMethod: 'structured', |
| titlePrompt: 'Generate a descriptive title for this agent conversation', |
| titlePromptTemplate: 'Agent conversation summary: {{content}}', |
| recursionLimit: 15, |
| capabilities: expect.arrayContaining([ |
| AgentCapabilities.tools, |
| AgentCapabilities.actions, |
| ]), |
| }), |
| }), |
| }), |
| ); |
| }); |
|
|
| it('should handle missing title configuration options with defaults', async () => { |
| const config = { |
| endpoints: { |
| [EModelEndpoint.openAI]: { |
| titleConvo: true, |
| |
| }, |
| }, |
| }; |
|
|
| const result = await AppService({ config }); |
|
|
| expect(result).toEqual( |
| expect.objectContaining({ |
| endpoints: expect.objectContaining({ |
| [EModelEndpoint.openAI]: expect.objectContaining({ |
| titleConvo: true, |
| }), |
| }), |
| }), |
| ); |
|
|
| |
| expect(result.endpoints[EModelEndpoint.openAI].titlePrompt).toBeUndefined(); |
| expect(result.endpoints[EModelEndpoint.openAI].titlePromptTemplate).toBeUndefined(); |
| expect(result.endpoints[EModelEndpoint.openAI].titleMethod).toBeUndefined(); |
| }); |
|
|
| it('should correctly configure titleEndpoint when specified', async () => { |
| const config: Partial<TCustomConfig> = { |
| endpoints: { |
| [EModelEndpoint.openAI]: { |
| titleConvo: true, |
| titleModel: 'gpt-3.5-turbo', |
| titleEndpoint: EModelEndpoint.anthropic, |
| titlePrompt: 'Generate a concise title', |
| }, |
| [EModelEndpoint.agents]: { |
| disableBuilder: false, |
| capabilities: [AgentCapabilities.tools], |
| maxCitations: 30, |
| maxCitationsPerFile: 7, |
| minRelevanceScore: 0.45, |
| titleEndpoint: 'custom-provider', |
| titleMethod: 'structured', |
| }, |
| }, |
| }; |
|
|
| const result = await AppService({ config }); |
|
|
| expect(result).toEqual( |
| expect.objectContaining({ |
| endpoints: expect.objectContaining({ |
| |
| [EModelEndpoint.openAI]: expect.objectContaining({ |
| titleConvo: true, |
| titleModel: 'gpt-3.5-turbo', |
| titleEndpoint: EModelEndpoint.anthropic, |
| titlePrompt: 'Generate a concise title', |
| }), |
| |
| [EModelEndpoint.agents]: expect.objectContaining({ |
| titleEndpoint: 'custom-provider', |
| titleMethod: 'structured', |
| }), |
| }), |
| }), |
| ); |
| }); |
|
|
| it('should correctly configure all endpoint when specified', async () => { |
| const config: Partial<TCustomConfig> = { |
| endpoints: { |
| all: { |
| titleConvo: true, |
| titleModel: 'gpt-4o-mini', |
| titleMethod: 'structured', |
| titlePrompt: 'Default title prompt for all endpoints', |
| titlePromptTemplate: 'Default template: {{conversation}}', |
| titleEndpoint: EModelEndpoint.anthropic, |
| streamRate: 50, |
| }, |
| [EModelEndpoint.openAI]: { |
| titleConvo: true, |
| titleModel: 'gpt-3.5-turbo', |
| }, |
| }, |
| }; |
|
|
| const result = await AppService({ config }); |
|
|
| expect(result).toEqual( |
| expect.objectContaining({ |
| |
| endpoints: expect.objectContaining({ |
| all: expect.objectContaining({ |
| titleConvo: true, |
| titleModel: 'gpt-4o-mini', |
| titleMethod: 'structured', |
| titlePrompt: 'Default title prompt for all endpoints', |
| titlePromptTemplate: 'Default template: {{conversation}}', |
| titleEndpoint: EModelEndpoint.anthropic, |
| streamRate: 50, |
| }), |
| |
| [EModelEndpoint.openAI]: expect.objectContaining({ |
| titleConvo: true, |
| titleModel: 'gpt-3.5-turbo', |
| }), |
| }), |
| }), |
| ); |
| }); |
| }); |
|
|
| describe('AppService updating app config and issuing warnings', () => { |
| let initialEnv: NodeJS.ProcessEnv; |
|
|
| beforeEach(() => { |
| |
| initialEnv = { ...process.env }; |
|
|
| process.env.CDN_PROVIDER = undefined; |
| jest.clearAllMocks(); |
| }); |
|
|
| afterEach(() => { |
| |
| process.env = { ...initialEnv }; |
| }); |
|
|
| it('should initialize app config with default values if config is empty', async () => { |
| const config = {}; |
|
|
| const result = await AppService({ config }); |
|
|
| expect(result).toEqual( |
| expect.objectContaining({ |
| config: {}, |
| fileStrategy: FileSources.local, |
| registration: expect.objectContaining({ |
| socialLogins: defaultSocialLogins, |
| }), |
| balance: expect.objectContaining({ |
| enabled: false, |
| startBalance: undefined, |
| }), |
| }), |
| ); |
| }); |
|
|
| it('should initialize app config with values from config', async () => { |
| |
| const config: Partial<TCustomConfig> = { |
| fileStrategy: FileSources.firebase, |
| registration: { socialLogins: ['testLogin'] }, |
| balance: { |
| enabled: false, |
| startBalance: 5000, |
| autoRefillEnabled: true, |
| refillIntervalValue: 15, |
| refillIntervalUnit: 'hours', |
| refillAmount: 5000, |
| }, |
| }; |
|
|
| const result = await AppService({ config }); |
|
|
| expect(result).toEqual( |
| expect.objectContaining({ |
| config, |
| fileStrategy: config.fileStrategy, |
| registration: expect.objectContaining({ |
| socialLogins: config.registration?.socialLogins, |
| }), |
| balance: config.balance, |
| }), |
| ); |
| }); |
|
|
| it('should apply the assistants endpoint configuration correctly to app config', async () => { |
| const config: Partial<TCustomConfig> = { |
| endpoints: { |
| assistants: { |
| version: 'v2', |
| retrievalModels: ['gpt-4', 'gpt-3.5-turbo'], |
| capabilities: [], |
| disableBuilder: true, |
| pollIntervalMs: 5000, |
| timeoutMs: 30000, |
| supportedIds: ['id1', 'id2'], |
| }, |
| }, |
| }; |
|
|
| const result = await AppService({ config }); |
|
|
| expect(result).toEqual( |
| expect.objectContaining({ |
| endpoints: expect.objectContaining({ |
| assistants: expect.objectContaining({ |
| disableBuilder: true, |
| pollIntervalMs: 5000, |
| timeoutMs: 30000, |
| supportedIds: ['id1', 'id2'], |
| }), |
| }), |
| }), |
| ); |
|
|
| |
| expect(result.endpoints.assistants.excludedIds).toBeUndefined(); |
| }); |
|
|
| it('should not parse environment variable references in OCR config', async () => { |
| |
| const config: Partial<TCustomConfig> = { |
| ocr: { |
| apiKey: '${OCR_API_KEY_CUSTOM_VAR_NAME}', |
| baseURL: '${OCR_BASEURL_CUSTOM_VAR_NAME}', |
| strategy: OCRStrategy.MISTRAL_OCR, |
| mistralModel: 'mistral-medium', |
| }, |
| }; |
|
|
| |
| process.env.OCR_API_KEY_CUSTOM_VAR_NAME = 'actual-api-key'; |
| process.env.OCR_BASEURL_CUSTOM_VAR_NAME = 'https://actual-ocr-url.com'; |
|
|
| const result = await AppService({ config }); |
|
|
| |
| expect(result).toEqual( |
| expect.objectContaining({ |
| ocr: expect.objectContaining({ |
| apiKey: '${OCR_API_KEY_CUSTOM_VAR_NAME}', |
| baseURL: '${OCR_BASEURL_CUSTOM_VAR_NAME}', |
| strategy: 'mistral_ocr', |
| mistralModel: 'mistral-medium', |
| }), |
| }), |
| ); |
| }); |
|
|
| it('should correctly configure peoplePicker permissions when specified', async () => { |
| const config = { |
| interface: { |
| peoplePicker: { |
| users: true, |
| groups: true, |
| roles: true, |
| }, |
| }, |
| }; |
|
|
| const result = await AppService({ config }); |
|
|
| |
| expect(result).toEqual( |
| expect.objectContaining({ |
| interfaceConfig: expect.objectContaining({ |
| peoplePicker: expect.objectContaining({ |
| users: true, |
| groups: true, |
| roles: true, |
| }), |
| }), |
| }), |
| ); |
| }); |
| }); |
|
|