tryConsume(1), "consume #{$i} should succeed"); } self::assertFalse($limiter->tryConsume(1), 'consume #7 should be denied'); } public function testRefillRestoresTokensOverTime(): void { $clock = FixedClock::at('2026-04-29T00:00:00Z'); $limiter = new RateLimiter($clock, refillPerSecond: 3.0, capacity: 6.0); for ($i = 0; $i < 6; $i++) { $limiter->tryConsume(1); } self::assertFalse($limiter->tryConsume(1)); $clock->advance('+1 second'); // After 1s @ 3/s we should have 3 fresh tokens. self::assertTrue($limiter->tryConsume(1)); self::assertTrue($limiter->tryConsume(1)); self::assertTrue($limiter->tryConsume(1)); self::assertFalse($limiter->tryConsume(1)); } public function testKeysAreIsolated(): void { $clock = FixedClock::at('2026-04-29T00:00:00Z'); $limiter = new RateLimiter($clock, refillPerSecond: 1.0, capacity: 2.0); self::assertTrue($limiter->tryConsume(1)); self::assertTrue($limiter->tryConsume(1)); self::assertFalse($limiter->tryConsume(1)); // Token 2's bucket is untouched. self::assertTrue($limiter->tryConsume(2)); self::assertTrue($limiter->tryConsume(2)); } }