jest.mock('~/cache/getLogStores'); const mockGetAppConfig = jest.fn(); jest.mock('~/server/services/Config/app', () => ({ getAppConfig: (...args) => mockGetAppConfig(...args), })); jest.mock('~/server/services/Config/ldap', () => ({ getLdapConfig: jest.fn(() => null), })); jest.mock('~/server/middleware/roles/capabilities', () => ({ hasCapability: jest.fn(), })); jest.mock('@librechat/data-schemas', () => ({ ...jest.requireActual('@librechat/data-schemas'), getTenantId: jest.fn(() => undefined), })); jest.mock('@librechat/api', () => ({ ...jest.requireActual('@librechat/api'), getCloudFrontConfig: jest.fn(() => null), })); const request = require('supertest'); const express = require('express'); const configRoute = require('../config'); function createApp(user) { const app = express(); app.disable('x-powered-by'); if (user) { app.use((req, _res, next) => { req.user = user; next(); }); } app.use('/api/config', configRoute); return app; } const baseAppConfig = { registration: { socialLogins: ['google', 'github'] }, interfaceConfig: { modelSelect: true }, turnstileConfig: { siteKey: 'test-key' }, modelSpecs: { list: [{ name: 'test-spec' }] }, }; const mockUser = { id: 'user123', role: 'USER', tenantId: undefined, }; afterEach(() => { jest.resetAllMocks(); delete process.env.RUM_ENABLED; delete process.env.RUM_PROVIDER; delete process.env.RUM_URL; delete process.env.RUM_PROXY_TARGET_URL; delete process.env.RUM_SERVICE_NAME; delete process.env.RUM_AUTH_MODE; delete process.env.RUM_PUBLIC_TOKEN; delete process.env.RUM_TRACE_PROPAGATION_TARGETS; delete process.env.RUM_CONSOLE_CAPTURE; delete process.env.RUM_DISABLE_REPLAY; delete process.env.RUM_ADVANCED_NETWORK_CAPTURE; delete process.env.RUM_SAMPLE_RATE; delete process.env.RUM_ENVIRONMENT; }); describe('GET /api/config RUM config', () => { it('includes public-token RUM config when enabled with valid env', async () => { mockGetAppConfig.mockResolvedValue(baseAppConfig); process.env.RUM_ENABLED = 'true'; process.env.RUM_URL = 'https://rum.example.com'; process.env.RUM_PUBLIC_TOKEN = 'public-token'; process.env.RUM_TRACE_PROPAGATION_TARGETS = 'https://app.example.com,https://api.openai.com,*,http://api.example.com'; process.env.RUM_SAMPLE_RATE = '0.25'; process.env.RUM_ENVIRONMENT = 'test'; const app = createApp(null); const response = await request(app).get('/api/config'); expect(response.body.rum).toEqual({ provider: 'hyperdx', enabled: true, url: 'https://rum.example.com', serviceName: 'librechat-web', authMode: 'publicToken', publicToken: 'public-token', tracePropagationTargets: ['https://app.example.com', 'https://api.openai.com'], consoleCapture: false, disableReplay: true, advancedNetworkCapture: false, sampleRate: 0.25, environment: 'test', }); }); it('omits malformed RUM config', async () => { mockGetAppConfig.mockResolvedValue(baseAppConfig); process.env.RUM_ENABLED = 'true'; process.env.RUM_URL = 'not a url'; process.env.RUM_PUBLIC_TOKEN = 'public-token'; const app = createApp(null); const response = await request(app).get('/api/config'); expect(response.body).not.toHaveProperty('rum'); }); it('includes proxy RUM config when enabled with valid env', async () => { mockGetAppConfig.mockResolvedValue(baseAppConfig); process.env.RUM_ENABLED = 'true'; process.env.RUM_AUTH_MODE = 'proxy'; process.env.RUM_PROXY_TARGET_URL = 'http://otel-collector:4318'; const app = createApp(mockUser); const response = await request(app).get('/api/config'); expect(response.body.rum).toEqual({ provider: 'hyperdx', enabled: true, url: '/api/rum', serviceName: 'librechat-web', authMode: 'proxy', consoleCapture: false, disableReplay: true, advancedNetworkCapture: false, }); }); it('omits proxy RUM config without a target collector URL', async () => { mockGetAppConfig.mockResolvedValue(baseAppConfig); process.env.RUM_ENABLED = 'true'; process.env.RUM_AUTH_MODE = 'proxy'; const app = createApp(mockUser); const response = await request(app).get('/api/config'); expect(response.body).not.toHaveProperty('rum'); }); it('omits RUM config when the URL contains credentials', async () => { mockGetAppConfig.mockResolvedValue(baseAppConfig); process.env.RUM_ENABLED = 'true'; process.env.RUM_URL = 'https://user:password@rum.example.com'; process.env.RUM_PUBLIC_TOKEN = 'public-token'; const app = createApp(null); const response = await request(app).get('/api/config'); expect(response.body).not.toHaveProperty('rum'); }); it('allows IPv6 localhost HTTP RUM URLs in public-token mode', async () => { mockGetAppConfig.mockResolvedValue(baseAppConfig); process.env.RUM_ENABLED = 'true'; process.env.RUM_URL = 'http://[::1]:4318'; process.env.RUM_PUBLIC_TOKEN = 'public-token'; const app = createApp(null); const response = await request(app).get('/api/config'); expect(response.body.rum?.url).toBe('http://[::1]:4318'); }); it('omits unsupported userJwt RUM config for authenticated users', async () => { mockGetAppConfig.mockResolvedValue(baseAppConfig); process.env.RUM_ENABLED = 'true'; process.env.RUM_URL = 'https://rum.example.com'; process.env.RUM_AUTH_MODE = 'userJwt'; const app = createApp(mockUser); const response = await request(app).get('/api/config'); expect(response.body).not.toHaveProperty('rum'); }); it('omits unsupported userJwt RUM config for unauthenticated users', async () => { mockGetAppConfig.mockResolvedValue(baseAppConfig); process.env.RUM_ENABLED = 'true'; process.env.RUM_URL = 'https://rum.example.com'; process.env.RUM_AUTH_MODE = 'userJwt'; const app = createApp(null); const response = await request(app).get('/api/config'); expect(response.body).not.toHaveProperty('rum'); }); });