create(['email' => 'jan@voorbeeld.nl']); $response = $this->postJson('/api/v1/auth/forgot-password', [ 'email' => 'jan@voorbeeld.nl', 'app' => 'app', ]); $response->assertOk(); Queue::assertPushed(SendTransactionalEmail::class, function ($job) { return $job->recipientEmail === 'jan@voorbeeld.nl' && $job->type === EmailTemplateType::PASSWORD_RESET; }); } public function test_forgot_password_returns_same_success_for_nonexisting_email(): void { Queue::fake(); $response = $this->postJson('/api/v1/auth/forgot-password', [ 'email' => 'onbekend@voorbeeld.nl', 'app' => 'app', ]); $response->assertOk(); } public function test_forgot_password_requires_app_parameter(): void { $response = $this->postJson('/api/v1/auth/forgot-password', [ 'email' => 'jan@voorbeeld.nl', ]); $response->assertStatus(422); $response->assertJsonValidationErrors('app'); } public function test_forgot_password_validates_app_values(): void { $response = $this->postJson('/api/v1/auth/forgot-password', [ 'email' => 'jan@voorbeeld.nl', 'app' => 'invalid', ]); $response->assertStatus(422); $response->assertJsonValidationErrors('app'); } public function test_forgot_password_validates_email_required(): void { $response = $this->postJson('/api/v1/auth/forgot-password', [ 'app' => 'app', ]); $response->assertStatus(422); $response->assertJsonValidationErrors('email'); } public function test_forgot_password_is_rate_limited(): void { for ($i = 0; $i < 5; $i++) { $this->postJson('/api/v1/auth/forgot-password', [ 'email' => 'test@voorbeeld.nl', 'app' => 'portal', ]); } $response = $this->postJson('/api/v1/auth/forgot-password', [ 'email' => 'test@voorbeeld.nl', 'app' => 'portal', ]); $response->assertStatus(429); } // ─── Reset Password ───────────────────────────────────────────────── public function test_reset_password_with_valid_token(): void { $user = User::factory()->create(['email' => 'jan@voorbeeld.nl']); $token = Password::createToken($user); $response = $this->postJson('/api/v1/auth/reset-password', [ 'token' => $token, 'email' => 'jan@voorbeeld.nl', 'password' => 'NieuwWachtwoord1', 'password_confirmation' => 'NieuwWachtwoord1', ]); $response->assertOk(); $user->refresh(); $this->assertTrue(Hash::check('NieuwWachtwoord1', $user->password)); } public function test_reset_password_revokes_all_tokens(): void { $user = User::factory()->create(['email' => 'jan@voorbeeld.nl']); $user->createToken('test-token'); $this->assertCount(1, $user->tokens); $token = Password::createToken($user); $this->postJson('/api/v1/auth/reset-password', [ 'token' => $token, 'email' => 'jan@voorbeeld.nl', 'password' => 'NieuwWachtwoord1', 'password_confirmation' => 'NieuwWachtwoord1', ]); $this->assertCount(0, $user->tokens()->get()); } public function test_reset_password_with_invalid_token_returns_422(): void { User::factory()->create(['email' => 'jan@voorbeeld.nl']); $response = $this->postJson('/api/v1/auth/reset-password', [ 'token' => 'invalid-token-here', 'email' => 'jan@voorbeeld.nl', 'password' => 'NieuwWachtwoord1', 'password_confirmation' => 'NieuwWachtwoord1', ]); $response->assertStatus(422); } public function test_reset_password_requires_confirmation(): void { $user = User::factory()->create(['email' => 'jan@voorbeeld.nl']); $token = Password::createToken($user); $response = $this->postJson('/api/v1/auth/reset-password', [ 'token' => $token, 'email' => 'jan@voorbeeld.nl', 'password' => 'NieuwWachtwoord1', ]); $response->assertStatus(422); $response->assertJsonValidationErrors('password'); } public function test_reset_password_validates_required_fields(): void { $response = $this->postJson('/api/v1/auth/reset-password', []); $response->assertStatus(422); $response->assertJsonValidationErrors(['token', 'email', 'password']); } }