LibreChat/packages/api/src/app/config.test.ts
Sebastien Bruel e95e0052da
🗄️ feat: Allow Skipping Transactions When Balance is Disabled (#9419)
* Disable transaction creation when balance is disabled

* Add configuration to disable transactions creation

* chore: remove comments

---------

Co-authored-by: Danny Avila <danacordially@gmail.com>
2025-09-05 11:21:02 -04:00

285 lines
9.5 KiB
TypeScript

import { getTransactionsConfig, getBalanceConfig } from './config';
import { logger } from '@librechat/data-schemas';
import { FileSources } from 'librechat-data-provider';
import type { AppConfig } from '~/types';
import type { TCustomConfig } from 'librechat-data-provider';
// Helper function to create a minimal AppConfig for testing
const createTestAppConfig = (overrides: Partial<AppConfig> = {}): AppConfig => {
const minimalConfig: TCustomConfig = {
version: '1.0.0',
cache: true,
interface: {
endpointsMenu: true,
},
registration: {
socialLogins: [],
},
endpoints: {},
};
return {
config: minimalConfig,
paths: {
uploads: '',
imageOutput: '',
publicPath: '',
},
fileStrategy: FileSources.local,
fileStrategies: {},
imageOutputType: 'png',
...overrides,
};
};
jest.mock('@librechat/data-schemas', () => ({
logger: {
warn: jest.fn(),
},
}));
jest.mock('~/utils', () => ({
isEnabled: jest.fn((value) => value === 'true'),
normalizeEndpointName: jest.fn((name) => name),
}));
describe('getTransactionsConfig', () => {
beforeEach(() => {
jest.clearAllMocks();
delete process.env.CHECK_BALANCE;
delete process.env.START_BALANCE;
});
describe('when appConfig is not provided', () => {
it('should return default config with enabled: true', () => {
const result = getTransactionsConfig();
expect(result).toEqual({ enabled: true });
});
});
describe('when appConfig is provided', () => {
it('should return transactions config when explicitly set to false', () => {
const appConfig = createTestAppConfig({
transactions: { enabled: false },
balance: { enabled: false },
});
const result = getTransactionsConfig(appConfig);
expect(result).toEqual({ enabled: false });
expect(logger.warn).not.toHaveBeenCalled();
});
it('should return transactions config when explicitly set to true', () => {
const appConfig = createTestAppConfig({
transactions: { enabled: true },
balance: { enabled: false },
});
const result = getTransactionsConfig(appConfig);
expect(result).toEqual({ enabled: true });
expect(logger.warn).not.toHaveBeenCalled();
});
it('should return default config when transactions is not defined', () => {
const appConfig = createTestAppConfig({
balance: { enabled: false },
});
const result = getTransactionsConfig(appConfig);
expect(result).toEqual({ enabled: true });
expect(logger.warn).not.toHaveBeenCalled();
});
describe('balance and transactions interaction', () => {
it('should force transactions to be enabled when balance is enabled but transactions is disabled', () => {
const appConfig = createTestAppConfig({
transactions: { enabled: false },
balance: { enabled: true },
});
const result = getTransactionsConfig(appConfig);
expect(result).toEqual({ enabled: true });
expect(logger.warn).toHaveBeenCalledWith(
'Configuration warning: transactions.enabled=false is incompatible with balance.enabled=true. ' +
'Transactions will be enabled to ensure balance tracking works correctly.',
);
});
it('should not override transactions when balance is enabled and transactions is enabled', () => {
const appConfig = createTestAppConfig({
transactions: { enabled: true },
balance: { enabled: true },
});
const result = getTransactionsConfig(appConfig);
expect(result).toEqual({ enabled: true });
expect(logger.warn).not.toHaveBeenCalled();
});
it('should allow transactions to be disabled when balance is disabled', () => {
const appConfig = createTestAppConfig({
transactions: { enabled: false },
balance: { enabled: false },
});
const result = getTransactionsConfig(appConfig);
expect(result).toEqual({ enabled: false });
expect(logger.warn).not.toHaveBeenCalled();
});
it('should use default when balance is enabled but transactions is not defined', () => {
const appConfig = createTestAppConfig({
balance: { enabled: true },
});
const result = getTransactionsConfig(appConfig);
expect(result).toEqual({ enabled: true });
expect(logger.warn).not.toHaveBeenCalled();
});
});
describe('with environment variables for balance', () => {
it('should force transactions enabled when CHECK_BALANCE env is true and transactions is false', () => {
process.env.CHECK_BALANCE = 'true';
const appConfig = createTestAppConfig({
transactions: { enabled: false },
});
const result = getTransactionsConfig(appConfig);
expect(result).toEqual({ enabled: true });
expect(logger.warn).toHaveBeenCalledWith(
'Configuration warning: transactions.enabled=false is incompatible with balance.enabled=true. ' +
'Transactions will be enabled to ensure balance tracking works correctly.',
);
});
it('should allow transactions disabled when CHECK_BALANCE env is false', () => {
process.env.CHECK_BALANCE = 'false';
const appConfig = createTestAppConfig({
transactions: { enabled: false },
});
const result = getTransactionsConfig(appConfig);
expect(result).toEqual({ enabled: false });
expect(logger.warn).not.toHaveBeenCalled();
});
});
describe('edge cases', () => {
it('should handle empty appConfig object', () => {
const appConfig = createTestAppConfig();
const result = getTransactionsConfig(appConfig);
expect(result).toEqual({ enabled: true });
expect(logger.warn).not.toHaveBeenCalled();
});
it('should handle appConfig with null balance', () => {
const appConfig = createTestAppConfig({
transactions: { enabled: false },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
balance: null as any,
});
const result = getTransactionsConfig(appConfig);
expect(result).toEqual({ enabled: false });
expect(logger.warn).not.toHaveBeenCalled();
});
it('should handle appConfig with undefined balance', () => {
const appConfig = createTestAppConfig({
transactions: { enabled: false },
balance: undefined,
});
const result = getTransactionsConfig(appConfig);
expect(result).toEqual({ enabled: false });
expect(logger.warn).not.toHaveBeenCalled();
});
it('should handle appConfig with balance enabled undefined', () => {
const appConfig = createTestAppConfig({
transactions: { enabled: false },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
balance: { enabled: undefined as any },
});
const result = getTransactionsConfig(appConfig);
expect(result).toEqual({ enabled: false });
expect(logger.warn).not.toHaveBeenCalled();
});
});
});
});
describe('getBalanceConfig', () => {
beforeEach(() => {
jest.clearAllMocks();
delete process.env.CHECK_BALANCE;
delete process.env.START_BALANCE;
});
describe('when appConfig is not provided', () => {
it('should return config based on environment variables', () => {
process.env.CHECK_BALANCE = 'true';
process.env.START_BALANCE = '1000';
const result = getBalanceConfig();
expect(result).toEqual({
enabled: true,
startBalance: 1000,
});
});
it('should return empty config when no env vars are set', () => {
const result = getBalanceConfig();
expect(result).toEqual({ enabled: false });
});
it('should handle CHECK_BALANCE true without START_BALANCE', () => {
process.env.CHECK_BALANCE = 'true';
const result = getBalanceConfig();
expect(result).toEqual({
enabled: true,
});
});
it('should handle START_BALANCE without CHECK_BALANCE', () => {
process.env.START_BALANCE = '5000';
const result = getBalanceConfig();
expect(result).toEqual({
enabled: false,
startBalance: 5000,
});
});
});
describe('when appConfig is provided', () => {
it('should merge appConfig balance with env config', () => {
process.env.CHECK_BALANCE = 'true';
process.env.START_BALANCE = '1000';
const appConfig = createTestAppConfig({
balance: {
enabled: false,
startBalance: 2000,
autoRefillEnabled: true,
},
});
const result = getBalanceConfig(appConfig);
expect(result).toEqual({
enabled: false,
startBalance: 2000,
autoRefillEnabled: true,
});
});
it('should use env config when appConfig balance is not provided', () => {
process.env.CHECK_BALANCE = 'true';
process.env.START_BALANCE = '3000';
const appConfig = createTestAppConfig();
const result = getBalanceConfig(appConfig);
expect(result).toEqual({
enabled: true,
startBalance: 3000,
});
});
it('should handle appConfig with null balance', () => {
process.env.CHECK_BALANCE = 'true';
const appConfig = createTestAppConfig({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
balance: null as any,
});
const result = getBalanceConfig(appConfig);
expect(result).toEqual({
enabled: true,
});
});
});
});