summaryrefslogtreecommitdiff
path: root/spec/support/shared_examples
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-05-19 15:44:42 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-05-19 15:44:42 +0000
commit4555e1b21c365ed8303ffb7a3325d773c9b8bf31 (patch)
tree5423a1c7516cffe36384133ade12572cf709398d /spec/support/shared_examples
parente570267f2f6b326480d284e0164a6464ba4081bc (diff)
downloadgitlab-ce-4555e1b21c365ed8303ffb7a3325d773c9b8bf31.tar.gz
Add latest changes from gitlab-org/gitlab@13-12-stable-eev13.12.0-rc42
Diffstat (limited to 'spec/support/shared_examples')
-rw-r--r--spec/support/shared_examples/alert_notification_service_shared_examples.rb44
-rw-r--r--spec/support/shared_examples/boards/lists/update_service_shared_examples.rb28
-rw-r--r--spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/features/board_sidebar_labels_examples.rb77
-rw-r--r--spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb27
-rw-r--r--spec/support/shared_examples/features/sidebar_shared_examples.rb39
-rw-r--r--spec/support/shared_examples/features/variable_list_shared_examples.rb24
-rw-r--r--spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb22
-rw-r--r--spec/support/shared_examples/finders/packages_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/graphql/mutations/boards/update_list_shared_examples.rb48
-rw-r--r--spec/support/shared_examples/graphql/mutations/security/ci_configuration_shared_examples.rb114
-rw-r--r--spec/support/shared_examples/graphql/resolvers/packages_resolvers_shared_examples.rb63
-rw-r--r--spec/support/shared_examples/lib/gitlab/diff_file_collections_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/lib/gitlab/jwt_token_shared_examples.rb49
-rw-r--r--spec/support/shared_examples/lib/gitlab/sql/set_operator_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/chat_service_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/chat_slash_commands_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/clusters/elastic_stack_client_shared.rb82
-rw-r--r--spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/concerns/cron_schedulable_shared_examples.rb23
-rw-r--r--spec/support/shared_examples/models/concerns/repository_storage_movable_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/models/packages/debian/architecture_shared_examples.rb18
-rw-r--r--spec/support/shared_examples/models/packages/debian/component_file_shared_example.rb55
-rw-r--r--spec/support/shared_examples/models/packages/debian/component_shared_examples.rb12
-rw-r--r--spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb45
-rw-r--r--spec/support/shared_examples/models/slack_mattermost_notifications_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/wiki_shared_examples.rb42
-rw-r--r--spec/support/shared_examples/namespaces/traversal_examples.rb (renamed from spec/support/shared_examples/namespaces/namespace_traversal_examples.rb)17
-rw-r--r--spec/support/shared_examples/policies/project_policy_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb345
-rw-r--r--spec/support/shared_examples/requests/api/graphql/mutations/boards/update_list_shared_examples.rb43
-rw-r--r--spec/support/shared_examples/requests/api/graphql/mutations/destroy_list_shared_examples.rb87
-rw-r--r--spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb129
-rw-r--r--spec/support/shared_examples/requests/api/issuable_update_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/multiple_and_scoped_issue_boards_shared_examples.rb1
-rw-r--r--spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb251
-rw-r--r--spec/support/shared_examples/requests/api/time_tracking_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/requests/clusters/integrations_controller_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/rack_attack_shared_examples.rb5
-rw-r--r--spec/support/shared_examples/row_lock_shared_examples.rb27
-rw-r--r--spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb20
-rw-r--r--spec/support/shared_examples/serializers/pipeline_artifacts_shared_example.rb21
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb161
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/alert_recovery_shared_examples.rb113
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/incident_creation_shared_examples.rb52
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/incident_resolution_shared_examples.rb44
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/notifications_shared_examples.rb34
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/system_notes_shared_examples.rb34
-rw-r--r--spec/support/shared_examples/services/alert_management_shared_examples.rb136
-rw-r--r--spec/support/shared_examples/services/boards/boards_recent_visit_shared_examples.rb65
-rw-r--r--spec/support/shared_examples/services/boards/create_service_shared_examples.rb25
-rw-r--r--spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/boards/lists_destroy_service_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/services/common_system_notes_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/destroy_label_links_shared_examples.rb20
-rw-r--r--spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb24
-rw-r--r--spec/support/shared_examples/services/issuable_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/merge_request_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb11
-rw-r--r--spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/packages_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/schedule_bulk_repository_shard_moves_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb91
-rw-r--r--spec/support/shared_examples/services/updating_mentions_shared_examples.rb2
70 files changed, 2096 insertions, 594 deletions
diff --git a/spec/support/shared_examples/alert_notification_service_shared_examples.rb b/spec/support/shared_examples/alert_notification_service_shared_examples.rb
deleted file mode 100644
index fc935effe0e..00000000000
--- a/spec/support/shared_examples/alert_notification_service_shared_examples.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples 'Alert Notification Service sends notification email' do
- let(:notification_service) { spy }
-
- it 'sends a notification' do
- expect(NotificationService)
- .to receive(:new)
- .and_return(notification_service)
-
- expect(notification_service)
- .to receive_message_chain(:async, :prometheus_alerts_fired)
-
- expect(subject).to be_success
- end
-end
-
-RSpec.shared_examples 'Alert Notification Service sends no notifications' do |http_status: nil|
- it 'does not notify' do
- expect(NotificationService).not_to receive(:new)
-
- if http_status.present?
- expect(subject).to be_error
- expect(subject.http_status).to eq(http_status)
- else
- expect(subject).to be_success
- end
- end
-end
-
-RSpec.shared_examples 'creates status-change system note for an auto-resolved alert' do
- it 'has 2 new system notes' do
- expect { subject }.to change(Note, :count).by(2)
- expect(Note.last.note).to include('Resolved')
- end
-end
-
-# Requires `source` to be defined
-RSpec.shared_examples 'creates single system note based on the source of the alert' do
- it 'has one new system note' do
- expect { subject }.to change(Note, :count).by(1)
- expect(Note.last.note).to include(source)
- end
-end
diff --git a/spec/support/shared_examples/boards/lists/update_service_shared_examples.rb b/spec/support/shared_examples/boards/lists/update_service_shared_examples.rb
index d8a74f2582d..1fab31cd513 100644
--- a/spec/support/shared_examples/boards/lists/update_service_shared_examples.rb
+++ b/spec/support/shared_examples/boards/lists/update_service_shared_examples.rb
@@ -2,14 +2,30 @@
RSpec.shared_examples 'moving list' do
context 'when user can admin list' do
- it 'calls Lists::MoveService to update list position' do
+ before do
board.resource_parent.add_developer(user)
+ end
+
+ context 'when the new position is valid' do
+ it 'calls Lists::MoveService to update list position' do
+ expect_next_instance_of(Boards::Lists::MoveService, board.resource_parent, user, params) do |move_service|
+ expect(move_service).to receive(:execute).with(list).and_call_original
+ end
- expect_next_instance_of(Boards::Lists::MoveService, board.resource_parent, user, params) do |move_service|
- expect(move_service).to receive(:execute).with(list).and_call_original
+ service.execute(list)
end
- service.execute(list)
+ it 'returns a success response' do
+ expect(service.execute(list)).to be_success
+ end
+ end
+
+ context 'when the new position is invalid' do
+ let(:params) { { position: 10 } }
+
+ it 'returns error response' do
+ expect(service.execute(list)).to be_error
+ end
end
end
@@ -19,6 +35,10 @@ RSpec.shared_examples 'moving list' do
service.execute(list)
end
+
+ it 'returns an error response' do
+ expect(service.execute(list)).to be_error
+ end
end
end
diff --git a/spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb b/spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb
index dd71107455f..70a684c12bf 100644
--- a/spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb
@@ -40,7 +40,7 @@ RSpec.shared_examples 'project access tokens available #create' do
it 'returns success message' do
subject
- expect(response.flash[:notice]).to match('Your new project access token has been created.')
+ expect(controller).to set_flash[:notice].to match('Your new project access token has been created.')
end
it 'creates project access token' do
@@ -88,7 +88,7 @@ RSpec.shared_examples 'project access tokens available #create' do
it 'shows a failure alert' do
subject
- expect(response.flash[:alert]).to match("Failed to create new project access token: Failed!")
+ expect(controller).to set_flash[:alert].to match("Failed to create new project access token: Failed!")
end
end
end
diff --git a/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb b/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb
index 5ecc5c08bbd..a4eb6a839c0 100644
--- a/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb
@@ -35,7 +35,7 @@ RSpec.shared_examples 'issuable notes filter' do
get :discussions, params: params.merge(notes_filter: notes_filter)
end
- it 'does not set notes filter when database is in read only mode' do
+ it 'does not set notes filter when database is in read-only mode' do
allow(Gitlab::Database).to receive(:read_only?).and_return(true)
notes_filter = UserPreference::NOTES_FILTERS[:only_comments]
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 0a040557ffe..cfee26a0d6a 100644
--- a/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
@@ -130,8 +130,8 @@ RSpec.shared_examples 'wiki controller actions' do
it_behaves_like 'fetching history', :ok do
let(:allow_read_wiki) { true }
- it 'assigns @page_versions' do
- expect(assigns(:page_versions)).to be_present
+ it 'assigns @commits' do
+ expect(assigns(:commits)).to be_present
end
end
diff --git a/spec/support/shared_examples/features/board_sidebar_labels_examples.rb b/spec/support/shared_examples/features/board_sidebar_labels_examples.rb
new file mode 100644
index 00000000000..520980c2615
--- /dev/null
+++ b/spec/support/shared_examples/features/board_sidebar_labels_examples.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'labels from nested groups and projects' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:group_label) { create(:group_label, group: group, name: 'Group label') }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:project_label) { create(:label, project: project, name: 'Project label') }
+
+ let_it_be(:subgroup) { create(:group, parent: group) }
+ let_it_be(:subgroup_label) { create(:group_label, group: subgroup, name: 'Subgroup label') }
+ let_it_be(:subproject) { create(:project, group: subgroup) }
+ let_it_be(:subproject_label) { create(:label, project: subproject, name: 'Subproject label') }
+
+ let_it_be(:subgroup2) { create(:group, parent: group) }
+ let_it_be(:subgroup2_label) { create(:group_label, group: subgroup2, name: 'Subgroup2 label') }
+
+ let_it_be(:maintainer) { create(:user) }
+
+ let(:labels_select) { find("[data-testid='sidebar-labels']") }
+ let(:labels_dropdown) { labels_select.find('[data-testid="dropdown-content"]')}
+
+ before do
+ group.add_maintainer(maintainer)
+
+ sign_in(maintainer)
+ end
+end
+
+RSpec.shared_examples "an issue from a subgroup's project is selected" do
+ context 'when editing labels' do
+ before do
+ click_card_and_edit_label
+ end
+
+ it 'displays the label from the top-level group' do
+ expect(labels_dropdown).to have_content(group_label.name)
+ end
+
+ it 'displays the label from the subgroup' do
+ expect(labels_dropdown).to have_content(subgroup_label.name)
+ end
+
+ it 'displays the label from the project' do
+ expect(labels_dropdown).to have_content(subproject_label.name)
+ end
+
+ it "does not display labels from the subgroup's siblings (project or group)" do
+ aggregate_failures do
+ expect(labels_dropdown).not_to have_content(project_label.name)
+ expect(labels_dropdown).not_to have_content(subgroup2_label.name)
+ end
+ end
+ end
+end
+
+RSpec.shared_examples 'an issue from a direct descendant project is selected' do
+ context 'when editing labels' do
+ before do
+ click_card_and_edit_label
+ end
+
+ it 'displays the label from the top-level group' do
+ expect(labels_dropdown).to have_content(group_label.name)
+ end
+
+ it 'displays the label from the project' do
+ expect(labels_dropdown).to have_content(project_label.name)
+ end
+
+ it "does not display labels from the project's siblings or their descendents" do
+ aggregate_failures do
+ expect(labels_dropdown).not_to have_content(subgroup_label.name)
+ expect(labels_dropdown).not_to have_content(subproject_label.name)
+ end
+ end
+ 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 49c3674277d..736c353c2aa 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
@@ -22,32 +22,7 @@ RSpec.shared_examples 'issuable invite members experiments' do
end
end
- context 'when invite_members_version_b experiment is enabled' do
- before do
- stub_experiment_for_subject(invite_members_version_b: true)
- end
-
- it 'shows a link for inviting members and follows through to modal' do
- project.add_developer(user)
- visit issuable_path
-
- find('.block.assignee .edit-link').click
-
- wait_for_requests
-
- page.within '.dropdown-menu-user' do
- expect(page).to have_link('Invite Members', href: '#')
- expect(page).to have_selector('[data-track-event="click_invite_members_version_b"]')
- expect(page).to have_selector('[data-track-label="edit_assignee"]')
- end
-
- click_link 'Invite Members'
-
- expect(page).to have_content("Oops, this feature isn't ready yet")
- end
- end
-
- context 'when invite_members_version_b experiment is disabled' do
+ context 'when user cannot invite members in assignee dropdown' do
it 'shows author in assignee dropdown and no invite link' do
project.add_developer(user)
visit issuable_path
diff --git a/spec/support/shared_examples/features/sidebar_shared_examples.rb b/spec/support/shared_examples/features/sidebar_shared_examples.rb
index 429efbe6ba0..c9508818f74 100644
--- a/spec/support/shared_examples/features/sidebar_shared_examples.rb
+++ b/spec/support/shared_examples/features/sidebar_shared_examples.rb
@@ -44,22 +44,24 @@ RSpec.shared_examples 'issue boards sidebar' do
context 'in notifications subscription' do
it 'displays notifications toggle', :aggregate_failures do
page.within('[data-testid="sidebar-notifications"]') do
- expect(page).to have_selector('[data-testid="notification-subscribe-toggle"]')
+ expect(page).to have_selector('[data-testid="subscription-toggle"]')
expect(page).to have_content('Notifications')
- expect(page).not_to have_content('Notifications have been disabled by the project or group owner')
+ expect(page).not_to have_content('Disabled by project owner')
end
end
it 'shows toggle as on then as off as user toggles to subscribe and unsubscribe', :aggregate_failures do
- toggle = find('[data-testid="notification-subscribe-toggle"]')
+ wait_for_requests
- toggle.click
+ click_button 'Notifications'
- expect(toggle).to have_css("button.is-checked")
+ expect(page).to have_button('Notifications', class: 'is-checked')
- toggle.click
+ click_button 'Notifications'
- expect(toggle).not_to have_css("button.is-checked")
+ wait_for_requests
+
+ expect(page).not_to have_button('Notifications', class: 'is-checked')
end
context 'when notifications have been disabled' do
@@ -71,9 +73,28 @@ RSpec.shared_examples 'issue boards sidebar' do
it 'displays a message that notifications have been disabled' do
page.within('[data-testid="sidebar-notifications"]') do
- expect(page).not_to have_selector('[data-testid="notification-subscribe-toggle"]')
- expect(page).to have_content('Notifications have been disabled by the project or group owner')
+ expect(page).to have_button('Notifications', class: 'is-disabled')
+ expect(page).to have_content('Disabled by project owner')
+ end
+ end
+ end
+ end
+
+ context 'confidentiality' do
+ it 'make issue confidential' do
+ page.within('.confidentiality') do
+ expect(page).to have_content('Not confidential')
+
+ click_button 'Edit'
+ expect(page).to have_css('.sidebar-item-warning-message')
+
+ within('.sidebar-item-warning-message') do
+ click_button 'Turn on'
end
+
+ wait_for_requests
+
+ expect(page).to have_content('This issue is confidential')
end
end
end
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 2fd88b610e9..4b94411f009 100644
--- a/spec/support/shared_examples/features/variable_list_shared_examples.rb
+++ b/spec/support/shared_examples/features/variable_list_shared_examples.rb
@@ -8,7 +8,7 @@ RSpec.shared_examples 'variable list' do
end
it 'adds a new CI variable' do
- click_button('Add Variable')
+ click_button('Add variable')
fill_variable('key', 'key_value') do
click_button('Add variable')
@@ -22,7 +22,7 @@ RSpec.shared_examples 'variable list' do
end
it 'adds a new protected variable' do
- click_button('Add Variable')
+ click_button('Add variable')
fill_variable('key', 'key_value') do
click_button('Add variable')
@@ -37,7 +37,7 @@ RSpec.shared_examples 'variable list' do
end
it 'defaults to unmasked' do
- click_button('Add Variable')
+ click_button('Add variable')
fill_variable('key', 'key_value') do
click_button('Add variable')
@@ -149,7 +149,7 @@ RSpec.shared_examples 'variable list' do
end
it 'shows a validation error box about duplicate keys' do
- click_button('Add Variable')
+ click_button('Add variable')
fill_variable('key', 'key_value') do
click_button('Add variable')
@@ -157,7 +157,7 @@ RSpec.shared_examples 'variable list' do
wait_for_requests
- click_button('Add Variable')
+ click_button('Add variable')
fill_variable('key', 'key_value') do
click_button('Add variable')
@@ -170,7 +170,7 @@ RSpec.shared_examples 'variable list' do
end
it 'prevents a variable to be added if no values are provided when a variable is set to masked' do
- click_button('Add Variable')
+ click_button('Add variable')
page.within('#add-ci-variable') do
find('[data-qa-selector="ci_variable_key_field"] input').set('empty_mask_key')
@@ -182,7 +182,7 @@ RSpec.shared_examples 'variable list' do
end
it 'shows validation error box about unmaskable values' do
- click_button('Add Variable')
+ click_button('Add variable')
fill_variable('empty_mask_key', '???', protected: true, masked: true) do
expect(page).to have_content('This variable can not be masked')
@@ -192,7 +192,7 @@ RSpec.shared_examples 'variable list' do
it 'handles multiple edits and a deletion' do
# Create two variables
- click_button('Add Variable')
+ click_button('Add variable')
fill_variable('akey', 'akeyvalue') do
click_button('Add variable')
@@ -200,7 +200,7 @@ RSpec.shared_examples 'variable list' do
wait_for_requests
- click_button('Add Variable')
+ click_button('Add variable')
fill_variable('zkey', 'zkeyvalue') do
click_button('Add variable')
@@ -224,7 +224,7 @@ RSpec.shared_examples 'variable list' do
wait_for_requests
# Add another variable
- click_button('Add Variable')
+ click_button('Add variable')
fill_variable('ckey', 'ckeyvalue') do
click_button('Add variable')
@@ -249,7 +249,7 @@ RSpec.shared_examples 'variable list' do
end
it 'defaults to protected' do
- click_button('Add Variable')
+ click_button('Add variable')
page.within('#add-ci-variable') do
expect(find('[data-testid="ci-variable-protected-checkbox"]')).to be_checked
@@ -269,7 +269,7 @@ RSpec.shared_examples 'variable list' do
end
it 'defaults to unprotected' do
- click_button('Add Variable')
+ click_button('Add variable')
page.within('#add-ci-variable') do
expect(find('[data-testid="ci-variable-protected-checkbox"]')).not_to be_checked
diff --git a/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb
index 8a6d5d88ca6..f2576931642 100644
--- a/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb
@@ -24,8 +24,8 @@ RSpec.shared_examples 'User creates wiki page' do
page.within(".wiki-form") do
fill_in(:wiki_content, with: "")
- page.execute_script("window.onbeforeunload = null")
page.execute_script("document.querySelector('.wiki-form').submit()")
+ page.accept_alert # manually force form submit
end
expect(page).to have_content("The form contains the following error:").and have_content("Content can't be blank")
diff --git a/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb
index d185e9dd81c..db2a96d9649 100644
--- a/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb
@@ -93,8 +93,8 @@ RSpec.shared_examples 'User updates wiki page' do
it 'shows a validation error message if the form is force submitted', :js do
fill_in(:wiki_content, with: '')
- page.execute_script("window.onbeforeunload = null")
page.execute_script("document.querySelector('.wiki-form').submit()")
+ page.accept_alert # manually force form submit
expect(page).to have_selector('.wiki-form')
expect(page).to have_content('Edit Page')
@@ -117,14 +117,6 @@ RSpec.shared_examples 'User updates wiki page' do
expect(page).to have_selector('.atwho-view')
end
- it 'shows the error message', :js do
- wiki_page.update(content: 'Update') # rubocop:disable Rails/SaveBang
-
- click_button('Save changes')
-
- expect(page).to have_content('Someone edited the page the same time you did.')
- end
-
it 'updates a page', :js do
fill_in('Content', with: 'Updated Wiki Content')
click_on('Save changes')
@@ -145,6 +137,18 @@ RSpec.shared_examples 'User updates wiki page' do
end
it_behaves_like 'wiki file attachments'
+
+ context 'when multiple people edit the page at the same time' do
+ it 'preserves user changes in the wiki editor', :js do
+ wiki_page.update(content: 'Some Other Updates') # rubocop:disable Rails/SaveBang
+
+ fill_in('Content', with: 'Updated Wiki Content')
+ click_on('Save changes')
+
+ expect(page).to have_content('Someone edited the page the same time you did.')
+ expect(find('textarea#wiki_content').value).to eq('Updated Wiki Content')
+ end
+ end
end
context 'when the page is in a subdir', :js do
diff --git a/spec/support/shared_examples/finders/packages_shared_examples.rb b/spec/support/shared_examples/finders/packages_shared_examples.rb
index 2d4e8d0df1f..b3ec2336cca 100644
--- a/spec/support/shared_examples/finders/packages_shared_examples.rb
+++ b/spec/support/shared_examples/finders/packages_shared_examples.rb
@@ -20,9 +20,11 @@ end
RSpec.shared_examples 'concerning package statuses' do
let_it_be(:hidden_package) { create(:maven_package, :hidden, project: project) }
+ let_it_be(:error_package) { create(:maven_package, :error, project: project) }
- context 'hidden packages' do
+ context 'displayable packages' do
it { is_expected.not_to include(hidden_package) }
+ it { is_expected.to include(error_package) }
end
context 'with status param' do
diff --git a/spec/support/shared_examples/graphql/mutations/boards/update_list_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/boards/update_list_shared_examples.rb
new file mode 100644
index 00000000000..4385cd519be
--- /dev/null
+++ b/spec/support/shared_examples/graphql/mutations/boards/update_list_shared_examples.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'update board list mutation' do
+ describe '#resolve' do
+ let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
+ let(:list_update_params) { { position: 1, collapsed: true } }
+
+ subject { mutation.resolve(list: list, **list_update_params) }
+
+ before_all do
+ group.add_reporter(reporter)
+ group.add_guest(guest)
+ list.update_preferences_for(reporter, collapsed: false)
+ end
+
+ context 'with permission to admin board lists' do
+ let(:current_user) { reporter }
+
+ it 'updates the list position and collapsed state as expected' do
+ subject
+
+ reloaded_list = list.reload
+ expect(reloaded_list.position).to eq(1)
+ expect(reloaded_list.collapsed?(current_user)).to eq(true)
+ end
+ end
+
+ context 'with permission to read board lists' do
+ let(:current_user) { guest }
+
+ it 'updates the list collapsed state but not the list position' do
+ subject
+
+ reloaded_list = list.reload
+ expect(reloaded_list.position).to eq(0)
+ expect(reloaded_list.collapsed?(current_user)).to eq(true)
+ end
+ end
+
+ context 'without permission to read board lists' do
+ let(:current_user) { create(:user) }
+
+ it 'raises Resource Not Found error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/graphql/mutations/security/ci_configuration_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/security/ci_configuration_shared_examples.rb
new file mode 100644
index 00000000000..2bb3d807aa7
--- /dev/null
+++ b/spec/support/shared_examples/graphql/mutations/security/ci_configuration_shared_examples.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.shared_examples_for 'graphql mutations security ci configuration' do
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:user) { create(:user) }
+
+ let(:branch) do
+ "set-secret-config"
+ end
+
+ let(:success_path) do
+ "http://127.0.0.1:3000/root/demo-historic-secrets/-/merge_requests/new?"
+ end
+
+ let(:service_response) do
+ ServiceResponse.success(payload: { branch: branch, success_path: success_path })
+ end
+
+ let(:error) { "An error occured!" }
+
+ let(:service_error_response) do
+ ServiceResponse.error(message: error)
+ end
+
+ specify { expect(described_class).to require_graphql_authorizations(:push_code) }
+
+ describe '#resolve' do
+ let(:result) { subject }
+
+ it 'raises an error if the resource is not accessible to the user' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+
+ context 'when user does not have enough permissions' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'when user is a maintainer of a different project' do
+ before do
+ create(:project_empty_repo).add_maintainer(user)
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'when the user does not have permission to create a new branch' do
+ let(:error_message) { 'You are not allowed to create protected branches on this project.' }
+
+ before do
+ project.add_developer(user)
+
+ allow_next_instance_of(::Files::MultiService) do |multi_service|
+ allow(multi_service).to receive(:execute).and_raise(Gitlab::Git::PreReceiveError.new("GitLab: #{error_message}"))
+ end
+ end
+
+ it 'returns an array of errors' do
+ expect(result).to match(
+ branch: be_nil,
+ success_path: be_nil,
+ errors: match_array([error_message])
+ )
+ end
+ end
+
+ context 'when the user can create a merge request' do
+ before do
+ project.add_developer(user)
+ end
+
+ context 'when service successfully generates a path to create a new merge request' do
+ before do
+ allow_next_instance_of(service) do |service|
+ allow(service).to receive(:execute).and_return(service_response)
+ end
+ end
+
+ it 'returns a success path' do
+ expect(result).to match(
+ branch: branch,
+ success_path: success_path,
+ errors: []
+ )
+ end
+ end
+
+ context 'when service can not generate any path to create a new merge request' do
+ before do
+ allow_next_instance_of(service) do |service|
+ allow(service).to receive(:execute).and_return(service_error_response)
+ end
+ end
+
+ it 'returns an array of errors' do
+ expect(result).to match(
+ branch: be_nil,
+ success_path: be_nil,
+ errors: match_array([error])
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/graphql/resolvers/packages_resolvers_shared_examples.rb b/spec/support/shared_examples/graphql/resolvers/packages_resolvers_shared_examples.rb
new file mode 100644
index 00000000000..3d6fec85490
--- /dev/null
+++ b/spec/support/shared_examples/graphql/resolvers/packages_resolvers_shared_examples.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'group and projects packages resolver' do
+ context 'without sort' do
+ let_it_be(:npm_package) { create(:package, project: project) }
+
+ it { is_expected.to contain_exactly(npm_package) }
+ end
+
+ context 'with sorting and filtering' do
+ let_it_be(:conan_package) do
+ create(:conan_package, name: 'bar', project: project, created_at: 1.day.ago, version: "1.0.0", status: 'default')
+ end
+
+ let_it_be(:maven_package) do
+ create(:maven_package, name: 'foo', project: project, created_at: 1.hour.ago, version: "2.0.0", status: 'error')
+ end
+
+ let_it_be(:repository3) do
+ create(:maven_package, name: 'baz', project: project, created_at: 1.minute.ago, version: nil)
+ end
+
+ [:created_desc, :name_desc, :version_desc, :type_asc].each do |order|
+ context "#{order}" do
+ let(:args) { { sort: order } }
+
+ it { is_expected.to eq([maven_package, conan_package]) }
+ end
+ end
+
+ [:created_asc, :name_asc, :version_asc, :type_desc].each do |order|
+ context "#{order}" do
+ let(:args) { { sort: order } }
+
+ it { is_expected.to eq([conan_package, maven_package]) }
+ end
+ end
+
+ context 'filter by package_name' do
+ let(:args) { { package_name: 'bar', sort: :created_desc } }
+
+ it { is_expected.to eq([conan_package]) }
+ end
+
+ context 'filter by package_type' do
+ let(:args) { { package_type: 'conan', sort: :created_desc } }
+
+ it { is_expected.to eq([conan_package]) }
+ end
+
+ context 'filter by status' do
+ let(:args) { { status: 'error', sort: :created_desc } }
+
+ it { is_expected.to eq([maven_package]) }
+ end
+
+ context 'include_versionless' do
+ let(:args) { { include_versionless: true, sort: :created_desc } }
+
+ it { is_expected.to include(repository3) }
+ end
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/diff_file_collections_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/diff_file_collections_shared_examples.rb
index c9e03ced0dd..1f7325df11a 100644
--- a/spec/support/shared_examples/lib/gitlab/diff_file_collections_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/diff_file_collections_shared_examples.rb
@@ -166,16 +166,6 @@ shared_examples_for 'sortable diff files' do
it 'returns sorted diff files' do
expect(raw_diff_files_paths).to eq(sorted_diff_files_paths)
end
-
- context 'when sort_diffs feature flag is disabled' do
- before do
- stub_feature_flags(sort_diffs: false)
- end
-
- it 'returns unsorted diff files' do
- expect(raw_diff_files_paths).to eq(unsorted_diff_files_paths)
- end
- end
end
end
end
diff --git a/spec/support/shared_examples/lib/gitlab/jwt_token_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/jwt_token_shared_examples.rb
new file mode 100644
index 00000000000..5c92bb3b0d4
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/jwt_token_shared_examples.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'a gitlab jwt token' do
+ let_it_be(:base_secret) { SecureRandom.base64(64) }
+
+ let(:jwt_secret) do
+ OpenSSL::HMAC.hexdigest(
+ 'SHA256',
+ base_secret,
+ described_class::HMAC_KEY
+ )
+ end
+
+ before do
+ allow(Settings).to receive(:attr_encrypted_db_key_base).and_return(base_secret)
+ end
+
+ describe '#secret' do
+ subject { described_class.secret }
+
+ it { is_expected.to eq(jwt_secret) }
+ end
+
+ describe '#decode' do
+ let(:encoded_jwt_token) { jwt_token.encoded }
+
+ subject(:decoded_jwt_token) { described_class.decode(encoded_jwt_token) }
+
+ context 'with a custom payload' do
+ let(:personal_access_token) { create(:personal_access_token) }
+ let(:jwt_token) { described_class.new.tap { |jwt_token| jwt_token['token'] = personal_access_token.token } }
+
+ it 'returns the correct token' do
+ expect(decoded_jwt_token['token']).to eq jwt_token['token']
+ end
+
+ it 'returns nil and logs the exception after expiration' do
+ travel_to((described_class::HMAC_EXPIRES_IN + 1.minute).ago) do
+ encoded_jwt_token
+ end
+
+ expect(Gitlab::ErrorTracking).to receive(:track_exception)
+ .with(instance_of(JWT::ExpiredSignature))
+
+ expect(decoded_jwt_token).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/sql/set_operator_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/sql/set_operator_shared_examples.rb
index aa6a51c3646..8d758ed1655 100644
--- a/spec/support/shared_examples/lib/gitlab/sql/set_operator_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/sql/set_operator_shared_examples.rb
@@ -21,7 +21,7 @@ RSpec.shared_examples 'SQL set operator' do |operator_keyword|
expect(set_operator.to_sql).to eq("(#{to_sql(relation_1)})\n#{operator_keyword}\n(#{to_sql(relation_2)})")
end
- it 'skips Model.none segements' do
+ it 'skips Model.none segments' do
empty_relation = User.none
set_operator = described_class.new([empty_relation, relation_1, relation_2])
diff --git a/spec/support/shared_examples/models/chat_service_shared_examples.rb b/spec/support/shared_examples/models/chat_service_shared_examples.rb
index 59e249bb865..4a47aad0957 100644
--- a/spec/support/shared_examples/models/chat_service_shared_examples.rb
+++ b/spec/support/shared_examples/models/chat_service_shared_examples.rb
@@ -163,7 +163,7 @@ RSpec.shared_examples "chat service" do |service_name|
context "with issue events" do
let(:opts) { { title: "Awesome issue", description: "please fix" } }
let(:sample_data) do
- service = Issues::CreateService.new(project, user, opts)
+ service = Issues::CreateService.new(project: project, current_user: user, params: opts)
issue = service.execute
service.hook_data(issue, "open")
end
@@ -182,7 +182,7 @@ RSpec.shared_examples "chat service" do |service_name|
end
let(:sample_data) do
- service = MergeRequests::CreateService.new(project, user, opts)
+ service = MergeRequests::CreateService.new(project: project, current_user: user, params: opts)
merge_request = service.execute
service.hook_data(merge_request, "open")
end
diff --git a/spec/support/shared_examples/models/chat_slash_commands_shared_examples.rb b/spec/support/shared_examples/models/chat_slash_commands_shared_examples.rb
index 0ee24dd93d7..49729afce61 100644
--- a/spec/support/shared_examples/models/chat_slash_commands_shared_examples.rb
+++ b/spec/support/shared_examples/models/chat_slash_commands_shared_examples.rb
@@ -81,7 +81,7 @@ RSpec.shared_examples 'chat slash commands service' do
end
context 'when the user is authenticated' do
- let!(:chat_name) { create(:chat_name, service: subject) }
+ let!(:chat_name) { create(:chat_name, integration: subject) }
let(:params) { { token: 'token', team_id: chat_name.team_id, user_id: chat_name.chat_id } }
subject do
diff --git a/spec/support/shared_examples/models/clusters/elastic_stack_client_shared.rb b/spec/support/shared_examples/models/clusters/elastic_stack_client_shared.rb
new file mode 100644
index 00000000000..d3ce916cd64
--- /dev/null
+++ b/spec/support/shared_examples/models/clusters/elastic_stack_client_shared.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+# Input
+# - factory: [:clusters_applications_elastic_stack, :clusters_integrations_elastic_stack]
+RSpec.shared_examples 'cluster-based #elasticsearch_client' do |factory|
+ describe '#elasticsearch_client' do
+ context 'cluster is nil' do
+ subject { build(factory, cluster: nil) }
+
+ it 'returns nil' do
+ expect(subject.cluster).to be_nil
+ expect(subject.elasticsearch_client).to be_nil
+ end
+ end
+
+ context "cluster doesn't have kubeclient" do
+ let(:cluster) { create(:cluster) }
+
+ subject { create(factory, cluster: cluster) }
+
+ it 'returns nil' do
+ expect(subject.elasticsearch_client).to be_nil
+ end
+ end
+
+ context 'cluster has kubeclient' do
+ let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
+ let(:kubernetes_url) { subject.cluster.platform_kubernetes.api_url }
+ let(:kube_client) { subject.cluster.kubeclient.core_client }
+
+ subject { create(factory, cluster: cluster) }
+
+ before do
+ subject.cluster.platform_kubernetes.namespace = 'a-namespace'
+ stub_kubeclient_discover(cluster.platform_kubernetes.api_url)
+
+ create(:cluster_kubernetes_namespace,
+ cluster: cluster,
+ cluster_project: cluster.cluster_project,
+ project: cluster.cluster_project.project)
+ end
+
+ it 'creates proxy elasticsearch_client' do
+ expect(subject.elasticsearch_client).to be_instance_of(Elasticsearch::Transport::Client)
+ end
+
+ it 'copies proxy_url, options and headers from kube client to elasticsearch_client' do
+ expect(Elasticsearch::Client)
+ .to(receive(:new))
+ .with(url: a_valid_url)
+ .and_call_original
+
+ client = subject.elasticsearch_client
+ faraday_connection = client.transport.connections.first.connection
+
+ expect(faraday_connection.headers["Authorization"]).to eq(kube_client.headers[:Authorization])
+ expect(faraday_connection.ssl.cert_store).to be_instance_of(OpenSSL::X509::Store)
+ expect(faraday_connection.ssl.verify).to eq(1)
+ expect(faraday_connection.options.timeout).to be_nil
+ end
+
+ context 'when cluster is not reachable' do
+ before do
+ allow(kube_client).to receive(:proxy_url).and_raise(Kubeclient::HttpError.new(401, 'Unauthorized', nil))
+ end
+
+ it 'returns nil' do
+ expect(subject.elasticsearch_client).to be_nil
+ end
+ end
+
+ context 'when timeout is provided' do
+ it 'sets timeout in elasticsearch_client' do
+ client = subject.elasticsearch_client(timeout: 123)
+ faraday_connection = client.transport.connections.first.connection
+
+ expect(faraday_connection.options.timeout).to eq(123)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb b/spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb
index 3db5d7a8d7d..ec9756007f1 100644
--- a/spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb
@@ -30,10 +30,6 @@ RSpec.shared_examples 'a BulkInsertSafe model' do |klass|
expect { target_class.set_callback(name) {} }.not_to raise_error
end
end
-
- it 'does not raise an error when the call is triggered by belongs_to' do
- expect { target_class.belongs_to(:other_record) }.not_to raise_error
- end
end
describe '.bulk_insert!' do
diff --git a/spec/support/shared_examples/models/concerns/cron_schedulable_shared_examples.rb b/spec/support/shared_examples/models/concerns/cron_schedulable_shared_examples.rb
new file mode 100644
index 00000000000..47a02a8fa49
--- /dev/null
+++ b/spec/support/shared_examples/models/concerns/cron_schedulable_shared_examples.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'handles set_next_run_at' do
+ context 'when schedule runs every minute' do
+ it "updates next_run_at to the worker's execution time" do
+ travel_to(1.day.ago) do
+ expect(schedule.next_run_at).to eq(cron_worker_next_run_at)
+ end
+ end
+ end
+
+ context 'when there are two different schedules in the same time zones' do
+ it 'sets the sames next_run_at' do
+ expect(schedule_1.next_run_at).to eq(schedule_2.next_run_at)
+ end
+ end
+
+ context 'when cron is updated for existing schedules' do
+ it 'updates next_run_at automatically' do
+ expect { schedule.update!(cron: new_cron) }.to change { schedule.next_run_at }
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/concerns/repository_storage_movable_shared_examples.rb b/spec/support/shared_examples/models/concerns/repository_storage_movable_shared_examples.rb
index 819cf6018fe..3f1588c46b3 100644
--- a/spec/support/shared_examples/models/concerns/repository_storage_movable_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/repository_storage_movable_shared_examples.rb
@@ -36,7 +36,7 @@ RSpec.shared_examples 'handles repository moves' do
container.set_repository_read_only!
expect(subject).not_to be_valid
- expect(subject.errors[error_key].first).to match(/is read only/)
+ expect(subject.errors[error_key].first).to match(/is read-only/)
end
end
end
diff --git a/spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb b/spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb
index 17948d648cb..d23f95b2e9e 100644
--- a/spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb
+++ b/spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb
@@ -58,6 +58,19 @@ RSpec.shared_examples 'value stream analytics stage' do
it { expect(stage).not_to be_valid }
end
+
+ # rubocop: disable Rails/SaveBang
+ describe '.by_value_stream' do
+ it 'finds stages by value stream' do
+ stage1 = create(factory)
+ create(factory) # other stage with different value stream
+
+ result = described_class.by_value_stream(stage1.value_stream)
+
+ expect(result).to eq([stage1])
+ end
+ end
+ # rubocop: enable Rails/SaveBang
end
describe '#subject_class' do
diff --git a/spec/support/shared_examples/models/packages/debian/architecture_shared_examples.rb b/spec/support/shared_examples/models/packages/debian/architecture_shared_examples.rb
index fbb94b4f5c1..33a04059491 100644
--- a/spec/support/shared_examples/models/packages/debian/architecture_shared_examples.rb
+++ b/spec/support/shared_examples/models/packages/debian/architecture_shared_examples.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
RSpec.shared_examples 'Debian Distribution Architecture' do |factory, container, can_freeze|
- let_it_be_with_refind(:architecture) { create(factory) } # rubocop:disable Rails/SaveBang
- let_it_be(:architecture_same_distribution, freeze: can_freeze) { create(factory, distribution: architecture.distribution) }
+ let_it_be_with_refind(:architecture) { create(factory, name: 'name1') }
+ let_it_be(:architecture_same_distribution, freeze: can_freeze) { create(factory, distribution: architecture.distribution, name: 'name2') }
let_it_be(:architecture_same_name, freeze: can_freeze) { create(factory, name: architecture.name) }
subject { architecture }
@@ -30,20 +30,22 @@ RSpec.shared_examples 'Debian Distribution Architecture' do |factory, container,
end
describe 'scopes' do
+ describe '.ordered_by_name' do
+ subject { described_class.with_distribution(architecture.distribution).ordered_by_name }
+
+ it { expect(subject).to match_array([architecture, architecture_same_distribution]) }
+ end
+
describe '.with_distribution' do
subject { described_class.with_distribution(architecture.distribution) }
- it 'does not return other distributions' do
- expect(subject.to_a).to match_array([architecture, architecture_same_distribution])
- end
+ it { expect(subject).to match_array([architecture, architecture_same_distribution]) }
end
describe '.with_name' do
subject { described_class.with_name(architecture.name) }
- it 'does not return other distributions' do
- expect(subject.to_a).to match_array([architecture, architecture_same_name])
- end
+ it { expect(subject).to match_array([architecture, architecture_same_name]) }
end
end
end
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 02ced49ee94..e6b16d5881d 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
@@ -114,11 +114,7 @@ RSpec.shared_examples 'Debian Component File' do |container_type, can_freeze|
subject { described_class.with_container(container2) }
it do
- queries = ActiveRecord::QueryRecorder.new do
- expect(subject.to_a).to contain_exactly(component_file_other_container)
- end
-
- expect(queries.count).to eq(1)
+ expect(subject.to_a).to contain_exactly(component_file_other_container)
end
end
@@ -126,11 +122,7 @@ RSpec.shared_examples 'Debian Component File' do |container_type, can_freeze|
subject { described_class.with_codename_or_suite(distribution2.codename) }
it do
- queries = ActiveRecord::QueryRecorder.new do
- expect(subject.to_a).to contain_exactly(component_file_other_container)
- end
-
- expect(queries.count).to eq(1)
+ expect(subject.to_a).to contain_exactly(component_file_other_container)
end
end
@@ -138,11 +130,7 @@ RSpec.shared_examples 'Debian Component File' do |container_type, can_freeze|
subject { described_class.with_component_name(component1_2.name) }
it do
- queries = ActiveRecord::QueryRecorder.new do
- expect(subject.to_a).to contain_exactly(component_file_other_component)
- end
-
- expect(queries.count).to eq(1)
+ expect(subject.to_a).to contain_exactly(component_file_other_component)
end
end
@@ -150,14 +138,7 @@ RSpec.shared_examples 'Debian Component File' do |container_type, can_freeze|
subject { described_class.with_file_type(:source) }
it do
- # let_it_be_with_refind triggers a query
- component_file_with_file_type_source
-
- queries = ActiveRecord::QueryRecorder.new do
- expect(subject.to_a).to contain_exactly(component_file_with_file_type_source)
- end
-
- expect(queries.count).to eq(1)
+ expect(subject.to_a).to contain_exactly(component_file_with_file_type_source)
end
end
@@ -165,11 +146,7 @@ RSpec.shared_examples 'Debian Component File' do |container_type, can_freeze|
subject { described_class.with_architecture_name(architecture1_2.name) }
it do
- queries = ActiveRecord::QueryRecorder.new do
- expect(subject.to_a).to contain_exactly(component_file_other_architecture)
- end
-
- expect(queries.count).to eq(1)
+ expect(subject.to_a).to contain_exactly(component_file_other_architecture)
end
end
@@ -177,11 +154,7 @@ RSpec.shared_examples 'Debian Component File' do |container_type, can_freeze|
subject { described_class.with_compression_type(:xz) }
it do
- queries = ActiveRecord::QueryRecorder.new do
- expect(subject.to_a).to contain_exactly(component_file_other_compression_type)
- end
-
- expect(queries.count).to eq(1)
+ expect(subject.to_a).to contain_exactly(component_file_other_compression_type)
end
end
@@ -189,11 +162,19 @@ RSpec.shared_examples 'Debian Component File' do |container_type, can_freeze|
subject { described_class.with_file_sha256('other_sha256') }
it do
- queries = ActiveRecord::QueryRecorder.new do
- expect(subject.to_a).to contain_exactly(component_file_other_file_sha256)
- end
+ expect(subject.to_a).to contain_exactly(component_file_other_file_sha256)
+ 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) }
- expect(queries.count).to eq(1)
+ subject { described_class.created_before(2.hours.ago) }
+
+ it do
+ expect(subject.to_a).to contain_exactly(component_file1, component_file2)
end
end
end
diff --git a/spec/support/shared_examples/models/packages/debian/component_shared_examples.rb b/spec/support/shared_examples/models/packages/debian/component_shared_examples.rb
index 23e76d32fb0..635d45f40e5 100644
--- a/spec/support/shared_examples/models/packages/debian/component_shared_examples.rb
+++ b/spec/support/shared_examples/models/packages/debian/component_shared_examples.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
RSpec.shared_examples 'Debian Distribution Component' do |factory, container, can_freeze|
- let_it_be_with_refind(:component) { create(factory) } # rubocop:disable Rails/SaveBang
- let_it_be(:component_same_distribution, freeze: can_freeze) { create(factory, distribution: component.distribution) }
+ let_it_be_with_refind(:component) { create(factory, name: 'name1') }
+ let_it_be(:component_same_distribution, freeze: can_freeze) { create(factory, distribution: component.distribution, name: 'name2') }
let_it_be(:component_same_name, freeze: can_freeze) { create(factory, name: component.name) }
subject { component }
@@ -32,6 +32,14 @@ RSpec.shared_examples 'Debian Distribution Component' do |factory, container, ca
end
describe 'scopes' do
+ describe '.ordered_by_name' do
+ subject { described_class.with_distribution(component.distribution).ordered_by_name }
+
+ it 'sorts by name' do
+ expect(subject.to_a).to eq([component, component_same_distribution])
+ end
+ end
+
describe '.with_distribution' do
subject { described_class.with_distribution(component.distribution) }
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 9eacacf725f..8693d6868e9 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
@@ -19,11 +19,6 @@ RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze|
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) }
-
- if container != :group
- it { is_expected.to have_many(:publications).class_name('Packages::Debian::Publication').inverse_of(:distribution).with_foreign_key(:distribution_id) }
- it { is_expected.to have_many(:packages).class_name('Packages::Package').through(:publications) }
- end
end
describe 'validations' do
@@ -228,4 +223,44 @@ RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze|
end
end
end
+
+ if container == :project
+ describe 'project distribution specifics' do
+ describe 'relationships' do
+ it { is_expected.to have_many(:publications).class_name('Packages::Debian::Publication').inverse_of(:distribution).with_foreign_key(:distribution_id) }
+ it { is_expected.to have_many(:packages).class_name('Packages::Package').through(:publications) }
+ it { is_expected.to have_many(:package_files).class_name('Packages::PackageFile').through(:packages) }
+ end
+ end
+ else
+ describe 'group distribution specifics' do
+ let_it_be(:public_project) { create(:project, :public, group: distribution_with_suite.container)}
+ let_it_be(:public_distribution_with_same_codename) { create(:debian_project_distribution, container: public_project, codename: distribution_with_suite.codename) }
+ let_it_be(:public_package_with_same_codename) { create(:debian_package, project: public_project, published_in: public_distribution_with_same_codename)}
+ let_it_be(:public_distribution_with_same_suite) { create(:debian_project_distribution, container: public_project, suite: distribution_with_suite.suite) }
+ let_it_be(:public_package_with_same_suite) { create(:debian_package, project: public_project, published_in: public_distribution_with_same_suite)}
+
+ let_it_be(:private_project) { create(:project, :private, group: distribution_with_suite.container)}
+ let_it_be(:private_distribution_with_same_codename) { create(:debian_project_distribution, container: private_project, codename: distribution_with_suite.codename) }
+ let_it_be(:private_package_with_same_codename) { create(:debian_package, project: private_project, published_in: private_distribution_with_same_codename)}
+ let_it_be(:private_distribution_with_same_suite) { create(:debian_project_distribution, container: private_project, suite: distribution_with_suite.suite) }
+ let_it_be(:private_package_with_same_suite) { create(:debian_package, project: private_project, published_in: private_distribution_with_same_codename)}
+
+ describe '#packages' do
+ subject { distribution_with_suite.packages }
+
+ it 'returns only public packages with same codename' do
+ expect(subject.to_a).to contain_exactly(public_package_with_same_codename)
+ end
+ end
+
+ describe '#package_files' do
+ subject { distribution_with_suite.package_files }
+
+ it 'returns only files from public packages with same codename' do
+ expect(subject.to_a).to contain_exactly(*public_package_with_same_codename.package_files)
+ end
+ end
+ end
+ end
end
diff --git a/spec/support/shared_examples/models/slack_mattermost_notifications_shared_examples.rb b/spec/support/shared_examples/models/slack_mattermost_notifications_shared_examples.rb
index 71a76121d38..09b7d1be704 100644
--- a/spec/support/shared_examples/models/slack_mattermost_notifications_shared_examples.rb
+++ b/spec/support/shared_examples/models/slack_mattermost_notifications_shared_examples.rb
@@ -201,7 +201,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) }
+ let(:data) { Gitlab::DataBuilder::Deployment.build(deployment, Time.current) }
it_behaves_like 'calls the service API with the event message', /Deploy to (.*?) created/
end
diff --git a/spec/support/shared_examples/models/wiki_shared_examples.rb b/spec/support/shared_examples/models/wiki_shared_examples.rb
index 6b243aef3e6..2498bf35a09 100644
--- a/spec/support/shared_examples/models/wiki_shared_examples.rb
+++ b/spec/support/shared_examples/models/wiki_shared_examples.rb
@@ -469,34 +469,30 @@ RSpec.shared_examples 'wiki model' do
end
describe '#delete_page' do
- shared_examples 'delete_page operations' do
- let(:page) { create(:wiki_page, wiki: wiki) }
+ let(:page) { create(:wiki_page, wiki: wiki) }
- it 'deletes the page' do
- subject.delete_page(page)
+ it 'deletes the page' do
+ subject.delete_page(page)
- expect(subject.list_pages.count).to eq(0)
- end
+ expect(subject.list_pages.count).to eq(0)
+ end
- it 'sets the correct commit email' do
- subject.delete_page(page)
+ it 'sets the correct commit email' do
+ subject.delete_page(page)
- expect(user.commit_email).not_to eq(user.email)
- expect(commit.author_email).to eq(user.commit_email)
- expect(commit.committer_email).to eq(user.commit_email)
- end
+ expect(user.commit_email).not_to eq(user.email)
+ expect(commit.author_email).to eq(user.commit_email)
+ expect(commit.committer_email).to eq(user.commit_email)
+ end
- it 'runs after_wiki_activity callbacks' do
- page
+ it 'runs after_wiki_activity callbacks' do
+ page
- expect(subject).to receive(:after_wiki_activity)
+ expect(subject).to receive(:after_wiki_activity)
- subject.delete_page(page)
- end
+ subject.delete_page(page)
end
- it_behaves_like 'delete_page operations'
-
context 'when an error is raised' do
it 'logs the error and returns false' do
page = build(:wiki_page, wiki: wiki)
@@ -509,14 +505,6 @@ RSpec.shared_examples 'wiki model' do
expect(subject.delete_page(page)).to be_falsey
end
end
-
- context 'when feature flag :gitaly_replace_wiki_delete_page is disabled' do
- before do
- stub_feature_flags(gitaly_replace_wiki_delete_page: false)
- end
-
- it_behaves_like 'delete_page operations'
- end
end
describe '#ensure_repository' do
diff --git a/spec/support/shared_examples/namespaces/namespace_traversal_examples.rb b/spec/support/shared_examples/namespaces/traversal_examples.rb
index 36e5808fa28..77a1705627e 100644
--- a/spec/support/shared_examples/namespaces/namespace_traversal_examples.rb
+++ b/spec/support/shared_examples/namespaces/traversal_examples.rb
@@ -39,16 +39,17 @@ RSpec.shared_examples 'namespace traversal' do
end
describe '#ancestors' do
- let(:group) { create(:group) }
- let(:nested_group) { create(:group, parent: group) }
- let(:deep_nested_group) { create(:group, parent: nested_group) }
- let(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:nested_group) { create(:group, parent: group) }
+ let_it_be(:deep_nested_group) { create(:group, parent: nested_group) }
+ let_it_be(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
it 'returns the correct ancestors' do
- expect(very_deep_nested_group.ancestors).to include(group, nested_group, deep_nested_group)
- expect(deep_nested_group.ancestors).to include(group, nested_group)
- expect(nested_group.ancestors).to include(group)
- expect(group.ancestors).to eq([])
+ # #reload is called to make sure traversal_ids are reloaded
+ expect(very_deep_nested_group.reload.ancestors).to contain_exactly(group, nested_group, deep_nested_group)
+ expect(deep_nested_group.reload.ancestors).to contain_exactly(group, nested_group)
+ expect(nested_group.reload.ancestors).to contain_exactly(group)
+ expect(group.reload.ancestors).to eq([])
end
describe '#recursive_ancestors' do
diff --git a/spec/support/shared_examples/policies/project_policy_shared_examples.rb b/spec/support/shared_examples/policies/project_policy_shared_examples.rb
index d05e5eb9120..013c9b61b99 100644
--- a/spec/support/shared_examples/policies/project_policy_shared_examples.rb
+++ b/spec/support/shared_examples/policies/project_policy_shared_examples.rb
@@ -57,7 +57,7 @@ RSpec.shared_examples 'project policies as anonymous' do
context 'when a project has pending invites' do
let(:group) { create(:group, :public) }
let(:project) { create(:project, :public, namespace: group) }
- let(:user_permissions) { [:create_merge_request_in, :create_project, :create_issue, :create_note, :upload_file, :award_emoji] }
+ let(:user_permissions) { [:create_merge_request_in, :create_project, :create_issue, :create_note, :upload_file, :award_emoji, :create_incident] }
let(:anonymous_permissions) { guest_permissions - user_permissions }
let(:current_user) { anonymous }
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 87aaac673c1..c938c6432fe 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
@@ -106,7 +106,7 @@ RSpec.shared_examples 'conan authenticate endpoint' do
expect(payload['user_id']).to eq(personal_access_token.user_id)
duration = payload['exp'] - payload['iat']
- expect(duration).to eq(1.hour)
+ expect(duration).to eq(::Gitlab::ConanToken::CONAN_TOKEN_EXPIRE_TIME)
end
end
end
@@ -661,7 +661,7 @@ RSpec.shared_examples 'workhorse package file upload endpoint' do
end
RSpec.shared_examples 'creates build_info when there is a job' do
- context 'with job token' do
+ context 'with job token', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/294047' do
let(:jwt) { build_jwt_from_job(job) }
it 'creates a build_info record' do
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 acaa0d8c2bc..dfd19167dcd 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
@@ -1,20 +1,16 @@
# frozen_string_literal: true
-RSpec.shared_context 'Debian repository shared context' do |object_type|
+RSpec.shared_context 'Debian repository shared context' do |container_type, can_freeze|
include_context 'workhorse headers'
before do
stub_feature_flags(debian_packages: true)
end
- if object_type == :project
- let(:project) { create(:project, :public) }
- elsif object_type == :group
- let(:group) { create(:group, :public) }
- end
-
- let(:user) { create(:user) }
- let(:personal_access_token) { create(:personal_access_token, user: user) }
+ let_it_be(:private_container, freeze: can_freeze) { create(container_type, :private) }
+ let_it_be(:public_container, freeze: can_freeze) { create(container_type, :public) }
+ 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(:component) { 'main' }
@@ -36,7 +32,7 @@ RSpec.shared_context 'Debian repository shared context' do |object_type|
end
end
- let(:params) { workhorse_params }
+ let(:api_params) { workhorse_params }
let(:auth_headers) { {} }
let(:wh_headers) do
@@ -57,12 +53,12 @@ RSpec.shared_context 'Debian repository shared context' do |object_type|
api(url),
method: method,
file_key: :file,
- params: params,
+ params: api_params,
headers: headers,
send_rewritten_field: send_rewritten_field
)
else
- send method, api(url), headers: headers, params: params
+ send method, api(url), headers: headers, params: api_params
end
end
end
@@ -81,289 +77,190 @@ RSpec.shared_context 'Debian repository auth headers' do |user_role, user_token,
end
end
-RSpec.shared_context 'Debian repository project access' do |project_visibility_level, user_role, user_token, auth_method|
+RSpec.shared_context 'Debian repository access' do |visibility_level, user_role, add_member, user_token, auth_method|
include_context 'Debian repository auth headers', user_role, user_token, auth_method do
+ let(:containers) { { private: private_container, public: public_container } }
+ let(:container) { containers[visibility_level] }
+
before do
- project.update_column(:visibility_level, Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
+ container.send("add_#{user_role}", user) if add_member && user_role != :anonymous
end
end
end
-RSpec.shared_examples 'Debian project repository GET request' do |user_role, add_member, status, body|
- context "for user type #{user_role}" do
- before do
- project.send("add_#{user_role}", user) if add_member && user_role != :anonymous
- end
+RSpec.shared_examples 'Debian repository GET request' do |status, body = nil|
+ and_body = body.nil? ? '' : ' and expected body'
- and_body = body.nil? ? '' : ' and expected body'
+ it "returns #{status}#{and_body}" do
+ subject
- it "returns #{status}#{and_body}" do
- subject
+ expect(response).to have_gitlab_http_status(status)
- expect(response).to have_gitlab_http_status(status)
-
- unless body.nil?
- expect(response.body).to eq(body)
- end
+ unless body.nil?
+ expect(response.body).to eq(body)
end
end
end
-RSpec.shared_examples 'Debian project repository PUT request' do |user_role, add_member, status, body|
- context "for user type #{user_role}" do
- before do
- project.send("add_#{user_role}", user) if add_member && user_role != :anonymous
- end
+RSpec.shared_examples 'Debian repository upload request' do |status, body = nil|
+ and_body = body.nil? ? '' : ' and expected body'
- and_body = body.nil? ? '' : ' and expected body'
+ if status == :created
+ it 'creates package files', :aggregate_failures do
+ pending "Debian package creation not implemented"
- if status == :created
- it 'creates package files', :aggregate_failures do
- pending "Debian package creation not implemented"
- expect { subject }
- .to change { project.packages.debian.count }.by(1)
+ expect { subject }
+ .to change { container.packages.debian.count }.by(1)
- expect(response).to have_gitlab_http_status(status)
- expect(response.media_type).to eq('text/plain')
+ 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)
- end
+ unless body.nil?
+ expect(response.body).to eq(body)
end
- it_behaves_like 'a package tracking event', described_class.name, 'push_package'
- else
- it "returns #{status}#{and_body}", :aggregate_failures do
- subject
+ end
+ it_behaves_like 'a package tracking event', described_class.name, 'push_package'
+ else
+ it "returns #{status}#{and_body}", :aggregate_failures do
+ subject
- expect(response).to have_gitlab_http_status(status)
+ expect(response).to have_gitlab_http_status(status)
- unless body.nil?
- expect(response.body).to eq(body)
- end
+ unless body.nil?
+ expect(response.body).to eq(body)
end
end
end
end
-RSpec.shared_examples 'Debian project repository PUT authorize request' do |user_role, add_member, status, body, is_authorize|
- context "for user type #{user_role}" do
- before do
- project.send("add_#{user_role}", user) if add_member && user_role != :anonymous
- end
-
- and_body = body.nil? ? '' : ' and expected body'
+RSpec.shared_examples 'Debian repository upload authorize request' do |status, body = nil|
+ and_body = body.nil? ? '' : ' and expected body'
- if status == :created
- it 'authorizes package file upload', :aggregate_failures do
- subject
+ if status == :created
+ it 'authorizes package file upload', :aggregate_failures do
+ subject
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.media_type).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
- expect(json_response['TempPath']).to eq(Packages::PackageFileUploader.workhorse_local_upload_path)
- expect(json_response['RemoteObject']).to be_nil
- expect(json_response['MaximumSize']).to be_nil
- end
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.media_type).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ expect(json_response['TempPath']).to eq(Packages::PackageFileUploader.workhorse_local_upload_path)
+ expect(json_response['RemoteObject']).to be_nil
+ expect(json_response['MaximumSize']).to be_nil
+ end
- context 'without a valid token' do
- let(:workhorse_token) { 'invalid' }
+ context 'without a valid token' do
+ let(:workhorse_token) { 'invalid' }
- it 'rejects request' do
- subject
+ it 'rejects request' do
+ subject
- expect(response).to have_gitlab_http_status(:forbidden)
- end
+ expect(response).to have_gitlab_http_status(:forbidden)
end
+ end
- context 'bypassing gitlab-workhorse' do
- let(:workhorse_headers) { {} }
+ context 'bypassing gitlab-workhorse' do
+ let(:workhorse_headers) { {} }
- it 'rejects request' do
- subject
+ it 'rejects request' do
+ subject
- expect(response).to have_gitlab_http_status(:forbidden)
- end
+ expect(response).to have_gitlab_http_status(:forbidden)
end
- else
- it "returns #{status}#{and_body}", :aggregate_failures do
- subject
+ end
+ else
+ it "returns #{status}#{and_body}", :aggregate_failures do
+ subject
- expect(response).to have_gitlab_http_status(status)
+ expect(response).to have_gitlab_http_status(status)
- unless body.nil?
- expect(response.body).to eq(body)
- end
+ unless body.nil?
+ expect(response.body).to eq(body)
end
end
end
end
-RSpec.shared_examples 'rejects Debian access with unknown project id' do
- context 'with an unknown project' do
- let(:project) { double(id: non_existing_record_id) }
+RSpec.shared_examples 'rejects Debian access with unknown container id' do
+ context 'with an unknown container' do
+ let(:container) { double(id: non_existing_record_id) }
context 'as anonymous' do
- it_behaves_like 'Debian project repository GET request', :anonymous, true, :unauthorized, nil
+ it_behaves_like 'Debian repository GET request', :unauthorized, nil
end
context 'as authenticated user' do
subject { get api(url), headers: basic_auth_header(user.username, personal_access_token.token) }
- it_behaves_like 'Debian project repository GET request', :anonymous, true, :not_found, nil
+ it_behaves_like 'Debian repository GET request', :not_found, nil
end
end
end
-RSpec.shared_examples 'Debian project repository GET endpoint' do |success_status, success_body|
- context 'with valid project' do
+RSpec.shared_examples 'Debian repository read endpoint' do |desired_behavior, success_status, success_body|
+ context 'with valid container' do
using RSpec::Parameterized::TableSyntax
- where(:project_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 | 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' | :anonymous | false | true | success_status | success_body
- 'PRIVATE' | :developer | true | true | success_status | success_body
- 'PRIVATE' | :guest | true | true | :forbidden | nil
- 'PRIVATE' | :developer | true | false | :unauthorized | nil
- 'PRIVATE' | :guest | true | false | :unauthorized | nil
- 'PRIVATE' | :developer | false | true | :not_found | nil
- '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
+ 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 | 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 | :anonymous | false | true | success_status | success_body
+ :private | :developer | true | true | success_status | success_body
+ :private | :guest | true | true | :forbidden | nil
+ :private | :developer | true | false | :unauthorized | nil
+ :private | :guest | true | false | :unauthorized | nil
+ :private | :developer | false | true | :not_found | nil
+ :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
end
with_them do
- include_context 'Debian repository project access', params[:project_visibility_level], params[:user_role], params[:user_token], :basic do
- it_behaves_like 'Debian project repository GET request', params[:user_role], params[:member], params[:expected_status], params[:expected_body]
+ 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 project id'
-end
-
-RSpec.shared_examples 'Debian project repository PUT endpoint' do |success_status, success_body, is_authorize = false|
- context 'with valid project' do
- using RSpec::Parameterized::TableSyntax
-
- where(:project_visibility_level, :user_role, :member, :user_token, :expected_status, :expected_body) do
- 'PUBLIC' | :developer | true | true | success_status | nil
- 'PUBLIC' | :guest | true | true | :forbidden | nil
- 'PUBLIC' | :developer | true | false | :unauthorized | nil
- 'PUBLIC' | :guest | true | false | :unauthorized | nil
- 'PUBLIC' | :developer | false | true | :forbidden | nil
- 'PUBLIC' | :guest | false | true | :forbidden | nil
- 'PUBLIC' | :developer | false | false | :unauthorized | nil
- 'PUBLIC' | :guest | false | false | :unauthorized | nil
- 'PUBLIC' | :anonymous | false | true | :unauthorized | nil
- 'PRIVATE' | :developer | true | true | success_status | nil
- 'PRIVATE' | :guest | true | true | :forbidden | nil
- 'PRIVATE' | :developer | true | false | :unauthorized | nil
- 'PRIVATE' | :guest | true | false | :unauthorized | nil
- 'PRIVATE' | :developer | false | true | :not_found | nil
- '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
- end
-
- with_them do
- include_context 'Debian repository project access', params[:project_visibility_level], params[:user_role], params[:user_token], :basic do
- desired_behavior = if is_authorize
- 'Debian project repository PUT authorize request'
- else
- 'Debian project repository PUT request'
- end
-
- it_behaves_like desired_behavior, params[:user_role], params[:member], params[:expected_status], params[:expected_body]
- end
- end
- end
-
- it_behaves_like 'rejects Debian access with unknown project id'
-end
-
-RSpec.shared_context 'Debian repository group access' do |group_visibility_level, user_role, user_token, auth_method|
- include_context 'Debian repository auth headers', user_role, user_token, auth_method do
- before do
- group.update_column(:visibility_level, Gitlab::VisibilityLevel.const_get(group_visibility_level, false))
- end
- end
-end
-
-RSpec.shared_examples 'Debian group repository GET request' do |user_role, add_member, status, body|
- context "for user type #{user_role}" do
- before do
- group.send("add_#{user_role}", user) if add_member && user_role != :anonymous
- end
-
- and_body = body.nil? ? '' : ' and expected body'
-
- it "returns #{status}#{and_body}" do
- subject
-
- expect(response).to have_gitlab_http_status(status)
-
- unless body.nil?
- expect(response.body).to eq(body)
- end
- end
- end
-end
-
-RSpec.shared_examples 'rejects Debian access with unknown group id' do
- context 'with an unknown group' do
- let(:group) { double(id: non_existing_record_id) }
-
- context 'as anonymous' do
- it_behaves_like 'Debian group repository GET request', :anonymous, true, :unauthorized, nil
- end
-
- context 'as authenticated user' do
- subject { get api(url), headers: basic_auth_header(user.username, personal_access_token.token) }
-
- it_behaves_like 'Debian group repository GET request', :anonymous, true, :not_found, nil
- end
- end
+ it_behaves_like 'rejects Debian access with unknown container id'
end
-RSpec.shared_examples 'Debian group repository GET endpoint' do |success_status, success_body|
- context 'with valid group' do
+RSpec.shared_examples 'Debian repository write endpoint' do |desired_behavior, success_status, success_body|
+ context 'with valid container' do
using RSpec::Parameterized::TableSyntax
- where(:group_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 | 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' | :anonymous | false | true | success_status | success_body
- 'PRIVATE' | :developer | true | true | success_status | success_body
- 'PRIVATE' | :guest | true | true | :forbidden | nil
- 'PRIVATE' | :developer | true | false | :unauthorized | nil
- 'PRIVATE' | :guest | true | false | :unauthorized | nil
- 'PRIVATE' | :developer | false | true | :not_found | nil
- '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
+ 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 | :forbidden | nil
+ :public | :developer | true | false | :unauthorized | nil
+ :public | :guest | true | false | :unauthorized | nil
+ :public | :developer | false | true | :forbidden | nil
+ :public | :guest | false | true | :forbidden | nil
+ :public | :developer | false | false | :unauthorized | nil
+ :public | :guest | false | false | :unauthorized | nil
+ :public | :anonymous | false | true | :unauthorized | nil
+ :private | :developer | true | true | success_status | success_body
+ :private | :guest | true | true | :forbidden | nil
+ :private | :developer | true | false | :unauthorized | nil
+ :private | :guest | true | false | :unauthorized | nil
+ :private | :developer | false | true | :not_found | nil
+ :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
end
with_them do
- include_context 'Debian repository group access', params[:group_visibility_level], params[:user_role], params[:user_token], :basic do
- it_behaves_like 'Debian group repository GET request', params[:user_role], params[:member], params[:expected_status], params[:expected_body]
+ 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 group id'
+ it_behaves_like 'rejects Debian access with unknown container id'
end
diff --git a/spec/support/shared_examples/requests/api/graphql/mutations/boards/update_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/mutations/boards/update_list_shared_examples.rb
new file mode 100644
index 00000000000..9b55b0f061f
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/graphql/mutations/boards/update_list_shared_examples.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'a GraphQL request to update board list' do
+ context 'the user is not allowed to read board lists' do
+ it_behaves_like 'a mutation that returns a top-level access error'
+ end
+
+ before do
+ list.update_preferences_for(current_user, collapsed: false)
+ end
+
+ context 'when user has permissions to admin board lists' do
+ before do
+ group.add_reporter(current_user)
+ end
+
+ it 'updates the list position and collapsed state' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['list']).to include(
+ 'position' => 1,
+ 'collapsed' => true
+ )
+ end
+ end
+
+ context 'when user has permissions to read board lists' do
+ before do
+ group.add_guest(current_user)
+ end
+
+ it 'updates the list collapsed state but not the list position' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['list']).to include(
+ 'position' => 0,
+ 'collapsed' => true
+ )
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/graphql/mutations/destroy_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/mutations/destroy_list_shared_examples.rb
new file mode 100644
index 00000000000..0cec67ff541
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/graphql/mutations/destroy_list_shared_examples.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.shared_examples 'board lists destroy request' do
+ include GraphqlHelpers
+
+ subject { post_graphql_mutation(mutation, current_user: current_user) }
+
+ shared_examples 'does not destroy the list and returns an error' do
+ it 'does not destroy the list' do
+ expect { subject }.not_to change { klass.count }
+ end
+
+ it 'returns an error and not nil list' do
+ subject
+
+ expect(mutation_response['errors']).not_to be_empty
+ expect(mutation_response['list']).not_to be_nil
+ end
+ end
+
+ context 'when the user does not have permission' do
+ it 'does not destroy the list' do
+ expect { subject }.not_to change { klass.count }
+ end
+
+ it 'returns an error' do
+ subject
+
+ expect(graphql_errors.first['message']).to include("The resource that you are attempting to access does not exist or you don't have permission to perform this action")
+ end
+ end
+
+ context 'when the user has permission' do
+ before do
+ group.add_maintainer(current_user)
+ end
+
+ context 'when given id is not for a list' do
+ # could be any non-list thing
+ let_it_be(:list) { group }
+
+ it 'returns an error' do
+ subject
+
+ expect(graphql_errors.first['message']).to include('does not represent an instance of')
+ end
+ end
+
+ context 'when list does not exist' do
+ let(:variables) do
+ {
+ list_id: "gid://gitlab/#{klass}/#{non_existing_record_id}"
+ }
+ end
+
+ it 'returns a top level error' do
+ subject
+
+ expect(graphql_errors.first['message']).to include('No object found for')
+ end
+ end
+
+ context 'when everything is ok' do
+ it 'destroys the list' do
+ expect { subject }.to change { klass.count }.by(-1)
+ end
+
+ it 'returns an empty list' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response).to have_key('list')
+ expect(mutation_response['list']).to be_nil
+ expect(mutation_response['errors']).to be_empty
+ end
+ end
+
+ context 'when the list is not destroyable' do
+ before do
+ list.update!(list_type: :backlog)
+ end
+
+ it_behaves_like 'does not destroy the list and returns an error'
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb
index 66fbfa798b0..af4c9286e7c 100644
--- a/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb
@@ -3,6 +3,38 @@
RSpec.shared_examples 'group and project packages query' do
include GraphqlHelpers
+ let_it_be(:versionaless_package) { create(:maven_package, project: project1, version: nil) }
+ let_it_be(:maven_package) { create(:maven_package, project: project1, name: 'tab', version: '4.0.0', created_at: 5.days.ago) }
+ let_it_be(:package) { create(:npm_package, project: project1, name: 'uab', version: '5.0.0', created_at: 4.days.ago) }
+ let_it_be(:composer_package) { create(:composer_package, project: project2, name: 'vab', version: '6.0.0', created_at: 3.days.ago) }
+ let_it_be(:debian_package) { create(:debian_package, project: project2, name: 'zab', version: '7.0.0', created_at: 2.days.ago) }
+ let_it_be(:composer_metadatum) do
+ create(:composer_metadatum, package: composer_package,
+ target_sha: 'afdeh',
+ composer_json: { name: 'x', type: 'y', license: 'z', version: 1 })
+ end
+
+ let(:package_names) { graphql_data_at(resource_type, :packages, :nodes, :name) }
+ let(:target_shas) { graphql_data_at(resource_type, :packages, :nodes, :metadata, :target_sha) }
+ let(:packages) { graphql_data_at(resource_type, :packages, :nodes) }
+
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ #{all_graphql_fields_for('packages'.classify, excluded: ['project'])}
+ metadata { #{query_graphql_fragment('ComposerMetadata')} }
+ }
+ QUERY
+ end
+
+ let(:query) do
+ graphql_query_for(
+ resource_type,
+ { 'fullPath' => resource.full_path },
+ query_graphql_field('packages', {}, fields)
+ )
+ end
+
context 'when user has access to the resource' do
before do
resource.add_reporter(current_user)
@@ -48,4 +80,101 @@ RSpec.shared_examples 'group and project packages query' do
expect(packages).to be_nil
end
end
+
+ describe 'sorting and pagination' do
+ let_it_be(:ascending_packages) { [maven_package, package, composer_package, debian_package].map { |package| global_id_of(package)} }
+
+ let(:data_path) { [resource_type, :packages] }
+
+ before do
+ resource.add_reporter(current_user)
+ end
+
+ [:CREATED_ASC, :NAME_ASC, :VERSION_ASC, :TYPE_ASC].each do |order|
+ context "#{order}" do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { order }
+ let(:first_param) { 4 }
+ let(:expected_results) { ascending_packages }
+ end
+ end
+ end
+
+ [:CREATED_DESC, :NAME_DESC, :VERSION_DESC, :TYPE_DESC].each do |order|
+ context "#{order}" do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { order }
+ let(:first_param) { 4 }
+ let(:expected_results) { ascending_packages.reverse }
+ end
+ end
+ end
+
+ context 'with an invalid sort' do
+ let(:query) do
+ graphql_query_for(
+ resource_type,
+ { 'fullPath' => resource.full_path },
+ query_nodes(:packages, :name, args: { sort: :WRONG_ORDER })
+ )
+ end
+
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+
+ it 'throws an error' do
+ expect_graphql_errors_to_include(/Argument \'sort\' on Field \'packages\' has an invalid value/)
+ end
+ end
+
+ def pagination_query(params)
+ graphql_query_for(resource_type, { 'fullPath' => resource.full_path },
+ query_nodes(:packages, :id, include_pagination_info: true, args: params)
+ )
+ end
+ end
+
+ describe 'filtering' do
+ subject { packages }
+
+ let(:query) do
+ graphql_query_for(
+ resource_type,
+ { 'fullPath' => resource.full_path },
+ query_nodes(:packages, :name, args: params)
+ )
+ end
+
+ before do
+ resource.add_reporter(current_user)
+ post_graphql(query, current_user: current_user)
+ end
+
+ context 'package_name' do
+ let(:params) { { package_name: maven_package.name } }
+
+ it { is_expected.to contain_exactly({ "name" => maven_package.name }) }
+ end
+
+ context 'package_type' do
+ let(:params) { { package_type: :COMPOSER } }
+
+ it { is_expected.to contain_exactly({ "name" => composer_package.name }) }
+ end
+
+ context 'status' do
+ let_it_be(:errored_package) { create(:maven_package, project: project1, status: 'error') }
+
+ let(:params) { { status: :ERROR } }
+
+ it { is_expected.to contain_exactly({ "name" => errored_package.name }) }
+ end
+
+ context 'include_versionless' do
+ let(:params) { { include_versionless: true } }
+
+ it { is_expected.to include({ "name" => versionaless_package.name }) }
+ end
+ end
end
diff --git a/spec/support/shared_examples/requests/api/issuable_update_shared_examples.rb b/spec/support/shared_examples/requests/api/issuable_update_shared_examples.rb
index ded381fd402..a3378d4619b 100644
--- a/spec/support/shared_examples/requests/api/issuable_update_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/issuable_update_shared_examples.rb
@@ -3,7 +3,7 @@
RSpec.shared_examples 'issuable update endpoint' do
let(:area) { entity.class.name.underscore.pluralize }
- describe 'PUT /projects/:id/issues/:issue_id' do
+ describe 'PUT /projects/:id/issues/:issue_iid' do
let(:url) { "/projects/#{project.id}/#{area}/#{entity.iid}" }
it 'clears labels when labels param is nil' do
diff --git a/spec/support/shared_examples/requests/api/multiple_and_scoped_issue_boards_shared_examples.rb b/spec/support/shared_examples/requests/api/multiple_and_scoped_issue_boards_shared_examples.rb
index 54aa9d47dd8..fa111ca5811 100644
--- a/spec/support/shared_examples/requests/api/multiple_and_scoped_issue_boards_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/multiple_and_scoped_issue_boards_shared_examples.rb
@@ -14,7 +14,6 @@ RSpec.shared_examples 'multiple and scoped issue boards' do |route_definition|
post api(root_url, user), params: { name: "new board" }
expect(response).to have_gitlab_http_status(:created)
-
expect(response).to match_response_schema('public_api/v4/board', dir: "ee")
end
end
diff --git a/spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb b/spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb
new file mode 100644
index 00000000000..70cc9b1e6b5
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb
@@ -0,0 +1,251 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'when package feature is disabled' do
+ before do
+ stub_config(packages: { enabled: false })
+ end
+
+ it_behaves_like 'returning response status', :not_found
+end
+
+RSpec.shared_examples 'without authentication' do
+ it_behaves_like 'returning response status', :unauthorized
+end
+
+RSpec.shared_examples 'with authentication' do
+ where(:user_role, :token_header, :token_type, :valid_token, :status) do
+ :guest | 'PRIVATE-TOKEN' | :personal_access_token | true | :not_found
+ :guest | 'PRIVATE-TOKEN' | :personal_access_token | false | :unauthorized
+ :guest | 'DEPLOY-TOKEN' | :deploy_token | true | :not_found
+ :guest | 'DEPLOY-TOKEN' | :deploy_token | false | :unauthorized
+ :guest | 'JOB-TOKEN' | :job_token | true | :not_found
+ :guest | 'JOB-TOKEN' | :job_token | false | :unauthorized
+ :reporter | 'PRIVATE-TOKEN' | :personal_access_token | true | :not_found
+ :reporter | 'PRIVATE-TOKEN' | :personal_access_token | false | :unauthorized
+ :reporter | 'DEPLOY-TOKEN' | :deploy_token | true | :not_found
+ :reporter | 'DEPLOY-TOKEN' | :deploy_token | false | :unauthorized
+ :reporter | 'JOB-TOKEN' | :job_token | true | :not_found
+ :reporter | 'JOB-TOKEN' | :job_token | false | :unauthorized
+ :developer | 'PRIVATE-TOKEN' | :personal_access_token | true | :not_found
+ :developer | 'PRIVATE-TOKEN' | :personal_access_token | false | :unauthorized
+ :developer | 'DEPLOY-TOKEN' | :deploy_token | true | :not_found
+ :developer | 'DEPLOY-TOKEN' | :deploy_token | false | :unauthorized
+ :developer | 'JOB-TOKEN' | :job_token | true | :not_found
+ :developer | 'JOB-TOKEN' | :job_token | false | :unauthorized
+ end
+
+ with_them do
+ before do
+ project.send("add_#{user_role}", user) unless user_role == :anonymous
+ end
+
+ let(:token) { valid_token ? tokens[token_type] : 'invalid-token123' }
+ let(:headers) { { token_header => token } }
+
+ it_behaves_like 'returning response status', params[:status]
+ end
+end
+
+RSpec.shared_examples 'an unimplemented route' do
+ it_behaves_like 'without authentication'
+ it_behaves_like 'with authentication'
+ it_behaves_like 'when package feature is disabled'
+end
+
+RSpec.shared_examples 'grants terraform module download' do |user_type, status, add_member = true|
+ context "for user type #{user_type}" do
+ before do
+ group.send("add_#{user_type}", user) if add_member && user_type != :anonymous
+ end
+
+ it_behaves_like 'returning response status', status
+
+ it 'returns a valid response' do
+ subject
+
+ expect(response.headers).to include 'X-Terraform-Get'
+ end
+ end
+end
+
+RSpec.shared_examples 'returns terraform module packages' do |user_type, status, add_member = true|
+ context "for user type #{user_type}" do
+ before do
+ group.send("add_#{user_type}", user) if add_member && user_type != :anonymous
+ end
+
+ it_behaves_like 'returning response status', status
+
+ it 'returning a valid response' do
+ subject
+
+ expect(json_response).to match_schema('public_api/v4/packages/terraform/modules/v1/versions')
+ end
+ end
+end
+
+RSpec.shared_examples 'returns no terraform module packages' do |user_type, status, add_member = true|
+ context "for user type #{user_type}" do
+ before do
+ group.send("add_#{user_type}", user) if add_member && user_type != :anonymous
+ end
+
+ it_behaves_like 'returning response status', status
+
+ it 'returns a response with no versions' do
+ subject
+
+ expect(json_response['modules'][0]['versions'].size).to eq(0)
+ end
+ end
+end
+
+RSpec.shared_examples 'grants terraform module 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
+ end
+end
+
+RSpec.shared_examples 'grants terraform module package file 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
+ it_behaves_like 'a package tracking event', described_class.name, 'pull_package'
+ end
+end
+
+RSpec.shared_examples 'rejects terraform module 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
+ end
+end
+
+RSpec.shared_examples 'process terraform module workhorse authorization' 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 'has the proper content type' do
+ subject
+
+ expect(response.media_type).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ end
+
+ context 'with a request that bypassed gitlab-workhorse' do
+ let(:headers) do
+ { 'HTTP_PRIVATE_TOKEN' => personal_access_token.token }
+ .merge(workhorse_headers)
+ .tap { |h| h.delete(Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER) }
+ end
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ it_behaves_like 'returning response status', :forbidden
+ end
+ end
+end
+
+RSpec.shared_examples 'process terraform module upload' do |user_type, status, add_member = true|
+ RSpec.shared_examples 'creates terraform module package files' do
+ it 'creates package files', :aggregate_failures do
+ expect { subject }
+ .to change { project.packages.count }.by(1)
+ .and change { Packages::PackageFile.count }.by(1)
+ expect(response).to have_gitlab_http_status(status)
+
+ package_file = project.packages.last.package_files.reload.last
+ expect(package_file.file_name).to eq('mymodule-mysystem-1.0.0.tgz')
+ end
+ end
+
+ context "for user type #{user_type}" do
+ before do
+ project.send("add_#{user_type}", user) if add_member && user_type != :anonymous
+ end
+
+ context 'with object storage disabled' do
+ before do
+ stub_package_file_object_storage(enabled: false)
+ end
+
+ context 'without a file from workhorse' do
+ let(:send_rewritten_field) { false }
+
+ it_behaves_like 'returning response status', :bad_request
+ end
+
+ context 'with correct params' do
+ it_behaves_like 'package workhorse uploads'
+ it_behaves_like 'creates terraform module package files'
+ it_behaves_like 'a package tracking event', described_class.name, 'push_package'
+ end
+ end
+
+ context 'with object storage enabled' do
+ let(:tmp_object) do
+ fog_connection.directories.new(key: 'packages').files.create( # rubocop:disable Rails/SaveBang
+ key: "tmp/uploads/#{file_name}",
+ body: 'content'
+ )
+ end
+
+ let(:fog_file) { fog_to_uploaded_file(tmp_object) }
+ let(:params) { { file: fog_file, 'file.remote_id' => file_name } }
+
+ context 'and direct upload enabled' do
+ let(:fog_connection) do
+ stub_package_file_object_storage(direct_upload: true)
+ end
+
+ it_behaves_like 'creates terraform module package files'
+
+ ['123123', '../../123123'].each do |remote_id|
+ context "with invalid remote_id: #{remote_id}" do
+ let(:params) do
+ {
+ file: fog_file,
+ 'file.remote_id' => remote_id
+ }
+ end
+
+ it_behaves_like 'returning response status', :forbidden
+ end
+ end
+ end
+
+ context 'and direct upload disabled' do
+ context 'and background upload disabled' do
+ let(:fog_connection) do
+ stub_package_file_object_storage(direct_upload: false, background_upload: false)
+ end
+
+ it_behaves_like 'creates terraform module package files'
+ end
+
+ context 'and background upload enabled' do
+ let(:fog_connection) do
+ stub_package_file_object_storage(direct_upload: false, background_upload: true)
+ end
+
+ it_behaves_like 'creates terraform module package files'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/time_tracking_shared_examples.rb b/spec/support/shared_examples/requests/api/time_tracking_shared_examples.rb
index fb6d6603beb..afc902dd184 100644
--- a/spec/support/shared_examples/requests/api/time_tracking_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/time_tracking_shared_examples.rb
@@ -125,6 +125,22 @@ RSpec.shared_examples 'time tracking endpoints' do |issuable_name|
expect(json_response['message']['base'].first).to eq(_('Time to subtract exceeds the total time spent'))
end
end
+
+ if issuable_name == 'merge_request'
+ it 'calls update service with :use_specialized_service param' do
+ expect(::MergeRequests::UpdateService).to receive(:new).with(project: project, current_user: user, params: hash_including(use_specialized_service: true))
+
+ post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/add_spent_time", user), params: { duration: '2h' }
+ end
+ end
+
+ if issuable_name == 'issue'
+ it 'calls update service without :use_specialized_service param' do
+ expect(::Issues::UpdateService).to receive(:new).with(project: project, current_user: user, params: hash_not_including(use_specialized_service: true))
+
+ post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/add_spent_time", user), params: { duration: '2h' }
+ end
+ end
end
describe "POST /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/reset_spent_time" do
diff --git a/spec/support/shared_examples/requests/clusters/integrations_controller_shared_examples.rb b/spec/support/shared_examples/requests/clusters/integrations_controller_shared_examples.rb
index 490c7d12115..91fdcbd9b1d 100644
--- a/spec/support/shared_examples/requests/clusters/integrations_controller_shared_examples.rb
+++ b/spec/support/shared_examples/requests/clusters/integrations_controller_shared_examples.rb
@@ -2,7 +2,7 @@
RSpec.shared_examples '#create_or_update action' do
let(:params) do
- { integration: { application_type: Clusters::Applications::Prometheus.application_name, enabled: true } }
+ { integration: { application_type: 'prometheus', enabled: true } }
end
let(:path) { raise NotImplementedError }
diff --git a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
index 926da827e75..95817624658 100644
--- a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
+++ b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
#
# Requires let variables:
-# * throttle_setting_prefix: "throttle_authenticated_api", "throttle_authenticated_web", "throttle_protected_paths"
+# * throttle_setting_prefix: "throttle_authenticated_api", "throttle_authenticated_web", "throttle_protected_paths", "throttle_authenticated_packages_api"
# * request_method
# * request_args
# * other_user_request_args
@@ -13,7 +13,8 @@ RSpec.shared_examples 'rate-limited token-authenticated requests' do
{
"throttle_protected_paths" => "throttle_authenticated_protected_paths_api",
"throttle_authenticated_api" => "throttle_authenticated_api",
- "throttle_authenticated_web" => "throttle_authenticated_web"
+ "throttle_authenticated_web" => "throttle_authenticated_web",
+ "throttle_authenticated_packages_api" => "throttle_authenticated_packages_api"
}
end
diff --git a/spec/support/shared_examples/row_lock_shared_examples.rb b/spec/support/shared_examples/row_lock_shared_examples.rb
new file mode 100644
index 00000000000..5e003172215
--- /dev/null
+++ b/spec/support/shared_examples/row_lock_shared_examples.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+# Ensure that a SQL command to lock this row(s) was requested.
+# Ensure a transaction also occurred.
+# Be careful! This form of spec is not foolproof, but better than nothing.
+
+RSpec.shared_examples 'locked row' do
+ it "has locked row" do
+ table_name = row.class.table_name
+ ids_regex = /SELECT.*FROM.*#{table_name}.*"#{table_name}"."id" = #{row.id}.+FOR UPDATE/m
+
+ expect(recorded_queries.log).to include a_string_matching 'SAVEPOINT'
+ expect(recorded_queries.log).to include a_string_matching ids_regex
+ end
+end
+
+RSpec.shared_examples 'locked rows' do
+ it "has locked rows" do
+ table_name = rows.first.class.table_name
+
+ row_ids = rows.map(&:id).join(', ')
+ ids_regex = /SELECT.+FROM.+"#{table_name}".+"#{table_name}"."id" IN \(#{row_ids}\).+FOR UPDATE/m
+
+ expect(recorded_queries.log).to include a_string_matching 'SAVEPOINT'
+ expect(recorded_queries.log).to include a_string_matching ids_regex
+ end
+end
diff --git a/spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb b/spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb
index 00146335ef7..9d7ae6bcb3d 100644
--- a/spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb
+++ b/spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb
@@ -20,9 +20,27 @@ RSpec.shared_examples 'avoid N+1 on environments serialization' do
expect { serialize(grouping: false) }.not_to exceed_query_limit(control.count)
end
- def serialize(grouping:)
+ it 'does not preload for environments that does not exist in the page', :request_store do
+ create_environment_with_associations(project)
+
+ first_page_query = ActiveRecord::QueryRecorder.new do
+ serialize(grouping: false, query: { page: 1, per_page: 1 } )
+ end
+
+ second_page_query = ActiveRecord::QueryRecorder.new do
+ serialize(grouping: false, query: { page: 2, per_page: 1 } )
+ end
+
+ expect(second_page_query.count).to be < first_page_query.count
+ end
+
+ def serialize(grouping:, query: nil)
+ query ||= { page: 1, per_page: 1 }
+ request = double(url: "#{Gitlab.config.gitlab.url}:8080/api/v4/projects?#{query.to_query}", query_parameters: query)
+
EnvironmentSerializer.new(current_user: user, project: project).yield_self do |serializer|
serializer.within_folders if grouping
+ serializer.with_pagination(request, spy('response'))
serializer.represent(Environment.where(project: project))
end
end
diff --git a/spec/support/shared_examples/serializers/pipeline_artifacts_shared_example.rb b/spec/support/shared_examples/serializers/pipeline_artifacts_shared_example.rb
deleted file mode 100644
index d5ffd5e7510..00000000000
--- a/spec/support/shared_examples/serializers/pipeline_artifacts_shared_example.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-RSpec.shared_examples 'public artifacts' do
- let_it_be(:project) { create(:project, :public) }
- let(:pipeline) { create(:ci_empty_pipeline, status: :success, project: project) }
-
- context 'that has artifacts' do
- let!(:build) { create(:ci_build, :success, :artifacts, pipeline: pipeline) }
-
- it 'contains information about artifacts' do
- expect(subject[:details][:artifacts].length).to eq(1)
- end
- end
-
- context 'that has non public artifacts' do
- let!(:build) { create(:ci_build, :success, :artifacts, :non_public_artifacts, pipeline: pipeline) }
-
- it 'does not contain information about artifacts' do
- expect(subject[:details][:artifacts].length).to eq(0)
- end
- end
-end
diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb
new file mode 100644
index 00000000000..218a3462c35
--- /dev/null
+++ b/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb
@@ -0,0 +1,161 @@
+# frozen_string_literal: true
+
+# This shared_example requires the following variables:
+# - `service`, the service which includes AlertManagement::AlertProcessing
+RSpec.shared_examples 'creates an alert management alert or errors' do
+ it { is_expected.to be_success }
+
+ it 'creates AlertManagement::Alert' do
+ expect(Gitlab::AppLogger).not_to receive(:warn)
+
+ expect { subject }.to change(AlertManagement::Alert, :count).by(1)
+ end
+
+ it 'executes the alert service hooks' do
+ expect_next_instance_of(AlertManagement::Alert) do |alert|
+ expect(alert).to receive(:execute_services)
+ end
+
+ subject
+ end
+
+ context 'and fails to save' do
+ let(:errors) { double(messages: { hosts: ['hosts array is over 255 chars'] })}
+
+ before do
+ allow(service).to receive(:alert).and_call_original
+ allow(service).to receive_message_chain(:alert, :save).and_return(false)
+ allow(service).to receive_message_chain(:alert, :errors).and_return(errors)
+ end
+
+ it_behaves_like 'alerts service responds with an error', :bad_request
+
+ it 'writes a warning to the log' do
+ expect(Gitlab::AppLogger).to receive(:warn).with(
+ message: "Unable to create AlertManagement::Alert from #{source}",
+ project_id: project.id,
+ alert_errors: { hosts: ['hosts array is over 255 chars'] }
+ )
+
+ subject
+ end
+ end
+end
+
+# This shared_example requires the following variables:
+# - last_alert_attributes, last created alert
+# - project, project that alert created
+# - payload_raw, hash representation of payload
+# - environment, project's environment
+# - fingerprint, fingerprint hash
+RSpec.shared_examples 'properly assigns the alert properties' do
+ specify do
+ subject
+
+ expect(last_alert_attributes).to match({
+ project_id: project.id,
+ title: payload_raw.fetch(:title),
+ started_at: Time.zone.parse(payload_raw.fetch(:start_time)),
+ severity: payload_raw.fetch(:severity, nil),
+ status: AlertManagement::Alert.status_value(:triggered),
+ events: 1,
+ domain: domain,
+ hosts: payload_raw.fetch(:hosts, nil),
+ payload: payload_raw.with_indifferent_access,
+ issue_id: nil,
+ description: payload_raw.fetch(:description, nil),
+ monitoring_tool: payload_raw.fetch(:monitoring_tool, nil),
+ service: payload_raw.fetch(:service, nil),
+ fingerprint: Digest::SHA1.hexdigest(fingerprint),
+ environment_id: environment.id,
+ ended_at: nil,
+ prometheus_alert_id: nil
+ }.with_indifferent_access)
+ end
+end
+
+RSpec.shared_examples 'does not create an alert management alert' do
+ specify do
+ expect { subject }.not_to change(AlertManagement::Alert, :count)
+ end
+end
+
+# This shared_example requires the following variables:
+# - `alert`, the alert for which events should be incremented
+RSpec.shared_examples 'adds an alert management alert event' do
+ specify do
+ expect(alert).not_to receive(:execute_services)
+
+ expect { subject }.to change { alert.reload.events }.by(1)
+
+ expect(subject).to be_success
+ end
+
+ it_behaves_like 'does not create an alert management alert'
+end
+
+# This shared_example requires the following variables:
+# - `alert`, the alert for which events should not be incremented
+RSpec.shared_examples 'does not add an alert management alert event' do
+ specify do
+ expect { subject }.not_to change { alert.reload.events }
+ end
+end
+
+RSpec.shared_examples 'processes new firing alert' do
+ include_examples 'processes never-before-seen alert'
+
+ context 'for an existing alert with the same fingerprint' do
+ let_it_be(:gitlab_fingerprint) { Digest::SHA1.hexdigest(fingerprint) }
+
+ context 'which is triggered' do
+ let_it_be(:alert) { create(:alert_management_alert, :triggered, fingerprint: gitlab_fingerprint, project: project) }
+
+ it_behaves_like 'adds an alert management alert event'
+ it_behaves_like 'sends alert notification emails if enabled'
+ it_behaves_like 'processes incident issues if enabled', with_issue: true
+
+ it_behaves_like 'does not create an alert management alert'
+ it_behaves_like 'does not create a system note for alert'
+
+ context 'with an existing resolved alert as well' do
+ let_it_be(:resolved_alert) { create(:alert_management_alert, :resolved, project: project, fingerprint: gitlab_fingerprint) }
+
+ it_behaves_like 'adds an alert management alert event'
+ it_behaves_like 'sends alert notification emails if enabled'
+ it_behaves_like 'processes incident issues if enabled', with_issue: true
+
+ it_behaves_like 'does not create an alert management alert'
+ it_behaves_like 'does not create a system note for alert'
+ end
+ end
+
+ context 'which is acknowledged' do
+ let_it_be(:alert) { create(:alert_management_alert, :acknowledged, fingerprint: gitlab_fingerprint, project: project) }
+
+ it_behaves_like 'adds an alert management alert event'
+ it_behaves_like 'processes incident issues if enabled', with_issue: true
+
+ it_behaves_like 'does not create an alert management alert'
+ it_behaves_like 'does not create a system note for alert'
+ it_behaves_like 'does not send alert notification emails'
+ end
+
+ context 'which is ignored' do
+ let_it_be(:alert) { create(:alert_management_alert, :ignored, fingerprint: gitlab_fingerprint, project: project) }
+
+ it_behaves_like 'adds an alert management alert event'
+ it_behaves_like 'processes incident issues if enabled', with_issue: true
+
+ it_behaves_like 'does not create an alert management alert'
+ it_behaves_like 'does not create a system note for alert'
+ it_behaves_like 'does not send alert notification emails'
+ end
+
+ context 'which is resolved' do
+ let_it_be(:alert) { create(:alert_management_alert, :resolved, fingerprint: gitlab_fingerprint, project: project) }
+
+ include_examples 'processes never-before-seen alert'
+ end
+ end
+end
diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/alert_recovery_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/alert_recovery_shared_examples.rb
new file mode 100644
index 00000000000..86e7da5bcbe
--- /dev/null
+++ b/spec/support/shared_examples/services/alert_management/alert_processing/alert_recovery_shared_examples.rb
@@ -0,0 +1,113 @@
+# frozen_string_literal: true
+
+# This shared_example requires the following variables:
+# - `alert`, the alert to be resolved
+RSpec.shared_examples 'resolves an existing alert management alert' do
+ it 'sets the end time and status' do
+ expect(Gitlab::AppLogger).not_to receive(:warn)
+
+ expect { subject }
+ .to change { alert.reload.resolved? }.to(true)
+ .and change { alert.ended_at.present? }.to(true)
+
+ expect(subject).to be_success
+ end
+end
+
+# This shared_example requires the following variables:
+# - `alert`, the alert not to be updated
+RSpec.shared_examples 'does not change the alert end time' do
+ specify do
+ expect { subject }.not_to change { alert.reload.ended_at }
+ end
+end
+
+# This shared_example requires the following variables:
+# - `project`, expected project for an incoming alert
+# - `service`, a service which includes AlertManagement::AlertProcessing
+# - `alert` (optional), the alert which should fail to resolve. If not
+# included, the log is expected to correspond to a new alert
+RSpec.shared_examples 'writes a warning to the log for a failed alert status update' do
+ before do
+ allow(service).to receive(:alert).and_call_original
+ allow(service).to receive_message_chain(:alert, :resolve).and_return(false)
+ end
+
+ specify do
+ expect(Gitlab::AppLogger).to receive(:warn).with(
+ message: 'Unable to update AlertManagement::Alert status to resolved',
+ project_id: project.id,
+ alert_id: alert ? alert.id : (last_alert_id + 1)
+ )
+
+ # Failure to resolve a recovery alert is not a critical failure
+ expect(subject).to be_success
+ end
+
+ private
+
+ def last_alert_id
+ AlertManagement::Alert.connection
+ .select_value("SELECT nextval('#{AlertManagement::Alert.sequence_name}')")
+ end
+end
+
+RSpec.shared_examples 'processes recovery alert' do
+ context 'seen for the first time' do
+ let(:alert) { AlertManagement::Alert.last }
+
+ include_examples 'processes never-before-seen recovery alert'
+ end
+
+ context 'for an existing alert with the same fingerprint' do
+ let_it_be(:gitlab_fingerprint) { Digest::SHA1.hexdigest(fingerprint) }
+
+ context 'which is triggered' do
+ let_it_be(:alert) { create(:alert_management_alert, :triggered, project: project, fingerprint: gitlab_fingerprint, monitoring_tool: source) }
+
+ it_behaves_like 'resolves an existing alert management alert'
+ it_behaves_like 'creates expected system notes for alert', :recovery_alert, :resolve_alert
+ it_behaves_like 'sends alert notification emails if enabled'
+ it_behaves_like 'closes related incident if enabled'
+ it_behaves_like 'writes a warning to the log for a failed alert status update'
+
+ it_behaves_like 'does not create an alert management alert'
+ it_behaves_like 'does not process incident issues'
+ it_behaves_like 'does not add an alert management alert event'
+ end
+
+ context 'which is ignored' do
+ let_it_be(:alert) { create(:alert_management_alert, :ignored, project: project, fingerprint: gitlab_fingerprint, monitoring_tool: source) }
+
+ it_behaves_like 'resolves an existing alert management alert'
+ it_behaves_like 'creates expected system notes for alert', :recovery_alert, :resolve_alert
+ it_behaves_like 'sends alert notification emails if enabled'
+ it_behaves_like 'closes related incident if enabled'
+ it_behaves_like 'writes a warning to the log for a failed alert status update'
+
+ it_behaves_like 'does not create an alert management alert'
+ it_behaves_like 'does not process incident issues'
+ it_behaves_like 'does not add an alert management alert event'
+ end
+
+ context 'which is acknowledged' do
+ let_it_be(:alert) { create(:alert_management_alert, :acknowledged, project: project, fingerprint: gitlab_fingerprint, monitoring_tool: source) }
+
+ it_behaves_like 'resolves an existing alert management alert'
+ it_behaves_like 'creates expected system notes for alert', :recovery_alert, :resolve_alert
+ it_behaves_like 'sends alert notification emails if enabled'
+ it_behaves_like 'closes related incident if enabled'
+ it_behaves_like 'writes a warning to the log for a failed alert status update'
+
+ it_behaves_like 'does not create an alert management alert'
+ it_behaves_like 'does not process incident issues'
+ it_behaves_like 'does not add an alert management alert event'
+ end
+
+ context 'which is resolved' do
+ let_it_be(:alert) { create(:alert_management_alert, :resolved, project: project, fingerprint: gitlab_fingerprint, monitoring_tool: source) }
+
+ include_examples 'processes never-before-seen recovery alert'
+ end
+ end
+end
diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/incident_creation_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/incident_creation_shared_examples.rb
new file mode 100644
index 00000000000..c6ac07b6dd5
--- /dev/null
+++ b/spec/support/shared_examples/services/alert_management/alert_processing/incident_creation_shared_examples.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+# Expects usage of 'incident settings enabled' context.
+#
+# This shared_example includes the following option:
+# - with_issue: includes a test for when the defined `alert` has an associated issue
+#
+# This shared_example requires the following variables:
+# - `alert`, required if :with_issue is true
+RSpec.shared_examples 'processes incident issues if enabled' do |with_issue: false|
+ include_examples 'processes incident issues', with_issue
+
+ context 'with incident setting disabled' do
+ let(:create_issue) { false }
+
+ it_behaves_like 'does not process incident issues'
+ end
+end
+
+RSpec.shared_examples 'processes incident issues' do |with_issue: false|
+ before do
+ allow_next_instance_of(AlertManagement::Alert) do |alert|
+ allow(alert).to receive(:execute_services)
+ end
+ end
+
+ specify do
+ expect(IncidentManagement::ProcessAlertWorkerV2)
+ .to receive(:perform_async)
+ .with(kind_of(Integer))
+
+ Sidekiq::Testing.inline! do
+ expect(subject).to be_success
+ end
+ end
+
+ context 'with issue', if: with_issue do
+ before do
+ alert.update!(issue: create(:issue, project: project))
+ end
+
+ it_behaves_like 'does not process incident issues'
+ end
+end
+
+RSpec.shared_examples 'does not process incident issues' do
+ specify do
+ expect(IncidentManagement::ProcessAlertWorkerV2).not_to receive(:perform_async)
+
+ subject
+ end
+end
diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/incident_resolution_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/incident_resolution_shared_examples.rb
new file mode 100644
index 00000000000..132f1e0422e
--- /dev/null
+++ b/spec/support/shared_examples/services/alert_management/alert_processing/incident_resolution_shared_examples.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+# Expects usage of 'incident settings enabled' context.
+#
+# This shared_example requires the following variables:
+# - `alert`, alert for which related incidents should be closed
+# - `project`, project of the alert
+RSpec.shared_examples 'closes related incident if enabled' do
+ context 'with issue' do
+ before do
+ alert.update!(issue: create(:issue, project: project))
+ end
+
+ it { expect { subject }.to change { alert.issue.reload.closed? }.from(false).to(true) }
+ it { expect { subject }.to change(ResourceStateEvent, :count).by(1) }
+ end
+
+ context 'without issue' do
+ it { expect { subject }.not_to change { alert.reload.issue } }
+ it { expect { subject }.not_to change(ResourceStateEvent, :count) }
+ end
+
+ context 'with incident setting disabled' do
+ let(:auto_close_incident) { false }
+
+ it_behaves_like 'does not close related incident'
+ end
+end
+
+RSpec.shared_examples 'does not close related incident' do
+ context 'with issue' do
+ before do
+ alert.update!(issue: create(:issue, project: project))
+ end
+
+ it { expect { subject }.not_to change { alert.issue.reload.state } }
+ it { expect { subject }.not_to change(ResourceStateEvent, :count) }
+ end
+
+ context 'without issue' do
+ it { expect { subject }.not_to change { alert.reload.issue } }
+ it { expect { subject }.not_to change(ResourceStateEvent, :count) }
+ end
+end
diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/notifications_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/notifications_shared_examples.rb
new file mode 100644
index 00000000000..5f30b58176b
--- /dev/null
+++ b/spec/support/shared_examples/services/alert_management/alert_processing/notifications_shared_examples.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+# Expects usage of 'incident settings enabled' context.
+#
+# This shared_example includes the following option:
+# - count: number of notifications expected to be sent
+RSpec.shared_examples 'sends alert notification emails if enabled' do |count: 1|
+ include_examples 'sends alert notification emails', count
+
+ context 'with email setting disabled' do
+ let(:send_email) { false }
+
+ it_behaves_like 'does not send alert notification emails'
+ end
+end
+
+RSpec.shared_examples 'sends alert notification emails' do |count: 1|
+ let(:notification_async) { double(NotificationService::Async) }
+
+ specify do
+ allow(NotificationService).to receive_message_chain(:new, :async).and_return(notification_async)
+ expect(notification_async).to receive(:prometheus_alerts_fired).exactly(count).times
+
+ subject
+ end
+end
+
+RSpec.shared_examples 'does not send alert notification emails' do
+ specify do
+ expect(NotificationService).not_to receive(:new)
+
+ subject
+ end
+end
diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/system_notes_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/system_notes_shared_examples.rb
new file mode 100644
index 00000000000..57d598c0259
--- /dev/null
+++ b/spec/support/shared_examples/services/alert_management/alert_processing/system_notes_shared_examples.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+# This shared_example includes the following option:
+# - notes: any of [:new_alert, :recovery_alert, :resolve_alert].
+# Represents which notes are expected to be created.
+#
+# This shared_example requires the following variables:
+# - `source` (optional), the monitoring tool or integration name
+# expected in the applicable system notes
+RSpec.shared_examples 'creates expected system notes for alert' do |*notes|
+ let(:expected_note_count) { expected_notes.length }
+ let(:new_notes) { Note.last(expected_note_count).pluck(:note) }
+ let(:expected_notes) do
+ {
+ new_alert: source,
+ recovery_alert: source,
+ resolve_alert: 'Resolved'
+ }.slice(*notes)
+ end
+
+ it "for #{notes.join(', ')}" do
+ expect { subject }.to change(Note, :count).by(expected_note_count)
+
+ expected_notes.each_value.with_index do |value, index|
+ expect(new_notes[index]).to include(value)
+ end
+ end
+end
+
+RSpec.shared_examples 'does not create a system note for alert' do
+ specify do
+ expect { subject }.not_to change(Note, :count)
+ end
+end
diff --git a/spec/support/shared_examples/services/alert_management_shared_examples.rb b/spec/support/shared_examples/services/alert_management_shared_examples.rb
index d9f28a97a0f..827ae42f970 100644
--- a/spec/support/shared_examples/services/alert_management_shared_examples.rb
+++ b/spec/support/shared_examples/services/alert_management_shared_examples.rb
@@ -1,111 +1,77 @@
# frozen_string_literal: true
-RSpec.shared_examples 'creates an alert management alert' do
- it { is_expected.to be_success }
+RSpec.shared_examples 'alerts service responds with an error and takes no actions' do |http_status|
+ include_examples 'alerts service responds with an error', http_status
- it 'creates AlertManagement::Alert' do
- expect { subject }.to change(AlertManagement::Alert, :count).by(1)
- end
-
- it 'executes the alert service hooks' do
- expect_next_instance_of(AlertManagement::Alert) do |alert|
- expect(alert).to receive(:execute_services)
- end
+ it_behaves_like 'does not create an alert management alert'
+ it_behaves_like 'does not create a system note for alert'
+ it_behaves_like 'does not process incident issues'
+ it_behaves_like 'does not send alert notification emails'
+end
- subject
+RSpec.shared_examples 'alerts service responds with an error' do |http_status|
+ specify do
+ expect(subject).to be_error
+ expect(subject.http_status).to eq(http_status)
end
end
# This shared_example requires the following variables:
-# - last_alert_attributes, last created alert
-# - project, project that alert created
-# - payload_raw, hash representation of payload
-# - environment, project's environment
-# - fingerprint, fingerprint hash
-RSpec.shared_examples 'assigns the alert properties' do
- it 'ensures that created alert has all data properly assigned' do
- subject
-
- expect(last_alert_attributes).to match(
- project_id: project.id,
- title: payload_raw.fetch(:title),
- started_at: Time.zone.parse(payload_raw.fetch(:start_time)),
- severity: payload_raw.fetch(:severity),
- status: AlertManagement::Alert.status_value(:triggered),
- events: 1,
- domain: domain,
- hosts: payload_raw.fetch(:hosts),
- payload: payload_raw.with_indifferent_access,
- issue_id: nil,
- description: payload_raw.fetch(:description),
- monitoring_tool: payload_raw.fetch(:monitoring_tool),
- service: payload_raw.fetch(:service),
- fingerprint: Digest::SHA1.hexdigest(fingerprint),
- environment_id: environment.id,
- ended_at: nil,
- prometheus_alert_id: nil
+# - `service`, a service which includes ::IncidentManagement::Settings
+RSpec.shared_context 'incident management settings enabled' do
+ let(:auto_close_incident) { true }
+ let(:create_issue) { true }
+ let(:send_email) { true }
+
+ let(:incident_management_setting) do
+ double(
+ auto_close_incident?: auto_close_incident,
+ create_issue?: create_issue,
+ send_email?: send_email
)
end
-end
-RSpec.shared_examples 'does not an create alert management alert' do
- it 'does not create alert' do
- expect { subject }.not_to change(AlertManagement::Alert, :count)
+ before do
+ allow(ProjectServiceWorker).to receive(:perform_async)
+ allow(service)
+ .to receive(:incident_management_setting)
+ .and_return(incident_management_setting)
end
end
-RSpec.shared_examples 'adds an alert management alert event' do
- it { is_expected.to be_success }
-
- it 'does not create an alert' do
- expect { subject }.not_to change(AlertManagement::Alert, :count)
- end
-
- it 'increases alert events count' do
- expect { subject }.to change { alert.reload.events }.by(1)
- end
-
- it 'does not executes the alert service hooks' do
- expect(alert).not_to receive(:execute_services)
-
- subject
- end
+RSpec.shared_examples 'processes never-before-seen alert' do
+ it_behaves_like 'creates an alert management alert or errors'
+ it_behaves_like 'creates expected system notes for alert', :new_alert
+ it_behaves_like 'processes incident issues if enabled'
+ it_behaves_like 'sends alert notification emails if enabled'
end
-RSpec.shared_examples 'processes incident issues' do
- let(:create_incident_service) { spy }
-
- before do
- allow_any_instance_of(AlertManagement::Alert).to receive(:execute_services)
+RSpec.shared_examples 'processes never-before-seen recovery alert' do
+ it_behaves_like 'creates an alert management alert or errors'
+ it_behaves_like 'creates expected system notes for alert', :new_alert, :recovery_alert, :resolve_alert
+ it_behaves_like 'sends alert notification emails if enabled'
+ it_behaves_like 'does not process incident issues'
+ it_behaves_like 'writes a warning to the log for a failed alert status update' do
+ let(:alert) { nil } # Ensure the next alert id is used
end
- it 'processes issues' do
- expect(IncidentManagement::ProcessAlertWorker)
- .to receive(:perform_async)
- .with(nil, nil, kind_of(Integer))
- .once
+ it 'resolves the alert' do
+ subject
- Sidekiq::Testing.inline! do
- expect(subject).to be_success
- end
+ expect(AlertManagement::Alert.last.ended_at).to be_present
+ expect(AlertManagement::Alert.last.resolved?).to be(true)
end
end
-RSpec.shared_examples 'does not process incident issues' do
- it 'does not process issues' do
- expect(IncidentManagement::ProcessAlertWorker)
- .not_to receive(:perform_async)
+RSpec.shared_examples 'processes one firing and one resolved prometheus alerts' do
+ it 'creates AlertManagement::Alert' do
+ expect(Gitlab::AppLogger).not_to receive(:warn)
- expect(subject).to be_success
+ expect { subject }
+ .to change(AlertManagement::Alert, :count).by(2)
+ .and change(Note, :count).by(4)
end
-end
-
-RSpec.shared_examples 'does not process incident issues due to error' do |http_status:|
- it 'does not process issues' do
- expect(IncidentManagement::ProcessAlertWorker)
- .not_to receive(:perform_async)
- expect(subject).to be_error
- expect(subject.http_status).to eq(http_status)
- end
+ it_behaves_like 'processes incident issues'
+ it_behaves_like 'sends alert notification emails', count: 2
end
diff --git a/spec/support/shared_examples/services/boards/boards_recent_visit_shared_examples.rb b/spec/support/shared_examples/services/boards/boards_recent_visit_shared_examples.rb
new file mode 100644
index 00000000000..68ea460dabc
--- /dev/null
+++ b/spec/support/shared_examples/services/boards/boards_recent_visit_shared_examples.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'boards recent visit' do
+ let_it_be(:user) { create(:user) }
+
+ describe '#visited' do
+ it 'creates a visit if one does not exists' do
+ expect { described_class.visited!(user, board) }.to change(described_class, :count).by(1)
+ end
+
+ shared_examples 'was visited previously' do
+ let_it_be(:visit) do
+ create(visit_relation,
+ board_parent_relation => board_parent,
+ board_relation => board,
+ user: user,
+ updated_at: 7.days.ago
+ )
+ end
+
+ it 'updates the timestamp' do
+ freeze_time do
+ described_class.visited!(user, board)
+
+ expect(described_class.count).to eq 1
+ expect(described_class.first.updated_at).to be_like_time(Time.zone.now)
+ end
+ end
+ end
+
+ it_behaves_like 'was visited previously'
+
+ context 'when we try to create a visit that is not unique' do
+ before do
+ expect(described_class).to receive(:find_or_create_by).and_raise(ActiveRecord::RecordNotUnique, 'record not unique')
+ expect(described_class).to receive(:find_or_create_by).and_return(visit)
+ end
+
+ it_behaves_like 'was visited previously'
+ end
+ end
+
+ describe '#latest' do
+ def create_visit(time)
+ create(visit_relation, board_parent_relation => board_parent, user: user, updated_at: time)
+ end
+
+ it 'returns the most recent visited' do
+ create_visit(7.days.ago)
+ create_visit(5.days.ago)
+ recent = create_visit(1.day.ago)
+
+ expect(described_class.latest(user, board_parent)).to eq recent
+ end
+
+ it 'returns last 3 visited boards' do
+ create_visit(7.days.ago)
+ visit1 = create_visit(3.days.ago)
+ visit2 = create_visit(2.days.ago)
+ visit3 = create_visit(5.days.ago)
+
+ expect(described_class.latest(user, board_parent, count: 3)).to eq([visit2, visit1, visit3])
+ end
+ end
+end
diff --git a/spec/support/shared_examples/services/boards/create_service_shared_examples.rb b/spec/support/shared_examples/services/boards/create_service_shared_examples.rb
new file mode 100644
index 00000000000..63b5e3a5a84
--- /dev/null
+++ b/spec/support/shared_examples/services/boards/create_service_shared_examples.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'boards recent visit create service' do
+ let_it_be(:user) { create(:user) }
+
+ subject(:service) { described_class.new(board.resource_parent, user) }
+
+ it 'returns nil when there is no user' do
+ service.current_user = nil
+
+ expect(service.execute(board)).to be_nil
+ end
+
+ it 'returns nil when database is read only' do
+ allow(Gitlab::Database).to receive(:read_only?) { true }
+
+ expect(service.execute(board)).to be_nil
+ end
+
+ it 'records the visit' do
+ expect(model).to receive(:visited!).once
+
+ service.execute(board)
+ end
+end
diff --git a/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb b/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb
index 4aa5d7d890b..7d4fbeea0dc 100644
--- a/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb
@@ -146,7 +146,7 @@ RSpec.shared_examples 'issues move service' do |group|
params.merge!(move_after_id: issue1.id, move_before_id: issue2.id)
match_params = { move_between_ids: [issue1.id, issue2.id], board_group_id: parent.id }
- expect(Issues::UpdateService).to receive(:new).with(issue.project, user, match_params).and_return(double(execute: build(:issue)))
+ expect(Issues::UpdateService).to receive(:new).with(project: issue.project, current_user: user, params: match_params).and_return(double(execute: build(:issue)))
described_class.new(parent, user, params).execute(issue)
end
diff --git a/spec/support/shared_examples/services/boards/lists_destroy_service_shared_examples.rb b/spec/support/shared_examples/services/boards/lists_destroy_service_shared_examples.rb
index 94da405e491..af88644ced7 100644
--- a/spec/support/shared_examples/services/boards/lists_destroy_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/boards/lists_destroy_service_shared_examples.rb
@@ -3,30 +3,27 @@
RSpec.shared_examples 'lists destroy service' do
context 'when list type is label' do
it 'removes list from board' do
- list = create(:list, board: board)
service = described_class.new(parent, user)
expect { service.execute(list) }.to change(board.lists, :count).by(-1)
end
it 'decrements position of higher lists' do
- development = create(:list, board: board, position: 0)
- review = create(:list, board: board, position: 1)
- staging = create(:list, board: board, position: 2)
- closed = board.lists.closed.first
+ development = create(list_type, params.merge(position: 0))
+ review = create(list_type, params.merge(position: 1))
+ staging = create(list_type, params.merge(position: 2))
described_class.new(parent, user).execute(development)
expect(review.reload.position).to eq 0
expect(staging.reload.position).to eq 1
- expect(closed.reload.position).to be_nil
+ expect(closed_list.reload.position).to be_nil
end
end
it 'does not remove list from board when list type is closed' do
- list = board.lists.closed.first
service = described_class.new(parent, user)
- expect { service.execute(list) }.not_to change(board.lists, :count)
+ expect { service.execute(closed_list) }.not_to change(board.lists, :count)
end
end
diff --git a/spec/support/shared_examples/services/common_system_notes_shared_examples.rb b/spec/support/shared_examples/services/common_system_notes_shared_examples.rb
index 7b277d4bede..ce412ef55de 100644
--- a/spec/support/shared_examples/services/common_system_notes_shared_examples.rb
+++ b/spec/support/shared_examples/services/common_system_notes_shared_examples.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.shared_examples 'system note creation' do |update_params, note_text|
- subject { described_class.new(project, user).execute(issuable, old_labels: []) }
+ subject { described_class.new(project: project, current_user: user).execute(issuable, old_labels: []) }
before do
issuable.assign_attributes(update_params)
@@ -18,7 +18,7 @@ RSpec.shared_examples 'system note creation' do |update_params, note_text|
end
RSpec.shared_examples 'draft notes creation' do |action|
- subject { described_class.new(project, user).execute(issuable, old_labels: []) }
+ subject { described_class.new(project: project, current_user: user).execute(issuable, old_labels: []) }
it 'creates Draft toggle and title change notes' do
expect { subject }.to change { Note.count }.from(0).to(2)
diff --git a/spec/support/shared_examples/services/destroy_label_links_shared_examples.rb b/spec/support/shared_examples/services/destroy_label_links_shared_examples.rb
new file mode 100644
index 00000000000..d2b52468c25
--- /dev/null
+++ b/spec/support/shared_examples/services/destroy_label_links_shared_examples.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples_for 'service deleting label links of an issuable' do
+ let_it_be(:label_link) { create(:label_link, target: target) }
+
+ def execute
+ described_class.new(target.id, target.class.name).execute
+ end
+
+ it 'deletes label links for specified target ID and type' do
+ control_count = ActiveRecord::QueryRecorder.new { execute }.count
+
+ # Create more label links for the target
+ create(:label_link, target: target)
+ create(:label_link, target: target)
+
+ expect { execute }.not_to exceed_query_limit(control_count)
+ expect(target.reload.label_links.count).to eq(0)
+ end
+end
diff --git a/spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb b/spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb
index ccc287c10de..e776c098fa0 100644
--- a/spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb
@@ -1,10 +1,6 @@
# frozen_string_literal: true
shared_examples_for 'service deleting todos' do
- before do
- stub_feature_flags(destroy_issuable_todos_async: group)
- end
-
it 'destroys associated todos asynchronously' do
expect(TodosDestroyer::DestroyedIssuableWorker)
.to receive(:perform_async)
@@ -12,20 +8,14 @@ shared_examples_for 'service deleting todos' do
subject.execute(issuable)
end
+end
- context 'when destroy_issuable_todos_async feature is disabled for group' do
- before do
- stub_feature_flags(destroy_issuable_todos_async: false)
- end
-
- it 'destroy associated todos synchronously' do
- expect_next_instance_of(TodosDestroyer::DestroyedIssuableWorker) do |worker|
- expect(worker)
- .to receive(:perform)
- .with(issuable.id, issuable.class.name)
- end
+shared_examples_for 'service deleting label links' do
+ it 'destroys associated label links asynchronously' do
+ expect(Issuable::LabelLinksDestroyWorker)
+ .to receive(:perform_async)
+ .with(issuable.id, issuable.class.name)
- subject.execute(issuable)
- end
+ subject.execute(issuable)
end
end
diff --git a/spec/support/shared_examples/services/issuable_shared_examples.rb b/spec/support/shared_examples/services/issuable_shared_examples.rb
index 5b3e0f9e0b9..a50a386afe1 100644
--- a/spec/support/shared_examples/services/issuable_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable_shared_examples.rb
@@ -4,14 +4,14 @@ RSpec.shared_examples 'cache counters invalidator' do
it 'invalidates counter cache for assignees' do
expect_any_instance_of(User).to receive(:invalidate_merge_request_cache_counts)
- described_class.new(project, user, {}).execute(merge_request)
+ described_class.new(project: project, current_user: user).execute(merge_request)
end
end
RSpec.shared_examples 'updating a single task' do
def update_issuable(opts)
issuable = try(:issue) || try(:merge_request)
- described_class.new(project, user, opts).execute(issuable)
+ described_class.new(project: project, current_user: user, params: opts).execute(issuable)
end
before do
diff --git a/spec/support/shared_examples/services/merge_request_shared_examples.rb b/spec/support/shared_examples/services/merge_request_shared_examples.rb
index 178b6bc47e1..d2595b92cbc 100644
--- a/spec/support/shared_examples/services/merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/services/merge_request_shared_examples.rb
@@ -70,7 +70,7 @@ RSpec.shared_examples 'merge request reviewers cache counters invalidator' do
it 'invalidates counter cache for reviewers' do
expect(merge_request.reviewers).to all(receive(:invalidate_merge_request_cache_counts))
- described_class.new(project, user, {}).execute(merge_request)
+ described_class.new(project: project, current_user: user).execute(merge_request)
end
end
@@ -86,7 +86,7 @@ RSpec.shared_examples_for 'a service that can create a merge request' do
context 'when project has been forked', :sidekiq_might_not_need_inline do
let(:forked_project) { fork_project(project, user1, repository: true) }
- let(:service) { described_class.new(forked_project, user1, changes, push_options) }
+ let(:service) { described_class.new(project: forked_project, current_user: user1, changes: changes, push_options: push_options) }
before do
allow(forked_project).to receive(:empty_repo?).and_return(false)
diff --git a/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb b/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb
index b6c33eac7b4..4df12f7849b 100644
--- a/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb
+++ b/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb
@@ -12,13 +12,22 @@ RSpec.shared_examples 'misconfigured dashboard service response' do |status_code
end
RSpec.shared_examples 'valid dashboard service response for schema' do
+ file_ref_resolver = proc do |uri|
+ file = Rails.root.join(uri.path)
+ raise StandardError, "Ref file #{uri.path} must be json" unless uri.path.ends_with?('.json')
+ raise StandardError, "File #{file.to_path} doesn't exists" unless file.exist?
+
+ Gitlab::Json.parse(File.read(file))
+ end
+
it 'returns a json representation of the dashboard' do
result = service_call
expect(result.keys).to contain_exactly(:dashboard, :status)
expect(result[:status]).to eq(:success)
- expect(JSON::Validator.fully_validate(dashboard_schema, result[:dashboard])).to be_empty
+ validator = JSONSchemer.schema(dashboard_schema, ref_resolver: file_ref_resolver)
+ expect(validator.valid?(result[:dashboard].with_indifferent_access)).to be true
end
end
diff --git a/spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb b/spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb
index 8398dd3c453..f7a6bd3676a 100644
--- a/spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb
+++ b/spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb
@@ -7,6 +7,8 @@ RSpec.shared_examples 'updating the namespace package setting attributes' do |fr
expect { subject }
.to change { namespace.package_settings.reload.maven_duplicates_allowed }.from(from[:maven_duplicates_allowed]).to(to[:maven_duplicates_allowed])
.and change { namespace.package_settings.reload.maven_duplicate_exception_regex }.from(from[:maven_duplicate_exception_regex]).to(to[:maven_duplicate_exception_regex])
+ .and change { namespace.package_settings.reload.generic_duplicates_allowed }.from(from[:generic_duplicates_allowed]).to(to[:generic_duplicates_allowed])
+ .and change { namespace.package_settings.reload.generic_duplicate_exception_regex }.from(from[:generic_duplicate_exception_regex]).to(to[:generic_duplicate_exception_regex])
end
end
@@ -26,6 +28,8 @@ RSpec.shared_examples 'creating the namespace package setting' do
expect(namespace.package_setting_relation.maven_duplicates_allowed).to eq(package_settings[:maven_duplicates_allowed])
expect(namespace.package_setting_relation.maven_duplicate_exception_regex).to eq(package_settings[:maven_duplicate_exception_regex])
+ expect(namespace.package_setting_relation.generic_duplicates_allowed).to eq(package_settings[:generic_duplicates_allowed])
+ expect(namespace.package_setting_relation.generic_duplicate_exception_regex).to eq(package_settings[:generic_duplicate_exception_regex])
end
it_behaves_like 'returning a success'
diff --git a/spec/support/shared_examples/services/packages_shared_examples.rb b/spec/support/shared_examples/services/packages_shared_examples.rb
index 4e34c191306..72878e925dc 100644
--- a/spec/support/shared_examples/services/packages_shared_examples.rb
+++ b/spec/support/shared_examples/services/packages_shared_examples.rb
@@ -203,7 +203,9 @@ RSpec.shared_examples 'filters on each package_type' do |is_project: false|
let_it_be(:package7) { create(:generic_package, project: project) }
let_it_be(:package8) { create(:golang_package, project: project) }
let_it_be(:package9) { create(:debian_package, project: project) }
- let_it_be(:package9) { create(:rubygems_package, project: project) }
+ let_it_be(:package10) { create(:rubygems_package, project: project) }
+ let_it_be(:package11) { create(:helm_package, project: project) }
+ let_it_be(:package12) { create(:terraform_module_package, project: project) }
Packages::Package.package_types.keys.each do |package_type|
context "for package type #{package_type}" do
diff --git a/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb b/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
index 1fb1b9f79b2..275ddebc18c 100644
--- a/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
@@ -47,7 +47,7 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
expect(original_repository_double).to receive(:remove)
end
- it "moves the project and its #{repository_type} repository to the new storage and unmarks the repository as read only" do
+ it "moves the project and its #{repository_type} repository to the new storage and unmarks the repository as read-only" do
old_project_repository_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
project.repository.path_to_repo
end
diff --git a/spec/support/shared_examples/services/schedule_bulk_repository_shard_moves_shared_examples.rb b/spec/support/shared_examples/services/schedule_bulk_repository_shard_moves_shared_examples.rb
index e67fc4ab04a..97304680316 100644
--- a/spec/support/shared_examples/services/schedule_bulk_repository_shard_moves_shared_examples.rb
+++ b/spec/support/shared_examples/services/schedule_bulk_repository_shard_moves_shared_examples.rb
@@ -27,7 +27,7 @@ RSpec.shared_examples 'moves repository shard in bulk' do
container.set_repository_read_only!
expect(subject).to receive(:log_info)
- .with(/Container #{container.full_path} \(#{container.id}\) was skipped: #{container.class} is read only/)
+ .with(/Container #{container.full_path} \(#{container.id}\) was skipped: #{container.class} is read-only/)
expect { subject.execute(source_storage_name, destination_storage_name) }
.to change(move_service_klass, :count).by(0)
end
diff --git a/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb b/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb
new file mode 100644
index 00000000000..538fd2bb513
--- /dev/null
+++ b/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.shared_examples_for 'services security ci configuration create service' do |skip_w_params|
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
+
+ describe '#execute' do
+ let(:params) { {} }
+
+ context 'user does not belong to project' do
+ it 'returns an error status' do
+ expect(result.status).to eq(:error)
+ expect(result.payload[:success_path]).to be_nil
+ end
+
+ it 'does not track a snowplow event' do
+ subject
+
+ expect_no_snowplow_event
+ end
+ end
+
+ context 'user belongs to project' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'does track the snowplow event' do
+ subject
+
+ expect_snowplow_event(**snowplow_event)
+ end
+
+ it 'raises exception if the user does not have permission to create a new branch' do
+ allow(project).to receive(:repository).and_raise(Gitlab::Git::PreReceiveError, "You are not allowed to create protected branches on this project.")
+
+ expect { subject }.to raise_error(Gitlab::Git::PreReceiveError)
+ end
+
+ context 'when exception is raised' do
+ let_it_be(:project) { create(:project, :repository) }
+
+ before do
+ allow(project.repository).to receive(:add_branch).and_raise(StandardError, "The unexpected happened!")
+ end
+
+ context 'when branch was created' do
+ before do
+ allow(project.repository).to receive(:branch_exists?).and_return(true)
+ end
+
+ it 'tries to rm branch' do
+ expect(project.repository).to receive(:rm_branch).with(user, branch_name)
+ expect { subject }.to raise_error(StandardError)
+ end
+ end
+
+ context 'when branch was not created' do
+ before do
+ allow(project.repository).to receive(:branch_exists?).and_return(false)
+ end
+
+ it 'does not try to rm branch' do
+ expect(project.repository).not_to receive(:rm_branch)
+ expect { subject }.to raise_error(StandardError)
+ end
+ end
+ end
+
+ context 'with no parameters' do
+ it 'returns the path to create a new merge request' do
+ expect(result.status).to eq(:success)
+ expect(result.payload[:success_path]).to match(/#{Gitlab::Routing.url_helpers.project_new_merge_request_url(project, {})}(.*)description(.*)source_branch/)
+ end
+ end
+
+ unless skip_w_params
+ context 'with parameters' do
+ let(:params) { non_empty_params }
+
+ it 'returns the path to create a new merge request' do
+ expect(result.status).to eq(:success)
+ expect(result.payload[:success_path]).to match(/#{Gitlab::Routing.url_helpers.project_new_merge_request_url(project, {})}(.*)description(.*)source_branch/)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/services/updating_mentions_shared_examples.rb b/spec/support/shared_examples/services/updating_mentions_shared_examples.rb
index 84f6c4d136a..13a2aa9ddac 100644
--- a/spec/support/shared_examples/services/updating_mentions_shared_examples.rb
+++ b/spec/support/shared_examples/services/updating_mentions_shared_examples.rb
@@ -15,7 +15,7 @@ RSpec.shared_examples 'updating mentions' do |service_class|
def update_mentionable(opts)
perform_enqueued_jobs do
- service_class.new(project, user, opts).execute(mentionable)
+ service_class.new(project: project, current_user: user, params: opts).execute(mentionable)
end
mentionable.reload