create([ 'username' => 'okagent', 'password' => 'secret12', 'status' => 1, ]); $this->post('/user/login', [ 'username' => 'okagent', 'password' => 'secret12', ])->assertOk() ->assertJson([ 'code' => 0, 'msg' => '登录成功', 'data' => route('user.home'), ]); $this->assertAuthenticated('agent'); } #[Test] public function ajax_login_rejects_bad_password(): void { User::query()->create([ 'username' => 'okagent', 'password' => 'secret12', 'status' => 1, ]); $response = $this->post('/user/login', [ 'username' => 'okagent', 'password' => 'wrongpass', ])->assertOk() ->assertJsonPath('code', 1); $this->assertStringContainsString('用户名或密码错误', (string) $response->json('msg')); $this->assertGuest('agent'); } #[Test] public function login_is_rate_limited_after_failures(): void { User::query()->create([ 'username' => 'okagent', 'password' => 'secret12', 'status' => 1, ]); for ($i = 0; $i < 5; $i++) { $this->post('/user/login', [ 'username' => 'okagent', 'password' => 'bad-password', ])->assertOk()->assertJsonPath('code', 1); } $response = $this->post('/user/login', [ 'username' => 'okagent', 'password' => 'secret12', ])->assertOk() ->assertJsonPath('code', 1); $this->assertStringContainsString('登陆失败次数过多', (string) $response->json('msg')); } #[Test] public function account_is_locked_after_five_failed_attempts(): void { $agent = User::query()->create([ 'username' => 'okagent', 'password' => 'secret12', 'status' => 1, ]); for ($i = 4; $i >= 1; $i--) { $response = $this->post('/user/login', [ 'username' => 'okagent', 'password' => 'bad-password', ])->assertOk()->assertJsonPath('code', 1); $this->assertStringContainsString('剩余 '.$i.' 次', (string) $response->json('msg')); } $this->post('/user/login', [ 'username' => 'okagent', 'password' => 'bad-password', ])->assertOk() ->assertJsonPath('code', 1) ->assertJsonPath('msg', '密码连续输错 5 次,账号已被封锁,请联系管理员解除'); $this->assertNotNull($agent->fresh()->locked_at); $this->assertTrue($agent->fresh()->isLocked()); $this->assertSame(5, (int) $agent->fresh()->login_attempts); } #[Test] public function locked_account_cannot_login_with_correct_password(): void { User::query()->create([ 'username' => 'okagent', 'password' => 'secret12', 'status' => 1, 'login_attempts' => 5, 'locked_at' => now(), ]); $this->post('/user/login', [ 'username' => 'okagent', 'password' => 'secret12', ])->assertOk() ->assertJsonPath('code', 1) ->assertJsonPath('msg', '账号已被封锁(连续输错密码 5 次),请联系管理员解除'); $this->assertGuest('agent'); } #[Test] public function successful_login_clears_failed_attempts(): void { $agent = User::query()->create([ 'username' => 'okagent', 'password' => 'secret12', 'status' => 1, 'login_attempts' => 3, ]); $this->post('/user/login', [ 'username' => 'okagent', 'password' => 'secret12', ])->assertOk()->assertJsonPath('code', 0); $this->assertAuthenticated('agent'); $this->assertSame(0, (int) $agent->fresh()->login_attempts); $this->assertNull($agent->fresh()->locked_at); } #[Test] public function admin_can_unlock_agent_account(): void { $admin = \App\Models\Admin::query()->create([ 'username' => 'admin', 'password' => 'admin123', 'status' => 1, 'is_super' => 0, ]); $locked = User::query()->create([ 'username' => 'okagent', 'password' => 'secret12', 'status' => 1, 'login_attempts' => 5, 'locked_at' => now(), ]); $this->actingAs($admin, 'admin') ->postJson('/admin/agents/'.$locked->id.'/unlock') ->assertOk() ->assertJsonPath('code', 0) ->assertJsonPath('msg', '已解除封禁'); $this->assertNull($locked->fresh()->locked_at); $this->assertSame(0, (int) $locked->fresh()->login_attempts); $this->assertFalse($locked->fresh()->isLocked()); } #[Test] public function unlock_returns_error_for_unlocked_agent(): void { $admin = \App\Models\Admin::query()->create([ 'username' => 'admin', 'password' => 'admin123', 'status' => 1, ]); $agent = User::query()->create([ 'username' => 'okagent', 'password' => 'secret12', 'status' => 1, ]); $this->actingAs($admin, 'admin') ->postJson('/admin/agents/'.$agent->id.'/unlock') ->assertOk() ->assertJsonPath('code', 1) ->assertJsonPath('msg', '该账号未被封禁'); } }