Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 0 additions & 141 deletions packages/billing/src/__tests__/balance-calculator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,147 +404,6 @@ describe('Balance Calculator - calculateUsageAndBalance', () => {
})
})

describe('shouldBlockFreeUserOverdraw', () => {
afterEach(() => {
clearMockedModules()
})

async function importModule() {
await mockModule('@codebuff/internal/db', () => ({
default: {},
}))
await mockModule('@codebuff/common/analytics', () => ({
trackEvent: () => {},
}))
return import('@codebuff/billing/balance-calculator')
}

it('should block when exhausted free-tier user tries to consume', async () => {
const { shouldBlockFreeUserOverdraw } = await importModule()
expect(
shouldBlockFreeUserOverdraw([{ balance: 0, type: 'free' }], 100),
).toBe(true)
})

it('should block when free-tier user balance is less than charge', async () => {
const { shouldBlockFreeUserOverdraw } = await importModule()
expect(
shouldBlockFreeUserOverdraw([{ balance: 50, type: 'free' }], 100),
).toBe(true)
})

it('should not block when free-tier user has sufficient balance', async () => {
const { shouldBlockFreeUserOverdraw } = await importModule()
expect(
shouldBlockFreeUserOverdraw([{ balance: 500, type: 'free' }], 100),
).toBe(false)
})

it('should not block when user has a subscription grant even with zero balance', async () => {
const { shouldBlockFreeUserOverdraw } = await importModule()
expect(
shouldBlockFreeUserOverdraw(
[
{ balance: 0, type: 'free' },
{ balance: 0, type: 'subscription' },
],
100,
),
).toBe(false)
})

it('should not block when user has a purchase grant', async () => {
const { shouldBlockFreeUserOverdraw } = await importModule()
expect(
shouldBlockFreeUserOverdraw(
[
{ balance: 0, type: 'free' },
{ balance: 10, type: 'purchase' },
],
100,
),
).toBe(false)
})

it('should not block when credits to charge is 0 (free-mode agent)', async () => {
const { shouldBlockFreeUserOverdraw } = await importModule()
expect(
shouldBlockFreeUserOverdraw([{ balance: 0, type: 'free' }], 0),
).toBe(false)
})

it('should block referral-only user with insufficient credits', async () => {
const { shouldBlockFreeUserOverdraw } = await importModule()
expect(
shouldBlockFreeUserOverdraw([{ balance: 50, type: 'referral' }], 100),
).toBe(true)
})

it('should block user in debt with no paid grants', async () => {
const { shouldBlockFreeUserOverdraw } = await importModule()
expect(
shouldBlockFreeUserOverdraw([{ balance: -100, type: 'free' }], 50),
).toBe(true)
})

it('should aggregate balance across multiple unpaid grants', async () => {
const { shouldBlockFreeUserOverdraw } = await importModule()
// Total balance: 110, charge: 100 → not blocked
expect(
shouldBlockFreeUserOverdraw(
[
{ balance: 30, type: 'free' },
{ balance: 80, type: 'referral' },
],
100,
),
).toBe(false)
})
})

describe('InsufficientCreditsError', () => {
afterEach(() => {
clearMockedModules()
})

async function importModule() {
await mockModule('@codebuff/internal/db', () => ({
default: {},
}))
await mockModule('@codebuff/common/analytics', () => ({
trackEvent: () => {},
}))
return import('@codebuff/billing/balance-calculator')
}

it('should be an instance of Error with the correct name and fields', async () => {
const { InsufficientCreditsError } = await importModule()
const err = new InsufficientCreditsError(-50, 200)
expect(err).toBeInstanceOf(Error)
expect(err).toBeInstanceOf(InsufficientCreditsError)
expect(err.name).toBe('InsufficientCreditsError')
expect(err.netBalance).toBe(-50)
expect(err.chargeAmount).toBe(200)
expect(err.message).toBe(
'Insufficient credits for free-tier user: balance=-50, charge=200',
)
})

it('should be exported from the billing barrel (@codebuff/billing)', async () => {
await mockModule('@codebuff/internal/db', () => ({
default: {},
}))
await mockModule('@codebuff/common/analytics', () => ({
trackEvent: () => {},
}))
const billing = await import('@codebuff/billing')
expect(typeof billing.InsufficientCreditsError).toBe('function')
const err = new billing.InsufficientCreditsError(0, 100)
expect(err).toBeInstanceOf(Error)
expect(err.name).toBe('InsufficientCreditsError')
})
})

describe('consumeFromOrderedGrants - credit consumption bugs', () => {
// Regression tests for two compounding bugs:
// 1. Pass 1 ("repay debt") was directionally wrong: consumption reduced debt instead of
Expand Down
Loading
Loading