Files
manyfold/spec/models/user_spec.rb
2026-02-25 15:05:13 +00:00

361 lines
11 KiB
Ruby

require "rails_helper"
RSpec.describe User do
it_behaves_like "Follower"
it_behaves_like "Caber::Subject"
it "requires a username" do
expect(build(:user, username: nil)).not_to be_valid
end
it "allows single-character usernames" do
expect(build(:user, username: "a")).to be_valid
end
it "allows two-character usernames" do
expect(build(:user, username: "ab")).to be_valid
end
it "allow three-character usernames" do
expect(build(:user, username: "abc")).to be_valid
end
it "doesn't allows > 225-character usernames" do
expect(build(:user, username: SecureRandom.alphanumeric(226))).not_to be_valid
end
it "allows numbers in usernames" do
expect(build(:user, username: "3jane")).to be_valid
end
it "allows non-ASCII usernames" do
expect(build(:user, username: "إعلانات")).to be_valid
end
it "allows hyphens in usernames" do
expect(build(:user, username: "user-name")).to be_valid
end
it "allows underscores in usernames" do
expect(build(:user, username: "user_name")).to be_valid
end
it "allows dots in usernames" do
expect(build(:user, username: "user.name")).to be_valid
end
it "allows semicolons in usernames" do
expect(build(:user, username: "user;name")).to be_valid
end
it "doesn't allow other punctuation in usernames" do
expect(build(:user, username: "user/name")).not_to be_valid
end
it "doesn't allow usernames that differ by case" do
create(:user, username: "userName")
expect(build(:user, username: "USERNAME")).not_to be_valid
end
it "doesn't allow usernames that differ by punctuation" do
create(:user, username: "username")
expect(build(:user, username: "user.name")).not_to be_valid
end
it "gets member role by default" do
u = create(:user)
expect(u).to have_role(:member)
end
[
:pagination_settings,
:renderer_settings,
:tag_cloud_settings,
:problem_settings,
:file_list_settings,
:tour_state
].each do |field|
it "has default #{field.to_s.humanize} set on creation" do # rubocop:disable RSpec/MultipleExpectations
user = create(:user)
expect(user.send(field)).to be_a Hash
expect(user.send(field).keys).not_to be_empty
end
end
it "gets contributor and member roles if contributor is set as default" do # rubocop:disable RSpec/MultipleExpectations
allow(SiteSettings).to receive(:default_signup_role).and_return("contributor")
u = create(:user)
expect(u).to have_role(:member)
expect(u).to have_role(:contributor)
end
it "enables quotas" do
SiteSettings.enable_user_quota = true
SiteSettings.default_user_quota = 100
expect(build(:user).has_quota?).to be_truthy # rubocop:disable RSpec/PredicateMatcher
end
it "assigned site quota not explicitly defined" do
expect(build(:user).quota).to eq SiteSettings.default_user_quota
end
it "uses site default quota" do
SiteSettings.default_user_quota = 100
user = create(:user, quota: 42, quota_use_site_default: true)
expect(user.quota).to eq SiteSettings.default_user_quota
end
it "considers zero quota value as unlimited" do
user = create(:user, quota: 0, quota_use_site_default: false)
expect(user.has_quota?).to be_falsey # rubocop:disable RSpec/PredicateMatcher
end
context "when serializing JSON fields" do
let(:user) { create(:user) }
[
:pagination_settings,
:renderer_settings,
:tag_cloud_settings,
:problem_settings,
:file_list_settings,
:tour_state
].each do |field|
it "deserializes #{field.to_s.humanize} correctly" do
expect(user.reload.send(field)).to be_a Hash
end
it "serializes #{field.to_s.humanize} correctly" do
user.update!(field => {"foo" => {"bar" => "qux"}})
expect(user.reload.send(field).dig("foo", "bar")).to eq "qux"
end
end
end
context "when autocreating creator" do
it "creates creator successfully if data is valid" do
user = create(:user, creators_attributes: [{slug: "creator", name: "Creator"}])
expect(user.creators.first.name).to eq "Creator"
end
it "validates creator data properly" do
user = build(:user, creators_attributes: [{slug: "invalid+slug", name: ""}])
expect(user).not_to be_valid
end
it "logs errors with creator data properly" do # rubocop:disable RSpec/MultipleExpectations
user = build(:user, creators_attributes: [{slug: "invalid+slug", name: ""}])
user.validate
expect(user.errors.where("creators.name").first.type).to eq :blank
expect(user.errors.where("creators.slug").first.type).to eq :invalid
end
end
context "with owner permissions on a creator" do
let!(:user) { create(:user) }
let!(:creator) { create(:creator) }
it "accesses owned creators through association" do
creator.grant_permission_to("own", user)
expect(user.reload.creators).to include creator
end
it "can't access non-owned creators through association" do
creator.grant_permission_to("own", create(:moderator))
expect(user.creators).to be_empty
end
it "doesn't access viewable creators through association" do
creator.grant_permission_to("view", user)
expect(user.reload.creators).to be_empty
end
end
context "with omniauth" do
let(:auth_data) do
OpenStruct.new({
provider: "openid_connect",
uid: "auth|123456789",
info: OpenStruct.new({
email: "test@example.com",
email_verified: true,
preferred_username: "username",
nickname: "nick"
})
})
end
it "can create a valid user from auth data" do
expect(described_class.from_omniauth(auth_data)).to be_valid
end
it "stores auth provider" do
user = described_class.from_omniauth(auth_data)
expect(user.auth_provider).to eq "openid_connect"
end
it "stores auth uid" do
user = described_class.from_omniauth(auth_data)
expect(user.auth_uid).to eq "auth|123456789"
end
it "stores email" do
user = described_class.from_omniauth(auth_data)
expect(user.email).to eq "test@example.com"
end
it "stores username" do
user = described_class.from_omniauth(auth_data)
expect(user.username).to eq "username"
end
it "stores nickname as username if preferred username not available" do
auth_data.info.preferred_username = nil
user = described_class.from_omniauth(auth_data)
expect(user.username).to eq "nick"
end
it "stores email username as username if preferred username and nickname not available" do
auth_data.info.preferred_username = nil
auth_data.info.nickname = nil
user = described_class.from_omniauth(auth_data)
expect(user.username).to eq "test"
end
it "matches an existing user with identical auth details" do
user = create(:user, auth_provider: "openid_connect", auth_uid: "auth|123456789")
expect(described_class.from_omniauth(auth_data)).to eq user
end
it "matches an existing user with matching email" do
user = create(:user, email: "test@example.com")
expect(described_class.from_omniauth(auth_data)).to eq user
end
it "deconflicts a user with matching username by using one of the other options" do
create(:user, username: "username")
new_user = described_class.from_omniauth(auth_data)
expect(new_user.username).to eq "nick"
end
context "with an unverified email address" do
let(:auth_data) do
OpenStruct.new({
provider: "openid_connect",
uid: "auth|123456789",
info: OpenStruct.new({
email: "test@example.com",
email_verified: false,
preferred_username: "username",
nickname: "nick"
})
})
end
it "throws an error if there is an existing user with that email" do
create(:user, email: "test@example.com")
expect { described_class.from_omniauth(auth_data) }.to raise_error(OmniAuth::Strategies::OpenIDConnect::CallbackError)
end
it "creates a new user if there is no existing user with that email" do
new_user = described_class.from_omniauth(auth_data)
expect(new_user.email).to eq "test@example.com"
end
end
context "with no email_verified claim" do
let(:auth_data) do
OpenStruct.new({
provider: "openid_connect",
uid: "auth|123456789",
info: OpenStruct.new({
email: "test@example.com",
preferred_username: "username",
nickname: "nick"
})
})
end
it "creates a new user if there is no existing user with that email" do
new_user = described_class.from_omniauth(auth_data)
expect(new_user.email).to eq "test@example.com"
end
it "throws an error if there is an existing user with that email" do
create(:user, email: "test@example.com")
expect { described_class.from_omniauth(auth_data) }.to raise_error(OmniAuth::Strategies::OpenIDConnect::CallbackError)
end
end
end
{
"username" => "username",
"user.name" => "user.name",
"user-name" => "user-name",
"user_name" => "user_name",
"user name" => "user-name",
"user.name.1" => "user.name.1",
"user@example.com" => "user-example.com"
}.each_pair do |input, expected|
context "when OmniAuth has a username like '#{input}'" do
subject(:user) { described_class.from_omniauth(auth_data) }
let(:auth_data) {
OpenStruct.new({
provider: "openid_connect",
uid: "auth|123456789",
info: OpenStruct.new({
email: "test@example.com",
preferred_username: input
})
})
}
it "creates a valid user" do
expect(user).to be_valid
end
it "produces the expected username" do
expect(user.username).to eq expected
end
end
end
context "with an invalid user" do
subject(:user) { create(:user) }
before do
user.username = "@@@"
user.save validate: false
end
it "has an invalid user for testing" do
expect(user).not_to be_valid
end
{
sensitive_content_handling: "hide",
interface_language: "en",
sort_order: "recent",
pagination_settings: {},
renderer_settings: {},
tag_cloud_settings: {},
problem_settings: {},
file_list_settings: {},
tour_state: {}
}.each_pair do |attribute, value|
it "doesn't validate if only saving #{attribute}" do
user.send :"#{attribute}=", value
expect(user).to be_valid
end
end
it "validates if non-settings data is set" do
user.email = "test@example.com"
user.tour_state = {}
expect(user).not_to be_valid
end
end
end