Skip to content

Commit e9dc782

Browse files
committed
docs: update billing knowledge with comprehensive DI testing documentation
Replaced outdated "Mock database module directly" guidance with: - Complete table of all DI interfaces and injectable dependencies - Code examples showing how to use deps parameters - Testing best practices for billing functions - Example test demonstrating the DI pattern This helps future developers understand how to properly test billing functions using the DI patterns established in this PR.
1 parent 0549fc6 commit e9dc782

File tree

1 file changed

+81
-3
lines changed

1 file changed

+81
-3
lines changed

packages/billing/src/billing.knowledge.md

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,84 @@ Triggers when:
6262
- Amount >= 500 credits (minimum)
6363
- If debt exists: amount = max(configured amount, debt amount)
6464

65-
## Testing
66-
67-
Mock database module directly, not getOrderedActiveGrants. Pass explicit 'now' parameter to control grant expiration.
65+
## Testing with Dependency Injection
66+
67+
All billing functions support dependency injection (DI) via optional `deps` parameters, enabling comprehensive unit testing without mocking modules.
68+
69+
### DI Patterns
70+
71+
Each function accepts an optional `deps` object with injectable dependencies:
72+
73+
```typescript
74+
// Example: Testing consumeCreditsAndAddAgentStep
75+
await consumeCreditsAndAddAgentStep({
76+
messageId: 'test-msg',
77+
userId: 'user-1',
78+
// ... other params
79+
deps: {
80+
withSerializableTransaction: mockTransaction,
81+
trackEvent: vi.fn(),
82+
reportPurchasedCreditsToStripe: vi.fn(),
83+
},
84+
})
85+
```
86+
87+
### Available DI Interfaces
88+
89+
| Function | Deps Interface | Injectable Dependencies |
90+
|----------|----------------|------------------------|
91+
| `consumeCreditsAndAddAgentStep` | `ConsumeCreditsAndAddAgentStepDeps` | `withSerializableTransaction`, `trackEvent`, `reportPurchasedCreditsToStripe` |
92+
| `calculateUsageThisCycle` | `CalculateUsageThisCycleDeps` | `db` |
93+
| `validateAutoTopupStatus` | `ValidateAutoTopupStatusDeps` | `db`, `stripeServer` |
94+
| `checkAndTriggerAutoTopup` | `CheckAndTriggerAutoTopupDeps` | `db`, `stripeServer`, `calculateUsageAndBalanceFn`, `validateAutoTopupStatusFn`, `processAndGrantCreditFn` |
95+
| `checkAndTriggerOrgAutoTopup` | `CheckAndTriggerOrgAutoTopupDeps` | `db`, `stripeServer`, `calculateOrganizationUsageAndBalanceFn`, `grantOrganizationCreditsFn` |
96+
| `reportPurchasedCreditsToStripe` | `ReportPurchasedCreditsToStripeDeps` | `db`, `stripeServer`, `shouldAttemptStripeMetering` |
97+
| `getPreviousFreeGrantAmount` | `GetPreviousFreeGrantAmountDeps` | `db` |
98+
| `calculateTotalReferralBonus` | `CalculateTotalReferralBonusDeps` | `db` |
99+
| `processAndGrantCredit` | `ProcessAndGrantCreditDeps` | `grantCreditFn`, `logSyncFailure` |
100+
| `syncOrganizationBillingCycle` | `SyncOrganizationBillingCycleDeps` | `db`, `stripeServer` |
101+
| `findOrganizationForRepository` | `FindOrganizationForRepositoryDeps` | `db` |
102+
| `consumeOrganizationCredits` | `ConsumeOrgCreditsDeps` | `withSerializableTransaction`, `trackEvent`, `reportToStripe` |
103+
| `grantOrganizationCredits` | `GrantOrgCreditsDeps` | `db`, `transaction` |
104+
105+
### Functions with `conn` Parameter
106+
107+
Some functions accept a `conn` parameter for transaction context:
108+
109+
- `getOrderedActiveGrants({ userId, now, conn })` - Pass `tx` inside transactions
110+
- `getOrderedActiveOrganizationGrants({ organizationId, now, conn })` - Same pattern
111+
- `calculateUsageAndBalance({ ..., conn })` - Pass transaction for consistent reads
112+
113+
### Testing Best Practices
114+
115+
1. **Use `createMockBillingDb()`** from `@codebuff/common/testing/mock-db` for database mocking
116+
2. **Pass explicit `now` parameter** to control grant expiration in tests
117+
3. **Mock Stripe** by injecting a mock `stripeServer` via deps
118+
4. **Use `vi.fn()`** for tracking function calls (analytics, Stripe reporting)
119+
120+
### Example Test
121+
122+
```typescript
123+
import { createMockBillingDb } from '@codebuff/common/testing/mock-db'
124+
import { checkAndTriggerAutoTopup } from '@codebuff/billing/auto-topup'
125+
126+
test('triggers auto-topup when balance is low', async () => {
127+
const mockDb = createMockBillingDb()
128+
const mockStripe = { paymentMethods: { list: vi.fn() }, ... }
129+
const mockCalculateBalance = vi.fn().mockResolvedValue({
130+
balance: { totalRemaining: 100, totalDebt: 0 }
131+
})
132+
133+
await checkAndTriggerAutoTopup({
134+
userId: 'user-1',
135+
logger: mockLogger,
136+
deps: {
137+
db: mockDb,
138+
stripeServer: mockStripe,
139+
calculateUsageAndBalanceFn: mockCalculateBalance,
140+
},
141+
})
142+
143+
expect(mockCalculateBalance).toHaveBeenCalled()
144+
})
145+
```

0 commit comments

Comments
 (0)