setUpRedis(); } protected function tearDown(): void { parent::tearDown(); $this->tearDownRedis(); } public function testItLocksTasksWhenNoSlotAvailable() { $store = []; (new DurationLimiter($this->redis(), 'key', 2, 2))->block(0, function () use (&$store) { $store[] = 1; }); (new DurationLimiter($this->redis(), 'key', 2, 2))->block(0, function () use (&$store) { $store[] = 2; }); try { (new DurationLimiter($this->redis(), 'key', 2, 2))->block(0, function () use (&$store) { $store[] = 3; }); } catch (Throwable $e) { $this->assertInstanceOf(LimiterTimeoutException::class, $e); } $this->assertEquals([1, 2], $store); sleep(2); (new DurationLimiter($this->redis(), 'key', 2, 2))->block(0, function () use (&$store) { $store[] = 3; }); $this->assertEquals([1, 2, 3], $store); } public function testItFailsImmediatelyOrRetriesForAWhileBasedOnAGivenTimeout() { $store = []; (new DurationLimiter($this->redis(), 'key', 1, 1))->block(2, function () use (&$store) { $store[] = 1; }); try { (new DurationLimiter($this->redis(), 'key', 1, 1))->block(0, function () use (&$store) { $store[] = 2; }); } catch (Throwable $e) { $this->assertInstanceOf(LimiterTimeoutException::class, $e); } (new DurationLimiter($this->redis(), 'key', 1, 1))->block(2, function () use (&$store) { $store[] = 3; }); $this->assertEquals([1, 3], $store); } public function testItReturnsTheCallbackResult() { $limiter = new DurationLimiter($this->redis(), 'key', 1, 1); $result = $limiter->block(1, function () { return 'foo'; }); $this->assertSame('foo', $result); } private function redis() { return $this->redis['phpredis']->connection(); } }