Skip to content

Commit 412753c

Browse files
chore: wip
1 parent d705417 commit 412753c

File tree

1 file changed

+328
-0
lines changed

1 file changed

+328
-0
lines changed
Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
import { describe, expect, it, mock, beforeEach } from 'bun:test'
2+
import { fetchById, fetchByCode, checkBalance } from '../gift-cards/fetch'
3+
import { store } from '../gift-cards/store'
4+
import { update, updateBalance } from '../gift-cards/update'
5+
import { remove, deactivate } from '../gift-cards/destroy'
6+
import { db } from '@stacksjs/database'
7+
8+
// Mock the database
9+
mock.module('@stacksjs/database', () => ({
10+
db: {
11+
selectFrom: mock(() => ({
12+
where: mock(() => ({
13+
selectAll: mock(() => ({
14+
executeTakeFirst: mock(() => Promise.resolve({
15+
id: 1,
16+
code: 'GIFT123',
17+
initial_balance: 100,
18+
current_balance: 75,
19+
currency: 'USD',
20+
status: 'ACTIVE',
21+
is_active: true,
22+
is_digital: true,
23+
is_reloadable: false,
24+
recipient_email: 'recipient@example.com',
25+
recipient_name: 'Gift Recipient',
26+
expiry_date: '2025-12-31',
27+
created_at: new Date(),
28+
updated_at: new Date(),
29+
})),
30+
})),
31+
})),
32+
})),
33+
insertInto: mock(() => ({
34+
values: mock(() => ({
35+
executeTakeFirst: mock(() => Promise.resolve({ insertId: 1 })),
36+
})),
37+
})),
38+
updateTable: mock(() => ({
39+
set: mock(() => ({
40+
where: mock(() => ({
41+
execute: mock(() => Promise.resolve()),
42+
})),
43+
})),
44+
})),
45+
deleteFrom: mock(() => ({
46+
where: mock(() => ({
47+
executeTakeFirst: mock(() => Promise.resolve({ numDeletedRows: 1 })),
48+
})),
49+
})),
50+
},
51+
}))
52+
53+
// Mock the request validation
54+
class MockRequest {
55+
private data: Record<string, any> = {}
56+
57+
constructor(data: Record<string, any>) {
58+
this.data = data
59+
}
60+
61+
validate() {
62+
return Promise.resolve()
63+
}
64+
65+
get(key: string) {
66+
return this.data[key]
67+
}
68+
}
69+
70+
describe('Gift Card Module', () => {
71+
beforeEach(() => {
72+
// Reset mocks before each test
73+
mock.restore()
74+
})
75+
76+
describe('fetchById', () => {
77+
it('should fetch a gift card by ID', async () => {
78+
const giftCard = await fetchById(1)
79+
80+
expect(giftCard).toBeDefined()
81+
expect(giftCard?.id).toBe(1)
82+
expect(giftCard?.code).toBe('GIFT123')
83+
expect(giftCard?.initial_balance).toBe(100)
84+
85+
// Verify db was called correctly
86+
expect(db.selectFrom).toHaveBeenCalledWith('gift_cards')
87+
expect(db.selectFrom('gift_cards').where).toHaveBeenCalledWith('id', '=', 1)
88+
})
89+
})
90+
91+
describe('fetchByCode', () => {
92+
it('should fetch a gift card by code', async () => {
93+
const giftCard = await fetchByCode('GIFT123')
94+
95+
expect(giftCard).toBeDefined()
96+
expect(giftCard?.code).toBe('GIFT123')
97+
98+
// Verify db was called correctly
99+
expect(db.selectFrom).toHaveBeenCalledWith('gift_cards')
100+
expect(db.selectFrom('gift_cards').where).toHaveBeenCalledWith('code', '=', 'GIFT123')
101+
})
102+
})
103+
104+
describe('store', () => {
105+
it('should create a new gift card', async () => {
106+
const requestData = {
107+
code: 'NEWGIFT456',
108+
initial_balance: 200,
109+
currency: 'USD',
110+
status: 'ACTIVE',
111+
is_active: true,
112+
is_digital: true,
113+
recipient_email: 'new@example.com',
114+
recipient_name: 'New Recipient',
115+
}
116+
117+
const request = new MockRequest(requestData)
118+
const giftCard = await store(request as any)
119+
120+
expect(giftCard).toBeDefined()
121+
expect(giftCard?.id).toBe(1)
122+
expect(giftCard?.code).toBe('GIFT123') // From the mock response
123+
124+
// Verify db was called correctly
125+
expect(db.insertInto).toHaveBeenCalledWith('gift_cards')
126+
expect(db.insertInto('gift_cards').values).toHaveBeenCalledTimes(1)
127+
})
128+
129+
it('should throw an error if code already exists', async () => {
130+
// Mock the database to throw a duplicate entry error
131+
mock.module('@stacksjs/database', () => ({
132+
db: {
133+
insertInto: mock(() => ({
134+
values: mock(() => ({
135+
executeTakeFirst: mock(() => {
136+
throw new Error('Duplicate entry for key code')
137+
}),
138+
})),
139+
})),
140+
},
141+
}))
142+
143+
const requestData = {
144+
code: 'DUPLICATE',
145+
initial_balance: 50,
146+
currency: 'USD',
147+
}
148+
149+
const request = new MockRequest(requestData)
150+
151+
await expect(store(request as any)).rejects.toThrow('A gift card with this code already exists')
152+
})
153+
})
154+
155+
describe('update', () => {
156+
it('should update an existing gift card', async () => {
157+
const requestData = {
158+
code: 'UPDATED456',
159+
current_balance: 80,
160+
status: 'ACTIVE',
161+
}
162+
163+
const request = new MockRequest(requestData)
164+
const giftCard = await update(1, request as any)
165+
166+
expect(giftCard).toBeDefined()
167+
expect(giftCard?.id).toBe(1)
168+
169+
// Verify db was called correctly
170+
expect(db.updateTable).toHaveBeenCalledWith('gift_cards')
171+
expect(db.updateTable('gift_cards').set).toHaveBeenCalledTimes(1)
172+
expect(db.selectFrom).toHaveBeenCalledWith('gift_cards')
173+
})
174+
175+
it('should return the gift card without updating if no data provided', async () => {
176+
const request = new MockRequest({})
177+
const giftCard = await update(1, request as any)
178+
179+
expect(giftCard).toBeDefined()
180+
expect(giftCard?.id).toBe(1)
181+
182+
// Verify db was not called to update
183+
expect(db.updateTable).not.toHaveBeenCalled()
184+
})
185+
})
186+
187+
describe('updateBalance', () => {
188+
it('should update a gift card balance', async () => {
189+
const giftCard = await updateBalance(1, -25)
190+
191+
expect(giftCard).toBeDefined()
192+
expect(giftCard?.id).toBe(1)
193+
194+
// Verify db was called correctly
195+
expect(db.updateTable).toHaveBeenCalledWith('gift_cards')
196+
expect(db.updateTable('gift_cards').set).toHaveBeenCalledTimes(1)
197+
expect(db.selectFrom).toHaveBeenCalledWith('gift_cards')
198+
})
199+
200+
it('should throw an error if balance would go below zero', async () => {
201+
// Mock the database to return a gift card with low balance
202+
mock.module('@stacksjs/database', () => ({
203+
db: {
204+
selectFrom: mock(() => ({
205+
where: mock(() => ({
206+
selectAll: mock(() => ({
207+
executeTakeFirst: mock(() => Promise.resolve({
208+
id: 1,
209+
code: 'LOWBALANCE',
210+
initial_balance: 100,
211+
current_balance: 10,
212+
currency: 'USD',
213+
status: 'ACTIVE',
214+
is_active: true,
215+
})),
216+
})),
217+
})),
218+
})),
219+
},
220+
}))
221+
222+
await expect(updateBalance(1, -20)).rejects.toThrow('Insufficient gift card balance')
223+
})
224+
})
225+
226+
describe('remove', () => {
227+
it('should delete a gift card', async () => {
228+
const result = await remove(1)
229+
230+
expect(result).toBe(true)
231+
232+
// Verify db was called correctly
233+
expect(db.deleteFrom).toHaveBeenCalledWith('gift_cards')
234+
expect(db.deleteFrom('gift_cards').where).toHaveBeenCalledWith('id', '=', 1)
235+
})
236+
237+
it('should throw an error if gift card not found', async () => {
238+
// Mock the database to return undefined for the gift card
239+
mock.module('@stacksjs/database', () => ({
240+
db: {
241+
selectFrom: mock(() => ({
242+
where: mock(() => ({
243+
selectAll: mock(() => ({
244+
executeTakeFirst: mock(() => Promise.resolve(undefined)),
245+
})),
246+
})),
247+
})),
248+
},
249+
}))
250+
251+
await expect(remove(999)).rejects.toThrow('Gift card with ID 999 not found')
252+
})
253+
})
254+
255+
describe('deactivate', () => {
256+
it('should deactivate a gift card', async () => {
257+
const result = await deactivate(1)
258+
259+
expect(result).toBe(true)
260+
261+
// Verify db was called correctly
262+
expect(db.updateTable).toHaveBeenCalledWith('gift_cards')
263+
expect(db.updateTable('gift_cards').set).toHaveBeenCalledWith({
264+
is_active: false,
265+
status: 'DEACTIVATED',
266+
updated_at: expect.any(Date),
267+
})
268+
})
269+
})
270+
271+
describe('checkBalance', () => {
272+
it('should check a valid gift card balance', async () => {
273+
const result = await checkBalance('GIFT123')
274+
275+
expect(result.valid).toBe(true)
276+
expect(result.balance).toBe(75)
277+
expect(result.currency).toBe('USD')
278+
})
279+
280+
it('should return invalid for non-existent gift card', async () => {
281+
// Mock the database to return undefined for the gift card
282+
mock.module('@stacksjs/database', () => ({
283+
db: {
284+
selectFrom: mock(() => ({
285+
where: mock(() => ({
286+
selectAll: mock(() => ({
287+
executeTakeFirst: mock(() => Promise.resolve(undefined)),
288+
})),
289+
})),
290+
})),
291+
},
292+
}))
293+
294+
const result = await checkBalance('INVALID')
295+
296+
expect(result.valid).toBe(false)
297+
expect(result.message).toBe('Gift card not found')
298+
})
299+
300+
it('should return invalid for inactive gift card', async () => {
301+
// Mock the database to return an inactive gift card
302+
mock.module('@stacksjs/database', () => ({
303+
db: {
304+
selectFrom: mock(() => ({
305+
where: mock(() => ({
306+
selectAll: mock(() => ({
307+
executeTakeFirst: mock(() => Promise.resolve({
308+
id: 2,
309+
code: 'INACTIVE',
310+
initial_balance: 100,
311+
current_balance: 100,
312+
currency: 'USD',
313+
status: 'DEACTIVATED',
314+
is_active: false,
315+
})),
316+
})),
317+
})),
318+
})),
319+
},
320+
}))
321+
322+
const result = await checkBalance('INACTIVE')
323+
324+
expect(result.valid).toBe(false)
325+
expect(result.message).toBe('Gift card is deactivated')
326+
})
327+
})
328+
})

0 commit comments

Comments
 (0)