feat: initial release v0.3.0
This commit is contained in:
79
tests/unit/billing/runtime-usage.test.ts
Normal file
79
tests/unit/billing/runtime-usage.test.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { AsyncLocalStorage } from 'node:async_hooks'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { recordTextUsage, withTextUsageCollection } from '@/lib/billing/runtime-usage'
|
||||
|
||||
describe('billing/runtime-usage', () => {
|
||||
it('ignores records outside of collection scope', () => {
|
||||
expect(() => {
|
||||
recordTextUsage({
|
||||
model: 'm',
|
||||
inputTokens: 10,
|
||||
outputTokens: 20,
|
||||
})
|
||||
}).not.toThrow()
|
||||
})
|
||||
|
||||
it('collects and normalizes token usage', async () => {
|
||||
const { textUsage } = await withTextUsageCollection(async () => {
|
||||
recordTextUsage({
|
||||
model: 'test-model',
|
||||
inputTokens: 10.9,
|
||||
outputTokens: -2,
|
||||
})
|
||||
return { ok: true }
|
||||
})
|
||||
|
||||
expect(textUsage).toEqual([
|
||||
{
|
||||
model: 'test-model',
|
||||
inputTokens: 10,
|
||||
outputTokens: 0,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('falls back to empty usage when store is unavailable at read time', async () => {
|
||||
const getStoreSpy = vi.spyOn(AsyncLocalStorage.prototype, 'getStore')
|
||||
getStoreSpy.mockReturnValueOnce(undefined as never)
|
||||
|
||||
const payload = await withTextUsageCollection(async () => ({ ok: true }))
|
||||
|
||||
expect(payload).toEqual({ result: { ok: true }, textUsage: [] })
|
||||
getStoreSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('normalizes NaN and zero token values to zero', async () => {
|
||||
const { textUsage } = await withTextUsageCollection(async () => {
|
||||
recordTextUsage({
|
||||
model: 'nan-model',
|
||||
inputTokens: Number.NaN,
|
||||
outputTokens: 0,
|
||||
})
|
||||
return { ok: true }
|
||||
})
|
||||
|
||||
expect(textUsage).toEqual([
|
||||
{
|
||||
model: 'nan-model',
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('isolates concurrent async local storage contexts', async () => {
|
||||
const [left, right] = await Promise.all([
|
||||
withTextUsageCollection(async () => {
|
||||
recordTextUsage({ model: 'left', inputTokens: 1, outputTokens: 2 })
|
||||
return 'left'
|
||||
}),
|
||||
withTextUsageCollection(async () => {
|
||||
recordTextUsage({ model: 'right', inputTokens: 3, outputTokens: 4 })
|
||||
return 'right'
|
||||
}),
|
||||
])
|
||||
|
||||
expect(left.textUsage).toEqual([{ model: 'left', inputTokens: 1, outputTokens: 2 }])
|
||||
expect(right.textUsage).toEqual([{ model: 'right', inputTokens: 3, outputTokens: 4 }])
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user