LibreChat/api/server/services/start/checks.spec.js

204 lines
6 KiB
JavaScript
Raw Normal View History

🧩 feat: Web Search Config Validations & Clipboard Citation Processing (#7530) * 🔧 chore: Add missing optional `scraperTimeout` to webSearchSchema * chore: Add missing optional `scraperTimeout` to web search authentication result * chore: linting * feat: Integrate attachment handling and citation processing in message components - Added `useAttachments` hook to manage message attachments and search results. - Updated `MessageParts`, `ContentParts`, and `ContentRender` components to utilize the new hook for improved attachment handling. - Enhanced `useCopyToClipboard` to format citations correctly, including support for composite citations and deduplication. - Introduced utility functions for citation processing and cleanup. - Added tests for the new `useCopyToClipboard` functionality to ensure proper citation formatting and handling. * feat: Add configuration for LibreChat Code Interpreter API and Web Search variables * fix: Update searchResults type to use SearchResultData for better type safety * feat: Add web search configuration validation and logging - Introduced `checkWebSearchConfig` function to validate web search configuration values, ensuring they are environment variable references. - Added logging for proper configuration and warnings for incorrect values. - Created unit tests for `checkWebSearchConfig` to cover various scenarios, including valid and invalid configurations. * docs: Update README to include Web Search feature details - Added a section for the Web Search feature, highlighting its capabilities to search the internet and enhance AI context. - Included links for further information on the Web Search functionality. * ci: Add mock for checkWebSearchConfig in AppService tests * chore: linting * feat: Enhance Shared Messages with Web Search UI by adding searchResults prop to SearchContent and MinimalHoverButtons components * chore: linting * refactor: remove Meilisearch index sync from importConversations function * feat: update safeSearch implementation to use SafeSearchTypes enum * refactor: remove commented-out code in loadTools function * fix: ensure responseMessageId handles latestMessage ID correctly * feat: enhance Vite configuration for improved chunking and caching - Added additional globIgnores for map files in Workbox configuration. - Implemented high-impact chunking for various large libraries to optimize performance. - Increased chunkSizeWarningLimit from 1200 to 1500 for better handling of larger chunks. * refactor: move health check hook to Root, fix bad setState for Temporary state - Enhanced the `useHealthCheck` hook to initiate health checks only when the user is authenticated. - Added logic for managing health check intervals and handling window focus events. - Introduced a new test suite for `useHealthCheck` to cover various scenarios including authentication state changes and error handling. - Removed the health check invocation from `ChatRoute` and added it to `Root` for global health monitoring. * fix: update font alias in Vite configuration for correct path resolution
2025-05-24 10:23:17 -04:00
// Mock librechat-data-provider
jest.mock('librechat-data-provider', () => ({
...jest.requireActual('librechat-data-provider'),
extractVariableName: jest.fn(),
}));
// Mock the config logger
jest.mock('~/config', () => ({
logger: {
debug: jest.fn(),
warn: jest.fn(),
},
}));
const { checkWebSearchConfig } = require('./checks');
const { logger } = require('~/config');
const { extractVariableName } = require('librechat-data-provider');
describe('checkWebSearchConfig', () => {
let originalEnv;
beforeEach(() => {
// Clear all mocks
jest.clearAllMocks();
// Store original environment
originalEnv = process.env;
// Reset process.env
process.env = { ...originalEnv };
});
afterEach(() => {
// Restore original environment
process.env = originalEnv;
});
describe('when webSearchConfig is undefined or null', () => {
it('should return early without logging when config is undefined', () => {
checkWebSearchConfig(undefined);
expect(logger.debug).not.toHaveBeenCalled();
expect(logger.warn).not.toHaveBeenCalled();
});
it('should return early without logging when config is null', () => {
checkWebSearchConfig(null);
expect(logger.debug).not.toHaveBeenCalled();
expect(logger.warn).not.toHaveBeenCalled();
});
});
describe('when config values are proper environment variable references', () => {
it('should log debug message for each valid environment variable with value set', () => {
const config = {
serperApiKey: '${SERPER_API_KEY}',
jinaApiKey: '${JINA_API_KEY}',
};
extractVariableName.mockReturnValueOnce('SERPER_API_KEY').mockReturnValueOnce('JINA_API_KEY');
process.env.SERPER_API_KEY = 'test-serper-key';
process.env.JINA_API_KEY = 'test-jina-key';
checkWebSearchConfig(config);
expect(extractVariableName).toHaveBeenCalledWith('${SERPER_API_KEY}');
expect(extractVariableName).toHaveBeenCalledWith('${JINA_API_KEY}');
expect(logger.debug).toHaveBeenCalledWith(
'Web search serperApiKey: Using environment variable SERPER_API_KEY with value set',
);
expect(logger.debug).toHaveBeenCalledWith(
'Web search jinaApiKey: Using environment variable JINA_API_KEY with value set',
);
expect(logger.warn).not.toHaveBeenCalled();
});
it('should log debug message for environment variables not set in environment', () => {
const config = {
cohereApiKey: '${COHERE_API_KEY}',
};
extractVariableName.mockReturnValue('COHERE_API_KEY');
delete process.env.COHERE_API_KEY;
checkWebSearchConfig(config);
expect(logger.debug).toHaveBeenCalledWith(
'Web search cohereApiKey: Using environment variable COHERE_API_KEY (not set in environment, user provided value)',
);
expect(logger.warn).not.toHaveBeenCalled();
});
});
describe('when config values are actual values instead of environment variable references', () => {
it('should warn when serperApiKey contains actual API key', () => {
const config = {
serperApiKey: 'sk-1234567890abcdef',
};
extractVariableName.mockReturnValue(null);
checkWebSearchConfig(config);
expect(logger.warn).toHaveBeenCalledWith(
expect.stringContaining(
'❗ Web search configuration error: serperApiKey contains an actual value',
),
);
expect(logger.warn).toHaveBeenCalledWith(
expect.stringContaining('Current value: "sk-1234567..."'),
);
expect(logger.debug).not.toHaveBeenCalled();
});
it('should warn when firecrawlApiUrl contains actual URL', () => {
const config = {
firecrawlApiUrl: 'https://api.firecrawl.dev',
};
extractVariableName.mockReturnValue(null);
checkWebSearchConfig(config);
expect(logger.warn).toHaveBeenCalledWith(
expect.stringContaining(
'❗ Web search configuration error: firecrawlApiUrl contains an actual value',
),
);
expect(logger.warn).toHaveBeenCalledWith(
expect.stringContaining('Current value: "https://ap..."'),
);
});
it('should include documentation link in warning message', () => {
const config = {
firecrawlApiKey: 'fc-actual-key',
};
extractVariableName.mockReturnValue(null);
checkWebSearchConfig(config);
expect(logger.warn).toHaveBeenCalledWith(
expect.stringContaining(
'More info: https://www.librechat.ai/docs/configuration/librechat_yaml/web_search',
),
);
});
});
describe('when config contains mixed value types', () => {
it('should only process string values and ignore non-string values', () => {
const config = {
serperApiKey: '${SERPER_API_KEY}',
safeSearch: 1,
scraperTimeout: 7500,
jinaApiKey: 'actual-key',
};
extractVariableName.mockReturnValueOnce('SERPER_API_KEY').mockReturnValueOnce(null);
process.env.SERPER_API_KEY = 'test-key';
checkWebSearchConfig(config);
expect(extractVariableName).toHaveBeenCalledTimes(2);
expect(logger.debug).toHaveBeenCalledTimes(1);
expect(logger.warn).toHaveBeenCalledTimes(1);
});
});
describe('edge cases', () => {
it('should handle config with no web search keys', () => {
const config = {
someOtherKey: 'value',
anotherKey: '${SOME_VAR}',
};
checkWebSearchConfig(config);
expect(extractVariableName).not.toHaveBeenCalled();
expect(logger.debug).not.toHaveBeenCalled();
expect(logger.warn).not.toHaveBeenCalled();
});
it('should truncate long values in warning messages', () => {
const config = {
serperApiKey: 'this-is-a-very-long-api-key-that-should-be-truncated-in-the-warning-message',
};
extractVariableName.mockReturnValue(null);
checkWebSearchConfig(config);
expect(logger.warn).toHaveBeenCalledWith(
expect.stringContaining('Current value: "this-is-a-..."'),
);
});
});
});