Files

79 lines
2.5 KiB
PHP

<?php
declare(strict_types=1);
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Support\Facades\RateLimiter;
beforeEach(function () {
// Pest.php disables ThrottleRequests globally — re-enable for these tests.
$this->withMiddleware(ThrottleRequests::class);
RateLimiter::clear('login');
});
test('API login throttle eventually blocks after enough failed attempts', function () {
// The login endpoint stacks two protections: middleware throttle:10,1 and a
// controller-level RateLimiter keyed by IP+email (configurable via
// security_auth.login_max_attempts, default 5). After enough hits, one of
// them must kick in and return 429.
$sawBlock = false;
for ($i = 0; $i < 15; $i++) {
$r = $this->postJson('/api/v1/login', ['email' => 'x@x.com', 'password' => 'wrong']);
if ($r->getStatusCode() === 429) {
$sawBlock = true;
break;
}
}
expect($sawBlock)->toBeTrue();
});
test('API forgot-password throttle blocks after 5 requests', function () {
for ($i = 0; $i < 5; $i++) {
$this->postJson('/api/v1/forgot-password', ['email' => 'x@x.com']);
}
$this->postJson('/api/v1/forgot-password', ['email' => 'x@x.com'])
->assertStatus(429);
});
test('API OTP send throttle blocks after 5 requests', function () {
for ($i = 0; $i < 5; $i++) {
$this->postJson('/api/v1/otp/send', ['email' => 'x@x.com']);
}
$this->postJson('/api/v1/otp/send', ['email' => 'x@x.com'])
->assertStatus(429);
});
test('API register throttle blocks after 5 requests', function () {
for ($i = 0; $i < 5; $i++) {
$this->postJson('/api/v1/register', []);
}
$this->postJson('/api/v1/register', [])->assertStatus(429);
});
test('2FA verify throttle blocks after 5 requests', function () {
for ($i = 0; $i < 5; $i++) {
$this->post('/2fa', ['code' => '123456']);
}
$this->post('/2fa', ['code' => '123456'])->assertStatus(429);
});
test('different IPs do not share the same rate-limit bucket', function () {
for ($i = 0; $i < 5; $i++) {
$this->call('POST', '/api/v1/forgot-password',
parameters: ['email' => 'x@x.com'],
server: ['REMOTE_ADDR' => '10.0.0.1']
);
}
// Same email, different IP — should be fine
$r = $this->call('POST', '/api/v1/forgot-password',
parameters: ['email' => 'x@x.com'],
server: ['REMOTE_ADDR' => '10.0.0.2']
);
expect($r->getStatusCode())->not->toBe(429);
});