summaryrefslogtreecommitdiff
path: root/spec/services/users
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-05-19 15:44:42 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-05-19 15:44:42 +0000
commit4555e1b21c365ed8303ffb7a3325d773c9b8bf31 (patch)
tree5423a1c7516cffe36384133ade12572cf709398d /spec/services/users
parente570267f2f6b326480d284e0164a6464ba4081bc (diff)
downloadgitlab-ce-4555e1b21c365ed8303ffb7a3325d773c9b8bf31.tar.gz
Add latest changes from gitlab-org/gitlab@13-12-stable-eev13.12.0-rc42
Diffstat (limited to 'spec/services/users')
-rw-r--r--spec/services/users/ban_service_spec.rb50
-rw-r--r--spec/services/users/build_service_spec.rb244
-rw-r--r--spec/services/users/registrations_build_service_spec.rb73
-rw-r--r--spec/services/users/update_assigned_open_issue_count_service_spec.rb49
-rw-r--r--spec/services/users/update_todo_count_cache_service_spec.rb32
-rw-r--r--spec/services/users/upsert_credit_card_validation_service_spec.rb84
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