once() ->andThrow($exception); Log::shouldReceive('error') ->once() ->with( 'Failed to create password reset attempt.', \Mockery::on(fn (array $context): bool => $context['email_fingerprint'] === 'b5fc85e55755' && $context['exception'] === $exception), ); $this->expectExceptionObject($exception); (new ResetPasswordAttemptService)->createForEmail('ada@example.com', 'tenant-test'); } public function test_code_validation_logs_and_rethrows_unexpected_failures(): void { $exception = new RuntimeException('Database unavailable.'); DB::shouldReceive('transaction') ->once() ->andThrow($exception); Log::shouldReceive('error') ->once() ->with( 'Failed to validate password reset code.', \Mockery::on(fn (array $context): bool => $context['email_fingerprint'] === 'b5fc85e55755' && $context['exception'] === $exception), ); $this->expectExceptionObject($exception); (new ResetPasswordAttemptService)->validateCode('ada@example.com', '1234'); } public function test_password_reset_logs_and_rethrows_unexpected_failures(): void { $exception = new RuntimeException('Database unavailable.'); DB::shouldReceive('transaction') ->once() ->andThrow($exception); Log::shouldReceive('error') ->once() ->with( 'Failed to reset user password.', \Mockery::on(fn (array $context): bool => $context['email_fingerprint'] === 'b5fc85e55755' && $context['exception'] === $exception), ); $this->expectExceptionObject($exception); (new ResetPasswordAttemptService)->resetPassword( 'ada@example.com', '1234', 'NewSecret!456', ); } }