service = new RateLimiterService(); } public function testIpBlockEscalationAndUnblockFlow(): void { $cfg = config('RateLimiter'); $fingerprint = 'smoke-ip-' . uniqid('', true); // Initially no IP block. $this->assertNull($this->service->getIpBlock($fingerprint)); // Record enough failures to trigger soft block. $result = null; for ($i = 0; $i < $cfg->ipBlock['violation_soft']; $i++) { $result = $this->service->recordIpFailure($fingerprint); } $this->assertIsArray($result); $this->assertSame('soft', $result['level']); // Hitting again while blocked should eventually escalate to hard. $this->service->checkIp($fingerprint, 'jwtApi'); // return soft, escalate to medium $this->service->checkIp($fingerprint, 'jwtApi'); // return medium, escalate to hard $blocked = $this->service->checkIp($fingerprint, 'jwtApi'); // return hard $this->assertIsArray($blocked); $this->assertSame('hard', $blocked['level']); $this->service->unblockIp($fingerprint); $this->assertNull($this->service->getIpBlock($fingerprint)); } public function testUserFailureFlowAndManualUnblock(): void { $cfg = config('RateLimiter'); $identity = 'smoke-user-' . uniqid('', true) . '@example.com'; $this->assertNull($this->service->getUserBlock($identity)); $result = null; for ($i = 0; $i < $cfg->authApi['violation_soft']; $i++) { $result = $this->service->recordUserFailure($identity, 'authApi'); } $this->assertIsArray($result); $this->assertSame('soft', $result['level']); // Re-hits while blocked escalate from soft -> medium -> hard. $this->service->checkUser($identity); // return soft, escalate to medium $this->service->checkUser($identity); // return medium, escalate to hard $blocked = $this->service->checkUser($identity); // return hard $this->assertIsArray($blocked); $this->assertSame('hard', $blocked['level']); $this->service->unblockUser($identity); $this->assertNull($this->service->getUserBlock($identity)); } public function testJwtUserThrottleToSoftBlock(): void { $cfg = config('RateLimiter'); $identity = 'smoke-throttle-' . uniqid('', true) . '@example.com'; // First "limit" requests should pass. for ($i = 0; $i < $cfg->jwtApi['limit']; $i++) { $this->assertNull($this->service->checkUserThrottle($identity, 'jwtApi')); } // Over-limit attempts should return throttle first, then soft block after enough violations. $throttle = $this->service->checkUserThrottle($identity, 'jwtApi'); $this->assertIsArray($throttle); $this->assertSame('throttle', $throttle['level']); $blocked = null; for ($i = 0; $i < $cfg->jwtApi['violation_soft']; $i++) { $blocked = $this->service->checkUserThrottle($identity, 'jwtApi'); } $this->assertIsArray($blocked); $this->assertContains($blocked['level'], ['soft', 'medium', 'hard']); $this->service->unblockUser($identity); $this->assertNull($this->service->getUserBlock($identity)); } }