From 1168a7d82e0dfa706b00cf3cc00e1c987e5cee3c Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Mon, 18 Aug 2025 01:57:57 -0400 Subject: [PATCH] ci: update related tests --- api/models/Conversation.spec.js | 42 ++++---- api/models/Message.spec.js | 46 ++++----- .../controllers/PluginController.spec.js | 27 ++---- api/server/index.spec.js | 1 - api/server/routes/files/multer.spec.js | 19 ++-- api/server/routes/prompts.test.js | 1 - api/server/services/AppService.spec.js | 96 ++++++++++--------- .../services/Config/loadConfigModels.spec.js | 2 +- api/server/services/MCP.spec.js | 11 ++- api/server/services/domains.spec.js | 81 ++++++++-------- 10 files changed, 169 insertions(+), 157 deletions(-) diff --git a/api/models/Conversation.spec.js b/api/models/Conversation.spec.js index 1acdb7750c..1d047c9c1f 100644 --- a/api/models/Conversation.spec.js +++ b/api/models/Conversation.spec.js @@ -13,9 +13,9 @@ const { saveConvo, getConvo, } = require('./Conversation'); -jest.mock('~/server/services/Config/getCustomConfig'); +jest.mock('~/server/services/Config/app'); jest.mock('./Message'); -const { getCustomConfig } = require('~/server/services/Config/getCustomConfig'); +const { getAppConfig } = require('~/server/services/Config/app'); const { getMessages, deleteMessages } = require('./Message'); const { Conversation } = require('~/db/models'); @@ -118,9 +118,9 @@ describe('Conversation Operations', () => { describe('isTemporary conversation handling', () => { it('should save a conversation with expiredAt when isTemporary is true', async () => { - // Mock custom config with 24 hour retention - getCustomConfig.mockResolvedValue({ - interface: { + // Mock app config with 24 hour retention + getAppConfig.mockResolvedValue({ + interfaceConfig: { temporaryChatRetention: 24, }, }); @@ -167,9 +167,9 @@ describe('Conversation Operations', () => { }); it('should use custom retention period from config', async () => { - // Mock custom config with 48 hour retention - getCustomConfig.mockResolvedValue({ - interface: { + // Mock app config with 48 hour retention + getAppConfig.mockResolvedValue({ + interfaceConfig: { temporaryChatRetention: 48, }, }); @@ -194,9 +194,9 @@ describe('Conversation Operations', () => { }); it('should handle minimum retention period (1 hour)', async () => { - // Mock custom config with less than minimum retention - getCustomConfig.mockResolvedValue({ - interface: { + // Mock app config with less than minimum retention + getAppConfig.mockResolvedValue({ + interfaceConfig: { temporaryChatRetention: 0.5, // Half hour - should be clamped to 1 hour }, }); @@ -221,9 +221,9 @@ describe('Conversation Operations', () => { }); it('should handle maximum retention period (8760 hours)', async () => { - // Mock custom config with more than maximum retention - getCustomConfig.mockResolvedValue({ - interface: { + // Mock app config with more than maximum retention + getAppConfig.mockResolvedValue({ + interfaceConfig: { temporaryChatRetention: 10000, // Should be clamped to 8760 hours }, }); @@ -247,9 +247,9 @@ describe('Conversation Operations', () => { ); }); - it('should handle getCustomConfig errors gracefully', async () => { - // Mock getCustomConfig to throw an error - getCustomConfig.mockRejectedValue(new Error('Config service unavailable')); + it('should handle getAppConfig errors gracefully', async () => { + // Mock getAppConfig to throw an error + getAppConfig.mockRejectedValue(new Error('Config service unavailable')); mockReq.body = { isTemporary: true }; @@ -261,8 +261,8 @@ describe('Conversation Operations', () => { }); it('should use default retention when config is not provided', async () => { - // Mock getCustomConfig to return empty config - getCustomConfig.mockResolvedValue({}); + // Mock getAppConfig to return empty config + getAppConfig.mockResolvedValue({}); mockReq.body = { isTemporary: true }; @@ -285,8 +285,8 @@ describe('Conversation Operations', () => { it('should update expiredAt when saving existing temporary conversation', async () => { // First save a temporary conversation - getCustomConfig.mockResolvedValue({ - interface: { + getAppConfig.mockResolvedValue({ + interfaceConfig: { temporaryChatRetention: 24, }, }); diff --git a/api/models/Message.spec.js b/api/models/Message.spec.js index 8e954a12bd..9959a7f518 100644 --- a/api/models/Message.spec.js +++ b/api/models/Message.spec.js @@ -13,8 +13,8 @@ const { deleteMessagesSince, } = require('./Message'); -jest.mock('~/server/services/Config/getCustomConfig'); -const { getCustomConfig } = require('~/server/services/Config/getCustomConfig'); +jest.mock('~/server/services/Config/app'); +const { getAppConfig } = require('~/server/services/Config/app'); /** * @type {import('mongoose').Model} @@ -326,9 +326,9 @@ describe('Message Operations', () => { }); it('should save a message with expiredAt when isTemporary is true', async () => { - // Mock custom config with 24 hour retention - getCustomConfig.mockResolvedValue({ - interface: { + // Mock app config with 24 hour retention + getAppConfig.mockResolvedValue({ + interfaceConfig: { temporaryChatRetention: 24, }, }); @@ -375,9 +375,9 @@ describe('Message Operations', () => { }); it('should use custom retention period from config', async () => { - // Mock custom config with 48 hour retention - getCustomConfig.mockResolvedValue({ - interface: { + // Mock app config with 48 hour retention + getAppConfig.mockResolvedValue({ + interfaceConfig: { temporaryChatRetention: 48, }, }); @@ -402,9 +402,9 @@ describe('Message Operations', () => { }); it('should handle minimum retention period (1 hour)', async () => { - // Mock custom config with less than minimum retention - getCustomConfig.mockResolvedValue({ - interface: { + // Mock app config with less than minimum retention + getAppConfig.mockResolvedValue({ + interfaceConfig: { temporaryChatRetention: 0.5, // Half hour - should be clamped to 1 hour }, }); @@ -429,9 +429,9 @@ describe('Message Operations', () => { }); it('should handle maximum retention period (8760 hours)', async () => { - // Mock custom config with more than maximum retention - getCustomConfig.mockResolvedValue({ - interface: { + // Mock app config with more than maximum retention + getAppConfig.mockResolvedValue({ + interfaceConfig: { temporaryChatRetention: 10000, // Should be clamped to 8760 hours }, }); @@ -455,9 +455,9 @@ describe('Message Operations', () => { ); }); - it('should handle getCustomConfig errors gracefully', async () => { - // Mock getCustomConfig to throw an error - getCustomConfig.mockRejectedValue(new Error('Config service unavailable')); + it('should handle getAppConfig errors gracefully', async () => { + // Mock getAppConfig to throw an error + getAppConfig.mockRejectedValue(new Error('Config service unavailable')); mockReq.body = { isTemporary: true }; @@ -469,8 +469,8 @@ describe('Message Operations', () => { }); it('should use default retention when config is not provided', async () => { - // Mock getCustomConfig to return empty config - getCustomConfig.mockResolvedValue({}); + // Mock getAppConfig to return empty config + getAppConfig.mockResolvedValue({}); mockReq.body = { isTemporary: true }; @@ -493,8 +493,8 @@ describe('Message Operations', () => { it('should not update expiredAt on message update', async () => { // First save a temporary message - getCustomConfig.mockResolvedValue({ - interface: { + getAppConfig.mockResolvedValue({ + interfaceConfig: { temporaryChatRetention: 24, }, }); @@ -520,8 +520,8 @@ describe('Message Operations', () => { it('should preserve expiredAt when saving existing temporary message', async () => { // First save a temporary message - getCustomConfig.mockResolvedValue({ - interface: { + getAppConfig.mockResolvedValue({ + interfaceConfig: { temporaryChatRetention: 24, }, }); diff --git a/api/server/controllers/PluginController.spec.js b/api/server/controllers/PluginController.spec.js index a964011c83..4caedcdf3c 100644 --- a/api/server/controllers/PluginController.spec.js +++ b/api/server/controllers/PluginController.spec.js @@ -1,8 +1,7 @@ const { Constants } = require('librechat-data-provider'); -const { getCustomConfig, getCachedTools, getAppConfig } = require('~/server/services/Config'); +const { getCachedTools, getAppConfig } = require('~/server/services/Config'); const { getLogStores } = require('~/cache'); -// Mock the dependencies jest.mock('@librechat/data-schemas', () => ({ logger: { debug: jest.fn(), @@ -11,7 +10,6 @@ jest.mock('@librechat/data-schemas', () => ({ })); jest.mock('~/server/services/Config', () => ({ - getCustomConfig: jest.fn(), getCachedTools: jest.fn(), getAppConfig: jest.fn(), })); @@ -162,7 +160,6 @@ describe('PluginController', () => { getCachedTools.mockResolvedValueOnce(mockUserTools); convertMCPToolsToPlugins.mockReturnValue(mockConvertedPlugins); filterUniquePlugins.mockImplementation((plugins) => plugins); - getCustomConfig.mockResolvedValue(null); await getAvailableTools(mockReq, mockRes); @@ -184,7 +181,6 @@ describe('PluginController', () => { getCachedTools.mockResolvedValueOnce({}); convertMCPToolsToPlugins.mockReturnValue(mockUserPlugins); filterUniquePlugins.mockReturnValue([...mockUserPlugins, ...mockManifestPlugins]); - getCustomConfig.mockResolvedValue(null); await getAvailableTools(mockReq, mockRes); @@ -203,7 +199,6 @@ describe('PluginController', () => { convertMCPToolsToPlugins.mockReturnValue([]); filterUniquePlugins.mockReturnValue([mockPlugin]); checkPluginAuth.mockReturnValue(true); - getCustomConfig.mockResolvedValue(null); // Mock getCachedTools second call to return tool definitions getCachedTools.mockResolvedValueOnce({}).mockResolvedValueOnce({ tool1: true }); @@ -227,7 +222,6 @@ describe('PluginController', () => { filterUniquePlugins.mockReturnValue([mockToolkit]); checkPluginAuth.mockReturnValue(false); getToolkitKey.mockReturnValue('toolkit1'); - getCustomConfig.mockResolvedValue(null); // Mock getCachedTools second call to return tool definitions getCachedTools.mockResolvedValueOnce({}).mockResolvedValueOnce({ @@ -243,7 +237,6 @@ describe('PluginController', () => { describe('plugin.icon behavior', () => { const callGetAvailableToolsWithMCPServer = async (serverConfig) => { mockCache.get.mockResolvedValue(null); - getCustomConfig.mockResolvedValue(null); const functionTools = { [`test-tool${Constants.mcp_delimiter}test-server`]: { @@ -299,8 +292,8 @@ describe('PluginController', () => { describe('helper function integration', () => { it('should properly handle MCP tools with custom user variables', async () => { - const customConfig = { - mcpServers: { + const appConfig = { + mcpConfig: { 'test-server': { customUserVars: { API_KEY: { title: 'API Key', description: 'Your API key' }, @@ -327,7 +320,7 @@ describe('PluginController', () => { require('~/config').getMCPManager.mockReturnValue(mockMCPManager); mockCache.get.mockResolvedValue(null); - getCustomConfig.mockResolvedValue(customConfig); + getAppConfig.mockResolvedValue(appConfig); // First call returns user tools (empty in this case) getCachedTools.mockResolvedValueOnce({}); @@ -387,7 +380,6 @@ describe('PluginController', () => { getCachedTools.mockResolvedValue(null); convertMCPToolsToPlugins.mockReturnValue(undefined); filterUniquePlugins.mockImplementation((plugins) => plugins || []); - getCustomConfig.mockResolvedValue(null); await getAvailableTools(mockReq, mockRes); @@ -402,7 +394,6 @@ describe('PluginController', () => { getCachedTools.mockResolvedValue(undefined); convertMCPToolsToPlugins.mockReturnValue(undefined); filterUniquePlugins.mockImplementation((plugins) => plugins || []); - getCustomConfig.mockResolvedValue(null); checkPluginAuth.mockReturnValue(false); // Mock getCachedTools to return undefined for both calls @@ -440,7 +431,6 @@ describe('PluginController', () => { getCachedTools.mockResolvedValueOnce({}).mockResolvedValueOnce({}); convertMCPToolsToPlugins.mockReturnValue([]); filterUniquePlugins.mockImplementation((plugins) => plugins || []); - getCustomConfig.mockResolvedValue(null); checkPluginAuth.mockReturnValue(true); await getAvailableTools(mockReq, mockRes); @@ -467,12 +457,16 @@ describe('PluginController', () => { // Mock the MCP manager to return server config without customUserVars const mockMCPManager = { loadManifestTools: jest.fn().mockResolvedValue([]), - getRawConfig: jest.fn(), + getRawConfig: jest.fn().mockReturnValue(customConfig.mcpServers['test-server']), }; require('~/config').getMCPManager.mockReturnValue(mockMCPManager); mockCache.get.mockResolvedValue(null); - getCustomConfig.mockResolvedValue(customConfig); + getAppConfig.mockResolvedValue({ + mcpConfig: customConfig.mcpServers, + filteredTools: [], + includedTools: [], + }); getCachedTools.mockResolvedValueOnce(mockUserTools); const mockPlugin = { @@ -525,7 +519,6 @@ describe('PluginController', () => { filterUniquePlugins.mockReturnValue([mockToolkit]); checkPluginAuth.mockReturnValue(false); getToolkitKey.mockReturnValue(undefined); - getCustomConfig.mockResolvedValue(null); // Mock getCachedTools second call to return null getCachedTools.mockResolvedValueOnce({}).mockResolvedValueOnce(null); diff --git a/api/server/index.spec.js b/api/server/index.spec.js index 141ce24596..2a5ae3d5a3 100644 --- a/api/server/index.spec.js +++ b/api/server/index.spec.js @@ -5,7 +5,6 @@ const mongoose = require('mongoose'); jest.mock('~/server/services/Config', () => ({ loadCustomConfig: jest.fn(() => Promise.resolve({})), - getCustomConfig: jest.fn(() => Promise.resolve({})), setAppConfig: jest.fn(), getAppConfig: jest.fn().mockResolvedValue({ paths: { diff --git a/api/server/routes/files/multer.spec.js b/api/server/routes/files/multer.spec.js index a5056dc4a2..6f48c014e0 100644 --- a/api/server/routes/files/multer.spec.js +++ b/api/server/routes/files/multer.spec.js @@ -338,11 +338,11 @@ describe('Multer Configuration', () => { }); it('should use real config merging', async () => { - const { getCustomConfig } = require('~/server/services/Config'); + const { getAppConfig } = require('~/server/services/Config'); const multerInstance = await createMulterInstance(); - expect(getCustomConfig).toHaveBeenCalled(); + expect(getAppConfig).toHaveBeenCalled(); expect(multerInstance).toBeDefined(); }); @@ -557,25 +557,28 @@ describe('Multer Configuration', () => { }); it('should properly integrate real fileConfig with custom endpoints', async () => { - const { getCustomConfig } = require('~/server/services/Config'); + const { getAppConfig } = require('~/server/services/Config'); - // Mock a custom config with additional endpoints - getCustomConfig.mockResolvedValueOnce({ + // Mock appConfig with fileConfig + getAppConfig.mockResolvedValueOnce({ + paths: { + uploads: tempDir, + }, fileConfig: { endpoints: { anthropic: { supportedMimeTypes: ['text/plain', 'image/png'], }, }, - serverFileSizeLimit: 20, // 20 MB + serverFileSizeLimit: 20971520, // 20 MB in bytes (mergeFileConfig converts) }, }); const multerInstance = await createMulterInstance(); expect(multerInstance).toBeDefined(); - // Verify that getCustomConfig was called (we can't spy on the actual merge function easily) - expect(getCustomConfig).toHaveBeenCalled(); + // Verify that getAppConfig was called + expect(getAppConfig).toHaveBeenCalled(); }); }); }); diff --git a/api/server/routes/prompts.test.js b/api/server/routes/prompts.test.js index 530d5d67fa..29b806f620 100644 --- a/api/server/routes/prompts.test.js +++ b/api/server/routes/prompts.test.js @@ -14,7 +14,6 @@ const { // Mock modules before importing jest.mock('~/server/services/Config', () => ({ getCachedTools: jest.fn().mockResolvedValue({}), - getCustomConfig: jest.fn(), })); jest.mock('~/models/Role', () => ({ diff --git a/api/server/services/AppService.spec.js b/api/server/services/AppService.spec.js index b53c04cf0e..f50101ffeb 100644 --- a/api/server/services/AppService.spec.js +++ b/api/server/services/AppService.spec.js @@ -134,49 +134,53 @@ describe('AppService', () => { expect(process.env.CDN_PROVIDER).toEqual('testStrategy'); - expect(setAppConfig).toHaveBeenCalledWith({ - config: expect.objectContaining({ + expect(setAppConfig).toHaveBeenCalledWith( + 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, + turnstileConfig: mockedTurnstileConfig, + modelSpecs: undefined, + paths: expect.anything(), + ocr: expect.anything(), + imageOutputType: expect.any(String), + fileConfig: undefined, + secureImageLinks: undefined, + balance: { enabled: true }, + filteredTools: undefined, + includedTools: undefined, + webSearch: expect.objectContaining({ + safeSearch: 1, + jinaApiKey: '${JINA_API_KEY}', + 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, + agents: expect.objectContaining({ + disableBuilder: false, + capabilities: expect.arrayContaining([...defaultAgentCapabilities]), + maxCitations: 30, + maxCitationsPerFile: 7, + minRelevanceScore: 0.45, + }), }), - socialLogins: ['testLogin'], - fileStrategy: 'testStrategy', - interfaceConfig: expect.objectContaining({ - endpointsMenu: true, - modelSelect: true, - parameters: true, - sidePanel: true, - presets: true, - }), - mcpConfig: null, - turnstileConfig: mockedTurnstileConfig, - modelSpecs: undefined, - paths: expect.anything(), - ocr: expect.anything(), - imageOutputType: expect.any(String), - fileConfig: undefined, - secureImageLinks: undefined, - balance: { enabled: true }, - filteredTools: undefined, - includedTools: undefined, - webSearch: { - safeSearch: 1, - jinaApiKey: '${JINA_API_KEY}', - 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, - agents: { - disableBuilder: false, - capabilities: expect.arrayContaining([...defaultAgentCapabilities]), - maxCitations: 30, - maxCitationsPerFile: 7, - minRelevanceScore: 0.45, - }, - }); + ); }); it('should log a warning if the config version is outdated', async () => { @@ -261,6 +265,8 @@ describe('AppService', () => { adminFilter: undefined, adminIncluded: undefined, directory: expect.anything(), + imageOutputType: expect.any(String), + fileStrategy: expect.any(String), }); // Verify setCachedTools was called with the tools @@ -818,7 +824,9 @@ describe('AppService updating app config and issuing warnings', () => { paths: expect.anything(), config: {}, fileStrategy: FileSources.local, - socialLogins: defaultSocialLogins, + registration: expect.objectContaining({ + socialLogins: defaultSocialLogins, + }), balance: expect.objectContaining({ enabled: false, startBalance: undefined, @@ -850,7 +858,9 @@ describe('AppService updating app config and issuing warnings', () => { paths: expect.anything(), config: customConfig, fileStrategy: customConfig.fileStrategy, - socialLogins: customConfig.registration.socialLogins, + registration: expect.objectContaining({ + socialLogins: customConfig.registration.socialLogins, + }), balance: customConfig.balance, }), ); diff --git a/api/server/services/Config/loadConfigModels.spec.js b/api/server/services/Config/loadConfigModels.spec.js index 4bdd313743..fadefcf0b3 100644 --- a/api/server/services/Config/loadConfigModels.spec.js +++ b/api/server/services/Config/loadConfigModels.spec.js @@ -5,7 +5,7 @@ const { getAppConfig } = require('./app'); jest.mock('~/server/services/ModelService'); jest.mock('./getCustomConfig'); -jest.mock('./getAppConfig'); +jest.mock('./app'); const exampleConfig = { endpoints: { diff --git a/api/server/services/MCP.spec.js b/api/server/services/MCP.spec.js index 8c81abd685..3751c8a888 100644 --- a/api/server/services/MCP.spec.js +++ b/api/server/services/MCP.spec.js @@ -25,6 +25,7 @@ jest.mock('librechat-data-provider', () => ({ jest.mock('./Config', () => ({ loadCustomConfig: jest.fn(), + getAppConfig: jest.fn(), })); jest.mock('~/config', () => ({ @@ -65,8 +66,10 @@ describe('tests for the new helper functions used by the MCP connection status e server2: { type: 'http' }, }, }; + let mockGetAppConfig; beforeEach(() => { + mockGetAppConfig = require('./Config').getAppConfig; mockGetMCPManager.mockReturnValue({ getAllConnections: jest.fn(() => new Map()), getUserConnections: jest.fn(() => new Map()), @@ -75,7 +78,7 @@ describe('tests for the new helper functions used by the MCP connection status e }); it('should successfully return MCP setup data', async () => { - mockLoadCustomConfig.mockResolvedValue(mockConfig); + mockGetAppConfig.mockResolvedValue({ mcpConfig: mockConfig.mcpServers }); const mockAppConnections = new Map([['server1', { status: 'connected' }]]); const mockUserConnections = new Map([['server2', { status: 'disconnected' }]]); @@ -90,7 +93,7 @@ describe('tests for the new helper functions used by the MCP connection status e const result = await getMCPSetupData(mockUserId); - expect(mockLoadCustomConfig).toHaveBeenCalledWith(false); + expect(mockGetAppConfig).toHaveBeenCalled(); expect(mockGetMCPManager).toHaveBeenCalledWith(mockUserId); expect(mockMCPManager.getAllConnections).toHaveBeenCalled(); expect(mockMCPManager.getUserConnections).toHaveBeenCalledWith(mockUserId); @@ -105,12 +108,12 @@ describe('tests for the new helper functions used by the MCP connection status e }); it('should throw error when MCP config not found', async () => { - mockLoadCustomConfig.mockResolvedValue({}); + mockGetAppConfig.mockResolvedValue({}); await expect(getMCPSetupData(mockUserId)).rejects.toThrow('MCP config not found'); }); it('should handle null values from MCP manager gracefully', async () => { - mockLoadCustomConfig.mockResolvedValue(mockConfig); + mockGetAppConfig.mockResolvedValue({ mcpConfig: mockConfig.mcpServers }); const mockMCPManager = { getAllConnections: jest.fn(() => null), diff --git a/api/server/services/domains.spec.js b/api/server/services/domains.spec.js index b4537dd375..b6086174a7 100644 --- a/api/server/services/domains.spec.js +++ b/api/server/services/domains.spec.js @@ -25,14 +25,14 @@ describe('isEmailDomainAllowed', () => { it('should return true if customConfig is not available', async () => { const email = 'test@domain1.com'; getCustomConfig.mockResolvedValue(null); - const result = await isEmailDomainAllowed(email); + const result = await isEmailDomainAllowed(email, null); expect(result).toBe(true); }); it('should return true if allowedDomains is not defined in customConfig', async () => { const email = 'test@domain1.com'; getCustomConfig.mockResolvedValue({}); - const result = await isEmailDomainAllowed(email); + const result = await isEmailDomainAllowed(email, undefined); expect(result).toBe(true); }); @@ -43,7 +43,7 @@ describe('isEmailDomainAllowed', () => { allowedDomains: ['domain1.com', 'domain2.com'], }, }); - const result = await isEmailDomainAllowed(email); + const result = await isEmailDomainAllowed(email, ['domain1.com', 'domain2.com']); expect(result).toBe(true); }); @@ -54,7 +54,7 @@ describe('isEmailDomainAllowed', () => { allowedDomains: ['domain1.com', 'domain2.com'], }, }); - const result = await isEmailDomainAllowed(email); + const result = await isEmailDomainAllowed(email, ['domain1.com', 'domain2.com']); expect(result).toBe(false); }); }); @@ -83,8 +83,8 @@ describe('isActionDomainAllowed', () => { getCustomConfig.mockResolvedValue({ actions: { allowedDomains: ['http://', 'https://'] }, }); - expect(await isActionDomainAllowed('http://')).toBe(false); - expect(await isActionDomainAllowed('https://')).toBe(false); + expect(await isActionDomainAllowed('http://', ['http://', 'https://'])).toBe(false); + expect(await isActionDomainAllowed('https://', ['http://', 'https://'])).toBe(false); }); }); @@ -92,102 +92,107 @@ describe('isActionDomainAllowed', () => { describe('configuration handling', () => { it('should return true if customConfig is null', async () => { getCustomConfig.mockResolvedValue(null); - expect(await isActionDomainAllowed('example.com')).toBe(true); + expect(await isActionDomainAllowed('example.com', null)).toBe(true); }); it('should return true if actions.allowedDomains is not defined', async () => { getCustomConfig.mockResolvedValue({}); - expect(await isActionDomainAllowed('example.com')).toBe(true); + expect(await isActionDomainAllowed('example.com', undefined)).toBe(true); }); it('should return true if allowedDomains is empty array', async () => { getCustomConfig.mockResolvedValue({ actions: { allowedDomains: [] }, }); - expect(await isActionDomainAllowed('example.com')).toBe(true); + expect(await isActionDomainAllowed('example.com', [])).toBe(true); }); }); // Domain Matching Tests describe('domain matching', () => { + const allowedDomains = [ + 'example.com', + '*.subdomain.com', + 'specific.domain.com', + 'www.withprefix.com', + 'swapi.dev', + ]; + beforeEach(() => { getCustomConfig.mockResolvedValue({ actions: { - allowedDomains: [ - 'example.com', - '*.subdomain.com', - 'specific.domain.com', - 'www.withprefix.com', - 'swapi.dev', - ], + allowedDomains, }, }); }); it('should match exact domains', async () => { - expect(await isActionDomainAllowed('example.com')).toBe(true); - expect(await isActionDomainAllowed('other.com')).toBe(false); - expect(await isActionDomainAllowed('swapi.dev')).toBe(true); + expect(await isActionDomainAllowed('example.com', allowedDomains)).toBe(true); + expect(await isActionDomainAllowed('other.com', allowedDomains)).toBe(false); + expect(await isActionDomainAllowed('swapi.dev', allowedDomains)).toBe(true); }); it('should handle domains with www prefix', async () => { - expect(await isActionDomainAllowed('www.example.com')).toBe(true); - expect(await isActionDomainAllowed('www.withprefix.com')).toBe(true); + expect(await isActionDomainAllowed('www.example.com', allowedDomains)).toBe(true); + expect(await isActionDomainAllowed('www.withprefix.com', allowedDomains)).toBe(true); }); it('should handle full URLs', async () => { - expect(await isActionDomainAllowed('https://example.com')).toBe(true); - expect(await isActionDomainAllowed('http://example.com')).toBe(true); - expect(await isActionDomainAllowed('https://example.com/path')).toBe(true); + expect(await isActionDomainAllowed('https://example.com', allowedDomains)).toBe(true); + expect(await isActionDomainAllowed('http://example.com', allowedDomains)).toBe(true); + expect(await isActionDomainAllowed('https://example.com/path', allowedDomains)).toBe(true); }); it('should handle wildcard subdomains', async () => { - expect(await isActionDomainAllowed('test.subdomain.com')).toBe(true); - expect(await isActionDomainAllowed('any.subdomain.com')).toBe(true); - expect(await isActionDomainAllowed('subdomain.com')).toBe(true); + expect(await isActionDomainAllowed('test.subdomain.com', allowedDomains)).toBe(true); + expect(await isActionDomainAllowed('any.subdomain.com', allowedDomains)).toBe(true); + expect(await isActionDomainAllowed('subdomain.com', allowedDomains)).toBe(true); }); it('should handle specific subdomains', async () => { - expect(await isActionDomainAllowed('specific.domain.com')).toBe(true); - expect(await isActionDomainAllowed('other.domain.com')).toBe(false); + expect(await isActionDomainAllowed('specific.domain.com', allowedDomains)).toBe(true); + expect(await isActionDomainAllowed('other.domain.com', allowedDomains)).toBe(false); }); }); // Edge Cases describe('edge cases', () => { + const edgeAllowedDomains = ['example.com', '*.test.com']; + beforeEach(() => { getCustomConfig.mockResolvedValue({ actions: { - allowedDomains: ['example.com', '*.test.com'], + allowedDomains: edgeAllowedDomains, }, }); }); it('should handle domains with query parameters', async () => { - expect(await isActionDomainAllowed('example.com?param=value')).toBe(true); + expect(await isActionDomainAllowed('example.com?param=value', edgeAllowedDomains)).toBe(true); }); it('should handle domains with ports', async () => { - expect(await isActionDomainAllowed('example.com:8080')).toBe(true); + expect(await isActionDomainAllowed('example.com:8080', edgeAllowedDomains)).toBe(true); }); it('should handle domains with trailing slashes', async () => { - expect(await isActionDomainAllowed('example.com/')).toBe(true); + expect(await isActionDomainAllowed('example.com/', edgeAllowedDomains)).toBe(true); }); it('should handle case insensitivity', async () => { - expect(await isActionDomainAllowed('EXAMPLE.COM')).toBe(true); - expect(await isActionDomainAllowed('Example.Com')).toBe(true); + expect(await isActionDomainAllowed('EXAMPLE.COM', edgeAllowedDomains)).toBe(true); + expect(await isActionDomainAllowed('Example.Com', edgeAllowedDomains)).toBe(true); }); it('should handle invalid entries in allowedDomains', async () => { + const invalidAllowedDomains = ['example.com', null, undefined, '', 'test.com']; getCustomConfig.mockResolvedValue({ actions: { - allowedDomains: ['example.com', null, undefined, '', 'test.com'], + allowedDomains: invalidAllowedDomains, }, }); - expect(await isActionDomainAllowed('example.com')).toBe(true); - expect(await isActionDomainAllowed('test.com')).toBe(true); + expect(await isActionDomainAllowed('example.com', invalidAllowedDomains)).toBe(true); + expect(await isActionDomainAllowed('test.com', invalidAllowedDomains)).toBe(true); }); }); });