gitlab-org--gitlab-foss/spec/models/user_spec.rb
Luke Bennett 534a61179e
Improve the GitHub and Gitea import feature table interface
These are backend changes.
Use Vue for the import feature UI for "githubish"
providers (GitHub and Gitea).
Add "Go to project" button after a successful import.
Use CI-style status icons and improve spacing of the
table and its component.
Adds ETag polling to the github and gitea import
jobs endpoint.
2019-02-13 00:15:57 +00:00

3365 lines
106 KiB
Ruby

require 'spec_helper'
describe User do
include ProjectForksHelper
include TermsHelper
it_behaves_like 'having unique enum values'
describe 'modules' do
subject { described_class }
it { is_expected.to include_module(Gitlab::ConfigHelper) }
it { is_expected.to include_module(Referable) }
it { is_expected.to include_module(Sortable) }
it { is_expected.to include_module(TokenAuthenticatable) }
it { is_expected.to include_module(BlocksJsonSerialization) }
end
describe 'delegations' do
it { is_expected.to delegate_method(:path).to(:namespace).with_prefix }
end
describe 'associations' do
it { is_expected.to have_one(:namespace) }
it { is_expected.to have_one(:status) }
it { is_expected.to have_many(:snippets).dependent(:destroy) }
it { is_expected.to have_many(:members) }
it { is_expected.to have_many(:project_members) }
it { is_expected.to have_many(:group_members) }
it { is_expected.to have_many(:groups) }
it { is_expected.to have_many(:keys).dependent(:destroy) }
it { is_expected.to have_many(:deploy_keys).dependent(:nullify) }
it { is_expected.to have_many(:events).dependent(:destroy) }
it { is_expected.to have_many(:issues).dependent(:destroy) }
it { is_expected.to have_many(:notes).dependent(:destroy) }
it { is_expected.to have_many(:merge_requests).dependent(:destroy) }
it { is_expected.to have_many(:identities).dependent(:destroy) }
it { is_expected.to have_many(:spam_logs).dependent(:destroy) }
it { is_expected.to have_many(:todos) }
it { is_expected.to have_many(:award_emoji).dependent(:destroy) }
it { is_expected.to have_many(:triggers).dependent(:destroy) }
it { is_expected.to have_many(:builds).dependent(:nullify) }
it { is_expected.to have_many(:pipelines).dependent(:nullify) }
it { is_expected.to have_many(:chat_names).dependent(:destroy) }
it { is_expected.to have_many(:uploads) }
it { is_expected.to have_many(:reported_abuse_reports).dependent(:destroy).class_name('AbuseReport') }
it { is_expected.to have_many(:custom_attributes).class_name('UserCustomAttribute') }
it { is_expected.to have_many(:releases).dependent(:nullify) }
describe "#abuse_report" do
let(:current_user) { create(:user) }
let(:other_user) { create(:user) }
it { is_expected.to have_one(:abuse_report) }
it "refers to the abuse report whose user_id is the current user" do
abuse_report = create(:abuse_report, reporter: other_user, user: current_user)
expect(current_user.abuse_report).to eq(abuse_report)
end
it "does not refer to the abuse report whose reporter_id is the current user" do
create(:abuse_report, reporter: current_user, user: other_user)
expect(current_user.abuse_report).to be_nil
end
it "does not update the user_id of an abuse report when the user is updated" do
abuse_report = create(:abuse_report, reporter: current_user, user: other_user)
current_user.block
expect(abuse_report.reload.user).to eq(other_user)
end
end
describe '#group_members' do
it 'does not include group memberships for which user is a requester' do
user = create(:user)
group = create(:group, :public, :access_requestable)
group.request_access(user)
expect(user.group_members).to be_empty
end
end
describe '#project_members' do
it 'does not include project memberships for which user is a requester' do
user = create(:user)
project = create(:project, :public, :access_requestable)
project.request_access(user)
expect(user.project_members).to be_empty
end
end
end
describe 'validations' do
describe 'username' do
it 'validates presence' do
expect(subject).to validate_presence_of(:username)
end
it 'rejects blacklisted names' do
user = build(:user, username: 'dashboard')
expect(user).not_to be_valid
expect(user.errors.messages[:username]).to eq ['dashboard is a reserved name']
end
it 'allows child names' do
user = build(:user, username: 'avatar')
expect(user).to be_valid
end
it 'allows wildcard names' do
user = build(:user, username: 'blob')
expect(user).to be_valid
end
context 'when username is changed' do
let(:user) { build_stubbed(:user, username: 'old_path', namespace: build_stubbed(:namespace)) }
it 'validates move_dir is allowed for the namespace' do
expect(user.namespace).to receive(:any_project_has_container_registry_tags?).and_return(true)
user.username = 'new_path'
expect(user).to be_invalid
expect(user.errors.messages[:username].first).to match('cannot be changed if a personal project has container registry tags')
end
end
context 'when the username is in use by another user' do
let(:username) { 'foo' }
let!(:other_user) { create(:user, username: username) }
it 'is invalid' do
user = build(:user, username: username)
expect(user).not_to be_valid
expect(user.errors.full_messages).to eq(['Username has already been taken'])
end
end
end
it 'has a DB-level NOT NULL constraint on projects_limit' do
user = create(:user)
expect(user.persisted?).to eq(true)
expect do
user.update_columns(projects_limit: nil)
end.to raise_error(ActiveRecord::StatementInvalid)
end
it { is_expected.to validate_presence_of(:projects_limit) }
it { is_expected.to validate_numericality_of(:projects_limit) }
it { is_expected.to allow_value(0).for(:projects_limit) }
it { is_expected.not_to allow_value(-1).for(:projects_limit) }
it { is_expected.not_to allow_value(Gitlab::Database::MAX_INT_VALUE + 1).for(:projects_limit) }
it { is_expected.to validate_length_of(:bio).is_at_most(255) }
it_behaves_like 'an object with email-formated attributes', :email do
subject { build(:user) }
end
it_behaves_like 'an object with email-formated attributes', :public_email, :notification_email do
subject { build(:user).tap { |user| user.emails << build(:email, email: email_value) } }
end
describe '#commit_email' do
subject(:user) { create(:user) }
it 'defaults to the primary email' do
expect(user.email).to be_present
expect(user.commit_email).to eq(user.email)
end
it 'defaults to the primary email when the column in the database is null' do
user.update_column(:commit_email, nil)
found_user = described_class.find_by(id: user.id)
expect(found_user.commit_email).to eq(user.email)
end
it 'returns the private commit email when commit_email has _private' do
user.update_column(:commit_email, Gitlab::PrivateCommitEmail::TOKEN)
expect(user.commit_email).to eq(user.private_commit_email)
end
it 'can be set to a confirmed email' do
confirmed = create(:email, :confirmed, user: user)
user.commit_email = confirmed.email
expect(user).to be_valid
expect(user.commit_email).to eq(confirmed.email)
end
it 'can not be set to an unconfirmed email' do
unconfirmed = create(:email, user: user)
user.commit_email = unconfirmed.email
# This should set the commit_email attribute to the primary email
expect(user).to be_valid
expect(user.commit_email).to eq(user.email)
end
it 'can not be set to a non-existent email' do
user.commit_email = 'non-existent-email@nonexistent.nonexistent'
# This should set the commit_email attribute to the primary email
expect(user).to be_valid
expect(user.commit_email).to eq(user.email)
end
it 'can not be set to an invalid email, even if confirmed' do
confirmed = create(:email, :confirmed, :skip_validate, user: user, email: 'invalid')
user.commit_email = confirmed.email
expect(user).not_to be_valid
end
end
describe 'email' do
context 'when no signup domains whitelisted' do
before do
allow_any_instance_of(ApplicationSetting).to receive(:domain_whitelist).and_return([])
end
it 'accepts any email' do
user = build(:user, email: "info@example.com")
expect(user).to be_valid
end
end
context 'when a signup domain is whitelisted and subdomains are allowed' do
before do
allow_any_instance_of(ApplicationSetting).to receive(:domain_whitelist).and_return(['example.com', '*.example.com'])
end
it 'accepts info@example.com' do
user = build(:user, email: "info@example.com")
expect(user).to be_valid
end
it 'accepts info@test.example.com' do
user = build(:user, email: "info@test.example.com")
expect(user).to be_valid
end
it 'rejects example@test.com' do
user = build(:user, email: "example@test.com")
expect(user).to be_invalid
end
end
context 'when a signup domain is whitelisted and subdomains are not allowed' do
before do
allow_any_instance_of(ApplicationSetting).to receive(:domain_whitelist).and_return(['example.com'])
end
it 'accepts info@example.com' do
user = build(:user, email: "info@example.com")
expect(user).to be_valid
end
it 'rejects info@test.example.com' do
user = build(:user, email: "info@test.example.com")
expect(user).to be_invalid
end
it 'rejects example@test.com' do
user = build(:user, email: "example@test.com")
expect(user).to be_invalid
end
it 'accepts example@test.com when added by another user' do
user = build(:user, email: "example@test.com", created_by_id: 1)
expect(user).to be_valid
end
end
context 'domain blacklist' do
before do
allow_any_instance_of(ApplicationSetting).to receive(:domain_blacklist_enabled?).and_return(true)
allow_any_instance_of(ApplicationSetting).to receive(:domain_blacklist).and_return(['example.com'])
end
context 'when a signup domain is blacklisted' do
it 'accepts info@test.com' do
user = build(:user, email: 'info@test.com')
expect(user).to be_valid
end
it 'rejects info@example.com' do
user = build(:user, email: 'info@example.com')
expect(user).not_to be_valid
end
it 'accepts info@example.com when added by another user' do
user = build(:user, email: 'info@example.com', created_by_id: 1)
expect(user).to be_valid
end
end
context 'when a signup domain is blacklisted but a wildcard subdomain is allowed' do
before do
allow_any_instance_of(ApplicationSetting).to receive(:domain_blacklist).and_return(['test.example.com'])
allow_any_instance_of(ApplicationSetting).to receive(:domain_whitelist).and_return(['*.example.com'])
end
it 'gives priority to whitelist and allow info@test.example.com' do
user = build(:user, email: 'info@test.example.com')
expect(user).to be_valid
end
end
context 'with both lists containing a domain' do
before do
allow_any_instance_of(ApplicationSetting).to receive(:domain_whitelist).and_return(['test.com'])
end
it 'accepts info@test.com' do
user = build(:user, email: 'info@test.com')
expect(user).to be_valid
end
it 'rejects info@example.com' do
user = build(:user, email: 'info@example.com')
expect(user).not_to be_valid
end
end
end
context 'owns_notification_email' do
it 'accepts temp_oauth_email emails' do
user = build(:user, email: "temp-email-for-oauth@example.com")
expect(user).to be_valid
end
end
context 'set_commit_email' do
it 'keeps commit email when private commit email is being used' do
user = create(:user, commit_email: Gitlab::PrivateCommitEmail::TOKEN)
expect(user.read_attribute(:commit_email)).to eq(Gitlab::PrivateCommitEmail::TOKEN)
end
it 'keeps the commit email when nil' do
user = create(:user, commit_email: nil)
expect(user.read_attribute(:commit_email)).to be_nil
end
it 'reverts to nil when email is not verified' do
user = create(:user, commit_email: "foo@bar.com")
expect(user.read_attribute(:commit_email)).to be_nil
end
end
context 'owns_commit_email' do
it 'accepts private commit email' do
user = build(:user, commit_email: Gitlab::PrivateCommitEmail::TOKEN)
expect(user).to be_valid
end
it 'accepts nil commit email' do
user = build(:user, commit_email: nil)
expect(user).to be_valid
end
end
end
end
describe "scopes" do
describe ".with_two_factor" do
it "returns users with 2fa enabled via OTP" do
user_with_2fa = create(:user, :two_factor_via_otp)
user_without_2fa = create(:user)
users_with_two_factor = described_class.with_two_factor.pluck(:id)
expect(users_with_two_factor).to include(user_with_2fa.id)
expect(users_with_two_factor).not_to include(user_without_2fa.id)
end
it "returns users with 2fa enabled via U2F" do
user_with_2fa = create(:user, :two_factor_via_u2f)
user_without_2fa = create(:user)
users_with_two_factor = described_class.with_two_factor.pluck(:id)
expect(users_with_two_factor).to include(user_with_2fa.id)
expect(users_with_two_factor).not_to include(user_without_2fa.id)
end
it "returns users with 2fa enabled via OTP and U2F" do
user_with_2fa = create(:user, :two_factor_via_otp, :two_factor_via_u2f)
user_without_2fa = create(:user)
users_with_two_factor = described_class.with_two_factor.pluck(:id)
expect(users_with_two_factor).to eq([user_with_2fa.id])
expect(users_with_two_factor).not_to include(user_without_2fa.id)
end
it 'works with ORDER BY' do
user_with_2fa = create(:user, :two_factor_via_otp, :two_factor_via_u2f)
expect(described_class
.with_two_factor
.reorder_by_name).to eq([user_with_2fa])
end
end
describe ".without_two_factor" do
it "excludes users with 2fa enabled via OTP" do
user_with_2fa = create(:user, :two_factor_via_otp)
user_without_2fa = create(:user)
users_without_two_factor = described_class.without_two_factor.pluck(:id)
expect(users_without_two_factor).to include(user_without_2fa.id)
expect(users_without_two_factor).not_to include(user_with_2fa.id)
end
it "excludes users with 2fa enabled via U2F" do
user_with_2fa = create(:user, :two_factor_via_u2f)
user_without_2fa = create(:user)
users_without_two_factor = described_class.without_two_factor.pluck(:id)
expect(users_without_two_factor).to include(user_without_2fa.id)
expect(users_without_two_factor).not_to include(user_with_2fa.id)
end
it "excludes users with 2fa enabled via OTP and U2F" do
user_with_2fa = create(:user, :two_factor_via_otp, :two_factor_via_u2f)
user_without_2fa = create(:user)
users_without_two_factor = described_class.without_two_factor.pluck(:id)
expect(users_without_two_factor).to include(user_without_2fa.id)
expect(users_without_two_factor).not_to include(user_with_2fa.id)
end
end
describe '.limit_to_todo_authors' do
context 'when filtering by todo authors' do
let(:user1) { create(:user) }
let(:user2) { create(:user) }
before do
create(:todo, user: user1, author: user1, state: :done)
create(:todo, user: user2, author: user2, state: :pending)
end
it 'only returns users that have authored todos' do
users = described_class.limit_to_todo_authors(
user: user2,
with_todos: true,
todo_state: :pending
)
expect(users).to eq([user2])
end
it 'ignores users that do not have a todo in the matching state' do
users = described_class.limit_to_todo_authors(
user: user1,
with_todos: true,
todo_state: :pending
)
expect(users).to be_empty
end
end
context 'when not filtering by todo authors' do
it 'returns the input relation' do
user1 = create(:user)
user2 = create(:user)
rel = described_class.limit_to_todo_authors(user: user1)
expect(rel).to include(user1, user2)
end
end
context 'when no user is provided' do
it 'returns the input relation' do
user1 = create(:user)
user2 = create(:user)
rel = described_class.limit_to_todo_authors
expect(rel).to include(user1, user2)
end
end
end
describe '.by_username' do
it 'finds users regardless of the case passed' do
user = create(:user, username: 'CaMeLcAsEd')
user2 = create(:user, username: 'UPPERCASE')
expect(described_class.by_username(%w(CAMELCASED uppercase)))
.to contain_exactly(user, user2)
end
it 'finds a single user regardless of the case passed' do
user = create(:user, username: 'CaMeLcAsEd')
expect(described_class.by_username('CAMELCASED'))
.to contain_exactly(user)
end
end
end
describe "Respond to" do
it { is_expected.to respond_to(:admin?) }
it { is_expected.to respond_to(:name) }
it { is_expected.to respond_to(:external?) }
end
describe 'before save hook' do
context 'when saving an external user' do
let(:user) { create(:user) }
let(:external_user) { create(:user, external: true) }
it "sets other properties aswell" do
expect(external_user.can_create_team).to be_falsey
expect(external_user.can_create_group).to be_falsey
expect(external_user.projects_limit).to be 0
end
end
describe '#check_for_verified_email' do
let(:user) { create(:user) }
let(:secondary) { create(:email, :confirmed, email: 'secondary@example.com', user: user) }
it 'allows a verfied secondary email to be used as the primary without needing reconfirmation' do
user.update!(email: secondary.email)
user.reload
expect(user.email).to eq secondary.email
expect(user.unconfirmed_email).to eq nil
expect(user.confirmed?).to be_truthy
end
end
end
describe 'after commit hook' do
describe '#update_emails_with_primary_email' do
before do
@user = create(:user, email: 'primary@example.com').tap do |user|
user.skip_reconfirmation!
end
@secondary = create :email, email: 'secondary@example.com', user: @user
@user.reload
end
it 'gets called when email updated' do
expect(@user).to receive(:update_emails_with_primary_email)
@user.update!(email: 'new_primary@example.com')
end
it 'adds old primary to secondary emails when secondary is a new email ' do
@user.update!(email: 'new_primary@example.com')
@user.reload
expect(@user.emails.count).to eq 2
expect(@user.emails.pluck(:email)).to match_array([@secondary.email, 'primary@example.com'])
end
it 'adds old primary to secondary emails if secondary is becoming a primary' do
@user.update!(email: @secondary.email)
@user.reload
expect(@user.emails.count).to eq 1
expect(@user.emails.first.email).to eq 'primary@example.com'
end
it 'transfers old confirmation values into new secondary' do
@user.update!(email: @secondary.email)
@user.reload
expect(@user.emails.count).to eq 1
expect(@user.emails.first.confirmed_at).not_to eq nil
end
end
describe '#update_notification_email' do
# Regression: https://gitlab.com/gitlab-org/gitlab-ce/issues/22846
context 'when changing :email' do
let(:user) { create(:user) }
let(:new_email) { 'new-email@example.com' }
it 'sets :unconfirmed_email' do
expect do
user.tap { |u| u.update!(email: new_email) }.reload
end.to change(user, :unconfirmed_email).to(new_email)
end
it 'does not change :notification_email' do
expect do
user.tap { |u| u.update!(email: new_email) }.reload
end.not_to change(user, :notification_email)
end
it 'updates :notification_email to the new email once confirmed' do
user.update!(email: new_email)
expect do
user.tap(&:confirm).reload
end.to change(user, :notification_email).to eq(new_email)
end
context 'and :notification_email is set to a secondary email' do
let!(:email_attrs) { attributes_for(:email, :confirmed, user: user) }
let(:secondary) { create(:email, :confirmed, email: 'secondary@example.com', user: user) }
before do
user.emails.create(email_attrs)
user.tap { |u| u.update!(notification_email: email_attrs[:email]) }.reload
end
it 'does not change :notification_email to :email' do
expect do
user.tap { |u| u.update!(email: new_email) }.reload
end.not_to change(user, :notification_email)
end
it 'does not change :notification_email to :email once confirmed' do
user.update!(email: new_email)
expect do
user.tap(&:confirm).reload
end.not_to change(user, :notification_email)
end
end
end
end
describe '#update_invalid_gpg_signatures' do
let(:user) do
create(:user, email: 'tula.torphy@abshire.ca').tap do |user|
user.skip_reconfirmation!
end
end
it 'does nothing when the name is updated' do
expect(user).not_to receive(:update_invalid_gpg_signatures)
user.update!(name: 'Bette')
end
it 'synchronizes the gpg keys when the email is updated' do
expect(user).to receive(:update_invalid_gpg_signatures).at_most(:twice)
user.update!(email: 'shawnee.ritchie@denesik.com')
end
end
end
describe '#update_tracked_fields!', :clean_gitlab_redis_shared_state do
let(:request) { OpenStruct.new(remote_ip: "127.0.0.1") }
let(:user) { create(:user) }
it 'writes trackable attributes' do
expect do
user.update_tracked_fields!(request)
end.to change { user.reload.current_sign_in_at }
end
it 'does not write trackable attributes when called a second time within the hour' do
user.update_tracked_fields!(request)
expect do
user.update_tracked_fields!(request)
end.not_to change { user.reload.current_sign_in_at }
end
it 'writes trackable attributes for a different user' do
user2 = create(:user)
user.update_tracked_fields!(request)
expect do
user2.update_tracked_fields!(request)
end.to change { user2.reload.current_sign_in_at }
end
it 'does not write if the DB is in read-only mode' do
expect(Gitlab::Database).to receive(:read_only?).and_return(true)
expect do
user.update_tracked_fields!(request)
end.not_to change { user.reload.current_sign_in_at }
end
end
shared_context 'user keys' do
let(:user) { create(:user) }
let!(:key) { create(:key, user: user) }
let!(:deploy_key) { create(:deploy_key, user: user) }
end
describe '#keys' do
include_context 'user keys'
context 'with key and deploy key stored' do
it 'returns stored key, but not deploy_key' do
expect(user.keys).to include key
expect(user.keys).not_to include deploy_key
end
end
end
describe '#deploy_keys' do
include_context 'user keys'
context 'with key and deploy key stored' do
it 'returns stored deploy key, but not normal key' do
expect(user.deploy_keys).to include deploy_key
expect(user.deploy_keys).not_to include key
end
end
end
describe '#confirm' do
before do
allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(true)
end
let(:user) { create(:user, confirmed_at: nil, unconfirmed_email: 'test@gitlab.com') }
it 'returns unconfirmed' do
expect(user.confirmed?).to be_falsey
end
it 'confirms a user' do
user.confirm
expect(user.confirmed?).to be_truthy
end
end
describe '#to_reference' do
let(:user) { create(:user) }
it 'returns a String reference to the object' do
expect(user.to_reference).to eq "@#{user.username}"
end
end
describe '#generate_password' do
it "does not generate password by default" do
user = create(:user, password: 'abcdefghe')
expect(user.password).to eq('abcdefghe')
end
end
describe 'ensure user preference' do
it 'has user preference upon user initialization' do
user = build(:user)
expect(user.user_preference).to be_present
expect(user.user_preference).not_to be_persisted
end
end
describe 'ensure incoming email token' do
it 'has incoming email token' do
user = create(:user)
expect(user.incoming_email_token).not_to be_blank
end
it 'uses SecureRandom to generate the incoming email token' do
expect(SecureRandom).to receive(:hex).and_return('3b8ca303')
user = create(:user)
expect(user.incoming_email_token).to eql('gitlab')
end
end
describe '#ensure_user_rights_and_limits' do
describe 'with external user' do
let(:user) { create(:user, external: true) }
it 'receives callback when external changes' do
expect(user).to receive(:ensure_user_rights_and_limits)
user.update(external: false)
end
it 'ensures correct rights and limits for user' do
stub_config_setting(default_can_create_group: true)
expect { user.update(external: false) }.to change { user.can_create_group }.to(true)
.and change { user.projects_limit }.to(Gitlab::CurrentSettings.default_projects_limit)
end
end
describe 'without external user' do
let(:user) { create(:user, external: false) }
it 'receives callback when external changes' do
expect(user).to receive(:ensure_user_rights_and_limits)
user.update(external: true)
end
it 'ensures correct rights and limits for user' do
expect { user.update(external: true) }.to change { user.can_create_group }.to(false)
.and change { user.projects_limit }.to(0)
end
end
end
describe 'feed token' do
it 'ensures a feed token on read' do
user = create(:user, feed_token: nil)
feed_token = user.feed_token
expect(feed_token).not_to be_blank
expect(user.reload.feed_token).to eq feed_token
end
end
describe '#recently_sent_password_reset?' do
it 'is false when reset_password_sent_at is nil' do
user = build_stubbed(:user, reset_password_sent_at: nil)
expect(user.recently_sent_password_reset?).to eq false
end
it 'is false when sent more than one minute ago' do
user = build_stubbed(:user, reset_password_sent_at: 5.minutes.ago)
expect(user.recently_sent_password_reset?).to eq false
end
it 'is true when sent less than one minute ago' do
user = build_stubbed(:user, reset_password_sent_at: Time.now)
expect(user.recently_sent_password_reset?).to eq true
end
end
describe '#disable_two_factor!' do
it 'clears all 2FA-related fields' do
user = create(:user, :two_factor)
expect(user).to be_two_factor_enabled
expect(user.encrypted_otp_secret).not_to be_nil
expect(user.otp_backup_codes).not_to be_nil
expect(user.otp_grace_period_started_at).not_to be_nil
user.disable_two_factor!
expect(user).not_to be_two_factor_enabled
expect(user.encrypted_otp_secret).to be_nil
expect(user.encrypted_otp_secret_iv).to be_nil
expect(user.encrypted_otp_secret_salt).to be_nil
expect(user.otp_backup_codes).to be_nil
expect(user.otp_grace_period_started_at).to be_nil
end
end
describe 'projects' do
before do
@user = create(:user)
@project = create(:project, namespace: @user.namespace)
@project_2 = create(:project, group: create(:group)) do |project|
project.add_maintainer(@user)
end
@project_3 = create(:project, group: create(:group)) do |project|
project.add_developer(@user)
end
end
it { expect(@user.authorized_projects).to include(@project) }
it { expect(@user.authorized_projects).to include(@project_2) }
it { expect(@user.authorized_projects).to include(@project_3) }
it { expect(@user.owned_projects).to include(@project) }
it { expect(@user.owned_projects).not_to include(@project_2) }
it { expect(@user.owned_projects).not_to include(@project_3) }
it { expect(@user.personal_projects).to include(@project) }
it { expect(@user.personal_projects).not_to include(@project_2) }
it { expect(@user.personal_projects).not_to include(@project_3) }
end
describe 'groups' do
let(:user) { create(:user) }
let(:group) { create(:group) }
before do
group.add_owner(user)
end
it { expect(user.several_namespaces?).to be_truthy }
it { expect(user.authorized_groups).to eq([group]) }
it { expect(user.owned_groups).to eq([group]) }
it { expect(user.namespaces).to contain_exactly(user.namespace, group) }
it { expect(user.manageable_namespaces).to contain_exactly(user.namespace, group) }
context 'with child groups', :nested_groups do
let!(:subgroup) { create(:group, parent: group) }
describe '#manageable_namespaces' do
it 'includes all the namespaces the user can manage' do
expect(user.manageable_namespaces).to contain_exactly(user.namespace, group, subgroup)
end
end
describe '#manageable_groups' do
it 'includes all the namespaces the user can manage' do
expect(user.manageable_groups).to contain_exactly(group, subgroup)
end
it 'does not include duplicates if a membership was added for the subgroup' do
subgroup.add_owner(user)
expect(user.manageable_groups).to contain_exactly(group, subgroup)
end
end
describe '#manageable_groups_with_routes' do
it 'eager loads routes from manageable groups' do
control_count =
ActiveRecord::QueryRecorder.new(skip_cached: false) do
user.manageable_groups_with_routes.map(&:route)
end.count
create(:group, parent: subgroup)
expect do
user.manageable_groups_with_routes.map(&:route)
end.not_to exceed_all_query_limit(control_count)
end
end
end
end
describe 'group multiple owners' do
before do
@user = create :user
@user2 = create :user
@group = create :group
@group.add_owner(@user)
@group.add_user(@user2, GroupMember::OWNER)
end
it { expect(@user2.several_namespaces?).to be_truthy }
end
describe 'namespaced' do
before do
@user = create :user
@project = create(:project, namespace: @user.namespace)
end
it { expect(@user.several_namespaces?).to be_falsey }
it { expect(@user.namespaces).to eq([@user.namespace]) }
end
describe 'blocking user' do
let(:user) { create(:user, name: 'John Smith') }
it "blocks user" do
user.block
expect(user.blocked?).to be_truthy
end
end
describe '.filter' do
let(:user) { double }
it 'filters by active users by default' do
expect(described_class).to receive(:active).and_return([user])
expect(described_class.filter(nil)).to include user
end
it 'filters by admins' do
expect(described_class).to receive(:admins).and_return([user])
expect(described_class.filter('admins')).to include user
end
it 'filters by blocked' do
expect(described_class).to receive(:blocked).and_return([user])
expect(described_class.filter('blocked')).to include user
end
it 'filters by two_factor_disabled' do
expect(described_class).to receive(:without_two_factor).and_return([user])
expect(described_class.filter('two_factor_disabled')).to include user
end
it 'filters by two_factor_enabled' do
expect(described_class).to receive(:with_two_factor).and_return([user])
expect(described_class.filter('two_factor_enabled')).to include user
end
it 'filters by wop' do
expect(described_class).to receive(:without_projects).and_return([user])
expect(described_class.filter('wop')).to include user
end
end
describe '.without_projects' do
let!(:project) { create(:project, :public, :access_requestable) }
let!(:user) { create(:user) }
let!(:user_without_project) { create(:user) }
let!(:user_without_project2) { create(:user) }
before do
# add user to project
project.add_maintainer(user)
# create invite to projet
create(:project_member, :developer, project: project, invite_token: '1234', invite_email: 'inviteduser1@example.com')
# create request to join project
project.request_access(user_without_project2)
end
it { expect(described_class.without_projects).not_to include user }
it { expect(described_class.without_projects).to include user_without_project }
it { expect(described_class.without_projects).to include user_without_project2 }
end
describe 'user creation' do
describe 'normal user' do
let(:user) { create(:user, name: 'John Smith') }
it { expect(user.admin?).to be_falsey }
it { expect(user.require_ssh_key?).to be_truthy }
it { expect(user.can_create_group?).to be_truthy }
it { expect(user.can_create_project?).to be_truthy }
it { expect(user.first_name).to eq('John') }
it { expect(user.external).to be_falsey }
end
describe 'with defaults' do
let(:user) { described_class.new }
it "applies defaults to user" do
expect(user.projects_limit).to eq(Gitlab.config.gitlab.default_projects_limit)
expect(user.can_create_group).to eq(Gitlab.config.gitlab.default_can_create_group)
expect(user.theme_id).to eq(Gitlab.config.gitlab.default_theme)
expect(user.external).to be_falsey
end
end
describe 'with default overrides' do
let(:user) { described_class.new(projects_limit: 123, can_create_group: false, can_create_team: true) }
it "applies defaults to user" do
expect(user.projects_limit).to eq(123)
expect(user.can_create_group).to be_falsey
expect(user.theme_id).to eq(1)
end
it 'does not undo projects_limit setting if it matches old DB default of 10' do
# If the real default project limit is 10 then this test is worthless
expect(Gitlab.config.gitlab.default_projects_limit).not_to eq(10)
user = described_class.new(projects_limit: 10)
expect(user.projects_limit).to eq(10)
end
end
context 'when Gitlab::CurrentSettings.user_default_external is true' do
before do
stub_application_setting(user_default_external: true)
end
it "creates external user by default" do
user = create(:user)
expect(user.external).to be_truthy
expect(user.can_create_group).to be_falsey
expect(user.projects_limit).to be 0
end
describe 'with default overrides' do
it "creates a non-external user" do
user = create(:user, external: false)
expect(user.external).to be_falsey
end
end
end
describe '#require_ssh_key?', :use_clean_rails_memory_store_caching do
protocol_and_expectation = {
'http' => false,
'ssh' => true,
'' => true
}
protocol_and_expectation.each do |protocol, expected|
it "has correct require_ssh_key?" do
stub_application_setting(enabled_git_access_protocol: protocol)
user = build(:user)
expect(user.require_ssh_key?).to eq(expected)
end
end
it 'returns false when the user has 1 or more SSH keys' do
key = create(:personal_key)
expect(key.user.require_ssh_key?).to eq(false)
end
end
end
describe '.find_for_database_authentication' do
it 'strips whitespace from login' do
user = create(:user)
expect(described_class.find_for_database_authentication({ login: " #{user.username} " })).to eq user
end
end
describe '.find_by_any_email' do
it 'finds user through private commit email' do
user = create(:user)
private_email = user.private_commit_email
expect(described_class.find_by_any_email(private_email)).to eq(user)
expect(described_class.find_by_any_email(private_email, confirmed: true)).to eq(user)
end
it 'finds by primary email' do
user = create(:user, email: 'foo@example.com')
expect(described_class.find_by_any_email(user.email)).to eq user
expect(described_class.find_by_any_email(user.email, confirmed: true)).to eq user
end
it 'finds by uppercased email' do
user = create(:user, email: 'foo@example.com')
expect(described_class.find_by_any_email(user.email.upcase)).to eq user
expect(described_class.find_by_any_email(user.email.upcase, confirmed: true)).to eq user
end
context 'finds by secondary email' do
let(:user) { email.user }
context 'primary email confirmed' do
context 'secondary email confirmed' do
let!(:email) { create(:email, :confirmed, email: 'foo@example.com') }
it 'finds user respecting the confirmed flag' do
expect(described_class.find_by_any_email(email.email)).to eq user
expect(described_class.find_by_any_email(email.email, confirmed: true)).to eq user
end
end
context 'secondary email not confirmed' do
let!(:email) { create(:email, email: 'foo@example.com') }
it 'finds user respecting the confirmed flag' do
expect(described_class.find_by_any_email(email.email)).to eq user
expect(described_class.find_by_any_email(email.email, confirmed: true)).to be_nil
end
end
end
context 'primary email not confirmed' do
let(:user) { create(:user, confirmed_at: nil) }
let!(:email) { create(:email, :confirmed, user: user, email: 'foo@example.com') }
it 'finds user respecting the confirmed flag' do
expect(described_class.find_by_any_email(email.email)).to eq user
expect(described_class.find_by_any_email(email.email, confirmed: true)).to be_nil
end
end
end
it 'returns nil when nothing found' do
expect(described_class.find_by_any_email('')).to be_nil
end
it 'returns nil when user is not confirmed' do
user = create(:user, email: 'foo@example.com', confirmed_at: nil)
expect(described_class.find_by_any_email(user.email, confirmed: false)).to eq(user)
expect(described_class.find_by_any_email(user.email, confirmed: true)).to be_nil
end
end
describe '.by_any_email' do
it 'returns an ActiveRecord::Relation' do
expect(described_class.by_any_email('foo@example.com'))
.to be_a_kind_of(ActiveRecord::Relation)
end
it 'returns a relation of users' do
user = create(:user)
expect(described_class.by_any_email(user.email)).to eq([user])
end
it 'returns a relation of users for confirmed users' do
user = create(:user)
expect(described_class.by_any_email(user.email, confirmed: true)).to eq([user])
end
it 'finds user through a private commit email' do
user = create(:user)
private_email = user.private_commit_email
expect(described_class.by_any_email(private_email)).to eq([user])
expect(described_class.by_any_email(private_email, confirmed: true)).to eq([user])
end
it 'finds user through a private commit email in an array' do
user = create(:user)
private_email = user.private_commit_email
expect(described_class.by_any_email([private_email])).to eq([user])
expect(described_class.by_any_email([private_email], confirmed: true)).to eq([user])
end
end
describe '.search' do
let!(:user) { create(:user, name: 'user', username: 'usern', email: 'email@gmail.com') }
let!(:user2) { create(:user, name: 'user name', username: 'username', email: 'someemail@gmail.com') }
let!(:user3) { create(:user, name: 'us', username: 'se', email: 'foo@gmail.com') }
describe 'name matching' do
it 'returns users with a matching name with exact match first' do
expect(described_class.search(user.name)).to eq([user, user2])
end
it 'returns users with a partially matching name' do
expect(described_class.search(user.name[0..2])).to eq([user, user2])
end
it 'returns users with a matching name regardless of the casing' do
expect(described_class.search(user2.name.upcase)).to eq([user2])
end
it 'returns users with a exact matching name shorter than 3 chars' do
expect(described_class.search(user3.name)).to eq([user3])
end
it 'returns users with a exact matching name shorter than 3 chars regardless of the casing' do
expect(described_class.search(user3.name.upcase)).to eq([user3])
end
end
describe 'email matching' do
it 'returns users with a matching Email' do
expect(described_class.search(user.email)).to eq([user])
end
it 'does not return users with a partially matching Email' do
expect(described_class.search(user.email[0..2])).not_to include(user, user2)
end
it 'returns users with a matching Email regardless of the casing' do
expect(described_class.search(user2.email.upcase)).to eq([user2])
end
end
describe 'username matching' do
it 'returns users with a matching username' do
expect(described_class.search(user.username)).to eq([user, user2])
end
it 'returns users with a partially matching username' do
expect(described_class.search(user.username[0..2])).to eq([user, user2])
end
it 'returns users with a matching username regardless of the casing' do
expect(described_class.search(user2.username.upcase)).to eq([user2])
end
it 'returns users with a exact matching username shorter than 3 chars' do
expect(described_class.search(user3.username)).to eq([user3])
end
it 'returns users with a exact matching username shorter than 3 chars regardless of the casing' do
expect(described_class.search(user3.username.upcase)).to eq([user3])
end
end
it 'returns no matches for an empty string' do
expect(described_class.search('')).to be_empty
end
it 'returns no matches for nil' do
expect(described_class.search(nil)).to be_empty
end
end
describe '.search_with_secondary_emails' do
delegate :search_with_secondary_emails, to: :described_class
let!(:user) { create(:user, name: 'John Doe', username: 'john.doe', email: 'john.doe@example.com' ) }
let!(:another_user) { create(:user, name: 'Albert Smith', username: 'albert.smith', email: 'albert.smith@example.com' ) }
let!(:email) do
create(:email, user: another_user, email: 'alias@example.com')
end
it 'returns users with a matching name' do
expect(search_with_secondary_emails(user.name)).to eq([user])
end
it 'returns users with a partially matching name' do
expect(search_with_secondary_emails(user.name[0..2])).to eq([user])
end
it 'returns users with a matching name regardless of the casing' do
expect(search_with_secondary_emails(user.name.upcase)).to eq([user])
end
it 'returns users with a matching email' do
expect(search_with_secondary_emails(user.email)).to eq([user])
end
it 'does not return users with a partially matching email' do
expect(search_with_secondary_emails(user.email[0..2])).not_to include([user])
end
it 'returns users with a matching email regardless of the casing' do
expect(search_with_secondary_emails(user.email.upcase)).to eq([user])
end
it 'returns users with a matching username' do
expect(search_with_secondary_emails(user.username)).to eq([user])
end
it 'returns users with a partially matching username' do
expect(search_with_secondary_emails(user.username[0..2])).to eq([user])
end
it 'returns users with a matching username regardless of the casing' do
expect(search_with_secondary_emails(user.username.upcase)).to eq([user])
end
it 'returns users with a matching whole secondary email' do
expect(search_with_secondary_emails(email.email)).to eq([email.user])
end
it 'does not return users with a matching part of secondary email' do
expect(search_with_secondary_emails(email.email[1..4])).not_to include([email.user])
end
it 'returns no matches for an empty string' do
expect(search_with_secondary_emails('')).to be_empty
end
it 'returns no matches for nil' do
expect(search_with_secondary_emails(nil)).to be_empty
end
end
describe '.find_by_ssh_key_id' do
context 'using an existing SSH key ID' do
let(:user) { create(:user) }
let(:key) { create(:key, user: user) }
it 'returns the corresponding User' do
expect(described_class.find_by_ssh_key_id(key.id)).to eq(user)
end
end
context 'using an invalid SSH key ID' do
it 'returns nil' do
expect(described_class.find_by_ssh_key_id(-1)).to be_nil
end
end
end
describe '.by_login' do
let(:username) { 'John' }
let!(:user) { create(:user, username: username) }
it 'gets the correct user' do
expect(described_class.by_login(user.email.upcase)).to eq user
expect(described_class.by_login(user.email)).to eq user
expect(described_class.by_login(username.downcase)).to eq user
expect(described_class.by_login(username)).to eq user
expect(described_class.by_login(nil)).to be_nil
expect(described_class.by_login('')).to be_nil
end
end
describe '.find_by_username' do
it 'returns nil if not found' do
expect(described_class.find_by_username('JohnDoe')).to be_nil
end
it 'is case-insensitive' do
user = create(:user, username: 'JohnDoe')
expect(described_class.find_by_username('JOHNDOE')).to eq user
end
end
describe '.find_by_username!' do
it 'raises RecordNotFound' do
expect { described_class.find_by_username!('JohnDoe') }
.to raise_error(ActiveRecord::RecordNotFound)
end
it 'is case-insensitive' do
user = create(:user, username: 'JohnDoe')
expect(described_class.find_by_username!('JOHNDOE')).to eq user
end
end
describe '.find_by_full_path' do
let!(:user) { create(:user) }
context 'with a route matching the given path' do
let!(:route) { user.namespace.route }
it 'returns the user' do
expect(described_class.find_by_full_path(route.path)).to eq(user)
end
it 'is case-insensitive' do
expect(described_class.find_by_full_path(route.path.upcase)).to eq(user)
expect(described_class.find_by_full_path(route.path.downcase)).to eq(user)
end
end
context 'with a redirect route matching the given path' do
let!(:redirect_route) { user.namespace.redirect_routes.create(path: 'foo') }
context 'without the follow_redirects option' do
it 'returns nil' do
expect(described_class.find_by_full_path(redirect_route.path)).to eq(nil)
end
end
context 'with the follow_redirects option set to true' do
it 'returns the user' do
expect(described_class.find_by_full_path(redirect_route.path, follow_redirects: true)).to eq(user)
end
it 'is case-insensitive' do
expect(described_class.find_by_full_path(redirect_route.path.upcase, follow_redirects: true)).to eq(user)
expect(described_class.find_by_full_path(redirect_route.path.downcase, follow_redirects: true)).to eq(user)
end
end
end
context 'without a route or a redirect route matching the given path' do
context 'without the follow_redirects option' do
it 'returns nil' do
expect(described_class.find_by_full_path('unknown')).to eq(nil)
end
end
context 'with the follow_redirects option set to true' do
it 'returns nil' do
expect(described_class.find_by_full_path('unknown', follow_redirects: true)).to eq(nil)
end
end
end
context 'with a group route matching the given path' do
let!(:group) { create(:group, path: 'group_path') }
context 'when the group namespace has an owner_id (legacy data)' do
before do
group.update!(owner_id: user.id)
end
it 'returns nil' do
expect(described_class.find_by_full_path('group_path')).to eq(nil)
end
end
context 'when the group namespace does not have an owner_id' do
it 'returns nil' do
expect(described_class.find_by_full_path('group_path')).to eq(nil)
end
end
end
end
describe 'all_ssh_keys' do
it { is_expected.to have_many(:keys).dependent(:destroy) }
it "has all ssh keys" do
user = create :user
key = create :key, key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD33bWLBxu48Sev9Fert1yzEO4WGcWglWF7K/AwblIUFselOt/QdOL9DSjpQGxLagO1s9wl53STIO8qGS4Ms0EJZyIXOEFMjFJ5xmjSy+S37By4sG7SsltQEHMxtbtFOaW5LV2wCrX+rUsRNqLMamZjgjcPO0/EgGCXIGMAYW4O7cwGZdXWYIhQ1Vwy+CsVMDdPkPgBXqK7nR/ey8KMs8ho5fMNgB5hBw/AL9fNGhRw3QTD6Q12Nkhl4VZES2EsZqlpNnJttnPdp847DUsT6yuLRlfiQfz5Cn9ysHFdXObMN5VYIiPFwHeYCZp1X2S4fDZooRE8uOLTfxWHPXwrhqSH", user_id: user.id
expect(user.all_ssh_keys).to include(a_string_starting_with(key.key))
end
end
describe '#avatar_type' do
let(:user) { create(:user) }
it 'is true if avatar is image' do
user.update_attribute(:avatar, 'uploads/avatar.png')
expect(user.avatar_type).to be_truthy
end
it 'is false if avatar is html page' do
user.update_attribute(:avatar, 'uploads/avatar.html')
expect(user.avatar_type).to eq(['file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff, ico'])
end
end
describe '#avatar_url' do
let(:user) { create(:user, :with_avatar) }
context 'when avatar file is uploaded' do
it 'shows correct avatar url' do
expect(user.avatar_url).to eq(user.avatar.url)
expect(user.avatar_url(only_path: false)).to eq([Gitlab.config.gitlab.url, user.avatar.url].join)
end
end
end
describe '#accept_pending_invitations!' do
let(:user) { create(:user, email: 'user@email.com') }
let!(:project_member_invite) { create(:project_member, :invited, invite_email: user.email) }
let!(:group_member_invite) { create(:group_member, :invited, invite_email: user.email) }
let!(:external_project_member_invite) { create(:project_member, :invited, invite_email: 'external@email.com') }
let!(:external_group_member_invite) { create(:group_member, :invited, invite_email: 'external@email.com') }
it 'accepts all the user members pending invitations and returns the accepted_members' do
accepted_members = user.accept_pending_invitations!
expect(accepted_members).to match_array([project_member_invite, group_member_invite])
expect(group_member_invite.reload).not_to be_invite
expect(project_member_invite.reload).not_to be_invite
expect(external_project_member_invite.reload).to be_invite
expect(external_group_member_invite.reload).to be_invite
end
end
describe '#all_emails' do
let(:user) { create(:user) }
it 'returns all emails' do
email_confirmed = create :email, user: user, confirmed_at: Time.now
email_unconfirmed = create :email, user: user
user.reload
expect(user.all_emails).to contain_exactly(
user.email,
user.private_commit_email,
email_unconfirmed.email,
email_confirmed.email
)
end
end
describe '#verified_emails' do
let(:user) { create(:user) }
it 'returns only confirmed emails' do
email_confirmed = create :email, user: user, confirmed_at: Time.now
create :email, user: user
expect(user.verified_emails).to contain_exactly(
user.email,
user.private_commit_email,
email_confirmed.email
)
end
end
describe '#verified_email?' do
let(:user) { create(:user) }
it 'returns true when the email is verified/confirmed' do
email_confirmed = create :email, user: user, confirmed_at: Time.now
create :email, user: user
user.reload
expect(user.verified_email?(user.email)).to be_truthy
expect(user.verified_email?(email_confirmed.email.titlecase)).to be_truthy
end
it 'returns true when user is found through private commit email' do
expect(user.verified_email?(user.private_commit_email)).to be_truthy
end
it 'returns true for an outdated private commit email' do
old_email = user.private_commit_email
user.update!(username: 'changed-username')
expect(user.verified_email?(old_email)).to be_truthy
end
it 'returns false when the email is not verified/confirmed' do
email_unconfirmed = create :email, user: user
user.reload
expect(user.verified_email?(email_unconfirmed.email)).to be_falsy
end
end
describe '#requires_ldap_check?' do
let(:user) { described_class.new }
it 'is false when LDAP is disabled' do
# Create a condition which would otherwise cause 'true' to be returned
allow(user).to receive(:ldap_user?).and_return(true)
user.last_credential_check_at = nil
expect(user.requires_ldap_check?).to be_falsey
end
context 'when LDAP is enabled' do
before do
allow(Gitlab.config.ldap).to receive(:enabled).and_return(true)
end
it 'is false for non-LDAP users' do
allow(user).to receive(:ldap_user?).and_return(false)
expect(user.requires_ldap_check?).to be_falsey
end
context 'and when the user is an LDAP user' do
before do
allow(user).to receive(:ldap_user?).and_return(true)
end
it 'is true when the user has never had an LDAP check before' do
user.last_credential_check_at = nil
expect(user.requires_ldap_check?).to be_truthy
end
it 'is true when the last LDAP check happened over 1 hour ago' do
user.last_credential_check_at = 2.hours.ago
expect(user.requires_ldap_check?).to be_truthy
end
end
end
end
context 'ldap synchronized user' do
describe '#ldap_user?' do
it 'is true if provider name starts with ldap' do
user = create(:omniauth_user, provider: 'ldapmain')
expect(user.ldap_user?).to be_truthy
end
it 'is false for other providers' do
user = create(:omniauth_user, provider: 'other-provider')
expect(user.ldap_user?).to be_falsey
end
it 'is false if no extern_uid is provided' do
user = create(:omniauth_user, extern_uid: nil)
expect(user.ldap_user?).to be_falsey
end
end
describe '#ldap_identity' do
it 'returns ldap identity' do
user = create :omniauth_user
expect(user.ldap_identity.provider).not_to be_empty
end
end
describe '#ldap_block' do
let(:user) { create(:omniauth_user, provider: 'ldapmain', name: 'John Smith') }
it 'blocks user flaging the action caming from ldap' do
user.ldap_block
expect(user.blocked?).to be_truthy
expect(user.ldap_blocked?).to be_truthy
end
end
end
describe '#full_website_url' do
let(:user) { create(:user) }
it 'begins with http if website url omits it' do
user.website_url = 'test.com'
expect(user.full_website_url).to eq 'http://test.com'
end
it 'begins with http if website url begins with http' do
user.website_url = 'http://test.com'
expect(user.full_website_url).to eq 'http://test.com'
end
it 'begins with https if website url begins with https' do
user.website_url = 'https://test.com'
expect(user.full_website_url).to eq 'https://test.com'
end
end
describe '#short_website_url' do
let(:user) { create(:user) }
it 'does not begin with http if website url omits it' do
user.website_url = 'test.com'
expect(user.short_website_url).to eq 'test.com'
end
it 'does not begin with http if website url begins with http' do
user.website_url = 'http://test.com'
expect(user.short_website_url).to eq 'test.com'
end
it 'does not begin with https if website url begins with https' do
user.website_url = 'https://test.com'
expect(user.short_website_url).to eq 'test.com'
end
end
describe '#sanitize_attrs' do
let(:user) { build(:user, name: 'test & user', skype: 'test&user') }
it 'encodes HTML entities in the Skype attribute' do
expect { user.sanitize_attrs }.to change { user.skype }.to('test&amp;user')
end
it 'does not encode HTML entities in the name attribute' do
expect { user.sanitize_attrs }.not_to change { user.name }
end
end
describe '#starred?' do
it 'determines if user starred a project' do
user = create :user
project1 = create(:project, :public)
project2 = create(:project, :public)
expect(user.starred?(project1)).to be_falsey
expect(user.starred?(project2)).to be_falsey
star1 = UsersStarProject.create!(project: project1, user: user)
expect(user.starred?(project1)).to be_truthy
expect(user.starred?(project2)).to be_falsey
star2 = UsersStarProject.create!(project: project2, user: user)
expect(user.starred?(project1)).to be_truthy
expect(user.starred?(project2)).to be_truthy
star1.destroy
expect(user.starred?(project1)).to be_falsey
expect(user.starred?(project2)).to be_truthy
star2.destroy
expect(user.starred?(project1)).to be_falsey
expect(user.starred?(project2)).to be_falsey
end
end
describe '#toggle_star' do
it 'toggles stars' do
user = create :user
project = create(:project, :public)
expect(user.starred?(project)).to be_falsey
user.toggle_star(project)
expect(user.starred?(project)).to be_truthy
user.toggle_star(project)
expect(user.starred?(project)).to be_falsey
end
end
describe '.find_by_private_commit_email' do
context 'with email' do
set(:user) { create(:user) }
it 'returns user through private commit email' do
expect(described_class.find_by_private_commit_email(user.private_commit_email)).to eq(user)
end
it 'returns nil when email other than private_commit_email is used' do
expect(described_class.find_by_private_commit_email(user.email)).to be_nil
end
end
it 'returns nil when email is nil' do
expect(described_class.find_by_private_commit_email(nil)).to be_nil
end
end
describe '#sort_by_attribute' do
before do
described_class.delete_all
@user = create :user, created_at: Date.today, current_sign_in_at: Date.today, name: 'Alpha'
@user1 = create :user, created_at: Date.today - 1, current_sign_in_at: Date.today - 1, name: 'Omega'
@user2 = create :user, created_at: Date.today - 2, name: 'Beta'
end
context 'when sort by recent_sign_in' do
let(:users) { described_class.sort_by_attribute('recent_sign_in') }
it 'sorts users by recent sign-in time' do
expect(users.first).to eq(@user)
expect(users.second).to eq(@user1)
end
it 'pushes users who never signed in to the end' do
expect(users.third).to eq(@user2)
end
end
context 'when sort by oldest_sign_in' do
let(:users) { described_class.sort_by_attribute('oldest_sign_in') }
it 'sorts users by the oldest sign-in time' do
expect(users.first).to eq(@user1)
expect(users.second).to eq(@user)
end
it 'pushes users who never signed in to the end' do
expect(users.third).to eq(@user2)
end
end
it 'sorts users in descending order by their creation time' do
expect(described_class.sort_by_attribute('created_desc').first).to eq(@user)
end
it 'sorts users in ascending order by their creation time' do
expect(described_class.sort_by_attribute('created_asc').first).to eq(@user2)
end
it 'sorts users by id in descending order when nil is passed' do
expect(described_class.sort_by_attribute(nil).first).to eq(@user2)
end
end
describe "#contributed_projects" do
subject { create(:user) }
let!(:project1) { create(:project) }
let!(:project2) { fork_project(project3) }
let!(:project3) { create(:project) }
let!(:merge_request) { create(:merge_request, source_project: project2, target_project: project3, author: subject) }
let!(:push_event) { create(:push_event, project: project1, author: subject) }
let!(:merge_event) { create(:event, :created, project: project3, target: merge_request, author: subject) }
before do
project1.add_maintainer(subject)
project2.add_maintainer(subject)
end
it "includes IDs for projects the user has pushed to" do
expect(subject.contributed_projects).to include(project1)
end
it "includes IDs for projects the user has had merge requests merged into" do
expect(subject.contributed_projects).to include(project3)
end
it "doesn't include IDs for unrelated projects" do
expect(subject.contributed_projects).not_to include(project2)
end
end
describe '#fork_of' do
let(:user) { create(:user) }
it "returns a user's fork of a project" do
project = create(:project, :public)
user_fork = fork_project(project, user, namespace: user.namespace)
expect(user.fork_of(project)).to eq(user_fork)
end
it 'returns nil if the project does not have a fork network' do
project = create(:project)
expect(user.fork_of(project)).to be_nil
end
end
describe '#can_be_removed?' do
subject { create(:user) }
context 'no owned groups' do
it { expect(subject.can_be_removed?).to be_truthy }
end
context 'has owned groups' do
before do
group = create(:group)
group.add_owner(subject)
end
it { expect(subject.can_be_removed?).to be_falsey }
end
end
describe "#recent_push" do
let(:user) { build(:user) }
let(:project) { build(:project) }
let(:event) { build(:push_event) }
it 'returns the last push event for the user' do
expect_any_instance_of(Users::LastPushEventService)
.to receive(:last_event_for_user)
.and_return(event)
expect(user.recent_push).to eq(event)
end
it 'returns the last push event for a project when one is given' do
expect_any_instance_of(Users::LastPushEventService)
.to receive(:last_event_for_project)
.and_return(event)
expect(user.recent_push(project)).to eq(event)
end
end
describe '#authorized_groups' do
let!(:user) { create(:user) }
let!(:private_group) { create(:group) }
let!(:child_group) { create(:group, parent: private_group) }
let!(:project_group) { create(:group) }
let!(:project) { create(:project, group: project_group) }
before do
private_group.add_user(user, Gitlab::Access::MAINTAINER)
project.add_maintainer(user)
end
subject { user.authorized_groups }
it { is_expected.to contain_exactly private_group, project_group }
end
describe '#membership_groups' do
let!(:user) { create(:user) }
let!(:parent_group) { create(:group) }
let!(:child_group) { create(:group, parent: parent_group) }
before do
parent_group.add_user(user, Gitlab::Access::MAINTAINER)
end
subject { user.membership_groups }
if Group.supports_nested_objects?
it { is_expected.to contain_exactly parent_group, child_group }
else
it { is_expected.to contain_exactly parent_group }
end
end
describe '#authorizations_for_projects' do
let!(:user) { create(:user) }
subject { Project.where("EXISTS (?)", user.authorizations_for_projects) }
it 'includes projects that belong to a user, but no other projects' do
owned = create(:project, :private, namespace: user.namespace)
member = create(:project, :private).tap { |p| p.add_maintainer(user) }
other = create(:project)
expect(subject).to include(owned)
expect(subject).to include(member)
expect(subject).not_to include(other)
end
it 'includes projects a user has access to, but no other projects' do
other_user = create(:user)
accessible = create(:project, :private, namespace: other_user.namespace) do |project|
project.add_developer(user)
end
other = create(:project)
expect(subject).to include(accessible)
expect(subject).not_to include(other)
end
context 'with min_access_level' do
let!(:user) { create(:user) }
let!(:project) { create(:project, :private, namespace: user.namespace) }
before do
project.add_developer(user)
end
subject { Project.where("EXISTS (?)", user.authorizations_for_projects(min_access_level: min_access_level)) }
context 'when developer access' do
let(:min_access_level) { Gitlab::Access::DEVELOPER }
it 'includes projects a user has access to' do
expect(subject).to include(project)
end
end
context 'when owner access' do
let(:min_access_level) { Gitlab::Access::OWNER }
it 'does not include projects with higher access level' do
expect(subject).not_to include(project)
end
end
end
end
describe '#authorized_projects', :delete do
context 'with a minimum access level' do
it 'includes projects for which the user is an owner' do
user = create(:user)
project = create(:project, :private, namespace: user.namespace)
expect(user.authorized_projects(Gitlab::Access::REPORTER))
.to contain_exactly(project)
end
it 'includes projects for which the user is a maintainer' do
user = create(:user)
project = create(:project, :private)
project.add_maintainer(user)
expect(user.authorized_projects(Gitlab::Access::REPORTER))
.to contain_exactly(project)
end
end
it "includes user's personal projects" do
user = create(:user)
project = create(:project, :private, namespace: user.namespace)
expect(user.authorized_projects).to include(project)
end
it "includes personal projects user has been given access to" do
user1 = create(:user)
user2 = create(:user)
project = create(:project, :private, namespace: user1.namespace)
project.add_developer(user2)
expect(user2.authorized_projects).to include(project)
end
it "includes projects of groups user has been added to" do
group = create(:group)
project = create(:project, group: group)
user = create(:user)
group.add_developer(user)
expect(user.authorized_projects).to include(project)
end
it "does not include projects of groups user has been removed from" do
group = create(:group)
project = create(:project, group: group)
user = create(:user)
member = group.add_developer(user)
expect(user.authorized_projects).to include(project)
member.destroy
expect(user.authorized_projects).not_to include(project)
end
it "includes projects shared with user's group" do
user = create(:user)
project = create(:project, :private)
group = create(:group)
group.add_reporter(user)
project.project_group_links.create(group: group)
expect(user.authorized_projects).to include(project)
end
it "does not include destroyed projects user had access to" do
user1 = create(:user)
user2 = create(:user)
project = create(:project, :private, namespace: user1.namespace)
project.add_developer(user2)
expect(user2.authorized_projects).to include(project)
project.destroy
expect(user2.authorized_projects).not_to include(project)
end
it "does not include projects of destroyed groups user had access to" do
group = create(:group)
project = create(:project, namespace: group)
user = create(:user)
group.add_developer(user)
expect(user.authorized_projects).to include(project)
group.destroy
expect(user.authorized_projects).not_to include(project)
end
end
describe '#projects_where_can_admin_issues' do
let(:user) { create(:user) }
it 'includes projects for which the user access level is above or equal to reporter' do
reporter_project = create(:project) { |p| p.add_reporter(user) }
developer_project = create(:project) { |p| p.add_developer(user) }
maintainer_project = create(:project) { |p| p.add_maintainer(user) }
expect(user.projects_where_can_admin_issues.to_a).to match_array([maintainer_project, developer_project, reporter_project])
expect(user.can?(:admin_issue, maintainer_project)).to eq(true)
expect(user.can?(:admin_issue, developer_project)).to eq(true)
expect(user.can?(:admin_issue, reporter_project)).to eq(true)
end
it 'does not include for which the user access level is below reporter' do
project = create(:project)
guest_project = create(:project) { |p| p.add_guest(user) }
expect(user.projects_where_can_admin_issues.to_a).to be_empty
expect(user.can?(:admin_issue, guest_project)).to eq(false)
expect(user.can?(:admin_issue, project)).to eq(false)
end
it 'does not include archived projects' do
project = create(:project, :archived)
expect(user.projects_where_can_admin_issues.to_a).to be_empty
expect(user.can?(:admin_issue, project)).to eq(false)
end
it 'does not include projects for which issues are disabled' do
project = create(:project, :issues_disabled)
expect(user.projects_where_can_admin_issues.to_a).to be_empty
expect(user.can?(:admin_issue, project)).to eq(false)
end
end
describe '#ci_owned_runners' do
let(:user) { create(:user) }
let!(:project) { create(:project) }
let(:runner) { create(:ci_runner, :project, projects: [project]) }
context 'without any projects nor groups' do
it 'does not load' do
expect(user.ci_owned_runners).to be_empty
end
end
context 'with personal projects runners' do
let(:namespace) { create(:namespace, owner: user) }
let!(:project) { create(:project, namespace: namespace) }
it 'loads' do
expect(user.ci_owned_runners).to contain_exactly(runner)
end
end
context 'with personal group runner' do
let!(:project) { create(:project) }
let(:group_runner) { create(:ci_runner, :group, groups: [group]) }
let!(:group) do
create(:group).tap do |group|
group.add_owner(user)
end
end
it 'loads' do
expect(user.ci_owned_runners).to contain_exactly(group_runner)
end
end
context 'with personal project and group runner' do
let(:namespace) { create(:namespace, owner: user) }
let!(:project) { create(:project, namespace: namespace) }
let!(:group_runner) { create(:ci_runner, :group, groups: [group]) }
let!(:group) do
create(:group).tap do |group|
group.add_owner(user)
end
end
it 'loads' do
expect(user.ci_owned_runners).to contain_exactly(runner, group_runner)
end
end
shared_examples :member do
context 'when the user is a maintainer' do
before do
add_user(:maintainer)
end
it 'loads' do
expect(user.ci_owned_runners).to contain_exactly(runner)
end
end
context 'when the user is a developer' do
before do
add_user(:developer)
end
it 'does not load' do
expect(user.ci_owned_runners).to be_empty
end
end
end
context 'with groups projects runners' do
let(:group) { create(:group) }
let!(:project) { create(:project, group: group) }
def add_user(access)
group.add_user(user, access)
end
it_behaves_like :member
end
context 'with groups runners' do
let!(:runner) { create(:ci_runner, :group, groups: [group]) }
let!(:group) { create(:group) }
def add_user(access)
group.add_user(user, access)
end
it_behaves_like :member
end
context 'with other projects runners' do
let!(:project) { create(:project) }
def add_user(access)
project.add_role(user, access)
end
it_behaves_like :member
end
context 'with subgroup with different owner for project runner', :nested_groups do
let(:group) { create(:group) }
let(:another_user) { create(:user) }
let(:subgroup) { create(:group, parent: group) }
let!(:project) { create(:project, group: subgroup) }
def add_user(access)
group.add_user(user, access)
group.add_user(another_user, :owner)
subgroup.add_user(another_user, :owner)
end
it_behaves_like :member
end
end
describe '#projects_with_reporter_access_limited_to' do
let(:project1) { create(:project) }
let(:project2) { create(:project) }
let(:user) { create(:user) }
before do
project1.add_reporter(user)
project2.add_guest(user)
end
it 'returns the projects when using a single project ID' do
projects = user.projects_with_reporter_access_limited_to(project1.id)
expect(projects).to eq([project1])
end
it 'returns the projects when using an Array of project IDs' do
projects = user.projects_with_reporter_access_limited_to([project1.id])
expect(projects).to eq([project1])
end
it 'returns the projects when using an ActiveRecord relation' do
projects = user
.projects_with_reporter_access_limited_to(Project.select(:id))
expect(projects).to eq([project1])
end
it 'does not return projects you do not have reporter access to' do
projects = user.projects_with_reporter_access_limited_to(project2.id)
expect(projects).to be_empty
end
end
describe '#all_expanded_groups' do
# foo/bar would also match foo/barbaz instead of just foo/bar and foo/bar/baz
let!(:user) { create(:user) }
# group
# _______ (foo) _______
# | |
# | |
# nested_group_1 nested_group_2
# (bar) (barbaz)
# | |
# | |
# nested_group_1_1 nested_group_2_1
# (baz) (baz)
#
let!(:group) { create :group }
let!(:nested_group_1) { create :group, parent: group, name: 'bar' }
let!(:nested_group_1_1) { create :group, parent: nested_group_1, name: 'baz' }
let!(:nested_group_2) { create :group, parent: group, name: 'barbaz' }
let!(:nested_group_2_1) { create :group, parent: nested_group_2, name: 'baz' }
subject { user.all_expanded_groups }
context 'user is not a member of any group' do
it 'returns an empty array' do
is_expected.to eq([])
end
end
context 'user is member of all groups' do
before do
group.add_reporter(user)
nested_group_1.add_developer(user)
nested_group_1_1.add_maintainer(user)
nested_group_2.add_developer(user)
nested_group_2_1.add_maintainer(user)
end
it 'returns all groups' do
is_expected.to match_array [
group,
nested_group_1, nested_group_1_1,
nested_group_2, nested_group_2_1
]
end
end
context 'user is member of the top group' do
before do
group.add_owner(user)
end
if Group.supports_nested_objects?
it 'returns all groups' do
is_expected.to match_array [
group,
nested_group_1, nested_group_1_1,
nested_group_2, nested_group_2_1
]
end
else
it 'returns the top-level groups' do
is_expected.to match_array [group]
end
end
end
context 'user is member of the first child (internal node), branch 1', :nested_groups do
before do
nested_group_1.add_owner(user)
end
it 'returns the groups in the hierarchy' do
is_expected.to match_array [
group,
nested_group_1, nested_group_1_1
]
end
end
context 'user is member of the first child (internal node), branch 2', :nested_groups do
before do
nested_group_2.add_owner(user)
end
it 'returns the groups in the hierarchy' do
is_expected.to match_array [
group,
nested_group_2, nested_group_2_1
]
end
end
context 'user is member of the last child (leaf node)', :nested_groups do
before do
nested_group_1_1.add_owner(user)
end
it 'returns the groups in the hierarchy' do
is_expected.to match_array [
group,
nested_group_1, nested_group_1_1
]
end
end
end
describe '#refresh_authorized_projects', :clean_gitlab_redis_shared_state do
let(:project1) { create(:project) }
let(:project2) { create(:project) }
let(:user) { create(:user) }
before do
project1.add_reporter(user)
project2.add_guest(user)
user.project_authorizations.delete_all
user.refresh_authorized_projects
end
it 'refreshes the list of authorized projects' do
expect(user.project_authorizations.count).to eq(2)
end
it 'stores the correct access levels' do
expect(user.project_authorizations.where(access_level: Gitlab::Access::GUEST).exists?).to eq(true)
expect(user.project_authorizations.where(access_level: Gitlab::Access::REPORTER).exists?).to eq(true)
end
end
describe '#access_level=' do
let(:user) { build(:user) }
it 'does nothing for an invalid access level' do
user.access_level = :invalid_access_level
expect(user.access_level).to eq(:regular)
expect(user.admin).to be false
end
it "assigns the 'admin' access level" do
user.access_level = :admin
expect(user.access_level).to eq(:admin)
expect(user.admin).to be true
end
it "doesn't clear existing access levels when an invalid access level is passed in" do
user.access_level = :admin
user.access_level = :invalid_access_level
expect(user.access_level).to eq(:admin)
expect(user.admin).to be true
end
it "accepts string values in addition to symbols" do
user.access_level = 'admin'
expect(user.access_level).to eq(:admin)
expect(user.admin).to be true
end
end
describe '#full_private_access?' do
it 'returns false for regular user' do
user = build(:user)
expect(user.full_private_access?).to be_falsy
end
it 'returns true for admin user' do
user = build(:user, :admin)
expect(user.full_private_access?).to be_truthy
end
end
describe '.ghost' do
it "creates a ghost user if one isn't already present" do
ghost = described_class.ghost
expect(ghost).to be_ghost
expect(ghost).to be_persisted
expect(ghost.namespace).not_to be_nil
expect(ghost.namespace).to be_persisted
end
it "does not create a second ghost user if one is already present" do
expect do
described_class.ghost
described_class.ghost
end.to change { described_class.count }.by(1)
expect(described_class.ghost).to eq(described_class.ghost)
end
context "when a regular user exists with the username 'ghost'" do
it "creates a ghost user with a non-conflicting username" do
create(:user, username: 'ghost')
ghost = described_class.ghost
expect(ghost).to be_persisted
expect(ghost.username).to eq('ghost1')
end
end
context "when a regular user exists with the email 'ghost@example.com'" do
it "creates a ghost user with a non-conflicting email" do
create(:user, email: 'ghost@example.com')
ghost = described_class.ghost
expect(ghost).to be_persisted
expect(ghost.email).to eq('ghost1@example.com')
end
end
context 'when a domain whitelist is in place' do
before do
stub_application_setting(domain_whitelist: ['gitlab.com'])
end
it 'creates a ghost user' do
expect(described_class.ghost).to be_persisted
end
end
end
describe '#update_two_factor_requirement' do
let(:user) { create :user }
context 'with 2FA requirement on groups' do
let(:group1) { create :group, require_two_factor_authentication: true, two_factor_grace_period: 23 }
let(:group2) { create :group, require_two_factor_authentication: true, two_factor_grace_period: 32 }
before do
group1.add_user(user, GroupMember::OWNER)
group2.add_user(user, GroupMember::OWNER)
user.update_two_factor_requirement
end
it 'requires 2FA' do
expect(user.require_two_factor_authentication_from_group).to be true
end
it 'uses the shortest grace period' do
expect(user.two_factor_grace_period).to be 23
end
end
context 'with 2FA requirement on nested parent group', :nested_groups do
let!(:group1) { create :group, require_two_factor_authentication: true }
let!(:group1a) { create :group, require_two_factor_authentication: false, parent: group1 }
before do
group1a.add_user(user, GroupMember::OWNER)
user.update_two_factor_requirement
end
it 'requires 2FA' do
expect(user.require_two_factor_authentication_from_group).to be true
end
end
context 'with 2FA requirement on nested child group', :nested_groups do
let!(:group1) { create :group, require_two_factor_authentication: false }
let!(:group1a) { create :group, require_two_factor_authentication: true, parent: group1 }
before do
group1.add_user(user, GroupMember::OWNER)
user.update_two_factor_requirement
end
it 'requires 2FA' do
expect(user.require_two_factor_authentication_from_group).to be true
end
end
context 'without 2FA requirement on groups' do
let(:group) { create :group }
before do
group.add_user(user, GroupMember::OWNER)
user.update_two_factor_requirement
end
it 'does not require 2FA' do
expect(user.require_two_factor_authentication_from_group).to be false
end
it 'falls back to the default grace period' do
expect(user.two_factor_grace_period).to be 48
end
end
end
context '.active' do
before do
described_class.ghost
create(:user, name: 'user', state: 'active')
create(:user, name: 'user', state: 'blocked')
end
it 'only counts active and non internal users' do
expect(described_class.active.count).to eq(1)
end
end
describe 'preferred language' do
it 'is English by default' do
user = create(:user)
expect(user.preferred_language).to eq('en')
end
end
context '#invalidate_issue_cache_counts' do
let(:user) { build_stubbed(:user) }
it 'invalidates cache for issue counter' do
cache_mock = double
expect(cache_mock).to receive(:delete).with(['users', user.id, 'assigned_open_issues_count'])
allow(Rails).to receive(:cache).and_return(cache_mock)
user.invalidate_issue_cache_counts
end
end
context '#invalidate_merge_request_cache_counts' do
let(:user) { build_stubbed(:user) }
it 'invalidates cache for Merge Request counter' do
cache_mock = double
expect(cache_mock).to receive(:delete).with(['users', user.id, 'assigned_open_merge_requests_count'])
allow(Rails).to receive(:cache).and_return(cache_mock)
user.invalidate_merge_request_cache_counts
end
end
context '#invalidate_personal_projects_count' do
let(:user) { build_stubbed(:user) }
it 'invalidates cache for personal projects counter' do
cache_mock = double
expect(cache_mock).to receive(:delete).with(['users', user.id, 'personal_projects_count'])
allow(Rails).to receive(:cache).and_return(cache_mock)
user.invalidate_personal_projects_count
end
end
describe '#allow_password_authentication_for_web?' do
context 'regular user' do
let(:user) { build(:user) }
it 'returns true when password authentication is enabled for the web interface' do
expect(user.allow_password_authentication_for_web?).to be_truthy
end
it 'returns false when password authentication is disabled for the web interface' do
stub_application_setting(password_authentication_enabled_for_web: false)
expect(user.allow_password_authentication_for_web?).to be_falsey
end
end
it 'returns false for ldap user' do
user = create(:omniauth_user, provider: 'ldapmain')
expect(user.allow_password_authentication_for_web?).to be_falsey
end
end
describe '#allow_password_authentication_for_git?' do
context 'regular user' do
let(:user) { build(:user) }
it 'returns true when password authentication is enabled for Git' do
expect(user.allow_password_authentication_for_git?).to be_truthy
end
it 'returns false when password authentication is disabled Git' do
stub_application_setting(password_authentication_enabled_for_git: false)
expect(user.allow_password_authentication_for_git?).to be_falsey
end
end
it 'returns false for ldap user' do
user = create(:omniauth_user, provider: 'ldapmain')
expect(user.allow_password_authentication_for_git?).to be_falsey
end
end
describe '#assigned_open_merge_requests_count' do
it 'returns number of open merge requests from non-archived projects' do
user = create(:user)
project = create(:project, :public)
archived_project = create(:project, :public, :archived)
create(:merge_request, source_project: project, author: user, assignee: user)
create(:merge_request, :closed, source_project: project, author: user, assignee: user)
create(:merge_request, source_project: archived_project, author: user, assignee: user)
expect(user.assigned_open_merge_requests_count(force: true)).to eq 1
end
end
describe '#assigned_open_issues_count' do
it 'returns number of open issues from non-archived projects' do
user = create(:user)
project = create(:project, :public)
archived_project = create(:project, :public, :archived)
create(:issue, project: project, author: user, assignees: [user])
create(:issue, :closed, project: project, author: user, assignees: [user])
create(:issue, project: archived_project, author: user, assignees: [user])
expect(user.assigned_open_issues_count(force: true)).to eq 1
end
end
describe '#personal_projects_count' do
it 'returns the number of personal projects using a single query' do
user = build(:user)
projects = double(:projects, count: 1)
expect(user).to receive(:personal_projects).and_return(projects)
expect(user.personal_projects_count).to eq(1)
end
end
describe '#projects_limit_left' do
it 'returns the number of projects that can be created by the user' do
user = build(:user)
allow(user).to receive(:projects_limit).and_return(10)
allow(user).to receive(:personal_projects_count).and_return(5)
expect(user.projects_limit_left).to eq(5)
end
end
describe '#ensure_namespace_correct' do
context 'for a new user' do
let(:user) { build(:user) }
it 'creates the namespace' do
expect(user.namespace).to be_nil
user.save!
expect(user.namespace).not_to be_nil
end
end
context 'for an existing user' do
let(:username) { 'foo' }
let(:user) { create(:user, username: username) }
context 'when the user is updated' do
context 'when the username is changed' do
let(:new_username) { 'bar' }
it 'changes the namespace (just to compare to when username is not changed)' do
expect do
Timecop.freeze(1.second.from_now) do
user.update!(username: new_username)
end
end.to change { user.namespace.updated_at }
end
it 'updates the namespace name' do
user.update!(username: new_username)
expect(user.namespace.name).to eq(new_username)
end
it 'updates the namespace path' do
user.update!(username: new_username)
expect(user.namespace.path).to eq(new_username)
end
context 'when there is a validation error (namespace name taken) while updating namespace' do
let!(:conflicting_namespace) { create(:group, path: new_username) }
it 'causes the user save to fail' do
expect(user.update(username: new_username)).to be_falsey
expect(user.namespace.errors.messages[:path].first).to eq('has already been taken')
end
it 'adds the namespace errors to the user' do
user.update(username: new_username)
expect(user.errors.full_messages.first).to eq('Username has already been taken')
end
end
end
context 'when the username is not changed' do
it 'does not change the namespace' do
expect do
user.update!(email: 'asdf@asdf.com')
end.not_to change { user.namespace.updated_at }
end
end
end
end
end
describe '#username_changed_hook' do
context 'for a new user' do
let(:user) { build(:user) }
it 'does not trigger system hook' do
expect(user).not_to receive(:system_hook_service)
user.save!
end
end
context 'for an existing user' do
let(:user) { create(:user, username: 'old-username') }
context 'when the username is changed' do
let(:new_username) { 'very-new-name' }
it 'triggers the rename system hook' do
system_hook_service = SystemHooksService.new
expect(system_hook_service).to receive(:execute_hooks_for).with(user, :rename)
expect(user).to receive(:system_hook_service).and_return(system_hook_service)
user.update!(username: new_username)
end
end
context 'when the username is not changed' do
it 'does not trigger system hook' do
expect(user).not_to receive(:system_hook_service)
user.update!(email: 'asdf@asdf.com')
end
end
end
end
describe '#sync_attribute?' do
let(:user) { described_class.new }
context 'oauth user' do
it 'returns true if name can be synced' do
stub_omniauth_setting(sync_profile_attributes: %w(name location))
expect(user.sync_attribute?(:name)).to be_truthy
end
it 'returns true if email can be synced' do
stub_omniauth_setting(sync_profile_attributes: %w(name email))
expect(user.sync_attribute?(:email)).to be_truthy
end
it 'returns true if location can be synced' do
stub_omniauth_setting(sync_profile_attributes: %w(location email))
expect(user.sync_attribute?(:email)).to be_truthy
end
it 'returns false if name can not be synced' do
stub_omniauth_setting(sync_profile_attributes: %w(location email))
expect(user.sync_attribute?(:name)).to be_falsey
end
it 'returns false if email can not be synced' do
stub_omniauth_setting(sync_profile_attributes: %w(location email))
expect(user.sync_attribute?(:name)).to be_falsey
end
it 'returns false if location can not be synced' do
stub_omniauth_setting(sync_profile_attributes: %w(location email))
expect(user.sync_attribute?(:name)).to be_falsey
end
it 'returns true for all syncable attributes if all syncable attributes can be synced' do
stub_omniauth_setting(sync_profile_attributes: true)
expect(user.sync_attribute?(:name)).to be_truthy
expect(user.sync_attribute?(:email)).to be_truthy
expect(user.sync_attribute?(:location)).to be_truthy
end
it 'returns false for all syncable attributes but email if no syncable attributes are declared' do
expect(user.sync_attribute?(:name)).to be_falsey
expect(user.sync_attribute?(:email)).to be_truthy
expect(user.sync_attribute?(:location)).to be_falsey
end
end
context 'ldap user' do
it 'returns true for email if ldap user' do
allow(user).to receive(:ldap_user?).and_return(true)
expect(user.sync_attribute?(:name)).to be_falsey
expect(user.sync_attribute?(:email)).to be_truthy
expect(user.sync_attribute?(:location)).to be_falsey
end
it 'returns true for email and location if ldap user and location declared as syncable' do
allow(user).to receive(:ldap_user?).and_return(true)
stub_omniauth_setting(sync_profile_attributes: %w(location))
expect(user.sync_attribute?(:name)).to be_falsey
expect(user.sync_attribute?(:email)).to be_truthy
expect(user.sync_attribute?(:location)).to be_truthy
end
end
end
describe '#confirm_deletion_with_password?' do
where(
password_automatically_set: [true, false],
ldap_user: [true, false],
password_authentication_disabled: [true, false]
)
with_them do
let!(:user) { create(:user, password_automatically_set: password_automatically_set) }
let!(:identity) { create(:identity, user: user) if ldap_user }
# Only confirm deletion with password if all inputs are false
let(:expected) { !(password_automatically_set || ldap_user || password_authentication_disabled) }
before do
stub_application_setting(password_authentication_enabled_for_web: !password_authentication_disabled)
stub_application_setting(password_authentication_enabled_for_git: !password_authentication_disabled)
end
it 'returns false unless all inputs are true' do
expect(user.confirm_deletion_with_password?).to eq(expected)
end
end
end
describe '#delete_async' do
let(:user) { create(:user) }
let(:deleted_by) { create(:user) }
it 'blocks the user then schedules them for deletion if a hard delete is specified' do
expect(DeleteUserWorker).to receive(:perform_async).with(deleted_by.id, user.id, hard_delete: true)
user.delete_async(deleted_by: deleted_by, params: { hard_delete: true })
expect(user).to be_blocked
end
it 'schedules user for deletion without blocking them' do
expect(DeleteUserWorker).to receive(:perform_async).with(deleted_by.id, user.id, {})
user.delete_async(deleted_by: deleted_by)
expect(user).not_to be_blocked
end
end
describe '#max_member_access_for_project_ids' do
shared_examples 'max member access for projects' do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:owner_project) { create(:project, group: group) }
let(:maintainer_project) { create(:project) }
let(:reporter_project) { create(:project) }
let(:developer_project) { create(:project) }
let(:guest_project) { create(:project) }
let(:no_access_project) { create(:project) }
let(:projects) do
[owner_project, maintainer_project, reporter_project, developer_project, guest_project, no_access_project].map(&:id)
end
let(:expected) do
{
owner_project.id => Gitlab::Access::OWNER,
maintainer_project.id => Gitlab::Access::MAINTAINER,
reporter_project.id => Gitlab::Access::REPORTER,
developer_project.id => Gitlab::Access::DEVELOPER,
guest_project.id => Gitlab::Access::GUEST,
no_access_project.id => Gitlab::Access::NO_ACCESS
}
end
before do
create(:group_member, user: user, group: group)
maintainer_project.add_maintainer(user)
reporter_project.add_reporter(user)
developer_project.add_developer(user)
guest_project.add_guest(user)
end
it 'returns correct roles for different projects' do
expect(user.max_member_access_for_project_ids(projects)).to eq(expected)
end
end
context 'with RequestStore enabled', :request_store do
include_examples 'max member access for projects'
def access_levels(projects)
user.max_member_access_for_project_ids(projects)
end
it 'does not perform extra queries when asked for projects who have already been found' do
access_levels(projects)
expect { access_levels(projects) }.not_to exceed_query_limit(0)
expect(access_levels(projects)).to eq(expected)
end
it 'only requests the extra projects when uncached projects are passed' do
second_maintainer_project = create(:project)
second_developer_project = create(:project)
second_maintainer_project.add_maintainer(user)
second_developer_project.add_developer(user)
all_projects = projects + [second_maintainer_project.id, second_developer_project.id]
expected_all = expected.merge(second_maintainer_project.id => Gitlab::Access::MAINTAINER,
second_developer_project.id => Gitlab::Access::DEVELOPER)
access_levels(projects)
queries = ActiveRecord::QueryRecorder.new { access_levels(all_projects) }
expect(queries.count).to eq(1)
expect(queries.log_message).to match(/\W(#{second_maintainer_project.id}, #{second_developer_project.id})\W/)
expect(access_levels(all_projects)).to eq(expected_all)
end
end
context 'with RequestStore disabled' do
include_examples 'max member access for projects'
end
end
describe '#max_member_access_for_group_ids' do
shared_examples 'max member access for groups' do
let(:user) { create(:user) }
let(:owner_group) { create(:group) }
let(:maintainer_group) { create(:group) }
let(:reporter_group) { create(:group) }
let(:developer_group) { create(:group) }
let(:guest_group) { create(:group) }
let(:no_access_group) { create(:group) }
let(:groups) do
[owner_group, maintainer_group, reporter_group, developer_group, guest_group, no_access_group].map(&:id)
end
let(:expected) do
{
owner_group.id => Gitlab::Access::OWNER,
maintainer_group.id => Gitlab::Access::MAINTAINER,
reporter_group.id => Gitlab::Access::REPORTER,
developer_group.id => Gitlab::Access::DEVELOPER,
guest_group.id => Gitlab::Access::GUEST,
no_access_group.id => Gitlab::Access::NO_ACCESS
}
end
before do
owner_group.add_owner(user)
maintainer_group.add_maintainer(user)
reporter_group.add_reporter(user)
developer_group.add_developer(user)
guest_group.add_guest(user)
end
it 'returns correct roles for different groups' do
expect(user.max_member_access_for_group_ids(groups)).to eq(expected)
end
end
context 'with RequestStore enabled', :request_store do
include_examples 'max member access for groups'
def access_levels(groups)
user.max_member_access_for_group_ids(groups)
end
it 'does not perform extra queries when asked for groups who have already been found' do
access_levels(groups)
expect { access_levels(groups) }.not_to exceed_query_limit(0)
expect(access_levels(groups)).to eq(expected)
end
it 'only requests the extra groups when uncached groups are passed' do
second_maintainer_group = create(:group)
second_developer_group = create(:group)
second_maintainer_group.add_maintainer(user)
second_developer_group.add_developer(user)
all_groups = groups + [second_maintainer_group.id, second_developer_group.id]
expected_all = expected.merge(second_maintainer_group.id => Gitlab::Access::MAINTAINER,
second_developer_group.id => Gitlab::Access::DEVELOPER)
access_levels(groups)
queries = ActiveRecord::QueryRecorder.new { access_levels(all_groups) }
expect(queries.count).to eq(1)
expect(queries.log_message).to match(/\W(#{second_maintainer_group.id}, #{second_developer_group.id})\W/)
expect(access_levels(all_groups)).to eq(expected_all)
end
end
context 'with RequestStore disabled' do
include_examples 'max member access for groups'
end
end
context 'changing a username' do
let(:user) { create(:user, username: 'foo') }
it 'creates a redirect route' do
expect { user.update!(username: 'bar') }
.to change { RedirectRoute.where(path: 'foo').count }.by(1)
end
it 'deletes the redirect when a user with the old username was created' do
user.update!(username: 'bar')
expect { create(:user, username: 'foo') }
.to change { RedirectRoute.where(path: 'foo').count }.by(-1)
end
end
describe '#required_terms_not_accepted?' do
let(:user) { build(:user) }
subject { user.required_terms_not_accepted? }
context "when terms are not enforced" do
it { is_expected.to be_falsy }
end
context "when terms are enforced and accepted by the user" do
before do
enforce_terms
accept_terms(user)
end
it { is_expected.to be_falsy }
end
context "when terms are enforced but the user has not accepted" do
before do
enforce_terms
end
it { is_expected.to be_truthy }
end
end
describe '#increment_failed_attempts!' do
subject(:user) { create(:user, failed_attempts: 0) }
it 'logs failed sign-in attempts' do
expect { user.increment_failed_attempts! }.to change(user, :failed_attempts).from(0).to(1)
end
it 'does not log failed sign-in attempts when in a GitLab read-only instance' do
allow(Gitlab::Database).to receive(:read_only?) { true }
expect { user.increment_failed_attempts! }.not_to change(user, :failed_attempts)
end
end
describe '#requires_usage_stats_consent?' do
let(:user) { create(:user, created_at: 8.days.ago) }
before do
allow(user).to receive(:has_current_license?).and_return false
end
context 'in single-user environment' do
it 'requires user consent after one week' do
create(:user, ghost: true)
expect(user.requires_usage_stats_consent?).to be true
end
it 'requires user consent after one week if there is another ghost user' do
expect(user.requires_usage_stats_consent?).to be true
end
it 'does not require consent in the first week' do
user.created_at = 6.days.ago
expect(user.requires_usage_stats_consent?).to be false
end
it 'does not require consent if usage stats were set by this user' do
allow(Gitlab::CurrentSettings).to receive(:usage_stats_set_by_user_id).and_return(user.id)
expect(user.requires_usage_stats_consent?).to be false
end
end
context 'in multi-user environment' do
before do
create(:user)
end
it 'does not require consent' do
expect(user.requires_usage_stats_consent?).to be false
end
end
end
context 'with uploads' do
it_behaves_like 'model with uploads', false do
let(:model_object) { create(:user, :with_avatar) }
let(:upload_attribute) { :avatar }
let(:uploader_class) { AttachmentUploader }
end
end
describe '.union_with_user' do
context 'when no user ID is provided' do
it 'returns the input relation' do
user = create(:user)
expect(described_class.union_with_user).to eq([user])
end
end
context 'when a user ID is provided' do
it 'includes the user object in the returned relation' do
user1 = create(:user)
user2 = create(:user)
users = described_class.where(id: user1.id).union_with_user(user2.id)
expect(users).to include(user1)
expect(users).to include(user2)
end
it 'does not re-apply any WHERE conditions on the outer query' do
relation = described_class.where(id: 1).union_with_user(2)
expect(relation.arel.where_sql).to be_nil
end
end
end
describe '.optionally_search' do
context 'using nil as the argument' do
it 'returns the current relation' do
user = create(:user)
expect(described_class.optionally_search).to eq([user])
end
end
context 'using an empty String as the argument' do
it 'returns the current relation' do
user = create(:user)
expect(described_class.optionally_search('')).to eq([user])
end
end
context 'using a non-empty String' do
it 'returns users matching the search query' do
user1 = create(:user)
create(:user)
expect(described_class.optionally_search(user1.name)).to eq([user1])
end
end
end
describe '.where_not_in' do
context 'without an argument' do
it 'returns the current relation' do
user = create(:user)
expect(described_class.where_not_in).to eq([user])
end
end
context 'using a list of user IDs' do
it 'excludes the users from the returned relation' do
user1 = create(:user)
user2 = create(:user)
expect(described_class.where_not_in([user2.id])).to eq([user1])
end
end
end
describe '.reorder_by_name' do
it 'reorders the input relation' do
user1 = create(:user, name: 'A')
user2 = create(:user, name: 'B')
expect(described_class.reorder_by_name).to eq([user1, user2])
end
end
end