@@ -344,7 +344,7 @@ public function testRateLimit()
344344 $ cachedKeySet = new CachedKeySet (
345345 $ this ->testJwksUri ,
346346 $ this ->getMockHttpClient ($ this ->testJwks1 , $ shouldBeCalledTimes ),
347- $ factory = $ this ->getMockHttpFactory ($ shouldBeCalledTimes ),
347+ $ this ->getMockHttpFactory ($ shouldBeCalledTimes ),
348348 new TestMemoryCacheItemPool (),
349349 10 , // expires after seconds
350350 true // enable rate limiting
@@ -358,6 +358,54 @@ public function testRateLimit()
358358 $ this ->assertFalse (isset ($ cachedKeySet [$ invalidKid ]));
359359 }
360360
361+ public function testRateLimitWithExpiresAfter ()
362+ {
363+ // We request the key 17 times, HTTP should only be called 15 times
364+ $ shouldBeCalledTimes = 10 ;
365+ $ cachedTimes = 2 ;
366+ $ afterExpirationTimes = 5 ;
367+
368+ $ totalHttpTimes = $ shouldBeCalledTimes + $ afterExpirationTimes ;
369+
370+ $ cachePool = new TestMemoryCacheItemPool ();
371+
372+ // Instantiate the cached key set
373+ $ cachedKeySet = new CachedKeySet (
374+ $ this ->testJwksUri ,
375+ $ this ->getMockHttpClient ($ this ->testJwks1 , $ totalHttpTimes ),
376+ $ this ->getMockHttpFactory ($ totalHttpTimes ),
377+ $ cachePool ,
378+ 10 , // expires after seconds
379+ true // enable rate limiting
380+ );
381+
382+ // Set the rate limit cache to expire after 1 second
383+ $ cacheItem = $ cachePool ->getItem ('jwksratelimitjwkshttpsjwk.uri ' );
384+ $ cacheItem ->set ([
385+ 'expiry ' => new \DateTime ('+1 second ' , new \DateTimeZone ('UTC ' )),
386+ 'callsPerMinute ' => 0 ,
387+ ]);
388+ $ cacheItem ->expiresAfter (1 );
389+ $ cachePool ->save ($ cacheItem );
390+
391+ $ invalidKid = 'invalidkey ' ;
392+ for ($ i = 0 ; $ i < $ shouldBeCalledTimes ; $ i ++) {
393+ $ this ->assertFalse (isset ($ cachedKeySet [$ invalidKid ]));
394+ }
395+
396+ // The next calls do not call HTTP
397+ for ($ i = 0 ; $ i < $ cachedTimes ; $ i ++) {
398+ $ this ->assertFalse (isset ($ cachedKeySet [$ invalidKid ]));
399+ }
400+
401+ sleep (1 ); // wait for cache to expire
402+
403+ // These calls DO call HTTP because the cache has expired
404+ for ($ i = 0 ; $ i < $ afterExpirationTimes ; $ i ++) {
405+ $ this ->assertFalse (isset ($ cachedKeySet [$ invalidKid ]));
406+ }
407+ }
408+
361409 /**
362410 * @dataProvider provideFullIntegration
363411 */
@@ -466,7 +514,10 @@ final class TestMemoryCacheItemPool implements CacheItemPoolInterface
466514
467515 public function getItem ($ key ): CacheItemInterface
468516 {
469- return current ($ this ->getItems ([$ key ]));
517+ $ item = current ($ this ->getItems ([$ key ]));
518+ $ item ->expiresAt (null ); // mimic symfony cache behavior
519+
520+ return $ item ;
470521 }
471522
472523 public function getItems (array $ keys = []): iterable
0 commit comments