55 upsertTestAccount ,
66 resetMockCounters ,
77 createMockCustomerBatch ,
8+ createMockCouponBatch ,
89 createPaginatedResponse ,
910 DatabaseValidator ,
1011 type MockStripeObject ,
@@ -19,6 +20,7 @@ describe('StripeSync Integration Tests', () => {
1920 let db : TestDatabase
2021 let validator : DatabaseValidator
2122 let mockCustomers : MockStripeObject [ ] = [ ]
23+ let mockCoupons : MockStripeObject [ ] = [ ]
2224
2325 beforeAll ( async ( ) => {
2426 db = await setupTestDatabase ( )
@@ -36,8 +38,13 @@ describe('StripeSync Integration Tests', () => {
3638
3739 resetMockCounters ( )
3840 mockCustomers = [ ]
41+ mockCoupons = [ ]
3942
40- await validator . clearAccountData ( TEST_ACCOUNT_ID , [ 'stripe.customers' , 'stripe.plans' ] )
43+ await validator . clearAccountData ( TEST_ACCOUNT_ID , [
44+ 'stripe.customers' ,
45+ 'stripe.plans' ,
46+ 'stripe.coupons' ,
47+ ] )
4148
4249 sync = await createTestStripeSync ( {
4350 databaseUrl : db . databaseUrl ,
@@ -59,6 +66,20 @@ describe('StripeSync Integration Tests', () => {
5966 Promise . resolve ( mockCustomers . find ( ( c ) => c . id === id ) ?? null )
6067 ) ,
6168 }
69+
70+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
71+ ; ( sync . stripe as any ) . coupons = {
72+ list : vi
73+ . fn ( )
74+ . mockImplementation ( ( params ) =>
75+ Promise . resolve ( createPaginatedResponse ( mockCoupons , params ) )
76+ ) ,
77+ retrieve : vi
78+ . fn ( )
79+ . mockImplementation ( ( id : string ) =>
80+ Promise . resolve ( mockCoupons . find ( ( c ) => c . id === id ) ?? null )
81+ ) ,
82+ }
6283 } )
6384
6485 it ( 'should have validator connected to database' , async ( ) => {
@@ -142,4 +163,89 @@ describe('StripeSync Integration Tests', () => {
142163 )
143164 } )
144165 } )
166+
167+ describe ( 'coupon fullSync' , ( ) => {
168+ it ( 'should sync all coupons via fullSync' , async ( ) => {
169+ mockCoupons = createMockCouponBatch ( 150 )
170+
171+ const result = await sync . fullSync ( [ 'coupon' ] , true , 1 , 50 , false )
172+
173+ expect ( result . totalSynced ) . toStrictEqual ( 150 )
174+
175+ const countInDb = await validator . getRowCount ( 'stripe.coupons' , TEST_ACCOUNT_ID )
176+ expect ( countInDb ) . toStrictEqual ( 150 )
177+
178+ const couponsInDb = await validator . getColumnValues ( 'stripe.coupons' , 'id' , TEST_ACCOUNT_ID )
179+ expect ( couponsInDb ) . toStrictEqual ( mockCoupons . map ( ( c ) => c . id ) )
180+ } )
181+
182+ it ( 'should sync new coupons for incremental consistency' , async ( ) => {
183+ await sync . fullSync ( [ 'coupon' ] , true , 2 , 50 , false )
184+
185+ mockCoupons = createMockCouponBatch ( 50 )
186+
187+ const result = await sync . fullSync ( [ 'coupon' ] , true , 2 , 50 , false , 0 )
188+
189+ expect ( result . totalSynced ) . toStrictEqual ( 50 )
190+
191+ const countInDb = await validator . getRowCount ( 'stripe.coupons' , TEST_ACCOUNT_ID )
192+ expect ( countInDb ) . toStrictEqual ( 50 )
193+
194+ const couponsInDb = await validator . getColumnValues ( 'stripe.coupons' , 'id' , TEST_ACCOUNT_ID )
195+ expect ( couponsInDb ) . toStrictEqual ( mockCoupons . map ( ( c ) => c . id ) )
196+ } )
197+
198+ it ( 'should backfill historical coupons and then pick up new coupons on next sync' , async ( ) => {
199+ const historicalStartTimestamp = Math . floor ( Date . now ( ) / 1000 ) - 10000
200+ const historicalCoupons = createMockCouponBatch ( 100 , historicalStartTimestamp )
201+ mockCoupons = historicalCoupons
202+
203+ const result = await sync . fullSync ( [ 'coupon' ] , true , 2 , 50 , false , 0 )
204+ expect ( result . totalSynced ) . toStrictEqual ( 100 )
205+
206+ let countInDb = await validator . getRowCount ( 'stripe.coupons' , TEST_ACCOUNT_ID )
207+ expect ( countInDb ) . toStrictEqual ( 100 )
208+
209+ const newStartTimestamp = Math . floor ( Date . now ( ) / 1000 )
210+ const newCoupons = createMockCouponBatch ( 3 , newStartTimestamp )
211+ mockCoupons = [ ...newCoupons , ...mockCoupons ]
212+
213+ const couponsAfterBackfill = await validator . getColumnValues (
214+ 'stripe.coupons' ,
215+ 'id' ,
216+ TEST_ACCOUNT_ID
217+ )
218+ expect ( couponsAfterBackfill ) . toStrictEqual ( historicalCoupons . map ( ( c ) => c . id ) )
219+
220+ await sync . fullSync ( [ 'coupon' ] , true , 2 , 50 , false , 0 )
221+
222+ countInDb = await validator . getRowCount ( 'stripe.coupons' , TEST_ACCOUNT_ID )
223+ expect ( countInDb ) . toStrictEqual ( 103 )
224+
225+ const couponsAfterIncremental = await validator . getColumnValues (
226+ 'stripe.coupons' ,
227+ 'id' ,
228+ TEST_ACCOUNT_ID
229+ )
230+ expect ( couponsAfterIncremental ) . toStrictEqual (
231+ [ ...historicalCoupons , ...newCoupons ] . map ( ( c ) => c . id )
232+ )
233+ } )
234+
235+ it ( 'should handle deleted coupons during sync' , async ( ) => {
236+ mockCoupons = createMockCouponBatch ( 10 )
237+
238+ await sync . fullSync ( [ 'coupon' ] , true , 1 , 50 , false )
239+
240+ const countInDb = await validator . getRowCount ( 'stripe.coupons' , TEST_ACCOUNT_ID )
241+ expect ( countInDb ) . toStrictEqual ( 10 )
242+
243+ mockCoupons = mockCoupons . map ( ( c , i ) => ( i < 3 ? { ...c , deleted : true } : c ) )
244+
245+ await sync . fullSync ( [ 'coupon' ] , true , 1 , 50 , false , 0 )
246+
247+ const finalCount = await validator . getRowCount ( 'stripe.coupons' , TEST_ACCOUNT_ID )
248+ expect ( finalCount ) . toStrictEqual ( 10 )
249+ } )
250+ } )
145251} )
0 commit comments