Skip to content

Commit a5b1506

Browse files
committed
fix(web): make ban-conditions tests Jest-compatible
1 parent f83289c commit a5b1506

File tree

1 file changed

+115
-78
lines changed

1 file changed

+115
-78
lines changed

web/src/lib/__tests__/ban-conditions.test.ts

Lines changed: 115 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,25 @@
1-
import { describe, it, expect, beforeEach, mock, spyOn } from 'bun:test'
1+
import { beforeEach, describe, expect, it } from '@jest/globals'
22

3-
import {
4-
DISPUTE_THRESHOLD,
5-
DISPUTE_WINDOW_DAYS,
6-
evaluateBanConditions,
7-
getUserByStripeCustomerId,
8-
banUser,
9-
type BanConditionContext,
10-
} from '../ban-conditions'
11-
12-
// Mock the database module
13-
const mockSelect = mock(() => ({
14-
from: mock(() => ({
15-
where: mock(() => ({
16-
limit: mock(() => Promise.resolve([])),
17-
})),
18-
})),
19-
}))
20-
21-
const mockUpdate = mock(() => ({
22-
set: mock(() => ({
23-
where: mock(() => Promise.resolve()),
24-
})),
25-
}))
26-
27-
mock.module('@codebuff/internal/db', () => ({
3+
jest.mock('@codebuff/internal/db', () => ({
4+
__esModule: true,
285
default: {
29-
select: mockSelect,
30-
update: mockUpdate,
6+
select: jest.fn(() => ({
7+
from: jest.fn(() => ({
8+
where: jest.fn(() => ({
9+
limit: jest.fn(() => Promise.resolve([])),
10+
})),
11+
})),
12+
})),
13+
update: jest.fn(() => ({
14+
set: jest.fn(() => ({
15+
where: jest.fn(() => Promise.resolve()),
16+
})),
17+
})),
3118
},
3219
}))
3320

34-
mock.module('@codebuff/internal/db/schema', () => ({
21+
jest.mock('@codebuff/internal/db/schema', () => ({
22+
__esModule: true,
3523
user: {
3624
id: 'id',
3725
banned: 'banned',
@@ -41,31 +29,45 @@ mock.module('@codebuff/internal/db/schema', () => ({
4129
},
4230
}))
4331

44-
// Mock Stripe server
45-
const mockDisputesList = mock((): Promise<{ data: any[] }> =>
46-
Promise.resolve({
47-
data: [],
48-
}),
49-
)
50-
51-
mock.module('@codebuff/internal/util/stripe', () => ({
32+
jest.mock('@codebuff/internal/util/stripe', () => ({
33+
__esModule: true,
5234
stripeServer: {
5335
disputes: {
54-
list: mockDisputesList,
36+
list: jest.fn(() =>
37+
Promise.resolve({
38+
data: [],
39+
}),
40+
),
5541
},
5642
},
5743
}))
5844

59-
// Mock drizzle-orm eq function
60-
mock.module('drizzle-orm', () => ({
61-
eq: mock((a: any, b: any) => ({ column: a, value: b })),
45+
jest.mock('drizzle-orm', () => ({
46+
__esModule: true,
47+
eq: jest.fn((a: any, b: any) => ({ column: a, value: b })),
6248
}))
6349

50+
import db from '@codebuff/internal/db'
51+
import { stripeServer } from '@codebuff/internal/util/stripe'
52+
53+
import {
54+
DISPUTE_THRESHOLD,
55+
DISPUTE_WINDOW_DAYS,
56+
banUser,
57+
evaluateBanConditions,
58+
getUserByStripeCustomerId,
59+
type BanConditionContext,
60+
} from '../ban-conditions'
61+
62+
const mockSelect = db.select as unknown as jest.Mock
63+
const mockUpdate = db.update as unknown as jest.Mock
64+
const mockDisputesList = stripeServer.disputes.list as unknown as jest.Mock
65+
6466
const createMockLogger = () => ({
65-
debug: mock(() => {}),
66-
info: mock(() => {}),
67-
warn: mock(() => {}),
68-
error: mock(() => {}),
67+
debug: jest.fn(() => {}),
68+
info: jest.fn(() => {}),
69+
warn: jest.fn(() => {}),
70+
error: jest.fn(() => {}),
6971
})
7072

7173
describe('ban-conditions', () => {
@@ -104,11 +106,14 @@ describe('ban-conditions', () => {
104106

105107
it('returns shouldBan: false when disputes are below threshold', async () => {
106108
// Create disputes for the customer (below threshold)
107-
const disputes = Array.from({ length: DISPUTE_THRESHOLD - 1 }, (_, i) => ({
108-
id: `dp_${i}`,
109-
charge: { customer: 'cus_123' },
110-
created: Math.floor(Date.now() / 1000),
111-
}))
109+
const disputes = Array.from(
110+
{ length: DISPUTE_THRESHOLD - 1 },
111+
(_, i) => ({
112+
id: `dp_${i}`,
113+
charge: { customer: 'cus_123' },
114+
created: Math.floor(Date.now() / 1000),
115+
}),
116+
)
112117

113118
mockDisputesList.mockResolvedValueOnce({ data: disputes })
114119

@@ -151,11 +156,14 @@ describe('ban-conditions', () => {
151156

152157
it('returns shouldBan: true when disputes exceed threshold', async () => {
153158
// Create disputes for the customer (above threshold)
154-
const disputes = Array.from({ length: DISPUTE_THRESHOLD + 3 }, (_, i) => ({
155-
id: `dp_${i}`,
156-
charge: { customer: 'cus_123' },
157-
created: Math.floor(Date.now() / 1000),
158-
}))
159+
const disputes = Array.from(
160+
{ length: DISPUTE_THRESHOLD + 3 },
161+
(_, i) => ({
162+
id: `dp_${i}`,
163+
charge: { customer: 'cus_123' },
164+
created: Math.floor(Date.now() / 1000),
165+
}),
166+
)
159167

160168
mockDisputesList.mockResolvedValueOnce({ data: disputes })
161169

@@ -176,13 +184,37 @@ describe('ban-conditions', () => {
176184
// Mix of disputes from different customers
177185
const disputes = [
178186
// Disputes for our customer
179-
{ id: 'dp_1', charge: { customer: 'cus_123' }, created: Math.floor(Date.now() / 1000) },
180-
{ id: 'dp_2', charge: { customer: 'cus_123' }, created: Math.floor(Date.now() / 1000) },
187+
{
188+
id: 'dp_1',
189+
charge: { customer: 'cus_123' },
190+
created: Math.floor(Date.now() / 1000),
191+
},
192+
{
193+
id: 'dp_2',
194+
charge: { customer: 'cus_123' },
195+
created: Math.floor(Date.now() / 1000),
196+
},
181197
// Disputes for other customers (should be ignored)
182-
{ id: 'dp_3', charge: { customer: 'cus_other' }, created: Math.floor(Date.now() / 1000) },
183-
{ id: 'dp_4', charge: { customer: 'cus_different' }, created: Math.floor(Date.now() / 1000) },
184-
{ id: 'dp_5', charge: { customer: 'cus_another' }, created: Math.floor(Date.now() / 1000) },
185-
{ id: 'dp_6', charge: { customer: 'cus_more' }, created: Math.floor(Date.now() / 1000) },
198+
{
199+
id: 'dp_3',
200+
charge: { customer: 'cus_other' },
201+
created: Math.floor(Date.now() / 1000),
202+
},
203+
{
204+
id: 'dp_4',
205+
charge: { customer: 'cus_different' },
206+
created: Math.floor(Date.now() / 1000),
207+
},
208+
{
209+
id: 'dp_5',
210+
charge: { customer: 'cus_another' },
211+
created: Math.floor(Date.now() / 1000),
212+
},
213+
{
214+
id: 'dp_6',
215+
charge: { customer: 'cus_more' },
216+
created: Math.floor(Date.now() / 1000),
217+
},
186218
]
187219

188220
mockDisputesList.mockResolvedValueOnce({ data: disputes })
@@ -265,10 +297,15 @@ describe('ban-conditions', () => {
265297
expect(callArgs.expand).toEqual(['data.charge'])
266298

267299
// Verify the created.gte is within the expected window
268-
const expectedWindowStart = beforeCall - DISPUTE_WINDOW_DAYS * 24 * 60 * 60
300+
const expectedWindowStart =
301+
beforeCall - DISPUTE_WINDOW_DAYS * 24 * 60 * 60
269302
const windowTolerance = afterCall - beforeCall + 1 // Allow for time passing during test
270-
expect(callArgs.created.gte).toBeGreaterThanOrEqual(expectedWindowStart - windowTolerance)
271-
expect(callArgs.created.gte).toBeLessThanOrEqual(expectedWindowStart + windowTolerance)
303+
expect(callArgs.created.gte).toBeGreaterThanOrEqual(
304+
expectedWindowStart - windowTolerance,
305+
)
306+
expect(callArgs.created.gte).toBeLessThanOrEqual(
307+
expectedWindowStart + windowTolerance,
308+
)
272309
})
273310

274311
// REGRESSION TEST: Without expand: ['data.charge'], dispute.charge is a string ID,
@@ -287,7 +324,7 @@ describe('ban-conditions', () => {
287324
await evaluateBanConditions(context)
288325

289326
const callArgs = (mockDisputesList.mock.calls as any)[0]?.[0]
290-
327+
291328
// This is critical: without expand, dispute.charge is just a string ID like "ch_xxx"
292329
// and we cannot access dispute.charge.customer to filter by customer.
293330
// If this test fails, the ban condition will NEVER match any disputes.
@@ -320,9 +357,9 @@ describe('ban-conditions', () => {
320357
name: 'Test User',
321358
}
322359

323-
const limitMock = mock(() => Promise.resolve([mockUser]))
324-
const whereMock = mock(() => ({ limit: limitMock }))
325-
const fromMock = mock(() => ({ where: whereMock }))
360+
const limitMock = jest.fn(() => Promise.resolve([mockUser]))
361+
const whereMock = jest.fn(() => ({ limit: limitMock }))
362+
const fromMock = jest.fn(() => ({ where: whereMock }))
326363
mockSelect.mockReturnValueOnce({ from: fromMock })
327364

328365
const result = await getUserByStripeCustomerId('cus_123')
@@ -331,9 +368,9 @@ describe('ban-conditions', () => {
331368
})
332369

333370
it('returns null when user not found', async () => {
334-
const limitMock = mock(() => Promise.resolve([]))
335-
const whereMock = mock(() => ({ limit: limitMock }))
336-
const fromMock = mock(() => ({ where: whereMock }))
371+
const limitMock = jest.fn(() => Promise.resolve([]))
372+
const whereMock = jest.fn(() => ({ limit: limitMock }))
373+
const fromMock = jest.fn(() => ({ where: whereMock }))
337374
mockSelect.mockReturnValueOnce({ from: fromMock })
338375

339376
const result = await getUserByStripeCustomerId('cus_nonexistent')
@@ -342,9 +379,9 @@ describe('ban-conditions', () => {
342379
})
343380

344381
it('queries with correct stripe_customer_id', async () => {
345-
const limitMock = mock(() => Promise.resolve([]))
346-
const whereMock = mock(() => ({ limit: limitMock }))
347-
const fromMock = mock(() => ({ where: whereMock }))
382+
const limitMock = jest.fn(() => Promise.resolve([]))
383+
const whereMock = jest.fn(() => ({ limit: limitMock }))
384+
const fromMock = jest.fn(() => ({ where: whereMock }))
348385
mockSelect.mockReturnValueOnce({ from: fromMock })
349386

350387
await getUserByStripeCustomerId('cus_test_123')
@@ -358,8 +395,8 @@ describe('ban-conditions', () => {
358395

359396
describe('banUser', () => {
360397
it('updates user banned status to true', async () => {
361-
const whereMock = mock(() => Promise.resolve())
362-
const setMock = mock(() => ({ where: whereMock }))
398+
const whereMock = jest.fn(() => Promise.resolve())
399+
const setMock = jest.fn(() => ({ where: whereMock }))
363400
mockUpdate.mockReturnValueOnce({ set: setMock })
364401

365402
const logger = createMockLogger()
@@ -371,8 +408,8 @@ describe('ban-conditions', () => {
371408
})
372409

373410
it('logs the ban action with user ID and reason', async () => {
374-
const whereMock = mock(() => Promise.resolve())
375-
const setMock = mock(() => ({ where: whereMock }))
411+
const whereMock = jest.fn(() => Promise.resolve())
412+
const setMock = jest.fn(() => ({ where: whereMock }))
376413
mockUpdate.mockReturnValueOnce({ set: setMock })
377414

378415
const logger = createMockLogger()

0 commit comments

Comments
 (0)