11import { describe , expect , it , vi } from 'vitest'
22
3+ const mockIsDbError = vi . hoisted ( ( ) => vi . fn ( ( ) => false ) )
4+
5+ vi . mock ( 'astro:db' , ( ) => ( {
6+ isDbError : mockIsDbError ,
7+ } ) )
8+
39describe ( 'rateLimit utilities' , ( ) => {
410 it ( 'checkRateLimit returns limiter result' , async ( ) => {
511 vi . resetModules ( )
12+ mockIsDbError . mockReturnValue ( false )
613
714 vi . doMock ( '@actions/utils/environment/environmentActions' , ( ) => ( {
815 isProd : ( ) => false ,
916 } ) )
10- vi . doMock ( '@actions/utils/rateLimit /store' , ( ) => ( {
17+ vi . doMock ( '.. /store' , ( ) => ( {
1118 withRateLimitWindow : vi . fn ( ) ,
1219 } ) )
13- vi . doMock ( 'astro:db' , ( ) => ( {
14- isDbError : ( ) => false ,
15- } ) )
16-
1720 const { checkRateLimit } = await import ( '..' )
1821
1922 const limiter = {
@@ -26,19 +29,16 @@ describe('rateLimit utilities', () => {
2629
2730 it ( 'rateLimiters bypass in non-prod (success, reset = now + window)' , async ( ) => {
2831 vi . resetModules ( )
32+ mockIsDbError . mockReturnValue ( false )
2933 vi . useFakeTimers ( )
3034 vi . setSystemTime ( new Date ( '2025-01-01T00:00:00.000Z' ) )
3135
3236 vi . doMock ( '@actions/utils/environment/environmentActions' , ( ) => ( {
3337 isProd : ( ) => false ,
3438 } ) )
35- vi . doMock ( '@actions/utils/rateLimit /store' , ( ) => ( {
39+ vi . doMock ( '.. /store' , ( ) => ( {
3640 withRateLimitWindow : vi . fn ( ) ,
3741 } ) )
38- vi . doMock ( 'astro:db' , ( ) => ( {
39- isDbError : ( ) => false ,
40- } ) )
41-
4242 const { rateLimiters } = await import ( '..' )
4343
4444 const result = await rateLimiters . export . limit ( 'abc' )
@@ -53,6 +53,7 @@ describe('rateLimit utilities', () => {
5353
5454 it ( 'prod: empty identifier is treated as allowed without hitting store' , async ( ) => {
5555 vi . resetModules ( )
56+ mockIsDbError . mockReturnValue ( false )
5657 vi . useFakeTimers ( )
5758 vi . setSystemTime ( new Date ( '2025-01-01T00:00:00.000Z' ) )
5859
@@ -61,13 +62,9 @@ describe('rateLimit utilities', () => {
6162 vi . doMock ( '@actions/utils/environment/environmentActions' , ( ) => ( {
6263 isProd : ( ) => true ,
6364 } ) )
64- vi . doMock ( '@actions/utils/rateLimit /store' , ( ) => ( {
65+ vi . doMock ( '.. /store' , ( ) => ( {
6566 withRateLimitWindow,
6667 } ) )
67- vi . doMock ( 'astro:db' , ( ) => ( {
68- isDbError : ( ) => false ,
69- } ) )
70-
7168 const { rateLimiters } = await import ( '..' )
7269
7370 const result = await rateLimiters . export . limit ( '' )
@@ -80,6 +77,7 @@ describe('rateLimit utilities', () => {
8077
8178 it ( 'prod: initializes a window when missing/expired' , async ( ) => {
8279 vi . resetModules ( )
80+ mockIsDbError . mockReturnValue ( false )
8381 vi . useFakeTimers ( )
8482 vi . setSystemTime ( new Date ( '2025-01-01T00:00:00.000Z' ) )
8583
@@ -105,13 +103,9 @@ describe('rateLimit utilities', () => {
105103 vi . doMock ( '@actions/utils/environment/environmentActions' , ( ) => ( {
106104 isProd : ( ) => true ,
107105 } ) )
108- vi . doMock ( '@actions/utils/rateLimit /store' , ( ) => ( {
106+ vi . doMock ( '.. /store' , ( ) => ( {
109107 withRateLimitWindow,
110108 } ) )
111- vi . doMock ( 'astro:db' , ( ) => ( {
112- isDbError : ( ) => false ,
113- } ) )
114-
115109 const { rateLimiters } = await import ( '..' )
116110
117111 const result = await rateLimiters . delete . limit ( 'user-1' )
@@ -130,6 +124,7 @@ describe('rateLimit utilities', () => {
130124
131125 it ( 'prod: increments hits when under limit' , async ( ) => {
132126 vi . resetModules ( )
127+ mockIsDbError . mockReturnValue ( false )
133128 vi . useFakeTimers ( )
134129 vi . setSystemTime ( new Date ( '2025-01-01T00:00:00.000Z' ) )
135130
@@ -152,13 +147,9 @@ describe('rateLimit utilities', () => {
152147 vi . doMock ( '@actions/utils/environment/environmentActions' , ( ) => ( {
153148 isProd : ( ) => true ,
154149 } ) )
155- vi . doMock ( '@actions/utils/rateLimit /store' , ( ) => ( {
150+ vi . doMock ( '.. /store' , ( ) => ( {
156151 withRateLimitWindow,
157152 } ) )
158- vi . doMock ( 'astro:db' , ( ) => ( {
159- isDbError : ( ) => false ,
160- } ) )
161-
162153 const { rateLimiters } = await import ( '..' )
163154
164155 const result = await rateLimiters . contact . limit ( 'user-2' )
@@ -171,6 +162,7 @@ describe('rateLimit utilities', () => {
171162
172163 it ( 'prod: denies when at/over limit' , async ( ) => {
173164 vi . resetModules ( )
165+ mockIsDbError . mockReturnValue ( false )
174166 vi . useFakeTimers ( )
175167 vi . setSystemTime ( new Date ( '2025-01-01T00:00:00.000Z' ) )
176168
@@ -186,13 +178,9 @@ describe('rateLimit utilities', () => {
186178 vi . doMock ( '@actions/utils/environment/environmentActions' , ( ) => ( {
187179 isProd : ( ) => true ,
188180 } ) )
189- vi . doMock ( '@actions/utils/rateLimit /store' , ( ) => ( {
181+ vi . doMock ( '.. /store' , ( ) => ( {
190182 withRateLimitWindow,
191183 } ) )
192- vi . doMock ( 'astro:db' , ( ) => ( {
193- isDbError : ( ) => false ,
194- } ) )
195-
196184 const { rateLimiters } = await import ( '..' )
197185
198186 await expect ( rateLimiters . consent . limit ( 'user-3' ) ) . resolves . toEqual ( {
@@ -205,67 +193,61 @@ describe('rateLimit utilities', () => {
205193
206194 it ( 'prod: returns failure on db error' , async ( ) => {
207195 vi . resetModules ( )
196+ mockIsDbError . mockReturnValue ( true )
208197 vi . useFakeTimers ( )
209198 vi . setSystemTime ( new Date ( '2025-01-01T00:00:00.000Z' ) )
210199
211- const dbErr = new Error ( 'db' )
212-
200+ const dbErr = Object . assign ( new Error ( 'db' ) , { libsqlError : true as const } )
201+ const withRateLimitWindow = vi . fn ( async ( ) => {
202+ throw dbErr
203+ } )
213204 vi . doMock ( '@actions/utils/environment/environmentActions' , ( ) => ( {
214205 isProd : ( ) => true ,
215206 } ) )
216- vi . doMock ( '@actions/utils/rateLimit/store' , ( ) => ( {
217- withRateLimitWindow : vi . fn ( async ( ) => {
218- throw dbErr
219- } ) ,
220- } ) )
221- vi . doMock ( 'astro:db' , ( ) => ( {
222- isDbError : ( err : unknown ) => err === dbErr ,
207+ vi . doMock ( '../store' , ( ) => ( {
208+ withRateLimitWindow,
223209 } ) )
224210
225211 const { rateLimiters } = await import ( '..' )
226212
227213 const result = await rateLimiters . export . limit ( 'user-4' )
228214
229215 expect ( result ) . toEqual ( { success : false , reset : Date . now ( ) + 60_000 } )
216+ expect ( withRateLimitWindow ) . toHaveBeenCalledTimes ( 1 )
217+ expect ( mockIsDbError ) . toHaveBeenCalledTimes ( 1 )
230218
231219 vi . useRealTimers ( )
232220 } )
233221
234222 it ( 'prod: rethrows non-db errors' , async ( ) => {
235223 vi . resetModules ( )
224+ mockIsDbError . mockReturnValue ( false )
236225
237226 const nonDbErr = new Error ( 'boom' )
238227
239228 vi . doMock ( '@actions/utils/environment/environmentActions' , ( ) => ( {
240229 isProd : ( ) => true ,
241230 } ) )
242- vi . doMock ( '@actions/utils/rateLimit /store' , ( ) => ( {
231+ vi . doMock ( '.. /store' , ( ) => ( {
243232 withRateLimitWindow : vi . fn ( async ( ) => {
244233 throw nonDbErr
245234 } ) ,
246235 } ) )
247- vi . doMock ( 'astro:db' , ( ) => ( {
248- isDbError : ( ) => false ,
249- } ) )
250-
251236 const { rateLimiters } = await import ( '..' )
252237
253238 await expect ( rateLimiters . export . limit ( 'user-5' ) ) . rejects . toThrow ( 'boom' )
254239 } )
255240
256241 it ( 'checkContactRateLimit always allows in non-prod' , async ( ) => {
257242 vi . resetModules ( )
243+ mockIsDbError . mockReturnValue ( false )
258244
259245 vi . doMock ( '@actions/utils/environment/environmentActions' , ( ) => ( {
260246 isProd : ( ) => false ,
261247 } ) )
262- vi . doMock ( '@actions/utils/rateLimit /store' , ( ) => ( {
248+ vi . doMock ( '.. /store' , ( ) => ( {
263249 withRateLimitWindow : vi . fn ( ) ,
264250 } ) )
265- vi . doMock ( 'astro:db' , ( ) => ( {
266- isDbError : ( ) => false ,
267- } ) )
268-
269251 const { checkContactRateLimit } = await import ( '..' )
270252
271253 for ( let i = 0 ; i < 20 ; i += 1 ) {
@@ -275,19 +257,16 @@ describe('rateLimit utilities', () => {
275257
276258 it ( 'checkContactRateLimit enforces limits in prod' , async ( ) => {
277259 vi . resetModules ( )
260+ mockIsDbError . mockReturnValue ( false )
278261 vi . useFakeTimers ( )
279262 vi . setSystemTime ( new Date ( '2025-01-01T00:00:00.000Z' ) )
280263
281264 vi . doMock ( '@actions/utils/environment/environmentActions' , ( ) => ( {
282265 isProd : ( ) => true ,
283266 } ) )
284- vi . doMock ( '@actions/utils/rateLimit /store' , ( ) => ( {
267+ vi . doMock ( '.. /store' , ( ) => ( {
285268 withRateLimitWindow : vi . fn ( ) ,
286269 } ) )
287- vi . doMock ( 'astro:db' , ( ) => ( {
288- isDbError : ( ) => false ,
289- } ) )
290-
291270 const { checkContactRateLimit } = await import ( '..' )
292271
293272 for ( let i = 0 ; i < 5 ; i += 1 ) {
@@ -300,18 +279,15 @@ describe('rateLimit utilities', () => {
300279
301280 it ( 'checkContactRateLimit resets after window passes in prod' , async ( ) => {
302281 vi . resetModules ( )
282+ mockIsDbError . mockReturnValue ( false )
303283 vi . useFakeTimers ( )
304284
305285 vi . doMock ( '@actions/utils/environment/environmentActions' , ( ) => ( {
306286 isProd : ( ) => true ,
307287 } ) )
308- vi . doMock ( '@actions/utils/rateLimit /store' , ( ) => ( {
288+ vi . doMock ( '.. /store' , ( ) => ( {
309289 withRateLimitWindow : vi . fn ( ) ,
310290 } ) )
311- vi . doMock ( 'astro:db' , ( ) => ( {
312- isDbError : ( ) => false ,
313- } ) )
314-
315291 const { checkContactRateLimit } = await import ( '..' )
316292
317293 vi . setSystemTime ( new Date ( '2025-01-01T00:00:00.000Z' ) )
0 commit comments