create([ 'email' => 'ada@example.com', 'password' => 'OldSecret!123', ]); $user->forceFill([ 'failed_login_attempts' => 5, 'last_failed_login_at' => now(), 'locked_until' => now()->addMinutes(15), ])->save(); $user->createToken('existing-session'); $attempt = $user->resetPasswordAttempts()->create([ 'codigo' => '0123', 'status' => ResetPasswordAttempt::STATUS_VALIDATED, ]); $this->postJson('/api/password/reset', [ 'email' => ' ADA@EXAMPLE.COM ', 'codigo' => '0123', 'password' => 'NewSecret!456', 'password_confirmation' => 'NewSecret!456', ])->assertOk() ->assertJsonPath('status', ResetPasswordAttempt::STATUS_USED); $user->refresh(); $this->assertTrue(Hash::check('NewSecret!456', $user->password)); $this->assertFalse(Hash::check('OldSecret!123', $user->password)); $this->assertSame(ResetPasswordAttempt::STATUS_USED, $attempt->fresh()->status); $this->assertDatabaseCount('personal_access_tokens', 0); $this->assertSame(0, $user->failed_login_attempts); $this->assertNull($user->last_failed_login_at); $this->assertNull($user->locked_until); } public function test_it_rejects_a_pending_expired_or_used_attempt(): void { $user = User::factory()->create([ 'email' => 'ada@example.com', 'password' => 'OldSecret!123', ]); foreach ([ ResetPasswordAttempt::STATUS_PENDING, ResetPasswordAttempt::STATUS_EXPIRED, ResetPasswordAttempt::STATUS_USED, ] as $status) { $user->resetPasswordAttempts()->create([ 'codigo' => '1234', 'status' => $status, ]); } $this->postJson('/api/password/reset', [ 'email' => 'ada@example.com', 'codigo' => '1234', 'password' => 'NewSecret!456', 'password_confirmation' => 'NewSecret!456', ])->assertUnprocessable() ->assertJsonValidationErrors('codigo'); $this->assertTrue(Hash::check('OldSecret!123', $user->fresh()->password)); } public function test_a_used_attempt_cannot_be_reused(): void { $user = User::factory()->create(['email' => 'ada@example.com']); $attempt = $user->resetPasswordAttempts()->create([ 'codigo' => '1234', 'status' => ResetPasswordAttempt::STATUS_VALIDATED, ]); $payload = [ 'email' => 'ada@example.com', 'codigo' => '1234', 'password' => 'NewSecret!456', 'password_confirmation' => 'NewSecret!456', ]; $this->postJson('/api/password/reset', $payload)->assertOk(); $this->postJson('/api/password/reset', $payload) ->assertUnprocessable() ->assertJsonValidationErrors('codigo'); $this->assertSame(ResetPasswordAttempt::STATUS_USED, $attempt->fresh()->status); } public function test_it_validates_password_confirmation_and_strength(): void { $this->postJson('/api/password/reset', [ 'email' => 'ada@example.com', 'codigo' => '1234', 'password' => 'weak', 'password_confirmation' => 'different', ])->assertUnprocessable() ->assertJsonValidationErrors('password'); } }