diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-19 15:44:42 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-19 15:44:42 +0000 |
commit | 4555e1b21c365ed8303ffb7a3325d773c9b8bf31 (patch) | |
tree | 5423a1c7516cffe36384133ade12572cf709398d /spec/services/users | |
parent | e570267f2f6b326480d284e0164a6464ba4081bc (diff) | |
download | gitlab-ce-4555e1b21c365ed8303ffb7a3325d773c9b8bf31.tar.gz |
Add latest changes from gitlab-org/gitlab@13-12-stable-eev13.12.0-rc42
Diffstat (limited to 'spec/services/users')
6 files changed, 405 insertions, 127 deletions
diff --git a/spec/services/users/ban_service_spec.rb b/spec/services/users/ban_service_spec.rb new file mode 100644 index 00000000000..0e6ac615da5 --- /dev/null +++ b/spec/services/users/ban_service_spec.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Users::BanService do + let(:current_user) { create(:admin) } + + subject(:service) { described_class.new(current_user) } + + describe '#execute' do + subject(:operation) { service.execute(user) } + + context 'when successful' do + let(:user) { create(:user) } + + it { is_expected.to eq(status: :success) } + + it "bans the user" do + expect { operation }.to change { user.state }.to('banned') + end + + it "blocks the user" do + expect { operation }.to change { user.blocked? }.from(false).to(true) + end + + it 'logs ban in application logs' do + allow(Gitlab::AppLogger).to receive(:info) + + operation + + expect(Gitlab::AppLogger).to have_received(:info).with(message: "User banned", user: "#{user.username}", email: "#{user.email}", banned_by: "#{current_user.username}", ip_address: "#{current_user.current_sign_in_ip}") + end + end + + context 'when failed' do + let(:user) { create(:user, :blocked) } + + it 'returns error result' do + aggregate_failures 'error result' do + expect(operation[:status]).to eq(:error) + expect(operation[:message]).to match(/State cannot transition/) + end + end + + it "does not change the user's state" do + expect { operation }.not_to change { user.state } + end + end + end +end diff --git a/spec/services/users/build_service_spec.rb b/spec/services/users/build_service_spec.rb index b2a7d349ce6..e8786c677d1 100644 --- a/spec/services/users/build_service_spec.rb +++ b/spec/services/users/build_service_spec.rb @@ -6,105 +6,76 @@ RSpec.describe Users::BuildService do using RSpec::Parameterized::TableSyntax describe '#execute' do - let(:params) { build_stubbed(:user).slice(:first_name, :last_name, :username, :email, :password) } - - context 'with an admin user' do - let(:params) { build_stubbed(:user).slice(:name, :username, :email, :password) } + let_it_be(:current_user) { nil } - let(:admin_user) { create(:admin) } - let(:service) { described_class.new(admin_user, ActionController::Parameters.new(params).permit!) } + let(:params) { build_stubbed(:user).slice(:first_name, :last_name, :username, :email, :password) } + let(:service) { described_class.new(current_user, params) } - it 'returns a valid user' do - expect(service.execute).to be_valid - end + shared_examples_for 'common build items' do + it { is_expected.to be_valid } it 'sets the created_by_id' do - expect(service.execute.created_by_id).to eq(admin_user.id) + expect(user.created_by_id).to eq(current_user&.id) end - context 'calls the UpdateCanonicalEmailService' do - specify do - expect(Users::UpdateCanonicalEmailService).to receive(:new).and_call_original + it 'calls UpdateCanonicalEmailService' do + expect(Users::UpdateCanonicalEmailService).to receive(:new).and_call_original - service.execute - end + user end - context 'allowed params' do - let(:params) do - { - access_level: 1, - admin: 1, - avatar: anything, - bio: 1, - can_create_group: 1, - color_scheme_id: 1, - email: 1, - external: 1, - force_random_password: 1, - hide_no_password: 1, - hide_no_ssh_key: 1, - linkedin: 1, - name: 1, - password: 1, - password_automatically_set: 1, - password_expires_at: 1, - projects_limit: 1, - remember_me: 1, - skip_confirmation: 1, - skype: 1, - theme_id: 1, - twitter: 1, - username: 1, - website_url: 1, - private_profile: 1, - organization: 1, - location: 1, - public_email: 1 - } - end + context 'when user_type is provided' do + context 'when project_bot' do + before do + params.merge!({ user_type: :project_bot }) + end - it 'sets all allowed attributes' do - admin_user # call first so the admin gets created before setting `expect` + it { expect(user.project_bot?).to be true } + end - expect(User).to receive(:new).with(hash_including(params)).and_call_original + context 'when not a project_bot' do + before do + params.merge!({ user_type: :alert_bot }) + end - service.execute + it { expect(user).to be_human } end end + end + shared_examples_for 'current user not admin' do context 'with "user_default_external" application setting' do where(:user_default_external, :external, :email, :user_default_internal_regex, :result) do true | nil | 'fl@example.com' | nil | true true | true | 'fl@example.com' | nil | true - true | false | 'fl@example.com' | nil | false + true | false | 'fl@example.com' | nil | true # admin difference true | nil | 'fl@example.com' | '' | true true | true | 'fl@example.com' | '' | true - true | false | 'fl@example.com' | '' | false + true | false | 'fl@example.com' | '' | true # admin difference true | nil | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false - true | true | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | true + true | true | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false # admin difference true | false | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false true | nil | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | true true | true | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | true - true | false | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | false + true | false | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | true # admin difference false | nil | 'fl@example.com' | nil | false - false | true | 'fl@example.com' | nil | true + false | true | 'fl@example.com' | nil | false # admin difference false | false | 'fl@example.com' | nil | false false | nil | 'fl@example.com' | '' | false - false | true | 'fl@example.com' | '' | true + false | true | 'fl@example.com' | '' | false # admin difference false | false | 'fl@example.com' | '' | false false | nil | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false - false | true | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | true + false | true | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false # admin difference false | false | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false false | nil | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | false - false | true | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | true + false | true | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | false # admin difference false | false | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | false end @@ -116,40 +87,11 @@ RSpec.describe Users::BuildService do params.merge!({ external: external, email: email }.compact) end - subject(:user) { service.execute } - - it 'correctly sets user.external' do + it 'sets the value of Gitlab::CurrentSettings.user_default_external' do expect(user.external).to eq(result) end end end - end - - context 'with non admin user' do - let(:user) { create(:user) } - let(:service) { described_class.new(user, params) } - - it 'raises AccessDeniedError exception' do - expect { service.execute }.to raise_error Gitlab::Access::AccessDeniedError - end - - context 'when authorization is skipped' do - subject(:built_user) { service.execute(skip_authorization: true) } - - it { is_expected.to be_valid } - - it 'sets the created_by_id' do - expect(built_user.created_by_id).to eq(user.id) - end - end - end - - context 'with nil user' do - let(:service) { described_class.new(nil, params) } - - it 'returns a valid user' do - expect(service.execute).to be_valid - end context 'when "send_user_confirmation_email" application setting is true' do before do @@ -157,7 +99,7 @@ RSpec.describe Users::BuildService do end it 'does not confirm the user' do - expect(service.execute).not_to be_confirmed + expect(user).not_to be_confirmed end end @@ -167,27 +109,103 @@ RSpec.describe Users::BuildService do end it 'confirms the user' do - expect(service.execute).to be_confirmed + expect(user).to be_confirmed end end - context 'when user_type is provided' do - subject(:user) { service.execute } + context 'with allowed params' do + let(:params) do + { + email: 1, + name: 1, + password: 1, + password_automatically_set: 1, + username: 1, + user_type: 'project_bot' + } + end - context 'when project_bot' do - before do - params.merge!({ user_type: :project_bot }) - end + it 'sets all allowed attributes' do + expect(User).to receive(:new).with(hash_including(params)).and_call_original - it { expect(user.project_bot?).to be true} + user end + end + end - context 'when not a project_bot' do - before do - params.merge!({ user_type: :alert_bot }) - end + context 'with nil current_user' do + subject(:user) { service.execute } + + it_behaves_like 'common build items' + it_behaves_like 'current user not admin' + end + + context 'with non admin current_user' do + let_it_be(:current_user) { create(:user) } + + let(:service) { described_class.new(current_user, params) } + + subject(:user) { service.execute(skip_authorization: true) } + + it 'raises AccessDeniedError exception when authorization is not skipped' do + expect { service.execute }.to raise_error Gitlab::Access::AccessDeniedError + end - it { expect(user.user_type).to be nil } + it_behaves_like 'common build items' + it_behaves_like 'current user not admin' + end + + context 'with an admin current_user' do + let_it_be(:current_user) { create(:admin) } + + let(:params) { build_stubbed(:user).slice(:name, :username, :email, :password) } + let(:service) { described_class.new(current_user, ActionController::Parameters.new(params).permit!) } + + subject(:user) { service.execute } + + it_behaves_like 'common build items' + + context 'with allowed params' do + let(:params) do + { + access_level: 1, + admin: 1, + avatar: anything, + bio: 1, + can_create_group: 1, + color_scheme_id: 1, + email: 1, + external: 1, + force_random_password: 1, + hide_no_password: 1, + hide_no_ssh_key: 1, + linkedin: 1, + name: 1, + password: 1, + password_automatically_set: 1, + password_expires_at: 1, + projects_limit: 1, + remember_me: 1, + skip_confirmation: 1, + skype: 1, + theme_id: 1, + twitter: 1, + username: 1, + website_url: 1, + private_profile: 1, + organization: 1, + location: 1, + public_email: 1, + user_type: 'project_bot', + note: 1, + view_diffs_file_by_file: 1 + } + end + + it 'sets all allowed attributes' do + expect(User).to receive(:new).with(hash_including(params)).and_call_original + + service.execute end end @@ -195,34 +213,34 @@ RSpec.describe Users::BuildService do where(:user_default_external, :external, :email, :user_default_internal_regex, :result) do true | nil | 'fl@example.com' | nil | true true | true | 'fl@example.com' | nil | true - true | false | 'fl@example.com' | nil | true + true | false | 'fl@example.com' | nil | false # admin difference true | nil | 'fl@example.com' | '' | true true | true | 'fl@example.com' | '' | true - true | false | 'fl@example.com' | '' | true + true | false | 'fl@example.com' | '' | false # admin difference true | nil | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false - true | true | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false + true | true | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | true # admin difference true | false | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false true | nil | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | true true | true | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | true - true | false | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | true + true | false | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | false # admin difference false | nil | 'fl@example.com' | nil | false - false | true | 'fl@example.com' | nil | false + false | true | 'fl@example.com' | nil | true # admin difference false | false | 'fl@example.com' | nil | false false | nil | 'fl@example.com' | '' | false - false | true | 'fl@example.com' | '' | false + false | true | 'fl@example.com' | '' | true # admin difference false | false | 'fl@example.com' | '' | false false | nil | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false - false | true | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false + false | true | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | true # admin difference false | false | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false false | nil | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | false - false | true | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | false + false | true | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | true # admin difference false | false | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | false end @@ -234,8 +252,6 @@ RSpec.describe Users::BuildService do params.merge!({ external: external, email: email }.compact) end - subject(:user) { service.execute } - it 'sets the value of Gitlab::CurrentSettings.user_default_external' do expect(user.external).to eq(result) end diff --git a/spec/services/users/registrations_build_service_spec.rb b/spec/services/users/registrations_build_service_spec.rb new file mode 100644 index 00000000000..bc3718dbdb2 --- /dev/null +++ b/spec/services/users/registrations_build_service_spec.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Users::RegistrationsBuildService do + describe '#execute' do + let(:base_params) { build_stubbed(:user).slice(:first_name, :last_name, :username, :email, :password) } + let(:skip_param) { {} } + let(:params) { base_params.merge(skip_param) } + + subject(:service) { described_class.new(nil, params) } + + before do + stub_application_setting(signup_enabled?: true) + end + + context 'when automatic user confirmation is not enabled' do + before do + stub_application_setting(send_user_confirmation_email: true) + end + + context 'when skip_confirmation is true' do + let(:skip_param) { { skip_confirmation: true } } + + it 'confirms the user' do + expect(service.execute).to be_confirmed + end + end + + context 'when skip_confirmation is not set' do + it 'does not confirm the user' do + expect(service.execute).not_to be_confirmed + end + end + + context 'when skip_confirmation is false' do + let(:skip_param) { { skip_confirmation: false } } + + it 'does not confirm the user' do + expect(service.execute).not_to be_confirmed + end + end + end + + context 'when automatic user confirmation is enabled' do + before do + stub_application_setting(send_user_confirmation_email: false) + end + + context 'when skip_confirmation is true' do + let(:skip_param) { { skip_confirmation: true } } + + it 'confirms the user' do + expect(service.execute).to be_confirmed + end + end + + context 'when skip_confirmation is not set the application setting takes precedence' do + it 'confirms the user' do + expect(service.execute).to be_confirmed + end + end + + context 'when skip_confirmation is false the application setting takes precedence' do + let(:skip_param) { { skip_confirmation: false } } + + it 'confirms the user' do + expect(service.execute).to be_confirmed + end + end + end + end +end diff --git a/spec/services/users/update_assigned_open_issue_count_service_spec.rb b/spec/services/users/update_assigned_open_issue_count_service_spec.rb new file mode 100644 index 00000000000..55fc60a7893 --- /dev/null +++ b/spec/services/users/update_assigned_open_issue_count_service_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Users::UpdateAssignedOpenIssueCountService do + let_it_be(:user) { create(:user) } + + describe '#initialize' do + context 'incorrect arguments provided' do + it 'raises an error if there are no target user' do + expect { described_class.new(target_user: nil) }.to raise_error(ArgumentError, /Please provide a target user/) + expect { described_class.new(target_user: "nonsense") }.to raise_error(ArgumentError, /Please provide a target user/) + end + end + + context 'when correct arguments provided' do + it 'is successful' do + expect { described_class.new(target_user: user) }.not_to raise_error + end + end + end + + describe "#execute", :clean_gitlab_redis_cache do + let(:fake_update_service) { double } + let(:fake_issue_count_service) { double } + let(:provided_value) { nil } + + subject { described_class.new(target_user: user).execute } + + context 'successful' do + it 'returns a success response' do + expect(subject).to be_success + end + + it 'writes the cache with the new value' do + expect(Rails.cache).to receive(:write).with(['users', user.id, 'assigned_open_issues_count'], 0, expires_in: User::COUNT_CACHE_VALIDITY_PERIOD) + + subject + end + + it 'calls the issues finder to get the latest value' do + expect(IssuesFinder).to receive(:new).with(user, assignee_id: user.id, state: 'opened', non_archived: true).and_return(fake_issue_count_service) + expect(fake_issue_count_service).to receive(:execute) + + subject + end + end + end +end diff --git a/spec/services/users/update_todo_count_cache_service_spec.rb b/spec/services/users/update_todo_count_cache_service_spec.rb index 3e3618b1291..3d96af928df 100644 --- a/spec/services/users/update_todo_count_cache_service_spec.rb +++ b/spec/services/users/update_todo_count_cache_service_spec.rb @@ -14,13 +14,21 @@ RSpec.describe Users::UpdateTodoCountCacheService do let_it_be(:todo5) { create(:todo, user: user2, state: :pending) } let_it_be(:todo6) { create(:todo, user: user2, state: :pending) } + def execute_all + described_class.new([user1.id, user2.id]).execute + end + + def execute_single + described_class.new([user1.id]).execute + end + it 'updates the todos_counts for users', :use_clean_rails_memory_store_caching do Rails.cache.write(['users', user1.id, 'todos_done_count'], 0) Rails.cache.write(['users', user1.id, 'todos_pending_count'], 0) Rails.cache.write(['users', user2.id, 'todos_done_count'], 0) Rails.cache.write(['users', user2.id, 'todos_pending_count'], 0) - expect { described_class.new([user1, user2]).execute } + expect { execute_all } .to change(user1, :todos_done_count).from(0).to(2) .and change(user1, :todos_pending_count).from(0).to(1) .and change(user2, :todos_done_count).from(0).to(1) @@ -28,7 +36,7 @@ RSpec.describe Users::UpdateTodoCountCacheService do Todo.delete_all - expect { described_class.new([user1, user2]).execute } + expect { execute_all } .to change(user1, :todos_done_count).from(2).to(0) .and change(user1, :todos_pending_count).from(1).to(0) .and change(user2, :todos_done_count).from(1).to(0) @@ -36,26 +44,24 @@ RSpec.describe Users::UpdateTodoCountCacheService do end it 'avoids N+1 queries' do - control_count = ActiveRecord::QueryRecorder.new { described_class.new([user1]).execute }.count + control_count = ActiveRecord::QueryRecorder.new { execute_single }.count - expect { described_class.new([user1, user2]).execute }.not_to exceed_query_limit(control_count) + expect { execute_all }.not_to exceed_query_limit(control_count) end it 'executes one query per batch of users' do stub_const("#{described_class}::QUERY_BATCH_SIZE", 1) - expect(ActiveRecord::QueryRecorder.new { described_class.new([user1]).execute }.count).to eq(1) - expect(ActiveRecord::QueryRecorder.new { described_class.new([user1, user2]).execute }.count).to eq(2) + expect(ActiveRecord::QueryRecorder.new { execute_single }.count).to eq(1) + expect(ActiveRecord::QueryRecorder.new { execute_all }.count).to eq(2) end - it 'sets the cache expire time to the users count_cache_validity_period' do - allow(user1).to receive(:count_cache_validity_period).and_return(1.minute) - allow(user2).to receive(:count_cache_validity_period).and_return(1.hour) - - expect(Rails.cache).to receive(:write).with(['users', user1.id, anything], anything, expires_in: 1.minute).twice - expect(Rails.cache).to receive(:write).with(['users', user2.id, anything], anything, expires_in: 1.hour).twice + it 'sets the correct cache expire time' do + expect(Rails.cache).to receive(:write) + .with(['users', user1.id, anything], anything, expires_in: User::COUNT_CACHE_VALIDITY_PERIOD) + .twice - described_class.new([user1, user2]).execute + execute_single end end end diff --git a/spec/services/users/upsert_credit_card_validation_service_spec.rb b/spec/services/users/upsert_credit_card_validation_service_spec.rb new file mode 100644 index 00000000000..148638fe5e7 --- /dev/null +++ b/spec/services/users/upsert_credit_card_validation_service_spec.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Users::UpsertCreditCardValidationService do + let_it_be(:user) { create(:user) } + + let(:user_id) { user.id } + let(:credit_card_validated_time) { Time.utc(2020, 1, 1) } + let(:params) { { user_id: user_id, credit_card_validated_at: credit_card_validated_time } } + + describe '#execute' do + subject(:service) { described_class.new(params) } + + context 'successfully set credit card validation record for the user' do + context 'when user does not have credit card validation record' do + it 'creates the credit card validation and returns a success' do + expect(user.credit_card_validated_at).to be nil + + result = service.execute + + expect(result.status).to eq(:success) + expect(user.reload.credit_card_validated_at).to eq(credit_card_validated_time) + end + end + + context 'when user has credit card validation record' do + let(:old_time) { Time.utc(1999, 2, 2) } + + before do + create(:credit_card_validation, user: user, credit_card_validated_at: old_time) + end + + it 'updates the credit card validation and returns a success' do + expect(user.credit_card_validated_at).to eq(old_time) + + result = service.execute + + expect(result.status).to eq(:success) + expect(user.reload.credit_card_validated_at).to eq(credit_card_validated_time) + end + end + end + + shared_examples 'returns an error without tracking the exception' do + it do + expect(Gitlab::ErrorTracking).not_to receive(:track_exception) + + result = service.execute + + expect(result.status).to eq(:error) + end + end + + context 'when user id does not exist' do + let(:user_id) { non_existing_record_id } + + it_behaves_like 'returns an error without tracking the exception' + end + + context 'when missing credit_card_validated_at' do + let(:params) { { user_id: user_id } } + + it_behaves_like 'returns an error without tracking the exception' + end + + context 'when missing user id' do + let(:params) { { credit_card_validated_at: credit_card_validated_time } } + + it_behaves_like 'returns an error without tracking the exception' + end + + context 'when unexpected exception happen' do + it 'tracks the exception and returns an error' do + expect(::Users::CreditCardValidation).to receive(:upsert).and_raise(e = StandardError.new('My exception!')) + expect(Gitlab::ErrorTracking).to receive(:track_exception).with(e, class: described_class.to_s, params: params) + + result = service.execute + + expect(result.status).to eq(:error) + end + end + end +end |