Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/support/shared_examples')
-rw-r--r--spec/support/shared_examples/ci/edit_job_token_scope_shared_examples.rb40
-rw-r--r--spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb44
-rw-r--r--spec/support/shared_examples/features/cascading_settings_shared_examples.rb18
-rw-r--r--spec/support/shared_examples/features/packages_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb21
-rw-r--r--spec/support/shared_examples/features/sidebar_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb11
-rw-r--r--spec/support/shared_examples/features/wiki/user_deletes_wiki_page_shared_examples.rb26
-rw-r--r--spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/graphql/design_fields_shared_examples.rb1
-rw-r--r--spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb12
-rw-r--r--spec/support/shared_examples/graphql/spam_protection_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/lib/cache_helpers_shared_examples.rb101
-rw-r--r--spec/support/shared_examples/lib/gitlab/cycle_analytics/event_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/lib/gitlab/import_export/relation_factory_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/lib/gitlab/kubernetes/network_policy_common_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/lib/gitlab/search_results_sorted_shared_examples.rb18
-rw-r--r--spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb41
-rw-r--r--spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/models/chat_integration_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/models/integrations/has_web_hook_shared_examples.rb97
-rw-r--r--spec/support/shared_examples/models/issue_tracker_service_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/member_shared_examples.rb256
-rw-r--r--spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb54
-rw-r--r--spec/support/shared_examples/models/wiki_shared_examples.rb95
-rw-r--r--spec/support/shared_examples/namespaces/traversal_examples.rb96
-rw-r--r--spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb18
-rw-r--r--spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/requests/api/graphql/noteable_shared_examples.rb22
-rw-r--r--spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb229
-rw-r--r--spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb136
-rw-r--r--spec/support/shared_examples/requests/api/packages_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/incident_creation_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb25
-rw-r--r--spec/support/shared_examples/services/jira_import/user_mapper_services_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/packages_shared_examples.rb1
-rw-r--r--spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb15
-rw-r--r--spec/support/shared_examples/services/service_ping/complete_service_ping_payload_shared_examples.rb9
-rw-r--r--spec/support/shared_examples/services/service_ping/service_ping_payload_with_all_expected_metrics_shared_examples.rb11
-rw-r--r--spec/support/shared_examples/services/service_ping/service_ping_payload_without_restricted_metrics_shared_examples.rb11
-rw-r--r--spec/support/shared_examples/services/snippets_shared_examples.rb74
-rw-r--r--spec/support/shared_examples/services/wikis/create_attachment_service_shared_examples.rb19
-rw-r--r--spec/support/shared_examples/workers/in_product_marketing_email_shared_example.rb15
49 files changed, 1393 insertions, 243 deletions
diff --git a/spec/support/shared_examples/ci/edit_job_token_scope_shared_examples.rb b/spec/support/shared_examples/ci/edit_job_token_scope_shared_examples.rb
new file mode 100644
index 00000000000..05b2b5f5de1
--- /dev/null
+++ b/spec/support/shared_examples/ci/edit_job_token_scope_shared_examples.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'editable job token scope' do
+ shared_examples 'returns error' do |error|
+ it 'returns an error response', :aggregate_failures do
+ expect(result).to be_error
+ expect(result.message).to eq(error)
+ end
+ end
+
+ context 'when job token scope is disabled for the given project' do
+ before do
+ allow(project).to receive(:ci_job_token_scope_enabled?).and_return(false)
+ end
+
+ it_behaves_like 'returns error', 'Job token scope is disabled for this project'
+ end
+
+ context 'when user does not have permissions to edit the job token scope' do
+ it_behaves_like 'returns error', 'Insufficient permissions to modify the job token scope'
+ end
+
+ context 'when user has permissions to edit the job token scope' do
+ before do
+ project.add_maintainer(current_user)
+ end
+
+ context 'when target project is not provided' do
+ let(:target_project) { nil }
+
+ it_behaves_like 'returns error', Ci::JobTokenScope::EditScopeValidations::TARGET_PROJECT_UNAUTHORIZED_OR_UNFOUND
+ end
+
+ context 'when target project is provided' do
+ context 'when user does not have permissions to read the target project' do
+ it_behaves_like 'returns error', Ci::JobTokenScope::EditScopeValidations::TARGET_PROJECT_UNAUTHORIZED_OR_UNFOUND
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb b/spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb
index 70a684c12bf..017e55309f7 100644
--- a/spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb
@@ -44,11 +44,13 @@ RSpec.shared_examples 'project access tokens available #create' do
end
it 'creates project access token' do
+ access_level = access_token_params[:access_level] || Gitlab::Access::MAINTAINER
subject
expect(created_token.name).to eq(access_token_params[:name])
expect(created_token.scopes).to eq(access_token_params[:scopes])
expect(created_token.expires_at).to eq(access_token_params[:expires_at])
+ expect(project.project_member(created_token.user).access_level).to eq(access_level)
end
it 'creates project bot user' do
diff --git a/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb b/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
index 9af35c189d0..e8f7e62d0d7 100644
--- a/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
@@ -1,10 +1,11 @@
# frozen_string_literal: true
RSpec.shared_examples 'wiki controller actions' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:other_user) { create(:user) }
+
let(:container) { raise NotImplementedError }
let(:routing_params) { raise NotImplementedError }
-
- let_it_be(:user) { create(:user) }
let(:wiki) { Wiki.for_container(container, user) }
let(:wiki_title) { 'page title test' }
@@ -458,6 +459,7 @@ RSpec.shared_examples 'wiki controller actions' do
describe 'DELETE #destroy' do
let(:id_param) { wiki_title }
+ let(:delete_user) { user }
subject(:request) do
delete(:destroy,
@@ -466,13 +468,21 @@ RSpec.shared_examples 'wiki controller actions' do
))
end
+ before do
+ sign_in(delete_user)
+ end
+
context 'when page exists' do
- it 'deletes the page' do
- expect do
- request
- end.to change { wiki.list_pages.size }.by(-1)
+ shared_examples 'deletes the page' do
+ specify do
+ expect do
+ request
+ end.to change { wiki.list_pages.size }.by(-1)
+ end
end
+ it_behaves_like 'deletes the page'
+
context 'but page cannot be deleted' do
before do
allow_next_instance_of(WikiPage) do |page|
@@ -489,6 +499,28 @@ RSpec.shared_examples 'wiki controller actions' do
expect(assigns(:error)).to eq('Could not delete wiki page')
end
end
+
+ context 'when user is a developer' do
+ let(:delete_user) { other_user }
+
+ before do
+ container.add_developer(other_user)
+ end
+
+ it_behaves_like 'deletes the page'
+ end
+
+ context 'when user is a reporter' do
+ let(:delete_user) { other_user }
+
+ before do
+ container.add_reporter(other_user)
+ end
+
+ it 'returns 404' do
+ is_expected.to have_gitlab_http_status(:not_found)
+ end
+ end
end
context 'when page does not exist' do
diff --git a/spec/support/shared_examples/features/cascading_settings_shared_examples.rb b/spec/support/shared_examples/features/cascading_settings_shared_examples.rb
index 29ef3da9a85..395f4fc54e0 100644
--- a/spec/support/shared_examples/features/cascading_settings_shared_examples.rb
+++ b/spec/support/shared_examples/features/cascading_settings_shared_examples.rb
@@ -13,10 +13,22 @@ RSpec.shared_examples 'a cascading setting' do
click_save_button
end
- it 'disables setting in subgroups' do
- visit subgroup_path
+ shared_examples 'subgroup settings are disabled' do
+ it 'disables setting in subgroups' do
+ visit subgroup_path
+
+ expect(find("#{setting_field_selector}[disabled]")).to be_checked
+ end
+ end
+
+ include_examples 'subgroup settings are disabled'
+
+ context 'when use_traversal_ids_for_ancestors is disabled' do
+ before do
+ stub_feature_flags(use_traversal_ids_for_ancestors: false)
+ end
- expect(find("#{setting_field_selector}[disabled]")).to be_checked
+ include_examples 'subgroup settings are disabled'
end
it 'does not show enforcement checkbox in subgroups' do
diff --git a/spec/support/shared_examples/features/packages_shared_examples.rb b/spec/support/shared_examples/features/packages_shared_examples.rb
index 4d2e13aa5bc..9e88db2e1c0 100644
--- a/spec/support/shared_examples/features/packages_shared_examples.rb
+++ b/spec/support/shared_examples/features/packages_shared_examples.rb
@@ -32,11 +32,9 @@ RSpec.shared_examples 'package details link' do |property|
expect(page).to have_current_path(project_package_path(package.project, package))
- page.within('[data-qa-selector="package_title"]') do
- expect(page).to have_content(package.name)
- end
+ expect(page).to have_css('.packages-app h1[data-testid="title"]', text: package.name)
- page.within('[data-qa-selector="package_information_content"]') do
+ page.within(%Q([name="#{package.name}"])) do
expect(page).to have_content('Installation')
expect(page).to have_content('Registry setup')
end
diff --git a/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb b/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb
new file mode 100644
index 00000000000..bb5460e2a6f
--- /dev/null
+++ b/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'search timeouts' do |scope|
+ context 'when search times out' do
+ before do
+ allow_next_instance_of(SearchService) do |service|
+ allow(service).to receive(:search_objects).and_raise(ActiveRecord::QueryCanceled)
+ end
+
+ visit(search_path(search: 'test', scope: scope))
+ end
+
+ it 'renders timeout information' do
+ expect(page).to have_content('Your search timed out')
+ end
+
+ it 'sets tab count to 0' do
+ expect(page.find('.search-filter .active')).to have_text('0')
+ end
+ end
+end
diff --git a/spec/support/shared_examples/features/sidebar_shared_examples.rb b/spec/support/shared_examples/features/sidebar_shared_examples.rb
index c9508818f74..5bfe929e957 100644
--- a/spec/support/shared_examples/features/sidebar_shared_examples.rb
+++ b/spec/support/shared_examples/features/sidebar_shared_examples.rb
@@ -175,12 +175,4 @@ RSpec.shared_examples 'issue boards sidebar' do
end
end
end
-
- def refresh_and_click_first_card
- page.refresh
-
- wait_for_requests
-
- first_card.click
- end
end
diff --git a/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb
index f2576931642..dfc9a45bd0d 100644
--- a/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb
@@ -20,17 +20,6 @@ RSpec.shared_examples 'User creates wiki page' do
click_link "Create your first page"
end
- it "shows validation error message if the form is force submitted", :js do
- page.within(".wiki-form") do
- fill_in(:wiki_content, with: "")
-
- page.execute_script("document.querySelector('.wiki-form').submit()")
- page.accept_alert # manually force form submit
- end
-
- expect(page).to have_content("The form contains the following error:").and have_content("Content can't be blank")
- end
-
it "disables the submit button", :js do
page.within(".wiki-form") do
fill_in(:wiki_content, with: "")
diff --git a/spec/support/shared_examples/features/wiki/user_deletes_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_deletes_wiki_page_shared_examples.rb
index ee0261771f9..55c89977a99 100644
--- a/spec/support/shared_examples/features/wiki/user_deletes_wiki_page_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/user_deletes_wiki_page_shared_examples.rb
@@ -7,18 +7,34 @@
RSpec.shared_examples 'User deletes wiki page' do
include WikiHelpers
+ let_it_be(:developer) { create(:user) }
+
let(:wiki_page) { create(:wiki_page, wiki: wiki) }
before do
+ wiki.container.add_developer(developer)
+
sign_in(user)
visit wiki_page_path(wiki, wiki_page)
end
- it 'deletes a page', :js do
- click_on('Edit')
- click_on('Delete')
- find('[data-testid="confirm_deletion_button"]').click
+ shared_examples 'deletes a wiki page' do
+ specify 'deletes a page', :js do
+ click_on('Edit')
+ click_on('Delete')
+ find('[data-testid="confirm_deletion_button"]').click
+
+ expect(page).to have_content('Wiki page was successfully deleted.')
+ end
+ end
+
+ context 'when user is the owner or maintainer' do
+ it_behaves_like 'deletes a wiki page'
+ end
+
+ context 'when user is a developer' do
+ let(:user) { developer }
- expect(page).to have_content('Wiki page was successfully deleted.')
+ it_behaves_like 'deletes a wiki page'
end
end
diff --git a/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb
index db2a96d9649..9587da0233e 100644
--- a/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb
@@ -90,19 +90,6 @@ RSpec.shared_examples 'User updates wiki page' do
expect(page).to have_field('wiki[message]', with: 'Update Wiki title')
end
- it 'shows a validation error message if the form is force submitted', :js do
- fill_in(:wiki_content, with: '')
-
- page.execute_script("document.querySelector('.wiki-form').submit()")
- page.accept_alert # manually force form submit
-
- expect(page).to have_selector('.wiki-form')
- expect(page).to have_content('Edit Page')
- expect(page).to have_content('The form contains the following error:')
- expect(page).to have_content("Content can't be blank")
- expect(find('textarea#wiki_content').value).to eq('')
- end
-
it "disables the submit button", :js do
page.within(".wiki-form") do
fill_in(:wiki_content, with: "")
diff --git a/spec/support/shared_examples/graphql/design_fields_shared_examples.rb b/spec/support/shared_examples/graphql/design_fields_shared_examples.rb
index 9c2eb3e5a5c..efbcfaf0e91 100644
--- a/spec/support/shared_examples/graphql/design_fields_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/design_fields_shared_examples.rb
@@ -27,6 +27,7 @@ RSpec.shared_examples 'a GraphQL type with design fields' do
describe '#image' do
let_it_be(:current_user) { create(:user) }
+
let(:schema) { GitlabSchema }
let(:query) { GraphQL::Query.new(schema) }
let(:context) { query.context }
diff --git a/spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb b/spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb
index 5e15c91cd41..011a2157f24 100644
--- a/spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb
@@ -3,17 +3,13 @@
require 'spec_helper'
RSpec.shared_examples 'a mutation which can mutate a spammable' do
- describe "#additional_spam_params" do
- it 'passes additional spam params to the service' do
+ describe "#spam_params" do
+ it 'passes spam params to the service constructor' do
args = [
project: anything,
current_user: anything,
- params: hash_including(
- api: true,
- request: instance_of(ActionDispatch::Request),
- captcha_response: captcha_response,
- spam_log_id: spam_log_id
- )
+ params: anything,
+ spam_params: instance_of(::Spam::SpamParams)
]
expect(service).to receive(:new).with(*args).and_call_original
diff --git a/spec/support/shared_examples/graphql/spam_protection_shared_examples.rb b/spec/support/shared_examples/graphql/spam_protection_shared_examples.rb
index 8fb89a4f80e..c0b71a494d0 100644
--- a/spec/support/shared_examples/graphql/spam_protection_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/spam_protection_shared_examples.rb
@@ -57,7 +57,7 @@ RSpec.shared_examples 'has spam protection' do
context 'and no CAPTCHA is required' do
let(:render_captcha) { false }
- it 'does not return a to-level error' do
+ it 'does not return a top-level error' do
send_request
expect(graphql_errors).to be_blank
diff --git a/spec/support/shared_examples/lib/cache_helpers_shared_examples.rb b/spec/support/shared_examples/lib/cache_helpers_shared_examples.rb
new file mode 100644
index 00000000000..845fa78a827
--- /dev/null
+++ b/spec/support/shared_examples/lib/cache_helpers_shared_examples.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples_for 'object cache helper' do
+ it { is_expected.to be_a(Gitlab::Json::PrecompiledJson) }
+
+ it "uses the presenter" do
+ expect(presenter).to receive(:represent).with(presentable, project: project)
+
+ subject
+ end
+
+ it "is valid JSON" do
+ parsed = Gitlab::Json.parse(subject.to_s)
+
+ expect(parsed).to be_a(Hash)
+ expect(parsed["id"]).to eq(presentable.id)
+ end
+
+ it "fetches from the cache" do
+ expect(instance.cache).to receive(:fetch).with("#{presenter.class.name}:#{presentable.cache_key}:#{user.cache_key}", expires_in: described_class::DEFAULT_EXPIRY).once
+
+ subject
+ end
+
+ context "when a cache context is supplied" do
+ before do
+ kwargs[:cache_context] = -> (item) { item.project.cache_key }
+ end
+
+ it "uses the context to augment the cache key" do
+ expect(instance.cache).to receive(:fetch).with("#{presenter.class.name}:#{presentable.cache_key}:#{project.cache_key}", expires_in: described_class::DEFAULT_EXPIRY).once
+
+ subject
+ end
+ end
+
+ context "when expires_in is supplied" do
+ it "sets the expiry when accessing the cache" do
+ kwargs[:expires_in] = 7.days
+
+ expect(instance.cache).to receive(:fetch).with("#{presenter.class.name}:#{presentable.cache_key}:#{user.cache_key}", expires_in: 7.days).once
+
+ subject
+ end
+ end
+end
+
+RSpec.shared_examples_for 'collection cache helper' do
+ it { is_expected.to be_an(Gitlab::Json::PrecompiledJson) }
+
+ it "uses the presenter" do
+ presentable.each do |item|
+ expect(presenter).to receive(:represent).with(item, project: project)
+ end
+
+ subject
+ end
+
+ it "is valid JSON" do
+ parsed = Gitlab::Json.parse(subject.to_s)
+
+ expect(parsed).to be_an(Array)
+
+ presentable.each_with_index do |item, i|
+ expect(parsed[i]["id"]).to eq(item.id)
+ end
+ end
+
+ it "fetches from the cache" do
+ keys = presentable.map { |item| "#{presenter.class.name}:#{item.cache_key}:#{user.cache_key}" }
+
+ expect(instance.cache).to receive(:fetch_multi).with(*keys, expires_in: described_class::DEFAULT_EXPIRY).once.and_call_original
+
+ subject
+ end
+
+ context "when a cache context is supplied" do
+ before do
+ kwargs[:cache_context] = -> (item) { item.project.cache_key }
+ end
+
+ it "uses the context to augment the cache key" do
+ keys = presentable.map { |item| "#{presenter.class.name}:#{item.cache_key}:#{project.cache_key}" }
+
+ expect(instance.cache).to receive(:fetch_multi).with(*keys, expires_in: described_class::DEFAULT_EXPIRY).once.and_call_original
+
+ subject
+ end
+ end
+
+ context "expires_in is supplied" do
+ it "sets the expiry when accessing the cache" do
+ keys = presentable.map { |item| "#{presenter.class.name}:#{item.cache_key}:#{user.cache_key}" }
+ kwargs[:expires_in] = 7.days
+
+ expect(instance.cache).to receive(:fetch_multi).with(*keys, expires_in: 7.days).once.and_call_original
+
+ subject
+ end
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/cycle_analytics/event_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/cycle_analytics/event_shared_examples.rb
index 7d341d79bae..6e12b5a0e85 100644
--- a/spec/support/shared_examples/lib/gitlab/cycle_analytics/event_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/cycle_analytics/event_shared_examples.rb
@@ -3,6 +3,7 @@
RSpec.shared_examples_for 'value stream analytics event' do
let(:params) { {} }
let(:instance) { described_class.new(params) }
+ let(:expected_hash_code) { Digest::SHA256.hexdigest(instance.class.identifier.to_s) }
it { expect(described_class.name).to be_a_kind_of(String) }
it { expect(described_class.identifier).to be_a_kind_of(Symbol) }
@@ -19,4 +20,16 @@ RSpec.shared_examples_for 'value stream analytics event' do
expect(output_query).to be_a_kind_of(ActiveRecord::Relation)
end
end
+
+ describe '#hash_code' do
+ it 'returns a hash that uniquely identifies an event' do
+ expect(instance.hash_code).to eq(expected_hash_code)
+ end
+
+ it 'does not differ when the same object is built with the same params' do
+ another_instance_with_same_params = described_class.new(params)
+
+ expect(another_instance_with_same_params.hash_code).to eq(instance.hash_code)
+ end
+ end
end
diff --git a/spec/support/shared_examples/lib/gitlab/import_export/relation_factory_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/import_export/relation_factory_shared_examples.rb
index 33061f17bde..3c5c65f0690 100644
--- a/spec/support/shared_examples/lib/gitlab/import_export/relation_factory_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/import_export/relation_factory_shared_examples.rb
@@ -12,7 +12,7 @@ RSpec.shared_examples 'Notes user references' do
'id' => 111,
'access_level' => 30,
'source_id' => 1,
- 'source_type' => importable.class.name == 'Project' ? 'Project' : 'Namespace',
+ 'source_type' => importable.instance_of?(Project) ? 'Project' : 'Namespace',
'user_id' => 3,
'notification_level' => 3,
'created_at' => '2016-11-18T09:29:42.634Z',
diff --git a/spec/support/shared_examples/lib/gitlab/kubernetes/network_policy_common_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/kubernetes/network_policy_common_shared_examples.rb
index f018ece0d46..2633a89eeee 100644
--- a/spec/support/shared_examples/lib/gitlab/kubernetes/network_policy_common_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/kubernetes/network_policy_common_shared_examples.rb
@@ -19,7 +19,8 @@ RSpec.shared_examples 'network policy common specs' do
creation_timestamp: nil,
manifest: YAML.dump(policy.resource.deep_stringify_keys),
is_autodevops: false,
- is_enabled: true
+ is_enabled: true,
+ environment_ids: []
}
end
diff --git a/spec/support/shared_examples/lib/gitlab/search_results_sorted_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/search_results_sorted_shared_examples.rb
index eafb49cef71..e4f09dfa0b0 100644
--- a/spec/support/shared_examples/lib/gitlab/search_results_sorted_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/search_results_sorted_shared_examples.rb
@@ -33,3 +33,21 @@ RSpec.shared_examples 'search results sorted' do
end
end
end
+
+RSpec.shared_examples 'search results sorted by popularity' do
+ context 'sort: popularity_desc' do
+ let(:sort) { 'popularity_desc' }
+
+ it 'sorts results by upvotes' do
+ expect(results_popular.objects(scope).map(&:id)).to eq([popular_result.id, less_popular_result.id, non_popular_result.id])
+ end
+ end
+
+ context 'sort: popularity_asc' do
+ let(:sort) { 'popularity_asc' }
+
+ it 'sorts results by created_at' do
+ expect(results_popular.objects(scope).map(&:id)).to eq([non_popular_result.id, less_popular_result.id, popular_result.id])
+ end
+ end
+end
diff --git a/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb b/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb
index 1b110ab02b5..a84658780b9 100644
--- a/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb
+++ b/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb
@@ -5,9 +5,10 @@ RSpec.shared_examples 'store ActiveRecord info in RequestStore' do |db_role|
2.times do
Gitlab::WithRequestStore.with_request_store do
subscriber.sql(event)
+ connection = event.payload[:connection]
if db_role == :primary
- expect(described_class.db_counter_payload).to eq(
+ expected = {
db_count: record_query ? 1 : 0,
db_write_count: record_write_query ? 1 : 0,
db_cached_count: record_cached_query ? 1 : 0,
@@ -18,10 +19,13 @@ RSpec.shared_examples 'store ActiveRecord info in RequestStore' do |db_role|
db_replica_count: 0,
db_replica_duration_s: 0.0,
db_primary_wal_count: record_wal_query ? 1 : 0,
+ db_primary_wal_cached_count: record_wal_query && record_cached_query ? 1 : 0,
+ db_replica_wal_cached_count: 0,
db_replica_wal_count: 0
- )
+ }
+ expected[:"db_primary_#{::Gitlab::Database.dbname(connection)}_duration_s"] = 0.002 if record_query
elsif db_role == :replica
- expect(described_class.db_counter_payload).to eq(
+ expected = {
db_count: record_query ? 1 : 0,
db_write_count: record_write_query ? 1 : 0,
db_cached_count: record_cached_query ? 1 : 0,
@@ -32,15 +36,35 @@ RSpec.shared_examples 'store ActiveRecord info in RequestStore' do |db_role|
db_replica_count: record_query ? 1 : 0,
db_replica_duration_s: record_query ? 0.002 : 0,
db_replica_wal_count: record_wal_query ? 1 : 0,
+ db_replica_wal_cached_count: record_wal_query && record_cached_query ? 1 : 0,
+ db_primary_wal_cached_count: 0,
db_primary_wal_count: 0
- )
+ }
+ expected[:"db_replica_#{::Gitlab::Database.dbname(connection)}_duration_s"] = 0.002 if record_query
else
- expect(described_class.db_counter_payload).to eq(
+ expected = {
db_count: record_query ? 1 : 0,
db_write_count: record_write_query ? 1 : 0,
db_cached_count: record_cached_query ? 1 : 0
- )
+ }
end
+
+ expect(described_class.db_counter_payload).to eq(expected)
+ end
+ end
+ end
+
+ context 'when multiple_database_metrics is disabled' do
+ before do
+ stub_feature_flags(multiple_database_metrics: false)
+ end
+
+ it 'does not include per database metrics' do
+ Gitlab::WithRequestStore.with_request_store do
+ subscriber.sql(event)
+ connection = event.payload[:connection]
+
+ expect(described_class.db_counter_payload).not_to include(:"db_replica_#{::Gitlab::Database.dbname(connection)}_duration_s")
end
end
end
@@ -71,7 +95,10 @@ RSpec.shared_examples 'record ActiveRecord metrics in a metrics transaction' do
end
if record_wal_query
- expect(transaction).to receive(:increment).with("gitlab_transaction_db_#{db_role}_wal_count_total".to_sym, 1) if db_role
+ if db_role
+ expect(transaction).to receive(:increment).with("gitlab_transaction_db_#{db_role}_wal_count_total".to_sym, 1)
+ expect(transaction).to receive(:increment).with("gitlab_transaction_db_#{db_role}_wal_cached_count_total".to_sym, 1) if record_cached_query
+ end
else
expect(transaction).not_to receive(:increment).with("gitlab_transaction_db_#{db_role}_wal_count_total".to_sym, 1) if db_role
end
diff --git a/spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb b/spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb
index 42f82987989..03f565e0aac 100644
--- a/spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb
+++ b/spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb
@@ -165,9 +165,9 @@ RSpec.shared_examples 'AtomicInternalId' do |validate_presence: true|
3.times { supply.next_value }
end
- current_value = described_class.public_send(method_name, scope_value, &:current_value)
-
- expect(current_value).to eq(iid + 3)
+ described_class.public_send(method_name, scope_value) do |supply|
+ expect(supply.next_value).to eq(iid + 4)
+ end
end
end
diff --git a/spec/support/shared_examples/models/chat_integration_shared_examples.rb b/spec/support/shared_examples/models/chat_integration_shared_examples.rb
index 9f3be3e2e06..72659dd5f3b 100644
--- a/spec/support/shared_examples/models/chat_integration_shared_examples.rb
+++ b/spec/support/shared_examples/models/chat_integration_shared_examples.rb
@@ -13,7 +13,7 @@ RSpec.shared_examples "chat integration" do |integration_name|
end
it { is_expected.to validate_presence_of(:webhook) }
- it_behaves_like "issue tracker service URL attribute", :webhook
+ it_behaves_like "issue tracker integration URL attribute", :webhook
end
context "when integration is inactive" do
@@ -163,7 +163,7 @@ RSpec.shared_examples "chat integration" do |integration_name|
context "with issue events" do
let(:opts) { { title: "Awesome issue", description: "please fix" } }
let(:sample_data) do
- service = Issues::CreateService.new(project: project, current_user: user, params: opts)
+ service = Issues::CreateService.new(project: project, current_user: user, params: opts, spam_params: nil)
issue = service.execute
service.hook_data(issue, "open")
end
diff --git a/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb b/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb
index 66448aca2c5..2d4c0b60f2b 100644
--- a/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb
@@ -8,7 +8,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
def execute_with_options(options)
receive(:new).with(webhook_url, options.merge(http_client: Integrations::SlackMattermostNotifier::HTTPClient))
- .and_return(double(:slack_service).as_null_object)
+ .and_return(double(:slack_integration).as_null_object)
end
describe "Associations" do
@@ -23,7 +23,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
end
it { is_expected.to validate_presence_of(:webhook) }
- it_behaves_like 'issue tracker service URL attribute', :webhook
+ it_behaves_like 'issue tracker integration URL attribute', :webhook
end
context 'when service is inactive' 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 d23f95b2e9e..cf38a583944 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
@@ -122,6 +122,22 @@ RSpec.shared_examples 'value stream analytics stage' do
expect(stage.parent_id).to eq(parent.id)
end
end
+
+ describe '#hash_code' do
+ it 'does not differ when the same object is built with the same params' do
+ stage_1 = build(factory)
+ stage_2 = build(factory)
+
+ expect(stage_1.events_hash_code).to eq(stage_2.events_hash_code)
+ end
+
+ it 'differs when the stage events are different' do
+ stage_1 = build(factory, start_event_identifier: :merge_request_created, end_event_identifier: :merge_request_merged)
+ stage_2 = build(factory, start_event_identifier: :issue_created, end_event_identifier: :issue_first_mentioned_in_commit)
+
+ expect(stage_1.events_hash_code).not_to eq(stage_2.events_hash_code)
+ end
+ end
end
RSpec.shared_examples 'value stream analytics label based stage' do
diff --git a/spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb b/spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb
index 128999d02fa..e35ac9c0d0d 100644
--- a/spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb
+++ b/spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb
@@ -66,14 +66,14 @@ RSpec.shared_examples Integrations::BaseSlashCommands do
}
end
- let(:service) do
- project.create_mattermost_slash_commands_service(
+ let(:integration) do
+ project.create_mattermost_slash_commands_integration(
properties: { token: 'token' }
)
end
it 'generates the url' do
- response = service.trigger(params)
+ response = integration.trigger(params)
expect(response[:text]).to start_with(':wave: Hi there!')
end
diff --git a/spec/support/shared_examples/models/integrations/has_web_hook_shared_examples.rb b/spec/support/shared_examples/models/integrations/has_web_hook_shared_examples.rb
new file mode 100644
index 00000000000..1fa340a0cf4
--- /dev/null
+++ b/spec/support/shared_examples/models/integrations/has_web_hook_shared_examples.rb
@@ -0,0 +1,97 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples Integrations::HasWebHook do
+ include AfterNextHelpers
+
+ describe 'callbacks' do
+ it 'calls #update_web_hook! when enabled' do
+ expect(integration).to receive(:update_web_hook!)
+
+ integration.active = true
+ integration.save!
+ end
+
+ it 'does not call #update_web_hook! when disabled' do
+ expect(integration).not_to receive(:update_web_hook!)
+
+ integration.active = false
+ integration.save!
+ end
+
+ it 'does not call #update_web_hook! when validation fails' do
+ expect(integration).not_to receive(:update_web_hook!)
+
+ integration.active = true
+ integration.project = nil
+ expect(integration.save).to be(false)
+ end
+ end
+
+ describe '#hook_url' do
+ it 'returns a string' do
+ expect(integration.hook_url).to be_a(String)
+ end
+ end
+
+ describe '#hook_ssl_verification' do
+ it 'returns a boolean' do
+ expect(integration.hook_ssl_verification).to be_in([true, false])
+ end
+ end
+
+ describe '#update_web_hook!' do
+ def call
+ integration.update_web_hook!
+ end
+
+ it 'creates or updates a service hook' do
+ expect { call }.to change(ServiceHook, :count).by(1)
+ expect(integration.service_hook.url).to eq(hook_url)
+
+ integration.service_hook.update!(url: 'http://other.com')
+
+ expect { call }.to change { integration.service_hook.reload.url }.from('http://other.com').to(hook_url)
+ end
+
+ it 'raises an error if the service hook could not be saved' do
+ call
+ integration.service_hook.integration = nil
+
+ expect { call }.to raise_error(ActiveRecord::RecordInvalid)
+ end
+
+ it 'does not attempt to save the service hook if there are no changes' do
+ call
+
+ expect(integration.service_hook).not_to receive(:save!)
+
+ call
+ end
+ end
+
+ describe '#execute_web_hook!' do
+ let(:args) { ['foo', [1, 2, 3]] }
+
+ def call
+ integration.execute_web_hook!(*args)
+ end
+
+ it 'creates the webhook if necessary and executes it' do
+ expect_next(ServiceHook).to receive(:execute).with(*args)
+ expect { call }.to change(ServiceHook, :count).by(1)
+
+ expect(integration.service_hook).to receive(:execute).with(*args)
+ expect { call }.not_to change(ServiceHook, :count)
+ end
+
+ it 'raises an error if the service hook could not be saved' do
+ expect_next(ServiceHook).to receive(:execute).with(*args)
+
+ call
+ integration.service_hook.integration = nil
+
+ expect(integration.service_hook).not_to receive(:execute)
+ expect { call }.to raise_error(ActiveRecord::RecordInvalid)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/issue_tracker_service_shared_examples.rb b/spec/support/shared_examples/models/issue_tracker_service_shared_examples.rb
index b275d594792..6d519e561ee 100644
--- a/spec/support/shared_examples/models/issue_tracker_service_shared_examples.rb
+++ b/spec/support/shared_examples/models/issue_tracker_service_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples 'issue tracker service URL attribute' do |url_attr|
+RSpec.shared_examples 'issue tracker integration URL attribute' do |url_attr|
it { is_expected.to allow_value('https://example.com').for(url_attr) }
it { is_expected.not_to allow_value('example.com').for(url_attr) }
diff --git a/spec/support/shared_examples/models/member_shared_examples.rb b/spec/support/shared_examples/models/member_shared_examples.rb
index 7ede6f0d8d4..c111d250d34 100644
--- a/spec/support/shared_examples/models/member_shared_examples.rb
+++ b/spec/support/shared_examples/models/member_shared_examples.rb
@@ -75,3 +75,259 @@ RSpec.shared_examples '#valid_level_roles' do |entity_name|
expect(presenter.valid_level_roles).to eq(expected_roles)
end
end
+
+RSpec.shared_examples_for "member creation" do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:admin) { create(:admin) }
+
+ describe '#execute' do
+ it 'returns a Member object', :aggregate_failures do
+ member = described_class.new(source, user, :maintainer).execute
+
+ expect(member).to be_a member_type
+ expect(member).to be_persisted
+ end
+
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it 'sets members.created_by to the given admin current_user' do
+ member = described_class.new(source, user, :maintainer, current_user: admin).execute
+
+ expect(member.created_by).to eq(admin)
+ end
+ end
+
+ context 'when admin mode is disabled' do
+ it 'rejects setting members.created_by to the given admin current_user' do
+ member = described_class.new(source, user, :maintainer, current_user: admin).execute
+
+ expect(member.created_by).to be_nil
+ end
+ end
+
+ it 'sets members.expires_at to the given expires_at' do
+ member = described_class.new(source, user, :maintainer, expires_at: Date.new(2016, 9, 22)).execute
+
+ expect(member.expires_at).to eq(Date.new(2016, 9, 22))
+ end
+
+ described_class.access_levels.each do |sym_key, int_access_level|
+ it "accepts the :#{sym_key} symbol as access level", :aggregate_failures do
+ expect(source.users).not_to include(user)
+
+ member = described_class.new(source, user.id, sym_key).execute
+
+ expect(member.access_level).to eq(int_access_level)
+ expect(source.users.reload).to include(user)
+ end
+
+ it "accepts the #{int_access_level} integer as access level", :aggregate_failures do
+ expect(source.users).not_to include(user)
+
+ member = described_class.new(source, user.id, int_access_level).execute
+
+ expect(member.access_level).to eq(int_access_level)
+ expect(source.users.reload).to include(user)
+ end
+ end
+
+ context 'with no current_user' do
+ context 'when called with a known user id' do
+ it 'adds the user as a member' do
+ expect(source.users).not_to include(user)
+
+ described_class.new(source, user.id, :maintainer).execute
+
+ expect(source.users.reload).to include(user)
+ end
+ end
+
+ context 'when called with an unknown user id' do
+ it 'adds the user as a member' do
+ expect(source.users).not_to include(user)
+
+ described_class.new(source, non_existing_record_id, :maintainer).execute
+
+ expect(source.users.reload).not_to include(user)
+ end
+ end
+
+ context 'when called with a user object' do
+ it 'adds the user as a member' do
+ expect(source.users).not_to include(user)
+
+ described_class.new(source, user, :maintainer).execute
+
+ expect(source.users.reload).to include(user)
+ end
+ end
+
+ context 'when called with a requester user object' do
+ before do
+ source.request_access(user)
+ end
+
+ 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 do
+ described_class.new(source, user, :maintainer).execute
+ end.to raise_error(Gitlab::Access::AccessDeniedError)
+
+ expect(source.users.reload).not_to include(user)
+ expect(source.requesters.reload.exists?(user_id: user)).to be_truthy
+ end
+ end
+
+ context 'when called with a known user email' do
+ it 'adds the user as a member' do
+ expect(source.users).not_to include(user)
+
+ described_class.new(source, user.email, :maintainer).execute
+
+ expect(source.users.reload).to include(user)
+ end
+ end
+
+ context 'when called with an unknown user email' do
+ it 'creates an invited member' do
+ expect(source.users).not_to include(user)
+
+ described_class.new(source, 'user@example.com', :maintainer).execute
+
+ expect(source.members.invite.pluck(:invite_email)).to include('user@example.com')
+ end
+ end
+
+ context 'when called with an unknown user email starting with a number' do
+ it 'creates an invited member', :aggregate_failures do
+ email_starting_with_number = "#{user.id}_email@example.com"
+
+ described_class.new(source, email_starting_with_number, :maintainer).execute
+
+ expect(source.members.invite.pluck(:invite_email)).to include(email_starting_with_number)
+ expect(source.users.reload).not_to include(user)
+ end
+ end
+ end
+
+ context 'when current_user can update member', :enable_admin_mode do
+ it 'creates the member' do
+ expect(source.users).not_to include(user)
+
+ described_class.new(source, user, :maintainer, current_user: admin).execute
+
+ expect(source.users.reload).to include(user)
+ end
+
+ context 'when called with a requester user object' do
+ before do
+ source.request_access(user)
+ end
+
+ 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
+
+ described_class.new(source, user, :maintainer, current_user: admin).execute
+
+ expect(source.users.reload).to include(user)
+ expect(source.requesters.reload.exists?(user_id: user)).to be_falsy
+ end
+ end
+ end
+
+ context 'when current_user cannot update member' do
+ it 'does not create the member', :aggregate_failures do
+ expect(source.users).not_to include(user)
+
+ member = described_class.new(source, user, :maintainer, current_user: user).execute
+
+ expect(source.users.reload).not_to include(user)
+ expect(member).not_to be_persisted
+ end
+
+ context 'when called with a requester user object' do
+ before do
+ source.request_access(user)
+ end
+
+ it 'does not destroy the requester', :aggregate_failures do
+ expect(source.users).not_to include(user)
+ expect(source.requesters.exists?(user_id: user)).to be_truthy
+
+ described_class.new(source, user, :maintainer, current_user: user).execute
+
+ expect(source.users.reload).not_to include(user)
+ expect(source.requesters.exists?(user_id: user)).to be_truthy
+ end
+ end
+ end
+
+ context 'when member already exists' do
+ before do
+ source.add_user(user, :developer)
+ end
+
+ context 'with no current_user' do
+ it 'updates the member' do
+ expect(source.users).to include(user)
+
+ described_class.new(source, user, :maintainer).execute
+
+ expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER)
+ end
+ end
+
+ context 'when current_user can update member', :enable_admin_mode do
+ it 'updates the member' do
+ expect(source.users).to include(user)
+
+ described_class.new(source, user, :maintainer, current_user: admin).execute
+
+ expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER)
+ end
+ end
+
+ context 'when current_user cannot update member' do
+ it 'does not update the member' do
+ expect(source.users).to include(user)
+
+ described_class.new(source, user, :maintainer, current_user: user).execute
+
+ expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::DEVELOPER)
+ end
+ end
+ end
+ end
+
+ describe '.add_users' do
+ let_it_be(:user1) { create(:user) }
+ let_it_be(:user2) { create(:user) }
+
+ it 'returns a Member objects' do
+ members = described_class.add_users(source, [user1, user2], :maintainer)
+
+ expect(members).to be_a Array
+ expect(members.size).to eq(2)
+ expect(members.first).to be_a member_type
+ expect(members.first).to be_persisted
+ end
+
+ it 'returns an empty array' do
+ members = described_class.add_users(source, [], :maintainer)
+
+ expect(members).to be_a Array
+ expect(members).to be_empty
+ end
+
+ it 'supports different formats' do
+ list = ['joe@local.test', admin, user1.id, user2.id.to_s]
+
+ members = described_class.add_users(source, list, :maintainer)
+
+ expect(members.size).to eq(4)
+ expect(members.first).to be_invite
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb b/spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb
new file mode 100644
index 00000000000..c92e819db19
--- /dev/null
+++ b/spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'ci_cd_settings delegation' do
+ let(:exclude_attributes) { [] }
+
+ context 'when ci_cd_settings is destroyed but project is not' do
+ it 'allows methods delegated to ci_cd_settings to be nil', :aggregate_failures do
+ project = create(:project)
+ attributes = project.ci_cd_settings.attributes.keys - %w(id project_id) - exclude_attributes
+ project.ci_cd_settings.destroy!
+ project.reload
+ attributes.each do |attr|
+ method = project.respond_to?("ci_#{attr}") ? "ci_#{attr}" : attr
+ expect(project.send(method)).to be_nil, "#{attr} was not nil"
+ end
+ end
+ end
+end
+
+RSpec.shared_examples 'a ci_cd_settings predicate method' do |prefix: ''|
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:project) { create(:project) }
+
+ context 'when ci_cd_settings is nil' do
+ before do
+ allow(project).to receive(:ci_cd_settings).and_return(nil)
+ end
+
+ it 'returns false' do
+ expect(project.send("#{prefix}#{delegated_method}")).to be(false)
+ end
+ end
+
+ context 'when ci_cd_settings is not nil' do
+ where(:delegated_method_return, :subject_return) do
+ true | true
+ false | false
+ end
+
+ with_them do
+ let(:ci_cd_settings_double) { double('ProjectCiCdSetting') }
+
+ before do
+ allow(project).to receive(:ci_cd_settings).and_return(ci_cd_settings_double)
+ allow(ci_cd_settings_double).to receive(delegated_method).and_return(delegated_method_return)
+ end
+
+ it 'returns the expected boolean value' do
+ expect(project.send("#{prefix}#{delegated_method}")).to be(subject_return)
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/wiki_shared_examples.rb b/spec/support/shared_examples/models/wiki_shared_examples.rb
index 2498bf35a09..bc5956e3eec 100644
--- a/spec/support/shared_examples/models/wiki_shared_examples.rb
+++ b/spec/support/shared_examples/models/wiki_shared_examples.rb
@@ -2,6 +2,7 @@
RSpec.shared_examples 'wiki model' do
let_it_be(:user) { create(:user, :commit_email) }
+
let(:wiki_container) { raise NotImplementedError }
let(:wiki_container_without_repo) { raise NotImplementedError }
let(:wiki_lfs_enabled) { false }
@@ -536,4 +537,98 @@ RSpec.shared_examples 'wiki model' do
expect(subject.hook_attrs.keys).to contain_exactly(:web_url, :git_ssh_url, :git_http_url, :path_with_namespace, :default_branch)
end
end
+
+ describe '#default_branch' do
+ subject { wiki.default_branch }
+
+ before do
+ allow(Gitlab::DefaultBranch).to receive(:value).and_return('main')
+ end
+
+ context 'when repository is not created' do
+ let(:wiki_container) { wiki_container_without_repo }
+
+ it 'returns the instance default branch' do
+ expect(subject).to eq 'main'
+ end
+ end
+
+ context 'when repository is empty' do
+ let(:wiki_container) { wiki_container_without_repo }
+
+ before do
+ wiki.repository.create_if_not_exists
+ end
+
+ it 'returns the instance default branch' do
+ expect(subject).to eq 'main'
+ end
+ end
+
+ context 'when repository is not empty' do
+ it 'returns the repository default branch' do
+ wiki.create_page('index', 'test content')
+
+ expect(subject).to eq wiki.repository.root_ref
+ end
+ end
+ end
+
+ describe '#create_wiki_repository' do
+ let(:head_path) { Rails.root.join(TestEnv.repos_path, "#{wiki.disk_path}.git", 'HEAD') }
+ let(:default_branch) { 'foo' }
+
+ before do
+ allow(Gitlab::CurrentSettings).to receive(:default_branch_name).and_return(default_branch)
+ end
+
+ subject { wiki.create_wiki_repository }
+
+ context 'when repository is not created' do
+ let(:wiki_container) { wiki_container_without_repo }
+
+ it 'changes the HEAD reference to the default branch' do
+ expect(wiki.empty?).to eq true
+
+ subject
+
+ expect(File.read(head_path).squish).to eq "ref: refs/heads/#{default_branch}"
+ end
+ end
+
+ context 'when repository is empty' do
+ let(:wiki_container) { wiki_container_without_repo }
+
+ it 'changes the HEAD reference to the default branch' do
+ wiki.repository.create_if_not_exists
+ wiki.repository.raw_repository.write_ref('HEAD', 'refs/heads/bar')
+
+ subject
+
+ expect(File.read(head_path).squish).to eq "ref: refs/heads/#{default_branch}"
+ end
+ end
+
+ context 'when repository is not empty' do
+ before do
+ wiki.create_page('index', 'test content')
+ end
+
+ it 'does nothing when HEAD points to the right branch' do
+ expect(wiki.repository.raw_repository).not_to receive(:write_ref)
+
+ subject
+ end
+
+ context 'when HEAD points to the wrong branch' do
+ it 'rewrites HEAD with the right branch' do
+ wiki.repository.raw_repository.write_ref('HEAD', 'refs/heads/bar')
+
+ subject
+
+ expect(File.read(head_path).squish).to eq "ref: refs/heads/#{default_branch}"
+ end
+ end
+ end
+ end
end
diff --git a/spec/support/shared_examples/namespaces/traversal_examples.rb b/spec/support/shared_examples/namespaces/traversal_examples.rb
index ccc64c80fd4..f09634556c3 100644
--- a/spec/support/shared_examples/namespaces/traversal_examples.rb
+++ b/spec/support/shared_examples/namespaces/traversal_examples.rb
@@ -12,16 +12,18 @@ RSpec.shared_examples 'namespace traversal' do
it "makes a recursive query" do
groups.each do |group|
- expect { group.public_send(recursive_method).load }.to make_queries_matching(/WITH RECURSIVE/)
+ expect { group.public_send(recursive_method).try(:load) }.to make_queries_matching(/WITH RECURSIVE/)
end
end
end
- describe '#root_ancestor' do
- let_it_be(:group) { create(:group) }
- let_it_be(:nested_group) { create(:group, parent: group) }
- let_it_be(:deep_nested_group) { create(:group, parent: nested_group) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:nested_group) { create(:group, parent: group) }
+ let_it_be(:deep_nested_group) { create(:group, parent: nested_group) }
+ let_it_be(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
+ let_it_be(:groups) { [group, nested_group, deep_nested_group, very_deep_nested_group] }
+ describe '#root_ancestor' do
it 'returns the correct root ancestor' do
expect(group.root_ancestor).to eq(group)
expect(nested_group.root_ancestor).to eq(group)
@@ -29,8 +31,6 @@ RSpec.shared_examples 'namespace traversal' do
end
describe '#recursive_root_ancestor' do
- let(:groups) { [group, nested_group, deep_nested_group] }
-
it "is equivalent to #recursive_root_ancestor" do
groups.each do |group|
expect(group.root_ancestor).to eq(group.recursive_root_ancestor)
@@ -40,12 +40,8 @@ RSpec.shared_examples 'namespace traversal' do
end
describe '#self_and_hierarchy' do
- let!(:group) { create(:group, path: 'git_lab') }
- let!(:nested_group) { create(:group, parent: group) }
- let!(:deep_nested_group) { create(:group, parent: nested_group) }
- let!(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
- let!(:another_group) { create(:group, path: 'gitllab') }
- let!(:another_group_nested) { create(:group, path: 'foo', parent: another_group) }
+ let!(:another_group) { create(:group) }
+ let!(:another_group_nested) { create(:group, parent: another_group) }
it 'returns the correct tree' do
expect(group.self_and_hierarchy).to contain_exactly(group, nested_group, deep_nested_group, very_deep_nested_group)
@@ -54,18 +50,11 @@ RSpec.shared_examples 'namespace traversal' do
end
describe '#recursive_self_and_hierarchy' do
- let(:groups) { [group, nested_group, very_deep_nested_group] }
-
it_behaves_like 'recursive version', :self_and_hierarchy
end
end
describe '#ancestors' do
- let_it_be(:group) { create(:group) }
- let_it_be(:nested_group) { create(:group, parent: group) }
- let_it_be(:deep_nested_group) { create(:group, parent: nested_group) }
- let_it_be(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
-
it 'returns the correct ancestors' do
# #reload is called to make sure traversal_ids are reloaded
expect(very_deep_nested_group.reload.ancestors).to contain_exactly(group, nested_group, deep_nested_group)
@@ -75,18 +64,28 @@ RSpec.shared_examples 'namespace traversal' do
end
describe '#recursive_ancestors' do
- let(:groups) { [nested_group, deep_nested_group, very_deep_nested_group] }
+ let_it_be(:groups) { [nested_group, deep_nested_group, very_deep_nested_group] }
it_behaves_like 'recursive version', :ancestors
end
end
- describe '#self_and_ancestors' do
- let(:group) { create(:group) }
- let(:nested_group) { create(:group, parent: group) }
- let(:deep_nested_group) { create(:group, parent: nested_group) }
- let(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
+ describe '#ancestor_ids' do
+ it 'returns the correct ancestor ids' do
+ expect(very_deep_nested_group.ancestor_ids).to contain_exactly(group.id, nested_group.id, deep_nested_group.id)
+ expect(deep_nested_group.ancestor_ids).to contain_exactly(group.id, nested_group.id)
+ expect(nested_group.ancestor_ids).to contain_exactly(group.id)
+ expect(group.ancestor_ids).to be_empty
+ end
+
+ describe '#recursive_ancestor_ids' do
+ let_it_be(:groups) { [nested_group, deep_nested_group, very_deep_nested_group] }
+
+ it_behaves_like 'recursive version', :ancestor_ids
+ end
+ end
+ describe '#self_and_ancestors' do
it 'returns the correct ancestors' do
expect(very_deep_nested_group.self_and_ancestors).to contain_exactly(group, nested_group, deep_nested_group, very_deep_nested_group)
expect(deep_nested_group.self_and_ancestors).to contain_exactly(group, nested_group, deep_nested_group)
@@ -95,19 +94,30 @@ RSpec.shared_examples 'namespace traversal' do
end
describe '#recursive_self_and_ancestors' do
- let(:groups) { [nested_group, deep_nested_group, very_deep_nested_group] }
+ let_it_be(:groups) { [nested_group, deep_nested_group, very_deep_nested_group] }
it_behaves_like 'recursive version', :self_and_ancestors
end
end
+ describe '#self_and_ancestor_ids' do
+ it 'returns the correct ancestor ids' do
+ expect(very_deep_nested_group.self_and_ancestor_ids).to contain_exactly(group.id, nested_group.id, deep_nested_group.id, very_deep_nested_group.id)
+ expect(deep_nested_group.self_and_ancestor_ids).to contain_exactly(group.id, nested_group.id, deep_nested_group.id)
+ expect(nested_group.self_and_ancestor_ids).to contain_exactly(group.id, nested_group.id)
+ expect(group.self_and_ancestor_ids).to contain_exactly(group.id)
+ end
+
+ describe '#recursive_self_and_ancestor_ids' do
+ let_it_be(:groups) { [nested_group, deep_nested_group, very_deep_nested_group] }
+
+ it_behaves_like 'recursive version', :self_and_ancestor_ids
+ end
+ end
+
describe '#descendants' do
- let!(:group) { create(:group, path: 'git_lab') }
- let!(:nested_group) { create(:group, parent: group) }
- let!(:deep_nested_group) { create(:group, parent: nested_group) }
- let!(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
- let!(:another_group) { create(:group, path: 'gitllab') }
- let!(:another_group_nested) { create(:group, path: 'foo', parent: another_group) }
+ let!(:another_group) { create(:group) }
+ let!(:another_group_nested) { create(:group, parent: another_group) }
it 'returns the correct descendants' do
expect(very_deep_nested_group.descendants.to_a).to eq([])
@@ -117,19 +127,13 @@ RSpec.shared_examples 'namespace traversal' do
end
describe '#recursive_descendants' do
- let(:groups) { [group, nested_group, deep_nested_group, very_deep_nested_group] }
-
it_behaves_like 'recursive version', :descendants
end
end
describe '#self_and_descendants' do
- let!(:group) { create(:group, path: 'git_lab') }
- let!(:nested_group) { create(:group, parent: group) }
- let!(:deep_nested_group) { create(:group, parent: nested_group) }
- let!(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
- let!(:another_group) { create(:group, path: 'gitllab') }
- let!(:another_group_nested) { create(:group, path: 'foo', parent: another_group) }
+ let!(:another_group) { create(:group) }
+ let!(:another_group_nested) { create(:group, parent: another_group) }
it 'returns the correct descendants' do
expect(very_deep_nested_group.self_and_descendants).to contain_exactly(very_deep_nested_group)
@@ -139,24 +143,18 @@ RSpec.shared_examples 'namespace traversal' do
end
describe '#recursive_self_and_descendants' do
- let(:groups) { [group, nested_group, deep_nested_group, very_deep_nested_group] }
+ let_it_be(:groups) { [group, nested_group, deep_nested_group] }
it_behaves_like 'recursive version', :self_and_descendants
end
end
describe '#self_and_descendant_ids' do
- let!(:group) { create(:group, path: 'git_lab') }
- let!(:nested_group) { create(:group, parent: group) }
- let!(:deep_nested_group) { create(:group, parent: nested_group) }
-
subject { group.self_and_descendant_ids.pluck(:id) }
- it { is_expected.to contain_exactly(group.id, nested_group.id, deep_nested_group.id) }
+ it { is_expected.to contain_exactly(group.id, nested_group.id, deep_nested_group.id, very_deep_nested_group.id) }
describe '#recursive_self_and_descendant_ids' do
- let(:groups) { [group, nested_group, deep_nested_group] }
-
it_behaves_like 'recursive version', :self_and_descendant_ids
end
end
diff --git a/spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb b/spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb
index 3cdba315d1f..4d142199c95 100644
--- a/spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb
+++ b/spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb
@@ -233,6 +233,7 @@ RSpec.shared_examples 'issuable quick actions' do
context 'when user can update issuable' do
let_it_be(:developer) { create(:user) }
+
let(:note_author) { developer }
before do
@@ -260,6 +261,7 @@ RSpec.shared_examples 'issuable quick actions' do
context 'when user cannot update issuable' do
let_it_be(:non_member) { create(:user) }
+
let(:note_author) { non_member }
it 'applies commands that user can execute' do
diff --git a/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb
index 92849ddf1cb..052fd0622d0 100644
--- a/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb
+++ b/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb
@@ -72,6 +72,24 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type|
end
end
+ it 'shows the time tracking report when link is clicked' do
+ submit_time('/estimate 1w')
+ submit_time('/spend 1d')
+
+ wait_for_requests
+
+ page.within '.time-tracking-component-wrap' do
+ click_link 'Time tracking report'
+
+ wait_for_requests
+ end
+
+ page.within '#time-tracking-report' do
+ expect(find('tbody')).to have_content maintainer.name
+ expect(find('tbody')).to have_content '1d'
+ end
+ end
+
it 'hides the help state when close icon is clicked' do
page.within '.time-tracking-component-wrap' do
find('.help-button').click
diff --git a/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb
index 0530aa8c760..1f68dd7a382 100644
--- a/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb
@@ -12,15 +12,17 @@ RSpec.shared_context 'Debian repository shared context' do |container_type, can_
let_it_be(:user, freeze: true) { create(:user) }
let_it_be(:personal_access_token, freeze: true) { create(:personal_access_token, user: user) }
- let_it_be(:private_distribution, freeze: true) { create("debian_#{container_type}_distribution", container: private_container, codename: 'existing-codename') }
+ let_it_be(:private_distribution, freeze: true) { create("debian_#{container_type}_distribution", :with_file, container: private_container, codename: 'existing-codename') }
let_it_be(:private_component, freeze: true) { create("debian_#{container_type}_component", distribution: private_distribution, name: 'existing-component') }
let_it_be(:private_architecture_all, freeze: true) { create("debian_#{container_type}_architecture", distribution: private_distribution, name: 'all') }
let_it_be(:private_architecture, freeze: true) { create("debian_#{container_type}_architecture", distribution: private_distribution, name: 'existing-arch') }
+ let_it_be(:private_component_file) { create("debian_#{container_type}_component_file", component: private_component, architecture: private_architecture) }
- let_it_be(:public_distribution, freeze: true) { create("debian_#{container_type}_distribution", container: public_container, codename: 'existing-codename') }
+ let_it_be(:public_distribution, freeze: true) { create("debian_#{container_type}_distribution", :with_file, container: public_container, codename: 'existing-codename') }
let_it_be(:public_component, freeze: true) { create("debian_#{container_type}_component", distribution: public_distribution, name: 'existing-component') }
let_it_be(:public_architecture_all, freeze: true) { create("debian_#{container_type}_architecture", distribution: public_distribution, name: 'all') }
let_it_be(:public_architecture, freeze: true) { create("debian_#{container_type}_architecture", distribution: public_distribution, name: 'existing-arch') }
+ let_it_be(:public_component_file) { create("debian_#{container_type}_component_file", component: public_component, architecture: public_architecture) }
if container_type == :group
let_it_be(:private_project) { create(:project, :private, group: private_container) }
@@ -40,14 +42,15 @@ RSpec.shared_context 'Debian repository shared context' do |container_type, can_
let(:visibility_level) { :public }
let(:distribution) { { private: private_distribution, public: public_distribution }[visibility_level] }
+ let(:architecture) { { private: private_architecture, public: public_architecture }[visibility_level] }
+ let(:component) { { private: private_component, public: public_component }[visibility_level] }
+ let(:component_file) { { private: private_component_file, public: public_component_file }[visibility_level] }
- let(:component) { 'main' }
- let(:architecture) { 'amd64' }
let(:source_package) { 'sample' }
let(:letter) { source_package[0..2] == 'lib' ? source_package[0..3] : source_package[0] }
let(:package_name) { 'libsample0' }
let(:package_version) { '1.2.3~alpha2' }
- let(:file_name) { "#{package_name}_#{package_version}_#{architecture}.deb" }
+ let(:file_name) { "#{package_name}_#{package_version}_#{architecture.name}.deb" }
let(:method) { :get }
diff --git a/spec/support/shared_examples/requests/api/graphql/noteable_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/noteable_shared_examples.rb
index 9cf5bc04f65..7e1f4500779 100644
--- a/spec/support/shared_examples/requests/api/graphql/noteable_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/noteable_shared_examples.rb
@@ -31,6 +31,28 @@ RSpec.shared_examples 'a noteable graphql type we can query' do
expect(graphql_data_at(*path_to_noteable, :discussions, :nodes))
.to match_array(expected)
end
+
+ it 'can fetch discussion noteable' do
+ create(discussion_factory, project: project, noteable: noteable)
+ fields =
+ <<-QL.strip_heredoc
+ discussions {
+ nodes {
+ noteable {
+ __typename
+ ... on #{noteable.class.name.demodulize} {
+ id
+ }
+ }
+ }
+ }
+ QL
+
+ post_graphql(query(fields), current_user: current_user)
+
+ data = graphql_data_at(*path_to_noteable, :discussions, :nodes, :noteable, :id)
+ expect(data[0]).to eq(global_id_of(noteable))
+ end
end
describe '.notes' do
diff --git a/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb
index 585c4fb8a4e..1ad38a17f9c 100644
--- a/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: true
-RSpec.shared_examples 'rejects helm packages access' do |user_type, status, add_member = true|
+RSpec.shared_examples 'rejects helm packages access' do |user_type, status|
context "for user type #{user_type}" do
before do
- project.send("add_#{user_type}", user) if add_member && user_type != :anonymous
+ project.send("add_#{user_type}", user) if user_type != :anonymous && user_type != :not_a_member
end
it_behaves_like 'returning response status', status
@@ -18,19 +18,170 @@ RSpec.shared_examples 'rejects helm packages access' do |user_type, status, add_
end
end
-RSpec.shared_examples 'process helm download content request' do |user_type, status, add_member = true|
+RSpec.shared_examples 'process helm service index request' do |user_type, status|
context "for user type #{user_type}" do
before do
- project.send("add_#{user_type}", user) if add_member && user_type != :anonymous
+ project.send("add_#{user_type}", user) if user_type != :anonymous && user_type != :not_a_member
end
- it_behaves_like 'returning response status', status
+ it 'returns a valid YAML response', :aggregate_failures do
+ subject
+
+ expect(response).to have_gitlab_http_status(status)
+
+ expect(response.media_type).to eq('text/yaml')
+ expect(response.body).to start_with("---\napiVersion: v1\nentries:\n")
+
+ yaml_response = YAML.safe_load(response.body)
+
+ expect(yaml_response.keys).to contain_exactly('apiVersion', 'entries', 'generated', 'serverInfo')
+ expect(yaml_response['entries']).to be_a(Hash)
+ expect(yaml_response['entries'].keys).to contain_exactly(package.name)
+ expect(yaml_response['serverInfo']).to eq({ 'contextPath' => "/api/v4/projects/#{project.id}/packages/helm" })
+
+ package_entry = yaml_response['entries'][package.name]
+
+ expect(package_entry.length).to eq(1)
+ expect(package_entry.first.keys).to contain_exactly('name', 'version', 'apiVersion', 'created', 'digest', 'urls')
+ expect(package_entry.first['digest']).to eq('fd2b2fa0329e80a2a602c2bb3b40608bcd6ee5cf96cf46fd0d2800a4c129c9db')
+ expect(package_entry.first['urls']).to eq(["charts/#{package.name}-#{package.version}.tgz"])
+ end
+ end
+end
+
+RSpec.shared_examples 'process helm workhorse authorization' do |user_type, status, test_bypass: false|
+ context "for user type #{user_type}" do
+ before do
+ project.send("add_#{user_type}", user) if user_type != :anonymous && user_type != :not_a_member
+ end
+
+ it 'has the proper status and content type' do
+ subject
+
+ expect(response).to have_gitlab_http_status(status)
+ expect(response.media_type).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ end
+
+ context 'with a request that bypassed gitlab-workhorse' do
+ let(:headers) do
+ basic_auth_header(user.username, personal_access_token.token)
+ .merge(workhorse_headers)
+ .tap { |h| h.delete(Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER) }
+ end
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ it_behaves_like 'returning response status', :forbidden
+ end
+ end
+end
+
+RSpec.shared_examples 'process helm upload' do |user_type, status|
+ shared_examples 'creates helm package files' do
+ it 'creates package files' do
+ expect(::Packages::Helm::ExtractionWorker).to receive(:perform_async).once
+ expect { subject }
+ .to change { project.packages.count }.by(1)
+ .and change { Packages::PackageFile.count }.by(1)
+ expect(response).to have_gitlab_http_status(status)
+
+ package_file = project.packages.last.package_files.reload.last
+ expect(package_file.file_name).to eq('package.tgz')
+ end
+ end
+
+ context "for user type #{user_type}" do
+ before do
+ project.send("add_#{user_type}", user) if user_type != :anonymous && user_type != :not_a_member
+ end
+
+ context 'with object storage disabled' do
+ before do
+ stub_package_file_object_storage(enabled: false)
+ end
+
+ context 'without a file from workhorse' do
+ let(:send_rewritten_field) { false }
+
+ it_behaves_like 'returning response status', :bad_request
+ end
+
+ context 'with correct params' do
+ it_behaves_like 'package workhorse uploads'
+ it_behaves_like 'creates helm package files'
+ it_behaves_like 'a package tracking event', 'API::HelmPackages', 'push_package'
+ end
+ end
+
+ context 'with object storage enabled' do
+ let(:tmp_object) do
+ fog_connection.directories.new(key: 'packages').files.create( # rubocop:disable Rails/SaveBang
+ key: "tmp/uploads/#{file_name}",
+ body: 'content'
+ )
+ end
+
+ let(:fog_file) { fog_to_uploaded_file(tmp_object) }
+ let(:params) { { chart: fog_file, 'chart.remote_id' => file_name } }
+
+ context 'and direct upload enabled' do
+ let(:fog_connection) do
+ stub_package_file_object_storage(direct_upload: true)
+ end
+
+ it_behaves_like 'creates helm package files'
+
+ ['123123', '../../123123'].each do |remote_id|
+ context "with invalid remote_id: #{remote_id}" do
+ let(:params) do
+ {
+ chart: fog_file,
+ 'chart.remote_id' => remote_id
+ }
+ end
+
+ it_behaves_like 'returning response status', :forbidden
+ end
+ end
+ end
+
+ context 'and direct upload disabled' do
+ context 'and background upload disabled' do
+ let(:fog_connection) do
+ stub_package_file_object_storage(direct_upload: false, background_upload: false)
+ end
+
+ it_behaves_like 'creates helm package files'
+ end
+
+ context 'and background upload enabled' do
+ let(:fog_connection) do
+ stub_package_file_object_storage(direct_upload: false, background_upload: true)
+ end
+
+ it_behaves_like 'creates helm package files'
+ end
+ end
+ end
+
+ it_behaves_like 'background upload schedules a file migration'
+ end
+end
+
+RSpec.shared_examples 'process helm download content request' do |user_type, status|
+ context "for user type #{user_type}" do
+ before do
+ project.send("add_#{user_type}", user) if user_type != :anonymous && user_type != :not_a_member
+ end
it_behaves_like 'a package tracking event', 'API::HelmPackages', 'pull_package'
- it 'returns a valid package archive' do
+ it 'returns expected status and a valid package archive' do
subject
+ expect(response).to have_gitlab_http_status(status)
expect(response.media_type).to eq('application/octet-stream')
end
end
@@ -51,3 +202,69 @@ RSpec.shared_examples 'rejects helm access with unknown project id' do
end
end
end
+
+RSpec.shared_examples 'handling helm chart index requests' do
+ context 'with valid project' do
+ subject { get api(url), headers: headers }
+
+ using RSpec::Parameterized::TableSyntax
+
+ context 'personal token' do
+ where(:visibility, :user_role, :shared_examples_name, :expected_status) do
+ :public | :guest | 'process helm service index request' | :success
+ :public | :not_a_member | 'process helm service index request' | :success
+ :public | :anonymous | 'process helm service index request' | :success
+ :private | :reporter | 'process helm service index request' | :success
+ :private | :guest | 'rejects helm packages access' | :forbidden
+ :private | :not_a_member | 'rejects helm packages access' | :not_found
+ :private | :anonymous | 'rejects helm packages access' | :unauthorized
+ end
+
+ with_them do
+ let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, personal_access_token.token) }
+
+ before do
+ project.update!(visibility: visibility.to_s)
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status]
+ end
+ end
+
+ context 'when an invalid token is passed' do
+ let(:headers) { basic_auth_header(user.username, 'wrong') }
+
+ it_behaves_like 'returning response status', :unauthorized
+ end
+
+ context 'with job token' do
+ where(:visibility, :user_role, :shared_examples_name, :expected_status) do
+ :public | :guest | 'process helm service index request' | :success
+ :public | :not_a_member | 'process helm service index request' | :success
+ :public | :anonymous | 'process helm service index request' | :success
+ :private | :reporter | 'process helm service index request' | :success
+ :private | :guest | 'rejects helm packages access' | :forbidden
+ :private | :not_a_member | 'rejects helm packages access' | :not_found
+ :private | :anonymous | 'rejects helm packages access' | :unauthorized
+ end
+
+ with_them do
+ let_it_be(:ci_build) { create(:ci_build, project: project, user: user, status: :running) }
+
+ let(:headers) { user_role == :anonymous ? {} : job_basic_auth_header(ci_build) }
+
+ before do
+ project.update!(visibility: visibility.to_s)
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status]
+ end
+ end
+ end
+
+ it_behaves_like 'deploy token for package GET requests'
+
+ it_behaves_like 'rejects helm access with unknown project id' do
+ subject { get api(url) }
+ end
+end
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 617fdecbb5b..878cbc10a24 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
@@ -136,8 +136,8 @@ RSpec.shared_examples 'process nuget workhorse authorization' do |user_type, sta
end
end
-RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member = true|
- RSpec.shared_examples 'creates nuget package files' do
+RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member = true, symbol_package = false|
+ shared_examples 'creates nuget package files' do
it 'creates package files' do
expect(::Packages::Nuget::ExtractionWorker).to receive(:perform_async).once
expect { subject }
@@ -146,7 +146,7 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
expect(response).to have_gitlab_http_status(status)
package_file = target.packages.last.package_files.reload.last
- expect(package_file.file_name).to eq('package.nupkg')
+ expect(package_file.file_name).to eq(file_name)
end
end
@@ -169,7 +169,12 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
context 'with correct params' do
it_behaves_like 'package workhorse uploads'
it_behaves_like 'creates nuget package files'
- it_behaves_like 'a package tracking event', 'API::NugetPackages', 'push_package'
+
+ if symbol_package
+ it_behaves_like 'a package tracking event', 'API::NugetPackages', 'push_symbol_package'
+ else
+ it_behaves_like 'a package tracking event', 'API::NugetPackages', 'push_package'
+ end
end
end
@@ -300,6 +305,18 @@ RSpec.shared_examples 'process nuget download content request' do |user_type, st
it_behaves_like 'rejects nuget packages access', :anonymous, :not_found
end
+ context 'with symbol package' do
+ let(:format) { 'snupkg' }
+
+ it 'returns a valid package archive' do
+ subject
+
+ expect(response.media_type).to eq('application/octet-stream')
+ end
+
+ it_behaves_like 'a package tracking event', 'API::NugetPackages', 'pull_symbol_package'
+ end
+
context 'with lower case package name' do
let_it_be(:package_name) { 'dummy.package' }
@@ -407,3 +424,114 @@ RSpec.shared_examples 'rejects nuget access with unknown target id' do
end
end
end
+
+RSpec.shared_examples 'nuget authorize upload endpoint' do
+ using RSpec::Parameterized::TableSyntax
+
+ context 'with valid project' do
+ where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
+ 'PUBLIC' | :developer | true | true | 'process nuget workhorse authorization' | :success
+ 'PUBLIC' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :developer | false | true | 'rejects nuget packages access' | :forbidden
+ 'PUBLIC' | :guest | false | true | 'rejects nuget packages access' | :forbidden
+ 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :developer | true | true | 'process nuget workhorse authorization' | :success
+ 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ '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
+ 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
+ end
+
+ with_them do
+ let(:token) { user_token ? personal_access_token.token : 'wrong' }
+ let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
+ let(:headers) { user_headers.merge(workhorse_headers) }
+
+ before do
+ update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false))
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
+ end
+ end
+
+ it_behaves_like 'deploy token for package uploads'
+
+ it_behaves_like 'job token for package uploads', authorize_endpoint: true do
+ let_it_be(:job) { create(:ci_build, :running, user: user, project: project) }
+ end
+
+ it_behaves_like 'rejects nuget access with unknown target id'
+
+ it_behaves_like 'rejects nuget access with invalid target id'
+end
+
+RSpec.shared_examples 'nuget upload endpoint' do |symbol_package: false|
+ using RSpec::Parameterized::TableSyntax
+
+ context 'with valid project' do
+ where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
+ 'PUBLIC' | :developer | true | true | 'process nuget upload' | :created
+ 'PUBLIC' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :developer | false | true | 'rejects nuget packages access' | :forbidden
+ 'PUBLIC' | :guest | false | true | 'rejects nuget packages access' | :forbidden
+ 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :developer | true | true | 'process nuget upload' | :created
+ 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ '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
+ 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
+ end
+
+ with_them do
+ let(:token) { user_token ? personal_access_token.token : 'wrong' }
+ let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
+ let(:headers) { user_headers.merge(workhorse_headers) }
+ let(:snowplow_gitlab_standard_context) { { project: project, user: user, namespace: project.namespace } }
+
+ before do
+ update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false))
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member], symbol_package
+ end
+ end
+
+ it_behaves_like 'deploy token for package uploads'
+
+ it_behaves_like 'job token for package uploads' do
+ let_it_be(:job) { create(:ci_build, :running, user: user, project: project) }
+ end
+
+ it_behaves_like 'rejects nuget access with unknown target id'
+
+ it_behaves_like 'rejects nuget access with invalid target id'
+
+ context 'file size above maximum limit' do
+ let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_headers) }
+
+ before do
+ allow_next_instance_of(UploadedFile) do |uploaded_file|
+ allow(uploaded_file).to receive(:size).and_return(project.actual_limits.nuget_max_file_size + 1)
+ end
+ end
+
+ it_behaves_like 'returning response status', :bad_request
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/packages_shared_examples.rb b/spec/support/shared_examples/requests/api/packages_shared_examples.rb
index 42c29084d7b..ecde4ee8565 100644
--- a/spec/support/shared_examples/requests/api/packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/packages_shared_examples.rb
@@ -100,7 +100,7 @@ RSpec.shared_examples 'job token for package GET requests' do
end
end
-RSpec.shared_examples 'job token for package uploads' do |authorize_endpoint: false|
+RSpec.shared_examples 'job token for package uploads' do |authorize_endpoint: false, accept_invalid_username: false|
context 'with job token headers' do
let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, job.token).merge(workhorse_headers) }
@@ -133,7 +133,11 @@ RSpec.shared_examples 'job token for package uploads' do |authorize_endpoint: fa
context 'invalid user' do
let(:headers) { basic_auth_header('foo', job.token).merge(workhorse_headers) }
- it_behaves_like 'returning response status', :unauthorized
+ if accept_invalid_username
+ it_behaves_like 'returning response status', :success
+ else
+ it_behaves_like 'returning response status', :unauthorized
+ end
end
end
end
@@ -143,7 +147,7 @@ RSpec.shared_examples 'a package tracking event' do |category, action|
stub_feature_flags(collect_package_events: true)
end
- it "creates a gitlab tracking event #{action}", :snowplow do
+ it "creates a gitlab tracking event #{action}", :snowplow, :aggregate_failures do
expect { subject }.to change { Packages::Event.count }.by(1)
expect_snowplow_event(category: category, action: action, **snowplow_gitlab_standard_context)
diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb
index 218a3462c35..92a7d7ab3a3 100644
--- a/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb
+++ b/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb
@@ -13,7 +13,7 @@ RSpec.shared_examples 'creates an alert management alert or errors' do
it 'executes the alert service hooks' do
expect_next_instance_of(AlertManagement::Alert) do |alert|
- expect(alert).to receive(:execute_services)
+ expect(alert).to receive(:execute_integrations)
end
subject
@@ -84,7 +84,7 @@ end
# - `alert`, the alert for which events should be incremented
RSpec.shared_examples 'adds an alert management alert event' do
specify do
- expect(alert).not_to receive(:execute_services)
+ expect(alert).not_to receive(:execute_integrations)
expect { subject }.to change { alert.reload.events }.by(1)
diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/incident_creation_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/incident_creation_shared_examples.rb
index c6ac07b6dd5..98834f01ce2 100644
--- a/spec/support/shared_examples/services/alert_management/alert_processing/incident_creation_shared_examples.rb
+++ b/spec/support/shared_examples/services/alert_management/alert_processing/incident_creation_shared_examples.rb
@@ -20,7 +20,7 @@ end
RSpec.shared_examples 'processes incident issues' do |with_issue: false|
before do
allow_next_instance_of(AlertManagement::Alert) do |alert|
- allow(alert).to receive(:execute_services)
+ allow(alert).to receive(:execute_integrations)
end
end
diff --git a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
index ba176b616c3..eafcbd77040 100644
--- a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
@@ -157,8 +157,13 @@ end
RSpec.shared_examples 'a container registry auth service' do
include_context 'container registry auth service context'
+ before do
+ stub_feature_flags(container_registry_migration_phase1: false)
+ end
+
describe '#full_access_token' do
let_it_be(:project) { create(:project) }
+
let(:token) { described_class.full_access_token(project.full_path) }
subject { { token: token } }
@@ -172,6 +177,7 @@ RSpec.shared_examples 'a container registry auth service' do
describe '#pull_access_token' do
let_it_be(:project) { create(:project) }
+
let(:token) { described_class.pull_access_token(project.full_path) }
subject { { token: token } }
@@ -432,6 +438,7 @@ RSpec.shared_examples 'a container registry auth service' do
context 'for external user' do
context 'disallow anyone to pull or push images' do
let_it_be(:current_user) { create(:user, external: true) }
+
let(:current_params) do
{ scopes: ["repository:#{project.full_path}:pull,push"] }
end
@@ -442,6 +449,7 @@ RSpec.shared_examples 'a container registry auth service' do
context 'disallow anyone to delete images' do
let_it_be(:current_user) { create(:user, external: true) }
+
let(:current_params) do
{ scopes: ["repository:#{project.full_path}:*"] }
end
@@ -452,6 +460,7 @@ RSpec.shared_examples 'a container registry auth service' do
context 'disallow anyone to delete images since registry 2.7' do
let_it_be(:current_user) { create(:user, external: true) }
+
let(:current_params) do
{ scopes: ["repository:#{project.full_path}:delete"] }
end
@@ -620,6 +629,22 @@ RSpec.shared_examples 'a container registry auth service' do
end
end
end
+
+ context 'for project with private container registry' do
+ let_it_be(:project, reload: true) { create(:project, :public) }
+
+ before do
+ project.project_feature.update!(container_registry_access_level: ProjectFeature::PRIVATE)
+ end
+
+ it_behaves_like 'pullable for being team member'
+
+ context 'when you are admin' do
+ let_it_be(:current_user) { create(:admin) }
+
+ it_behaves_like 'pullable for being team member'
+ end
+ end
end
context 'when pushing' do
diff --git a/spec/support/shared_examples/services/jira_import/user_mapper_services_shared_examples.rb b/spec/support/shared_examples/services/jira_import/user_mapper_services_shared_examples.rb
index cbe5c7d89db..0151723793e 100644
--- a/spec/support/shared_examples/services/jira_import/user_mapper_services_shared_examples.rb
+++ b/spec/support/shared_examples/services/jira_import/user_mapper_services_shared_examples.rb
@@ -3,7 +3,7 @@
RSpec.shared_examples 'mapping jira users' do
let(:client) { double }
- let_it_be(:jira_service) { create(:jira_service, project: project, active: true) }
+ let_it_be(:jira_integration) { create(:jira_integration, project: project, active: true) }
before do
allow(subject).to receive(:client).and_return(client)
diff --git a/spec/support/shared_examples/services/packages_shared_examples.rb b/spec/support/shared_examples/services/packages_shared_examples.rb
index 72878e925dc..6bc4f171d9c 100644
--- a/spec/support/shared_examples/services/packages_shared_examples.rb
+++ b/spec/support/shared_examples/services/packages_shared_examples.rb
@@ -43,6 +43,7 @@ end
RSpec.shared_examples 'assigns status to package' do
context 'with status param' do
let_it_be(:status) { 'hidden' }
+
let(:params) { super().merge(status: status) }
it 'assigns the status to the package' do
diff --git a/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb b/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
index 275ddebc18c..14af35e58b7 100644
--- a/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
@@ -123,9 +123,10 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
.with(repository.raw)
.and_raise(Gitlab::Git::CommandError)
- result = subject.execute
+ expect do
+ subject.execute
+ end.to raise_error(Gitlab::Git::CommandError)
- expect(result).to be_error
expect(project).not_to be_repository_read_only
expect(project.repository_storage).to eq('default')
expect(repository_storage_move).to be_failed
@@ -149,9 +150,10 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
expect(original_repository_double).to receive(:remove)
.and_raise(Gitlab::Git::CommandError)
- result = subject.execute
+ expect do
+ subject.execute
+ end.to raise_error(Gitlab::Git::CommandError)
- expect(result).to be_error
expect(repository_storage_move).to be_cleanup_failed
end
end
@@ -170,9 +172,10 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
allow(repository_double).to receive(:checksum)
.and_return('not matching checksum')
- result = subject.execute
+ expect do
+ subject.execute
+ end.to raise_error(UpdateRepositoryStorageMethods::Error, /Failed to verify \w+ repository checksum from \w+ to not matching checksum/)
- expect(result).to be_error
expect(project).not_to be_repository_read_only
expect(project.repository_storage).to eq('default')
end
diff --git a/spec/support/shared_examples/services/service_ping/complete_service_ping_payload_shared_examples.rb b/spec/support/shared_examples/services/service_ping/complete_service_ping_payload_shared_examples.rb
new file mode 100644
index 00000000000..8dcff99fb6f
--- /dev/null
+++ b/spec/support/shared_examples/services/service_ping/complete_service_ping_payload_shared_examples.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'complete service ping payload' do
+ it_behaves_like 'service ping payload with all expected metrics' do
+ let(:expected_metrics) do
+ standard_metrics + subscription_metrics + operational_metrics + optional_metrics
+ end
+ end
+end
diff --git a/spec/support/shared_examples/services/service_ping/service_ping_payload_with_all_expected_metrics_shared_examples.rb b/spec/support/shared_examples/services/service_ping/service_ping_payload_with_all_expected_metrics_shared_examples.rb
new file mode 100644
index 00000000000..535e7291b7e
--- /dev/null
+++ b/spec/support/shared_examples/services/service_ping/service_ping_payload_with_all_expected_metrics_shared_examples.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'service ping payload with all expected metrics' do
+ specify do
+ aggregate_failures do
+ expected_metrics.each do |metric|
+ is_expected.to have_usage_metric metric['key_path']
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/services/service_ping/service_ping_payload_without_restricted_metrics_shared_examples.rb b/spec/support/shared_examples/services/service_ping/service_ping_payload_without_restricted_metrics_shared_examples.rb
new file mode 100644
index 00000000000..9f18174cbc7
--- /dev/null
+++ b/spec/support/shared_examples/services/service_ping/service_ping_payload_without_restricted_metrics_shared_examples.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'service ping payload without restricted metrics' do
+ specify do
+ aggregate_failures do
+ restricted_metrics.each do |metric|
+ is_expected.not_to have_usage_metric metric['key_path']
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/services/snippets_shared_examples.rb b/spec/support/shared_examples/services/snippets_shared_examples.rb
index 0c4db7ded69..5a44f739b27 100644
--- a/spec/support/shared_examples/services/snippets_shared_examples.rb
+++ b/spec/support/shared_examples/services/snippets_shared_examples.rb
@@ -1,23 +1,6 @@
# frozen_string_literal: true
RSpec.shared_examples 'checking spam' do
- let(:request) { double(:request, headers: headers) }
- let(:headers) { nil }
- let(:api) { true }
- let(:captcha_response) { 'abc123' }
- let(:spam_log_id) { 1 }
- let(:disable_spam_action_service) { false }
-
- let(:extra_opts) do
- {
- request: request,
- api: api,
- captcha_response: captcha_response,
- spam_log_id: spam_log_id,
- disable_spam_action_service: disable_spam_action_service
- }
- end
-
before do
allow_next_instance_of(UserAgentDetailService) do |instance|
allow(instance).to receive(:create)
@@ -25,73 +8,20 @@ RSpec.shared_examples 'checking spam' do
end
it 'executes SpamActionService' do
- spam_params = Spam::SpamParams.new(
- api: api,
- captcha_response: captcha_response,
- spam_log_id: spam_log_id
- )
expect_next_instance_of(
Spam::SpamActionService,
{
spammable: kind_of(Snippet),
- request: request,
+ spam_params: spam_params,
user: an_instance_of(User),
action: action
}
) do |instance|
- expect(instance).to receive(:execute).with(spam_params: spam_params)
+ expect(instance).to receive(:execute)
end
subject
end
-
- context 'when CAPTCHA arguments are passed in the headers' do
- let(:headers) do
- {
- 'X-GitLab-Spam-Log-Id' => spam_log_id,
- 'X-GitLab-Captcha-Response' => captcha_response
- }
- end
-
- let(:extra_opts) do
- {
- request: request,
- api: api,
- disable_spam_action_service: disable_spam_action_service
- }
- end
-
- it 'executes the SpamActionService correctly' do
- spam_params = Spam::SpamParams.new(
- api: api,
- captcha_response: captcha_response,
- spam_log_id: spam_log_id
- )
- expect_next_instance_of(
- Spam::SpamActionService,
- {
- spammable: kind_of(Snippet),
- request: request,
- user: an_instance_of(User),
- action: action
- }
- ) do |instance|
- expect(instance).to receive(:execute).with(spam_params: spam_params)
- end
-
- subject
- end
- end
-
- context 'when spam action service is disabled' do
- let(:disable_spam_action_service) { true }
-
- it 'request parameter is not passed to the service' do
- expect(Spam::SpamActionService).not_to receive(:new)
-
- subject
- end
- end
end
shared_examples 'invalid params error response' do
diff --git a/spec/support/shared_examples/services/wikis/create_attachment_service_shared_examples.rb b/spec/support/shared_examples/services/wikis/create_attachment_service_shared_examples.rb
index 555a6d5eed0..1646c18a0ed 100644
--- a/spec/support/shared_examples/services/wikis/create_attachment_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/wikis/create_attachment_service_shared_examples.rb
@@ -25,6 +25,25 @@ RSpec.shared_examples 'Wikis::CreateAttachmentService#execute' do |container_typ
container.add_developer(user)
end
+ context 'creates wiki repository if it does not exist' do
+ let(:container) { create(container_type) } # rubocop:disable Rails/SaveBang
+
+ it 'creates wiki repository' do
+ expect { service.execute }.to change { container.wiki.repository.exists? }.to(true)
+ end
+
+ context 'if an error is raised creating the repository' do
+ it 'catches error and return gracefully' do
+ allow(container.wiki).to receive(:repository_exists?).and_return(false)
+
+ result = service.execute
+
+ expect(result[:status]).to eq :error
+ expect(result[:message]).to eq 'Error creating the wiki repository'
+ end
+ end
+ end
+
context 'creates branch if it does not exists' do
let(:branch_name) { 'new_branch' }
let(:opts) { file_opts.merge(branch_name: branch_name) }
diff --git a/spec/support/shared_examples/workers/in_product_marketing_email_shared_example.rb b/spec/support/shared_examples/workers/in_product_marketing_email_shared_example.rb
deleted file mode 100644
index c4391f61369..00000000000
--- a/spec/support/shared_examples/workers/in_product_marketing_email_shared_example.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples 'in-product marketing email' do
- before do
- stub_application_setting(in_product_marketing_emails_enabled: in_product_marketing_emails_enabled)
- stub_experiment(in_product_marketing_emails: experiment_active)
- allow(::Gitlab).to receive(:com?).and_return(is_gitlab_com)
- end
-
- it 'executes the email service service' do
- expect(Namespaces::InProductMarketingEmailsService).to receive(:send_for_all_tracks_and_intervals).exactly(executes_service).times
-
- subject.perform
- end
-end