66 lines
2.1 KiB
TypeScript
66 lines
2.1 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
const lookupMock = vi.hoisted(() => ({
|
|
resolveBuiltinPricing: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('@/lib/model-pricing/lookup', () => ({
|
|
resolveBuiltinPricing: lookupMock.resolveBuiltinPricing,
|
|
}))
|
|
|
|
import { calcImage, calcText, calcVideo, calcVoice } from '@/lib/billing/cost'
|
|
|
|
describe('billing/cost error branches', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it('throws ambiguous pricing error when catalog has multiple candidates', () => {
|
|
lookupMock.resolveBuiltinPricing.mockReturnValue({
|
|
status: 'ambiguous_model',
|
|
apiType: 'image',
|
|
modelId: 'shared-model',
|
|
candidates: [
|
|
{
|
|
apiType: 'image',
|
|
provider: 'p1',
|
|
modelId: 'shared-model',
|
|
pricing: { mode: 'flat', flatAmount: 1 },
|
|
},
|
|
{
|
|
apiType: 'image',
|
|
provider: 'p2',
|
|
modelId: 'shared-model',
|
|
pricing: { mode: 'flat', flatAmount: 1 },
|
|
},
|
|
],
|
|
})
|
|
|
|
expect(() => calcImage('shared-model', 1)).toThrow('Ambiguous image pricing modelId')
|
|
})
|
|
|
|
it('throws unknown model when catalog returns not_configured', () => {
|
|
lookupMock.resolveBuiltinPricing.mockReturnValue({
|
|
status: 'not_configured',
|
|
})
|
|
|
|
expect(() => calcImage('provider::missing-image-model', 1)).toThrow('Unknown image model pricing')
|
|
})
|
|
|
|
it('normalizes invalid numeric inputs to zero before pricing', () => {
|
|
lookupMock.resolveBuiltinPricing.mockImplementation(
|
|
(input: { selections?: { tokenType?: 'input' | 'output' } }) => {
|
|
if (input.selections?.tokenType === 'input') return { status: 'resolved', amount: 2 }
|
|
if (input.selections?.tokenType === 'output') return { status: 'resolved', amount: 4 }
|
|
return { status: 'resolved', amount: 3 }
|
|
},
|
|
)
|
|
|
|
expect(calcText('text-model', Number.NaN, 1_000_000)).toBeCloseTo(4, 8)
|
|
expect(calcText('text-model', 1_000_000, Number.NaN)).toBeCloseTo(2, 8)
|
|
expect(calcImage('image-model', Number.NaN)).toBe(0)
|
|
expect(calcVideo('video-model', '720p', Number.NaN)).toBe(0)
|
|
expect(calcVoice(Number.NaN)).toBe(0)
|
|
})
|
|
})
|