1
+ import { SimpleResourceStorage as SimpleStorage } from '@discue/mongodb-resource-client'
2
+ import { expect } from 'chai'
3
+ import { randomUUID as uuid } from 'node:crypto'
4
+ import EventStorage from '../../../lib/billing/event-log-storage.js'
5
+ import SubscriptionStorage from '../../../lib/billing/subscription-hook-storage.js'
6
+ import SubscriptionInfoStorage from '../../../lib/billing/subscription-info.js'
7
+ import subscriptionCreated from '../../billing-fixtues/subscription-created.js'
8
+ import mongoClient from "../mongodb-client.js"
9
+
10
+ const client = mongoClient ( )
11
+
12
+ const eventStorageBackend = new SimpleStorage ( { collectionName : '_events' , client } )
13
+ const eventStorage = new EventStorage ( { storage : eventStorageBackend } )
14
+
15
+ const subscriptionStorageBackend = new SimpleStorage ( { collectionName : '_subscriptions' , client } )
16
+ const subscriptionStorage = new SubscriptionStorage ( { storage : subscriptionStorageBackend , eventStorage } )
17
+
18
+ const subscriptionInfoBackend = new SimpleStorage ( { collectionName : '_subscriptions' , client } )
19
+ const subscriptionInfo = new SubscriptionInfoStorage ( { storage : subscriptionInfoBackend , eventStorage } )
20
+
21
+ describe ( 'SubscriptionHook Billing API' , ( ) => {
22
+
23
+ let nextId
24
+
25
+ beforeEach ( ( ) => {
26
+ nextId = `sub_${ uuid ( ) } `
27
+ } )
28
+
29
+ afterEach ( async ( ) => {
30
+ await subscriptionStorageBackend . delete ( nextId )
31
+ } )
32
+
33
+ after ( async ( ) => {
34
+ await subscriptionStorageBackend . close ( )
35
+ await eventStorageBackend . close ( )
36
+ await subscriptionInfoBackend . close ( )
37
+ } )
38
+
39
+ after ( async ( ) => {
40
+ return client . close ( )
41
+ } )
42
+
43
+ describe ( '.getSubscriptionsByCustomData' , ( ) => {
44
+ it ( 'returns a subscription by customData' , async ( ) => {
45
+ const subscription = subscriptionCreated ( )
46
+ subscription . data . id = nextId
47
+ await subscriptionStorage . store ( subscription )
48
+
49
+ const subs = await subscriptionInfo . getSubscriptionsByCustomData ( {
50
+ ids : [ "77693b60-e2b5-404b-a7f0-51d4dda7284a" ]
51
+ } )
52
+
53
+ expect ( subs ) . to . have . length ( 1 )
54
+ expect ( subs . at ( 0 ) . id ) . to . equal ( nextId )
55
+ } )
56
+
57
+ it ( 'returns null if no subscription was found' , async ( ) => {
58
+ const subscription = subscriptionCreated ( )
59
+ subscription . data . id = nextId
60
+ await subscriptionStorage . store ( subscription )
61
+
62
+ const info = await subscriptionInfo . getSubscriptionsByCustomData ( { ids : [ 123 ] } )
63
+ expect ( info ) . to . have . length ( 0 )
64
+ } )
65
+ } )
66
+
67
+ describe ( '.getStatusTrailByCustomData' , ( ) => {
68
+ it ( 'returns a history' , async ( ) => {
69
+ const subscription = subscriptionCreated ( )
70
+ subscription . data . id = nextId
71
+ await subscriptionStorage . store ( subscription )
72
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 200 ) )
73
+
74
+ const subs = await subscriptionInfo . getStatusTrailByCustomData ( {
75
+ ids : [ "77693b60-e2b5-404b-a7f0-51d4dda7284a" ]
76
+ } )
77
+
78
+ expect ( subs ) . to . have . length ( 1 )
79
+ expect ( subs . at ( 0 ) . action ) . to . equal ( 'create' )
80
+ } )
81
+
82
+ it ( 'returns null if no subscription was found' , async ( ) => {
83
+ const subscription = subscriptionCreated ( )
84
+ subscription . data . id = nextId
85
+ await subscriptionStorage . store ( subscription )
86
+
87
+ const info = await subscriptionInfo . getSubscriptionsByCustomData ( { ids : [ 123 ] } )
88
+ expect ( info ) . to . have . length ( 0 )
89
+ } )
90
+ } )
91
+
92
+ describe ( '.getActiveProductsByCustomData' , ( ) => {
93
+ it ( 'returns empty list if no products active' , async ( ) => {
94
+ const subscription = subscriptionCreated ( )
95
+ subscription . data . id = nextId
96
+ await subscriptionStorage . store ( subscription )
97
+
98
+ const info = await subscriptionInfo . getActiveProductsByCustomData ( {
99
+ ids : [ "123" ]
100
+ } )
101
+ expect ( Object . keys ( info ) ) . to . have . length ( 0 )
102
+ } )
103
+
104
+ it ( 'returns empty list if sub status is canceled' , async ( ) => {
105
+ const subscription = subscriptionCreated ( )
106
+ subscription . data . id = nextId
107
+ subscription . data . status = 'canceled'
108
+ await subscriptionStorage . store ( subscription )
109
+
110
+ const info = await subscriptionInfo . getActiveProductsByCustomData ( {
111
+ ids : [ "77693b60-e2b5-404b-a7f0-51d4dda7284a" ]
112
+ } )
113
+ expect ( Object . keys ( info ) ) . to . have . length ( 0 )
114
+ } )
115
+
116
+ it ( 'returns empty list if sub status is paused' , async ( ) => {
117
+ const subscription = subscriptionCreated ( )
118
+ subscription . data . id = nextId
119
+ subscription . data . status = 'paused'
120
+ await subscriptionStorage . store ( subscription )
121
+
122
+ const info = await subscriptionInfo . getActiveProductsByCustomData ( {
123
+ ids : [ "77693b60-e2b5-404b-a7f0-51d4dda7284a" ]
124
+ } )
125
+ expect ( Object . keys ( info ) ) . to . have . length ( 0 )
126
+ } )
127
+
128
+ it ( 'returns active products if sub status is active' , async ( ) => {
129
+ const subscription = subscriptionCreated ( )
130
+ subscription . data . id = nextId
131
+ await subscriptionStorage . store ( subscription )
132
+
133
+ const subs = await subscriptionInfo . getActiveProductsByCustomData ( {
134
+ ids : [ "77693b60-e2b5-404b-a7f0-51d4dda7284a" ]
135
+ } )
136
+
137
+ expect ( subs ) . to . have . keys ( subscription . data . id )
138
+ const products = subs [ subscription . data . id ]
139
+
140
+ expect ( products ) . to . have . length ( 2 )
141
+ expect ( products ) . to . include ( 'pro_01gsz4t5hdjse780zja8vvr7jg' )
142
+ expect ( products ) . to . include ( 'pro_01h1vjes1y163xfj1rh1tkfb65' )
143
+ } )
144
+
145
+ it ( 'returns active products if sub status is trialing' , async ( ) => {
146
+ const subscription = subscriptionCreated ( )
147
+ subscription . data . id = nextId
148
+ subscription . data . status = 'trialing'
149
+ await subscriptionStorage . store ( subscription )
150
+
151
+ const subs = await subscriptionInfo . getActiveProductsByCustomData ( {
152
+ ids : [ "77693b60-e2b5-404b-a7f0-51d4dda7284a" ]
153
+ } )
154
+
155
+ expect ( subs ) . to . have . keys ( subscription . data . id )
156
+ const products = subs [ subscription . data . id ]
157
+
158
+ expect ( products ) . to . have . length ( 2 )
159
+ expect ( products ) . to . include ( 'pro_01gsz4t5hdjse780zja8vvr7jg' )
160
+ expect ( products ) . to . include ( 'pro_01h1vjes1y163xfj1rh1tkfb65' )
161
+ } )
162
+
163
+ it ( 'returns active products if sub status is past_due' , async ( ) => {
164
+ const subscription = subscriptionCreated ( )
165
+ subscription . data . id = nextId
166
+ subscription . data . status = 'past_due'
167
+ await subscriptionStorage . store ( subscription )
168
+
169
+ const subs = await subscriptionInfo . getActiveProductsByCustomData ( {
170
+ ids : [ "77693b60-e2b5-404b-a7f0-51d4dda7284a" ]
171
+ } )
172
+
173
+ expect ( subs ) . to . have . keys ( subscription . data . id )
174
+ const products = subs [ subscription . data . id ]
175
+
176
+ expect ( products ) . to . have . length ( 2 )
177
+ expect ( products ) . to . include ( 'pro_01gsz4t5hdjse780zja8vvr7jg' )
178
+ expect ( products ) . to . include ( 'pro_01h1vjes1y163xfj1rh1tkfb65' )
179
+ } )
180
+
181
+ it ( 'does not return inactive products' , async ( ) => {
182
+ const subscription = subscriptionCreated ( )
183
+ subscription . data . id = nextId
184
+ subscription . data . items [ 0 ] . status = 'inactive'
185
+ await subscriptionStorage . store ( subscription )
186
+
187
+ const subs = await subscriptionInfo . getActiveProductsByCustomData ( {
188
+ ids : [ "77693b60-e2b5-404b-a7f0-51d4dda7284a" ]
189
+ } )
190
+
191
+ expect ( subs ) . to . have . keys ( subscription . data . id )
192
+ const products = subs [ subscription . data . id ]
193
+
194
+ expect ( products ) . to . have . length ( 1 )
195
+ expect ( products ) . to . include ( 'pro_01h1vjes1y163xfj1rh1tkfb65' )
196
+ } )
197
+
198
+ it ( 'does not return products with status trialing' , async ( ) => {
199
+ const subscription = subscriptionCreated ( )
200
+ subscription . data . id = nextId
201
+ subscription . data . items [ 0 ] . status = 'trialing'
202
+ await subscriptionStorage . store ( subscription )
203
+
204
+ const subs = await subscriptionInfo . getActiveProductsByCustomData ( {
205
+ ids : [ "77693b60-e2b5-404b-a7f0-51d4dda7284a" ]
206
+ } )
207
+
208
+ expect ( subs ) . to . have . keys ( subscription . data . id )
209
+ const products = subs [ subscription . data . id ]
210
+
211
+ expect ( products ) . to . have . length ( 2 )
212
+ expect ( products ) . to . include ( 'pro_01gsz4t5hdjse780zja8vvr7jg' )
213
+ expect ( products ) . to . include ( 'pro_01h1vjes1y163xfj1rh1tkfb65' )
214
+ } )
215
+ } )
216
+ } )
0 commit comments