get('/admin/login')->assertOk(); $this->get('/admin/captcha') ->assertOk() ->assertHeader('Content-Type', 'image/png'); } public function test_admin_can_login_with_valid_captcha_and_credentials(): void { config([ 'app.admin_user' => 'admin', 'app.admin_password' => 'secret-pass', ]); $captcha = 'ABCD1'; $response = $this ->withSession(['admin_captcha_hash' => hash('sha256', strtolower($captcha))]) ->post('/admin/login', [ 'username' => 'admin', 'password' => 'secret-pass', 'captcha' => $captcha, ]); $response->assertRedirect(route('admin.dashboard')); $this->assertTrue((bool) session('admin_authenticated')); $this->assertSame('admin', session('admin_username')); } public function test_admin_login_is_rate_limited_when_too_many_attempts(): void { config([ 'app.admin_user' => 'admin', 'app.admin_password' => 'secret-pass', ]); $key = 'admin|127.0.0.1'; RateLimiter::clear($key); for ($attempt = 0; $attempt < 5; $attempt++) { RateLimiter::hit($key, 120); } $response = $this ->withServerVariables(['REMOTE_ADDR' => '127.0.0.1']) ->withSession(['admin_captcha_hash' => hash('sha256', 'abcde')]) ->from('/admin/login') ->post('/admin/login', [ 'username' => 'admin', 'password' => 'wrong', 'captcha' => 'ABCDE', ]); $response->assertRedirect('/admin/login'); $response->assertSessionHasErrors(['username']); RateLimiter::clear($key); } }