diff options
Diffstat (limited to 'spec/support/shared_examples')
34 files changed, 528 insertions, 339 deletions
diff --git a/spec/support/shared_examples/analytics/cycle_analytics/parentable_examples.rb b/spec/support/shared_examples/analytics/cycle_analytics/parentable_examples.rb new file mode 100644 index 00000000000..5fd0e685c67 --- /dev/null +++ b/spec/support/shared_examples/analytics/cycle_analytics/parentable_examples.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'value stream analytics namespace models' do + let(:factory_name) { nil } + + context 'when ProjectNamespace is given' do + it 'is valid' do + project_namespace = create(:project_namespace) + model = build(factory_name, namespace: project_namespace) + + expect(model).to be_valid + expect(model.save).to be(true) + expect(model.namespace).to eq(project_namespace) + end + end + + context 'when Namespace is given' do + it 'fails' do + namespace = create(:namespace) + model = build(factory_name, namespace: namespace) + + expect(model).to be_invalid + + error_message = s_('CycleAnalytics|the assigned object is not supported') + expect(model.errors.messages_for(:namespace)).to eq([error_message]) + end + end +end diff --git a/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb b/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb index 5506b05ca55..de38d1ff9f8 100644 --- a/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb +++ b/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb @@ -258,7 +258,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do .to receive(:new).with(provider_repo, provider_repo[:name], user.namespace, user, type: provider, **access_params) .and_return(double(execute: project)) - post :create, format: :json + post :create, params: { target_namespace: user.namespace }, format: :json expect(response).to have_gitlab_http_status(:ok) end @@ -272,7 +272,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do .to receive(:new).with(provider_repo, provider_repo[:name], user.namespace, user, type: provider, **access_params) .and_return(double(execute: project)) - post :create, format: :json + post :create, params: { target_namespace: user.namespace_path }, format: :json expect(response).to have_gitlab_http_status(:unprocessable_entity) expect(json_response['errors']).to eq('Name is invalid, Path is old') @@ -286,7 +286,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do expect(store).to receive(:touch) { "realtime_changes_import_#{provider}_path" } end - post :create, format: :json + post :create, params: { target_namespace: user.namespace_path }, format: :json end context "when the repository owner is the provider user" do @@ -296,7 +296,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do .to receive(:new).with(provider_repo, provider_repo[:name], user.namespace, user, type: provider, **access_params) .and_return(double(execute: project)) - post :create, format: :json + post :create, params: { target_namespace: user.namespace_path }, format: :json end end @@ -308,7 +308,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do .to receive(:new).with(provider_repo, provider_repo[:name], user.namespace, user, type: provider, **access_params) .and_return(double(execute: project)) - post :create, format: :json + post :create, params: { target_namespace: user.namespace_path }, format: :json end end end @@ -333,7 +333,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do .to receive(:new).with(provider_repo, provider_repo[:name], existing_namespace, user, type: provider, **access_params) .and_return(double(execute: project)) - post :create, format: :json + post :create, params: { target_namespace: user.namespace_path }, format: :json end end @@ -345,47 +345,17 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do .to receive(:new).with(provider_repo, provider_repo[:name], user.namespace, user, type: provider, **access_params) .and_return(double(execute: project)) - post :create, format: :json + post :create, params: { target_namespace: user.namespace_path }, format: :json end end end context "when a namespace with the provider user's username doesn't exist" do context "when current user can create namespaces" do - it "creates the namespace" do - expect(Gitlab::LegacyGithubImport::ProjectCreator) - .to receive(:new).and_return(double(execute: project)) - - expect { post :create, params: { target_namespace: provider_repo[:name] }, format: :json }.to change { Namespace.count }.by(1) - end - - it "takes the new namespace" do - expect(Gitlab::LegacyGithubImport::ProjectCreator) - .to receive(:new).with(provider_repo, provider_repo[:name], an_instance_of(Group), user, type: provider, **access_params) - .and_return(double(execute: project)) - - post :create, params: { target_namespace: provider_repo[:name] }, format: :json - end - end - - context "when current user can't create namespaces" do - before do - user.update_attribute(:can_create_group, false) - end - - it "doesn't create the namespace" do - expect(Gitlab::LegacyGithubImport::ProjectCreator) - .to receive(:new).and_return(double(execute: project)) - - expect { post :create, format: :json }.not_to change { Namespace.count } - end + it "does not create the namespace" do + expect(Gitlab::LegacyGithubImport::ProjectCreator).not_to receive(:new) - it "takes the current user's namespace" do - expect(Gitlab::LegacyGithubImport::ProjectCreator) - .to receive(:new).with(provider_repo, provider_repo[:name], user.namespace, user, type: provider, **access_params) - .and_return(double(execute: project)) - - post :create, format: :json + expect { post :create, params: { target_namespace: provider_repo[:name] }, format: :json }.not_to change { Namespace.count } end end end @@ -405,14 +375,6 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do post :create, params: { target_namespace: test_namespace.name, new_name: test_name }, format: :json end - - it 'takes the selected name and default namespace' do - expect(Gitlab::LegacyGithubImport::ProjectCreator) - .to receive(:new).with(provider_repo, test_name, user.namespace, user, type: provider, **access_params) - .and_return(double(execute: project)) - - post :create, params: { new_name: test_name }, format: :json - end end context 'user has chosen an existing nested namespace and name for the project' do @@ -437,31 +399,16 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do context 'user has chosen a non-existent nested namespaces and name for the project' do let(:test_name) { 'test_name' } - it 'takes the selected namespace and name' do + it 'does not take the selected namespace and name' do expect(Gitlab::LegacyGithubImport::ProjectCreator) - .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, type: provider, **access_params) - .and_return(double(execute: project)) + .not_to receive(:new) post :create, params: { target_namespace: 'foo/bar', new_name: test_name }, format: :json end - it 'creates the namespaces' do - allow(Gitlab::LegacyGithubImport::ProjectCreator) - .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, type: provider, **access_params) - .and_return(double(execute: project)) - + it 'does not create namespaces' do expect { post :create, params: { target_namespace: 'foo/bar', new_name: test_name }, format: :json } - .to change { Namespace.count }.by(2) - end - - it 'new namespace has the right parent' do - allow(Gitlab::LegacyGithubImport::ProjectCreator) - .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, type: provider, **access_params) - .and_return(double(execute: project)) - - post :create, params: { target_namespace: 'foo/bar', new_name: test_name }, format: :json - - expect(Namespace.find_by_path_or_name('bar').parent.path).to eq('foo') + .not_to change { Namespace.count } end end @@ -473,55 +420,25 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do parent_namespace.add_owner(user) end - it 'takes the selected namespace and name' do - expect(Gitlab::LegacyGithubImport::ProjectCreator) - .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, type: provider, **access_params) - .and_return(double(execute: project)) + it 'does not take the selected namespace and name' do + expect(Gitlab::LegacyGithubImport::ProjectCreator).not_to receive(:new) post :create, params: { target_namespace: 'foo/foobar/bar', new_name: test_name }, format: :json end - it 'creates the namespaces' do - allow(Gitlab::LegacyGithubImport::ProjectCreator) - .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, type: provider, **access_params) - .and_return(double(execute: project)) - + it 'does not create the namespaces' do expect { post :create, params: { target_namespace: 'foo/foobar/bar', new_name: test_name }, format: :json } - .to change { Namespace.count }.by(2) + .not_to change { Namespace.count } end it 'does not create a new namespace under the user namespace' do - expect(Gitlab::LegacyGithubImport::ProjectCreator) - .to receive(:new).with(provider_repo, test_name, user.namespace, user, type: provider, **access_params) - .and_return(double(execute: project)) + expect(Gitlab::LegacyGithubImport::ProjectCreator).not_to receive(:new) expect { post :create, params: { target_namespace: "#{user.namespace_path}/test_group", new_name: test_name }, format: :js } .not_to change { Namespace.count } end end - context 'user cannot create a subgroup inside a group is not a member of' do - let(:test_name) { 'test_name' } - let!(:parent_namespace) { create(:group, name: 'foo') } - - it 'does not take the selected namespace and name' do - expect(Gitlab::LegacyGithubImport::ProjectCreator) - .to receive(:new).with(provider_repo, test_name, user.namespace, user, type: provider, **access_params) - .and_return(double(execute: project)) - - post :create, params: { target_namespace: 'foo/foobar/bar', new_name: test_name }, format: :js - end - - it 'does not create the namespaces' do - allow(Gitlab::LegacyGithubImport::ProjectCreator) - .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, type: provider, **access_params) - .and_return(double(execute: project)) - - expect { post :create, params: { target_namespace: 'foo/foobar/bar', new_name: test_name }, format: :js } - .not_to change { Namespace.count } - end - end - context 'user can use a group without having permissions to create a group' do let(:test_name) { 'test_name' } let!(:group) { create(:group, name: 'foo') } diff --git a/spec/support/shared_examples/controllers/issuables_list_metadata_shared_examples.rb b/spec/support/shared_examples/controllers/issuables_list_metadata_shared_examples.rb index 02915206cc5..446bc4cd92f 100644 --- a/spec/support/shared_examples/controllers/issuables_list_metadata_shared_examples.rb +++ b/spec/support/shared_examples/controllers/issuables_list_metadata_shared_examples.rb @@ -42,6 +42,10 @@ RSpec.shared_examples 'issuables list meta-data' do |issuable_type, action = nil let(:result_issuable) { issuables.first } let(:search) { result_issuable.title } + before do + stub_application_setting(search_rate_limit: 0, search_rate_limit_unauthenticated: 0) + end + # .simple_sorts is the same across all Sortable classes sorts = ::Issue.simple_sorts.keys + %w[popularity priority label_priority] sorts.each do |sort| diff --git a/spec/support/shared_examples/controllers/rate_limited_endpoint_shared_examples.rb b/spec/support/shared_examples/controllers/rate_limited_endpoint_shared_examples.rb index 20edca1ee9f..b34038ca0e4 100644 --- a/spec/support/shared_examples/controllers/rate_limited_endpoint_shared_examples.rb +++ b/spec/support/shared_examples/controllers/rate_limited_endpoint_shared_examples.rb @@ -5,7 +5,9 @@ # - current_user # - error_message # optional -RSpec.shared_examples 'rate limited endpoint' do |rate_limit_key:| +RSpec.shared_examples 'rate limited endpoint' do |rate_limit_key:, graphql: false| + let(:error_message) { _('This endpoint has been requested too many times. Try again later.') } + context 'when rate limiter enabled', :freeze_time, :clean_gitlab_redis_rate_limiting do let(:expected_logger_attributes) do { @@ -25,8 +27,6 @@ RSpec.shared_examples 'rate limited endpoint' do |rate_limit_key:| end end - let(:error_message) { _('This endpoint has been requested too many times. Try again later.') } - before do allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(rate_limit_key).and_return(1) end @@ -37,12 +37,16 @@ RSpec.shared_examples 'rate limited endpoint' do |rate_limit_key:| request request - expect(response).to have_gitlab_http_status(:too_many_requests) + if graphql + expect_graphql_errors_to_include(error_message) + else + expect(response).to have_gitlab_http_status(:too_many_requests) - if example.metadata[:type] == :controller - expect(response.body).to eq(error_message) - else # it is API spec - expect(response.body).to eq({ message: { error: error_message } }.to_json) + if response.content_type == 'application/json' # it is API spec + expect(response.body).to eq({ message: { error: error_message } }.to_json) + else + expect(response.body).to eq(error_message) + end end end end @@ -57,7 +61,11 @@ RSpec.shared_examples 'rate limited endpoint' do |rate_limit_key:| request - expect(response).not_to have_gitlab_http_status(:too_many_requests) + if graphql + expect_graphql_errors_to_be_empty + else + expect(response).not_to have_gitlab_http_status(:too_many_requests) + end end end end diff --git a/spec/support/shared_examples/features/code_highlight_shared_examples.rb b/spec/support/shared_examples/features/code_highlight_shared_examples.rb new file mode 100644 index 00000000000..3917ac9b489 --- /dev/null +++ b/spec/support/shared_examples/features/code_highlight_shared_examples.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'code highlight' do + include PreferencesHelper + + let_it_be(:current_user) { user } + let_it_be(:scheme_class) { user_color_scheme } + + it 'has highlighted code', :js do + wait_for_requests + expect(subject).to have_selector(".js-syntax-highlight.#{scheme_class}") + end +end diff --git a/spec/support/shared_examples/features/content_editor_shared_examples.rb b/spec/support/shared_examples/features/content_editor_shared_examples.rb index efdf7513b2d..6cd9c4ce1c4 100644 --- a/spec/support/shared_examples/features/content_editor_shared_examples.rb +++ b/spec/support/shared_examples/features/content_editor_shared_examples.rb @@ -4,7 +4,8 @@ RSpec.shared_examples 'edits content using the content editor' do let(:content_editor_testid) { '[data-testid="content-editor"] [contenteditable].ProseMirror' } def switch_to_content_editor - find('[data-testid="toggle-editing-mode-button"] label', text: 'Rich text').click + click_button _('View rich text') + click_button _('Rich text') end def type_in_content_editor(keys) diff --git a/spec/support/shared_examples/features/dashboard/sidebar_shared_examples.rb b/spec/support/shared_examples/features/dashboard/sidebar_shared_examples.rb new file mode 100644 index 00000000000..efbd735c451 --- /dev/null +++ b/spec/support/shared_examples/features/dashboard/sidebar_shared_examples.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +RSpec.shared_examples "a dashboard page with sidebar" do |page_path, menu_label| + before do + sign_in(user) + visit send(page_path) + end + + let(:sidebar_css) { "aside.nav-sidebar[aria-label=\"Your work\"]" } + let(:active_menu_item_css) { "li.active[data-track-label=\"#{menu_label}_menu\"]" } + + it "shows the \"Your work\" sidebar" do + expect(page).to have_css(sidebar_css) + end + + it "shows the correct sidebar menu item as active" do + within(sidebar_css) do + expect(page).to have_css(active_menu_item_css) + end + end +end diff --git a/spec/support/shared_examples/features/reportable_note_shared_examples.rb b/spec/support/shared_examples/features/reportable_note_shared_examples.rb index c35f711111b..9d859403465 100644 --- a/spec/support/shared_examples/features/reportable_note_shared_examples.rb +++ b/spec/support/shared_examples/features/reportable_note_shared_examples.rb @@ -36,7 +36,7 @@ RSpec.shared_examples 'reportable note' do |type| dropdown.click_link('Report abuse to administrator') expect(find('#user_name')['value']).to match(note.author.username) - expect(find('#abuse_report_message')['value']).to match(noteable_note_url(note)) + expect(find('#abuse_report_reported_from_url')['value']).to match(noteable_note_url(note)) end def open_dropdown(dropdown) diff --git a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb index 9d1f05d5543..6f4072ba762 100644 --- a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb +++ b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb @@ -550,6 +550,24 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context expect(items).to contain_exactly(item1, item4, item5) end end + + context 'using OR' do + let(:params) { { or: { label_name: [label.title, label2.title].join(',') } } } + + it 'returns items that have at least one of the given labels' do + expect(items).to contain_exactly(item2, item3) + end + + context 'when feature flag is disabled' do + before do + stub_feature_flags(or_issuable_queries: false) + end + + it 'does not add any filter' do + expect(items).to contain_exactly(item1, item2, item3, item4, item5) + end + end + end end context 'filtering by a label that includes any or none in the title' do diff --git a/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb b/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb index 0d0dbb112de..19ceb465383 100644 --- a/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb +++ b/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb @@ -14,7 +14,10 @@ RSpec.shared_examples "a user type with merge request interaction type" do name username email + emails publicEmail + commitEmail + namespaceCommitEmails avatarUrl webUrl webPath diff --git a/spec/support/shared_examples/lib/sidebars/your_work/menus/menu_item_examples.rb b/spec/support/shared_examples/lib/sidebars/your_work/menus/menu_item_examples.rb new file mode 100644 index 00000000000..19c94a3ba5b --- /dev/null +++ b/spec/support/shared_examples/lib/sidebars/your_work/menus/menu_item_examples.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.shared_examples 'menu item shows pill based on count' do |count| + describe '#has_pill?' do + context 'when count is zero' do + it 'returns false' do + allow(user).to receive(count).and_return(0) + expect(subject.has_pill?).to eq false + end + end + + context 'when count is larger than zero' do + it 'returns true' do + allow(user).to receive(count).and_return(3) + expect(subject.has_pill?).to eq true + end + end + end + + describe '#pill_count' do + it "returns the #{count} of the user" do + allow(user).to receive(count).and_return(123) + expect(subject.pill_count).to eq 123 + end + + it 'memoizes the query' do + subject.pill_count + + control = ActiveRecord::QueryRecorder.new do + subject.pill_count + end + + expect(control.count).to eq 0 + end + end +end diff --git a/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb b/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb index a20bb794095..f98be12523d 100644 --- a/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb +++ b/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb @@ -14,13 +14,14 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes| counter_attributes.each do |attribute| describe attribute do describe '#increment_counter', :redis do - let(:increment) { 10 } + let(:amount) { 10 } + let(:increment) { Gitlab::Counters::Increment.new(amount: amount) } let(:counter_key) { model.counter(attribute).key } subject { model.increment_counter(attribute, increment) } context 'when attribute is a counter attribute' do - where(:increment) { [10, -3] } + where(:amount) { [10, -3] } with_them do it 'increments the counter in Redis and logs it' do @@ -29,8 +30,8 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes| message: 'Increment counter attribute', attribute: attribute, project_id: model.project_id, - increment: increment, - new_counter_value: 0 + increment, + increment: amount, + new_counter_value: 0 + amount, current_db_value: model.read_attribute(attribute), 'correlation_id' => an_instance_of(String), 'meta.feature_category' => 'test', @@ -42,7 +43,7 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes| Gitlab::Redis::SharedState.with do |redis| counter = redis.get(counter_key) - expect(counter).to eq(increment.to_s) + expect(counter).to eq(amount.to_s) end end @@ -59,8 +60,8 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes| end end - context 'when increment is 0' do - let(:increment) { 0 } + context 'when increment amount is 0' do + let(:amount) { 0 } it 'does nothing' do expect(FlushCounterIncrementsWorker).not_to receive(:perform_in) @@ -71,37 +72,49 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes| end end end - end - end - describe '#reset_counter!' do - let(:attribute) { counter_attributes.first } - let(:counter_key) { model.counter(attribute).key } + describe '#bulk_increment_counter', :redis do + let(:increments) { [Gitlab::Counters::Increment.new(amount: 10), Gitlab::Counters::Increment.new(amount: 5)] } + let(:total_amount) { increments.sum(&:amount) } + let(:counter_key) { model.counter(attribute).key } - before do - model.update!(attribute => 123) - model.increment_counter(attribute, 10) - end + subject { model.bulk_increment_counter(attribute, increments) } - subject { model.reset_counter!(attribute) } + context 'when attribute is a counter attribute' do + it 'increments the counter in Redis and logs it' do + expect(Gitlab::AppLogger).to receive(:info).with( + hash_including( + message: 'Increment counter attribute', + attribute: attribute, + project_id: model.project_id, + increment: total_amount, + new_counter_value: 0 + total_amount, + current_db_value: model.read_attribute(attribute), + 'correlation_id' => an_instance_of(String), + 'meta.feature_category' => 'test', + 'meta.caller_id' => 'caller' + ) + ) - it 'resets the attribute value to 0 and clears existing counter', :aggregate_failures do - expect { subject }.to change { model.reload.send(attribute) }.from(123).to(0) + subject - Gitlab::Redis::SharedState.with do |redis| - key_exists = redis.exists?(counter_key) - expect(key_exists).to be_falsey - end - end + Gitlab::Redis::SharedState.with do |redis| + counter = redis.get(counter_key) + expect(counter).to eq(total_amount.to_s) + end + end - it_behaves_like 'obtaining lease to update database' do - context 'when the execution raises error' do - before do - allow(model).to receive(:update!).and_raise(StandardError, 'Something went wrong') - end + it 'does not increment the counter for the record' do + expect { subject }.not_to change { model.reset.read_attribute(attribute) } + end - it 'reraises error' do - expect { subject }.to raise_error(StandardError, 'Something went wrong') + it 'schedules a worker to flush counter increments asynchronously' do + expect(FlushCounterIncrementsWorker).to receive(:perform_in) + .with(Gitlab::Counters::BufferedCounter::WORKER_DELAY, model.class.name, model.id, attribute) + .and_call_original + + subject + end end end end diff --git a/spec/support/shared_examples/models/concerns/integrations/reset_secret_fields_shared_examples.rb b/spec/support/shared_examples/models/concerns/integrations/reset_secret_fields_shared_examples.rb index 873f858e432..c51a6c4f6fd 100644 --- a/spec/support/shared_examples/models/concerns/integrations/reset_secret_fields_shared_examples.rb +++ b/spec/support/shared_examples/models/concerns/integrations/reset_secret_fields_shared_examples.rb @@ -42,7 +42,7 @@ RSpec.shared_examples Integrations::ResetSecretFields do # Treat values as persisted integration.reset_updated_properties - integration.instance_variable_set('@old_data_fields', nil) if integration.supports_data_fields? + integration.instance_variable_set(:@old_data_fields, nil) if integration.supports_data_fields? end context 'when an exposing field has changed' do diff --git a/spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb b/spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb index 457ee49938f..5eeefacdeb9 100644 --- a/spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb +++ b/spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb @@ -25,7 +25,7 @@ RSpec.shared_examples 'value stream analytics stage' do stage = described_class.new(valid_params.except(:parent)) expect(stage).to be_invalid - expect(stage.errors[parent_name]).to include("can't be blank") + expect(stage.errors[parent_name]).to include('must exist') end it 'validates presence of start_event_identifier' do diff --git a/spec/support/shared_examples/models/member_shared_examples.rb b/spec/support/shared_examples/models/member_shared_examples.rb index f8cff5c5558..7159c55e303 100644 --- a/spec/support/shared_examples/models/member_shared_examples.rb +++ b/spec/support/shared_examples/models/member_shared_examples.rb @@ -207,16 +207,14 @@ RSpec.shared_examples_for "member creation" do source.request_access(user) end - it 'does not add the requester as a regular member', :aggregate_failures do + it 'adds the requester as a member', :aggregate_failures do expect(source.users).not_to include(user) - expect(source.requesters.exists?(user_id: user)).to be_truthy + expect(source.requesters.exists?(user_id: user)).to eq(true) - expect do - described_class.add_member(source, user, :maintainer) - end.to raise_error(Gitlab::Access::AccessDeniedError) + described_class.add_member(source, user, :maintainer) - expect(source.users.reload).not_to include(user) - expect(source.requesters.reload.exists?(user_id: user)).to be_truthy + expect(source.users.reload).to include(user) + expect(source.requesters.reload.exists?(user_id: user)).to eq(false) end end diff --git a/spec/support/shared_examples/models/members_notifications_shared_example.rb b/spec/support/shared_examples/models/members_notifications_shared_example.rb index e74aab95e46..e28220334ac 100644 --- a/spec/support/shared_examples/models/members_notifications_shared_example.rb +++ b/spec/support/shared_examples/models/members_notifications_shared_example.rb @@ -51,7 +51,7 @@ RSpec.shared_examples 'members notifications' do |entity_type| it "calls NotificationService.new_#{entity_type}_member" do expect(notification_service).to receive(:"new_#{entity_type}_member").with(member) - member.accept_request + member.accept_request(create(:user)) end end diff --git a/spec/support/shared_examples/models/relative_positioning_shared_examples.rb b/spec/support/shared_examples/models/relative_positioning_shared_examples.rb index b8d12a6da59..2b46c8c8fb9 100644 --- a/spec/support/shared_examples/models/relative_positioning_shared_examples.rb +++ b/spec/support/shared_examples/models/relative_positioning_shared_examples.rb @@ -20,6 +20,7 @@ RSpec.shared_examples 'a class that supports relative positioning' do let(:new_item) { create_item(relative_position: nil) } let(:set_size) { RelativePositioning.mover.context(item1).scoped_items.count } + let(:items_with_nil_position_sample_quantity) { 101 } def create_item(params = {}) create(factory, params.merge(default_params)) @@ -163,7 +164,7 @@ RSpec.shared_examples 'a class that supports relative positioning' do end it 'can move many nulls' do - nils = create_items_with_positions([nil] * 101) + nils = create_items_with_positions([nil] * items_with_nil_position_sample_quantity) described_class.move_nulls_to_end(nils) diff --git a/spec/support/shared_examples/models/resource_event_shared_examples.rb b/spec/support/shared_examples/models/resource_event_shared_examples.rb index 80806ee768a..8cab2de076d 100644 --- a/spec/support/shared_examples/models/resource_event_shared_examples.rb +++ b/spec/support/shared_examples/models/resource_event_shared_examples.rb @@ -161,3 +161,15 @@ RSpec.shared_examples 'a resource event for merge requests' do end end end + +RSpec.shared_examples 'a note for work item resource event' do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be(:work_item) { create(:work_item, :task, project: project, author: user) } + + it 'builds synthetic note with correct synthetic_note_class' do + event = build(described_class.name.underscore.to_sym, issue: work_item) + + expect(event.work_item_synthetic_system_note.class.name).to eq(event.synthetic_note_class.name) + end +end diff --git a/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb b/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb index eb742921d35..5aaa93aecef 100644 --- a/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb +++ b/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb @@ -60,8 +60,11 @@ RSpec.shared_examples 'UpdateProjectStatistics' do |with_counter_attribute| end it 'stores pending increments for async update' do + expected_increment = have_attributes(amount: delta, ref: subject.id) + expect(ProjectStatistics) .to receive(:increment_statistic) + .with(project, project_statistics_name, expected_increment) .and_call_original subject.write_attribute(statistic_attribute, read_attribute + delta) @@ -108,11 +111,8 @@ RSpec.shared_examples 'UpdateProjectStatistics' do |with_counter_attribute| end context 'when it is destroyed from the project level' do - it 'does not update the project statistics' do - expect(ProjectStatistics) - .not_to receive(:increment_statistic) - - expect(Projects::DestroyService.new(project, project.first_owner).execute).to eq(true) + it 'does not store pending increments for async update' do + expect { Projects::DestroyService.new(project, project.first_owner).execute }.not_to change { read_pending_increment } end it 'does not schedule a namespace statistics worker' do diff --git a/spec/support/shared_examples/namespaces/members.rb b/spec/support/shared_examples/namespaces/members.rb new file mode 100644 index 00000000000..ed1ea23226c --- /dev/null +++ b/spec/support/shared_examples/namespaces/members.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'query without source filters' do + it do + expect(subject.where_values_hash.keys).not_to include('source_id', 'source_type') + end +end + +RSpec.shared_examples 'query with source filters' do + it do + expect(subject.where_values_hash.keys).to include('source_id', 'source_type') + end +end diff --git a/spec/support/shared_examples/observability/csp_shared_examples.rb b/spec/support/shared_examples/observability/csp_shared_examples.rb index 868d7023d14..0cd211f69eb 100644 --- a/spec/support/shared_examples/observability/csp_shared_examples.rb +++ b/spec/support/shared_examples/observability/csp_shared_examples.rb @@ -2,9 +2,17 @@ # Verifies that the proper CSP rules for Observabilty UI are applied to a given controller/path # -# The path under test needs to be declared with `let(:tested_path) { .. }` in the context including this example +# It requires the following variables declared in the context including this example: +# +# - `tested_path`: the path under test +# - `user`: the test user +# - `group`: the test group +# +# e.g. # # ``` +# let_it_be(:group) { create(:group) } +# let_it_be(:user) { create(:user) } # it_behaves_like "observability csp policy" do # let(:tested_path) { ....the path under test } # end @@ -33,6 +41,9 @@ RSpec.shared_examples 'observability csp policy' do |controller_class = describe before do setup_csp_for_controller(controller_class, csp, any_time: true) + group.add_developer(user) + login_as(user) + allow(Gitlab::Observability).to receive(:observability_enabled?).and_return(true) end subject do @@ -48,6 +59,40 @@ RSpec.shared_examples 'observability csp policy' do |controller_class = describe end end + context 'when observability is disabled' do + let(:csp) do + ActionDispatch::ContentSecurityPolicy.new do |p| + p.frame_src 'https://something.test' + end + end + + before do + allow(Gitlab::Observability).to receive(:observability_enabled?).and_return(false) + end + + it 'does not add observability urls to the csp header' do + expect(subject).to include("frame-src https://something.test") + expect(subject).not_to include("#{observability_url} #{signin_url} #{oauth_url}") + end + end + + context 'when checking if observability is enabled' do + let(:csp) do + ActionDispatch::ContentSecurityPolicy.new do |p| + p.frame_src 'https://something.test' + end + end + + it 'check access for a given user and group' do + allow(Gitlab::Observability).to receive(:observability_enabled?) + + get tested_path + + expect(Gitlab::Observability).to have_received(:observability_enabled?) + .with(user, group).at_least(:once) + end + end + context 'when frame-src exists in the CSP config' do let(:csp) do ActionDispatch::ContentSecurityPolicy.new do |p| diff --git a/spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb index ca6536444fd..d8690356f81 100644 --- a/spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb +++ b/spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb @@ -12,10 +12,10 @@ RSpec.shared_examples 'close quick action' do |issuable_type| before do case issuable_type when :merge_request - visit public_send('namespace_project_new_merge_request_path', project.namespace, project, new_url_opts) + visit public_send(:namespace_project_new_merge_request_path, project.namespace, project, new_url_opts) wait_for_all_requests when :issue - visit public_send('new_namespace_project_issue_path', project.namespace, project, new_url_opts) + visit public_send(:new_namespace_project_issue_path, project.namespace, project, new_url_opts) wait_for_all_requests end end diff --git a/spec/support/shared_examples/quick_actions/issue/promote_to_incident_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issue/promote_to_incident_quick_action_shared_examples.rb index 5167d27f8b9..3f1a98ca08e 100644 --- a/spec/support/shared_examples/quick_actions/issue/promote_to_incident_quick_action_shared_examples.rb +++ b/spec/support/shared_examples/quick_actions/issue/promote_to_incident_quick_action_shared_examples.rb @@ -36,5 +36,33 @@ RSpec.shared_examples 'promote_to_incident quick action' do expect(page).to have_content('Could not apply promote_to_incident command') end end + + context 'on issue creation' do + it 'promotes issue to incident' do + visit new_project_issue_path(project) + fill_in('Title', with: 'Title') + fill_in('Description', with: '/promote_to_incident') + click_button('Create issue') + + wait_for_all_requests + + expect(page).to have_content("Incident created just now by #{user.name}") + end + + context 'when incident is selected for issue type' do + it 'promotes issue to incident' do + visit new_project_issue_path(project) + fill_in('Title', with: 'Title') + find('.js-issuable-type-filter-dropdown-wrap').click + click_link('Incident') + fill_in('Description', with: '/promote_to_incident') + click_button('Create issue') + + wait_for_all_requests + + expect(page).to have_content("Incident created just now by #{user.name}") + end + end + end end end diff --git a/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb index d4479e462af..d4af9e570d1 100644 --- a/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb @@ -17,6 +17,11 @@ RSpec.shared_examples 'graphql issue list request spec' do end describe 'filters' do + before_all do + issue_a.assignee_ids = current_user.id + issue_b.assignee_ids = another_user.id + end + context 'when filtering by assignees' do context 'when both assignee_username filters are provided' do let(:issue_filter_params) do @@ -44,12 +49,30 @@ RSpec.shared_examples 'graphql issue list request spec' do end context 'when filtering by unioned arguments' do - let(:issue_filter_params) { { or: { assignee_usernames: [current_user.username, another_user.username] } } } + context 'when filtering by assignees' do + let(:issue_filter_params) { { or: { assignee_usernames: [current_user.username, another_user.username] } } } - it 'returns correctly filtered issues' do - post_query + it 'returns correctly filtered issues' do + post_query - expect(issue_ids).to match_array(expected_unioned_assignee_issues.map { |i| i.to_gid.to_s }) + expect(issue_ids).to match_array([issue_a, issue_b].map { |i| i.to_gid.to_s }) + end + end + + context 'when filtering by labels' do + let_it_be(:label_a) { create(:label, project: issue_a.project) } + let_it_be(:label_b) { create(:label, project: issue_b.project) } + + let(:issue_filter_params) { { or: { label_names: [label_a.title, label_b.title] } } } + + it 'returns correctly filtered issues' do + issue_a.label_ids = label_a.id + issue_b.label_ids = label_b.id + + post_graphql(query, current_user: current_user) + + expect(issue_ids).to match_array([issue_a, issue_b].map { |i| i.to_gid.to_s }) + end end context 'when argument is blank' do @@ -63,6 +86,8 @@ RSpec.shared_examples 'graphql issue list request spec' do end context 'when feature flag is disabled' do + let(:issue_filter_params) { { or: { assignee_usernames: [current_user.username] } } } + it 'returns an error' do stub_feature_flags(or_issuable_queries: false) diff --git a/spec/support/shared_examples/requests/api/issuable_search_shared_examples.rb b/spec/support/shared_examples/requests/api/issuable_search_shared_examples.rb index 9f67bd69560..fcde3b65b4f 100644 --- a/spec/support/shared_examples/requests/api/issuable_search_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/issuable_search_shared_examples.rb @@ -34,3 +34,35 @@ RSpec.shared_examples 'issuable anonymous search' do end end end + +RSpec.shared_examples 'issuable API rate-limited search' do + it_behaves_like 'rate limited endpoint', rate_limit_key: :search_rate_limit do + let(:current_user) { user } + + def request + get api(url, current_user), params: { scope: 'all', search: issuable.title } + end + end + + it_behaves_like 'rate limited endpoint', rate_limit_key: :search_rate_limit_unauthenticated do + def request + get api(url), params: { scope: 'all', search: issuable.title } + end + end + + context 'when rate_limit_issuable_searches is disabled', :freeze_time, :clean_gitlab_redis_rate_limiting do + before do + stub_feature_flags(rate_limit_issuable_searches: false) + + allow(Gitlab::ApplicationRateLimiter).to receive(:threshold) + .with(:search_rate_limit_unauthenticated).and_return(1) + end + + it 'does not enforce the rate limit' do + get api(url), params: { scope: 'all', search: issuable.title } + get api(url), params: { scope: 'all', search: issuable.title } + + expect(response).to have_gitlab_http_status(:ok) + end + end +end diff --git a/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb b/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb index 290bf58fb6b..17d8b9c7fab 100644 --- a/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb @@ -1,6 +1,11 @@ # frozen_string_literal: true -RSpec.shared_examples 'handling nuget service requests' do |anonymous_requests_example_name: 'process nuget service index request', anonymous_requests_status: :success| +RSpec.shared_examples 'handling nuget service requests' do |example_names_with_status: {}| + anonymous_requests_example_name = example_names_with_status.fetch(:anonymous_requests_example_name, 'process nuget service index request') + anonymous_requests_status = example_names_with_status.fetch(:anonymous_requests_status, :success) + guest_requests_example_name = example_names_with_status.fetch(:guest_requests_example_name, 'rejects nuget packages access') + guest_requests_status = example_names_with_status.fetch(:guest_requests_status, :forbidden) + subject { get api(url) } context 'with valid target' do @@ -18,7 +23,7 @@ RSpec.shared_examples 'handling nuget service requests' do |anonymous_requests_e 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized 'PUBLIC' | :anonymous | false | true | anonymous_requests_example_name | anonymous_requests_status 'PRIVATE' | :developer | true | true | 'process nuget service index request' | :success - 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden + 'PRIVATE' | :guest | true | true | guest_requests_example_name | guest_requests_status 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found @@ -54,7 +59,7 @@ RSpec.shared_examples 'handling nuget service requests' do |anonymous_requests_e 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized 'PUBLIC' | :anonymous | false | true | anonymous_requests_example_name | anonymous_requests_status 'PRIVATE' | :developer | true | true | 'process nuget service index request' | :success - 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden + 'PRIVATE' | :guest | true | true | guest_requests_example_name | guest_requests_status 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found @@ -90,9 +95,14 @@ RSpec.shared_examples 'handling nuget service requests' do |anonymous_requests_e it_behaves_like 'rejects nuget access with invalid target id' end -RSpec.shared_examples 'handling nuget metadata requests with package name' do |anonymous_requests_example_name: 'process nuget metadata request at package name level', anonymous_requests_status: :success| +RSpec.shared_examples 'handling nuget metadata requests with package name' do |example_names_with_status: {}| include_context 'with expected presenters dependency groups' + anonymous_requests_example_name = example_names_with_status.fetch(:anonymous_requests_example_name, 'process nuget metadata request at package name level') + anonymous_requests_status = example_names_with_status.fetch(:anonymous_requests_status, :success) + guest_requests_example_name = example_names_with_status.fetch(:guest_requests_example_name, 'rejects nuget packages access') + guest_requests_status = example_names_with_status.fetch(:guest_requests_status, :forbidden) + let_it_be(:package_name) { 'Dummy.Package' } let_it_be(:packages) { create_list(:nuget_package, 5, :with_metadatum, name: package_name, project: project) } let_it_be(:tags) { packages.each { |pkg| create(:packages_tag, package: pkg, name: 'test') } } @@ -117,7 +127,7 @@ RSpec.shared_examples 'handling nuget metadata requests with package name' do |a 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized 'PUBLIC' | :anonymous | false | true | anonymous_requests_example_name | anonymous_requests_status 'PRIVATE' | :developer | true | true | 'process nuget metadata request at package name level' | :success - 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden + 'PRIVATE' | :guest | true | true | guest_requests_example_name | guest_requests_status 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found @@ -152,9 +162,14 @@ RSpec.shared_examples 'handling nuget metadata requests with package name' do |a end end -RSpec.shared_examples 'handling nuget metadata requests with package name and package version' do |anonymous_requests_example_name: 'process nuget metadata request at package name and package version level', anonymous_requests_status: :success| +RSpec.shared_examples 'handling nuget metadata requests with package name and package version' do |example_names_with_status: {}| include_context 'with expected presenters dependency groups' + anonymous_requests_example_name = example_names_with_status.fetch(:anonymous_requests_example_name, 'process nuget metadata request at package name and package version level') + anonymous_requests_status = example_names_with_status.fetch(:anonymous_requests_status, :success) + guest_requests_example_name = example_names_with_status.fetch(:guest_requests_example_name, 'rejects nuget packages access') + guest_requests_status = example_names_with_status.fetch(:guest_requests_status, :forbidden) + let_it_be(:package_name) { 'Dummy.Package' } let_it_be(:package) { create(:nuget_package, :with_metadatum, name: package_name, project: project) } let_it_be(:tag) { create(:packages_tag, package: package, name: 'test') } @@ -179,7 +194,7 @@ RSpec.shared_examples 'handling nuget metadata requests with package name and pa 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized 'PUBLIC' | :anonymous | false | true | anonymous_requests_example_name | anonymous_requests_status 'PRIVATE' | :developer | true | true | 'process nuget metadata request at package name and package version level' | :success - 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden + 'PRIVATE' | :guest | true | true | guest_requests_example_name | guest_requests_status 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found @@ -214,7 +229,12 @@ RSpec.shared_examples 'handling nuget metadata requests with package name and pa it_behaves_like 'rejects nuget access with invalid target id' end -RSpec.shared_examples 'handling nuget search requests' do |anonymous_requests_example_name: 'process nuget search request', anonymous_requests_status: :success| +RSpec.shared_examples 'handling nuget search requests' do |example_names_with_status: {}| + anonymous_requests_example_name = example_names_with_status.fetch(:anonymous_requests_example_name, 'process nuget search request') + anonymous_requests_status = example_names_with_status.fetch(:anonymous_requests_status, :success) + guest_requests_example_name = example_names_with_status.fetch(:guest_requests_example_name, 'rejects nuget packages access') + guest_requests_status = example_names_with_status.fetch(:guest_requests_status, :forbidden) + let_it_be(:package_a) { create(:nuget_package, :with_metadatum, name: 'Dummy.PackageA', project: project) } let_it_be(:tag) { create(:packages_tag, package: package_a, name: 'test') } let_it_be(:packages_b) { create_list(:nuget_package, 5, name: 'Dummy.PackageB', project: project) } @@ -244,7 +264,7 @@ RSpec.shared_examples 'handling nuget search requests' do |anonymous_requests_ex 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized 'PUBLIC' | :anonymous | false | true | anonymous_requests_example_name | anonymous_requests_status 'PRIVATE' | :developer | true | true | 'process nuget search request' | :success - 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden + 'PRIVATE' | :guest | true | true | guest_requests_example_name | guest_requests_status 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found diff --git a/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb index bace570e47a..3abe545db59 100644 --- a/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb @@ -379,6 +379,26 @@ RSpec.shared_examples 'process nuget search request' do |user_type, status, add_ end end +RSpec.shared_examples 'process empty nuget search request' do |user_type, status, add_member = true| + before do + target.send("add_#{user_type}", user) if add_member && user_type != :anonymous + end + + it_behaves_like 'returning response status', status + + it 'returns a valid json response' do + subject + + expect(response.media_type).to eq('application/json') + expect(json_response).to be_a(Hash) + expect(json_response).to match_schema('public_api/v4/packages/nuget/search') + expect(json_response['totalHits']).to eq(0) + expect(json_response['data'].map { |e| e['versions'].size }).to be_empty + end + + it_behaves_like 'a package tracking event', 'API::NugetPackages', 'search_package' +end + RSpec.shared_examples 'rejects nuget access with invalid target id' do context 'with a target id with invalid integers' do using RSpec::Parameterized::TableSyntax diff --git a/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb b/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb index 12f2b5d78a5..e47ff2fcd59 100644 --- a/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb +++ b/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb @@ -30,7 +30,7 @@ RSpec.shared_examples 'issuable link creation' do context 'when user has no permission to target issuable' do let(:params) do - { issuable_references: [guest_issuable.to_reference(issuable_parent)] } + { issuable_references: [restricted_issuable.to_reference(issuable_parent)] } end it 'returns error' do diff --git a/spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb b/spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb index cc170c6544d..1532e870dcc 100644 --- a/spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb +++ b/spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb @@ -1,10 +1,11 @@ # frozen_string_literal: true -RSpec.shared_examples 'a destroyable issuable link' do +RSpec.shared_examples 'a destroyable issuable link' do |required_role: :reporter| context 'when successfully removes an issuable link' do before do - issuable_link.source.resource_parent.add_reporter(user) - issuable_link.target.resource_parent.add_reporter(user) + [issuable_link.target, issuable_link.source].each do |issuable| + issuable.resource_parent.try(:"add_#{required_role}", user) + end end it 'removes related issue' do diff --git a/spec/support/shared_examples/services/repositories/housekeeping_shared_examples.rb b/spec/support/shared_examples/services/repositories/housekeeping_shared_examples.rb index 8a937303711..8cc71230ba4 100644 --- a/spec/support/shared_examples/services/repositories/housekeeping_shared_examples.rb +++ b/spec/support/shared_examples/services/repositories/housekeeping_shared_examples.rb @@ -65,12 +65,9 @@ RSpec.shared_examples 'housekeeps repository' do # At push 200 expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :gc, :the_lease_key, :the_uuid) .once - # At push 50, 100, 150 - expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :full_repack, :the_lease_key, :the_uuid) - .exactly(3).times - # At push 10, 20, ... (except those above) + # At push 10, 20, ... (except the gc call) expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :incremental_repack, :the_lease_key, :the_uuid) - .exactly(16).times + .exactly(19).times 201.times do subject.increment! @@ -79,37 +76,6 @@ RSpec.shared_examples 'housekeeps repository' do expect(resource.pushes_since_gc).to eq(1) end - - context 'when optimized_repository feature flag is disabled' do - before do - stub_feature_flags(optimized_housekeeping: false) - end - - it 'calls also the garbage collect worker with pack_refs every 6 commits' do - allow(subject).to receive(:try_obtain_lease).and_return(:the_uuid) - allow(subject).to receive(:lease_key).and_return(:the_lease_key) - - # At push 200 - expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :gc, :the_lease_key, :the_uuid) - .once - # At push 50, 100, 150 - expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :full_repack, :the_lease_key, :the_uuid) - .exactly(3).times - # At push 10, 20, ... (except those above) - expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :incremental_repack, :the_lease_key, :the_uuid) - .exactly(16).times - # At push 6, 12, 18, ... (except those above) - expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :pack_refs, :the_lease_key, :the_uuid) - .exactly(27).times - - 201.times do - subject.increment! - subject.execute if subject.needed? - end - - expect(resource.pushes_since_gc).to eq(1) - end - end end it 'runs the task specifically requested' do @@ -136,15 +102,11 @@ RSpec.shared_examples 'housekeeps repository' do expect(subject.needed?).to eq(true) end - context 'when optimized_housekeeping is disabled' do - before do - stub_feature_flags(optimized_housekeeping: false) - end + it 'when incremental repack period is not multiple of gc period' do + allow(Gitlab::CurrentSettings).to receive(:housekeeping_incremental_repack_period).and_return(12) + allow(resource).to receive(:pushes_since_gc).and_return(200) - it 'returns true pack refs is needed' do - allow(resource).to receive(:pushes_since_gc).and_return(described_class::PACK_REFS_PERIOD) - expect(subject.needed?).to eq(true) - end + expect(subject.needed?).to eq(true) end end diff --git a/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb b/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb index 105c4247ff7..716be8c6210 100644 --- a/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb +++ b/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb @@ -88,6 +88,68 @@ RSpec.shared_examples_for 'services security ci configuration create service' do end end + context 'when existing ci config contains anchors/aliases' do + let(:params) { {} } + let(:unsupported_yaml) do + <<-YAML + image: python:latest + + cache: &global_cache + key: 'common-cache' + paths: + - .cache/pip + - venv/ + + test: + cache: + <<: *global_cache + key: 'custom-cache' + script: + - python setup.py test + - pip install tox flake8 # you can also use tox + - tox -e py36,flake8 + YAML + end + + it 'fails with error' do + expect(project).to receive(:ci_config_for).and_return(unsupported_yaml) + + expect { result }.to raise_error(Gitlab::Graphql::Errors::MutationError, '.gitlab-ci.yml with aliases/anchors is not supported. Please change the CI configuration manually.') + end + end + + context 'when parsing existing ci config gives a Psych error' do + let(:params) { {} } + let(:invalid_yaml) do + <<-YAML + image: python:latest + + test: + script: + - python setup.py test + - pip install tox flake8 # you can also use tox + - tox -e py36,flake8 + YAML + end + + it 'fails with error' do + expect(project).to receive(:ci_config_for).and_return(invalid_yaml) + expect(YAML).to receive(:safe_load).and_raise(Psych::Exception) + + expect { result }.to raise_error(Gitlab::Graphql::Errors::MutationError, /merge request creation mutation failed/) + end + end + + context 'when parsing existing ci config gives any other error' do + let(:params) { {} } + let_it_be(:repository) { project.repository } + + it 'is successful' do + expect(repository).to receive(:root_ref_sha).and_raise(StandardError) + expect(result.status).to eq(:success) + end + end + unless skip_w_params context 'with parameters' do let(:params) { non_empty_params } diff --git a/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb b/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb index 09ebc495e61..8ec955940c0 100644 --- a/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb +++ b/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb @@ -256,6 +256,7 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d Class.new(Gitlab::BackgroundMigration::BatchedMigrationJob) do job_arguments :matching_status operation_name :update_all + feature_category :code_review_workflow def perform each_sub_batch( @@ -325,16 +326,16 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d stub_const('Gitlab::BackgroundMigration::ExampleDataMigration', migration_class) end - shared_examples 'batched background migration execution' do - subject(:full_migration_run) do - # process all batches, then do an extra execution to mark the job as finished - (number_of_batches + 1).times do - described_class.new.perform + subject(:full_migration_run) do + # process all batches, then do an extra execution to mark the job as finished + (number_of_batches + 1).times do + described_class.new.perform - travel_to((migration.interval + described_class::INTERVAL_VARIANCE).seconds.from_now) - end + travel_to((migration.interval + described_class::INTERVAL_VARIANCE).seconds.from_now) end + end + shared_examples 'batched background migration execution' do it 'marks the migration record as finished' do expect { full_migration_run }.to change { migration.reload.status }.from(1).to(3) # active -> finished end @@ -404,6 +405,15 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d end it_behaves_like 'batched background migration execution' + + it 'assigns proper feature category to the context and the worker' do + expected_feature_category = migration_class.feature_category.to_s + + expect { full_migration_run }.to change { + Gitlab::ApplicationContext.current["meta.feature_category"] + }.to(expected_feature_category) + .and change { described_class.get_feature_category }.from(:database).to(expected_feature_category) + end end context 'when parallel execution is enabled', :sidekiq_inline do diff --git a/spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb b/spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb index 503e331ea2e..ba1bdfa7aa8 100644 --- a/spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb +++ b/spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb @@ -24,19 +24,6 @@ RSpec.shared_examples 'can collect git garbage' do |update_statistics: true| subject.perform(*params) end - - context 'when optimized_housekeeping feature is disabled' do - before do - stub_feature_flags(optimized_housekeeping: false) - end - - specify do - expect(subject).to receive(:get_gitaly_client).with(task, repository.raw_repository).and_return(repository_service) - expect(repository_service).to receive(gitaly_task) - - subject.perform(*params) - end - end end shared_examples 'it updates the resource statistics' do @@ -91,20 +78,6 @@ RSpec.shared_examples 'can collect git garbage' do |update_statistics: true| expect { subject.perform(*params) }.to raise_exception(Gitlab::Git::Repository::NoRepository) end - - context 'when optimized_housekeeping feature flag is disabled' do - before do - stub_feature_flags(optimized_housekeeping: false) - end - - it 'handles gRPC errors' do - allow_next_instance_of(Gitlab::GitalyClient::RepositoryService, repository.raw_repository) do |instance| - allow(instance).to receive(:garbage_collect).and_raise(GRPC::NotFound) - end - - expect { subject.perform(*params) }.to raise_exception(Gitlab::Git::Repository::NoRepository) - end - end end context 'with different lease than the active one' do @@ -161,51 +134,6 @@ RSpec.shared_examples 'can collect git garbage' do |update_statistics: true| end end - context 'repack_full' do - let(:task) { :full_repack } - let(:gitaly_task) { :repack_full } - - before do - expect(subject).to receive(:get_lease_uuid).and_return(lease_uuid) - end - - it_behaves_like 'it calls Gitaly' - it_behaves_like 'it updates the resource statistics' if update_statistics - end - - context 'pack_refs' do - let(:task) { :pack_refs } - let(:gitaly_task) { :pack_refs } - - before do - expect(subject).to receive(:get_lease_uuid).and_return(lease_uuid) - end - - it_behaves_like 'it calls Gitaly' do - let(:repository_service) { instance_double(Gitlab::GitalyClient::RefService) } - end - - it 'does not update the resource statistics' do - expect(statistics_service_klass).not_to receive(:new) - - subject.perform(*params) - end - end - - context 'repack_incremental' do - let(:task) { :incremental_repack } - let(:gitaly_task) { :repack_incremental } - - before do - expect(subject).to receive(:get_lease_uuid).and_return(lease_uuid) - - statistics_keys.delete(:repository_size) - end - - it_behaves_like 'it calls Gitaly' - it_behaves_like 'it updates the resource statistics' if update_statistics - end - context 'prune' do before do expect(subject).to receive(:get_lease_uuid).and_return(lease_uuid) @@ -219,41 +147,5 @@ RSpec.shared_examples 'can collect git garbage' do |update_statistics: true| subject.perform(resource.id, 'prune', lease_key, lease_uuid) end end - - shared_examples 'gc tasks' do - before do - allow(subject).to receive(:get_lease_uuid).and_return(lease_uuid) - allow(subject).to receive(:bitmaps_enabled?).and_return(bitmaps_enabled) - - stub_feature_flags(optimized_housekeeping: false) - end - - it 'cleans up repository after finishing' do - expect(resource).to receive(:cleanup).and_call_original - - subject.perform(resource.id, 'gc', lease_key, lease_uuid) - end - - it 'prune calls garbage_collect with the option prune: true' do - repository_service = instance_double(Gitlab::GitalyClient::RepositoryService) - - expect(subject).to receive(:get_gitaly_client).with(:prune, repository.raw_repository).and_return(repository_service) - expect(repository_service).to receive(:garbage_collect).with(bitmaps_enabled, prune: true) - - subject.perform(resource.id, 'prune', lease_key, lease_uuid) - end - end - - context 'with bitmaps enabled' do - let(:bitmaps_enabled) { true } - - include_examples 'gc tasks' - end - - context 'with bitmaps disabled' do - let(:bitmaps_enabled) { false } - - include_examples 'gc tasks' - end end end diff --git a/spec/support/shared_examples/workers/update_repository_move_shared_examples.rb b/spec/support/shared_examples/workers/update_repository_move_shared_examples.rb index c50dc6d5372..9b7183a9eac 100644 --- a/spec/support/shared_examples/workers/update_repository_move_shared_examples.rb +++ b/spec/support/shared_examples/workers/update_repository_move_shared_examples.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true RSpec.shared_examples 'an update storage move worker' do + it 'has the `until_executed` deduplicate strategy' do + expect(described_class.get_deduplicate_strategy).to eq(:until_executed) + end + describe '#perform' do let(:service) { double(:update_repository_storage_service) } |