Skip to content

Commit 8e5b789

Browse files
committed
test(billing): add unit tests for fetchPaymentMethods function
- 6 new tests with Stripe API mocking via mockModule - Tests combined card+link results, empty results, cards-only, links-only - Verifies correct API parameters (customer ID, payment method types) - Verifies cards-first-then-links ordering - Total: 42 tests for auto-topup-helpers (was 36, now 42)
1 parent a9940ea commit 8e5b789

File tree

1 file changed

+180
-1
lines changed

1 file changed

+180
-1
lines changed

packages/billing/src/__tests__/auto-topup-helpers.test.ts

Lines changed: 180 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
import { describe, expect, it } from 'bun:test'
1+
import {
2+
clearMockedModules,
3+
mockModule,
4+
} from '@codebuff/common/testing/mock-modules'
5+
import { afterEach, beforeEach, describe, expect, it } from 'bun:test'
26

37
import {
8+
fetchPaymentMethods,
49
filterValidPaymentMethods,
510
findValidPaymentMethod,
611
isValidPaymentMethod,
@@ -54,6 +59,180 @@ function createPaymentMethodWithType(
5459
}
5560

5661
describe('auto-topup-helpers', () => {
62+
describe('fetchPaymentMethods', () => {
63+
let mockPaymentMethodsList: ReturnType<typeof createMockPaymentMethodsList>
64+
65+
function createMockPaymentMethodsList(options?: {
66+
cards?: Stripe.PaymentMethod[]
67+
links?: Stripe.PaymentMethod[]
68+
}) {
69+
const cards = options?.cards ?? []
70+
const links = options?.links ?? []
71+
const calls: Array<{ customer: string; type: string }> = []
72+
73+
return {
74+
calls,
75+
list: async (params: { customer: string; type: string }) => {
76+
calls.push({ customer: params.customer, type: params.type })
77+
if (params.type === 'card') {
78+
return { data: cards }
79+
}
80+
if (params.type === 'link') {
81+
return { data: links }
82+
}
83+
return { data: [] }
84+
},
85+
}
86+
}
87+
88+
beforeEach(async () => {
89+
mockPaymentMethodsList = createMockPaymentMethodsList()
90+
await mockModule('@codebuff/internal/util/stripe', () => ({
91+
stripeServer: {
92+
paymentMethods: mockPaymentMethodsList,
93+
},
94+
}))
95+
})
96+
97+
afterEach(() => {
98+
clearMockedModules()
99+
})
100+
101+
it('should return combined card and link payment methods', async () => {
102+
const card1 = createCardPaymentMethod('pm_card_1', 2099, 12)
103+
const card2 = createCardPaymentMethod('pm_card_2', 2050, 6)
104+
const link1 = createLinkPaymentMethod('pm_link_1')
105+
106+
mockPaymentMethodsList = createMockPaymentMethodsList({
107+
cards: [card1, card2],
108+
links: [link1],
109+
})
110+
await mockModule('@codebuff/internal/util/stripe', () => ({
111+
stripeServer: {
112+
paymentMethods: mockPaymentMethodsList,
113+
},
114+
}))
115+
116+
const result = await fetchPaymentMethods('cus_123')
117+
118+
expect(result).toHaveLength(3)
119+
expect(result[0].id).toBe('pm_card_1')
120+
expect(result[1].id).toBe('pm_card_2')
121+
expect(result[2].id).toBe('pm_link_1')
122+
})
123+
124+
it('should return empty array when customer has no payment methods', async () => {
125+
mockPaymentMethodsList = createMockPaymentMethodsList({
126+
cards: [],
127+
links: [],
128+
})
129+
await mockModule('@codebuff/internal/util/stripe', () => ({
130+
stripeServer: {
131+
paymentMethods: mockPaymentMethodsList,
132+
},
133+
}))
134+
135+
const result = await fetchPaymentMethods('cus_456')
136+
137+
expect(result).toEqual([])
138+
})
139+
140+
it('should return only cards when no link methods exist', async () => {
141+
const card1 = createCardPaymentMethod('pm_card_1', 2099, 12)
142+
const card2 = createCardPaymentMethod('pm_card_2', 2050, 6)
143+
144+
mockPaymentMethodsList = createMockPaymentMethodsList({
145+
cards: [card1, card2],
146+
links: [],
147+
})
148+
await mockModule('@codebuff/internal/util/stripe', () => ({
149+
stripeServer: {
150+
paymentMethods: mockPaymentMethodsList,
151+
},
152+
}))
153+
154+
const result = await fetchPaymentMethods('cus_789')
155+
156+
expect(result).toHaveLength(2)
157+
expect(result[0].id).toBe('pm_card_1')
158+
expect(result[1].id).toBe('pm_card_2')
159+
})
160+
161+
it('should return only links when no card methods exist', async () => {
162+
const link1 = createLinkPaymentMethod('pm_link_1')
163+
const link2 = createLinkPaymentMethod('pm_link_2')
164+
165+
mockPaymentMethodsList = createMockPaymentMethodsList({
166+
cards: [],
167+
links: [link1, link2],
168+
})
169+
await mockModule('@codebuff/internal/util/stripe', () => ({
170+
stripeServer: {
171+
paymentMethods: mockPaymentMethodsList,
172+
},
173+
}))
174+
175+
const result = await fetchPaymentMethods('cus_abc')
176+
177+
expect(result).toHaveLength(2)
178+
expect(result[0].id).toBe('pm_link_1')
179+
expect(result[1].id).toBe('pm_link_2')
180+
})
181+
182+
it('should call Stripe API with correct customer ID and payment method types', async () => {
183+
const card = createCardPaymentMethod('pm_card', 2099, 12)
184+
const link = createLinkPaymentMethod('pm_link')
185+
186+
mockPaymentMethodsList = createMockPaymentMethodsList({
187+
cards: [card],
188+
links: [link],
189+
})
190+
await mockModule('@codebuff/internal/util/stripe', () => ({
191+
stripeServer: {
192+
paymentMethods: mockPaymentMethodsList,
193+
},
194+
}))
195+
196+
await fetchPaymentMethods('cus_test_customer')
197+
198+
expect(mockPaymentMethodsList.calls).toHaveLength(2)
199+
expect(mockPaymentMethodsList.calls).toContainEqual({
200+
customer: 'cus_test_customer',
201+
type: 'card',
202+
})
203+
expect(mockPaymentMethodsList.calls).toContainEqual({
204+
customer: 'cus_test_customer',
205+
type: 'link',
206+
})
207+
})
208+
209+
it('should preserve order with cards first then links', async () => {
210+
const card1 = createCardPaymentMethod('pm_card_1', 2099, 12)
211+
const link1 = createLinkPaymentMethod('pm_link_1')
212+
const card2 = createCardPaymentMethod('pm_card_2', 2050, 6)
213+
const link2 = createLinkPaymentMethod('pm_link_2')
214+
215+
mockPaymentMethodsList = createMockPaymentMethodsList({
216+
cards: [card1, card2],
217+
links: [link1, link2],
218+
})
219+
await mockModule('@codebuff/internal/util/stripe', () => ({
220+
stripeServer: {
221+
paymentMethods: mockPaymentMethodsList,
222+
},
223+
}))
224+
225+
const result = await fetchPaymentMethods('cus_order')
226+
227+
expect(result.map((pm) => pm.id)).toEqual([
228+
'pm_card_1',
229+
'pm_card_2',
230+
'pm_link_1',
231+
'pm_link_2',
232+
])
233+
})
234+
})
235+
57236
describe('isValidPaymentMethod', () => {
58237
describe('card payment methods', () => {
59238
it('should return true for card with future expiration date', () => {

0 commit comments

Comments
 (0)