96.99% test coverage
This commit is contained in:
@@ -37,4 +37,422 @@ class UserTest < ActiveSupport::TestCase
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user