jest.mock( '../config/config', () => ({ security: { apiKeyPrefix: 'cr_' } }), { virtual: true } ) jest.mock('../src/models/redis', () => ({ setApiKey: jest.fn(), getApiKey: jest.fn(), incrementTokenUsage: jest.fn(), incrementDailyCost: jest.fn(), incrementAccountUsage: jest.fn(), addUsageRecord: jest.fn() })) jest.mock('../src/services/costRankService', () => ({ addKeyToIndexes: jest.fn() })) jest.mock('../src/services/apiKeyIndexService', () => ({ addToIndex: jest.fn(), updateIndex: jest.fn() })) jest.mock('../src/utils/logger', () => ({ success: jest.fn(), warn: jest.fn(), error: jest.fn(), database: jest.fn(), debug: jest.fn() })) jest.mock('../src/services/serviceRatesService', () => ({ getService: jest.fn(), getServiceRate: jest.fn() })) jest.mock('../src/services/requestDetailService', () => ({ captureRequestDetail: jest.fn() })) jest.mock('../src/services/billingEventPublisher', () => ({ publishBillingEvent: jest.fn() })) jest.mock('../src/utils/costCalculator', () => ({ calculateCost: jest.fn() })) jest.mock('../src/utils/modelHelper', () => ({ isClaudeFamilyModel: jest.fn(() => false) })) jest.mock('../src/utils/requestDetailHelper', () => ({ finalizeRequestDetailMeta: jest.fn((value) => value) })) const redis = require('../src/models/redis') const serviceRatesService = require('../src/services/serviceRatesService') const requestDetailService = require('../src/services/requestDetailService') const billingEventPublisher = require('../src/services/billingEventPublisher') const CostCalculator = require('../src/utils/costCalculator') const apiKeyService = require('../src/services/apiKeyService') describe('apiKeyService openai responses config', () => { beforeEach(() => { jest.clearAllMocks() redis.getApiKey.mockResolvedValue({ id: 'key-1', name: 'Key', serviceRates: '{}' }) redis.setApiKey.mockResolvedValue() redis.incrementTokenUsage.mockResolvedValue() redis.incrementDailyCost.mockResolvedValue() redis.incrementAccountUsage.mockResolvedValue() redis.addUsageRecord.mockResolvedValue() serviceRatesService.getService.mockReturnValue('claude') serviceRatesService.getServiceRate.mockResolvedValue(1) requestDetailService.captureRequestDetail.mockResolvedValue({ captured: true }) billingEventPublisher.publishBillingEvent.mockResolvedValue() }) test('generateApiKey stores default toggle values', async () => { redis.setApiKey.mockResolvedValue() const result = await apiKeyService.generateApiKey({ name: 'Test Key' }) const [, storedKeyData] = redis.setApiKey.mock.calls[0] expect(storedKeyData.enableOpenAIResponsesCodexAdaptation).toBe('true') expect(storedKeyData.enableOpenAIResponsesPayloadRules).toBe('false') expect(storedKeyData.openaiResponsesPayloadRules).toBe('[]') expect(result.enableOpenAIResponsesCodexAdaptation).toBe(true) expect(result.enableOpenAIResponsesPayloadRules).toBe(false) expect(result.openaiResponsesPayloadRules).toEqual([]) }) test('updateApiKey serializes toggle and payload rule fields', async () => { redis.getApiKey.mockResolvedValue({ id: 'key-1', apiKey: 'hashed-key', name: 'Old Key', isActive: 'true', tags: '[]' }) redis.setApiKey.mockResolvedValue() await apiKeyService.updateApiKey('key-1', { enableOpenAIResponsesCodexAdaptation: false, enableOpenAIResponsesPayloadRules: true, openaiResponsesPayloadRules: [{ path: 'model', valueType: 'string', value: 'gpt-5' }] }) const [, storedKeyData] = redis.setApiKey.mock.calls[0] expect(storedKeyData.enableOpenAIResponsesCodexAdaptation).toBe('false') expect(storedKeyData.enableOpenAIResponsesPayloadRules).toBe('true') expect(storedKeyData.openaiResponsesPayloadRules).toBe( JSON.stringify([{ path: 'model', valueType: 'string', value: 'gpt-5' }]) ) }) test('getApiKeyById returns parsed toggle and rule values', async () => { redis.getApiKey.mockResolvedValue({ id: 'key-1', name: 'Key', apiKey: 'hashed-key', tokenLimit: '0', isActive: 'true', createdAt: '2025-01-01T00:00:00.000Z', lastUsedAt: '', expiresAt: '', userId: '', userUsername: '', createdBy: 'admin', permissions: '[]', dailyCostLimit: '0', totalCostLimit: '0', claudeAccountId: '', claudeConsoleAccountId: '', geminiAccountId: '', openaiAccountId: '', bedrockAccountId: '', droidAccountId: '', azureOpenaiAccountId: '', ccrAccountId: '', enableOpenAIResponsesCodexAdaptation: 'false', enableOpenAIResponsesPayloadRules: 'true', openaiResponsesPayloadRules: JSON.stringify([ { path: 'model', valueType: 'string', value: 'gpt-5' } ]) }) const result = await apiKeyService.getApiKeyById('key-1') expect(result.enableOpenAIResponsesCodexAdaptation).toBe(false) expect(result.enableOpenAIResponsesPayloadRules).toBe(true) expect(result.openaiResponsesPayloadRules).toEqual([ { path: 'model', valueType: 'string', value: 'gpt-5' } ]) }) test('recordUsageWithDetails uses CostCalculator unknown fallback for missing model pricing', async () => { CostCalculator.calculateCost.mockReturnValue({ costs: { input: 0.051618, output: 0.000765, cacheCreate: 0, cacheWrite: 0, cacheRead: 0.0006144, total: 0.0529974 }, debug: { usedFallbackPricing: true, pricingSource: 'unknown-fallback', isLongContextRequest: false }, usingDynamicPricing: false }) const result = await apiKeyService.recordUsageWithDetails( 'key-1', { input_tokens: 17206, output_tokens: 51, cache_creation_input_tokens: 0, cache_read_input_tokens: 2048 }, 'mimo-v2.5-pro', 'acct-1', 'claude-console', { requestId: 'req-1', endpoint: '/api/v1/messages', method: 'POST', statusCode: 200 } ) expect(CostCalculator.calculateCost).toHaveBeenCalledWith( { input_tokens: 17206, output_tokens: 51, cache_creation_input_tokens: 0, cache_read_input_tokens: 2048 }, 'mimo-v2.5-pro' ) expect(result.realCost).toBeCloseTo(0.0529974, 10) expect(result.ratedCost).toBeCloseTo(0.0529974, 10) expect(redis.incrementDailyCost.mock.calls[0][0]).toBe('key-1') expect(redis.incrementDailyCost.mock.calls[0][1]).toBeCloseTo(0.0529974, 10) expect(redis.incrementDailyCost.mock.calls[0][2]).toBeCloseTo(0.0529974, 10) expect(redis.addUsageRecord).toHaveBeenCalledWith( 'key-1', expect.objectContaining({ model: 'mimo-v2.5-pro', cost: 0.052997, realCost: 0.052997, usedFallbackPricing: true, pricingSource: 'unknown-fallback', costBreakdown: expect.objectContaining({ input: 0.051618, output: 0.000765, cacheRead: 0.0006144, total: 0.0529974 }) }) ) expect(requestDetailService.captureRequestDetail).toHaveBeenCalledWith( expect.objectContaining({ requestId: 'req-1', model: 'mimo-v2.5-pro', cost: 0.052997, realCost: 0.052997, usedFallbackPricing: true, pricingSource: 'unknown-fallback' }) ) }) })