Files
waoowaoo/tests/unit/billing/task-policy.test.ts

83 lines
3.1 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { TASK_TYPE } from '@/lib/task/types'
import { buildDefaultTaskBillingInfo, isBillableTaskType } from '@/lib/billing/task-policy'
import type { TaskBillingInfo } from '@/lib/task/types'
function expectBillableInfo(info: TaskBillingInfo | null): Extract<TaskBillingInfo, { billable: true }> {
expect(info).toBeTruthy()
expect(info?.billable).toBe(true)
if (!info || !info.billable) {
throw new Error('Expected billable task billing info')
}
return info
}
describe('billing/task-policy', () => {
const billingPayload = {
analysisModel: 'anthropic/claude-sonnet-4',
imageModel: 'seedream',
videoModel: 'doubao-seedance-1-5-pro-251215',
} as const
it('builds TaskBillingInfo for every billable task type', () => {
for (const taskType of Object.values(TASK_TYPE)) {
if (!isBillableTaskType(taskType)) continue
const info = expectBillableInfo(buildDefaultTaskBillingInfo(taskType, billingPayload))
expect(info.taskType).toBe(taskType)
expect(info.maxFrozenCost).toBeGreaterThanOrEqual(0)
}
})
it('returns null for a non-billable task type', () => {
const fake = 'not_billable' as unknown as (typeof TASK_TYPE)[keyof typeof TASK_TYPE]
expect(isBillableTaskType(fake)).toBe(false)
expect(buildDefaultTaskBillingInfo(fake, {})).toBeNull()
})
it('builds text billing info from explicit model payload', () => {
const info = expectBillableInfo(buildDefaultTaskBillingInfo(TASK_TYPE.ANALYZE_NOVEL, {
analysisModel: 'anthropic/claude-sonnet-4',
}))
expect(info.apiType).toBe('text')
expect(info.model).toBe('anthropic/claude-sonnet-4')
expect(info.quantity).toBe(4200)
})
it('returns null for missing required models in text/image/video tasks', () => {
expect(buildDefaultTaskBillingInfo(TASK_TYPE.ANALYZE_NOVEL, {})).toBeNull()
expect(buildDefaultTaskBillingInfo(TASK_TYPE.IMAGE_PANEL, {})).toBeNull()
expect(buildDefaultTaskBillingInfo(TASK_TYPE.VIDEO_PANEL, {})).toBeNull()
})
it('honors candidateCount/count for image tasks', () => {
const info = expectBillableInfo(buildDefaultTaskBillingInfo(TASK_TYPE.IMAGE_PANEL, {
candidateCount: 4,
imageModel: 'seedream4',
}))
expect(info.apiType).toBe('image')
expect(info.quantity).toBe(4)
expect(info.model).toBe('seedream4')
})
it('builds video billing info from firstLastFrame.flModel', () => {
const info = expectBillableInfo(buildDefaultTaskBillingInfo(TASK_TYPE.VIDEO_PANEL, {
firstLastFrame: {
flModel: 'doubao-seedance-1-0-pro-250528',
},
duration: 8,
}))
expect(info.apiType).toBe('video')
expect(info.model).toBe('doubao-seedance-1-0-pro-250528')
expect(info.quantity).toBe(1)
})
it('uses explicit lip sync model from payload', () => {
const info = expectBillableInfo(buildDefaultTaskBillingInfo(TASK_TYPE.LIP_SYNC, {
lipSyncModel: 'vidu::vidu-lipsync',
}))
expect(info.apiType).toBe('lip-sync')
expect(info.model).toBe('vidu::vidu-lipsync')
expect(info.quantity).toBe(1)
})
})