summaryrefslogtreecommitdiff
path: root/spec/support/shared_examples
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-06-16 18:25:58 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-06-16 18:25:58 +0000
commita5f4bba440d7f9ea47046a0a561d49adf0a1e6d4 (patch)
treefb69158581673816a8cd895f9d352dcb3c678b1e /spec/support/shared_examples
parentd16b2e8639e99961de6ddc93909f3bb5c1445ba1 (diff)
downloadgitlab-ce-a5f4bba440d7f9ea47046a0a561d49adf0a1e6d4.tar.gz
Add latest changes from gitlab-org/gitlab@14-0-stable-eev14.0.0-rc42
Diffstat (limited to 'spec/support/shared_examples')
-rw-r--r--spec/support/shared_examples/ci/badge_template_shared_examples.rb57
-rw-r--r--spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/features/discussion_comments_shared_example.rb4
-rw-r--r--spec/support/shared_examples/features/integrations/user_activates_mattermost_slash_command_integration_shared_examples.rb34
-rw-r--r--spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb12
-rw-r--r--spec/support/shared_examples/features/variable_list_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/finders/assignees_filter_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb6
-rw-r--r--spec/support/shared_examples/graphql/mutations/resolves_subscription_shared_examples.rb53
-rw-r--r--spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb74
-rw-r--r--spec/support/shared_examples/lib/gitlab/experimentation_shared_examples.rb20
-rw-r--r--spec/support/shared_examples/models/chat_integration_shared_examples.rb (renamed from spec/support/shared_examples/models/chat_service_shared_examples.rb)84
-rw-r--r--spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb (renamed from spec/support/shared_examples/models/slack_mattermost_notifications_shared_examples.rb)33
-rw-r--r--spec/support/shared_examples/models/concerns/timebox_shared_examples.rb39
-rw-r--r--spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb (renamed from spec/support/shared_examples/models/chat_slash_commands_shared_examples.rb)2
-rw-r--r--spec/support/shared_examples/models/mentionable_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/packages/debian/component_file_shared_example.rb18
-rw-r--r--spec/support/shared_examples/models/packages/debian/distribution_key_shared_examples.rb49
-rw-r--r--spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb1
-rw-r--r--spec/support/shared_examples/namespaces/linear_traversal_examples.rb23
-rw-r--r--spec/support/shared_examples/namespaces/traversal_examples.rb38
-rw-r--r--spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb12
-rw-r--r--spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb222
-rw-r--r--spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb27
-rw-r--r--spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb53
-rw-r--r--spec/support/shared_examples/requests/api/packages_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb173
-rw-r--r--spec/support/shared_examples/requests/api/resource_label_events_api_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/tracking_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/clusters/parse_cluster_applications_artifact_shared_examples.rb89
-rw-r--r--spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb166
-rw-r--r--spec/support/shared_examples/services/users/build_service_shared_examples.rb125
-rw-r--r--spec/support/shared_examples/uncached_response_shared_examples.rb12
33 files changed, 1168 insertions, 280 deletions
diff --git a/spec/support/shared_examples/ci/badge_template_shared_examples.rb b/spec/support/shared_examples/ci/badge_template_shared_examples.rb
new file mode 100644
index 00000000000..94aec33ecc2
--- /dev/null
+++ b/spec/support/shared_examples/ci/badge_template_shared_examples.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'a badge template' do |badge_type|
+ describe '#key_text' do
+ it "says #{badge_type} by default" do
+ expect(template.key_text).to eq(badge_type)
+ end
+
+ context 'when custom key_text is defined' do
+ before do
+ allow(badge).to receive(:customization).and_return({ key_text: "custom text" })
+ end
+
+ it 'returns custom value' do
+ expect(template.key_text).to eq("custom text")
+ end
+
+ context 'when its size is larger than the max allowed value' do
+ before do
+ allow(badge).to receive(:customization).and_return({ key_text: 't' * (::Gitlab::Ci::Badge::Template::MAX_KEY_TEXT_SIZE + 1) } )
+ end
+
+ it 'returns default value' do
+ expect(template.key_text).to eq(badge_type)
+ end
+ end
+ end
+ end
+
+ describe '#key_width' do
+ let_it_be(:default_key_width) { ::Gitlab::Ci::Badge::Template::DEFAULT_KEY_WIDTH }
+
+ it 'is fixed by default' do
+ expect(template.key_width).to eq(default_key_width)
+ end
+
+ context 'when custom key_width is defined' do
+ before do
+ allow(badge).to receive(:customization).and_return({ key_width: 101 })
+ end
+
+ it 'returns custom value' do
+ expect(template.key_width).to eq(101)
+ end
+
+ context 'when it is larger than the max allowed value' do
+ before do
+ allow(badge).to receive(:customization).and_return({ key_width: ::Gitlab::Ci::Badge::Template::MAX_KEY_WIDTH + 1 })
+ end
+
+ it 'returns default value' do
+ expect(template.key_width).to eq(default_key_width)
+ end
+ end
+ end
+ end
+end
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 cfee26a0d6a..9af35c189d0 100644
--- a/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
@@ -298,7 +298,7 @@ RSpec.shared_examples 'wiki controller actions' do
expect(response.headers['Content-Disposition']).to match(/^inline/)
expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq('true')
expect(response.cache_control[:public]).to be(false)
- expect(response.cache_control[:extras]).to include('no-store')
+ expect(response.headers['Cache-Control']).to eq('no-store')
end
end
end
@@ -486,7 +486,7 @@ RSpec.shared_examples 'wiki controller actions' do
end.not_to change { wiki.list_pages.size }
expect(response).to render_template('shared/wikis/edit')
- expect(assigns(:error).message).to eq('Could not delete wiki page')
+ expect(assigns(:error)).to eq('Could not delete wiki page')
end
end
end
diff --git a/spec/support/shared_examples/features/discussion_comments_shared_example.rb b/spec/support/shared_examples/features/discussion_comments_shared_example.rb
index 808e0be6be2..ff2878f77b4 100644
--- a/spec/support/shared_examples/features/discussion_comments_shared_example.rb
+++ b/spec/support/shared_examples/features/discussion_comments_shared_example.rb
@@ -11,6 +11,8 @@ RSpec.shared_examples 'thread comments for commit and snippet' do |resource_name
let(:comment) { 'My comment' }
it 'clicking "Comment" will post a comment' do
+ wait_for_all_requests
+
expect(page).to have_selector toggle_selector
find("#{form_selector} .note-textarea").send_keys(comment)
@@ -29,6 +31,8 @@ RSpec.shared_examples 'thread comments for commit and snippet' do |resource_name
find("#{form_selector} .note-textarea").send_keys(comment)
find(toggle_selector).click
+
+ wait_for_all_requests
end
it 'has a "Comment" item (selected by default) and "Start thread" item' do
diff --git a/spec/support/shared_examples/features/integrations/user_activates_mattermost_slash_command_integration_shared_examples.rb b/spec/support/shared_examples/features/integrations/user_activates_mattermost_slash_command_integration_shared_examples.rb
new file mode 100644
index 00000000000..cfa043322db
--- /dev/null
+++ b/spec/support/shared_examples/features/integrations/user_activates_mattermost_slash_command_integration_shared_examples.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'user activates the Mattermost Slash Command integration' do
+ it 'shows a help message' do
+ expect(page).to have_content('Use this service to perform common')
+ end
+
+ it 'shows a token placeholder' do
+ token_placeholder = find_field('service_token')['placeholder']
+
+ expect(token_placeholder).to eq('XXxxXXxxXXxxXXxxXXxxXXxx')
+ end
+
+ it 'redirects to the integrations page after saving but not activating' do
+ token = ('a'..'z').to_a.join
+
+ fill_in 'service_token', with: token
+ click_active_checkbox
+ click_save_integration
+
+ expect(current_path).to eq(edit_path)
+ expect(page).to have_content('Mattermost slash commands settings saved, but not active.')
+ end
+
+ it 'redirects to the integrations page after activating' do
+ token = ('a'..'z').to_a.join
+
+ fill_in 'service_token', with: token
+ click_save_integration
+
+ expect(current_path).to eq(edit_path)
+ expect(page).to have_content('Mattermost slash commands settings saved and active.')
+ end
+end
diff --git a/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb b/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb
index 736c353c2aa..c0cfc27ceaf 100644
--- a/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb
+++ b/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb
@@ -1,9 +1,12 @@
# frozen_string_literal: true
-RSpec.shared_examples 'issuable invite members experiments' do
+RSpec.shared_examples 'issuable invite members' do
context 'when a privileged user can invite' do
- it 'shows a link for inviting members and launches invite modal' do
+ before do
project.add_maintainer(user)
+ end
+
+ it 'shows a link for inviting members and launches invite modal' do
visit issuable_path
find('.block.assignee .edit-link').click
@@ -23,8 +26,11 @@ RSpec.shared_examples 'issuable invite members experiments' do
end
context 'when user cannot invite members in assignee dropdown' do
- it 'shows author in assignee dropdown and no invite link' do
+ before do
project.add_developer(user)
+ end
+
+ it 'shows author in assignee dropdown and no invite link' do
visit issuable_path
find('.block.assignee .edit-link').click
diff --git a/spec/support/shared_examples/features/variable_list_shared_examples.rb b/spec/support/shared_examples/features/variable_list_shared_examples.rb
index 4b94411f009..997500415a9 100644
--- a/spec/support/shared_examples/features/variable_list_shared_examples.rb
+++ b/spec/support/shared_examples/features/variable_list_shared_examples.rb
@@ -283,6 +283,8 @@ RSpec.shared_examples 'variable list' do
end
def fill_variable(key, value, protected: false, masked: false)
+ wait_for_requests
+
page.within('#add-ci-variable') do
find('[data-qa-selector="ci_variable_key_field"] input').set(key)
find('[data-qa-selector="ci_variable_value_field"]').set(value) if value.present?
diff --git a/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb b/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb
index 96b05db4cd9..5cbbed1468f 100644
--- a/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb
+++ b/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb
@@ -24,6 +24,12 @@ RSpec.shared_examples 'assignee NOT username filter' do
end
end
+RSpec.shared_examples 'assignee OR filter' do
+ it 'returns issuables assigned to the given users' do
+ expect(issuables).to contain_exactly(*expected_issuables)
+ end
+end
+
RSpec.shared_examples 'no assignee filter' do
let(:params) { { assignee_id: 'None' } }
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 fc795012ce7..5e15c91cd41 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
@@ -6,9 +6,9 @@ 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
args = [
- anything,
- anything,
- hash_including(
+ project: anything,
+ current_user: anything,
+ params: hash_including(
api: true,
request: instance_of(ActionDispatch::Request),
captcha_response: captcha_response,
diff --git a/spec/support/shared_examples/graphql/mutations/resolves_subscription_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/resolves_subscription_shared_examples.rb
index ebba312e895..678bb908343 100644
--- a/spec/support/shared_examples/graphql/mutations/resolves_subscription_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/resolves_subscription_shared_examples.rb
@@ -2,44 +2,37 @@
require 'spec_helper'
-RSpec.shared_examples 'a subscribeable graphql resource' do
- let(:project) { resource.project }
- let_it_be(:user) { create(:user) }
-
- subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
+RSpec.shared_examples 'a subscribeable not accessible graphql resource' do
+ let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
- specify { expect(described_class).to require_graphql_authorizations(permission_name) }
+ subject { mutation.resolve(project_path: resource.project.full_path, iid: resource.iid, subscribed_state: true) }
- describe '#resolve' do
- let(:subscribe) { true }
- let(:mutated_resource) { subject[resource.class.name.underscore.to_sym] }
-
- subject { mutation.resolve(project_path: resource.project.full_path, iid: resource.iid, subscribed_state: subscribe) }
+ it 'raises an error if the resource is not accessible to the user' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+end
- it 'raises an error if the resource is not accessible to the user' do
- expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
- end
+RSpec.shared_examples 'a subscribeable graphql resource' do
+ let(:mutated_resource) { subject[resource.class.name.underscore.to_sym] }
+ let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
+ let(:subscribe) { true }
- context 'when the user can update the resource' do
- before do
- resource.project.add_developer(user)
- end
+ subject { mutation.resolve(project_path: resource.project.full_path, iid: resource.iid, subscribed_state: subscribe) }
- it 'subscribes to the resource' do
- expect(mutated_resource).to eq(resource)
- expect(mutated_resource.subscribed?(user, project)).to eq(true)
- expect(subject[:errors]).to be_empty
- end
+ it 'subscribes to the resource' do
+ expect(mutated_resource).to eq(resource)
+ expect(mutated_resource.subscribed?(user, project)).to eq(true)
+ expect(subject[:errors]).to be_empty
+ end
- context 'when passing subscribe as false' do
- let(:subscribe) { false }
+ context 'when passing subscribe as false' do
+ let(:subscribe) { false }
- it 'unsubscribes from the discussion' do
- resource.subscribe(user, project)
+ it 'unsubscribes from the discussion' do
+ resource.subscribe(user, project)
- expect(mutated_resource.subscribed?(user, project)).to eq(false)
- end
- end
+ expect(mutated_resource.subscribed?(user, project)).to eq(false)
+ expect(subject[:errors]).to be_empty
end
end
end
diff --git a/spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb
index 9c95d1ff9d9..3760325675a 100644
--- a/spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb
@@ -29,6 +29,34 @@ RSpec.shared_examples 'common trace features' do
end
end
+ describe '#read' do
+ context 'gitlab_ci_archived_trace_consistent_reads feature flag enabled' do
+ before do
+ stub_feature_flags(gitlab_ci_archived_trace_consistent_reads: trace.job.project)
+ end
+
+ it 'calls ::Gitlab::Database::LoadBalancing::Sticking.unstick_or_continue_sticking' do
+ expect(::Gitlab::Database::LoadBalancing::Sticking).to receive(:unstick_or_continue_sticking)
+ .with(described_class::LOAD_BALANCING_STICKING_NAMESPACE, trace.job.id)
+ .and_call_original
+
+ trace.read { |stream| stream }
+ end
+ end
+
+ context 'gitlab_ci_archived_trace_consistent_reads feature flag disabled' do
+ before do
+ stub_feature_flags(gitlab_ci_archived_trace_consistent_reads: false)
+ end
+
+ it 'does not call ::Gitlab::Database::LoadBalancing::Sticking.unstick_or_continue_sticking' do
+ expect(::Gitlab::Database::LoadBalancing::Sticking).not_to receive(:unstick_or_continue_sticking)
+
+ trace.read { |stream| stream }
+ end
+ end
+ end
+
describe '#extract_coverage' do
let(:regex) { '\(\d+.\d+\%\) covered' }
@@ -253,6 +281,52 @@ RSpec.shared_examples 'common trace features' do
describe '#archive!' do
subject { trace.archive! }
+ context 'when live trace chunks exists' do
+ before do
+ # Build a trace_chunk manually
+ # It is possible to do so with trace.set but only if ci_enable_live_trace FF is enabled
+ #
+ # We need the job to have a trace_chunk because we only use #stick in
+ # the case where trace_chunks exist.
+ stream = Gitlab::Ci::Trace::Stream.new do
+ Gitlab::Ci::Trace::ChunkedIO.new(trace.job)
+ end
+
+ stream.set(+"12\n34")
+ end
+
+ # We check the before setup actually sets up job trace_chunks
+ it 'has job trace_chunks' do
+ expect(trace.job.trace_chunks).to be_present
+ end
+
+ context 'gitlab_ci_archived_trace_consistent_reads feature flag enabled' do
+ before do
+ stub_feature_flags(gitlab_ci_archived_trace_consistent_reads: trace.job.project)
+ end
+
+ it 'calls ::Gitlab::Database::LoadBalancing::Sticking.stick' do
+ expect(::Gitlab::Database::LoadBalancing::Sticking).to receive(:stick)
+ .with(described_class::LOAD_BALANCING_STICKING_NAMESPACE, trace.job.id)
+ .and_call_original
+
+ subject
+ end
+ end
+
+ context 'gitlab_ci_archived_trace_consistent_reads feature flag disabled' do
+ before do
+ stub_feature_flags(gitlab_ci_archived_trace_consistent_reads: false)
+ end
+
+ it 'does not call ::Gitlab::Database::LoadBalancing::Sticking.stick' do
+ expect(::Gitlab::Database::LoadBalancing::Sticking).not_to receive(:stick)
+
+ subject
+ end
+ end
+ end
+
context 'when build status is success' do
let!(:build) { create(:ci_build, :success, :trace_live) }
diff --git a/spec/support/shared_examples/lib/gitlab/experimentation_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/experimentation_shared_examples.rb
new file mode 100644
index 00000000000..5baa6478225
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/experimentation_shared_examples.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'tracks assignment and records the subject' do |experiment, subject_type|
+ it 'tracks the assignment', :experiment do
+ expect(experiment(experiment))
+ .to track(:assignment)
+ .with_context(subject_type => subject)
+ .on_next_instance
+
+ action
+ end
+
+ it 'records the subject' do
+ stub_experiments(experiment => :candidate)
+
+ expect(Experiment).to receive(:add_subject).with(experiment.to_s, variant: :experimental, subject: subject)
+
+ action
+ end
+end
diff --git a/spec/support/shared_examples/models/chat_service_shared_examples.rb b/spec/support/shared_examples/models/chat_integration_shared_examples.rb
index 4a47aad0957..9f3be3e2e06 100644
--- a/spec/support/shared_examples/models/chat_service_shared_examples.rb
+++ b/spec/support/shared_examples/models/chat_integration_shared_examples.rb
@@ -1,13 +1,13 @@
# frozen_string_literal: true
-RSpec.shared_examples "chat service" do |service_name|
+RSpec.shared_examples "chat integration" do |integration_name|
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
end
describe "Validations" do
- context "when service is active" do
+ context "when integration is active" do
before do
subject.active = true
end
@@ -16,7 +16,7 @@ RSpec.shared_examples "chat service" do |service_name|
it_behaves_like "issue tracker service URL attribute", :webhook
end
- context "when service is inactive" do
+ context "when integration is inactive" do
before do
subject.active = false
end
@@ -47,12 +47,12 @@ RSpec.shared_examples "chat service" do |service_name|
WebMock.stub_request(:post, webhook_url)
end
- shared_examples "triggered #{service_name} service" do |branches_to_be_notified: nil|
+ shared_examples "triggered #{integration_name} integration" do |branches_to_be_notified: nil|
before do
subject.branches_to_be_notified = branches_to_be_notified if branches_to_be_notified
end
- it "calls #{service_name} API" do
+ it "calls #{integration_name} API" do
result = subject.execute(sample_data)
expect(result).to be(true)
@@ -63,12 +63,12 @@ RSpec.shared_examples "chat service" do |service_name|
end
end
- shared_examples "untriggered #{service_name} service" do |branches_to_be_notified: nil|
+ shared_examples "untriggered #{integration_name} integration" do |branches_to_be_notified: nil|
before do
subject.branches_to_be_notified = branches_to_be_notified if branches_to_be_notified
end
- it "does not call #{service_name} API" do
+ it "does not call #{integration_name} API" do
result = subject.execute(sample_data)
expect(result).to be(false)
@@ -81,7 +81,7 @@ RSpec.shared_examples "chat service" do |service_name|
Gitlab::DataBuilder::Push.build_sample(project, user)
end
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
it "specifies the webhook when it is configured", if: defined?(client) do
expect(client).to receive(:new).with(client_arguments).and_return(double(:chat_service).as_null_object)
@@ -95,19 +95,19 @@ RSpec.shared_examples "chat service" do |service_name|
end
context "when only default branch are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "default"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "default"
end
context "when only protected branches are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "protected"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "protected"
end
context "when default and protected branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "default_and_protected"
end
context "when all branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "all"
end
end
@@ -121,19 +121,19 @@ RSpec.shared_examples "chat service" do |service_name|
end
context "when only default branch are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "default"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "default"
end
context "when only protected branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "protected"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "protected"
end
context "when default and protected branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "default_and_protected"
end
context "when all branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "all"
end
end
@@ -143,19 +143,19 @@ RSpec.shared_examples "chat service" do |service_name|
end
context "when only default branch are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "default"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "default"
end
context "when only protected branches are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "protected"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "protected"
end
context "when default and protected branches are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "default_and_protected"
end
context "when all branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "all"
end
end
end
@@ -168,7 +168,7 @@ RSpec.shared_examples "chat service" do |service_name|
service.hook_data(issue, "open")
end
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
end
context "with merge events" do
@@ -191,7 +191,7 @@ RSpec.shared_examples "chat service" do |service_name|
project.add_developer(user)
end
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
end
context "with wiki page events" do
@@ -207,7 +207,7 @@ RSpec.shared_examples "chat service" do |service_name|
let(:wiki_page) { create(:wiki_page, wiki: project.wiki, **opts) }
let(:sample_data) { Gitlab::DataBuilder::WikiPage.build(wiki_page, user, "create") }
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
end
context "with note events" do
@@ -222,7 +222,7 @@ RSpec.shared_examples "chat service" do |service_name|
note: "a comment on a commit")
end
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
end
context "with merge request comment" do
@@ -230,7 +230,7 @@ RSpec.shared_examples "chat service" do |service_name|
create(:note_on_merge_request, project: project, note: "merge request note")
end
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
end
context "with issue comment" do
@@ -238,7 +238,7 @@ RSpec.shared_examples "chat service" do |service_name|
create(:note_on_issue, project: project, note: "issue note")
end
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
end
context "with snippet comment" do
@@ -246,7 +246,7 @@ RSpec.shared_examples "chat service" do |service_name|
create(:note_on_project_snippet, project: project, note: "snippet note")
end
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
end
end
@@ -262,14 +262,14 @@ RSpec.shared_examples "chat service" do |service_name|
context "with failed pipeline" do
let(:status) { "failed" }
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
end
context "with succeeded pipeline" do
let(:status) { "success" }
context "with default notify_only_broken_pipelines" do
- it "does not call #{service_name} API" do
+ it "does not call #{integration_name} API" do
result = subject.execute(sample_data)
expect(result).to be_falsy
@@ -281,7 +281,7 @@ RSpec.shared_examples "chat service" do |service_name|
subject.notify_only_broken_pipelines = false
end
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
end
end
@@ -291,19 +291,19 @@ RSpec.shared_examples "chat service" do |service_name|
end
context "when only default branch are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "default"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "default"
end
context "when only protected branches are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "protected"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "protected"
end
context "when default and protected branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "default_and_protected"
end
context "when all branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "all"
end
end
@@ -317,19 +317,19 @@ RSpec.shared_examples "chat service" do |service_name|
end
context "when only default branch are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "default"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "default"
end
context "when only protected branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "protected"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "protected"
end
context "when default and protected branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "default_and_protected"
end
context "when all branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "all"
end
end
@@ -339,19 +339,19 @@ RSpec.shared_examples "chat service" do |service_name|
end
context "when only default branch are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "default"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "default"
end
context "when only protected branches are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "protected"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "protected"
end
context "when default and protected branches are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "default_and_protected"
end
context "when all branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "all"
end
end
end
diff --git a/spec/support/shared_examples/models/slack_mattermost_notifications_shared_examples.rb b/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb
index 09b7d1be704..66448aca2c5 100644
--- a/spec/support/shared_examples/models/slack_mattermost_notifications_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb
@@ -1,13 +1,13 @@
# frozen_string_literal: true
-RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
+RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
include StubRequests
let(:chat_service) { described_class.new }
let(:webhook_url) { 'https://example.gitlab.com' }
def execute_with_options(options)
- receive(:new).with(webhook_url, options.merge(http_client: SlackMattermost::Notifier::HTTPClient))
+ receive(:new).with(webhook_url, options.merge(http_client: Integrations::SlackMattermostNotifier::HTTPClient))
.and_return(double(:slack_service).as_null_object)
end
@@ -81,7 +81,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
shared_examples 'calls the service API with the event message' do |event_message|
specify do
- expect_next_instance_of(Slack::Messenger) do |messenger|
+ expect_next_instance_of(::Slack::Messenger) do |messenger|
expect(messenger).to receive(:ping).with(event_message, anything).and_call_original
end
@@ -95,7 +95,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
let(:chat_service_params) { { username: 'slack_username' } }
it 'uses the username as an option' do
- expect(Slack::Messenger).to execute_with_options(username: 'slack_username')
+ expect(::Slack::Messenger).to execute_with_options(username: 'slack_username')
execute_service
end
@@ -110,7 +110,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
let(:chat_service_params) { { push_channel: 'random' } }
it 'uses the right channel for push event' do
- expect(Slack::Messenger).to execute_with_options(channel: ['random'])
+ expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
execute_service
end
@@ -128,6 +128,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
context 'issue events' do
let_it_be(:issue) { create(:issue) }
+
let(:data) { issue.to_hook_data(user) }
it_behaves_like 'calls the service API with the event message', /Issue (.*?) opened by/
@@ -136,7 +137,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
let(:chat_service_params) { { issue_channel: 'random' } }
it 'uses the right channel for issue event' do
- expect(Slack::Messenger).to execute_with_options(channel: ['random'])
+ expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
execute_service
end
@@ -147,7 +148,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
end
it 'falls back to issue channel' do
- expect(Slack::Messenger).to execute_with_options(channel: ['random'])
+ expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
execute_service
end
@@ -156,7 +157,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
let(:chat_service_params) { { issue_channel: 'random', confidential_issue_channel: 'confidential' } }
it 'uses the confidential issue channel when it is defined' do
- expect(Slack::Messenger).to execute_with_options(channel: ['confidential'])
+ expect(::Slack::Messenger).to execute_with_options(channel: ['confidential'])
execute_service
end
@@ -167,6 +168,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
context 'merge request events' do
let_it_be(:merge_request) { create(:merge_request) }
+
let(:data) { merge_request.to_hook_data(user) }
it_behaves_like 'calls the service API with the event message', /opened merge request/
@@ -175,7 +177,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
let(:chat_service_params) { { merge_request_channel: 'random' } }
it 'uses the right channel for merge request event' do
- expect(Slack::Messenger).to execute_with_options(channel: ['random'])
+ expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
execute_service
end
@@ -184,15 +186,16 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
context 'wiki page events' do
let_it_be(:wiki_page) { create(:wiki_page, wiki: project.wiki, message: 'user created page: Awesome wiki_page') }
+
let(:data) { Gitlab::DataBuilder::WikiPage.build(wiki_page, user, 'create') }
- it_behaves_like 'calls the service API with the event message', / created (.*?)wikis\/(.*?)|wiki page> in/
+ it_behaves_like 'calls the service API with the event message', %r{ created (.*?)wikis/(.*?)|wiki page> in}
context 'with event channel' do
let(:chat_service_params) { { wiki_page_channel: 'random' } }
it 'uses the right channel for wiki event' do
- expect(Slack::Messenger).to execute_with_options(channel: ['random'])
+ expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
execute_service
end
@@ -201,6 +204,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
context 'deployment events' do
let_it_be(:deployment) { create(:deployment) }
+
let(:data) { Gitlab::DataBuilder::Deployment.build(deployment, Time.current) }
it_behaves_like 'calls the service API with the event message', /Deploy to (.*?) created/
@@ -208,6 +212,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
context 'note event' do
let_it_be(:issue_note) { create(:note_on_issue, project: project, note: "issue note") }
+
let(:data) { Gitlab::DataBuilder::Note.build(issue_note, user) }
it_behaves_like 'calls the service API with the event message', /commented on issue/
@@ -216,7 +221,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
let(:chat_service_params) { { note_channel: 'random' } }
it 'uses the right channel' do
- expect(Slack::Messenger).to execute_with_options(channel: ['random'])
+ expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
execute_service
end
@@ -227,7 +232,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
end
it 'falls back to note channel' do
- expect(Slack::Messenger).to execute_with_options(channel: ['random'])
+ expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
execute_service
end
@@ -236,7 +241,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
let(:chat_service_params) { { note_channel: 'random', confidential_note_channel: 'confidential' } }
it 'uses confidential channel' do
- expect(Slack::Messenger).to execute_with_options(channel: ['confidential'])
+ expect(::Slack::Messenger).to execute_with_options(channel: ['confidential'])
execute_service
end
diff --git a/spec/support/shared_examples/models/concerns/timebox_shared_examples.rb b/spec/support/shared_examples/models/concerns/timebox_shared_examples.rb
index 68142e667a4..39121b73bc5 100644
--- a/spec/support/shared_examples/models/concerns/timebox_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/timebox_shared_examples.rb
@@ -86,45 +86,6 @@ RSpec.shared_examples 'a timebox' do |timebox_type|
expect(timebox.errors[:project_id]).to include("#{timebox_type} should belong either to a project or a group.")
end
end
-
- describe "#uniqueness_of_title" do
- context "per project" do
- it "does not accept the same title in a project twice" do
- new_timebox = timebox.dup
- expect(new_timebox).not_to be_valid
- end
-
- it "accepts the same title in another project" do
- project = create(:project)
- new_timebox = timebox.dup
- new_timebox.project = project
-
- expect(new_timebox).to be_valid
- end
- end
-
- context "per group" do
- let(:timebox) { create(timebox_type, *timebox_args, group: group) }
-
- before do
- project.update!(group: group)
- end
-
- it "does not accept the same title in a group twice" do
- new_timebox = described_class.new(group: group, title: timebox.title)
-
- expect(new_timebox).not_to be_valid
- end
-
- it "does not accept the same title of a child project timebox" do
- create(timebox_type, *timebox_args, project: group.projects.first)
-
- new_timebox = described_class.new(group: group, title: timebox.title)
-
- expect(new_timebox).not_to be_valid
- end
- end
- end
end
describe "Associations" do
diff --git a/spec/support/shared_examples/models/chat_slash_commands_shared_examples.rb b/spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb
index 49729afce61..128999d02fa 100644
--- a/spec/support/shared_examples/models/chat_slash_commands_shared_examples.rb
+++ b/spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples 'chat slash commands service' do
+RSpec.shared_examples Integrations::BaseSlashCommands do
describe "Associations" do
it { is_expected.to respond_to :token }
it { is_expected.to have_many :chat_names }
diff --git a/spec/support/shared_examples/models/mentionable_shared_examples.rb b/spec/support/shared_examples/models/mentionable_shared_examples.rb
index 2392658e584..04630484964 100644
--- a/spec/support/shared_examples/models/mentionable_shared_examples.rb
+++ b/spec/support/shared_examples/models/mentionable_shared_examples.rb
@@ -66,7 +66,7 @@ RSpec.shared_examples 'a mentionable' do
expect(subject.gfm_reference).to eq(backref_text)
end
- it "extracts references from its reference property" do
+ it "extracts references from its reference property", :clean_gitlab_redis_cache do
# De-duplicate and omit itself
refs = subject.referenced_mentionables
expect(refs.size).to eq(6)
@@ -98,7 +98,7 @@ RSpec.shared_examples 'a mentionable' do
end
end
- it 'creates cross-reference notes' do
+ it 'creates cross-reference notes', :clean_gitlab_redis_cache do
mentioned_objects = [mentioned_issue, mentioned_mr, mentioned_commit,
ext_issue, ext_mr, ext_commit]
diff --git a/spec/support/shared_examples/models/packages/debian/component_file_shared_example.rb b/spec/support/shared_examples/models/packages/debian/component_file_shared_example.rb
index e6b16d5881d..f08ee820463 100644
--- a/spec/support/shared_examples/models/packages/debian/component_file_shared_example.rb
+++ b/spec/support/shared_examples/models/packages/debian/component_file_shared_example.rb
@@ -142,6 +142,14 @@ RSpec.shared_examples 'Debian Component File' do |container_type, can_freeze|
end
end
+ describe '.with_architecture' do
+ subject { described_class.with_architecture(architecture1_2) }
+
+ it do
+ expect(subject.to_a).to contain_exactly(component_file_other_architecture)
+ end
+ end
+
describe '.with_architecture_name' do
subject { described_class.with_architecture_name(architecture1_2.name) }
@@ -166,12 +174,12 @@ RSpec.shared_examples 'Debian Component File' do |container_type, can_freeze|
end
end
- describe '.created_before' do
- let_it_be(:component_file1) { create("debian_#{container_type}_component_file", component: component1_1, architecture: architecture1_1, created_at: 4.hours.ago) }
- let_it_be(:component_file2) { create("debian_#{container_type}_component_file", component: component1_1, architecture: architecture1_1, created_at: 3.hours.ago) }
- let_it_be(:component_file3) { create("debian_#{container_type}_component_file", component: component1_1, architecture: architecture1_1, created_at: 1.hour.ago) }
+ describe '.updated_before' do
+ let_it_be(:component_file1) { create("debian_#{container_type}_component_file", component: component1_1, architecture: architecture1_1, updated_at: 4.hours.ago) }
+ let_it_be(:component_file2) { create("debian_#{container_type}_component_file", component: component1_1, architecture: architecture1_1, updated_at: 3.hours.ago) }
+ let_it_be(:component_file3) { create("debian_#{container_type}_component_file", component: component1_1, architecture: architecture1_1, updated_at: 1.hour.ago) }
- subject { described_class.created_before(2.hours.ago) }
+ subject { described_class.updated_before(2.hours.ago) }
it do
expect(subject.to_a).to contain_exactly(component_file1, component_file2)
diff --git a/spec/support/shared_examples/models/packages/debian/distribution_key_shared_examples.rb b/spec/support/shared_examples/models/packages/debian/distribution_key_shared_examples.rb
new file mode 100644
index 00000000000..26794c83736
--- /dev/null
+++ b/spec/support/shared_examples/models/packages/debian/distribution_key_shared_examples.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.shared_examples 'Debian Distribution Key' do |container|
+ let_it_be_with_refind(:distribution_key) { create("debian_#{container}_distribution_key") } # rubocop:disable Rails/SaveBang
+
+ subject { distribution_key }
+
+ describe 'relationships' do
+ it { is_expected.to belong_to(:distribution).class_name("Packages::Debian::#{container.capitalize}Distribution").inverse_of(:key) }
+ end
+
+ describe 'validations' do
+ describe "#distribution" do
+ it { is_expected.to validate_presence_of(:distribution) }
+ end
+
+ describe '#private_key' do
+ it { is_expected.to validate_presence_of(:private_key) }
+
+ it { is_expected.to allow_value("-----BEGIN PGP PRIVATE KEY BLOCK-----\n...").for(:private_key) }
+ it { is_expected.not_to allow_value('A').for(:private_key).with_message('must be ASCII armored') }
+ end
+
+ describe '#passphrase' do
+ it { is_expected.to validate_presence_of(:passphrase) }
+
+ it { is_expected.to allow_value('P@$$w0rd').for(:passphrase) }
+ it { is_expected.to allow_value('A' * 255).for(:passphrase) }
+ it { is_expected.not_to allow_value('A' * 256).for(:passphrase) }
+ end
+
+ describe '#public_key' do
+ it { is_expected.to validate_presence_of(:public_key) }
+
+ it { is_expected.to allow_value("-----BEGIN PGP PUBLIC KEY BLOCK-----\n...").for(:public_key) }
+ it { is_expected.not_to allow_value('A').for(:public_key).with_message('must be ASCII armored') }
+ end
+
+ describe '#fingerprint' do
+ it { is_expected.to validate_presence_of(:passphrase) }
+
+ it { is_expected.to allow_value('abc').for(:passphrase) }
+ it { is_expected.to allow_value('A' * 255).for(:passphrase) }
+ it { is_expected.not_to allow_value('A' * 256).for(:passphrase) }
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
index 8693d6868e9..5459d17b1df 100644
--- a/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
+++ b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
@@ -17,6 +17,7 @@ RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze|
it { is_expected.to belong_to(container) }
it { is_expected.to belong_to(:creator).class_name('User') }
+ it { is_expected.to have_one(:key).class_name("Packages::Debian::#{container.capitalize}DistributionKey").with_foreign_key(:distribution_id).inverse_of(:distribution) }
it { is_expected.to have_many(:components).class_name("Packages::Debian::#{container.capitalize}Component").inverse_of(:distribution) }
it { is_expected.to have_many(:architectures).class_name("Packages::Debian::#{container.capitalize}Architecture").inverse_of(:distribution) }
end
diff --git a/spec/support/shared_examples/namespaces/linear_traversal_examples.rb b/spec/support/shared_examples/namespaces/linear_traversal_examples.rb
new file mode 100644
index 00000000000..2fd90c36953
--- /dev/null
+++ b/spec/support/shared_examples/namespaces/linear_traversal_examples.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+# Traversal examples common to linear and recursive methods are in
+# spec/support/shared_examples/namespaces/traversal_examples.rb
+
+RSpec.shared_examples 'linear namespace traversal' do
+ context 'when use_traversal_ids feature flag is enabled' do
+ before do
+ stub_feature_flags(use_traversal_ids: true)
+ end
+
+ context 'scopes' do
+ describe '.as_ids' do
+ let_it_be(:namespace1) { create(:group) }
+ let_it_be(:namespace2) { create(:group) }
+
+ subject { Namespace.where(id: [namespace1, namespace2]).as_ids.pluck(:id) }
+
+ it { is_expected.to contain_exactly(namespace1.id, namespace2.id) }
+ 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 77a1705627e..ccc64c80fd4 100644
--- a/spec/support/shared_examples/namespaces/traversal_examples.rb
+++ b/spec/support/shared_examples/namespaces/traversal_examples.rb
@@ -17,6 +17,28 @@ RSpec.shared_examples 'namespace traversal' do
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) }
+
+ it 'returns the correct root ancestor' do
+ expect(group.root_ancestor).to eq(group)
+ expect(nested_group.root_ancestor).to eq(group)
+ expect(deep_nested_group.root_ancestor).to eq(group)
+ 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)
+ end
+ end
+ end
+ end
+
describe '#self_and_hierarchy' do
let!(:group) { create(:group, path: 'git_lab') }
let!(:nested_group) { create(:group, parent: group) }
@@ -122,4 +144,20 @@ RSpec.shared_examples 'namespace traversal' do
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) }
+
+ 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
end
diff --git a/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb
index c938c6432fe..20606ae942d 100644
--- a/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb
@@ -294,16 +294,6 @@ RSpec.shared_examples 'rejects invalid upload_url params' do
end
end
-RSpec.shared_examples 'successful response when using Unicorn' do
- context 'on Unicorn', :unicorn do
- it 'returns successfully' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
-end
-
RSpec.shared_examples 'recipe snapshot endpoint' do
subject { get api(url), headers: headers }
@@ -372,7 +362,6 @@ RSpec.shared_examples 'recipe upload_urls endpoint' do
it_behaves_like 'rejects invalid recipe'
it_behaves_like 'rejects invalid upload_url params'
- it_behaves_like 'successful response when using Unicorn'
it 'returns a set of upload urls for the files requested' do
subject
@@ -434,7 +423,6 @@ RSpec.shared_examples 'package upload_urls endpoint' do
it_behaves_like 'rejects invalid recipe'
it_behaves_like 'rejects invalid upload_url params'
- it_behaves_like 'successful response when using Unicorn'
it 'returns a set of upload urls for the files requested' do
expected_response = {
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 dfd19167dcd..0530aa8c760 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,7 +12,35 @@ 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(:distribution) { 'bullseye' }
+ let_it_be(:private_distribution, freeze: true) { create("debian_#{container_type}_distribution", 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(:public_distribution, freeze: true) { create("debian_#{container_type}_distribution", 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') }
+
+ if container_type == :group
+ let_it_be(:private_project) { create(:project, :private, group: private_container) }
+ let_it_be(:public_project) { create(:project, :public, group: public_container) }
+ let_it_be(:private_project_distribution) { create(:debian_project_distribution, container: private_project, codename: 'existing-codename') }
+ let_it_be(:public_project_distribution) { create(:debian_project_distribution, container: public_project, codename: 'existing-codename') }
+ else
+ let_it_be(:private_project) { private_container }
+ let_it_be(:public_project) { public_container }
+ let_it_be(:private_project_distribution) { private_distribution }
+ let_it_be(:public_project_distribution) { public_distribution }
+ end
+
+ let_it_be(:private_package) { create(:debian_package, project: private_project, published_in: private_project_distribution) }
+ let_it_be(:public_package) { create(:debian_package, project: public_project, published_in: public_project_distribution) }
+
+ let(:visibility_level) { :public }
+
+ let(:distribution) { { private: private_distribution, public: public_distribution }[visibility_level] }
+
let(:component) { 'main' }
let(:architecture) { 'amd64' }
let(:source_package) { 'sample' }
@@ -97,7 +125,7 @@ RSpec.shared_examples 'Debian repository GET request' do |status, body = nil|
expect(response).to have_gitlab_http_status(status)
unless body.nil?
- expect(response.body).to eq(body)
+ expect(response.body).to match(body)
end
end
end
@@ -107,16 +135,25 @@ RSpec.shared_examples 'Debian repository upload request' do |status, body = nil|
if status == :created
it 'creates package files', :aggregate_failures do
- pending "Debian package creation not implemented"
+ expect(::Packages::Debian::FindOrCreateIncomingService).to receive(:new).with(container, user).and_call_original
+ expect(::Packages::Debian::CreatePackageFileService).to receive(:new).with(be_a(Packages::Package), be_an(Hash)).and_call_original
+
+ if file_name.end_with? '.changes'
+ expect(::Packages::Debian::ProcessChangesWorker).to receive(:perform_async)
+ else
+ expect(::Packages::Debian::ProcessChangesWorker).not_to receive(:perform_async)
+ end
expect { subject }
.to change { container.packages.debian.count }.by(1)
+ .and change { container.packages.debian.where(name: 'incoming').count }.by(1)
+ .and change { container.package_files.count }.by(1)
expect(response).to have_gitlab_http_status(status)
expect(response.media_type).to eq('text/plain')
unless body.nil?
- expect(response.body).to eq(body)
+ expect(response.body).to match(body)
end
end
it_behaves_like 'a package tracking event', described_class.name, 'push_package'
@@ -127,7 +164,7 @@ RSpec.shared_examples 'Debian repository upload request' do |status, body = nil|
expect(response).to have_gitlab_http_status(status)
unless body.nil?
- expect(response.body).to eq(body)
+ expect(response.body).to match(body)
end
end
end
@@ -173,18 +210,112 @@ RSpec.shared_examples 'Debian repository upload authorize request' do |status, b
expect(response).to have_gitlab_http_status(status)
unless body.nil?
- expect(response.body).to eq(body)
+ expect(response.body).to match(body)
+ end
+ end
+ end
+end
+
+RSpec.shared_examples 'Debian repository POST distribution request' do |status, body|
+ and_body = body.nil? ? '' : ' and expected body'
+
+ if status == :created
+ it 'creates distribution', :aggregate_failures do
+ expect(::Packages::Debian::CreateDistributionService).to receive(:new).with(container, user, api_params).and_call_original
+
+ expect { subject }
+ .to change { Packages::Debian::GroupDistribution.all.count + Packages::Debian::ProjectDistribution.all.count }.by(1)
+ .and change { Packages::Debian::GroupComponent.all.count + Packages::Debian::ProjectComponent.all.count }.by(1)
+ .and change { Packages::Debian::GroupArchitecture.all.count + Packages::Debian::ProjectArchitecture.all.count }.by(2)
+
+ expect(response).to have_gitlab_http_status(status)
+ expect(response.media_type).to eq('application/json')
+
+ unless body.nil?
+ expect(response.body).to match(body)
+ end
+ end
+ else
+ it "returns #{status}#{and_body}", :aggregate_failures do
+ subject
+
+ expect(response).to have_gitlab_http_status(status)
+
+ unless body.nil?
+ expect(response.body).to match(body)
+ end
+ end
+ end
+end
+
+RSpec.shared_examples 'Debian repository PUT distribution request' do |status, body|
+ and_body = body.nil? ? '' : ' and expected body'
+
+ if status == :success
+ it 'updates distribution', :aggregate_failures do
+ expect(::Packages::Debian::UpdateDistributionService).to receive(:new).with(distribution, api_params.except(:codename)).and_call_original
+
+ expect { subject }
+ .to not_change { Packages::Debian::GroupDistribution.all.count + Packages::Debian::ProjectDistribution.all.count }
+ .and not_change { Packages::Debian::GroupComponent.all.count + Packages::Debian::ProjectComponent.all.count }
+ .and not_change { Packages::Debian::GroupArchitecture.all.count + Packages::Debian::ProjectArchitecture.all.count }
+
+ expect(response).to have_gitlab_http_status(status)
+ expect(response.media_type).to eq('application/json')
+
+ unless body.nil?
+ expect(response.body).to match(body)
+ end
+ end
+ else
+ it "returns #{status}#{and_body}", :aggregate_failures do
+ subject
+
+ expect(response).to have_gitlab_http_status(status)
+
+ unless body.nil?
+ expect(response.body).to match(body)
end
end
end
end
-RSpec.shared_examples 'rejects Debian access with unknown container id' do
+RSpec.shared_examples 'Debian repository DELETE distribution request' do |status, body|
+ and_body = body.nil? ? '' : ' and expected body'
+
+ if status == :success
+ it 'updates distribution', :aggregate_failures do
+ expect { subject }
+ .to change { Packages::Debian::GroupDistribution.all.count + Packages::Debian::ProjectDistribution.all.count }.by(-1)
+ .and change { Packages::Debian::GroupComponent.all.count + Packages::Debian::ProjectComponent.all.count }.by(-1)
+ .and change { Packages::Debian::GroupArchitecture.all.count + Packages::Debian::ProjectArchitecture.all.count }.by(-2)
+
+ expect(response).to have_gitlab_http_status(status)
+ expect(response.media_type).to eq('application/json')
+
+ unless body.nil?
+ expect(response.body).to match(body)
+ end
+ end
+ else
+ it "returns #{status}#{and_body}", :aggregate_failures do
+ subject
+
+ expect(response).to have_gitlab_http_status(status)
+
+ unless body.nil?
+ expect(response.body).to match(body)
+ end
+ end
+ end
+end
+
+RSpec.shared_examples 'rejects Debian access with unknown container id' do |hidden_status|
context 'with an unknown container' do
let(:container) { double(id: non_existing_record_id) }
context 'as anonymous' do
- it_behaves_like 'Debian repository GET request', :unauthorized, nil
+ it_behaves_like 'Debian repository GET request', hidden_status, nil
end
context 'as authenticated user' do
@@ -195,19 +326,25 @@ RSpec.shared_examples 'rejects Debian access with unknown container id' do
end
end
-RSpec.shared_examples 'Debian repository read endpoint' do |desired_behavior, success_status, success_body|
+RSpec.shared_examples 'Debian repository read endpoint' do |desired_behavior, success_status, success_body, authenticate_non_public: true|
+ hidden_status = if authenticate_non_public
+ :unauthorized
+ else
+ :not_found
+ end
+
context 'with valid container' do
using RSpec::Parameterized::TableSyntax
where(:visibility_level, :user_role, :member, :user_token, :expected_status, :expected_body) do
:public | :developer | true | true | success_status | success_body
:public | :guest | true | true | success_status | success_body
- :public | :developer | true | false | success_status | success_body
- :public | :guest | true | false | success_status | success_body
+ :public | :developer | true | false | :unauthorized | nil
+ :public | :guest | true | false | :unauthorized | nil
:public | :developer | false | true | success_status | success_body
:public | :guest | false | true | success_status | success_body
- :public | :developer | false | false | success_status | success_body
- :public | :guest | false | false | success_status | success_body
+ :public | :developer | false | false | :unauthorized | nil
+ :public | :guest | false | false | :unauthorized | nil
:public | :anonymous | false | true | success_status | success_body
:private | :developer | true | true | success_status | success_body
:private | :guest | true | true | :forbidden | nil
@@ -217,7 +354,7 @@ RSpec.shared_examples 'Debian repository read endpoint' do |desired_behavior, su
:private | :guest | false | true | :not_found | nil
:private | :developer | false | false | :unauthorized | nil
:private | :guest | false | false | :unauthorized | nil
- :private | :anonymous | false | true | :unauthorized | nil
+ :private | :anonymous | false | true | hidden_status | nil
end
with_them do
@@ -227,10 +364,16 @@ RSpec.shared_examples 'Debian repository read endpoint' do |desired_behavior, su
end
end
- it_behaves_like 'rejects Debian access with unknown container id'
+ it_behaves_like 'rejects Debian access with unknown container id', hidden_status
end
-RSpec.shared_examples 'Debian repository write endpoint' do |desired_behavior, success_status, success_body|
+RSpec.shared_examples 'Debian repository write endpoint' do |desired_behavior, success_status, success_body, authenticate_non_public: true|
+ hidden_status = if authenticate_non_public
+ :unauthorized
+ else
+ :not_found
+ end
+
context 'with valid container' do
using RSpec::Parameterized::TableSyntax
@@ -252,7 +395,50 @@ RSpec.shared_examples 'Debian repository write endpoint' do |desired_behavior, s
:private | :guest | false | true | :not_found | nil
:private | :developer | false | false | :unauthorized | nil
:private | :guest | false | false | :unauthorized | nil
- :private | :anonymous | false | true | :unauthorized | nil
+ :private | :anonymous | false | true | hidden_status | nil
+ end
+
+ with_them do
+ include_context 'Debian repository access', params[:visibility_level], params[:user_role], params[:member], params[:user_token], :basic do
+ it_behaves_like "Debian repository #{desired_behavior}", params[:expected_status], params[:expected_body]
+ end
+ end
+ end
+
+ it_behaves_like 'rejects Debian access with unknown container id', hidden_status
+end
+
+RSpec.shared_examples 'Debian repository maintainer write endpoint' do |desired_behavior, success_status, success_body, authenticate_non_public: true|
+ hidden_status = if authenticate_non_public
+ :unauthorized
+ else
+ :not_found
+ end
+
+ context 'with valid container' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:visibility_level, :user_role, :member, :user_token, :expected_status, :expected_body) do
+ :public | :maintainer | true | true | success_status | success_body
+ :public | :developer | true | true | :forbidden | nil
+ :public | :guest | true | true | :forbidden | nil
+ :public | :maintainer | true | false | :unauthorized | nil
+ :public | :guest | true | false | :unauthorized | nil
+ :public | :maintainer | false | true | :forbidden | nil
+ :public | :guest | false | true | :forbidden | nil
+ :public | :maintainer | false | false | :unauthorized | nil
+ :public | :guest | false | false | :unauthorized | nil
+ :public | :anonymous | false | true | :unauthorized | nil
+ :private | :maintainer | true | true | success_status | success_body
+ :private | :developer | true | true | :forbidden | nil
+ :private | :guest | true | true | :forbidden | nil
+ :private | :maintainer | true | false | :unauthorized | nil
+ :private | :guest | true | false | :unauthorized | nil
+ :private | :maintainer | false | true | :not_found | nil
+ :private | :guest | false | true | :not_found | nil
+ :private | :maintainer | false | false | :unauthorized | nil
+ :private | :guest | false | false | :unauthorized | nil
+ :private | :anonymous | false | true | hidden_status | nil
end
with_them do
@@ -262,5 +448,5 @@ RSpec.shared_examples 'Debian repository write endpoint' do |desired_behavior, s
end
end
- it_behaves_like 'rejects Debian access with unknown container id'
+ it_behaves_like 'rejects Debian access with unknown container id', hidden_status
end
diff --git a/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb
new file mode 100644
index 00000000000..41a61ba5fd7
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'a package detail' do
+ it_behaves_like 'a working graphql query' do
+ it 'matches the JSON schema' do
+ expect(package_details).to match_schema('graphql/packages/package_details')
+ end
+ end
+end
+
+RSpec.shared_examples 'a package with files' do
+ it 'has the right amount of files' do
+ expect(package_files_response.length).to be(package.package_files.length)
+ end
+
+ it 'has the basic package files data' do
+ expect(first_file_response).to include(
+ 'id' => global_id_of(first_file),
+ 'fileName' => first_file.file_name,
+ 'size' => first_file.size.to_s,
+ 'downloadPath' => first_file.download_path,
+ 'fileSha1' => first_file.file_sha1,
+ 'fileMd5' => first_file.file_md5,
+ 'fileSha256' => first_file.file_sha256
+ )
+ end
+end
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
new file mode 100644
index 00000000000..585c4fb8a4e
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'rejects helm packages access' do |user_type, status, add_member = true|
+ context "for user type #{user_type}" do
+ before do
+ project.send("add_#{user_type}", user) if add_member && user_type != :anonymous
+ end
+
+ it_behaves_like 'returning response status', status
+
+ if status == :unauthorized
+ it 'has the correct response header' do
+ subject
+
+ expect(response.headers['WWW-Authenticate']).to eq 'Basic realm="GitLab Packages Registry"'
+ end
+ end
+ end
+end
+
+RSpec.shared_examples 'process helm download content request' do |user_type, status, add_member = true|
+ context "for user type #{user_type}" do
+ before do
+ project.send("add_#{user_type}", user) if add_member && user_type != :anonymous
+ end
+
+ it_behaves_like 'returning response status', status
+
+ it_behaves_like 'a package tracking event', 'API::HelmPackages', 'pull_package'
+
+ it 'returns a valid package archive' do
+ subject
+
+ expect(response.media_type).to eq('application/octet-stream')
+ end
+ end
+end
+
+RSpec.shared_examples 'rejects helm access with unknown project id' do
+ context 'with an unknown project' do
+ let(:project) { OpenStruct.new(id: 1234567890) }
+
+ context 'as anonymous' do
+ it_behaves_like 'rejects helm packages access', :anonymous, :unauthorized
+ end
+
+ context 'as authenticated user' do
+ subject { get api(url), headers: basic_auth_header(user.username, personal_access_token.token) }
+
+ it_behaves_like 'rejects helm packages access', :anonymous, :not_found
+ end
+ 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 eb86b7c37d5..42c29084d7b 100644
--- a/spec/support/shared_examples/requests/api/packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/packages_shared_examples.rb
@@ -146,6 +146,6 @@ RSpec.shared_examples 'a package tracking event' do |category, action|
it "creates a gitlab tracking event #{action}", :snowplow do
expect { subject }.to change { Packages::Event.count }.by(1)
- expect_snowplow_event(category: category, action: action)
+ expect_snowplow_event(category: category, action: action, **snowplow_gitlab_standard_context)
end
end
diff --git a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
index bbcf856350d..8a351226123 100644
--- a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
@@ -110,6 +110,7 @@ RSpec.shared_examples 'PyPI package versions' do |user_type, status, add_member
context "for user type #{user_type}" do
before do
project.send("add_#{user_type}", user) if add_member && user_type != :anonymous
+ group.send("add_#{user_type}", user) if add_member && user_type != :anonymous
end
it 'returns the package listing' do
@@ -127,6 +128,7 @@ RSpec.shared_examples 'PyPI package download' do |user_type, status, add_member
context "for user type #{user_type}" do
before do
project.send("add_#{user_type}", user) if add_member && user_type != :anonymous
+ group.send("add_#{user_type}", user) if add_member && user_type != :anonymous
end
it 'returns the package listing' do
@@ -144,24 +146,185 @@ RSpec.shared_examples 'process PyPI api request' do |user_type, status, add_memb
context "for user type #{user_type}" do
before do
project.send("add_#{user_type}", user) if add_member && user_type != :anonymous
+ group.send("add_#{user_type}", user) if add_member && user_type != :anonymous
end
it_behaves_like 'returning response status', status
end
end
+RSpec.shared_examples 'unknown PyPI scope id' do
+ context 'as anonymous' do
+ it_behaves_like 'process PyPI api request', :anonymous, :not_found
+ end
+
+ context 'as authenticated user' do
+ subject { get api(url), headers: basic_auth_header(user.username, personal_access_token.token) }
+
+ it_behaves_like 'process PyPI api request', :anonymous, :not_found
+ end
+end
+
RSpec.shared_examples 'rejects PyPI access with unknown project id' do
context 'with an unknown project' do
let(:project) { OpenStruct.new(id: 1234567890) }
- context 'as anonymous' do
- it_behaves_like 'process PyPI api request', :anonymous, :not_found
+ it_behaves_like 'unknown PyPI scope id'
+ end
+end
+
+RSpec.shared_examples 'rejects PyPI access with unknown group id' do
+ context 'with an unknown project' do
+ let(:group) { OpenStruct.new(id: 1234567890) }
+
+ it_behaves_like 'unknown PyPI scope id'
+ end
+end
+
+RSpec.shared_examples 'pypi simple API 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 | 'PyPI package versions' | :success
+ :public | :guest | true | true | 'PyPI package versions' | :success
+ :public | :developer | true | false | 'PyPI package versions' | :success
+ :public | :guest | true | false | 'PyPI package versions' | :success
+ :public | :developer | false | true | 'PyPI package versions' | :success
+ :public | :guest | false | true | 'PyPI package versions' | :success
+ :public | :developer | false | false | 'PyPI package versions' | :success
+ :public | :guest | false | false | 'PyPI package versions' | :success
+ :public | :anonymous | false | true | 'PyPI package versions' | :success
+ :private | :developer | true | true | 'PyPI package versions' | :success
+ :private | :guest | true | true | 'process PyPI api request' | :forbidden
+ :private | :developer | true | false | 'process PyPI api request' | :unauthorized
+ :private | :guest | true | false | 'process PyPI api request' | :unauthorized
+ :private | :developer | false | true | 'process PyPI api request' | :not_found
+ :private | :guest | false | true | 'process PyPI api request' | :not_found
+ :private | :developer | false | false | 'process PyPI api request' | :unauthorized
+ :private | :guest | false | false | 'process PyPI api request' | :unauthorized
+ :private | :anonymous | false | true | 'process PyPI api request' | :unauthorized
end
- context 'as authenticated user' do
- subject { get api(url), headers: basic_auth_header(user.username, personal_access_token.token) }
+ with_them do
+ let(:token) { user_token ? personal_access_token.token : 'wrong' }
+ let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
- it_behaves_like 'process PyPI api request', :anonymous, :not_found
+ before do
+ project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility_level.to_s))
+ group.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility_level.to_s))
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
+ end
+ end
+
+ context 'with a normalized package name' do
+ let_it_be(:package) { create(:pypi_package, project: project, name: 'my.package') }
+
+ let(:url) { "/projects/#{project.id}/packages/pypi/simple/my-package" }
+ let(:headers) { basic_auth_header(user.username, personal_access_token.token) }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
+
+ it_behaves_like 'PyPI package versions', :developer, :success
+ end
+end
+
+RSpec.shared_examples 'pypi file download endpoint' do
+ using RSpec::Parameterized::TableSyntax
+
+ context 'with valid project' do
+ where(:visibility_level, :user_role, :member, :user_token) do
+ :public | :developer | true | true
+ :public | :guest | true | true
+ :public | :developer | true | false
+ :public | :guest | true | false
+ :public | :developer | false | true
+ :public | :guest | false | true
+ :public | :developer | false | false
+ :public | :guest | false | false
+ :public | :anonymous | false | true
+ :private | :developer | true | true
+ :private | :guest | true | true
+ :private | :developer | true | false
+ :private | :guest | true | false
+ :private | :developer | false | true
+ :private | :guest | false | true
+ :private | :developer | false | false
+ :private | :guest | false | false
+ :private | :anonymous | false | true
end
+
+ with_them do
+ let(:token) { user_token ? personal_access_token.token : 'wrong' }
+ let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
+
+ before do
+ project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility_level.to_s))
+ group.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility_level.to_s))
+ end
+
+ it_behaves_like 'PyPI package download', params[:user_role], :success, params[:member]
+ end
+ end
+
+ context 'with deploy token headers' do
+ let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token) }
+
+ context 'valid token' do
+ it_behaves_like 'returning response status', :success
+ end
+
+ context 'invalid token' do
+ let(:headers) { basic_auth_header('foo', 'bar') }
+
+ it_behaves_like 'returning response status', :success
+ end
+ end
+
+ context 'with job token headers' do
+ let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, job.token) }
+
+ context 'valid token' do
+ it_behaves_like 'returning response status', :success
+ end
+
+ context 'invalid token' do
+ let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, 'bar') }
+
+ it_behaves_like 'returning response status', :unauthorized
+ end
+
+ context 'invalid user' do
+ let(:headers) { basic_auth_header('foo', job.token) }
+
+ it_behaves_like 'returning response status', :success
+ end
+ end
+end
+
+RSpec.shared_examples 'a pypi user namespace endpoint' do
+ using RSpec::Parameterized::TableSyntax
+
+ # only group namespaces are supported at this time
+ where(:visibility_level, :user_role, :expected_status) do
+ :public | :owner | :not_found
+ :private | :owner | :not_found
+ :public | :external | :not_found
+ :private | :external | :not_found
+ :public | :anonymous | :not_found
+ :private | :anonymous | :not_found
+ end
+
+ with_them do
+ let_it_be_with_reload(:group) { create(:namespace) }
+ let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, personal_access_token.token) }
+
+ before do
+ group.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility_level.to_s))
+ group.update_column(:owner_id, user.id) if user_role == :owner
+ end
+
+ it_behaves_like 'returning response status', params[:expected_status]
end
end
diff --git a/spec/support/shared_examples/requests/api/resource_label_events_api_shared_examples.rb b/spec/support/shared_examples/requests/api/resource_label_events_api_shared_examples.rb
index 675b6c5cef6..2ac78131e08 100644
--- a/spec/support/shared_examples/requests/api/resource_label_events_api_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/resource_label_events_api_shared_examples.rb
@@ -48,7 +48,7 @@ RSpec.shared_examples 'resource_label_events API' do |parent_type, eventable_typ
get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_label_events", user)
expect(json_response).to be_an Array
- expect(json_response).to eq []
+ expect(json_response).to be_empty
end
end
end
diff --git a/spec/support/shared_examples/requests/api/tracking_shared_examples.rb b/spec/support/shared_examples/requests/api/tracking_shared_examples.rb
index 826139635ed..af13e3fc14d 100644
--- a/spec/support/shared_examples/requests/api/tracking_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/tracking_shared_examples.rb
@@ -4,6 +4,6 @@ RSpec.shared_examples 'a gitlab tracking event' do |category, action|
it "creates a gitlab tracking event #{action}", :snowplow do
subject
- expect_snowplow_event(category: category, action: action)
+ expect_snowplow_event(category: category, action: action, **snowplow_standard_context_params)
end
end
diff --git a/spec/support/shared_examples/services/clusters/parse_cluster_applications_artifact_shared_examples.rb b/spec/support/shared_examples/services/clusters/parse_cluster_applications_artifact_shared_examples.rb
deleted file mode 100644
index 466300017d9..00000000000
--- a/spec/support/shared_examples/services/clusters/parse_cluster_applications_artifact_shared_examples.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples 'parse cluster applications artifact' do |release_name|
- let(:application_class) { Clusters::Cluster::APPLICATIONS[release_name] }
- let(:cluster_application) { cluster.public_send("application_#{release_name}") }
- let(:file) { fixture_file_upload(Rails.root.join(fixture)) }
- let(:artifact) { create(:ci_job_artifact, :cluster_applications, job: job, file: file) }
-
- context 'release is missing' do
- let(:fixture) { "spec/fixtures/helm/helm_list_v2_#{release_name}_missing.json.gz" }
-
- context 'application does not exist' do
- it 'does not create or destroy an application' do
- expect do
- described_class.new(job, user).execute(artifact)
- end.not_to change(application_class, :count)
- end
- end
-
- context 'application exists' do
- before do
- create("clusters_applications_#{release_name}".to_sym, :installed, cluster: cluster)
- end
-
- it 'marks the application as uninstalled' do
- described_class.new(job, user).execute(artifact)
-
- cluster_application.reload
- expect(cluster_application).to be_uninstalled
- end
- end
- end
-
- context 'release is deployed' do
- let(:fixture) { "spec/fixtures/helm/helm_list_v2_#{release_name}_deployed.json.gz" }
-
- context 'application does not exist' do
- it 'creates an application and marks it as installed' do
- expect do
- described_class.new(job, user).execute(artifact)
- end.to change(application_class, :count)
-
- expect(cluster_application).to be_persisted
- expect(cluster_application).to be_externally_installed
- end
- end
-
- context 'application exists' do
- before do
- create("clusters_applications_#{release_name}".to_sym, :errored, cluster: cluster)
- end
-
- it 'marks the application as installed' do
- described_class.new(job, user).execute(artifact)
-
- expect(cluster_application).to be_externally_installed
- end
- end
- end
-
- context 'release is failed' do
- let(:fixture) { "spec/fixtures/helm/helm_list_v2_#{release_name}_failed.json.gz" }
-
- context 'application does not exist' do
- it 'creates an application and marks it as errored' do
- expect do
- described_class.new(job, user).execute(artifact)
- end.to change(application_class, :count)
-
- expect(cluster_application).to be_persisted
- expect(cluster_application).to be_errored
- expect(cluster_application.status_reason).to eq('Helm release failed to install')
- end
- end
-
- context 'application exists' do
- before do
- create("clusters_applications_#{release_name}".to_sym, :installed, cluster: cluster)
- end
-
- it 'marks the application as errored' do
- described_class.new(job, user).execute(artifact)
-
- expect(cluster_application).to be_errored
- expect(cluster_application.status_reason).to eq('Helm release failed to install')
- end
- end
- end
-end
diff --git a/spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb b/spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb
new file mode 100644
index 00000000000..9ffeba1b1d0
--- /dev/null
+++ b/spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb
@@ -0,0 +1,166 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'Generate Debian Distribution and component files' do
+ let_it_be(:component_main) { create("debian_#{container_type}_component", distribution: distribution, name: 'main') }
+ let_it_be(:component_contrib) { create("debian_#{container_type}_component", distribution: distribution, name: 'contrib') }
+
+ let_it_be(:architecture_all) { create("debian_#{container_type}_architecture", distribution: distribution, name: 'all') }
+ let_it_be(:architecture_amd64) { create("debian_#{container_type}_architecture", distribution: distribution, name: 'amd64') }
+ let_it_be(:architecture_arm64) { create("debian_#{container_type}_architecture", distribution: distribution, name: 'arm64') }
+
+ let_it_be(:component_file1) { create("debian_#{container_type}_component_file", component: component_contrib, architecture: architecture_all, updated_at: '2020-01-24T08:00:00Z', file_sha256: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', file_md5: 'd41d8cd98f00b204e9800998ecf8427e', file_fixture: nil, size: 0) } # updated
+ let_it_be(:component_file2) { create("debian_#{container_type}_component_file", component: component_main, architecture: architecture_all, updated_at: '2020-01-24T09:00:00Z', file_sha256: 'a') } # destroyed
+ let_it_be(:component_file3) { create("debian_#{container_type}_component_file", component: component_main, architecture: architecture_amd64, updated_at: '2020-01-24T10:54:59Z', file_sha256: 'b') } # destroyed, 1 second before last generation
+ let_it_be(:component_file4) { create("debian_#{container_type}_component_file", component: component_contrib, architecture: architecture_all, updated_at: '2020-01-24T10:55:00Z', file_sha256: 'c') } # kept, last generation
+ let_it_be(:component_file5) { create("debian_#{container_type}_component_file", component: component_contrib, architecture: architecture_all, updated_at: '2020-01-24T10:55:00Z', file_sha256: 'd') } # kept, last generation
+ let_it_be(:component_file6) { create("debian_#{container_type}_component_file", component: component_contrib, architecture: architecture_amd64, updated_at: '2020-01-25T15:17:18Z', file_sha256: 'e') } # kept, less than 1 hour ago
+
+ def check_component_file(release_date, component_name, component_file_type, architecture_name, expected_content)
+ component_file = distribution
+ .component_files
+ .with_component_name(component_name)
+ .with_file_type(component_file_type)
+ .with_architecture_name(architecture_name)
+ .order_updated_asc
+ .last
+
+ expect(component_file).not_to be_nil
+ expect(component_file.updated_at).to eq(release_date)
+
+ unless expected_content.nil?
+ component_file.file.use_file do |file_path|
+ expect(File.read(file_path)).to eq(expected_content)
+ end
+ end
+ end
+
+ it 'generates Debian distribution and component files', :aggregate_failures do
+ current_time = Time.utc(2020, 01, 25, 15, 17, 18, 123456)
+
+ travel_to(current_time) do
+ expect(Gitlab::ErrorTracking).not_to receive(:log_exception)
+
+ initial_count = 6
+ destroyed_count = 2
+ # updated_count = 1
+ created_count = 5
+
+ expect { subject }
+ .to not_change { Packages::Package.count }
+ .and not_change { Packages::PackageFile.count }
+ .and change { distribution.reload.updated_at }.to(current_time.round)
+ .and change { distribution.component_files.reset.count }.from(initial_count).to(initial_count - destroyed_count + created_count)
+ .and change { component_file1.reload.updated_at }.to(current_time.round)
+
+ debs = package.package_files.with_debian_file_type(:deb).preload_debian_file_metadata.to_a
+ pool_prefix = "pool/unstable/#{project.id}/p/#{package.name}"
+ expected_main_amd64_content = <<~EOF
+ Package: libsample0
+ Source: #{package.name}
+ Version: #{package.version}
+ Installed-Size: 7
+ Maintainer: #{debs[0].debian_fields['Maintainer']}
+ Architecture: amd64
+ Description: Some mostly empty lib
+ Used in GitLab tests.
+ .
+ Testing another paragraph.
+ Multi-Arch: same
+ Homepage: #{debs[0].debian_fields['Homepage']}
+ Section: libs
+ Priority: optional
+ Filename: #{pool_prefix}/libsample0_1.2.3~alpha2_amd64.deb
+ Size: 409600
+ MD5sum: #{debs[0].file_md5}
+ SHA256: #{debs[0].file_sha256}
+
+ Package: sample-dev
+ Source: #{package.name} (#{package.version})
+ Version: 1.2.3~binary
+ Installed-Size: 7
+ Maintainer: #{debs[1].debian_fields['Maintainer']}
+ Architecture: amd64
+ Depends: libsample0 (= 1.2.3~binary)
+ Description: Some mostly empty development files
+ Used in GitLab tests.
+ .
+ Testing another paragraph.
+ Multi-Arch: same
+ Homepage: #{debs[1].debian_fields['Homepage']}
+ Section: libdevel
+ Priority: optional
+ Filename: #{pool_prefix}/sample-dev_1.2.3~binary_amd64.deb
+ Size: 409600
+ MD5sum: #{debs[1].file_md5}
+ SHA256: #{debs[1].file_sha256}
+ EOF
+
+ check_component_file(current_time.round, 'main', :packages, 'all', nil)
+ check_component_file(current_time.round, 'main', :packages, 'amd64', expected_main_amd64_content)
+ check_component_file(current_time.round, 'main', :packages, 'arm64', nil)
+
+ check_component_file(current_time.round, 'contrib', :packages, 'all', nil)
+ check_component_file(current_time.round, 'contrib', :packages, 'amd64', nil)
+ check_component_file(current_time.round, 'contrib', :packages, 'arm64', nil)
+
+ main_amd64_size = expected_main_amd64_content.length
+ main_amd64_md5sum = Digest::MD5.hexdigest(expected_main_amd64_content)
+ main_amd64_sha256 = Digest::SHA256.hexdigest(expected_main_amd64_content)
+
+ contrib_all_size = component_file1.size
+ contrib_all_md5sum = component_file1.file_md5
+ contrib_all_sha256 = component_file1.file_sha256
+
+ expected_release_content = <<~EOF
+ Codename: unstable
+ Date: Sat, 25 Jan 2020 15:17:18 +0000
+ Valid-Until: Mon, 27 Jan 2020 15:17:18 +0000
+ Architectures: all amd64 arm64
+ Components: contrib main
+ MD5Sum:
+ #{contrib_all_md5sum} #{contrib_all_size} contrib/binary-all/Packages
+ d41d8cd98f00b204e9800998ecf8427e 0 contrib/binary-amd64/Packages
+ d41d8cd98f00b204e9800998ecf8427e 0 contrib/binary-arm64/Packages
+ d41d8cd98f00b204e9800998ecf8427e 0 main/binary-all/Packages
+ #{main_amd64_md5sum} #{main_amd64_size} main/binary-amd64/Packages
+ d41d8cd98f00b204e9800998ecf8427e 0 main/binary-arm64/Packages
+ SHA256:
+ #{contrib_all_sha256} #{contrib_all_size} contrib/binary-all/Packages
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/binary-amd64/Packages
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/binary-arm64/Packages
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-all/Packages
+ #{main_amd64_sha256} #{main_amd64_size} main/binary-amd64/Packages
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-arm64/Packages
+ EOF
+
+ distribution.file.use_file do |file_path|
+ expect(File.read(file_path)).to eq(expected_release_content)
+ end
+ end
+ end
+end
+
+RSpec.shared_examples 'Generate minimal Debian Distribution' do
+ it 'generates minimal distribution', :aggregate_failures do
+ travel_to(Time.utc(2020, 01, 25, 15, 17, 18, 123456)) do
+ expect(Gitlab::ErrorTracking).not_to receive(:log_exception)
+
+ expect { subject }
+ .to not_change { Packages::Package.count }
+ .and not_change { Packages::PackageFile.count }
+ .and not_change { distribution.component_files.reset.count }
+
+ expected_release_content = <<~EOF
+ Codename: unstable
+ Date: Sat, 25 Jan 2020 15:17:18 +0000
+ Valid-Until: Mon, 27 Jan 2020 15:17:18 +0000
+ MD5Sum:
+ SHA256:
+ EOF
+
+ distribution.file.use_file do |file_path|
+ expect(File.read(file_path)).to eq(expected_release_content)
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/services/users/build_service_shared_examples.rb b/spec/support/shared_examples/services/users/build_service_shared_examples.rb
new file mode 100644
index 00000000000..6a8695e1786
--- /dev/null
+++ b/spec/support/shared_examples/services/users/build_service_shared_examples.rb
@@ -0,0 +1,125 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'common user build items' do
+ it { is_expected.to be_valid }
+
+ it 'sets the created_by_id' do
+ expect(user.created_by_id).to eq(current_user&.id)
+ end
+
+ it 'calls UpdateCanonicalEmailService' do
+ expect(Users::UpdateCanonicalEmailService).to receive(:new).and_call_original
+
+ user
+ end
+
+ context 'when user_type is provided' do
+ context 'when project_bot' do
+ before do
+ params.merge!({ user_type: :project_bot })
+ end
+
+ it { expect(user.project_bot?).to be true }
+ end
+
+ context 'when not a project_bot' do
+ before do
+ params.merge!({ user_type: :alert_bot })
+ end
+
+ it { expect(user).to be_human }
+ end
+ end
+end
+
+RSpec.shared_examples_for 'current user not admin build items' do
+ using RSpec::Parameterized::TableSyntax
+
+ context 'with "user_default_external" application setting' do
+ where(:user_default_external, :external, :email, :user_default_internal_regex, :result) do
+ true | nil | 'fl@example.com' | nil | true
+ true | true | 'fl@example.com' | nil | true
+ true | false | 'fl@example.com' | nil | true # admin difference
+
+ true | nil | 'fl@example.com' | '' | true
+ true | true | 'fl@example.com' | '' | true
+ true | false | 'fl@example.com' | '' | true # admin difference
+
+ true | nil | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false
+ true | true | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false # admin difference
+ true | false | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false
+
+ true | nil | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | true
+ true | true | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | true
+ true | false | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | true # admin difference
+
+ false | nil | 'fl@example.com' | nil | false
+ false | true | 'fl@example.com' | nil | false # admin difference
+ false | false | 'fl@example.com' | nil | false
+
+ false | nil | 'fl@example.com' | '' | false
+ false | true | 'fl@example.com' | '' | false # admin difference
+ false | false | 'fl@example.com' | '' | false
+
+ false | nil | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false
+ false | true | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false # admin difference
+ false | false | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false
+
+ false | nil | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | false
+ false | true | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | false # admin difference
+ false | false | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | false
+ end
+
+ with_them do
+ before do
+ stub_application_setting(user_default_external: user_default_external)
+ stub_application_setting(user_default_internal_regex: user_default_internal_regex)
+
+ params.merge!({ external: external, email: email }.compact)
+ end
+
+ it 'sets the value of Gitlab::CurrentSettings.user_default_external' do
+ expect(user.external).to eq(result)
+ end
+ end
+ end
+
+ context 'when "send_user_confirmation_email" application setting is true' do
+ before do
+ stub_application_setting(send_user_confirmation_email: true, signup_enabled?: true)
+ end
+
+ it 'does not confirm the user' do
+ expect(user).not_to be_confirmed
+ end
+ end
+
+ context 'when "send_user_confirmation_email" application setting is false' do
+ before do
+ stub_application_setting(send_user_confirmation_email: false, signup_enabled?: true)
+ end
+
+ it 'confirms the user' do
+ expect(user).to be_confirmed
+ end
+ end
+
+ context 'with allowed params' do
+ let(:params) do
+ {
+ email: 1,
+ name: 1,
+ password: 1,
+ password_automatically_set: 1,
+ username: 1,
+ user_type: 'project_bot'
+ }
+ end
+
+ it 'sets all allowed attributes' do
+ expect(User).to receive(:new).with(hash_including(params)).and_call_original
+
+ user
+ end
+ end
+end
diff --git a/spec/support/shared_examples/uncached_response_shared_examples.rb b/spec/support/shared_examples/uncached_response_shared_examples.rb
deleted file mode 100644
index 3997017ff35..00000000000
--- a/spec/support/shared_examples/uncached_response_shared_examples.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-# frozen_string_literal: true
-#
-# Pairs with lib/gitlab/no_cache_headers.rb
-#
-
-RSpec.shared_examples 'uncached response' do
- it 'defines an uncached header response' do
- expect(response.headers["Cache-Control"]).to include("no-store", "no-cache")
- expect(response.headers["Pragma"]).to eq("no-cache")
- expect(response.headers["Expires"]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
- end
-end