summaryrefslogtreecommitdiff
path: root/spec/support/shared_examples
diff options
context:
space:
mode:
Diffstat (limited to 'spec/support/shared_examples')
-rw-r--r--spec/support/shared_examples/analytics/cycle_analytics/parentable_examples.rb28
-rw-r--r--spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb121
-rw-r--r--spec/support/shared_examples/controllers/issuables_list_metadata_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/controllers/rate_limited_endpoint_shared_examples.rb26
-rw-r--r--spec/support/shared_examples/features/code_highlight_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/features/content_editor_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/features/dashboard/sidebar_shared_examples.rb21
-rw-r--r--spec/support/shared_examples/features/reportable_note_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/finders/issues_finder_shared_examples.rb18
-rw-r--r--spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/lib/sidebars/your_work/menus/menu_item_examples.rb38
-rw-r--r--spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb75
-rw-r--r--spec/support/shared_examples/models/concerns/integrations/reset_secret_fields_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/member_shared_examples.rb12
-rw-r--r--spec/support/shared_examples/models/members_notifications_shared_example.rb2
-rw-r--r--spec/support/shared_examples/models/relative_positioning_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/models/resource_event_shared_examples.rb12
-rw-r--r--spec/support/shared_examples/models/update_project_statistics_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/namespaces/members.rb13
-rw-r--r--spec/support/shared_examples/observability/csp_shared_examples.rb47
-rw-r--r--spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/quick_actions/issue/promote_to_incident_quick_action_shared_examples.rb28
-rw-r--r--spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb33
-rw-r--r--spec/support/shared_examples/requests/api/issuable_search_shared_examples.rb32
-rw-r--r--spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb38
-rw-r--r--spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb20
-rw-r--r--spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb7
-rw-r--r--spec/support/shared_examples/services/repositories/housekeeping_shared_examples.rb50
-rw-r--r--spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb62
-rw-r--r--spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb24
-rw-r--r--spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb108
-rw-r--r--spec/support/shared_examples/workers/update_repository_move_shared_examples.rb4
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) }