Files

459 lines
13 KiB
Ruby

require "test_helper"
class UserTest < ActiveSupport::TestCase
test "should be valid with an email and password" do
user = User.new(email: "new-user@example.com", password: "password123456")
assert user.valid?
end
test "should be invalid without an email" do
user = User.new(password: "password123456")
assert_not user.valid?
end
test "should be invalid with a duplicate email" do
existing_user = users(:admin_user)
user = User.new(email: existing_user.email, password: "password123456")
assert_not user.valid?
end
test "should have a default role of contributor" do
user = User.new(email: "new-user@example.com", password: "password123456")
assert user.contributor?
end
test "can be a reviewer" do
user = User.new(email: "new-user@example.com", password: "password123456", role: :reviewer)
assert user.reviewer?
end
test "can be an admin" do
user = User.new(email: "new-user@example.com", password: "password123456", role: :admin)
assert user.admin?
end
test "should be invalid with a password shorter than 12 characters" do
user = User.new(email: "test@example.com", password: "short")
assert_not user.valid?
assert_includes user.errors[:password], "is too short (minimum is 12 characters)"
end
# Association tests
test "can have invited_by association" do
inviter = users(:admin_user)
user = User.create!(email: "invited@example.com", password: "password123456", invited_by: inviter)
assert_equal inviter, user.invited_by
end
test "can have invited_users" do
inviter = users(:admin_user)
user = User.create!(email: "invited@example.com", password: "password123456", invited_by: inviter)
assert_includes inviter.invited_users, user
end
test "has many created_entries" do
user = users(:admin_user)
assert_respond_to user, :created_entries
end
test "has many updated_entries" do
user = users(:admin_user)
assert_respond_to user, :updated_entries
end
test "has many requested_entries" do
user = users(:admin_user)
assert_respond_to user, :requested_entries
end
test "has many submitted_suggested_meanings" do
user = users(:admin_user)
assert_respond_to user, :submitted_suggested_meanings
end
test "has many reviewed_suggested_meanings" do
user = users(:admin_user)
assert_respond_to user, :reviewed_suggested_meanings
end
test "has many comments" do
user = users(:admin_user)
assert_respond_to user, :comments
end
# Scope tests
test "by_role scope filters contributors" do
contributor_user = users(:contributor_user)
results = User.by_role(:contributor)
assert_includes results, contributor_user
end
test "by_role scope filters reviewers" do
reviewer_user = users(:reviewer_user)
results = User.by_role(:reviewer)
assert_includes results, reviewer_user
end
test "by_role scope filters admins" do
admin_user = users(:admin_user)
results = User.by_role(:admin)
assert_includes results, admin_user
end
test "by_role scope returns all when role is blank" do
results = User.by_role(nil)
assert_equal User.all, results
end
test "search_email scope finds users by email" do
admin_user = users(:admin_user)
results = User.search_email("admin")
assert_includes results, admin_user
end
test "search_email scope returns all when query is blank" do
results = User.search_email(nil)
assert_equal User.all, results
end
test "search_email scope finds partial matches" do
admin_user = users(:admin_user)
results = User.search_email("exam")
assert_includes results, admin_user
end
# invitation_expired? tests
test "invitation_expired? returns false when invitation_sent_at is nil" do
user = User.new(email: "test@example.com", password: "password123456")
assert_not user.invitation_expired?
end
test "invitation_expired? returns false for recent invitation" do
user = User.new(
email: "test@example.com",
password: "password123456",
invitation_sent_at: 1.day.ago
)
assert_not user.invitation_expired?
end
test "invitation_expired? returns true for expired invitation" do
user = User.new(
email: "test@example.com",
password: "password123456",
invitation_sent_at: 15.days.ago
)
assert user.invitation_expired?
end
test "invitation_expired? returns false exactly at expiry boundary" do
user = User.new(
email: "test@example.com",
password: "password123456",
invitation_sent_at: 13.days.ago
)
assert_not user.invitation_expired?
end
# invitation_pending? tests
test "invitation_pending? returns true for valid pending invitation" do
user = User.new(
email: "test@example.com",
password: "password123456",
invitation_token: "valid_token",
invitation_sent_at: 1.day.ago,
invitation_accepted_at: nil
)
assert user.invitation_pending?
end
test "invitation_pending? returns false when invitation is accepted" do
user = User.new(
email: "test@example.com",
password: "password123456",
invitation_token: "valid_token",
invitation_sent_at: 1.day.ago,
invitation_accepted_at: Time.current
)
assert_not user.invitation_pending?
end
test "invitation_pending? returns false when invitation is expired" do
user = User.new(
email: "test@example.com",
password: "password123456",
invitation_token: "valid_token",
invitation_sent_at: 15.days.ago,
invitation_accepted_at: nil
)
assert_not user.invitation_pending?
end
test "invitation_pending? returns false when no invitation token" do
user = User.new(
email: "test@example.com",
password: "password123456",
invitation_token: nil,
invitation_sent_at: 1.day.ago,
invitation_accepted_at: nil
)
assert_not user.invitation_pending?
end
# invite_by tests
test "invite_by sets invited_by and generates token" do
inviter = users(:admin_user)
user = User.new(email: "test@example.com", password: "password123456")
user.invite_by(inviter)
assert_equal inviter, user.invited_by
assert_not_nil user.invitation_token
assert_not_nil user.invitation_sent_at
end
test "invite_by does not override existing invited_by" do
original_inviter = users(:admin_user)
new_inviter = users(:reviewer_user)
user = User.new(email: "test@example.com", password: "password123456", invited_by: original_inviter)
user.invite_by(new_inviter)
assert_equal original_inviter, user.invited_by
end
test "invite_by handles nil invitee" do
user = User.new(email: "test@example.com", password: "password123456")
user.invite_by(nil)
assert_nil user.invited_by
assert_not_nil user.invitation_token
assert_not_nil user.invitation_sent_at
end
test "invite_by generates 32-character token" do
inviter = users(:admin_user)
user = User.new(email: "test@example.com", password: "password123456")
user.invite_by(inviter)
assert user.invitation_token.length >= 32
end
# invite_by! tests
test "invite_by! saves the user" do
inviter = users(:admin_user)
user = User.new(email: "test@example.com", password: "password123456")
user.invite_by!(inviter)
assert_not_nil user.id
assert user.persisted?
end
test "invite_by! works without invitee parameter" do
user = User.new(email: "test@example.com", password: "password123456")
user.invite_by!
assert_not_nil user.id
assert_not_nil user.invitation_token
end
# find_by_valid_invitation_token tests
test "find_by_valid_invitation_token finds user with valid token" do
user = User.create!(
email: "test@example.com",
password: "password123456",
invitation_token: "valid_token_12345",
invitation_sent_at: 1.day.ago,
invitation_accepted_at: nil
)
found_user = User.find_by_valid_invitation_token("valid_token_12345")
assert_equal user, found_user
end
test "find_by_valid_invitation_token returns nil for expired token" do
User.create!(
email: "test@example.com",
password: "password123456",
invitation_token: "expired_token",
invitation_sent_at: 15.days.ago,
invitation_accepted_at: nil
)
found_user = User.find_by_valid_invitation_token("expired_token")
assert_nil found_user
end
test "find_by_valid_invitation_token returns nil for accepted invitation" do
User.create!(
email: "test@example.com",
password: "password123456",
invitation_token: "accepted_token",
invitation_sent_at: 1.day.ago,
invitation_accepted_at: Time.current
)
found_user = User.find_by_valid_invitation_token("accepted_token")
assert_nil found_user
end
test "find_by_valid_invitation_token returns nil for non-existent token" do
found_user = User.find_by_valid_invitation_token("nonexistent_token")
assert_nil found_user
end
# remember_me tests
test "remember_me generates token and saves" do
user = users(:admin_user)
token = user.remember_me
assert_not_nil token
assert_not_nil user.remember_token
assert_not_nil user.remember_created_at
assert_equal token, user.remember_token
end
test "remember_me returns the generated token" do
user = users(:admin_user)
token = user.remember_me
assert_kind_of String, token
assert token.length >= 32
end
test "remember_me updates database immediately" do
user = users(:admin_user)
user.remember_me
user.reload
assert_not_nil user.remember_token
assert_not_nil user.remember_created_at
end
test "remember_me saves without validation" do
user = users(:admin_user)
# Make user invalid (email already taken by changing to duplicate)
user.email = ""
token = user.remember_me
assert_not_nil token
user.reload
assert_not_nil user.remember_token
end
# forget_me tests
test "forget_me clears remember token and timestamp" do
user = users(:admin_user)
user.remember_me
assert_not_nil user.remember_token
user.forget_me
user.reload
assert_nil user.remember_token
assert_nil user.remember_created_at
end
test "forget_me works when no remember token exists" do
user = users(:admin_user)
user.forget_me
assert_nil user.remember_token
assert_nil user.remember_created_at
end
# remember_token_expired? tests
test "remember_token_expired? returns true when remember_created_at is nil" do
user = users(:admin_user)
assert user.remember_token_expired?
end
test "remember_token_expired? returns false for recent token" do
user = users(:admin_user)
user.remember_token = "token"
user.remember_created_at = 1.day.ago
user.save(validate: false)
assert_not user.remember_token_expired?
end
test "remember_token_expired? returns true for expired token" do
user = users(:admin_user)
user.remember_token = "token"
user.remember_created_at = 3.weeks.ago
user.save(validate: false)
assert user.remember_token_expired?
end
test "remember_token_expired? boundary at 2 weeks" do
user = users(:admin_user)
user.remember_token = "token"
user.remember_created_at = 13.days.ago
user.save(validate: false)
assert_not user.remember_token_expired?
end
# find_by_valid_remember_token tests
test "find_by_valid_remember_token finds user with valid token" do
user = users(:admin_user)
token = user.remember_me
found_user = User.find_by_valid_remember_token(token)
assert_equal user, found_user
end
test "find_by_valid_remember_token returns nil for expired token" do
user = users(:admin_user)
user.remember_token = "expired_token"
user.remember_created_at = 3.weeks.ago
user.save(validate: false)
found_user = User.find_by_valid_remember_token("expired_token")
assert_nil found_user
end
test "find_by_valid_remember_token returns nil for non-existent token" do
found_user = User.find_by_valid_remember_token("nonexistent_token")
assert_nil found_user
end
test "find_by_valid_remember_token returns nil when user has no remember_created_at" do
user = users(:admin_user)
user.remember_token = "token_without_timestamp"
user.remember_created_at = nil
user.save(validate: false)
found_user = User.find_by_valid_remember_token("token_without_timestamp")
assert_nil found_user
end
# Password authentication tests
test "authenticate returns user for correct password" do
user = User.create!(email: "test@example.com", password: "password123456")
assert_equal user, user.authenticate("password123456")
end
test "authenticate returns false for incorrect password" do
user = User.create!(email: "test@example.com", password: "password123456")
assert_not user.authenticate("wrongpassword")
end
# Email normalization tests
test "email uniqueness is case-insensitive" do
User.create!(email: "Test@Example.com", password: "password123456")
duplicate_user = User.new(email: "test@example.com", password: "password123456")
# Note: This depends on database collation, but should be tested
assert_not duplicate_user.valid?
end
# Constants tests
test "INVITATION_TOKEN_EXPIRY is 14 days" do
assert_equal 14.days, User::INVITATION_TOKEN_EXPIRY
end
test "REMEMBER_TOKEN_EXPIRY is 2 weeks" do
assert_equal 2.weeks, User::REMEMBER_TOKEN_EXPIRY
end
end