require "test_helper" class PasswordResetsControllerTest < ActionDispatch::IntegrationTest setup do @user = users(:admin_user) @user.update!(invitation_accepted_at: Time.current) end # NEW tests test "should get new" do get new_password_reset_url assert_response :success end test "should show password reset form" do get new_password_reset_url assert_select "form" assert_select "input[type=email]" end # CREATE tests test "should send reset email for existing user" do assert_enqueued_emails 1 do post password_resets_url, params: { email: @user.email } end assert_redirected_to login_url assert_match /password reset instructions/i, flash[:notice] end test "should generate reset token for existing user" do post password_resets_url, params: { email: @user.email } @user.reload assert_not_nil @user.reset_password_token assert_not_nil @user.reset_password_sent_at end test "should handle non-existent email gracefully" do post password_resets_url, params: { email: "nonexistent@example.com" } assert_redirected_to login_url assert_match /if that email address is in our system/i, flash[:notice] end test "should send invitation for user without accepted invitation" do pending_user = users(:pending_invitation) assert_nil pending_user.invitation_accepted_at assert_enqueued_emails 1 do post password_resets_url, params: { email: pending_user.email } end assert_redirected_to login_url end test "should handle blank email" do post password_resets_url, params: { email: "" } assert_redirected_to login_url end test "should handle email with whitespace" do post password_resets_url, params: { email: " #{@user.email} " } @user.reload assert_not_nil @user.reset_password_token end # EDIT tests test "should show reset password form with valid token" do @user.update!( reset_password_token: SecureRandom.urlsafe_base64(32), reset_password_sent_at: Time.current ) get edit_password_reset_url(@user.reset_password_token) assert_response :success assert_select "form" assert_select "input[type=password]", count: 2 end test "should reject invalid token" do get edit_password_reset_url("invalid_token") assert_redirected_to login_url assert_match /invalid/i, flash[:alert] end test "should reject expired token" do @user.update!( reset_password_token: SecureRandom.urlsafe_base64(32), reset_password_sent_at: 2.hours.ago ) get edit_password_reset_url(@user.reset_password_token) assert_redirected_to new_password_reset_url assert_match /expired/i, flash[:alert] end # UPDATE tests test "should reset password with valid token" do @user.update!( reset_password_token: SecureRandom.urlsafe_base64(32), reset_password_sent_at: Time.current ) patch password_reset_url(@user.reset_password_token), params: { password: "newpassword12345", password_confirmation: "newpassword12345" } assert_redirected_to root_url assert_match /password has been reset/i, flash[:notice] @user.reload assert_nil @user.reset_password_token assert_nil @user.reset_password_sent_at assert @user.authenticate("newpassword12345") end test "should auto-login after successful password reset" do @user.update!( reset_password_token: SecureRandom.urlsafe_base64(32), reset_password_sent_at: Time.current ) patch password_reset_url(@user.reset_password_token), params: { password: "newpassword12345", password_confirmation: "newpassword12345" } assert_equal @user.id, session[:user_id] end test "should reject mismatched passwords" do @user.update!( reset_password_token: SecureRandom.urlsafe_base64(32), reset_password_sent_at: Time.current ) patch password_reset_url(@user.reset_password_token), params: { password: "newpassword12345", password_confirmation: "differentpassword" } assert_response :unprocessable_entity assert_match /doesn't match/i, flash[:alert] end test "should reject blank password" do @user.update!( reset_password_token: SecureRandom.urlsafe_base64(32), reset_password_sent_at: Time.current ) patch password_reset_url(@user.reset_password_token), params: { password: "", password_confirmation: "" } assert_response :unprocessable_entity assert_match /cannot be blank/i, flash[:alert] end test "should reject expired token on update" do @user.update!( reset_password_token: SecureRandom.urlsafe_base64(32), reset_password_sent_at: 2.hours.ago ) patch password_reset_url(@user.reset_password_token), params: { password: "newpassword12345", password_confirmation: "newpassword12345" } assert_redirected_to new_password_reset_url assert_match /expired/i, flash[:alert] end test "should reject invalid token on update" do patch password_reset_url("invalid_token"), params: { password: "newpassword12345", password_confirmation: "newpassword12345" } assert_redirected_to login_url assert_match /invalid/i, flash[:alert] end test "should enforce password validations" do @user.update!( reset_password_token: SecureRandom.urlsafe_base64(32), reset_password_sent_at: Time.current ) # Password too short (less than 12 characters) patch password_reset_url(@user.reset_password_token), params: { password: "short", password_confirmation: "short" } assert_response :unprocessable_entity end end