Files

318 lines
7.8 KiB
Ruby

require "test_helper"
class AuthenticationFlowTest < ActionDispatch::IntegrationTest
setup do
@user = users(:admin_user)
@user.update!(invitation_accepted_at: Time.current)
Rails.cache.clear
end
test "user can sign in with valid credentials" do
get login_path
assert_response :success
post login_path, params: {
email: @user.email,
password: "password123456"
}
assert_redirected_to admin_root_path
follow_redirect!
assert_response :success
assert_equal @user.id, session[:user_id]
end
test "user cannot sign in with invalid password" do
post login_path, params: {
email: @user.email,
password: "wrongpassword"
}
assert_response :unprocessable_entity
assert_nil session[:user_id]
assert_select "div[role='alert']", text: /invalid email or password/i
end
test "user cannot sign in with non-existent email" do
post login_path, params: {
email: "nonexistent@example.com",
password: "password123456"
}
assert_response :unprocessable_entity
assert_nil session[:user_id]
end
test "pending user cannot sign in" do
pending_user = users(:pending_invitation)
post login_path, params: {
email: pending_user.email,
password: "password123456"
}
assert_response :unprocessable_entity
assert_nil session[:user_id]
assert_select "div[role='alert']", text: /pending/i
end
test "user can sign out" do
# Sign in first
post login_path, params: {
email: @user.email,
password: "password123456"
}
assert_equal @user.id, session[:user_id]
# Sign out
delete logout_path
assert_redirected_to root_path
assert_nil session[:user_id]
end
test "session persists across requests" do
post login_path, params: {
email: @user.email,
password: "password123456"
}
assert_equal @user.id, session[:user_id]
get root_path
assert_equal @user.id, session[:user_id]
get entries_path
assert_equal @user.id, session[:user_id]
end
test "remember me creates cookie" do
post login_path, params: {
email: @user.email,
password: "password123456",
remember_me: "1"
}
assert_not_nil cookies[:remember_token]
@user.reload
assert_not_nil @user.remember_token
assert_not_nil @user.remember_created_at
end
test "remember me cookie logs user in automatically" do
post login_path, params: {
email: @user.email,
password: "password123456",
remember_me: "1"
}
remember_cookie = cookies[:remember_token]
assert_not_nil remember_cookie
# Clear session
reset!
# Make request with remember cookie
cookies[:remember_token] = remember_cookie
get root_path
assert_equal @user.id, session[:user_id]
end
test "logout clears remember me cookie" do
# Sign in with remember me
post login_path, params: {
email: @user.email,
password: "password123456",
remember_me: "1"
}
assert_not_nil cookies[:remember_token]
# Sign out
delete logout_path
remember_cookie = cookies[:remember_token]
assert remember_cookie.nil? || remember_cookie.empty?
@user.reload
assert_nil @user.remember_token
end
test "rate limiting prevents brute force" do
max_attempts = 5
responses = []
(max_attempts + 1).times do
post login_path, params: {
email: @user.email,
password: "wrongpassword"
}
responses << response.status
end
assert responses.first(max_attempts).all? { |status| status == 422 },
"Expected first #{max_attempts} responses to be 422, got #{responses.first(max_attempts)}"
if Rails.cache.is_a?(ActiveSupport::Cache::NullStore)
assert responses.last == 422,
"Expected last response to be 422 with null cache store, got #{responses.last}"
else
assert responses.last == 429, "Expected last response to be 429, got #{responses.last}"
end
assert_select "div[role='alert']", text: /too many/i
end
test "successful login resets rate limit" do
# Fail a few times
3.times do
post login_path, params: {
email: @user.email,
password: "wrongpassword"
}
end
# Then succeed
post login_path, params: {
email: @user.email,
password: "password123456"
}
assert_redirected_to admin_root_path
# Should be able to login again immediately
delete logout_path
post login_path, params: {
email: @user.email,
password: "password123456"
}
assert_redirected_to admin_root_path
end
test "session timeout logs user out after inactivity" do
# Sign in
post login_path, params: {
email: @user.email,
password: "password123456"
}
assert_equal @user.id, session[:user_id]
# Simulate time passing
travel 4.days do
get root_path
assert_redirected_to login_path
assert_match /expired/i, flash[:alert]
assert_nil session[:user_id]
end
end
test "remember me prevents session timeout" do
# Sign in with remember me
post login_path, params: {
email: @user.email,
password: "password123456",
remember_me: "1"
}
remember_cookie = cookies[:remember_token]
# Simulate time passing (but within remember me period)
travel 5.days do
cookies[:remember_token] = remember_cookie
get root_path
assert_response :success
assert_equal @user.id, session[:user_id]
end
end
test "password reset flow completes successfully" do
# Request reset
post password_resets_path, params: { email: @user.email }
assert_redirected_to login_path
@user.reload
assert_not_nil @user.reset_password_token
# Visit reset form
get edit_password_reset_path(@user.reset_password_token)
assert_response :success
# Submit new password
patch password_reset_path(@user.reset_password_token), params: {
password: "newpassword12345",
password_confirmation: "newpassword12345"
}
assert_redirected_to root_path
assert_equal @user.id, session[:user_id]
@user.reload
assert @user.authenticate("newpassword12345")
assert_nil @user.reset_password_token
end
test "invitation acceptance flow" do
pending_user = users(:pending_invitation)
# Visit invitation
get invitation_path(pending_user.invitation_token)
assert_response :success
# Accept invitation
patch accept_invitation_path(pending_user.invitation_token), params: {
user: {
password: "newpassword12345",
password_confirmation: "newpassword12345"
}
}
assert_redirected_to root_path
assert_equal pending_user.id, session[:user_id]
pending_user.reload
assert_not_nil pending_user.invitation_accepted_at
assert pending_user.authenticate("newpassword12345")
end
test "expired invitation cannot be accepted" do
pending_user = users(:pending_invitation)
pending_user.update!(invitation_sent_at: 15.days.ago)
get invitation_path(pending_user.invitation_token)
assert_redirected_to root_path
assert_match /expired/i, flash[:alert]
end
test "admin user redirects to admin dashboard after login" do
post login_path, params: {
email: @user.email,
password: "password123456"
}
assert_redirected_to admin_root_path
end
test "contributor redirects to root after login" do
contributor = users(:contributor_user)
contributor.update!(invitation_accepted_at: Time.current)
post login_path, params: {
email: contributor.email,
password: "password123456"
}
assert_redirected_to root_path
end
test "already logged in user redirects from login page" do
# Sign in first
post login_path, params: {
email: @user.email,
password: "password123456"
}
# Try to visit login page again
get login_path
assert_redirected_to admin_root_path
end
end