@@ -257,6 +257,87 @@ describe('CacheOperations', () => {
257257 await cacheOperations . bulkEvict ( context ) ;
258258 expect ( mockCacheProvider . evict ) . toHaveBeenCalledTimes ( 1 ) ;
259259 } ) ;
260+
261+ describe ( 'debounceMs' , ( ) => {
262+ beforeEach ( ( ) => jest . useFakeTimers ( ) ) ;
263+
264+ it ( 'should collapse multiple calls within the debounce window into one eviction' , async ( ) => {
265+ const context : CacheEvictContext = {
266+ keys : [ 'k1' ] ,
267+ cacheProvider : mockCacheProvider ,
268+ debounceMs : 100 ,
269+ } ;
270+
271+ void cacheOperations . bulkEvict ( context ) ;
272+ void cacheOperations . bulkEvict ( context ) ;
273+ void cacheOperations . bulkEvict ( context ) ;
274+
275+ expect ( mockCacheProvider . evict ) . not . toHaveBeenCalled ( ) ;
276+
277+ jest . advanceTimersByTime ( 100 ) ;
278+ await Promise . resolve ( ) ; // flush microtasks
279+
280+ expect ( mockCacheProvider . evict ) . toHaveBeenCalledTimes ( 1 ) ;
281+ } ) ;
282+
283+ it ( 'should evict immediately when debounceMs is not set' , async ( ) => {
284+ const context : CacheEvictContext = { keys : [ 'k1' ] , cacheProvider : mockCacheProvider } ;
285+ await cacheOperations . bulkEvict ( context ) ;
286+ expect ( mockCacheProvider . evict ) . toHaveBeenCalledTimes ( 1 ) ;
287+ } ) ;
288+
289+ it ( 'should debounce independently for different key sets' , async ( ) => {
290+ void cacheOperations . bulkEvict ( {
291+ keys : [ 'k1' ] ,
292+ cacheProvider : mockCacheProvider ,
293+ debounceMs : 100 ,
294+ } ) ;
295+ void cacheOperations . bulkEvict ( {
296+ keys : [ 'k2' ] ,
297+ cacheProvider : mockCacheProvider ,
298+ debounceMs : 100 ,
299+ } ) ;
300+
301+ jest . advanceTimersByTime ( 100 ) ;
302+ await Promise . resolve ( ) ;
303+
304+ expect ( mockCacheProvider . evict ) . toHaveBeenCalledTimes ( 2 ) ;
305+ } ) ;
306+
307+ it ( 'should reset the timer when called again within the window' , async ( ) => {
308+ const context : CacheEvictContext = {
309+ keys : [ 'k1' ] ,
310+ cacheProvider : mockCacheProvider ,
311+ debounceMs : 100 ,
312+ } ;
313+
314+ void cacheOperations . bulkEvict ( context ) ;
315+ jest . advanceTimersByTime ( 50 ) ;
316+ void cacheOperations . bulkEvict ( context ) ; // resets timer
317+
318+ jest . advanceTimersByTime ( 50 ) ; // 100ms total elapsed, but timer was reset at 50ms
319+ expect ( mockCacheProvider . evict ) . not . toHaveBeenCalled ( ) ;
320+
321+ jest . advanceTimersByTime ( 50 ) ; // 50ms after reset — timer fires now
322+ await Promise . resolve ( ) ;
323+
324+ expect ( mockCacheProvider . evict ) . toHaveBeenCalledTimes ( 1 ) ;
325+ } ) ;
326+
327+ it ( 'should clear pending timers on module destroy without executing eviction' , ( ) => {
328+ const context : CacheEvictContext = {
329+ keys : [ 'k1' ] ,
330+ cacheProvider : mockCacheProvider ,
331+ debounceMs : 100 ,
332+ } ;
333+ void cacheOperations . bulkEvict ( context ) ;
334+
335+ cacheOperations . onModuleDestroy ( ) ;
336+ jest . advanceTimersByTime ( 200 ) ;
337+
338+ expect ( mockCacheProvider . evict ) . not . toHaveBeenCalled ( ) ;
339+ } ) ;
340+ } ) ;
260341 } ) ;
261342
262343 it ( 'should increase the wait time exponentially with each retry attempt' , async ( ) => {
0 commit comments