summaryrefslogtreecommitdiff
path: root/spec/support
diff options
context:
space:
mode:
Diffstat (limited to 'spec/support')
-rw-r--r--spec/support/atlassian/jira_connect/schemata.rb2
-rw-r--r--spec/support/banzai/filter_timeout_shared_examples.rb37
-rw-r--r--spec/support/before_all_adapter.rb51
-rw-r--r--spec/support/capybara.rb20
-rw-r--r--spec/support/counter_attribute.rb7
-rw-r--r--spec/support/cycle_analytics_helpers/test_generation.rb28
-rw-r--r--spec/support/database/query_recorder.rb12
-rw-r--r--spec/support/db_cleaner.rb4
-rw-r--r--spec/support/finder_collection_allowlist.yml4
-rw-r--r--spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb2
-rw-r--r--spec/support/gitlab_stubs/gitlab_ci.yml3
-rw-r--r--spec/support/helpers/batch_destroy_dependent_associations_helper.rb13
-rw-r--r--spec/support/helpers/ci/partitioning_helpers.rb11
-rw-r--r--spec/support/helpers/content_security_policy_helpers.rb10
-rw-r--r--spec/support/helpers/cookie_helper.rb6
-rw-r--r--spec/support/helpers/countries_controller_test_helper.rb9
-rw-r--r--spec/support/helpers/doc_url_helper.rb2
-rw-r--r--spec/support/helpers/features/branches_helpers.rb10
-rw-r--r--spec/support/helpers/features/invite_members_modal_helper.rb29
-rw-r--r--spec/support/helpers/features/runners_helpers.rb2
-rw-r--r--spec/support/helpers/gitaly_setup.rb2
-rw-r--r--spec/support/helpers/graphql_helpers.rb4
-rw-r--r--spec/support/helpers/javascript_fixtures_helpers.rb4
-rw-r--r--spec/support/helpers/listbox_input_helper.rb18
-rw-r--r--spec/support/helpers/migrations_helpers/work_item_types_helper.rb21
-rw-r--r--spec/support/helpers/project_template_test_helper.rb16
-rw-r--r--spec/support/helpers/repo_helpers.rb24
-rw-r--r--spec/support/helpers/search_helpers.rb2
-rw-r--r--spec/support/helpers/service_desk_helper.rb9
-rw-r--r--spec/support/helpers/smime_helper.rb2
-rw-r--r--spec/support/helpers/stub_configuration.rb11
-rw-r--r--spec/support/helpers/stub_object_storage.rb3
-rw-r--r--spec/support/helpers/stub_snowplow.rb2
-rw-r--r--spec/support/helpers/test_env.rb26
-rw-r--r--spec/support/helpers/usage_data_helpers.rb230
-rw-r--r--spec/support/helpers/workhorse_helpers.rb8
-rw-r--r--spec/support/import_export/common_util.rb4
-rw-r--r--spec/support/import_export/export_file_helper.rb4
-rw-r--r--spec/support/matchers/exceed_query_limit.rb18
-rw-r--r--spec/support/memory_instrumentation_helper.rb7
-rw-r--r--spec/support/migration.rb8
-rw-r--r--spec/support/migrations_helpers/vulnerabilities_findings_helper.rb8
-rw-r--r--spec/support/models/ci/partitioning_testing/cascade_check.rb7
-rw-r--r--spec/support/models/ci/partitioning_testing/schema_helpers.rb6
-rw-r--r--spec/support/patches/rspec_mocks_prepended_methods.rb2
-rw-r--r--spec/support/prometheus/additional_metrics_shared_examples.rb12
-rw-r--r--spec/support/redis/redis_shared_examples.rb55
-rw-r--r--spec/support/rspec.rb11
-rw-r--r--spec/support/rspec_order_todo.yml192
-rw-r--r--spec/support/shared_contexts/disable_user_tracking.rb10
-rw-r--r--spec/support/shared_contexts/email_shared_context.rb10
-rw-r--r--spec/support/shared_contexts/models/ci/job_token_scope.rb21
-rw-r--r--spec/support/shared_contexts/policies/group_policy_shared_context.rb38
-rw-r--r--spec/support/shared_contexts/rack_attack_shared_context.rb2
-rw-r--r--spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb4
-rw-r--r--spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb1
-rw-r--r--spec/support/shared_contexts/rubocop_default_rspec_language_config_context.rb32
-rw-r--r--spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb2
-rw-r--r--spec/support/shared_examples/boards/destroy_service_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/ci/log_downstream_pipeline_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/ci/retryable_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/ci/stuck_builds_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/controllers/destroy_hook_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb10
-rw-r--r--spec/support/shared_examples/controllers/variables_shared_examples.rb14
-rw-r--r--spec/support/shared_examples/csp.rb15
-rw-r--r--spec/support/shared_examples/features/container_registry_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/features/content_editor_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/discussion_comments_shared_example.rb4
-rw-r--r--spec/support/shared_examples/features/inviting_members_shared_examples.rb12
-rw-r--r--spec/support/shared_examples/features/reportable_note_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/features/runners_shared_examples.rb11
-rw-r--r--spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/features/sidebar/sidebar_milestone_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/features/wiki/file_attachments_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/finders/issues_finder_shared_examples.rb84
-rw-r--r--spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/graphql/label_fields.rb4
-rw-r--r--spec/support/shared_examples/graphql/mutations/incident_management_timeline_events_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/graphql/mutations/timelogs/create_shared_examples.rb32
-rw-r--r--spec/support/shared_examples/graphql/mutations/work_items/update_description_widget_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/graphql/notes_creation_shared_examples.rb56
-rw-r--r--spec/support/shared_examples/initializers/uses_gitlab_url_blocker_shared_examples.rb7
-rw-r--r--spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb8
-rw-r--r--spec/support/shared_examples/lib/gitlab/database/background_migration_job_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/lib/gitlab/middleware/multipart_shared_examples.rb42
-rw-r--r--spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb49
-rw-r--r--spec/support/shared_examples/mailers/notify_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb136
-rw-r--r--spec/support/shared_examples/models/concerns/integrations/base_slack_notification_shared_examples.rb34
-rw-r--r--spec/support/shared_examples/models/concerns/sanitizable_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/concerns/signature_type_shared_examples.rb21
-rw-r--r--spec/support/shared_examples/models/integrations/has_web_hook_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/models/label_note_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/member_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/update_highest_role_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/update_project_statistics_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/with_debian_distributions_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/observability/csp_shared_examples.rb123
-rw-r--r--spec/support/shared_examples/policies/project_policy_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb31
-rw-r--r--spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb36
-rw-r--r--spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb36
-rw-r--r--spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb14
-rw-r--r--spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb513
-rw-r--r--spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb17
-rw-r--r--spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb38
-rw-r--r--spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb18
-rw-r--r--spec/support/shared_examples/requests/api/notes_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb165
-rw-r--r--spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb20
-rw-r--r--spec/support/shared_examples/requests/api/packages_shared_examples.rb15
-rw-r--r--spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb20
-rw-r--r--spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/requests/rack_attack_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/security_training_providers_importer.rb2
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/incident_resolution_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/system_notes_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/alert_management_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/approval_state_updated_trigger_shared_examples.rb17
-rw-r--r--spec/support/shared_examples/services/boards/boards_create_service_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/boards/boards_recent_visit_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/boards/lists_destroy_service_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/boards/lists_list_service_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/services/container_expiration_policy_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/dependency_proxy_ttl_policies_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/incident_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/issuable/update_service_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/packages_shared_examples.rb14
-rw-r--r--spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/services/repositories/housekeeping_shared_examples.rb47
-rw-r--r--spec/support/shared_examples/services/schedule_bulk_repository_shard_moves_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/timelogs/create_service_shared_examples.rb47
-rw-r--r--spec/support/shared_examples/services/users/build_service_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/services/wiki_pages/create_service_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/wiki_pages/update_service_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/work_items/widgets/milestone_service_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/work_item_base_types_importer.rb6
-rw-r--r--spec/support/shared_examples/work_item_hierarchy_restrictions_importer.rb59
-rw-r--r--spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb203
-rw-r--r--spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb227
-rw-r--r--spec/support/shared_examples/workers/schedule_bulk_repository_shard_moves_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/workers/update_repository_move_shared_examples.rb4
156 files changed, 2433 insertions, 1131 deletions
diff --git a/spec/support/atlassian/jira_connect/schemata.rb b/spec/support/atlassian/jira_connect/schemata.rb
index 61e8aa8e15c..73a6833b7cc 100644
--- a/spec/support/atlassian/jira_connect/schemata.rb
+++ b/spec/support/atlassian/jira_connect/schemata.rb
@@ -11,7 +11,7 @@ module Atlassian
schemaVersion pipelineId buildNumber updateSequenceNumber
displayName url state issueKeys testInfo references
lastUpdated
- ),
+ ),
'properties' => {
'schemaVersion' => schema_version_type,
'pipelineId' => { 'type' => 'string' },
diff --git a/spec/support/banzai/filter_timeout_shared_examples.rb b/spec/support/banzai/filter_timeout_shared_examples.rb
new file mode 100644
index 00000000000..1f2ebe6fef6
--- /dev/null
+++ b/spec/support/banzai/filter_timeout_shared_examples.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+# This shared_example requires the following variables:
+# - text: The text to be run through the filter
+#
+# Usage:
+#
+# it_behaves_like 'filter timeout' do
+# let(:text) { 'some text' }
+# end
+RSpec.shared_examples 'filter timeout' do
+ context 'when rendering takes too long' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:context) { { project: project } }
+
+ it 'times out' do
+ stub_const("Banzai::Filter::TimeoutHtmlPipelineFilter::RENDER_TIMEOUT", 0.1)
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:call_with_timeout) do
+ sleep(0.2)
+ text
+ end
+ end
+
+ expect(Gitlab::RenderTimeout).to receive(:timeout).and_call_original
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
+ instance_of(Timeout::Error),
+ project_id: context[:project].id,
+ class_name: described_class.name.demodulize
+ )
+
+ result = filter(text)
+
+ expect(result.to_html).to eq text
+ end
+ end
+end
diff --git a/spec/support/before_all_adapter.rb b/spec/support/before_all_adapter.rb
index 890bdd6a2c4..f4946ff271f 100644
--- a/spec/support/before_all_adapter.rb
+++ b/spec/support/before_all_adapter.rb
@@ -1,27 +1,44 @@
# frozen_string_literal: true
-class BeforeAllAdapter # rubocop:disable Gitlab/NamespacedClass
- def self.all_connection_classes
- @all_connection_classes ||= [ActiveRecord::Base] + ActiveRecord::Base.descendants.select(&:connection_class?) # rubocop: disable Database/MultipleDatabases
- end
-
- def self.begin_transaction
- self.all_connection_classes.each do |connection_class|
- connection_class.connection.begin_transaction(joinable: false)
+module TestProfBeforeAllAdapter
+ module MultipleDatabaseAdapter
+ def self.all_connection_classes
+ @all_connection_classes ||= [ActiveRecord::Base] + ActiveRecord::Base.descendants.select(&:connection_class?) # rubocop: disable Database/MultipleDatabases
end
- end
- def self.rollback_transaction
- self.all_connection_classes.each do |connection_class|
- if connection_class.connection.open_transactions.zero?
- warn "!!! before_all transaction has been already rollbacked and " \
- "could work incorrectly"
- next
+ def self.begin_transaction
+ self.all_connection_classes.each do |connection_class|
+ connection_class.connection.begin_transaction(joinable: false)
end
+ end
- connection_class.connection.rollback_transaction
+ def self.rollback_transaction
+ self.all_connection_classes.each do |connection_class|
+ if connection_class.connection.open_transactions.zero?
+ warn "!!! before_all transaction has been already rollbacked and " \
+ "could work incorrectly"
+ next
+ end
+
+ connection_class.connection.rollback_transaction
+ end
end
end
+
+ # This class is required so we can disable transactions on migration specs
+ module NoTransactionAdapter
+ def self.begin_transaction; end
+
+ def self.rollback_transaction; end
+ end
+
+ def self.default_adapter
+ MultipleDatabaseAdapter
+ end
+
+ def self.no_transaction_adapter
+ NoTransactionAdapter
+ end
end
-TestProf::BeforeAll.adapter = ::BeforeAllAdapter
+TestProf::BeforeAll.adapter = ::TestProfBeforeAllAdapter.default_adapter
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
index 57065400220..aea853d1c23 100644
--- a/spec/support/capybara.rb
+++ b/spec/support/capybara.rb
@@ -16,15 +16,17 @@ Capybara.server_port = ENV['CAPYBARA_PORT'] if ENV['CAPYBARA_PORT']
JSConsoleError = Class.new(StandardError)
# Filter out innocuous JS console messages
-JS_CONSOLE_FILTER = Regexp.union([
- '"[HMR] Waiting for update signal from WDS..."',
- '"[WDS] Hot Module Replacement enabled."',
- '"[WDS] Live Reloading enabled."',
- 'Download the Vue Devtools extension',
- 'Download the Apollo DevTools',
- "Unrecognized feature: 'interest-cohort'",
- 'Does this page need fixes or improvements?'
-])
+JS_CONSOLE_FILTER = Regexp.union(
+ [
+ '"[HMR] Waiting for update signal from WDS..."',
+ '"[WDS] Hot Module Replacement enabled."',
+ '"[WDS] Live Reloading enabled."',
+ 'Download the Vue Devtools extension',
+ 'Download the Apollo DevTools',
+ "Unrecognized feature: 'interest-cohort'",
+ 'Does this page need fixes or improvements?'
+ ]
+)
CAPYBARA_WINDOW_SIZE = [1366, 768].freeze
diff --git a/spec/support/counter_attribute.rb b/spec/support/counter_attribute.rb
index 8bd40b72dcf..44df2df0ea5 100644
--- a/spec/support/counter_attribute.rb
+++ b/spec/support/counter_attribute.rb
@@ -7,12 +7,15 @@ RSpec.configure do |config|
CounterAttributeModel.class_eval do
include CounterAttribute
+ after_initialize { self.allow_package_size_counter = true }
+
counter_attribute :build_artifacts_size
counter_attribute :commit_count
+ counter_attribute :packages_size, if: ->(instance) { instance.allow_package_size_counter }
- attr_accessor :flushed
+ attr_accessor :flushed, :allow_package_size_counter
- counter_attribute_after_flush do |subject|
+ counter_attribute_after_commit do |subject|
subject.flushed = true
end
end
diff --git a/spec/support/cycle_analytics_helpers/test_generation.rb b/spec/support/cycle_analytics_helpers/test_generation.rb
index f866220b919..816caf5f775 100644
--- a/spec/support/cycle_analytics_helpers/test_generation.rb
+++ b/spec/support/cycle_analytics_helpers/test_generation.rb
@@ -42,17 +42,17 @@ module CycleAnalyticsHelpers
end_time = start_time + rand(1..5).days
start_time_conditions.each do |condition_name, condition_fn|
- Timecop.freeze(start_time) { condition_fn[self, data] }
+ travel_to(start_time) { condition_fn[self, data] }
end
# Run `before_end_fn` at the midpoint between `start_time` and `end_time`
- Timecop.freeze(start_time + (end_time - start_time) / 2) { before_end_fn[self, data] } if before_end_fn
+ travel_to(start_time + (end_time - start_time) / 2) { before_end_fn[self, data] } if before_end_fn
end_time_conditions.each do |condition_name, condition_fn|
- Timecop.freeze(end_time) { condition_fn[self, data] }
+ travel_to(end_time) { condition_fn[self, data] }
end
- Timecop.freeze(end_time + 1.day) { post_fn[self, data] } if post_fn
+ travel_to(end_time + 1.day) { post_fn[self, data] } if post_fn
end_time - start_time
end
@@ -74,14 +74,14 @@ module CycleAnalyticsHelpers
end_time = rand(1..10).days.from_now
start_time_conditions.each do |condition_name, condition_fn|
- Timecop.freeze(start_time) { condition_fn[self, data] }
+ travel_to(start_time) { condition_fn[self, data] }
end
end_time_conditions.each do |condition_name, condition_fn|
- Timecop.freeze(end_time) { condition_fn[self, data] }
+ travel_to(end_time) { condition_fn[self, data] }
end
- Timecop.freeze(end_time + 1.day) { post_fn[self, data] } if post_fn
+ travel_to(end_time + 1.day) { post_fn[self, data] } if post_fn
# Turn off the stub before checking assertions
allow(self).to receive(:project).and_call_original
@@ -97,17 +97,17 @@ module CycleAnalyticsHelpers
end_time = start_time + rand(1..5).days
# Run `before_end_fn` at the midpoint between `start_time` and `end_time`
- Timecop.freeze(start_time + (end_time - start_time) / 2) { before_end_fn[self, data] } if before_end_fn
+ travel_to(start_time + (end_time - start_time) / 2) { before_end_fn[self, data] } if before_end_fn
end_time_conditions.each do |condition_name, condition_fn|
- Timecop.freeze(start_time) { condition_fn[self, data] }
+ travel_to(start_time) { condition_fn[self, data] }
end
start_time_conditions.each do |condition_name, condition_fn|
- Timecop.freeze(end_time) { condition_fn[self, data] }
+ travel_to(end_time) { condition_fn[self, data] }
end
- Timecop.freeze(end_time + 1.day) { post_fn[self, data] } if post_fn
+ travel_to(end_time + 1.day) { post_fn[self, data] } if post_fn
expect(subject[phase].project_median).to be_nil
end
@@ -122,10 +122,10 @@ module CycleAnalyticsHelpers
end_time = rand(1..10).days.from_now
end_time_conditions.each_with_index do |(_condition_name, condition_fn), index|
- Timecop.freeze(end_time + index.days) { condition_fn[self, data] }
+ travel_to(end_time + index.days) { condition_fn[self, data] }
end
- Timecop.freeze(end_time + 1.day) { post_fn[self, data] } if post_fn
+ travel_to(end_time + 1.day) { post_fn[self, data] } if post_fn
expect(subject[phase].project_median).to be_nil
end
@@ -139,7 +139,7 @@ module CycleAnalyticsHelpers
start_time = Time.now
start_time_conditions.each do |condition_name, condition_fn|
- Timecop.freeze(start_time) { condition_fn[self, data] }
+ travel_to(start_time) { condition_fn[self, data] }
end
post_fn[self, data] if post_fn
diff --git a/spec/support/database/query_recorder.rb b/spec/support/database/query_recorder.rb
index 1050120e528..c0736221af3 100644
--- a/spec/support/database/query_recorder.rb
+++ b/spec/support/database/query_recorder.rb
@@ -3,7 +3,15 @@
RSpec.configure do |config|
# Truncate the query_recorder log file before starting the suite
config.before(:suite) do
- log_path = Rails.root.join(Gitlab::Database::QueryAnalyzers::QueryRecorder::LOG_FILE)
- File.write(log_path, '') if File.exist?(log_path)
+ log_file = Rails.root.join(Gitlab::Database::QueryAnalyzers::QueryRecorder.log_file)
+ File.write(log_file, '') if File.exist?(log_file)
+ File.delete("#{log_file}.gz") if File.exist?("#{log_file}.gz")
+ end
+
+ config.after(:suite) do
+ if ENV['CI']
+ log_file = Rails.root.join(Gitlab::Database::QueryAnalyzers::QueryRecorder.log_file)
+ system("gzip #{log_file}") if File.exist?(log_file)
+ end
end
end
diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb
index 24cdbe04fc2..588fe466a42 100644
--- a/spec/support/db_cleaner.rb
+++ b/spec/support/db_cleaner.rb
@@ -2,7 +2,7 @@
module DbCleaner
def all_connection_classes
- ::BeforeAllAdapter.all_connection_classes
+ ::TestProfBeforeAllAdapter::MultipleDatabaseAdapter.all_connection_classes
end
def delete_from_all_tables!(except: [])
@@ -12,7 +12,7 @@ module DbCleaner
end
def deletion_except_tables
- ['work_item_types']
+ %w[work_item_types work_item_hierarchy_restrictions]
end
def setup_database_cleaner
diff --git a/spec/support/finder_collection_allowlist.yml b/spec/support/finder_collection_allowlist.yml
index c8af07905c2..750295e16c4 100644
--- a/spec/support/finder_collection_allowlist.yml
+++ b/spec/support/finder_collection_allowlist.yml
@@ -1,8 +1,10 @@
# Allow list for spec/support/finder_collection.rb
-# Permenant excludes
+# Permanent excludes
# For example:
# FooFinder # Reason: It uses a memory backend
+- Namespaces::BilledUsersFinder # Reason: There is no need to have anything else besides the ids is current structure
+- Namespaces::FreeUserCap::UsersFinder # Reason: There is no need to have anything else besides the count
# Temporary excludes (aka TODOs)
# For example:
diff --git a/spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb b/spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb
index e9a13f7bf63..cef9860fe25 100644
--- a/spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb
+++ b/spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb
@@ -30,7 +30,7 @@ RSpec.shared_examples 'a correct instrumented metric query' do |params|
end
before do
- allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(false)
+ allow(metric.send(:relation).connection).to receive(:transaction_open?).and_return(false)
end
it 'has correct generate query' do
diff --git a/spec/support/gitlab_stubs/gitlab_ci.yml b/spec/support/gitlab_stubs/gitlab_ci.yml
index 94523591765..dbbfe044e35 100644
--- a/spec/support/gitlab_stubs/gitlab_ci.yml
+++ b/spec/support/gitlab_stubs/gitlab_ci.yml
@@ -12,7 +12,8 @@ variables:
description: 'value of KEY_VALUE_VAR'
DB_NAME: postgres
ENVIRONMENT_VAR:
- value: ['env var value', 'env var value2']
+ value: 'env var value'
+ options: ['env var value', 'env var value2']
description: 'env var description'
stages:
diff --git a/spec/support/helpers/batch_destroy_dependent_associations_helper.rb b/spec/support/helpers/batch_destroy_dependent_associations_helper.rb
new file mode 100644
index 00000000000..22170de053b
--- /dev/null
+++ b/spec/support/helpers/batch_destroy_dependent_associations_helper.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module BatchDestroyDependentAssociationsHelper
+ private
+
+ def delete_in_batches_regexps(table, column, resource, items, batch_size: 1000)
+ # rubocop:disable Layout/LineLength
+ select_query = %r{^SELECT "#{table}".* FROM "#{table}" WHERE.* "#{table}"."#{column}" = #{resource.id}.*ORDER BY "#{table}"."id" ASC LIMIT #{batch_size}}
+ # rubocop:enable Layout/LineLength
+
+ [select_query] + items.map { |item| %r{^DELETE FROM "#{table}" WHERE "#{table}"."id" = #{item.id}} }
+ end
+end
diff --git a/spec/support/helpers/ci/partitioning_helpers.rb b/spec/support/helpers/ci/partitioning_helpers.rb
new file mode 100644
index 00000000000..110199a3147
--- /dev/null
+++ b/spec/support/helpers/ci/partitioning_helpers.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Ci
+ module PartitioningHelpers
+ def stub_current_partition_id(id = Ci::PartitioningTesting::PartitionIdentifiers.ci_testing_partition_id)
+ allow(::Ci::Pipeline)
+ .to receive(:current_partition_value)
+ .and_return(id)
+ end
+ end
+end
diff --git a/spec/support/helpers/content_security_policy_helpers.rb b/spec/support/helpers/content_security_policy_helpers.rb
index 230075ead70..7e3de9fd219 100644
--- a/spec/support/helpers/content_security_policy_helpers.rb
+++ b/spec/support/helpers/content_security_policy_helpers.rb
@@ -4,11 +4,17 @@ module ContentSecurityPolicyHelpers
# Expecting 2 calls to current_content_security_policy by default:
# 1. call that's being tested
# 2. call in ApplicationController
- def setup_csp_for_controller(controller_class, csp = ActionDispatch::ContentSecurityPolicy.new, times: 2)
+ def setup_csp_for_controller(
+ controller_class, csp = ActionDispatch::ContentSecurityPolicy.new, times: 2,
+any_time: false)
expect_next_instance_of(controller_class) do |controller|
- expect(controller)
+ if any_time
+ expect(controller).to receive(:current_content_security_policy).at_least(:once).and_return(csp)
+ else
+ expect(controller)
.to receive(:current_content_security_policy).exactly(times).times
.and_return(csp)
+ end
end
end
end
diff --git a/spec/support/helpers/cookie_helper.rb b/spec/support/helpers/cookie_helper.rb
index ea4be12355b..8971c03a5cc 100644
--- a/spec/support/helpers/cookie_helper.rb
+++ b/spec/support/helpers/cookie_helper.rb
@@ -27,6 +27,12 @@ module CookieHelper
page.driver.browser.manage.cookie_named(name)
end
+ def wait_for_cookie_set(name)
+ wait_for("Complete setting cookie") do
+ get_cookie(name)
+ end
+ end
+
private
def on_a_page?
diff --git a/spec/support/helpers/countries_controller_test_helper.rb b/spec/support/helpers/countries_controller_test_helper.rb
deleted file mode 100644
index 5d36a29bba7..00000000000
--- a/spec/support/helpers/countries_controller_test_helper.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-module CountriesControllerTestHelper
- def world_deny_list
- ::World::DENYLIST + ::World::JH_MARKET
- end
-end
-
-CountriesControllerTestHelper.prepend_mod
diff --git a/spec/support/helpers/doc_url_helper.rb b/spec/support/helpers/doc_url_helper.rb
index bbff4827c56..f98c16a3cc4 100644
--- a/spec/support/helpers/doc_url_helper.rb
+++ b/spec/support/helpers/doc_url_helper.rb
@@ -13,7 +13,7 @@ module DocUrlHelper
"#{documentation_base_url}/ee/#{path}.html"
end
- def stub_doc_file_read(file_name: 'index.md', content: )
+ def stub_doc_file_read(content:, file_name: 'index.md')
expect_file_read(File.join(Rails.root, 'doc', file_name), content: content)
end
end
diff --git a/spec/support/helpers/features/branches_helpers.rb b/spec/support/helpers/features/branches_helpers.rb
index 2a50b41cb4e..d4f96718cc0 100644
--- a/spec/support/helpers/features/branches_helpers.rb
+++ b/spec/support/helpers/features/branches_helpers.rb
@@ -22,10 +22,14 @@ module Spec
end
def select_branch(branch_name)
- find(".js-branch-select").click
+ ref_selector = '.ref-selector'
+ find(ref_selector).click
+ wait_for_requests
- page.within("#new-branch-form .dropdown-menu") do
- click_link(branch_name)
+ page.within(ref_selector) do
+ fill_in _('Search by Git revision'), with: branch_name
+ wait_for_requests
+ find('li', text: branch_name, match: :prefer_exact).click
end
end
end
diff --git a/spec/support/helpers/features/invite_members_modal_helper.rb b/spec/support/helpers/features/invite_members_modal_helper.rb
index d02ec06d886..47cbd6b5208 100644
--- a/spec/support/helpers/features/invite_members_modal_helper.rb
+++ b/spec/support/helpers/features/invite_members_modal_helper.rb
@@ -5,7 +5,7 @@ module Spec
module Helpers
module Features
module InviteMembersModalHelper
- def invite_member(names, role: 'Guest', expires_at: nil, refresh: true)
+ def invite_member(names, role: 'Guest', expires_at: nil)
click_on 'Invite members'
page.within invite_modal_selector do
@@ -14,7 +14,23 @@ module Spec
submit_invites
end
- page.refresh if refresh
+ wait_for_requests
+ end
+
+ def invite_member_by_email(role)
+ click_on _('Invite members')
+
+ page.within invite_modal_selector do
+ choose_options(role, nil)
+ find(member_dropdown_selector).set('new_email@gitlab.com')
+ wait_for_requests
+
+ find('.dropdown-item', text: 'Invite "new_email@gitlab.com" by email').click
+
+ submit_invites
+
+ wait_for_requests
+ end
end
def input_invites(names)
@@ -43,8 +59,6 @@ module Spec
choose_options(role, expires_at)
submit_invites
-
- page.refresh
end
def submit_invites
@@ -52,12 +66,7 @@ module Spec
end
def choose_options(role, expires_at)
- unless role == 'Guest'
- click_button 'Guest'
- wait_for_requests
- click_button role
- end
-
+ select role, from: 'Select a role'
fill_in 'YYYY-MM-DD', with: expires_at.strftime('%Y-%m-%d') if expires_at
end
diff --git a/spec/support/helpers/features/runners_helpers.rb b/spec/support/helpers/features/runners_helpers.rb
index 63fc628358c..c5d26108953 100644
--- a/spec/support/helpers/features/runners_helpers.rb
+++ b/spec/support/helpers/features/runners_helpers.rb
@@ -50,7 +50,7 @@ module Spec
page.within(search_bar_selector) do
click_on filter
- # For OPERATOR_IS_ONLY, clicking the filter
+ # For OPERATORS_IS, clicking the filter
# immediately preselects "=" operator
page.find('input').send_keys(value)
diff --git a/spec/support/helpers/gitaly_setup.rb b/spec/support/helpers/gitaly_setup.rb
index 278dc79e1d0..20c104cd85c 100644
--- a/spec/support/helpers/gitaly_setup.rb
+++ b/spec/support/helpers/gitaly_setup.rb
@@ -205,7 +205,7 @@ module GitalySetup
# This code needs to work in an environment where we cannot use bundler,
# so we cannot easily use the toml-rb gem. This ad-hoc parser should be
# good enough.
- config_text = IO.read(toml)
+ config_text = File.read(toml)
config_text.lines.each do |line|
match_data = line.match(/^\s*(socket_path|listen_addr)\s*=\s*"([^"]*)"$/)
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index bd0efc96bd8..2176a477371 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -859,7 +859,7 @@ module GraphqlHelpers
# A lookahead that selects everything
def positive_lookahead
- double(selects?: true).tap do |selection|
+ double(selected?: true, selects?: true).tap do |selection|
allow(selection).to receive(:selection).and_return(selection)
allow(selection).to receive(:selections).and_return(selection)
allow(selection).to receive(:map).and_return(double(include?: true))
@@ -868,7 +868,7 @@ module GraphqlHelpers
# A lookahead that selects nothing
def negative_lookahead
- double(selects?: false).tap do |selection|
+ double(selected?: false, selects?: false, selections: []).tap do |selection|
allow(selection).to receive(:selection).and_return(selection)
end
end
diff --git a/spec/support/helpers/javascript_fixtures_helpers.rb b/spec/support/helpers/javascript_fixtures_helpers.rb
index 32e6e8d50bd..40eb46878ad 100644
--- a/spec/support/helpers/javascript_fixtures_helpers.rb
+++ b/spec/support/helpers/javascript_fixtures_helpers.rb
@@ -3,12 +3,14 @@
require 'action_dispatch/testing/test_request'
require 'fileutils'
require 'graphlyte'
+require 'active_support/testing/time_helpers'
require_relative '../../../lib/gitlab/popen'
module JavaScriptFixturesHelpers
extend ActiveSupport::Concern
include Gitlab::Popen
+ include ActiveSupport::Testing::TimeHelpers
extend self
@@ -22,7 +24,7 @@ module JavaScriptFixturesHelpers
# pick an arbitrary date from the past, so tests are not time dependent
# Also see spec/frontend/__helpers__/fake_date/jest.js
- Timecop.freeze(Time.utc(2015, 7, 3, 10)) { example.run }
+ travel_to(Time.utc(2015, 7, 3, 10)) { example.run }
raise NoMethodError.new('You need to set `response` for the fixture generator! This will automatically happen with `type: :controller` or `type: :request`.', 'response') unless respond_to?(:response)
diff --git a/spec/support/helpers/listbox_input_helper.rb b/spec/support/helpers/listbox_input_helper.rb
new file mode 100644
index 00000000000..ca7fbac5daa
--- /dev/null
+++ b/spec/support/helpers/listbox_input_helper.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module ListboxInputHelper
+ include WaitForRequests
+
+ def listbox_input(value, from:)
+ open_listbox_input(from) do
+ find('[role="option"]', text: value).click
+ end
+ end
+
+ def open_listbox_input(selector)
+ page.within(selector) do
+ page.find('button[aria-haspopup="listbox"]').click
+ yield
+ end
+ end
+end
diff --git a/spec/support/helpers/migrations_helpers/work_item_types_helper.rb b/spec/support/helpers/migrations_helpers/work_item_types_helper.rb
index b05caf265ee..40f84486537 100644
--- a/spec/support/helpers/migrations_helpers/work_item_types_helper.rb
+++ b/spec/support/helpers/migrations_helpers/work_item_types_helper.rb
@@ -2,26 +2,9 @@
module MigrationHelpers
module WorkItemTypesHelper
- DEFAULT_WORK_ITEM_TYPES = {
- issue: { name: 'Issue', icon_name: 'issue-type-issue', enum_value: 0 },
- incident: { name: 'Incident', icon_name: 'issue-type-incident', enum_value: 1 },
- test_case: { name: 'Test Case', icon_name: 'issue-type-test-case', enum_value: 2 },
- requirement: { name: 'Requirement', icon_name: 'issue-type-requirements', enum_value: 3 },
- task: { name: 'Task', icon_name: 'issue-type-task', enum_value: 4 }
- }.freeze
-
def reset_work_item_types
- work_item_types_table.delete_all
-
- DEFAULT_WORK_ITEM_TYPES.each do |type, attributes|
- work_item_types_table.create!(base_type: attributes[:enum_value], **attributes.slice(:name, :icon_name))
- end
- end
-
- private
-
- def work_item_types_table
- table(:work_item_types)
+ Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter.upsert_types
+ Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
end
end
end
diff --git a/spec/support/helpers/project_template_test_helper.rb b/spec/support/helpers/project_template_test_helper.rb
index eab41f6a1cf..bedbb8601e8 100644
--- a/spec/support/helpers/project_template_test_helper.rb
+++ b/spec/support/helpers/project_template_test_helper.rb
@@ -3,14 +3,14 @@
module ProjectTemplateTestHelper
def all_templates
%w[
- rails spring express iosswift dotnetcore android
- gomicro gatsby hugo jekyll plainhtml gitbook
- hexo middleman gitpod_spring_petclinic nfhugo
- nfjekyll nfplainhtml nfgitbook nfhexo salesforcedx
- serverless_framework tencent_serverless_framework
- jsonnet cluster_management kotlin_native_linux
- pelican
- ]
+ rails spring express iosswift dotnetcore android
+ gomicro gatsby hugo jekyll plainhtml gitbook
+ hexo middleman gitpod_spring_petclinic nfhugo
+ nfjekyll nfplainhtml nfgitbook nfhexo salesforcedx
+ serverless_framework tencent_serverless_framework
+ jsonnet cluster_management kotlin_native_linux
+ pelican bridgetown typo3_distribution
+ ]
end
end
diff --git a/spec/support/helpers/repo_helpers.rb b/spec/support/helpers/repo_helpers.rb
index e76a1dd5a74..9f37cf61cc9 100644
--- a/spec/support/helpers/repo_helpers.rb
+++ b/spec/support/helpers/repo_helpers.rb
@@ -137,4 +137,28 @@ eos
file_content: content
).execute
end
+
+ def create_and_delete_files(project, files, &block)
+ files.each do |filename, content|
+ project.repository.create_file(
+ project.creator,
+ filename,
+ content,
+ message: "Automatically created file #{filename}",
+ branch_name: project.default_branch_or_main
+ )
+ end
+
+ yield
+
+ ensure
+ files.each do |filename, _content|
+ project.repository.delete_file(
+ project.creator,
+ filename,
+ message: "Automatically deleted file #{filename}",
+ branch_name: project.default_branch_or_main
+ )
+ end
+ end
end
diff --git a/spec/support/helpers/search_helpers.rb b/spec/support/helpers/search_helpers.rb
index 7d0f8c09933..eab30be9243 100644
--- a/spec/support/helpers/search_helpers.rb
+++ b/spec/support/helpers/search_helpers.rb
@@ -35,6 +35,8 @@ module SearchHelpers
def select_search_scope(scope)
page.within '[data-testid="search-filter"]' do
click_link scope
+
+ wait_for_all_requests
end
end
diff --git a/spec/support/helpers/service_desk_helper.rb b/spec/support/helpers/service_desk_helper.rb
new file mode 100644
index 00000000000..d67ee5b8a11
--- /dev/null
+++ b/spec/support/helpers/service_desk_helper.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module ServiceDeskHelper
+ def set_template_file(file_name, content)
+ file_path = ".gitlab/issue_templates/#{file_name}.md"
+ project.repository.create_file(user, file_path, content, message: 'message', branch_name: 'master')
+ settings.update!(issue_template_key: file_name)
+ end
+end
diff --git a/spec/support/helpers/smime_helper.rb b/spec/support/helpers/smime_helper.rb
index fa16c433c6b..1a414c72fbb 100644
--- a/spec/support/helpers/smime_helper.rb
+++ b/spec/support/helpers/smime_helper.rb
@@ -17,7 +17,7 @@ module SmimeHelper
end
# returns a hash { key:, cert: } containing a generated key, cert pair
- def issue(email_address: 'test@example.com', cn: nil, signed_by:, expires_in:, certificate_authority:)
+ def issue(signed_by:, expires_in:, certificate_authority:, email_address: 'test@example.com', cn: nil)
key = OpenSSL::PKey::RSA.new(4096)
public_key = key.public_key
diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb
index 24c768258a1..4ca8f26be9e 100644
--- a/spec/support/helpers/stub_configuration.rb
+++ b/spec/support/helpers/stub_configuration.rb
@@ -20,6 +20,17 @@ module StubConfiguration
allow_any_instance_of(ApplicationSetting).to receive(:cached_html_up_to_date?).and_return(false)
end
+ # For enums with `_prefix: true`, this allows us to stub the application setting properly
+ def stub_application_setting_enum(setting, value)
+ stub_application_setting(setting.to_sym => value)
+
+ ApplicationSetting.send(setting.pluralize.to_sym).each_key do |key|
+ stub_application_setting("#{setting}_#{key}".to_sym => key == value)
+ end
+
+ Gitlab::CurrentSettings.send(setting)
+ end
+
def stub_not_protect_default_branch
stub_application_setting(
default_branch_protection: Gitlab::Access::PROTECTION_NONE)
diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb
index 87e2a71b1cd..c163ce1d880 100644
--- a/spec/support/helpers/stub_object_storage.rb
+++ b/spec/support/helpers/stub_object_storage.rb
@@ -12,7 +12,6 @@ module StubObjectStorage
uploader:,
enabled: true,
proxy_download: false,
- background_upload: false,
direct_upload: false,
cdn: {}
)
@@ -20,7 +19,6 @@ module StubObjectStorage
new_config = config.to_h.deep_symbolize_keys.merge({
enabled: enabled,
proxy_download: proxy_download,
- background_upload: background_upload,
direct_upload: direct_upload,
cdn: cdn
})
@@ -30,7 +28,6 @@ module StubObjectStorage
allow(config).to receive(:to_h).and_return(new_config)
allow(config).to receive(:enabled) { enabled }
allow(config).to receive(:proxy_download) { proxy_download }
- allow(config).to receive(:background_upload) { background_upload }
allow(config).to receive(:direct_upload) { direct_upload }
uploader_config = Settingslogic.new(new_config.deep_stringify_keys)
diff --git a/spec/support/helpers/stub_snowplow.rb b/spec/support/helpers/stub_snowplow.rb
index 85c605efea3..80342863f7b 100644
--- a/spec/support/helpers/stub_snowplow.rb
+++ b/spec/support/helpers/stub_snowplow.rb
@@ -10,7 +10,7 @@ module StubSnowplow
# rubocop:disable RSpec/AnyInstanceOf
allow_any_instance_of(Gitlab::Tracking::Destinations::Snowplow)
.to receive(:emitter)
- .and_return(SnowplowTracker::Emitter.new(host, buffer_size: buffer_size))
+ .and_return(SnowplowTracker::Emitter.new(endpoint: host, options: { buffer_size: buffer_size }))
# rubocop:enable RSpec/AnyInstanceOf
stub_application_setting(snowplow_enabled: true, snowplow_collector_hostname: host)
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index e1b461cf37e..3530d1b1a39 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -91,7 +91,8 @@ module TestEnv
'utf-16' => 'f05a987',
'gitaly-rename-test' => '94bb47c',
'smime-signed-commits' => 'ed775cc',
- 'Ääh-test-utf-8' => '7975be0'
+ 'Ääh-test-utf-8' => '7975be0',
+ 'ssh-signed-commit' => '7b5160f'
}.freeze
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
@@ -312,12 +313,6 @@ module TestEnv
end
end
- def storage_dir_exists?(storage, dir)
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- File.exist?(File.join(GitalySetup.repos_path(storage), dir))
- end
- end
-
def repos_path
@repos_path ||= GitalySetup.repos_path
end
@@ -375,6 +370,7 @@ module TestEnv
def seed_db
Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter.upsert_types
+ Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
end
private
@@ -427,6 +423,8 @@ module TestEnv
return if File.exist?(install_dir) && ci?
if component_needs_update?(install_dir, version)
+ puts "==> Starting #{component} set up...\n"
+
# Cleanup the component entirely to ensure we start fresh
FileUtils.rm_rf(install_dir) if fresh_install
@@ -486,12 +484,14 @@ module TestEnv
# The HEAD of the component_folder will be used as heuristic for the version
# of the binaries, allowing to use Git to determine if HEAD is later than
# the expected version. Note: Git considers HEAD to be an anchestor of HEAD.
- _out, exit_status = Gitlab::Popen.popen(%W[
- #{Gitlab.config.git.bin_path}
- -C #{component_folder}
- merge-base --is-ancestor
- #{expected_version} HEAD
-])
+ _out, exit_status = Gitlab::Popen.popen(
+ %W[
+ #{Gitlab.config.git.bin_path}
+ -C #{component_folder}
+ merge-base --is-ancestor
+ #{expected_version} HEAD
+ ]
+ )
exit_status == 0
end
diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb
index 92a946db337..78ceaf297a8 100644
--- a/spec/support/helpers/usage_data_helpers.rb
+++ b/spec/support/helpers/usage_data_helpers.rb
@@ -2,124 +2,118 @@
module UsageDataHelpers
COUNTS_KEYS = %i(
- assignee_lists
- ci_builds
- ci_internal_pipelines
- ci_external_pipelines
- ci_pipeline_config_auto_devops
- ci_pipeline_config_repository
- ci_runners
- ci_triggers
- ci_pipeline_schedules
- auto_devops_enabled
- auto_devops_disabled
- deploy_keys
- deployments
- successful_deployments
- failed_deployments
- environments
- clusters
- clusters_enabled
- project_clusters_enabled
- group_clusters_enabled
- instance_clusters_enabled
- clusters_disabled
- project_clusters_disabled
- group_clusters_disabled
- instance_clusters_disabled
- clusters_platforms_eks
- clusters_platforms_gke
- clusters_platforms_user
- clusters_integrations_prometheus
- clusters_management_project
- in_review_folder
- grafana_integrated_projects
- groups
- issues
- issues_created_from_gitlab_error_tracking_ui
- issues_with_associated_zoom_link
- issues_using_zoom_quick_actions
- issues_with_embedded_grafana_charts_approx
- incident_issues
- keys
- label_lists
- labels
- lfs_objects
- merge_requests
- milestone_lists
- milestones
- notes
- pool_repositories
- projects
- projects_imported_from_github
- projects_asana_active
- projects_jenkins_active
- projects_jira_active
- projects_jira_server_active
- projects_jira_cloud_active
- projects_jira_dvcs_cloud_active
- projects_jira_dvcs_server_active
- projects_slack_active
- projects_slack_slash_commands_active
- projects_custom_issue_tracker_active
- projects_mattermost_active
- projects_prometheus_active
- projects_with_repositories_enabled
- projects_with_error_tracking_enabled
- projects_with_enabled_alert_integrations
- projects_with_expiration_policy_enabled_with_older_than_unset
- projects_with_expiration_policy_enabled_with_older_than_set_to_7d
- projects_with_expiration_policy_enabled_with_older_than_set_to_14d
- projects_with_expiration_policy_enabled_with_older_than_set_to_30d
- projects_with_expiration_policy_enabled_with_older_than_set_to_60d
- projects_with_expiration_policy_enabled_with_older_than_set_to_90d
- projects_with_terraform_reports
- projects_with_terraform_states
- pages_domains
- protected_branches
- protected_branches_except_default
- releases
- remote_mirrors
- snippets
- personal_snippets
- project_snippets
- suggestions
- terraform_reports
- terraform_states
- todos
- uploads
- web_hooks
- user_preferences_user_gitpod_enabled
- ).freeze
+ assignee_lists
+ ci_builds
+ ci_internal_pipelines
+ ci_external_pipelines
+ ci_pipeline_config_auto_devops
+ ci_pipeline_config_repository
+ ci_runners
+ ci_triggers
+ ci_pipeline_schedules
+ auto_devops_enabled
+ auto_devops_disabled
+ deploy_keys
+ deployments
+ successful_deployments
+ failed_deployments
+ environments
+ clusters
+ clusters_enabled
+ project_clusters_enabled
+ group_clusters_enabled
+ instance_clusters_enabled
+ clusters_disabled
+ project_clusters_disabled
+ group_clusters_disabled
+ instance_clusters_disabled
+ clusters_platforms_eks
+ clusters_platforms_gke
+ clusters_platforms_user
+ clusters_integrations_prometheus
+ clusters_management_project
+ in_review_folder
+ grafana_integrated_projects
+ groups
+ issues
+ issues_created_from_gitlab_error_tracking_ui
+ issues_with_associated_zoom_link
+ issues_using_zoom_quick_actions
+ issues_with_embedded_grafana_charts_approx
+ incident_issues
+ keys
+ label_lists
+ labels
+ lfs_objects
+ merge_requests
+ milestone_lists
+ milestones
+ notes
+ pool_repositories
+ projects
+ projects_imported_from_github
+ projects_asana_active
+ projects_jenkins_active
+ projects_jira_active
+ projects_jira_server_active
+ projects_jira_cloud_active
+ projects_jira_dvcs_cloud_active
+ projects_jira_dvcs_server_active
+ projects_slack_active
+ projects_slack_slash_commands_active
+ projects_custom_issue_tracker_active
+ projects_mattermost_active
+ projects_prometheus_active
+ projects_with_repositories_enabled
+ projects_with_error_tracking_enabled
+ projects_with_enabled_alert_integrations
+ projects_with_terraform_reports
+ projects_with_terraform_states
+ pages_domains
+ protected_branches
+ protected_branches_except_default
+ releases
+ remote_mirrors
+ snippets
+ personal_snippets
+ project_snippets
+ suggestions
+ terraform_reports
+ terraform_states
+ todos
+ uploads
+ web_hooks
+ user_preferences_user_gitpod_enabled
+ ).freeze
USAGE_DATA_KEYS = %i(
- active_user_count
- counts
- counts_monthly
- recorded_at
- edition
- version
- installation_type
- uuid
- hostname
- mattermost_enabled
- signup_enabled
- ldap_enabled
- gravatar_enabled
- omniauth_enabled
- reply_by_email_enabled
- container_registry_enabled
- dependency_proxy_enabled
- gitlab_shared_runners_enabled
- gitlab_pages
- git
- gitaly
- database
- prometheus_metrics_enabled
- web_ide_clientside_preview_enabled
- object_store
- topology
- ).freeze
+ active_user_count
+ counts
+ counts_monthly
+ recorded_at
+ edition
+ version
+ installation_type
+ uuid
+ hostname
+ mattermost_enabled
+ signup_enabled
+ ldap_enabled
+ gravatar_enabled
+ omniauth_enabled
+ reply_by_email_enabled
+ container_registry_enabled
+ dependency_proxy_enabled
+ gitlab_shared_runners_enabled
+ gitlab_pages
+ git
+ gitaly
+ database
+ prometheus_metrics_enabled
+ web_ide_clientside_preview_enabled
+ object_store
+ topology
+ ).freeze
def stub_usage_data_connections
allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(false)
@@ -162,7 +156,6 @@ module UsageDataHelpers
'direct_upload' => true,
'connection' =>
{ 'provider' => 'AWS', 'aws_access_key_id' => 'minio', 'aws_secret_access_key' => 'gdk-minio', 'region' => 'gdk', 'endpoint' => 'http://127.0.0.1:9000', 'path_style' => true },
- 'background_upload' => false,
'proxy_download' => false } }
)
@@ -177,7 +170,6 @@ module UsageDataHelpers
'direct_upload' => true,
'connection' =>
{ 'provider' => 'AWS', 'aws_access_key_id' => 'minio', 'aws_secret_access_key' => 'gdk-minio', 'region' => 'gdk', 'endpoint' => 'http://127.0.0.1:9000', 'path_style' => true },
- 'background_upload' => false,
'proxy_download' => false } }
)
allow(Settings).to receive(:[]).with('uploads')
@@ -188,7 +180,6 @@ module UsageDataHelpers
'direct_upload' => true,
'connection' =>
{ 'provider' => 'AWS', 'aws_access_key_id' => 'minio', 'aws_secret_access_key' => 'gdk-minio', 'region' => 'gdk', 'endpoint' => 'http://127.0.0.1:9000', 'path_style' => true },
- 'background_upload' => false,
'proxy_download' => false } }
)
allow(Settings).to receive(:[]).with('packages')
@@ -200,7 +191,6 @@ module UsageDataHelpers
'direct_upload' => false,
'connection' =>
{ 'provider' => 'AWS', 'aws_access_key_id' => 'minio', 'aws_secret_access_key' => 'gdk-minio', 'region' => 'gdk', 'endpoint' => 'http://127.0.0.1:9000', 'path_style' => true },
- 'background_upload' => true,
'proxy_download' => false } }
)
end
diff --git a/spec/support/helpers/workhorse_helpers.rb b/spec/support/helpers/workhorse_helpers.rb
index 6f22df9ae0f..f894aff373c 100644
--- a/spec/support/helpers/workhorse_helpers.rb
+++ b/spec/support/helpers/workhorse_helpers.rb
@@ -39,11 +39,11 @@ module WorkhorseHelpers
# workhorse_finalize will transform file_key inside params as if it was the finalize call of an inline object storage upload.
# note that based on the content of the params it can simulate a disc acceleration or an object storage upload
- def workhorse_finalize(url, method: :post, file_key:, params:, headers: {}, send_rewritten_field: false)
+ def workhorse_finalize(url, file_key:, params:, method: :post, headers: {}, send_rewritten_field: false)
workhorse_finalize_with_multiple_files(url, method: method, file_keys: file_key, params: params, headers: headers, send_rewritten_field: send_rewritten_field)
end
- def workhorse_finalize_with_multiple_files(url, method: :post, file_keys:, params:, headers: {}, send_rewritten_field: false)
+ def workhorse_finalize_with_multiple_files(url, file_keys:, params:, method: :post, headers: {}, send_rewritten_field: false)
workhorse_request_with_multiple_files(method, url,
file_keys: file_keys,
params: params,
@@ -52,11 +52,11 @@ module WorkhorseHelpers
)
end
- def workhorse_request_with_file(method, url, file_key:, params:, env: {}, extra_headers: {}, send_rewritten_field:)
+ def workhorse_request_with_file(method, url, file_key:, params:, send_rewritten_field:, env: {}, extra_headers: {})
workhorse_request_with_multiple_files(method, url, file_keys: file_key, params: params, env: env, extra_headers: extra_headers, send_rewritten_field: send_rewritten_field)
end
- def workhorse_request_with_multiple_files(method, url, file_keys:, params:, env: {}, extra_headers: {}, send_rewritten_field:)
+ def workhorse_request_with_multiple_files(method, url, file_keys:, params:, send_rewritten_field:, env: {}, extra_headers: {})
workhorse_params = params.dup
file_keys = Array(file_keys)
diff --git a/spec/support/import_export/common_util.rb b/spec/support/import_export/common_util.rb
index 9da151895a7..3d7a0d29e71 100644
--- a/spec/support/import_export/common_util.rb
+++ b/spec/support/import_export/common_util.rb
@@ -83,7 +83,7 @@ module ImportExport
path = File.join(dir_path, "#{exportable_path}.json")
return unless File.exist?(path)
- Gitlab::Json.parse(IO.read(path))
+ Gitlab::Json.parse(File.read(path))
end
def consume_relations(dir_path, exportable_path, key)
@@ -101,7 +101,7 @@ module ImportExport
end
def project_json(filename)
- Gitlab::Json.parse(IO.read(filename))
+ Gitlab::Json.parse(File.read(filename))
end
end
end
diff --git a/spec/support/import_export/export_file_helper.rb b/spec/support/import_export/export_file_helper.rb
index 3134e5c32a3..9a26f50903f 100644
--- a/spec/support/import_export/export_file_helper.rb
+++ b/spec/support/import_export/export_file_helper.rb
@@ -117,8 +117,8 @@ module ExportFileHelper
# Check whether this is a hash attribute inside a model
if model.is_a?(Symbol)
return true if (safe_hashes[model] - parent.keys).empty?
- else
- return true if safe_model?(model, excluded_attributes, parent)
+ elsif safe_model?(model, excluded_attributes, parent)
+ return true
end
end
diff --git a/spec/support/matchers/exceed_query_limit.rb b/spec/support/matchers/exceed_query_limit.rb
index 6d7658b7c33..a5a017828b3 100644
--- a/spec/support/matchers/exceed_query_limit.rb
+++ b/spec/support/matchers/exceed_query_limit.rb
@@ -63,14 +63,16 @@ module ExceedQueryLimitHelpers
end
end
- MARGINALIA_ANNOTATION_REGEX = %r{\s*\/\*.*\*\/}.freeze
-
- DB_QUERY_RE = Regexp.union([
- /^(?<prefix>SELECT .* FROM "?[a-z_]+"?) (?<suffix>.*)$/m,
- /^(?<prefix>UPDATE "?[a-z_]+"?) (?<suffix>.*)$/m,
- /^(?<prefix>INSERT INTO "[a-z_]+" \((?:"[a-z_]+",?\s?)+\)) (?<suffix>.*)$/m,
- /^(?<prefix>DELETE FROM "[a-z_]+") (?<suffix>.*)$/m
- ]).freeze
+ MARGINALIA_ANNOTATION_REGEX = %r{\s*/\*.*\*/}.freeze
+
+ DB_QUERY_RE = Regexp.union(
+ [
+ /^(?<prefix>SELECT .* FROM "?[a-z_]+"?) (?<suffix>.*)$/m,
+ /^(?<prefix>UPDATE "?[a-z_]+"?) (?<suffix>.*)$/m,
+ /^(?<prefix>INSERT INTO "[a-z_]+" \((?:"[a-z_]+",?\s?)+\)) (?<suffix>.*)$/m,
+ /^(?<prefix>DELETE FROM "[a-z_]+") (?<suffix>.*)$/m
+ ]
+ ).freeze
def with_threshold(threshold)
@threshold = threshold
diff --git a/spec/support/memory_instrumentation_helper.rb b/spec/support/memory_instrumentation_helper.rb
index 84ec02fa5aa..51506376a75 100644
--- a/spec/support/memory_instrumentation_helper.rb
+++ b/spec/support/memory_instrumentation_helper.rb
@@ -5,13 +5,10 @@
# This concept is currently tried to be upstreamed here:
# - https://github.com/ruby/ruby/pull/3978
module MemoryInstrumentationHelper
- def skip_memory_instrumentation!
+ def verify_memory_instrumentation_available!
return if ::Gitlab::Memory::Instrumentation.available?
- # if we are running in CI, a test cannot be skipped
- return if ENV['CI']
-
- skip 'Missing a memory instrumentation patch. ' \
+ raise 'Ruby is missing a required patch that enables memory instrumentation. ' \
'More information can be found here: https://gitlab.com/gitlab-org/gitlab/-/issues/296530.'
end
end
diff --git a/spec/support/migration.rb b/spec/support/migration.rb
index 4d4a293e9ff..b1e75d9c9e2 100644
--- a/spec/support/migration.rb
+++ b/spec/support/migration.rb
@@ -20,6 +20,14 @@ RSpec.configure do |config|
Gitlab::CurrentSettings.clear_in_memory_application_settings!
end
+ config.prepend_before(:all, :migration) do
+ TestProf::BeforeAll.adapter = ::TestProfBeforeAllAdapter.no_transaction_adapter
+ end
+
+ config.append_after(:all, :migration) do
+ TestProf::BeforeAll.adapter = ::TestProfBeforeAllAdapter.default_adapter
+ end
+
config.append_after(:context, :migration) do
recreate_databases_and_seed_if_needed || ensure_schema_and_empty_tables
end
diff --git a/spec/support/migrations_helpers/vulnerabilities_findings_helper.rb b/spec/support/migrations_helpers/vulnerabilities_findings_helper.rb
index a3cccc3a75d..9a5313c3fa4 100644
--- a/spec/support/migrations_helpers/vulnerabilities_findings_helper.rb
+++ b/spec/support/migrations_helpers/vulnerabilities_findings_helper.rb
@@ -92,10 +92,10 @@ module MigrationHelpers
"url" => "http://goat:8080/WebGoat/logout",
"body" => "",
"headers" => [
- {
- "name" => "Accept",
- "value" => "*/*"
- }
+ {
+ "name" => "Accept",
+ "value" => "*/*"
+ }
]
},
"response" => {
diff --git a/spec/support/models/ci/partitioning_testing/cascade_check.rb b/spec/support/models/ci/partitioning_testing/cascade_check.rb
index f553a47ef4f..bcfc9675476 100644
--- a/spec/support/models/ci/partitioning_testing/cascade_check.rb
+++ b/spec/support/models/ci/partitioning_testing/cascade_check.rb
@@ -15,6 +15,13 @@ module PartitioningTesting
raise "partition_id was expected to equal #{partition_scope_value} but it was #{partition_id}."
end
+
+ class_methods do
+ # Allowing partition callback to be used with BulkInsertSafe
+ def _bulk_insert_callback_allowed?(name, args)
+ super || args.first == :after && args.second == :check_partition_cascade_value
+ end
+ end
end
end
diff --git a/spec/support/models/ci/partitioning_testing/schema_helpers.rb b/spec/support/models/ci/partitioning_testing/schema_helpers.rb
index 712178710da..3a79ed1b5a9 100644
--- a/spec/support/models/ci/partitioning_testing/schema_helpers.rb
+++ b/spec/support/models/ci/partitioning_testing/schema_helpers.rb
@@ -8,10 +8,10 @@ module Ci
module_function
def with_routing_tables
- Ci::BuildMetadata.table_name = :p_ci_builds_metadata
+ # model.table_name = :routing_table
yield
- ensure
- Ci::BuildMetadata.table_name = :ci_builds_metadata
+ # ensure
+ # model.table_name = :regular_table
end
# We're dropping the default values here to ensure that the application code
diff --git a/spec/support/patches/rspec_mocks_prepended_methods.rb b/spec/support/patches/rspec_mocks_prepended_methods.rb
index fa3a74c670c..d51fb37a499 100644
--- a/spec/support/patches/rspec_mocks_prepended_methods.rb
+++ b/spec/support/patches/rspec_mocks_prepended_methods.rb
@@ -45,7 +45,7 @@ module RSpec
private
def method_owner
- @method_owner ||= Object.instance_method(:method).bind(object).call(@method_name).owner
+ @method_owner ||= Object.instance_method(:method).bind_call(object, @method_name).owner
end
end
end
diff --git a/spec/support/prometheus/additional_metrics_shared_examples.rb b/spec/support/prometheus/additional_metrics_shared_examples.rb
index 3a5909cd908..e589baf0909 100644
--- a/spec/support/prometheus/additional_metrics_shared_examples.rb
+++ b/spec/support/prometheus/additional_metrics_shared_examples.rb
@@ -92,15 +92,15 @@ RSpec.shared_examples 'additional metrics query' do
metrics: [
{
title: 'title', weight: 1, y_label: 'Values', queries: [
- { query_range: 'query_range_a', result: query_range_result },
- { query_range: 'query_range_b', label: 'label', unit: 'unit', result: query_range_result }
- ]
+ { query_range: 'query_range_a', result: query_range_result },
+ { query_range: 'query_range_b', label: 'label', unit: 'unit', result: query_range_result }
+ ]
}
]
}
]
- expect(query_result).to match_schema('prometheus/additional_metrics_query_result')
+ expect(query_result.to_json).to match_schema('prometheus/additional_metrics_query_result')
expect(query_result).to eq(expected)
end
end
@@ -128,7 +128,7 @@ RSpec.shared_examples 'additional metrics query' do
queries_with_result_a = { queries: [{ query_range: 'query_range_a', result: query_range_result }] }
queries_with_result_b = { queries: [{ query_range: 'query_range_b', result: query_range_result }] }
- expect(query_result).to match_schema('prometheus/additional_metrics_query_result')
+ expect(query_result.to_json).to match_schema('prometheus/additional_metrics_query_result')
expect(query_result.count).to eq(2)
expect(query_result).to all(satisfy { |r| r[:metrics].count == 1 })
@@ -147,7 +147,7 @@ RSpec.shared_examples 'additional metrics query' do
it 'return group data only for query with results' do
queries_with_result = { queries: [{ query_range: 'query_range_a', result: query_range_result }] }
- expect(query_result).to match_schema('prometheus/additional_metrics_query_result')
+ expect(query_result.to_json).to match_schema('prometheus/additional_metrics_query_result')
expect(query_result.count).to eq(1)
expect(query_result).to all(satisfy { |r| r[:metrics].count == 1 })
diff --git a/spec/support/redis/redis_shared_examples.rb b/spec/support/redis/redis_shared_examples.rb
index 33945509675..0368fd63357 100644
--- a/spec/support/redis/redis_shared_examples.rb
+++ b/spec/support/redis/redis_shared_examples.rb
@@ -4,6 +4,7 @@ RSpec.shared_examples "redis_shared_examples" do
include StubENV
let(:test_redis_url) { "redis://redishost:#{redis_port}" }
+ let(:test_cluster_config) { { cluster: [{ host: "redis://redishost", port: redis_port }] } }
let(:config_file_name) { instance_specific_config_file }
let(:config_old_format_socket) { "spec/fixtures/config/redis_old_format_socket.yml" }
let(:config_new_format_socket) { "spec/fixtures/config/redis_new_format_socket.yml" }
@@ -11,6 +12,7 @@ RSpec.shared_examples "redis_shared_examples" do
let(:new_socket_path) { "/path/to/redis.sock" }
let(:config_old_format_host) { "spec/fixtures/config/redis_old_format_host.yml" }
let(:config_new_format_host) { "spec/fixtures/config/redis_new_format_host.yml" }
+ let(:config_cluster_format_host) { "spec/fixtures/config/redis_cluster_format_host.yml" }
let(:redis_port) { 6379 }
let(:redis_database) { 99 }
let(:sentinel_port) { 26379 }
@@ -191,6 +193,30 @@ RSpec.shared_examples "redis_shared_examples" do
end
end
end
+
+ context 'with redis cluster format' do
+ let(:config_file_name) { config_cluster_format_host }
+
+ where(:rails_env, :host) do
+ [
+ %w[development development-master],
+ %w[test test-master],
+ %w[production production-master]
+ ]
+ end
+
+ with_them do
+ it 'returns hash with cluster and password' do
+ is_expected.to include(password: 'myclusterpassword',
+ cluster: [
+ { host: "#{host}1", port: redis_port },
+ { host: "#{host}2", port: redis_port }
+ ]
+ )
+ is_expected.not_to have_key(:url)
+ end
+ end
+ end
end
end
@@ -317,6 +343,14 @@ RSpec.shared_examples "redis_shared_examples" do
expect(subject).to eq(redis_database)
end
end
+
+ context 'with cluster-mode' do
+ let(:config_file_name) { config_cluster_format_host }
+
+ it 'returns the correct db' do
+ expect(subject).to eq(0)
+ end
+ end
end
describe '#sentinels' do
@@ -350,6 +384,14 @@ RSpec.shared_examples "redis_shared_examples" do
is_expected.to be_nil
end
end
+
+ context 'when cluster is defined' do
+ let(:config_file_name) { config_cluster_format_host }
+
+ it 'returns nil' do
+ is_expected.to be_nil
+ end
+ end
end
describe '#sentinels?' do
@@ -370,6 +412,14 @@ RSpec.shared_examples "redis_shared_examples" do
is_expected.to be_falsey
end
end
+
+ context 'when cluster is defined' do
+ let(:config_file_name) { config_cluster_format_host }
+
+ it 'returns false' do
+ is_expected.to be_falsey
+ end
+ end
end
describe '#raw_config_hash' do
@@ -377,6 +427,11 @@ RSpec.shared_examples "redis_shared_examples" do
expect(subject).to receive(:fetch_config) { test_redis_url }
expect(subject.send(:raw_config_hash)).to eq(url: test_redis_url)
end
+
+ it 'returns cluster config without url key in a hash' do
+ expect(subject).to receive(:fetch_config) { test_cluster_config }
+ expect(subject.send(:raw_config_hash)).to eq(test_cluster_config)
+ end
end
describe '#fetch_config' do
diff --git a/spec/support/rspec.rb b/spec/support/rspec.rb
index 71dfc3fd5a3..ff0b5bebe33 100644
--- a/spec/support/rspec.rb
+++ b/spec/support/rspec.rb
@@ -8,6 +8,8 @@ require_relative "helpers/stub_object_storage"
require_relative "helpers/stub_env"
require_relative "helpers/fast_rails_root"
+require_relative "../../lib/gitlab/utils"
+
RSpec::Expectations.configuration.on_potential_false_positives = :raise
RSpec.configure do |config|
@@ -35,4 +37,13 @@ RSpec.configure do |config|
config.include StubObjectStorage
config.include StubENV
config.include FastRailsRoot
+
+ warn_missing_feature_category = Gitlab::Utils.to_boolean(ENV['RSPEC_WARN_MISSING_FEATURE_CATEGORY'], default: true)
+
+ # Add warning for example missing feature_category
+ config.before do |example|
+ if warn_missing_feature_category && example.metadata[:feature_category].blank? && !ENV['CI']
+ warn "Missing metadata feature_category: #{example.location} See https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#feature-category-metadata"
+ end
+ end
end
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index 67b7023f1ff..489ed89c048 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -5,9 +5,6 @@
#
---
- './ee/spec/components/billing/plan_component_spec.rb'
-- './ee/spec/components/namespaces/free_user_cap/alert_component_spec.rb'
-- './ee/spec/components/namespaces/free_user_cap/preview_alert_component_spec.rb'
-- './ee/spec/components/namespaces/free_user_cap/preview_usage_quota_alert_component_spec.rb'
- './ee/spec/components/namespaces/free_user_cap/usage_quota_alert_component_spec.rb'
- './ee/spec/components/namespaces/free_user_cap/usage_quota_trial_alert_component_spec.rb'
- './ee/spec/components/namespaces/storage/limit_alert_component_spec.rb'
@@ -137,8 +134,6 @@
- './ee/spec/controllers/projects/environments_controller_spec.rb'
- './ee/spec/controllers/projects/feature_flag_issues_controller_spec.rb'
- './ee/spec/controllers/projects/imports_controller_spec.rb'
-- './ee/spec/controllers/projects/incident_management/escalation_policies_controller_spec.rb'
-- './ee/spec/controllers/projects/incident_management/oncall_schedules_controller_spec.rb'
- './ee/spec/controllers/projects/insights_controller_spec.rb'
- './ee/spec/controllers/projects/integrations/jira/issues_controller_spec.rb'
- './ee/spec/controllers/projects/integrations/zentao/issues_controller_spec.rb'
@@ -262,7 +257,6 @@
- './ee/spec/features/boards/user_visits_board_spec.rb'
- './ee/spec/features/burndown_charts_spec.rb'
- './ee/spec/features/burnup_charts_spec.rb'
-- './ee/spec/features/ci/ci_minutes_spec.rb'
- './ee/spec/features/ci_shared_runner_settings_spec.rb'
- './ee/spec/features/ci_shared_runner_warnings_spec.rb'
- './ee/spec/features/clusters/cluster_detail_page_spec.rb'
@@ -353,8 +347,6 @@
- './ee/spec/features/groups/wiki/user_views_wiki_empty_spec.rb'
- './ee/spec/features/ide/user_commits_changes_spec.rb'
- './ee/spec/features/ide/user_opens_ide_spec.rb'
-- './ee/spec/features/incidents/incident_details_spec.rb'
-- './ee/spec/features/incidents/incidents_list_spec.rb'
- './ee/spec/features/integrations/jira/jira_issues_list_spec.rb'
- './ee/spec/features/invites_spec.rb'
- './ee/spec/features/issues/blocking_issues_spec.rb'
@@ -421,7 +413,6 @@
- './ee/spec/features/profiles/account_spec.rb'
- './ee/spec/features/profiles/billing_spec.rb'
- './ee/spec/features/profiles/password_spec.rb'
-- './ee/spec/features/profiles/usage_quotas_spec.rb'
- './ee/spec/features/profiles/user_visits_public_profile_spec.rb'
- './ee/spec/features/projects/active_tabs_spec.rb'
- './ee/spec/features/projects/audit_events_spec.rb'
@@ -592,13 +583,6 @@
- './ee/spec/finders/group_projects_finder_spec.rb'
- './ee/spec/finders/group_saml_identity_finder_spec.rb'
- './ee/spec/finders/groups_with_templates_finder_spec.rb'
-- './ee/spec/finders/incident_management/escalation_policies_finder_spec.rb'
-- './ee/spec/finders/incident_management/escalation_rules_finder_spec.rb'
-- './ee/spec/finders/incident_management/issuable_resource_links_finder_spec.rb'
-- './ee/spec/finders/incident_management/member_oncall_rotations_finder_spec.rb'
-- './ee/spec/finders/incident_management/oncall_rotations_finder_spec.rb'
-- './ee/spec/finders/incident_management/oncall_schedules_finder_spec.rb'
-- './ee/spec/finders/incident_management/oncall_users_finder_spec.rb'
- './ee/spec/finders/issues_finder_spec.rb'
- './ee/spec/finders/iterations/cadences_finder_spec.rb'
- './ee/spec/finders/iterations_finder_spec.rb'
@@ -625,8 +609,6 @@
- './ee/spec/finders/security/vulnerability_reads_finder_spec.rb'
- './ee/spec/finders/snippets_finder_spec.rb'
- './ee/spec/finders/software_license_policies_finder_spec.rb'
-- './ee/spec/finders/status_page/incident_comments_finder_spec.rb'
-- './ee/spec/finders/status_page/incidents_finder_spec.rb'
- './ee/spec/finders/template_finder_spec.rb'
- './ee/spec/finders/users_finder_spec.rb'
- './ee/spec/frontend/fixtures/analytics/charts.rb'
@@ -714,17 +696,6 @@
- './ee/spec/graphql/mutations/epics/create_spec.rb'
- './ee/spec/graphql/mutations/epics/update_spec.rb'
- './ee/spec/graphql/mutations/gitlab_subscriptions/activate_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/escalation_policy/create_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/escalation_policy/destroy_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/escalation_policy/update_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/issuable_resource_link/create_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/issuable_resource_link/destroy_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/oncall_rotation/create_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/oncall_rotation/destroy_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/oncall_rotation/update_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/oncall_schedule/create_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/oncall_schedule/destroy_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/oncall_schedule/update_spec.rb'
- './ee/spec/graphql/mutations/instance_security_dashboard/add_project_spec.rb'
- './ee/spec/graphql/mutations/instance_security_dashboard/remove_project_spec.rb'
- './ee/spec/graphql/mutations/issues/create_spec.rb'
@@ -796,12 +767,6 @@
- './ee/spec/graphql/resolvers/geo/snippet_repository_registries_resolver_spec.rb'
- './ee/spec/graphql/resolvers/geo/terraform_state_version_registries_resolver_spec.rb'
- './ee/spec/graphql/resolvers/geo/upload_registries_resolver_spec.rb'
-- './ee/spec/graphql/resolvers/incident_management/escalation_policies_resolver_spec.rb'
-- './ee/spec/graphql/resolvers/incident_management/issuable_resource_links_resolver_spec.rb'
-- './ee/spec/graphql/resolvers/incident_management/oncall_rotations_resolver_spec.rb'
-- './ee/spec/graphql/resolvers/incident_management/oncall_schedule_resolver_spec.rb'
-- './ee/spec/graphql/resolvers/incident_management/oncall_shifts_resolver_spec.rb'
-- './ee/spec/graphql/resolvers/incident_management/oncall_users_resolver_spec.rb'
- './ee/spec/graphql/resolvers/instance_security_dashboard/projects_resolver_spec.rb'
- './ee/spec/graphql/resolvers/instance_security_dashboard_resolver_spec.rb'
- './ee/spec/graphql/resolvers/iterations/cadences_resolver_spec.rb'
@@ -899,16 +864,6 @@
- './ee/spec/graphql/types/group_release_stats_type_spec.rb'
- './ee/spec/graphql/types/group_stats_type_spec.rb'
- './ee/spec/graphql/types/health_status_enum_spec.rb'
-- './ee/spec/graphql/types/incident_management/escalation_policy_type_spec.rb'
-- './ee/spec/graphql/types/incident_management/escalation_rule_input_type_spec.rb'
-- './ee/spec/graphql/types/incident_management/escalation_rule_type_spec.rb'
-- './ee/spec/graphql/types/incident_management/issuable_resource_link_type_enum_spec.rb'
-- './ee/spec/graphql/types/incident_management/issuable_resource_link_type_spec.rb'
-- './ee/spec/graphql/types/incident_management/oncall_participant_type_spec.rb'
-- './ee/spec/graphql/types/incident_management/oncall_rotation_date_input_type_spec.rb'
-- './ee/spec/graphql/types/incident_management/oncall_rotation_type_spec.rb'
-- './ee/spec/graphql/types/incident_management/oncall_schedule_type_spec.rb'
-- './ee/spec/graphql/types/incident_management/oncall_shift_type_spec.rb'
- './ee/spec/graphql/types/instance_security_dashboard_type_spec.rb'
- './ee/spec/graphql/types/issue_connection_type_spec.rb'
- './ee/spec/graphql/types/issue_type_spec.rb'
@@ -1049,7 +1004,6 @@
- './ee/spec/helpers/ee/operations_helper_spec.rb'
- './ee/spec/helpers/ee/personal_access_tokens_helper_spec.rb'
- './ee/spec/helpers/ee/profiles_helper_spec.rb'
-- './ee/spec/helpers/ee/projects/incidents_helper_spec.rb'
- './ee/spec/helpers/ee/projects/pipeline_helper_spec.rb'
- './ee/spec/helpers/ee/projects/security/api_fuzzing_configuration_helper_spec.rb'
- './ee/spec/helpers/ee/projects/security/configuration_helper_spec.rb'
@@ -1074,8 +1028,6 @@
- './ee/spec/helpers/groups/ldap_sync_helper_spec.rb'
- './ee/spec/helpers/groups/security_features_helper_spec.rb'
- './ee/spec/helpers/groups/sso_helper_spec.rb'
-- './ee/spec/helpers/incident_management/escalation_policy_helper_spec.rb'
-- './ee/spec/helpers/incident_management/oncall_schedule_helper_spec.rb'
- './ee/spec/helpers/kerberos_helper_spec.rb'
- './ee/spec/helpers/license_helper_spec.rb'
- './ee/spec/helpers/license_monitoring_helper_spec.rb'
@@ -1525,7 +1477,6 @@
- './ee/spec/lib/gitlab/geo/log_cursor/daemon_spec.rb'
- './ee/spec/lib/gitlab/geo/log_cursor/event_logs_spec.rb'
- './ee/spec/lib/gitlab/geo/log_cursor/events/cache_invalidation_event_spec.rb'
-- './ee/spec/lib/gitlab/geo/log_cursor/events/container_repository_updated_event_spec.rb'
- './ee/spec/lib/gitlab/geo/log_cursor/events/design_repository_updated_event_spec.rb'
- './ee/spec/lib/gitlab/geo/log_cursor/events/event_spec.rb'
- './ee/spec/lib/gitlab/geo/log_cursor/events/hashed_storage_attachments_event_spec.rb'
@@ -1572,7 +1523,6 @@
- './ee/spec/lib/gitlab/import_export/group/relation_factory_spec.rb'
- './ee/spec/lib/gitlab/import_export/project/object_builder_spec.rb'
- './ee/spec/lib/gitlab/import_sources_spec.rb'
-- './ee/spec/lib/gitlab/incident_management_spec.rb'
- './ee/spec/lib/gitlab/ingestion/bulk_insertable_task_spec.rb'
- './ee/spec/lib/gitlab/insights/executors/dora_executor_spec.rb'
- './ee/spec/lib/gitlab/insights/executors/issuable_executor_spec.rb'
@@ -1629,14 +1579,6 @@
- './ee/spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb'
- './ee/spec/lib/gitlab/spdx/catalogue_gateway_spec.rb'
- './ee/spec/lib/gitlab/spdx/catalogue_spec.rb'
-- './ee/spec/lib/gitlab/status_page/filter/image_filter_spec.rb'
-- './ee/spec/lib/gitlab/status_page/filter/mention_anonymization_filter_spec.rb'
-- './ee/spec/lib/gitlab/status_page/pipeline/post_process_pipeline_spec.rb'
-- './ee/spec/lib/gitlab/status_page_spec.rb'
-- './ee/spec/lib/gitlab/status_page/storage/s3_client_spec.rb'
-- './ee/spec/lib/gitlab/status_page/storage/s3_multipart_upload_spec.rb'
-- './ee/spec/lib/gitlab/status_page/storage_spec.rb'
-- './ee/spec/lib/gitlab/status_page/usage_data_counters/incident_counter_spec.rb'
- './ee/spec/lib/gitlab/subscription_portal/clients/graphql_spec.rb'
- './ee/spec/lib/gitlab/subscription_portal/client_spec.rb'
- './ee/spec/lib/gitlab/subscription_portal/clients/rest_spec.rb'
@@ -1686,7 +1628,6 @@
- './ee/spec/lib/gitlab/web_ide/config/entry/schema_spec.rb'
- './ee/spec/lib/gitlab/web_ide/config/entry/schemas_spec.rb'
- './ee/spec/lib/gitlab/web_ide/config/entry/schema/uri_spec.rb'
-- './ee/spec/lib/incident_management/oncall_shift_generator_spec.rb'
- './ee/spec/lib/omni_auth/strategies/group_saml_spec.rb'
- './ee/spec/lib/omni_auth/strategies/kerberos_spec.rb'
- './ee/spec/lib/peek/views/elasticsearch_spec.rb'
@@ -1830,7 +1771,6 @@
- './ee/spec/models/concerns/geo/verifiable_model_spec.rb'
- './ee/spec/models/concerns/geo/verification_state_spec.rb'
- './ee/spec/models/concerns/health_status_spec.rb'
-- './ee/spec/models/concerns/incident_management/base_pending_escalation_spec.rb'
- './ee/spec/models/concerns/password_complexity_spec.rb'
- './ee/spec/models/concerns/scim_paginatable_spec.rb'
- './ee/spec/models/container_registry/event_spec.rb'
@@ -1874,7 +1814,6 @@
- './ee/spec/models/ee/group_group_link_spec.rb'
- './ee/spec/models/ee/groups/feature_setting_spec.rb'
- './ee/spec/models/ee/group_spec.rb'
-- './ee/spec/models/ee/incident_management/project_incident_management_setting_spec.rb'
- './ee/spec/models/ee/integrations/jira_spec.rb'
- './ee/spec/models/ee/integration_spec.rb'
- './ee/spec/models/ee/iterations/cadence_spec.rb'
@@ -1930,7 +1869,6 @@
- './ee/spec/models/geo/cache_invalidation_event_spec.rb'
- './ee/spec/models/geo/ci_secure_file_registry_spec.rb'
- './ee/spec/models/geo/container_repository_registry_spec.rb'
-- './ee/spec/models/geo/container_repository_updated_event_spec.rb'
- './ee/spec/models/geo/deleted_project_spec.rb'
- './ee/spec/models/geo/design_registry_spec.rb'
- './ee/spec/models/geo/event_log_spec.rb'
@@ -1974,16 +1912,6 @@
- './ee/spec/models/historical_data_spec.rb'
- './ee/spec/models/hooks/group_hook_spec.rb'
- './ee/spec/models/identity_spec.rb'
-- './ee/spec/models/incident_management/escalation_policy_spec.rb'
-- './ee/spec/models/incident_management/escalation_rule_spec.rb'
-- './ee/spec/models/incident_management/issuable_escalation_status_spec.rb'
-- './ee/spec/models/incident_management/issuable_resource_link_spec.rb'
-- './ee/spec/models/incident_management/oncall_participant_spec.rb'
-- './ee/spec/models/incident_management/oncall_rotation_spec.rb'
-- './ee/spec/models/incident_management/oncall_schedule_spec.rb'
-- './ee/spec/models/incident_management/oncall_shift_spec.rb'
-- './ee/spec/models/incident_management/pending_escalations/alert_spec.rb'
-- './ee/spec/models/incident_management/pending_escalations/issue_spec.rb'
- './ee/spec/models/instance_security_dashboard_spec.rb'
- './ee/spec/models/integrations/chat_message/vulnerability_message_spec.rb'
- './ee/spec/models/integrations/github/remote_project_spec.rb'
@@ -2069,8 +1997,6 @@
- './ee/spec/models/snippet_spec.rb'
- './ee/spec/models/software_license_policy_spec.rb'
- './ee/spec/models/software_license_spec.rb'
-- './ee/spec/models/status_page/project_setting_spec.rb'
-- './ee/spec/models/status_page/published_incident_spec.rb'
- './ee/spec/models/storage_shard_spec.rb'
- './ee/spec/models/uploads/local_spec.rb'
- './ee/spec/models/upload_spec.rb'
@@ -2133,9 +2059,6 @@
- './ee/spec/policies/group_hook_policy_spec.rb'
- './ee/spec/policies/group_policy_spec.rb'
- './ee/spec/policies/identity_provider_policy_spec.rb'
-- './ee/spec/policies/incident_management/oncall_rotation_policy_spec.rb'
-- './ee/spec/policies/incident_management/oncall_schedule_policy_spec.rb'
-- './ee/spec/policies/incident_management/oncall_shift_policy_spec.rb'
- './ee/spec/policies/instance_security_dashboard_policy_spec.rb'
- './ee/spec/policies/issuable_policy_spec.rb'
- './ee/spec/policies/issue_policy_spec.rb'
@@ -2267,7 +2190,6 @@
- './ee/spec/requests/api/graphql/group/epics_spec.rb'
- './ee/spec/requests/api/graphql/group/external_audit_event_destinations_spec.rb'
- './ee/spec/requests/api/graphql/group_query_spec.rb'
-- './ee/spec/requests/api/graphql/incident_management/issuable_resource_links_spec.rb'
- './ee/spec/requests/api/graphql/instance_security_dashboard_spec.rb'
- './ee/spec/requests/api/graphql/iterations/cadences_spec.rb'
- './ee/spec/requests/api/graphql/iterations/iterations_spec.rb'
@@ -2321,16 +2243,6 @@
- './ee/spec/requests/api/graphql/mutations/epics/update_spec.rb'
- './ee/spec/requests/api/graphql/mutations/epic_tree/reorder_spec.rb'
- './ee/spec/requests/api/graphql/mutations/gitlab_subscriptions/activate_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/escalation_policy/create_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/escalation_policy/destroy_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/escalation_policy/update_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/issuable_resource_link/create_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/issuable_resource_link/destroy_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/oncall_rotation/create_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/oncall_rotation/update_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/oncall_schedule/create_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/oncall_schedule/destroy_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/oncall_schedule/update_spec.rb'
- './ee/spec/requests/api/graphql/mutations/issues/create_spec.rb'
- './ee/spec/requests/api/graphql/mutations/issues/promote_to_epic_spec.rb'
- './ee/spec/requests/api/graphql/mutations/issues/set_epic_spec.rb'
@@ -2378,11 +2290,6 @@
- './ee/spec/requests/api/graphql/project/dast_site_profile_spec.rb'
- './ee/spec/requests/api/graphql/project/dast_site_profiles_spec.rb'
- './ee/spec/requests/api/graphql/project/dast_site_validations_spec.rb'
-- './ee/spec/requests/api/graphql/project/incident_management/escalation_policies_spec.rb'
-- './ee/spec/requests/api/graphql/project/incident_management/escalation_policy/rules_spec.rb'
-- './ee/spec/requests/api/graphql/project/incident_management/oncall_participants_spec.rb'
-- './ee/spec/requests/api/graphql/project/incident_management/oncall_schedules_spec.rb'
-- './ee/spec/requests/api/graphql/project/incident_management/oncall_shifts_spec.rb'
- './ee/spec/requests/api/graphql/project/issues_spec.rb'
- './ee/spec/requests/api/graphql/project/merge_requests_spec.rb'
- './ee/spec/requests/api/graphql/project/path_locks_spec.rb'
@@ -2501,14 +2408,12 @@
- './ee/spec/requests/groups/roadmap_controller_spec.rb'
- './ee/spec/requests/groups/security/credentials_controller_spec.rb'
- './ee/spec/requests/groups/settings/reporting_controller_spec.rb'
-- './ee/spec/requests/groups/usage_quotas_spec.rb'
- './ee/spec/requests/jwt_controller_spec.rb'
- './ee/spec/requests/lfs_http_spec.rb'
- './ee/spec/requests/lfs_locks_api_spec.rb'
- './ee/spec/requests/omniauth_kerberos_spec.rb'
- './ee/spec/requests/projects/analytics/code_reviews_controller_spec.rb'
- './ee/spec/requests/projects/audit_events_spec.rb'
-- './ee/spec/requests/projects/incidents_controller_spec.rb'
- './ee/spec/requests/projects/issue_feature_flags_controller_spec.rb'
- './ee/spec/requests/projects/issues_controller_spec.rb'
- './ee/spec/requests/projects/merge_requests_controller_spec.rb'
@@ -2588,8 +2493,6 @@
- './ee/spec/serializers/fork_namespace_entity_spec.rb'
- './ee/spec/serializers/geo_project_registry_entity_spec.rb'
- './ee/spec/serializers/group_vulnerability_autocomplete_entity_spec.rb'
-- './ee/spec/serializers/incident_management/escalation_policy_entity_spec.rb'
-- './ee/spec/serializers/incident_management/oncall_schedule_entity_spec.rb'
- './ee/spec/serializers/integrations/field_entity_spec.rb'
- './ee/spec/serializers/integrations/jira_serializers/issue_detail_entity_spec.rb'
- './ee/spec/serializers/integrations/jira_serializers/issue_entity_spec.rb'
@@ -2620,10 +2523,6 @@
- './ee/spec/serializers/security/license_policy_entity_spec.rb'
- './ee/spec/serializers/security/vulnerability_report_data_entity_spec.rb'
- './ee/spec/serializers/security/vulnerability_report_data_serializer_spec.rb'
-- './ee/spec/serializers/status_page/incident_comment_entity_spec.rb'
-- './ee/spec/serializers/status_page/incident_entity_spec.rb'
-- './ee/spec/serializers/status_page/incident_serializer_spec.rb'
-- './ee/spec/serializers/status_page/renderer_spec.rb'
- './ee/spec/serializers/storage_shard_entity_spec.rb'
- './ee/spec/serializers/test_reports_comparer_entity_spec.rb'
- './ee/spec/serializers/test_reports_comparer_serializer_spec.rb'
@@ -2836,9 +2735,6 @@
- './ee/spec/services/ee/groups/deploy_tokens/revoke_service_spec.rb'
- './ee/spec/services/ee/groups/import_export/export_service_spec.rb'
- './ee/spec/services/ee/groups/import_export/import_service_spec.rb'
-- './ee/spec/services/ee/incident_management/issuable_escalation_statuses/after_update_service_spec.rb'
-- './ee/spec/services/ee/incident_management/issuable_escalation_statuses/create_service_spec.rb'
-- './ee/spec/services/ee/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb'
- './ee/spec/services/ee/integrations/test/project_service_spec.rb'
- './ee/spec/services/ee/ip_restrictions/update_service_spec.rb'
- './ee/spec/services/ee/issuable/bulk_update_service_spec.rb'
@@ -2956,7 +2852,6 @@
- './ee/spec/services/geo/cache_invalidation_event_store_spec.rb'
- './ee/spec/services/geo/container_repository_sync_service_spec.rb'
- './ee/spec/services/geo/container_repository_sync_spec.rb'
-- './ee/spec/services/geo/container_repository_updated_event_store_spec.rb'
- './ee/spec/services/geo/design_repository_sync_service_spec.rb'
- './ee/spec/services/geo/event_service_spec.rb'
- './ee/spec/services/geo/file_registry_removal_service_spec.rb'
@@ -3030,26 +2925,6 @@
- './ee/spec/services/groups/update_service_spec.rb'
- './ee/spec/services/historical_user_data/csv_service_spec.rb'
- './ee/spec/services/ide/schemas_config_service_spec.rb'
-- './ee/spec/services/incident_management/create_incident_sla_exceeded_label_service_spec.rb'
-- './ee/spec/services/incident_management/escalation_policies/create_service_spec.rb'
-- './ee/spec/services/incident_management/escalation_policies/destroy_service_spec.rb'
-- './ee/spec/services/incident_management/escalation_policies/update_service_spec.rb'
-- './ee/spec/services/incident_management/escalation_rules/destroy_service_spec.rb'
-- './ee/spec/services/incident_management/incidents/create_sla_service_spec.rb'
-- './ee/spec/services/incident_management/incidents/upload_metric_service_spec.rb'
-- './ee/spec/services/incident_management/issuable_resource_links/create_service_spec.rb'
-- './ee/spec/services/incident_management/issuable_resource_links/destroy_service_spec.rb'
-- './ee/spec/services/incident_management/oncall_rotations/create_service_spec.rb'
-- './ee/spec/services/incident_management/oncall_rotations/destroy_service_spec.rb'
-- './ee/spec/services/incident_management/oncall_rotations/edit_service_spec.rb'
-- './ee/spec/services/incident_management/oncall_rotations/remove_participant_service_spec.rb'
-- './ee/spec/services/incident_management/oncall_rotations/remove_participants_service_spec.rb'
-- './ee/spec/services/incident_management/oncall_schedules/create_service_spec.rb'
-- './ee/spec/services/incident_management/oncall_schedules/destroy_service_spec.rb'
-- './ee/spec/services/incident_management/oncall_schedules/update_service_spec.rb'
-- './ee/spec/services/incident_management/oncall_shifts/read_service_spec.rb'
-- './ee/spec/services/incident_management/pending_escalations/create_service_spec.rb'
-- './ee/spec/services/incident_management/pending_escalations/process_service_spec.rb'
- './ee/spec/services/issuable/destroy_label_links_service_spec.rb'
- './ee/spec/services/issue_feature_flags/list_service_spec.rb'
- './ee/spec/services/issues/build_service_spec.rb'
@@ -3233,13 +3108,6 @@
- './ee/spec/services/software_license_policies/create_service_spec.rb'
- './ee/spec/services/software_license_policies/update_service_spec.rb'
- './ee/spec/services/start_pull_mirroring_service_spec.rb'
-- './ee/spec/services/status_page/mark_for_publication_service_spec.rb'
-- './ee/spec/services/status_page/publish_attachments_service_spec.rb'
-- './ee/spec/services/status_page/publish_details_service_spec.rb'
-- './ee/spec/services/status_page/publish_list_service_spec.rb'
-- './ee/spec/services/status_page/publish_service_spec.rb'
-- './ee/spec/services/status_page/trigger_publish_service_spec.rb'
-- './ee/spec/services/status_page/unpublish_details_service_spec.rb'
- './ee/spec/services/system_notes/epics_service_spec.rb'
- './ee/spec/services/system_note_service_spec.rb'
- './ee/spec/services/system_notes/escalations_service_spec.rb'
@@ -3512,15 +3380,6 @@
- './ee/spec/workers/group_wikis/git_garbage_collect_worker_spec.rb'
- './ee/spec/workers/historical_data_worker_spec.rb'
- './ee/spec/workers/import_software_licenses_worker_spec.rb'
-- './ee/spec/workers/incident_management/apply_incident_sla_exceeded_label_worker_spec.rb'
-- './ee/spec/workers/incident_management/incident_sla_exceeded_check_worker_spec.rb'
-- './ee/spec/workers/incident_management/oncall_rotations/persist_all_rotations_shifts_job_spec.rb'
-- './ee/spec/workers/incident_management/oncall_rotations/persist_shifts_job_spec.rb'
-- './ee/spec/workers/incident_management/pending_escalations/alert_check_worker_spec.rb'
-- './ee/spec/workers/incident_management/pending_escalations/alert_create_worker_spec.rb'
-- './ee/spec/workers/incident_management/pending_escalations/issue_check_worker_spec.rb'
-- './ee/spec/workers/incident_management/pending_escalations/issue_create_worker_spec.rb'
-- './ee/spec/workers/incident_management/pending_escalations/schedule_check_cron_worker_spec.rb'
- './ee/spec/workers/iterations/cadences/create_iterations_worker_spec.rb'
- './ee/spec/workers/iterations/cadences/schedule_create_iterations_worker_spec.rb'
- './ee/spec/workers/iterations/roll_over_issues_worker_spec.rb'
@@ -3559,7 +3418,6 @@
- './ee/spec/workers/security/sync_scan_policies_worker_spec.rb'
- './ee/spec/workers/security/track_secure_scans_worker_spec.rb'
- './ee/spec/workers/set_user_status_based_on_user_cap_setting_worker_spec.rb'
-- './ee/spec/workers/status_page/publish_worker_spec.rb'
- './ee/spec/workers/store_security_reports_worker_spec.rb'
- './ee/spec/workers/sync_seat_link_request_worker_spec.rb'
- './ee/spec/workers/sync_seat_link_worker_spec.rb'
@@ -3787,7 +3645,6 @@
- './spec/controllers/projects/hooks_controller_spec.rb'
- './spec/controllers/projects/import/jira_controller_spec.rb'
- './spec/controllers/projects/imports_controller_spec.rb'
-- './spec/controllers/projects/incidents_controller_spec.rb'
- './spec/controllers/projects/issue_links_controller_spec.rb'
- './spec/controllers/projects/issues_controller_spec.rb'
- './spec/controllers/projects/jobs_controller_spec.rb'
@@ -4081,12 +3938,6 @@
- './spec/features/ide/user_commits_changes_spec.rb'
- './spec/features/ide/user_opens_merge_request_spec.rb'
- './spec/features/import/manifest_import_spec.rb'
-- './spec/features/incidents/incident_details_spec.rb'
-- './spec/features/incidents/incidents_list_spec.rb'
-- './spec/features/incidents/incident_timeline_events_spec.rb'
-- './spec/features/incidents/user_creates_new_incident_spec.rb'
-- './spec/features/incidents/user_filters_incidents_by_status_spec.rb'
-- './spec/features/incidents/user_searches_incidents_spec.rb'
- './spec/features/invites_spec.rb'
- './spec/features/issuables/issuable_list_spec.rb'
- './spec/features/issuables/markdown_references/internal_references_spec.rb'
@@ -4114,7 +3965,6 @@
- './spec/features/issues/form_spec.rb'
- './spec/features/issues/gfm_autocomplete_spec.rb'
- './spec/features/issues/group_label_sidebar_spec.rb'
-- './spec/features/issues/incident_issue_spec.rb'
- './spec/features/issues/issue_detail_spec.rb'
- './spec/features/issues/issue_header_spec.rb'
- './spec/features/issues/issue_sidebar_spec.rb'
@@ -4408,7 +4258,6 @@
- './spec/features/projects/integrations/user_activates_assembla_spec.rb'
- './spec/features/projects/integrations/user_activates_atlassian_bamboo_ci_spec.rb'
- './spec/features/projects/integrations/user_activates_emails_on_push_spec.rb'
-- './spec/features/projects/integrations/user_activates_flowdock_spec.rb'
- './spec/features/projects/integrations/user_activates_irker_spec.rb'
- './spec/features/projects/integrations/user_activates_issue_tracker_spec.rb'
- './spec/features/projects/integrations/user_activates_jetbrains_teamcity_ci_spec.rb'
@@ -4730,7 +4579,6 @@
- './spec/finders/groups/projects_requiring_authorizations_refresh/on_direct_membership_finder_spec.rb'
- './spec/finders/groups/projects_requiring_authorizations_refresh/on_transfer_finder_spec.rb'
- './spec/finders/groups/user_groups_finder_spec.rb'
-- './spec/finders/incident_management/timeline_events_finder_spec.rb'
- './spec/finders/issuables/crm_contact_filter_spec.rb'
- './spec/finders/issuables/crm_organization_filter_spec.rb'
- './spec/finders/issues_finder_spec.rb'
@@ -4907,10 +4755,6 @@
- './spec/graphql/mutations/discussions/toggle_resolve_spec.rb'
- './spec/graphql/mutations/environments/canary_ingress/update_spec.rb'
- './spec/graphql/mutations/groups/update_spec.rb'
-- './spec/graphql/mutations/incident_management/timeline_event/create_spec.rb'
-- './spec/graphql/mutations/incident_management/timeline_event/destroy_spec.rb'
-- './spec/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb'
-- './spec/graphql/mutations/incident_management/timeline_event/update_spec.rb'
- './spec/graphql/mutations/issues/create_spec.rb'
- './spec/graphql/mutations/issues/move_spec.rb'
- './spec/graphql/mutations/issues/set_assignees_spec.rb'
@@ -5023,7 +4867,6 @@
- './spec/graphql/resolvers/group_packages_resolver_spec.rb'
- './spec/graphql/resolvers/group_resolver_spec.rb'
- './spec/graphql/resolvers/groups_resolver_spec.rb'
-- './spec/graphql/resolvers/incident_management/timeline_events_resolver_spec.rb'
- './spec/graphql/resolvers/issues_resolver_spec.rb'
- './spec/graphql/resolvers/issue_status_counts_resolver_spec.rb'
- './spec/graphql/resolvers/kas/agent_configurations_resolver_spec.rb'
@@ -5218,8 +5061,6 @@
- './spec/graphql/types/group_member_relation_enum_spec.rb'
- './spec/graphql/types/group_member_type_spec.rb'
- './spec/graphql/types/group_type_spec.rb'
-- './spec/graphql/types/incident_management/escalation_status_enum_spec.rb'
-- './spec/graphql/types/incident_management/timeline_event_type_spec.rb'
- './spec/graphql/types/invitation_interface_spec.rb'
- './spec/graphql/types/issuable_searchable_field_enum_spec.rb'
- './spec/graphql/types/issuable_severity_enum_spec.rb'
@@ -5470,7 +5311,6 @@
- './spec/helpers/projects/cluster_agents_helper_spec.rb'
- './spec/helpers/projects/error_tracking_helper_spec.rb'
- './spec/helpers/projects_helper_spec.rb'
-- './spec/helpers/projects/incidents_helper_spec.rb'
- './spec/helpers/projects/pipeline_helper_spec.rb'
- './spec/helpers/projects/project_members_helper_spec.rb'
- './spec/helpers/projects/security/configuration_helper_spec.rb'
@@ -5739,7 +5579,6 @@
- './spec/lib/banzai/pipeline/emoji_pipeline_spec.rb'
- './spec/lib/banzai/pipeline/full_pipeline_spec.rb'
- './spec/lib/banzai/pipeline/gfm_pipeline_spec.rb'
-- './spec/lib/banzai/pipeline/incident_management/timeline_event_pipeline_spec.rb'
- './spec/lib/banzai/pipeline/jira_import/adf_commonmark_pipeline_spec.rb'
- './spec/lib/banzai/pipeline/plain_markdown_pipeline_spec.rb'
- './spec/lib/banzai/pipeline/post_process_pipeline_spec.rb'
@@ -7118,7 +6957,6 @@
- './spec/lib/gitlab/import/set_async_jid_spec.rb'
- './spec/lib/gitlab/import_sources_spec.rb'
- './spec/lib/gitlab/inactive_projects_deletion_warning_tracker_spec.rb'
-- './spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb'
- './spec/lib/gitlab/incoming_email_spec.rb'
- './spec/lib/gitlab/insecure_key_fingerprint_spec.rb'
- './spec/lib/gitlab/instrumentation_helper_spec.rb'
@@ -7598,7 +7436,6 @@
- './spec/lib/gitlab/tracking/destinations/snowplow_micro_spec.rb'
- './spec/lib/gitlab/tracking/destinations/snowplow_spec.rb'
- './spec/lib/gitlab/tracking/event_definition_spec.rb'
-- './spec/lib/gitlab/tracking/incident_management_spec.rb'
- './spec/lib/gitlab/tracking/snowplow_schema_validation_spec.rb'
- './spec/lib/gitlab/tracking_spec.rb'
- './spec/lib/gitlab/tracking/standard_context_spec.rb'
@@ -7902,7 +7739,6 @@
- './spec/migrations/20211207125331_remove_jobs_for_recalculate_vulnerabilities_occurrences_uuid_spec.rb'
- './spec/migrations/20211207135331_schedule_recalculate_uuid_on_vulnerabilities_occurrences4_spec.rb'
- './spec/migrations/20211210140629_encrypt_static_object_token_spec.rb'
-- './spec/migrations/20211214012507_backfill_incident_issue_escalation_statuses_spec.rb'
- './spec/migrations/20211217174331_mark_recalculate_finding_signatures_as_completed_spec.rb'
- './spec/migrations/20220106111958_add_insert_or_update_vulnerability_reads_trigger_spec.rb'
- './spec/migrations/20220106112043_add_update_vulnerability_reads_trigger_spec.rb'
@@ -7959,7 +7795,6 @@
- './spec/migrations/20220627090231_schedule_disable_legacy_open_source_license_for_inactive_public_projects_spec.rb'
- './spec/migrations/20220627152642_queue_update_delayed_project_removal_to_null_for_user_namespace_spec.rb'
- './spec/migrations/20220628012902_finalise_project_namespace_members_spec.rb'
-- './spec/migrations/20220629184402_unset_escalation_policies_for_alert_incidents_spec.rb'
- './spec/migrations/20220715163254_update_notes_in_past_spec.rb'
- './spec/migrations/20220721031446_schedule_disable_legacy_open_source_license_for_one_member_no_repo_projects_spec.rb'
- './spec/migrations/20220722084543_schedule_disable_legacy_open_source_license_for_no_issues_no_repo_projects_spec.rb'
@@ -8421,9 +8256,6 @@
- './spec/models/identity_spec.rb'
- './spec/models/import_export_upload_spec.rb'
- './spec/models/import_failure_spec.rb'
-- './spec/models/incident_management/issuable_escalation_status_spec.rb'
-- './spec/models/incident_management/project_incident_management_setting_spec.rb'
-- './spec/models/incident_management/timeline_event_spec.rb'
- './spec/models/instance_configuration_spec.rb'
- './spec/models/instance_metadata/kas_spec.rb'
- './spec/models/instance_metadata_spec.rb'
@@ -8455,7 +8287,6 @@
- './spec/models/integrations/ewm_spec.rb'
- './spec/models/integrations/external_wiki_spec.rb'
- './spec/models/integrations/field_spec.rb'
-- './spec/models/integrations/flowdock_spec.rb'
- './spec/models/integrations/hangouts_chat_spec.rb'
- './spec/models/integrations/harbor_spec.rb'
- './spec/models/integrations/irker_spec.rb'
@@ -8795,7 +8626,6 @@
- './spec/policies/group_member_policy_spec.rb'
- './spec/policies/group_policy_spec.rb'
- './spec/policies/identity_provider_policy_spec.rb'
-- './spec/policies/incident_management/timeline_event_policy_spec.rb'
- './spec/policies/instance_metadata_policy_spec.rb'
- './spec/policies/integration_policy_spec.rb'
- './spec/policies/issuable_policy_spec.rb'
@@ -9067,10 +8897,6 @@
- './spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb'
- './spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb'
- './spec/requests/api/graphql/mutations/groups/update_spec.rb'
-- './spec/requests/api/graphql/mutations/incident_management/timeline_event/create_spec.rb'
-- './spec/requests/api/graphql/mutations/incident_management/timeline_event/destroy_spec.rb'
-- './spec/requests/api/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb'
-- './spec/requests/api/graphql/mutations/incident_management/timeline_event/update_spec.rb'
- './spec/requests/api/graphql/mutations/issues/create_spec.rb'
- './spec/requests/api/graphql/mutations/issues/move_spec.rb'
- './spec/requests/api/graphql/mutations/issues/set_confidential_spec.rb'
@@ -9164,7 +8990,6 @@
- './spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb'
- './spec/requests/api/graphql/project/fork_targets_spec.rb'
- './spec/requests/api/graphql/project/grafana_integration_spec.rb'
-- './spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb'
- './spec/requests/api/graphql/project/issue/design_collection/version_spec.rb'
- './spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb'
- './spec/requests/api/graphql/project/issue/designs/designs_spec.rb'
@@ -9395,7 +9220,6 @@
- './spec/requests/projects/harbor/artifacts_controller_spec.rb'
- './spec/requests/projects/harbor/repositories_controller_spec.rb'
- './spec/requests/projects/harbor/tags_controller_spec.rb'
-- './spec/requests/projects/incident_management/pagerduty_incidents_spec.rb'
- './spec/requests/projects/integrations/shimos_controller_spec.rb'
- './spec/requests/projects/issue_links_controller_spec.rb'
- './spec/requests/projects/issues_controller_spec.rb'
@@ -9981,15 +9805,6 @@
- './spec/services/import/gitlab_projects/file_acquisition_strategies/remote_file_spec.rb'
- './spec/services/import/prepare_service_spec.rb'
- './spec/services/import/validate_remote_git_endpoint_service_spec.rb'
-- './spec/services/incident_management/incidents/create_service_spec.rb'
-- './spec/services/incident_management/issuable_escalation_statuses/after_update_service_spec.rb'
-- './spec/services/incident_management/issuable_escalation_statuses/build_service_spec.rb'
-- './spec/services/incident_management/issuable_escalation_statuses/create_service_spec.rb'
-- './spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb'
-- './spec/services/incident_management/pager_duty/process_webhook_service_spec.rb'
-- './spec/services/incident_management/timeline_events/create_service_spec.rb'
-- './spec/services/incident_management/timeline_events/destroy_service_spec.rb'
-- './spec/services/incident_management/timeline_events/update_service_spec.rb'
- './spec/services/integrations/propagate_service_spec.rb'
- './spec/services/integrations/test/project_service_spec.rb'
- './spec/services/issuable/bulk_update_service_spec.rb'
@@ -10379,8 +10194,6 @@
- './spec/services/system_notes/commit_service_spec.rb'
- './spec/services/system_notes/design_management_service_spec.rb'
- './spec/services/system_note_service_spec.rb'
-- './spec/services/system_notes/incident_service_spec.rb'
-- './spec/services/system_notes/incidents_service_spec.rb'
- './spec/services/system_notes/issuables_service_spec.rb'
- './spec/services/system_notes/merge_requests_service_spec.rb'
- './spec/services/system_notes/time_tracking_service_spec.rb'
@@ -10599,7 +10412,6 @@
- './spec/uploaders/records_uploads_spec.rb'
- './spec/uploaders/terraform/state_uploader_spec.rb'
- './spec/uploaders/uploader_helper_spec.rb'
-- './spec/uploaders/workers/object_storage/background_move_worker_spec.rb'
- './spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb'
- './spec/validators/addressable_url_validator_spec.rb'
- './spec/validators/any_field_validator_spec.rb'
@@ -10945,10 +10757,6 @@
- './spec/workers/hashed_storage/project_rollback_worker_spec.rb'
- './spec/workers/hashed_storage/rollbacker_worker_spec.rb'
- './spec/workers/import_issues_csv_worker_spec.rb'
-- './spec/workers/incident_management/add_severity_system_note_worker_spec.rb'
-- './spec/workers/incident_management/close_incident_worker_spec.rb'
-- './spec/workers/incident_management/pager_duty/process_incident_worker_spec.rb'
-- './spec/workers/incident_management/process_alert_worker_v2_spec.rb'
- './spec/workers/integrations/create_external_cross_reference_worker_spec.rb'
- './spec/workers/integrations/execute_worker_spec.rb'
- './spec/workers/integrations/irker_worker_spec.rb'
diff --git a/spec/support/shared_contexts/disable_user_tracking.rb b/spec/support/shared_contexts/disable_user_tracking.rb
new file mode 100644
index 00000000000..e6689c41d4a
--- /dev/null
+++ b/spec/support/shared_contexts/disable_user_tracking.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'when user tracking is disabled' do
+ before do
+ # rubocop:disable RSpec/AnyInstanceOf
+ allow_any_instance_of(User).to receive(:update_tracked_fields!)
+ allow_any_instance_of(Users::ActivityService).to receive(:execute)
+ # rubocop:enable RSpec/AnyInstanceOf
+ end
+end
diff --git a/spec/support/shared_contexts/email_shared_context.rb b/spec/support/shared_contexts/email_shared_context.rb
index 086cdf50e9d..12d4af5170b 100644
--- a/spec/support/shared_contexts/email_shared_context.rb
+++ b/spec/support/shared_contexts/email_shared_context.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_context :email_shared_context do
+RSpec.shared_context 'email shared context' do
let(:mail_key) { '59d8df8370b7e95c5a49fbf86aeb2c93' }
let(:receiver) { Gitlab::Email::Receiver.new(email_raw) }
let(:markdown) { '![image](uploads/image.png)' }
@@ -27,7 +27,7 @@ def service_desk_fixture(path, slug: nil, key: 'mykey')
fixture_file(path).gsub('project_slug', slug).gsub('project_key', key)
end
-RSpec.shared_examples :reply_processing_shared_examples do
+RSpec.shared_examples 'reply processing shared examples' do
context 'when the user could not be found' do
before do
user.destroy!
@@ -49,7 +49,7 @@ RSpec.shared_examples :reply_processing_shared_examples do
end
end
-RSpec.shared_examples :checks_permissions_on_noteable_examples do
+RSpec.shared_examples 'checks permissions on noteable examples' do
context 'when user has access' do
before do
project.add_reporter(user)
@@ -67,7 +67,7 @@ RSpec.shared_examples :checks_permissions_on_noteable_examples do
end
end
-RSpec.shared_examples :note_handler_shared_examples do |forwardable|
+RSpec.shared_examples 'note handler shared examples' do |forwardable|
context 'when the noteable could not be found' do
before do
noteable.destroy!
@@ -157,7 +157,7 @@ RSpec.shared_examples :note_handler_shared_examples do |forwardable|
noteable.update_attribute(:discussion_locked, true)
end
- it_behaves_like :checks_permissions_on_noteable_examples
+ it_behaves_like 'checks permissions on noteable examples'
end
context 'when everything is fine' do
diff --git a/spec/support/shared_contexts/models/ci/job_token_scope.rb b/spec/support/shared_contexts/models/ci/job_token_scope.rb
new file mode 100644
index 00000000000..51f671b139d
--- /dev/null
+++ b/spec/support/shared_contexts/models/ci/job_token_scope.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'with scoped projects' do
+ let_it_be(:inbound_scoped_project) { create_scoped_project(source_project, direction: :inbound) }
+ let_it_be(:outbound_scoped_project) { create_scoped_project(source_project, direction: :outbound) }
+ let_it_be(:unscoped_project1) { create(:project) }
+ let_it_be(:unscoped_project2) { create(:project) }
+
+ let_it_be(:link_out_of_scope) { create(:ci_job_token_project_scope_link, target_project: unscoped_project1) }
+
+ def create_scoped_project(source_project, direction:)
+ create(:project).tap do |scoped_project|
+ create(
+ :ci_job_token_project_scope_link,
+ source_project: source_project,
+ target_project: scoped_project,
+ direction: direction
+ )
+ end
+ end
+end
diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
index a6226fe903b..f6ac98c7669 100644
--- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
@@ -14,7 +14,7 @@ RSpec.shared_context 'GroupPolicy context' do
%i[
read_group read_counts
read_label read_issue_board_list read_milestone read_issue_board
- ]
+ ]
end
let(:guest_permissions) do
@@ -22,32 +22,32 @@ RSpec.shared_context 'GroupPolicy context' do
read_label read_group upload_file read_namespace read_group_activity
read_group_issues read_group_boards read_group_labels read_group_milestones
read_group_merge_requests
- ]
+ ]
end
let(:reporter_permissions) do
%i[
- admin_label
- admin_milestone
- admin_issue_board
- read_container_image
- read_harbor_registry
- read_metrics_dashboard_annotation
- read_prometheus
- read_crm_contact
- read_crm_organization
- ]
+ admin_label
+ admin_milestone
+ admin_issue_board
+ read_container_image
+ read_harbor_registry
+ read_metrics_dashboard_annotation
+ read_prometheus
+ read_crm_contact
+ read_crm_organization
+ ]
end
let(:developer_permissions) do
%i[
- create_metrics_dashboard_annotation
- delete_metrics_dashboard_annotation
- update_metrics_dashboard_annotation
- create_custom_emoji
- create_package
- read_cluster
- ]
+ create_metrics_dashboard_annotation
+ delete_metrics_dashboard_annotation
+ update_metrics_dashboard_annotation
+ create_custom_emoji
+ create_package
+ read_cluster
+ ]
end
let(:maintainer_permissions) do
diff --git a/spec/support/shared_contexts/rack_attack_shared_context.rb b/spec/support/shared_contexts/rack_attack_shared_context.rb
index e7b2ee76c3c..12625ead72b 100644
--- a/spec/support/shared_contexts/rack_attack_shared_context.rb
+++ b/spec/support/shared_contexts/rack_attack_shared_context.rb
@@ -6,7 +6,7 @@ RSpec.shared_context 'rack attack cache store' do
Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
# Make time-dependent tests deterministic
- Timecop.freeze { example.run }
+ freeze_time { example.run }
Rack::Attack.cache.store = Rails.cache
end
diff --git a/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb b/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb
index 3974338238a..7c37e5189f1 100644
--- a/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb
+++ b/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb
@@ -28,6 +28,10 @@ RSpec.shared_context 'conan api setup' do
)
end
+ let(:snowplow_gitlab_standard_context) do
+ { user: user, project: project, namespace: project.namespace, property: 'i_package_conan_user' }
+ end
+
before do
project.add_developer(user)
allow(Settings).to receive(:attr_encrypted_db_key_base).and_return(base_secret)
diff --git a/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb b/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb
index 89f290d8d68..1e50505162d 100644
--- a/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb
+++ b/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb
@@ -17,6 +17,7 @@ RSpec.shared_context 'npm api setup' do
let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) }
let(:package_name) { package.name }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_npm_user' } }
before do
# create a duplicated package without triggering model validation errors
diff --git a/spec/support/shared_contexts/rubocop_default_rspec_language_config_context.rb b/spec/support/shared_contexts/rubocop_default_rspec_language_config_context.rb
deleted file mode 100644
index a207c6ae9d1..00000000000
--- a/spec/support/shared_contexts/rubocop_default_rspec_language_config_context.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-# From https://github.com/rubocop/rubocop-rspec/blob/master/spec/shared/default_rspec_language_config_context.rb
-# This can be removed once we have https://github.com/rubocop/rubocop-rspec/pull/1377
-
-RSpec.shared_context 'with default RSpec/Language config' do
- include_context 'config'
-
- # Deep duplication is needed to prevent config leakage between examples
- let(:other_cops) do
- default_language = RuboCop::ConfigLoader
- .default_configuration['RSpec']['Language']
- default_include = RuboCop::ConfigLoader
- .default_configuration['RSpec']['Include']
- { 'RSpec' =>
- {
- 'Include' => default_include,
- 'Language' => deep_dup(default_language)
- } }
- end
-
- def deep_dup(object)
- case object
- when Array
- object.map { |item| deep_dup(item) }
- when Hash
- object.transform_values { |value| deep_dup(value) }
- else
- object # only collections undergo modifications and need duping
- end
- end
-end
diff --git a/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb b/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb
index e26b8cd8b37..7db479bcfd2 100644
--- a/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb
+++ b/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb
@@ -23,7 +23,7 @@ RSpec.shared_context 'container repository delete tags service shared context' d
end
def stub_delete_reference_requests(tags)
- tags = Array.wrap(tags).to_h { |tag| [tag, 200] } unless tags.is_a?(Hash)
+ tags = Array.wrap(tags).index_with { 200 } unless tags.is_a?(Hash)
tags.each do |tag, status|
stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/tags/reference/#{tag}")
diff --git a/spec/support/shared_examples/boards/destroy_service_shared_examples.rb b/spec/support/shared_examples/boards/destroy_service_shared_examples.rb
index b1cb58a736f..578ed0e6260 100644
--- a/spec/support/shared_examples/boards/destroy_service_shared_examples.rb
+++ b/spec/support/shared_examples/boards/destroy_service_shared_examples.rb
@@ -15,7 +15,7 @@ RSpec.shared_examples 'board destroy service' do
expect do
expect(service.execute(board)).to be_success
- end.to change(boards, :count).by(-1)
+ end.to change { boards.count }.by(-1)
end
end
@@ -23,7 +23,7 @@ RSpec.shared_examples 'board destroy service' do
it 'does remove board' do
expect do
service.execute(board)
- end.to change(boards, :count).by(-1)
+ end.to change { boards.count }.by(-1)
end
end
end
diff --git a/spec/support/shared_examples/ci/log_downstream_pipeline_shared_examples.rb b/spec/support/shared_examples/ci/log_downstream_pipeline_shared_examples.rb
index db724dcfe99..0c3b9ec4151 100644
--- a/spec/support/shared_examples/ci/log_downstream_pipeline_shared_examples.rb
+++ b/spec/support/shared_examples/ci/log_downstream_pipeline_shared_examples.rb
@@ -13,10 +13,8 @@ RSpec.shared_examples 'logs downstream pipeline creation' do
end
it 'logs details' do
- pipeline = nil
-
log_entry = record_downstream_pipeline_logs do
- pipeline = subject
+ downstream_pipeline
end
expect(log_entry).to be_present
@@ -24,7 +22,7 @@ RSpec.shared_examples 'logs downstream pipeline creation' do
message: "downstream pipeline created",
class: described_class.name,
root_pipeline_id: expected_root_pipeline.id,
- downstream_pipeline_id: pipeline.id,
+ downstream_pipeline_id: downstream_pipeline.id,
downstream_pipeline_relationship: expected_downstream_relationship,
hierarchy_size: expected_hierarchy_size,
root_pipeline_plan: expected_root_pipeline.project.actual_plan_name,
diff --git a/spec/support/shared_examples/ci/retryable_shared_examples.rb b/spec/support/shared_examples/ci/retryable_shared_examples.rb
index 4622dbe4e31..dc34ea8bb3c 100644
--- a/spec/support/shared_examples/ci/retryable_shared_examples.rb
+++ b/spec/support/shared_examples/ci/retryable_shared_examples.rb
@@ -10,7 +10,7 @@ RSpec.shared_examples 'a retryable job' do
describe '#set_enqueue_immediately!' do
it 'changes #enqueue_immediately? to true' do
expect { subject.set_enqueue_immediately! }
- .to change(subject, :enqueue_immediately?).from(false).to(true)
+ .to change { subject.enqueue_immediately? }.from(false).to(true)
end
end
end
diff --git a/spec/support/shared_examples/ci/stuck_builds_shared_examples.rb b/spec/support/shared_examples/ci/stuck_builds_shared_examples.rb
index 4fcea18393c..dd0a57c6b6d 100644
--- a/spec/support/shared_examples/ci/stuck_builds_shared_examples.rb
+++ b/spec/support/shared_examples/ci/stuck_builds_shared_examples.rb
@@ -30,6 +30,6 @@ end
RSpec.shared_examples 'job is unchanged' do
it 'does not change status' do
- expect { service.execute }.not_to change(job, :status)
+ expect { service.execute }.not_to change { job.status }
end
end
diff --git a/spec/support/shared_examples/controllers/destroy_hook_shared_examples.rb b/spec/support/shared_examples/controllers/destroy_hook_shared_examples.rb
index 710aa333dec..420973b6882 100644
--- a/spec/support/shared_examples/controllers/destroy_hook_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/destroy_hook_shared_examples.rb
@@ -9,7 +9,7 @@ RSpec.shared_examples 'Web hook destroyer' do
delete :destroy, params: params
expect(response).to have_gitlab_http_status(:found)
- expect(flash[:notice]).to eq("#{hook.model_name.human} was deleted")
+ expect(flash[:notice]).to eq('Webhook was deleted')
end
it 'displays a message about async delete', :aggregate_failures do
@@ -20,7 +20,7 @@ RSpec.shared_examples 'Web hook destroyer' do
delete :destroy, params: params
expect(response).to have_gitlab_http_status(:found)
- expect(flash[:notice]).to eq("#{hook.model_name.human} was scheduled for deletion")
+ expect(flash[:notice]).to eq('Webhook was scheduled for deletion')
end
it 'displays an error if deletion failed', :aggregate_failures do
diff --git a/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb b/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb
index bbbe93a644f..5506b05ca55 100644
--- a/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb
@@ -356,7 +356,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).and_return(double(execute: project))
- expect { post :create, params: { target_namespace: provider_repo[:name] }, format: :json }.to change(Namespace, :count).by(1)
+ expect { post :create, params: { target_namespace: provider_repo[:name] }, format: :json }.to change { Namespace.count }.by(1)
end
it "takes the new namespace" do
@@ -377,7 +377,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).and_return(double(execute: project))
- expect { post :create, format: :json }.not_to change(Namespace, :count)
+ expect { post :create, format: :json }.not_to change { Namespace.count }
end
it "takes the current user's namespace" do
diff --git a/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb b/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb
index 6749ebd471f..7e99066110d 100644
--- a/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb
+++ b/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb
@@ -16,12 +16,14 @@
RSpec.shared_examples 'Snowplow event tracking' do |overrides: {}|
let(:extra) { {} }
- it 'is not emitted if FF is disabled' do
- stub_feature_flags(feature_flag_name => false)
+ if try(:feature_flag_name)
+ it 'is not emitted if FF is disabled' do
+ stub_feature_flags(feature_flag_name => false)
- subject
+ subject
- expect_no_snowplow_event(category: category, action: action)
+ expect_no_snowplow_event(category: category, action: action)
+ end
end
it 'is emitted' do
diff --git a/spec/support/shared_examples/controllers/variables_shared_examples.rb b/spec/support/shared_examples/controllers/variables_shared_examples.rb
index 34632993cf0..d979683cce7 100644
--- a/spec/support/shared_examples/controllers/variables_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/variables_shared_examples.rb
@@ -19,13 +19,15 @@ RSpec.shared_examples 'PATCH #update updates variables' do
{ id: variable.id,
key: variable.key,
secret_value: variable.value,
- protected: variable.protected?.to_s }
+ protected: variable.protected?.to_s,
+ raw: (!variable.raw?).to_s }
end
let(:new_variable_attributes) do
{ key: 'new_key',
secret_value: 'dummy_value',
- protected: 'false' }
+ protected: 'false',
+ raw: 'true' }
end
let(:variables_scope) { owner.variables }
@@ -86,7 +88,13 @@ RSpec.shared_examples 'PATCH #update updates variables' do
end
it 'updates the existing variable' do
- expect { subject }.to change { variable.reload.value }.to('other_value')
+ old_raw = variable.raw
+
+ subject
+
+ variable.reload
+ expect(variable.value).to eq('other_value')
+ expect(variable.raw?).not_to be(old_raw)
end
it 'creates the new variable' do
diff --git a/spec/support/shared_examples/csp.rb b/spec/support/shared_examples/csp.rb
index 91242ae9f37..725d0a832c2 100644
--- a/spec/support/shared_examples/csp.rb
+++ b/spec/support/shared_examples/csp.rb
@@ -29,7 +29,8 @@ RSpec.shared_examples 'setting CSP' do |rule_name|
context 'when feature is enabled' do
it "appends to #{rule_name}" do
- is_expected.to eql("#{rule_name} #{default_csp_values} #{allowlisted_url}")
+ is_expected.to include("#{rule_name} #{default_csp_values}")
+ is_expected.to include(allowlisted_url)
end
end
@@ -37,7 +38,7 @@ RSpec.shared_examples 'setting CSP' do |rule_name|
include_context 'disable feature'
it "keeps original #{rule_name}" do
- is_expected.to eql("#{rule_name} #{default_csp_values}")
+ is_expected.to include("#{rule_name} #{default_csp_values}")
end
end
end
@@ -47,7 +48,8 @@ RSpec.shared_examples 'setting CSP' do |rule_name|
context 'when feature is enabled' do
it "uses default-src values in #{rule_name}" do
- is_expected.to eql("default-src #{default_csp_values}; #{rule_name} #{default_csp_values} #{allowlisted_url}")
+ is_expected.to include("default-src #{default_csp_values}")
+ is_expected.to include(allowlisted_url)
end
end
@@ -55,7 +57,7 @@ RSpec.shared_examples 'setting CSP' do |rule_name|
include_context 'disable feature'
it "does not add #{rule_name}" do
- is_expected.to eql("default-src #{default_csp_values}")
+ is_expected.to include("default-src #{default_csp_values}")
end
end
end
@@ -65,7 +67,8 @@ RSpec.shared_examples 'setting CSP' do |rule_name|
context 'when feature is enabled' do
it "uses default-src values in #{rule_name}" do
- is_expected.to eql("font-src #{default_csp_values}; #{rule_name} #{allowlisted_url}")
+ is_expected.to include("font-src #{default_csp_values}")
+ is_expected.not_to include("#{rule_name} #{default_csp_values}")
end
end
@@ -73,7 +76,7 @@ RSpec.shared_examples 'setting CSP' do |rule_name|
include_context 'disable feature'
it "does not add #{rule_name}" do
- is_expected.to eql("font-src #{default_csp_values}")
+ is_expected.to include("font-src #{default_csp_values}")
end
end
end
diff --git a/spec/support/shared_examples/features/container_registry_shared_examples.rb b/spec/support/shared_examples/features/container_registry_shared_examples.rb
index 784f82fdda1..06b2b8c621c 100644
--- a/spec/support/shared_examples/features/container_registry_shared_examples.rb
+++ b/spec/support/shared_examples/features/container_registry_shared_examples.rb
@@ -7,19 +7,3 @@ RSpec.shared_examples 'handling feature network errors with the container regist
expect(page).to have_content 'We are having trouble connecting to the Container Registry'
end
end
-
-RSpec.shared_examples 'rejecting tags destruction for an importing repository on' do |tags: []|
- it 'rejects the tag destruction operation' do
- service = instance_double('Projects::ContainerRepository::DeleteTagsService')
- expect(service).to receive(:execute).with(container_repository) { { status: :error, message: 'repository importing' } }
- expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(container_repository.project, user, tags: tags) { service }
-
- first('[data-testid="additional-actions"]').click
- first('[data-testid="single-delete-button"]').click
- expect(find('.modal .modal-title')).to have_content _('Remove tag')
- find('.modal .modal-footer .btn-danger').click
-
- expect(page).to have_content('Tags temporarily cannot be marked for deletion. Please try again in a few minutes.')
- expect(page).to have_link('More details', href: help_page_path('user/packages/container_registry/index', anchor: 'tags-temporarily-cannot-be-marked-for-deletion'))
- end
-end
diff --git a/spec/support/shared_examples/features/content_editor_shared_examples.rb b/spec/support/shared_examples/features/content_editor_shared_examples.rb
index f01e3c88dad..efdf7513b2d 100644
--- a/spec/support/shared_examples/features/content_editor_shared_examples.rb
+++ b/spec/support/shared_examples/features/content_editor_shared_examples.rb
@@ -327,7 +327,7 @@ RSpec.shared_examples 'edits content using the content editor' do
end
def dropdown_scroll_top
- evaluate_script("document.querySelector('#{suggestions_dropdown} .gl-new-dropdown-inner').scrollTop")
+ evaluate_script("document.querySelector('#{suggestions_dropdown} .gl-dropdown-inner').scrollTop")
end
end
end
diff --git a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
index 2cfe353d5d7..7f31ea8f9be 100644
--- a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
@@ -64,7 +64,7 @@ RSpec.shared_examples 'a creatable merge request' do
visit project_new_merge_request_path(source_project)
first('.js-target-project').click
- find('.dropdown-target-project .dropdown-content a', text: target_project.full_path).click
+ find('.dropdown-target-project li', text: target_project.full_path).click
wait_for_requests
diff --git a/spec/support/shared_examples/features/discussion_comments_shared_example.rb b/spec/support/shared_examples/features/discussion_comments_shared_example.rb
index 68c0d06e7d0..d6f1efc09fc 100644
--- a/spec/support/shared_examples/features/discussion_comments_shared_example.rb
+++ b/spec/support/shared_examples/features/discussion_comments_shared_example.rb
@@ -19,6 +19,8 @@ RSpec.shared_examples 'thread comments for commit and snippet' do |resource_name
find('.js-comment-button').click
+ wait_for_all_requests
+
expect(page).to have_content(comment)
new_comment = all(comments_selector).last
@@ -301,7 +303,7 @@ RSpec.shared_examples 'thread comments for issue, epic and merge request' do |re
if resource_name == 'merge request'
let(:note_id) { find("#{comments_selector} .note:first-child", match: :first)['data-note-id'] }
- let(:reply_id) { find("#{comments_selector} .note:last-of-type", match: :first)['data-note-id'] }
+ let(:reply_id) { all("#{comments_selector} [data-note-id]")[1]['data-note-id'] }
it 'can be replied to after resolving' do
find('button[data-testid="resolve-discussion-button"]').click
diff --git a/spec/support/shared_examples/features/inviting_members_shared_examples.rb b/spec/support/shared_examples/features/inviting_members_shared_examples.rb
index 277ec6a7fa7..2eca2a72997 100644
--- a/spec/support/shared_examples/features/inviting_members_shared_examples.rb
+++ b/spec/support/shared_examples/features/inviting_members_shared_examples.rb
@@ -81,7 +81,7 @@ RSpec.shared_examples 'inviting members' do |snowplow_invite_label|
invite_member(user2.name, role: 'Developer')
- invite_member(user2.name, role: 'Reporter', refresh: false)
+ invite_member(user2.name, role: 'Reporter')
expect(page).not_to have_selector(invite_modal_selector)
@@ -101,7 +101,7 @@ RSpec.shared_examples 'inviting members' do |snowplow_invite_label|
invite_member(email, role: 'Developer')
- invite_member(email, role: 'Reporter', refresh: false)
+ invite_member(email, role: 'Reporter')
expect(page).not_to have_selector(invite_modal_selector)
@@ -127,7 +127,7 @@ RSpec.shared_examples 'inviting members' do |snowplow_invite_label|
it 'adds the user as a member on sub-entity with higher access level', :js do
visit subentity_members_page_path
- invite_member(user2.name, role: role, refresh: false)
+ invite_member(user2.name, role: role)
expect(page).not_to have_selector(invite_modal_selector)
@@ -145,7 +145,7 @@ RSpec.shared_examples 'inviting members' do |snowplow_invite_label|
it 'fails with an error', :js do
visit subentity_members_page_path
- invite_member(user2.name, role: role, refresh: false)
+ invite_member(user2.name, role: role)
invite_modal = page.find(invite_modal_selector)
expect(invite_modal).to have_content "#{user2.name}: Access level should be greater than or equal to " \
@@ -177,7 +177,7 @@ RSpec.shared_examples 'inviting members' do |snowplow_invite_label|
visit subentity_members_page_path
- invite_member([user2.name, user3.name, user4.name, user6.name, user7.name], role: role, refresh: false)
+ invite_member([user2.name, user3.name, user4.name, user6.name, user7.name], role: role)
# we have more than 2 errors, so one will be hidden
invite_modal = page.find(invite_modal_selector)
@@ -266,7 +266,7 @@ RSpec.shared_examples 'inviting members' do |snowplow_invite_label|
it 'only shows the error for an invalid formatted email and does not display other member errors', :js do
visit subentity_members_page_path
- invite_member([user2.name, user3.name, 'bad@email'], role: role, refresh: false)
+ invite_member([user2.name, user3.name, 'bad@email'], role: role)
invite_modal = page.find(invite_modal_selector)
expect(invite_modal).to have_text('email contains an invalid email address')
diff --git a/spec/support/shared_examples/features/reportable_note_shared_examples.rb b/spec/support/shared_examples/features/reportable_note_shared_examples.rb
index 288e1df9b2a..c35f711111b 100644
--- a/spec/support/shared_examples/features/reportable_note_shared_examples.rb
+++ b/spec/support/shared_examples/features/reportable_note_shared_examples.rb
@@ -20,7 +20,7 @@ RSpec.shared_examples 'reportable note' do |type|
dropdown = comment.find(more_actions_selector)
open_dropdown(dropdown)
- expect(dropdown).to have_link('Report abuse to admin', href: abuse_report_path)
+ expect(dropdown).to have_link('Report abuse to administrator', href: abuse_report_path)
if type == 'issue' || type == 'merge_request'
expect(dropdown).to have_button('Delete comment')
@@ -33,7 +33,7 @@ RSpec.shared_examples 'reportable note' do |type|
dropdown = comment.find(more_actions_selector)
open_dropdown(dropdown)
- dropdown.click_link('Report abuse to admin')
+ dropdown.click_link('Report abuse to administrator')
expect(find('#user_name')['value']).to match(note.author.username)
expect(find('#abuse_report_message')['value']).to match(noteable_note_url(note))
diff --git a/spec/support/shared_examples/features/runners_shared_examples.rb b/spec/support/shared_examples/features/runners_shared_examples.rb
index a7bc19da45f..20078243cfb 100644
--- a/spec/support/shared_examples/features/runners_shared_examples.rb
+++ b/spec/support/shared_examples/features/runners_shared_examples.rb
@@ -63,10 +63,13 @@ RSpec.shared_examples 'shows and resets runner registration token' do
end
RSpec.shared_examples 'shows no runners registered' do
- it 'shows counts with 0' do
- expect(page).to have_text "#{s_('Runners|Online')} 0"
- expect(page).to have_text "#{s_('Runners|Offline')} 0"
- expect(page).to have_text "#{s_('Runners|Stale')} 0"
+ it 'shows total count with 0' do
+ expect(find('[data-testid="runner-type-tabs"]')).to have_text "#{s_('Runners|All')} 0"
+
+ # No stats are shown
+ expect(page).not_to have_text s_('Runners|Online')
+ expect(page).not_to have_text s_('Runners|Offline')
+ expect(page).not_to have_text s_('Runners|Stale')
end
it 'shows "no runners" message' do
diff --git a/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb b/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb
index cc74c977064..b73f40ff28c 100644
--- a/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb
+++ b/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb
@@ -6,7 +6,7 @@ RSpec.shared_examples 'search timeouts' do |scope|
context 'when search times out' do
before do
allow_next_instance_of(SearchService) do |service|
- allow(service).to receive(:search_objects).and_raise(ActiveRecord::QueryCanceled)
+ allow(service).to receive(:search_results).and_raise(ActiveRecord::QueryCanceled)
end
visit(search_path(search: 'test', scope: scope, **additional_params))
diff --git a/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb b/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb
index 281a70e46c4..95b306fdaaa 100644
--- a/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb
+++ b/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb
@@ -17,7 +17,7 @@ RSpec.shared_examples 'labels sidebar widget' do
end
it 'shows labels list in the dropdown' do
- expect(labels_widget.find('.gl-new-dropdown-contents')).to have_selector('li.gl-new-dropdown-item', count: 4)
+ expect(labels_widget.find('.gl-dropdown-contents')).to have_selector('li.gl-dropdown-item', count: 4)
end
it 'adds a label' do
@@ -57,8 +57,8 @@ RSpec.shared_examples 'labels sidebar widget' do
expect(page).to have_css('.labels-fetch-loading')
wait_for_all_requests
- expect(page).to have_css('[data-testid="dropdown-content"] .gl-new-dropdown-item')
- expect(page.all(:css, '[data-testid="dropdown-content"] .gl-new-dropdown-item').length).to eq(1)
+ expect(page).to have_css('[data-testid="dropdown-content"] .gl-dropdown-item')
+ expect(page.all(:css, '[data-testid="dropdown-content"] .gl-dropdown-item').length).to eq(1)
find_field('Search').native.send_keys(:enter)
click_button 'Close'
diff --git a/spec/support/shared_examples/features/sidebar/sidebar_milestone_shared_examples.rb b/spec/support/shared_examples/features/sidebar/sidebar_milestone_shared_examples.rb
index da730240e8e..f8982c242f8 100644
--- a/spec/support/shared_examples/features/sidebar/sidebar_milestone_shared_examples.rb
+++ b/spec/support/shared_examples/features/sidebar/sidebar_milestone_shared_examples.rb
@@ -20,15 +20,15 @@ RSpec.shared_examples 'milestone sidebar widget' do
it 'shows milestones list in the dropdown' do
# 5 milestones + "No milestone" = 6 items
- expect(milestone_widget.find('.gl-new-dropdown-contents')).to have_selector('li.gl-new-dropdown-item', count: 6)
+ expect(milestone_widget.find('.gl-dropdown-contents')).to have_selector('li.gl-dropdown-item', count: 6)
end
it 'shows expired milestone at the bottom of the list and milestone due earliest at the top of the list', :aggregate_failures do
- within(milestone_widget, '.gl-new-dropdown-contents') do
+ within(milestone_widget, '.gl-dropdown-contents') do
expect(page.find('li:last-child')).to have_content milestone_expired.title
[milestone3, milestone2, milestone1, milestone_no_duedate].each_with_index do |m, i|
- expect(page.all('li.gl-new-dropdown-item')[i + 1]).to have_content m.title
+ expect(page.all('li.gl-dropdown-item')[i + 1]).to have_content m.title
end
end
end
diff --git a/spec/support/shared_examples/features/wiki/file_attachments_shared_examples.rb b/spec/support/shared_examples/features/wiki/file_attachments_shared_examples.rb
index 8d1502bed84..7a3b94ad81d 100644
--- a/spec/support/shared_examples/features/wiki/file_attachments_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/file_attachments_shared_examples.rb
@@ -55,7 +55,7 @@ RSpec.shared_examples 'wiki file attachments' do
wait_for_requests
expect(page.find('#wiki_content').value)
- .to match(%r{\!\[dk\]\(uploads/\h{32}/dk\.png\)$})
+ .to match(%r{!\[dk\]\(uploads/\h{32}/dk\.png\)$})
end
it 'the links point to the wiki root url' do
diff --git a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb
index 8b3a344a841..9d1f05d5543 100644
--- a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb
+++ b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb
@@ -109,20 +109,77 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
end
context 'filtering by release' do
- context 'when the release tag is none' do
+ context 'when filter by none' do
let(:params) { { release_tag: 'none' } }
it 'returns items without releases' do
expect(items).to contain_exactly(item2, item3, item4, item5)
end
+
+ context 'when sort by milestone' do
+ let(:params) { { release_tag: 'none', sort: 'milestone_due_desc' } }
+
+ it 'returns items without any releases' do
+ expect(items).to contain_exactly(item2, item3, item4, item5)
+ end
+ end
+ end
+
+ context 'when filter by any' do
+ let(:params) { { release_tag: 'any' } }
+
+ it 'returns items with any releases' do
+ expect(items).to contain_exactly(item1)
+ end
+
+ context 'when sort by milestone' do
+ let(:params) { { release_tag: 'any', sort: 'milestone_due_desc' } }
+
+ it 'returns items without any releases' do
+ expect(items).to contain_exactly(item1)
+ end
+ end
end
- context 'when the release tag exists' do
+ context 'when filter by a release_tag' do
let(:params) { { project_id: project1.id, release_tag: release.tag } }
- it 'returns the items associated with that release' do
+ it 'returns the items associated with the release tag' do
expect(items).to contain_exactly(item1)
end
+
+ context 'when sort by milestone' do
+ let(:params) { { project_id: project1.id, release_tag: release.tag, sort: 'milestone_due_desc' } }
+
+ it 'returns the items associated with the release tag' do
+ expect(items).to contain_exactly(item1)
+ end
+ end
+ end
+
+ context 'when filter by a negated release_tag' do
+ let_it_be(:another_release) { create(:release, project: project1, tag: 'v2.0.0') }
+ let_it_be(:another_milestone) { create(:milestone, project: project1, releases: [another_release]) }
+ let_it_be(:another_item) do
+ create(factory,
+ project: project1,
+ milestone: another_milestone,
+ title: 'another item')
+ end
+
+ let(:params) { { not: { release_tag: release.tag, project_id: project1.id } } }
+
+ it 'returns the items not associated with the release' do
+ expect(items).to contain_exactly(another_item)
+ end
+
+ context 'when sort by milestone' do
+ let(:params) { { not: { release_tag: release.tag, project_id: project1.id }, sort: 'milestone_due_desc' } }
+
+ it 'returns the items not associated with the release' do
+ expect(items).to contain_exactly(another_item)
+ end
+ end
end
end
@@ -864,12 +921,15 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
context 'filtering by item type' do
let_it_be(:incident_item) { create(factory, issue_type: :incident, project: project1) }
+ let_it_be(:objective) { create(factory, issue_type: :objective, project: project1) }
+ let_it_be(:key_result) { create(factory, issue_type: :key_result, project: project1) }
context 'no type given' do
let(:params) { { issue_types: [] } }
it 'returns all items' do
- expect(items).to contain_exactly(incident_item, item1, item2, item3, item4, item5)
+ expect(items)
+ .to contain_exactly(incident_item, item1, item2, item3, item4, item5, objective, key_result)
end
end
@@ -881,6 +941,22 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
end
end
+ context 'objective type' do
+ let(:params) { { issue_types: ['objective'] } }
+
+ it 'returns incident items' do
+ expect(items).to contain_exactly(objective)
+ end
+ end
+
+ context 'key_result type' do
+ let(:params) { { issue_types: ['key_result'] } }
+
+ it 'returns incident items' do
+ expect(items).to contain_exactly(key_result)
+ end
+ end
+
context 'item type' do
let(:params) { { issue_types: ['issue'] } }
diff --git a/spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb b/spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb
index 601a53ed913..f00d6e776ec 100644
--- a/spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb
+++ b/spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb
@@ -237,8 +237,8 @@ RSpec.shared_examples 'snippet visibility' do
if project.private?
project.add_developer(external) unless member
- else
- member.delete if member
+ elsif member
+ member.delete
end
end
end
diff --git a/spec/support/shared_examples/graphql/label_fields.rb b/spec/support/shared_examples/graphql/label_fields.rb
index 4159e4e03ab..030a2feafcd 100644
--- a/spec/support/shared_examples/graphql/label_fields.rb
+++ b/spec/support/shared_examples/graphql/label_fields.rb
@@ -42,9 +42,7 @@ RSpec.shared_examples 'querying a GraphQL type with labels' do
make_query(
[
query_graphql_field(:label, label_params, all_graphql_fields_for(Label)),
- query_graphql_field(:labels, labels_params, [
- query_graphql_field(:nodes, nil, all_graphql_fields_for(Label))
- ])
+ query_graphql_field(:labels, labels_params, [query_graphql_field(:nodes, nil, all_graphql_fields_for(Label))])
]
)
end
diff --git a/spec/support/shared_examples/graphql/mutations/incident_management_timeline_events_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/incident_management_timeline_events_shared_examples.rb
index cd591248ff6..030aca5bd1c 100644
--- a/spec/support/shared_examples/graphql/mutations/incident_management_timeline_events_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/incident_management_timeline_events_shared_examples.rb
@@ -7,7 +7,7 @@ require 'spec_helper'
# * Defined expected timeline event via `let(:expected_timeline_event) { instance_double(...) }`
RSpec.shared_examples 'creating an incident timeline event' do
it 'creates a timeline event' do
- expect { resolve }.to change(IncidentManagement::TimelineEvent, :count).by(1)
+ expect { resolve }.to change { IncidentManagement::TimelineEvent.count }.by(1)
end
it 'responds with a timeline event', :aggregate_failures do
diff --git a/spec/support/shared_examples/graphql/mutations/timelogs/create_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/timelogs/create_shared_examples.rb
index f28348fb945..c6402a89f02 100644
--- a/spec/support/shared_examples/graphql/mutations/timelogs/create_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/timelogs/create_shared_examples.rb
@@ -1,6 +1,17 @@
# frozen_string_literal: true
RSpec.shared_examples 'issuable supports timelog creation mutation' do
+ let(:mutation_response) { graphql_mutation_response(:timelog_create) }
+ let(:mutation) do
+ variables = {
+ 'time_spent' => time_spent,
+ 'spent_at' => '2022-11-16T12:59:35+0100',
+ 'summary' => 'Test summary',
+ 'issuable_id' => issuable.to_global_id.to_s
+ }
+ graphql_mutation(:timelogCreate, variables)
+ end
+
context 'when the user is anonymous' do
before do
post_graphql_mutation(mutation, current_user: current_user)
@@ -32,13 +43,14 @@ RSpec.shared_examples 'issuable supports timelog creation mutation' do
it 'creates the timelog' do
expect do
post_graphql_mutation(mutation, current_user: current_user)
- end.to change(Timelog, :count).by(1)
+ end.to change { Timelog.count }.by(1)
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response['errors']).to be_empty
expect(mutation_response['timelog']).to include(
'timeSpent' => 3600,
- 'spentAt' => '2022-07-08T00:00:00Z',
+ # This also checks that the ISO time was converted to UTC
+ 'spentAt' => '2022-11-16T11:59:35Z',
'summary' => 'Test summary'
)
end
@@ -50,10 +62,11 @@ RSpec.shared_examples 'issuable supports timelog creation mutation' do
it 'returns an error' do
expect do
post_graphql_mutation(mutation, current_user: current_user)
- end.to change(Timelog, :count).by(0)
+ end.to change { Timelog.count }.by(0)
expect(response).to have_gitlab_http_status(:success)
- expect(mutation_response['errors']).to match_array(['Time spent can\'t be blank'])
+ expect(mutation_response['errors']).to match_array(
+ ['Time spent must be formatted correctly. For example: 1h 30m.'])
expect(mutation_response['timelog']).to be_nil
end
end
@@ -61,6 +74,17 @@ RSpec.shared_examples 'issuable supports timelog creation mutation' do
end
RSpec.shared_examples 'issuable does not support timelog creation mutation' do
+ let(:mutation_response) { graphql_mutation_response(:timelog_create) }
+ let(:mutation) do
+ variables = {
+ 'time_spent' => time_spent,
+ 'spent_at' => '2022-11-16T12:59:35+0100',
+ 'summary' => 'Test summary',
+ 'issuable_id' => issuable.to_global_id.to_s
+ }
+ graphql_mutation(:timelogCreate, variables)
+ end
+
context 'when the user is anonymous' do
before do
post_graphql_mutation(mutation, current_user: current_user)
diff --git a/spec/support/shared_examples/graphql/mutations/work_items/update_description_widget_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/work_items/update_description_widget_shared_examples.rb
index 56c2ca22e15..f672ec7f5ac 100644
--- a/spec/support/shared_examples/graphql/mutations/work_items/update_description_widget_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/work_items/update_description_widget_shared_examples.rb
@@ -5,7 +5,7 @@ RSpec.shared_examples 'update work item description widget' do
expect do
post_graphql_mutation(mutation, current_user: current_user)
work_item.reload
- end.to change(work_item, :description).from(nil).to(new_description)
+ end.to change { work_item.description }.from(nil).to(new_description)
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response['workItem']['widgets']).to include(
diff --git a/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb b/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb
index bdd4dbfe209..3ff93371c19 100644
--- a/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb
@@ -20,7 +20,7 @@ RSpec.shared_examples 'a Note mutation when the user does not have permission' d
it_behaves_like 'a Note mutation that does not create a Note'
it_behaves_like 'a mutation that returns top-level errors',
- errors: ['The resource that you are attempting to access does not exist or you don\'t have permission to perform this action']
+ errors: ['The resource that you are attempting to access does not exist or you don\'t have permission to perform this action']
end
RSpec.shared_examples 'a Note mutation when there are active record validation errors' do |model: Note|
@@ -74,7 +74,7 @@ RSpec.shared_examples 'a Note mutation when there are rate limit validation erro
it_behaves_like 'a Note mutation that does not create a Note'
it_behaves_like 'a mutation that returns top-level errors',
- errors: ['This endpoint has been requested too many times. Try again later.']
+ errors: ['This endpoint has been requested too many times. Try again later.']
context 'when the user is in the allowlist' do
before do
@@ -97,3 +97,55 @@ RSpec.shared_examples 'a Note mutation with confidential notes' do
expect(mutation_response['note']['internal']).to eq(true)
end
end
+
+RSpec.shared_examples 'a Note mutation updates a note successfully' do
+ it 'updates the Note' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(note.reload.note).to eq(updated_body)
+ end
+
+ it 'returns the updated Note' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['note']['body']).to eq(updated_body)
+ end
+end
+
+RSpec.shared_examples 'a Note mutation update with errors' do
+ context 'when there are ActiveRecord validation errors' do
+ let(:params) { { body: '', confidential: true } }
+
+ it_behaves_like 'a mutation that returns errors in the response',
+ errors: ["Note can't be blank", 'Confidential can not be changed for existing notes']
+
+ it 'does not update the Note' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(note.reload.note).to eq(original_body)
+ expect(note.confidential).to be_falsey
+ end
+
+ it 'returns the original Note' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['note']['body']).to eq(original_body)
+ expect(mutation_response['note']['confidential']).to be_falsey
+ end
+ end
+end
+
+RSpec.shared_examples 'a Note mutation update only with quick actions' do
+ context 'when body only contains quick actions' do
+ let(:updated_body) { '/close' }
+
+ it 'returns a nil note and empty errors' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response).to include(
+ 'errors' => [],
+ 'note' => nil
+ )
+ end
+ end
+end
diff --git a/spec/support/shared_examples/initializers/uses_gitlab_url_blocker_shared_examples.rb b/spec/support/shared_examples/initializers/uses_gitlab_url_blocker_shared_examples.rb
index afa495fc9a4..553e9f10b0d 100644
--- a/spec/support/shared_examples/initializers/uses_gitlab_url_blocker_shared_examples.rb
+++ b/spec/support/shared_examples/initializers/uses_gitlab_url_blocker_shared_examples.rb
@@ -152,4 +152,11 @@ RSpec.shared_examples 'a request using Gitlab::UrlBlocker' do
expect(request_stub).to have_been_requested
end
end
+
+ context 'when a non HTTP/HTTPS URL is provided' do
+ it 'raises an error' do
+ expect { make_request('ssh://example.com') }
+ .to raise_error(ArgumentError)
+ end
+ end
end
diff --git a/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_shared_examples.rb
index 459d4f5cd3e..7141492bd49 100644
--- a/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_shared_examples.rb
@@ -16,20 +16,20 @@ RSpec.shared_examples 'backfill migration for project repositories' do |storage|
projects.create!(name: "foo-#{index}", path: "foo-#{index}", namespace_id: group.id, storage_version: storage_version)
end
- expect { described_class.new.perform(1, projects.last.id) }.to change(project_repositories, :count).by(2)
+ expect { described_class.new.perform(1, projects.last.id) }.to change { project_repositories.count }.by(2)
end
it "does nothing for projects on #{storage} storage that have already a project_repository row" do
projects.create!(id: 1, name: 'foo', path: 'foo', namespace_id: group.id, storage_version: storage_version)
project_repositories.create!(project_id: 1, disk_path: 'phony/foo/bar', shard_id: shard.id)
- expect { described_class.new.perform(1, projects.last.id) }.not_to change(project_repositories, :count)
+ expect { described_class.new.perform(1, projects.last.id) }.not_to change { project_repositories.count }
end
it "does nothing for projects on #{storage == :legacy ? 'hashed' : 'legacy'} storage" do
projects.create!(name: 'foo', path: 'foo', namespace_id: group.id, storage_version: storage == :legacy ? 1 : nil)
- expect { described_class.new.perform(1, projects.last.id) }.not_to change(project_repositories, :count)
+ expect { described_class.new.perform(1, projects.last.id) }.not_to change { project_repositories.count }
end
it 'inserts rows in a single query' do
diff --git a/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb b/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb
index 9ffc55f7e7e..d471a758f3e 100644
--- a/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb
+++ b/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb
@@ -62,14 +62,14 @@ RSpec.shared_examples 'deployment metrics examples' do
describe '#deployment_frequency' do
subject { stage_summary.fourth[:value] }
- it 'includes the unit: `/day`' do
- expect(stage_summary.fourth[:unit]).to eq _('/day')
- end
-
before do
travel_to(5.days.ago) { create_deployment(project: project) }
end
+ it 'includes the unit: `/day`' do
+ expect(stage_summary.fourth[:unit]).to eq _('/day')
+ end
+
it 'returns 0.0 when there were deploys but the frequency was too low' do
options[:from] = 30.days.ago
diff --git a/spec/support/shared_examples/lib/gitlab/database/background_migration_job_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/background_migration_job_shared_examples.rb
index a28fefcfc58..286f10a186d 100644
--- a/spec/support/shared_examples/lib/gitlab/database/background_migration_job_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/database/background_migration_job_shared_examples.rb
@@ -28,7 +28,7 @@ RSpec.shared_examples 'finalized background migration' do |worker_class|
.new
.select do |scheduled|
scheduled.klass == worker_class.name &&
- scheduled.args.first == job_class_name
+ scheduled.args.first == job_class_name
end
expect(queued.size).to eq(0)
end
diff --git a/spec/support/shared_examples/lib/gitlab/middleware/multipart_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/middleware/multipart_shared_examples.rb
index 40deaa27955..16b048ae325 100644
--- a/spec/support/shared_examples/lib/gitlab/middleware/multipart_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/middleware/multipart_shared_examples.rb
@@ -25,10 +25,12 @@ RSpec.shared_examples 'handling all upload parameters conditions' do
end
it 'builds UploadedFiles' do
- expect_uploaded_files([
- { filepath: uploaded_filepath, original_filename: filename, remote_id: remote_id, size: uploaded_file.size, params_path: %w(file1) },
- { filepath: uploaded_filepath2, original_filename: filename2, remote_id: remote_id2, size: uploaded_file2.size, params_path: %w(file2) }
- ])
+ expect_uploaded_files(
+ [
+ { filepath: uploaded_filepath, original_filename: filename, remote_id: remote_id, size: uploaded_file.size, params_path: %w(file1) },
+ { filepath: uploaded_filepath2, original_filename: filename2, remote_id: remote_id2, size: uploaded_file2.size, params_path: %w(file2) }
+ ]
+ )
subject
end
@@ -61,10 +63,12 @@ RSpec.shared_examples 'handling all upload parameters conditions' do
end
it 'builds UploadedFiles' do
- expect_uploaded_files([
- { filepath: uploaded_filepath, original_filename: filename, remote_id: remote_id, size: uploaded_file.size, params_path: %w(user avatar) },
- { filepath: uploaded_filepath2, original_filename: filename2, remote_id: remote_id2, size: uploaded_file2.size, params_path: %w(user screenshot) }
- ])
+ expect_uploaded_files(
+ [
+ { filepath: uploaded_filepath, original_filename: filename, remote_id: remote_id, size: uploaded_file.size, params_path: %w(user avatar) },
+ { filepath: uploaded_filepath2, original_filename: filename2, remote_id: remote_id2, size: uploaded_file2.size, params_path: %w(user screenshot) }
+ ]
+ )
subject
end
@@ -101,10 +105,12 @@ RSpec.shared_examples 'handling all upload parameters conditions' do
end
it 'builds UploadedFiles' do
- expect_uploaded_files([
- { filepath: uploaded_file, original_filename: filename, remote_id: remote_id, size: uploaded_file.size, params_path: %w(user avatar bananas) },
- { filepath: uploaded_file2, original_filename: filename2, remote_id: remote_id2, size: uploaded_file2.size, params_path: %w(user friend ananas) }
- ])
+ expect_uploaded_files(
+ [
+ { filepath: uploaded_file, original_filename: filename, remote_id: remote_id, size: uploaded_file.size, params_path: %w(user avatar bananas) },
+ { filepath: uploaded_file2, original_filename: filename2, remote_id: remote_id2, size: uploaded_file2.size, params_path: %w(user friend ananas) }
+ ]
+ )
subject
end
@@ -133,11 +139,13 @@ RSpec.shared_examples 'handling all upload parameters conditions' do
end
it 'builds UploadedFiles' do
- expect_uploaded_files([
- { filepath: uploaded_filepath, original_filename: filename, remote_id: remote_id, size: uploaded_file.size, params_path: %w(file) },
- { filepath: uploaded_filepath2, original_filename: filename2, remote_id: remote_id2, size: uploaded_file2.size, params_path: %w(user avatar) },
- { filepath: uploaded_filepath3, original_filename: filename3, remote_id: remote_id3, size: uploaded_file3.size, params_path: %w(user friend avatar) }
- ])
+ expect_uploaded_files(
+ [
+ { filepath: uploaded_filepath, original_filename: filename, remote_id: remote_id, size: uploaded_file.size, params_path: %w(file) },
+ { filepath: uploaded_filepath2, original_filename: filename2, remote_id: remote_id2, size: uploaded_file2.size, params_path: %w(user avatar) },
+ { filepath: uploaded_filepath3, original_filename: filename3, remote_id: remote_id3, size: uploaded_file3.size, params_path: %w(user friend avatar) }
+ ]
+ )
subject
end
diff --git a/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb
index 481e11bcf0e..d4802a19202 100644
--- a/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb
@@ -1,38 +1,5 @@
# frozen_string_literal: true
-RSpec.shared_examples 'a daily tracked issuable event' do
- before do
- stub_application_setting(usage_ping_enabled: true)
- end
-
- def count_unique(date_from: 1.minute.ago, date_to: 1.minute.from_now)
- Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: action, start_date: date_from, end_date: date_to)
- end
-
- specify do
- aggregate_failures do
- expect(track_action(author: user1)).to be_truthy
- expect(track_action(author: user1)).to be_truthy
- expect(track_action(author: user2)).to be_truthy
- expect(count_unique).to eq(2)
- end
- end
-
- it 'does not track edit actions if author is not present' do
- expect(track_action(author: nil)).to be_nil
- end
-end
-
-RSpec.shared_examples 'does not track when feature flag is disabled' do |feature_flag|
- context "when feature flag #{feature_flag} is disabled" do
- it 'does not track action' do
- stub_feature_flags(feature_flag => false)
-
- expect(track_action(author: user1)).to be_nil
- end
- end
-end
-
RSpec.shared_examples 'a daily tracked issuable snowplow and service ping events for given event params' do
before do
stub_application_setting(usage_ping_enabled: true)
@@ -76,15 +43,27 @@ end
RSpec.shared_examples 'daily tracked issuable snowplow and service ping events with project' do
it_behaves_like 'a daily tracked issuable snowplow and service ping events for given event params' do
+ let(:context) do
+ Gitlab::Tracking::ServicePingContext
+ .new(data_source: :redis_hll, event: event_property)
+ .to_h
+ end
+
let(:track_params) { { project: project } }
- let(:event_params) { track_params.merge(label: event_label, property: event_property, namespace: project.namespace) }
+ let(:event_params) { track_params.merge(label: event_label, property: event_property, namespace: project.namespace, context: [context]) }
end
end
RSpec.shared_examples 'a daily tracked issuable snowplow and service ping events with namespace' do
it_behaves_like 'a daily tracked issuable snowplow and service ping events for given event params' do
+ let(:context) do
+ Gitlab::Tracking::ServicePingContext
+ .new(data_source: :redis_hll, event: event_property)
+ .to_h
+ end
+
let(:track_params) { { namespace: namespace } }
- let(:event_params) { track_params.merge(label: event_label, property: event_property) }
+ let(:event_params) { track_params.merge(label: event_label, property: event_property, context: [context]) }
end
end
diff --git a/spec/support/shared_examples/mailers/notify_shared_examples.rb b/spec/support/shared_examples/mailers/notify_shared_examples.rb
index 919311adc96..b0cbf0b0d65 100644
--- a/spec/support/shared_examples/mailers/notify_shared_examples.rb
+++ b/spec/support/shared_examples/mailers/notify_shared_examples.rb
@@ -75,7 +75,7 @@ RSpec.shared_examples 'a new thread email with reply-by-email enabled' do
aggregate_failures do
is_expected.to have_header('Message-ID', "<#{route_key}@#{host}>")
- is_expected.to have_header('References', /\A<reply\-.*@#{host}>\Z/ )
+ is_expected.to have_header('References', /\A<reply-.*@#{host}>\Z/ )
end
end
end
@@ -91,7 +91,7 @@ RSpec.shared_examples 'a thread answer email with reply-by-email enabled' do
aggregate_failures do
is_expected.to have_header('Message-ID', /\A<.*@#{host}>\Z/)
is_expected.to have_header('In-Reply-To', "<#{route_key}@#{host}>")
- is_expected.to have_header('References', /\A<reply\-.*@#{host}> <#{route_key}@#{host}>\Z/ )
+ is_expected.to have_header('References', /\A<reply-.*@#{host}> <#{route_key}@#{host}>\Z/ )
is_expected.to have_subject(/^Re: /)
end
end
diff --git a/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb b/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb
index ef4b08c7865..c07d1552ba2 100644
--- a/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb
+++ b/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb
@@ -129,8 +129,8 @@ RSpec.shared_examples 'record ActiveRecord metrics in a metrics transaction' do
expect(transaction).to receive(:increment).with("gitlab_transaction_db_#{db_role}_wal_count_total".to_sym, 1, { db_config_name: db_config_name })
expect(transaction).to receive(:increment).with("gitlab_transaction_db_#{db_role}_wal_cached_count_total".to_sym, 1, { db_config_name: db_config_name }) if record_cached_query
end
- else
- expect(transaction).not_to receive(:increment).with("gitlab_transaction_db_#{db_role}_wal_count_total".to_sym, 1, { db_config_name: db_config_name }) if db_role
+ elsif db_role
+ expect(transaction).not_to receive(:increment).with("gitlab_transaction_db_#{db_role}_wal_count_total".to_sym, 1, { db_config_name: db_config_name })
end
subscriber.sql(event)
diff --git a/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb b/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb
index a658d02f09a..a20bb794095 100644
--- a/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb
@@ -6,21 +6,18 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes|
Gitlab::ApplicationContext.push(feature_category: 'test', caller_id: 'caller')
end
- it 'defines a Redis counter_key' do
- expect(model.counter_key(:counter_name))
- .to eq("project:{#{model.project_id}}:counters:CounterAttributeModel:#{model.id}:counter_name")
- end
-
it 'defines a method to store counters' do
- expect(model.class.counter_attributes.to_a).to eq(counter_attributes)
+ registered_attributes = model.class.counter_attributes.map { |e| e[:attribute] } # rubocop:disable Rails/Pluck
+ expect(registered_attributes).to contain_exactly(*counter_attributes)
end
counter_attributes.each do |attribute|
describe attribute do
- describe '#delayed_increment_counter', :redis do
+ describe '#increment_counter', :redis do
let(:increment) { 10 }
+ let(:counter_key) { model.counter(attribute).key }
- subject { model.delayed_increment_counter(attribute, increment) }
+ subject { model.increment_counter(attribute, increment) }
context 'when attribute is a counter attribute' do
where(:increment) { [10, -3] }
@@ -44,7 +41,7 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes|
subject
Gitlab::Redis::SharedState.with do |redis|
- counter = redis.get(model.counter_key(attribute))
+ counter = redis.get(counter_key)
expect(counter).to eq(increment.to_s)
end
end
@@ -55,7 +52,7 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes|
it 'schedules a worker to flush counter increments asynchronously' do
expect(FlushCounterIncrementsWorker).to receive(:perform_in)
- .with(CounterAttribute::WORKER_DELAY, model.class.name, model.id, attribute)
+ .with(Gitlab::Counters::BufferedCounter::WORKER_DELAY, model.class.name, model.id, attribute)
.and_call_original
subject
@@ -73,128 +70,13 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes|
end
end
end
-
- context 'when attribute is not a counter attribute' do
- it 'raises ArgumentError' do
- expect { model.delayed_increment_counter(:unknown_attribute, 10) }
- .to raise_error(ArgumentError, 'unknown_attribute is not a counter attribute')
- end
- end
- end
- end
- end
-
- describe '.flush_increments_to_database!', :redis do
- let(:incremented_attribute) { counter_attributes.first }
-
- subject { model.flush_increments_to_database!(incremented_attribute) }
-
- it 'obtains an exclusive lease during processing' do
- expect(model)
- .to receive(:in_lock)
- .with(model.counter_lock_key(incremented_attribute), ttl: described_class::WORKER_LOCK_TTL)
- .and_call_original
-
- subject
- end
-
- context 'when there is a counter to flush' do
- before do
- model.delayed_increment_counter(incremented_attribute, 10)
- model.delayed_increment_counter(incremented_attribute, -3)
- end
-
- it 'updates the record and logs it', :aggregate_failures do
- expect(Gitlab::AppLogger).to receive(:info).with(
- hash_including(
- message: 'Acquiring lease for project statistics update',
- attributes: [incremented_attribute]
- )
- )
-
- expect(Gitlab::AppLogger).to receive(:info).with(
- hash_including(
- message: 'Flush counter attribute to database',
- attribute: incremented_attribute,
- project_id: model.project_id,
- increment: 7,
- previous_db_value: 0,
- new_db_value: 7,
- 'correlation_id' => an_instance_of(String),
- 'meta.feature_category' => 'test',
- 'meta.caller_id' => 'caller'
- )
- )
-
- expect { subject }.to change { model.reset.read_attribute(incremented_attribute) }.by(7)
- end
-
- it 'removes the increment entry from Redis' do
- Gitlab::Redis::SharedState.with do |redis|
- key_exists = redis.exists?(model.counter_key(incremented_attribute))
- expect(key_exists).to be_truthy
- end
-
- subject
-
- Gitlab::Redis::SharedState.with do |redis|
- key_exists = redis.exists?(model.counter_key(incremented_attribute))
- expect(key_exists).to be_falsey
- end
- end
- end
-
- context 'when there are no counters to flush' do
- context 'when there are no counters in the relative :flushed key' do
- it 'does not change the record' do
- expect { subject }.not_to change { model.reset.attributes }
- end
- end
-
- # This can be the case where updating counters in the database fails with error
- # and retrying the worker will retry flushing the counters but the main key has
- # disappeared and the increment has been moved to the "<...>:flushed" key.
- context 'when there are counters in the relative :flushed key' do
- before do
- Gitlab::Redis::SharedState.with do |redis|
- redis.incrby(model.counter_flushed_key(incremented_attribute), 10)
- end
- end
-
- it 'updates the record' do
- expect { subject }.to change { model.reset.read_attribute(incremented_attribute) }.by(10)
- end
-
- it 'deletes the relative :flushed key' do
- subject
-
- Gitlab::Redis::SharedState.with do |redis|
- key_exists = redis.exists?(model.counter_flushed_key(incremented_attribute))
- expect(key_exists).to be_falsey
- end
- end
- end
- end
-
- context 'when deleting :flushed key fails' do
- before do
- Gitlab::Redis::SharedState.with do |redis|
- redis.incrby(model.counter_flushed_key(incremented_attribute), 10)
-
- expect(redis).to receive(:del).and_raise('could not delete key')
- end
- end
-
- it 'does a rollback of the counter update' do
- expect { subject }.to raise_error('could not delete key')
-
- expect(model.reset.read_attribute(incremented_attribute)).to eq(0)
end
end
end
describe '#reset_counter!' do
let(:attribute) { counter_attributes.first }
+ let(:counter_key) { model.counter(attribute).key }
before do
model.update!(attribute => 123)
@@ -207,7 +89,7 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes|
expect { subject }.to change { model.reload.send(attribute) }.from(123).to(0)
Gitlab::Redis::SharedState.with do |redis|
- key_exists = redis.exists?(model.counter_key(attribute))
+ key_exists = redis.exists?(counter_key)
expect(key_exists).to be_falsey
end
end
diff --git a/spec/support/shared_examples/models/concerns/integrations/base_slack_notification_shared_examples.rb b/spec/support/shared_examples/models/concerns/integrations/base_slack_notification_shared_examples.rb
index f0581333b28..2e528f7996c 100644
--- a/spec/support/shared_examples/models/concerns/integrations/base_slack_notification_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/integrations/base_slack_notification_shared_examples.rb
@@ -5,14 +5,16 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
let_it_be(:project) { create(:project, :repository, :wiki_repo) }
let_it_be(:integration) { create(factory, branches_to_be_notified: 'all', project: project) }
- before do
- stub_request(:post, integration.webhook)
+ def usage_tracking_key(action)
+ prefix = integration.send(:metrics_key_prefix)
+
+ "#{prefix}_#{action}_notification"
end
it 'uses only known events', :aggregate_failures do
described_class::SUPPORTED_EVENTS_FOR_USAGE_LOG.each do |action|
expect(
- Gitlab::UsageDataCounters::HLLRedisCounter.known_event?("i_ecosystem_slack_service_#{action}_notification")
+ Gitlab::UsageDataCounters::HLLRedisCounter.known_event?(usage_tracking_key(action))
).to be true
end
end
@@ -20,7 +22,9 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
context 'when hook data includes a user object' do
let_it_be(:user) { create_default(:user) }
- shared_examples 'increases the usage data counter' do |event_name|
+ shared_examples 'increases the usage data counter' do |event|
+ let(:event_name) { usage_tracking_key(event) }
+
subject(:execute) { integration.execute(data) }
it 'increases the usage data counter' do
@@ -30,7 +34,7 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
execute
end
- it_behaves_like 'Snowplow event tracking' do
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:category) { described_class.to_s }
let(:action) { 'perform_integrations_action' }
@@ -47,7 +51,7 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
it 'does not increase the usage data counter' do
expect(Gitlab::UsageDataCounters::HLLRedisCounter)
- .not_to receive(:track_event).with('i_ecosystem_slack_service_pipeline_notification', values: user.id)
+ .not_to receive(:track_event).with(usage_tracking_key(:pipeline), values: user.id)
integration.execute(data)
end
@@ -58,13 +62,13 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
let(:data) { issue.to_hook_data(user) }
- it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_issue_notification'
+ it_behaves_like 'increases the usage data counter', :issue
end
context 'for push notification' do
let(:data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
- it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_push_notification'
+ it_behaves_like 'increases the usage data counter', :push
end
context 'for deployment notification' do
@@ -72,7 +76,7 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
let(:data) { Gitlab::DataBuilder::Deployment.build(deployment, deployment.status, Time.current) }
- it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_deployment_notification'
+ it_behaves_like 'increases the usage data counter', :deployment
end
context 'for wiki_page notification' do
@@ -88,7 +92,7 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
allow(project.wiki).to receive(:after_wiki_activity)
end
- it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_wiki_page_notification'
+ it_behaves_like 'increases the usage data counter', :wiki_page
end
context 'for merge_request notification' do
@@ -96,7 +100,7 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
let(:data) { merge_request.to_hook_data(user) }
- it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_merge_request_notification'
+ it_behaves_like 'increases the usage data counter', :merge_request
end
context 'for note notification' do
@@ -104,7 +108,7 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
let(:data) { Gitlab::DataBuilder::Note.build(issue_note, user) }
- it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_note_notification'
+ it_behaves_like 'increases the usage data counter', :note
end
context 'for tag_push notification' do
@@ -115,7 +119,7 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
Git::TagHooksService.new(project, user, change: { oldrev: oldrev, newrev: newrev, ref: ref }).send(:push_data)
end
- it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_tag_push_notification'
+ it_behaves_like 'increases the usage data counter', :tag_push
end
context 'for confidential note notification' do
@@ -125,7 +129,7 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
let(:data) { Gitlab::DataBuilder::Note.build(confidential_issue_note, user) }
- it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_confidential_note_notification'
+ it_behaves_like 'increases the usage data counter', :confidential_note
end
context 'for confidential issue notification' do
@@ -133,7 +137,7 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
let(:data) { issue.to_hook_data(user) }
- it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_confidential_issue_notification'
+ it_behaves_like 'increases the usage data counter', :confidential_issue
end
end
diff --git a/spec/support/shared_examples/models/concerns/sanitizable_shared_examples.rb b/spec/support/shared_examples/models/concerns/sanitizable_shared_examples.rb
index ed94a71892d..aedbfe4deb3 100644
--- a/spec/support/shared_examples/models/concerns/sanitizable_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/sanitizable_shared_examples.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.shared_examples 'sanitizable' do |factory, fields|
- let(:attributes) { fields.to_h { |field| [field, input] } }
+ let(:attributes) { fields.index_with { input } }
it 'includes Sanitizable' do
expect(described_class).to include(Sanitizable)
diff --git a/spec/support/shared_examples/models/concerns/signature_type_shared_examples.rb b/spec/support/shared_examples/models/concerns/signature_type_shared_examples.rb
new file mode 100644
index 00000000000..728855b74f8
--- /dev/null
+++ b/spec/support/shared_examples/models/concerns/signature_type_shared_examples.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+METHODS = %i[
+ gpg?
+ ssh?
+ x509?
+].freeze
+
+RSpec.shared_examples 'signature with type checking' do |type|
+ describe 'signature type checkers' do
+ where(:method, :expected) do
+ METHODS.map do |method|
+ [method, method == "#{type}?".to_sym]
+ end
+ end
+
+ with_them do
+ specify { expect(subject.public_send(method)).to eq(expected) }
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/integrations/has_web_hook_shared_examples.rb b/spec/support/shared_examples/models/integrations/has_web_hook_shared_examples.rb
index a764d47d7c0..a1269b158a2 100644
--- a/spec/support/shared_examples/models/integrations/has_web_hook_shared_examples.rb
+++ b/spec/support/shared_examples/models/integrations/has_web_hook_shared_examples.rb
@@ -65,7 +65,7 @@ RSpec.shared_examples Integrations::HasWebHook do
end
it 'creates or updates a service hook' do
- expect { call }.to change(ServiceHook, :count).by(1)
+ expect { call }.to change { ServiceHook.count }.by(1)
expect(integration.service_hook.url).to eq(hook_url)
integration.service_hook.update!(url: 'http://other.com')
@@ -98,10 +98,10 @@ RSpec.shared_examples Integrations::HasWebHook do
it 'creates the webhook if necessary and executes it' do
expect_next(ServiceHook).to receive(:execute).with(*args)
- expect { call }.to change(ServiceHook, :count).by(1)
+ expect { call }.to change { ServiceHook.count }.by(1)
expect(integration.service_hook).to receive(:execute).with(*args)
- expect { call }.not_to change(ServiceHook, :count)
+ expect { call }.not_to change { ServiceHook.count }
end
it 'raises an error if the service hook could not be saved' do
diff --git a/spec/support/shared_examples/models/label_note_shared_examples.rb b/spec/support/shared_examples/models/label_note_shared_examples.rb
index f61007f57fd..3facd533d7a 100644
--- a/spec/support/shared_examples/models/label_note_shared_examples.rb
+++ b/spec/support/shared_examples/models/label_note_shared_examples.rb
@@ -30,6 +30,8 @@ RSpec.shared_examples 'label note created from events' do
expect(note.noteable).to eq event.issuable
expect(note.note).to be_present
expect(note.note_html).to be_present
+ expect(note.created_at).to eq create_event.created_at
+ expect(note.updated_at).to eq create_event.created_at
end
it 'updates markdown cache if reference is not set yet' do
diff --git a/spec/support/shared_examples/models/member_shared_examples.rb b/spec/support/shared_examples/models/member_shared_examples.rb
index 287b046cbec..f8cff5c5558 100644
--- a/spec/support/shared_examples/models/member_shared_examples.rb
+++ b/spec/support/shared_examples/models/member_shared_examples.rb
@@ -491,7 +491,7 @@ RSpec.shared_examples_for "bulk member creation" do
:developer,
tasks_to_be_done: %w(issues),
tasks_project_id: task_project.id)
- end.not_to change(MemberTask, :count)
+ end.not_to change { MemberTask.count }
member.reset
expect(member.tasks_to_be_done).to match_array([:code, :ci])
@@ -505,7 +505,7 @@ RSpec.shared_examples_for "bulk member creation" do
:developer,
tasks_to_be_done: %w(issues),
tasks_project_id: task_project.id)
- end.to change(MemberTask, :count).by(1)
+ end.to change { MemberTask.count }.by(1)
member = source.members.find_by(user_id: user1.id)
expect(member.tasks_to_be_done).to match_array([:issues])
diff --git a/spec/support/shared_examples/models/update_highest_role_shared_examples.rb b/spec/support/shared_examples/models/update_highest_role_shared_examples.rb
index 34c4ada1718..1fdd0962fbb 100644
--- a/spec/support/shared_examples/models/update_highest_role_shared_examples.rb
+++ b/spec/support/shared_examples/models/update_highest_role_shared_examples.rb
@@ -25,7 +25,7 @@ RSpec.shared_examples 'update highest role with exclusive lease' do
expect(UpdateHighestRoleWorker).to receive(:perform_in).with(described_class::HIGHEST_ROLE_JOB_DELAY, user_id).and_call_original
- expect { subject }.to change(UpdateHighestRoleWorker.jobs, :size).by(1)
+ expect { subject }.to change { UpdateHighestRoleWorker.jobs.size }.by(1)
end
end
@@ -33,7 +33,7 @@ RSpec.shared_examples 'update highest role with exclusive lease' do
it 'only schedules one job' do
stub_exclusive_lease_taken(lease_key, timeout: described_class::HIGHEST_ROLE_LEASE_TIMEOUT)
- expect { subject }.not_to change(UpdateHighestRoleWorker.jobs, :size)
+ expect { subject }.not_to change { UpdateHighestRoleWorker.jobs.size }
end
end
end
diff --git a/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb b/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb
index b81bd514d0a..eb742921d35 100644
--- a/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb
+++ b/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb
@@ -15,7 +15,7 @@ RSpec.shared_examples 'UpdateProjectStatistics' do |with_counter_attribute|
def read_pending_increment
Gitlab::Redis::SharedState.with do |redis|
- key = project.statistics.counter_key(project_statistics_name)
+ key = project.statistics.counter(project_statistics_name).key
redis.get(key).to_i
end
end
@@ -25,7 +25,7 @@ RSpec.shared_examples 'UpdateProjectStatistics' do |with_counter_attribute|
def expect_flush_counter_increments_worker_performed
expect(FlushCounterIncrementsWorker)
.to receive(:perform_in)
- .with(CounterAttribute::WORKER_DELAY, project.statistics.class.name, project.statistics.id, project_statistics_name)
+ .with(Gitlab::Counters::BufferedCounter::WORKER_DELAY, project.statistics.class.name, project.statistics.id, project_statistics_name)
yield
diff --git a/spec/support/shared_examples/models/with_debian_distributions_shared_examples.rb b/spec/support/shared_examples/models/with_debian_distributions_shared_examples.rb
index e86f1e77447..d6071b20dca 100644
--- a/spec/support/shared_examples/models/with_debian_distributions_shared_examples.rb
+++ b/spec/support/shared_examples/models/with_debian_distributions_shared_examples.rb
@@ -9,9 +9,9 @@ RSpec.shared_examples 'model with Debian distributions' do
it 'removes distribution files on removal' do
distribution_file_paths = distributions.map do |distribution|
[distribution.file.path] +
- distribution.component_files.map do |component_file|
- component_file.file.path
- end
+ distribution.component_files.map do |component_file|
+ component_file.file.path
+ end
end.flatten
expect { subject.destroy! }
diff --git a/spec/support/shared_examples/observability/csp_shared_examples.rb b/spec/support/shared_examples/observability/csp_shared_examples.rb
new file mode 100644
index 00000000000..868d7023d14
--- /dev/null
+++ b/spec/support/shared_examples/observability/csp_shared_examples.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+
+# Verifies that the proper CSP rules for Observabilty UI are applied to a given controller/path
+#
+# The path under test needs to be declared with `let(:tested_path) { .. }` in the context including this example
+#
+# ```
+# it_behaves_like "observability csp policy" do
+# let(:tested_path) { ....the path under test }
+# end
+# ```
+#
+# It optionally supports specifying the controller class handling the tested path as a parameter, e.g.
+#
+# ```
+# it_behaves_like "observability csp policy", Groups::ObservabilityController
+# ```
+# (If not specified it will default to `described_class`)
+#
+RSpec.shared_examples 'observability csp policy' do |controller_class = described_class|
+ include ContentSecurityPolicyHelpers
+
+ let(:observability_url) { Gitlab::Observability.observability_url }
+ let(:signin_url) do
+ Gitlab::Utils.append_path(Gitlab.config.gitlab.url,
+ '/users/sign_in')
+ end
+
+ let(:oauth_url) do
+ Gitlab::Utils.append_path(Gitlab.config.gitlab.url,
+ '/oauth/authorize')
+ end
+
+ before do
+ setup_csp_for_controller(controller_class, csp, any_time: true)
+ end
+
+ subject do
+ get tested_path
+ response.headers['Content-Security-Policy']
+ end
+
+ context 'when there is no CSP config' do
+ let(:csp) { ActionDispatch::ContentSecurityPolicy.new }
+
+ it 'does not add any csp header' do
+ expect(subject).to be_blank
+ end
+ end
+
+ context 'when frame-src exists in the CSP config' do
+ let(:csp) do
+ ActionDispatch::ContentSecurityPolicy.new do |p|
+ p.frame_src 'https://something.test'
+ end
+ end
+
+ it 'appends the proper url to frame-src CSP directives' do
+ expect(subject).to include(
+ "frame-src https://something.test #{observability_url} #{signin_url} #{oauth_url}")
+ end
+ end
+
+ context 'when signin is already present in the policy' do
+ let(:csp) do
+ ActionDispatch::ContentSecurityPolicy.new do |p|
+ p.frame_src signin_url
+ end
+ end
+
+ it 'does not append signin again' do
+ expect(subject).to include(
+ "frame-src #{signin_url} #{observability_url} #{oauth_url};")
+ end
+ end
+
+ context 'when oauth is already present in the policy' do
+ let(:csp) do
+ ActionDispatch::ContentSecurityPolicy.new do |p|
+ p.frame_src oauth_url
+ end
+ end
+
+ it 'does not append oauth again' do
+ expect(subject).to include(
+ "frame-src #{oauth_url} #{observability_url} #{signin_url};")
+ end
+ end
+
+ context 'when default-src exists in the CSP config' do
+ let(:csp) do
+ ActionDispatch::ContentSecurityPolicy.new do |p|
+ p.default_src 'https://something.test'
+ end
+ end
+
+ it 'does not change default-src' do
+ expect(subject).to include(
+ "default-src https://something.test;")
+ end
+
+ it 'appends the proper url to frame-src CSP directives' do
+ expect(subject).to include(
+ "frame-src https://something.test #{observability_url} #{signin_url} #{oauth_url}")
+ end
+ end
+
+ context 'when frame-src and default-src exist in the CSP config' do
+ let(:csp) do
+ ActionDispatch::ContentSecurityPolicy.new do |p|
+ p.default_src 'https://something_default.test'
+ p.frame_src 'https://something.test'
+ end
+ end
+
+ it 'appends to frame-src CSP directives' do
+ expect(subject).to include(
+ "frame-src https://something.test #{observability_url} #{signin_url} #{oauth_url}")
+ expect(subject).to include(
+ "default-src https://something_default.test")
+ end
+ end
+end
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 cfcc3615e13..15d56c402d1 100644
--- a/spec/support/shared_examples/policies/project_policy_shared_examples.rb
+++ b/spec/support/shared_examples/policies/project_policy_shared_examples.rb
@@ -2,13 +2,13 @@
RSpec.shared_examples 'archived project policies' do
let(:feature_write_abilities) do
- described_class.readonly_features.flat_map do |feature|
+ described_class.archived_features.flat_map do |feature|
described_class.create_update_admin_destroy(feature)
end + additional_maintainer_permissions
end
let(:other_write_abilities) do
- described_class.readonly_abilities
+ described_class.archived_abilities
end
context 'when the project is archived' do
diff --git a/spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb b/spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb
index f7731af8dc6..f70621673d5 100644
--- a/spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb
+++ b/spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.shared_examples 'when regex matching everything is specified' do
- |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
let(:params) do
{ 'name_regex_delete' => '.*' }
end
@@ -23,6 +23,19 @@ RSpec.shared_examples 'when regex matching everything is specified' do
end
end
+RSpec.shared_examples 'when regex matching everything is specified and latest is not kept' do
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
+
+ let(:params) do
+ { 'name_regex_delete' => '.*', 'keep_latest' => false }
+ end
+
+ it_behaves_like 'removing the expected tags',
+ service_response_extra: service_response_extra,
+ supports_caching: supports_caching,
+ delete_expectations: delete_expectations
+end
+
RSpec.shared_examples 'when delete regex matching specific tags is used' do
|service_response_extra: {}, supports_caching: false|
let(:params) do
@@ -65,7 +78,7 @@ RSpec.shared_examples 'when delete regex matching specific tags is used with ove
end
RSpec.shared_examples 'with allow regex value' do
- |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
let(:params) do
{
'name_regex_delete' => '.*',
@@ -80,7 +93,7 @@ RSpec.shared_examples 'with allow regex value' do
end
RSpec.shared_examples 'when keeping only N tags' do
- |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
let(:params) do
{
'name_regex' => 'A|B.*|C',
@@ -99,7 +112,7 @@ RSpec.shared_examples 'when keeping only N tags' do
end
RSpec.shared_examples 'when not keeping N tags' do
- |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
let(:params) do
{ 'name_regex' => 'A|B.*|C' }
end
@@ -115,7 +128,7 @@ RSpec.shared_examples 'when not keeping N tags' do
end
RSpec.shared_examples 'when removing keeping only 3' do
- |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
let(:params) do
{ 'name_regex_delete' => '.*',
'keep_n' => 3 }
@@ -128,7 +141,7 @@ RSpec.shared_examples 'when removing keeping only 3' do
end
RSpec.shared_examples 'when removing older than 1 day' do
- |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
let(:params) do
{
'name_regex_delete' => '.*',
@@ -143,7 +156,7 @@ RSpec.shared_examples 'when removing older than 1 day' do
end
RSpec.shared_examples 'when combining all parameters' do
- |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
let(:params) do
{
'name_regex_delete' => '.*',
@@ -159,7 +172,7 @@ RSpec.shared_examples 'when combining all parameters' do
end
RSpec.shared_examples 'when running a container_expiration_policy' do
- |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
let(:user) { nil }
context 'with valid container_expiration_policy param' do
@@ -191,7 +204,7 @@ RSpec.shared_examples 'not removing anything' do |service_response_extra: {}, su
end
RSpec.shared_examples 'removing the expected tags' do
- |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
it 'removes the expected tags' do
delete_expectations.each { |expectation| expect_delete(expectation) }
expect_no_caching unless supports_caching
diff --git a/spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb b/spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb
index 4d142199c95..1cd529aa50b 100644
--- a/spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb
+++ b/spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb
@@ -1,21 +1,23 @@
# frozen_string_literal: true
RSpec.shared_examples 'issuable quick actions' do
- QuickAction = Struct.new(:action_text, :expectation, :before_action, keyword_init: true) do
- # Pass a block as :before_action if
- # issuable state needs to be changed before
- # the quick action is executed.
- def call_before_action
- before_action.call if before_action
- end
+ before do
+ stub_const('QuickAction', Struct.new(:action_text, :expectation, :before_action, keyword_init: true) do
+ # Pass a block as :before_action if
+ # issuable state needs to be changed before
+ # the quick action is executed.
+ def call_before_action
+ before_action.call if before_action
+ end
- def skip_access_check
- action_text["/todo"] ||
- action_text["/done"] ||
- action_text["/subscribe"] ||
- action_text["/shrug"] ||
- action_text["/tableflip"]
- end
+ def skip_access_check
+ action_text["/todo"] ||
+ action_text["/done"] ||
+ action_text["/subscribe"] ||
+ action_text["/shrug"] ||
+ action_text["/tableflip"]
+ end
+ end)
end
let(:unlabel_expectation) do
@@ -145,6 +147,12 @@ RSpec.shared_examples 'issuable quick actions' do
}
),
QuickAction.new(
+ action_text: "/labels ~feature",
+ expectation: ->(noteable, can_use_quick_action) {
+ expect(noteable.labels&.last&.id == feature_label.id).to eq(can_use_quick_action)
+ }
+ ),
+ QuickAction.new(
action_text: "/unlabel",
expectation: unlabel_expectation
),
diff --git a/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb
index 18304951e41..56a1cee44c8 100644
--- a/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb
+++ b/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb
@@ -22,6 +22,12 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type|
end
end
+ def open_create_timelog_form
+ page.within time_tracker_selector do
+ find('[data-testid="add-time-entry-button"]').click
+ end
+ end
+
it 'renders the sidebar component empty state' do
page.within '[data-testid="noTrackingPane"]' do
expect(page).to have_content 'No estimate or time spent'
@@ -74,11 +80,13 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type|
end
end
- it 'shows the help state when icon is clicked' do
- page.within time_tracker_selector do
- find('[data-testid="helpButton"]').click
- expect(page).to have_content 'Track time with quick actions'
- expect(page).to have_content 'Learn more'
+ it 'shows the create timelog form when add button is clicked' do
+ open_create_timelog_form
+
+ page.within '[data-testid="create-timelog-modal"]' do
+ expect(page).to have_content 'Add time entry'
+ expect(page).to have_content 'Time spent'
+ expect(page).to have_content 'Spent at'
end
end
@@ -123,24 +131,6 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type|
expect(page).to have_content '1d'
end
end
-
- it 'hides the help state when close icon is clicked' do
- page.within time_tracker_selector do
- find('[data-testid="helpButton"]').click
- find('[data-testid="closeHelpButton"]').click
-
- expect(page).not_to have_content 'Track time with quick actions'
- expect(page).not_to have_content 'Learn more'
- end
- end
-
- it 'displays the correct help url' do
- page.within time_tracker_selector do
- find('[data-testid="helpButton"]').click
-
- expect(find_link('Learn more')[:href]).to have_content('/help/user/project/time_tracking.md')
- end
- end
end
def submit_time(quick_action)
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 629d93676eb..c76bc7c3107 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
@@ -593,7 +593,7 @@ RSpec.shared_examples 'delete package endpoint' do
project.add_maintainer(user)
end
- it_behaves_like 'a gitlab tracking event', 'API::ConanPackages', 'delete_package'
+ it_behaves_like 'a package tracking event', 'API::ConanPackages', 'delete_package'
it 'deletes a package' do
expect { subject }.to change { Packages::Package.count }.from(2).to(1)
@@ -708,7 +708,7 @@ RSpec.shared_examples 'package file download endpoint' do
context 'tracking the conan_package.tgz download' do
let(:package_file) { package.package_files.find_by(file_name: ::Packages::Conan::FileMetadatum::PACKAGE_BINARY) }
- it_behaves_like 'a gitlab tracking event', 'API::ConanPackages', 'pull_package'
+ it_behaves_like 'a package tracking event', 'API::ConanPackages', 'pull_package'
end
end
@@ -781,7 +781,7 @@ RSpec.shared_examples 'workhorse package file upload endpoint' do
context 'tracking the conan_package.tgz upload' do
let(:file_name) { ::Packages::Conan::FileMetadatum::PACKAGE_BINARY }
- it_behaves_like 'a gitlab tracking event', 'API::ConanPackages', 'push_package'
+ it_behaves_like 'a package tracking event', 'API::ConanPackages', 'push_package'
end
end
@@ -849,12 +849,6 @@ RSpec.shared_examples 'uploads a package file' do
expect(package_file.file_name).to eq(params[:file].original_filename)
end
- it "doesn't attempt to migrate file to object storage" do
- expect(ObjectStorage::BackgroundMoveWorker).not_to receive(:perform_async)
-
- subject
- end
-
context 'with existing package' do
let!(:existing_package) { create(:conan_package, name: 'foo', version: 'bar', project: project) }
@@ -936,8 +930,6 @@ RSpec.shared_examples 'uploads a package file' do
end
end
end
-
- it_behaves_like 'background upload schedules a file migration'
end
end
diff --git a/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb
index 5469fd80a4f..d4479e462af 100644
--- a/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb
@@ -1,6 +1,15 @@
# frozen_string_literal: true
RSpec.shared_examples 'graphql issue list request spec' do
+ let(:issue_ids) { graphql_dig_at(issues_data, :id) }
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ #{all_graphql_fields_for('issues'.classify)}
+ }
+ QUERY
+ end
+
it_behaves_like 'a working graphql query' do
before do
post_query
@@ -109,10 +118,57 @@ RSpec.shared_examples 'graphql issue list request spec' do
let(:ids) { issue_ids }
end
end
+
+ context 'when filtering by confidentiality' do
+ context 'when fetching confidential issues' do
+ let(:issue_filter_params) { { confidential: true } }
+
+ it 'returns only confidential issues' do
+ post_query
+
+ expect(issue_ids).to match_array(to_gid_list(confidential_issues))
+ end
+
+ context 'when user cannot see confidential issues' do
+ it 'returns an empty list' do
+ post_query(external_user)
+
+ expect(issue_ids).to be_empty
+ end
+ end
+ end
+
+ context 'when fetching non-confidential issues' do
+ let(:issue_filter_params) { { confidential: false } }
+
+ it 'returns only non-confidential issues' do
+ post_query
+
+ expect(issue_ids).to match_array(to_gid_list(non_confidential_issues))
+ end
+
+ context 'when user cannot see confidential issues' do
+ it 'returns an empty list' do
+ post_query(external_user)
+
+ expect(issue_ids).to match_array(to_gid_list(public_non_confidential_issues))
+ end
+ end
+ end
+ end
end
describe 'sorting and pagination' do
context 'when sorting by severity' do
+ let(:expected_severity_sorted_asc) { [issue_c, issue_a, issue_b, issue_e, issue_d] }
+
+ before_all do
+ create(:issuable_severity, issue: issue_a, severity: :unknown)
+ create(:issuable_severity, issue: issue_b, severity: :low)
+ create(:issuable_severity, issue: issue_d, severity: :critical)
+ create(:issuable_severity, issue: issue_e, severity: :high)
+ end
+
context 'when ascending' do
it_behaves_like 'sorted paginated query' do
let(:sort_param) { :SEVERITY_ASC }
@@ -147,6 +203,459 @@ RSpec.shared_examples 'graphql issue list request spec' do
end
end
end
+
+ context 'when sorting by due date' do
+ context 'when ascending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :DUE_DATE_ASC }
+ let(:first_param) { 2 }
+ let(:all_records) { to_gid_list(expected_due_date_sorted_asc) }
+ end
+ end
+
+ context 'when descending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :DUE_DATE_DESC }
+ let(:first_param) { 2 }
+ let(:all_records) { to_gid_list(expected_due_date_sorted_desc) }
+ end
+ end
+ end
+
+ context 'when sorting by relative position' do
+ context 'when ascending' do
+ it_behaves_like 'sorted paginated query', is_reversible: true do
+ let(:sort_param) { :RELATIVE_POSITION_ASC }
+ let(:first_param) { 2 }
+ let(:all_records) { to_gid_list(expected_relative_position_sorted_asc) }
+ end
+ end
+ end
+
+ context 'when sorting by label priority' do
+ context 'when ascending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :LABEL_PRIORITY_ASC }
+ let(:first_param) { 2 }
+ let(:all_records) { to_gid_list(expected_label_priority_sorted_asc) }
+ end
+ end
+
+ context 'when descending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :LABEL_PRIORITY_DESC }
+ let(:first_param) { 2 }
+ let(:all_records) { to_gid_list(expected_label_priority_sorted_desc) }
+ end
+ end
+ end
+
+ context 'when sorting by milestone due date' do
+ context 'when ascending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :MILESTONE_DUE_ASC }
+ let(:first_param) { 2 }
+ let(:all_records) { to_gid_list(expected_milestone_sorted_asc) }
+ end
+ end
+
+ context 'when descending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :MILESTONE_DUE_DESC }
+ let(:first_param) { 2 }
+ let(:all_records) { to_gid_list(expected_milestone_sorted_desc) }
+ end
+ end
+ end
+ end
+
+ describe 'N+1 query checks' do
+ let(:extra_iid_for_second_query) { issue_b.iid.to_s }
+ let(:search_params) { { iids: [issue_a.iid.to_s] } }
+ let(:issue_filter_params) { search_params }
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ id
+ #{requested_fields}
+ }
+ QUERY
+ end
+
+ def execute_query
+ post_query
+ end
+
+ context 'when requesting `user_notes_count` and `user_discussions_count`' do
+ let(:requested_fields) { 'userNotesCount userDiscussionsCount' }
+
+ before do
+ create_list(:note_on_issue, 2, noteable: issue_a, project: issue_a.project)
+ create(:note_on_issue, noteable: issue_b, project: issue_b.project)
+ end
+
+ include_examples 'N+1 query check'
+ end
+
+ context 'when requesting `merge_requests_count`' do
+ let(:requested_fields) { 'mergeRequestsCount' }
+
+ before do
+ create_list(:merge_requests_closing_issues, 2, issue: issue_a)
+ create_list(:merge_requests_closing_issues, 3, issue: issue_b)
+ end
+
+ include_examples 'N+1 query check'
+ end
+
+ context 'when requesting `timelogs`' do
+ let(:requested_fields) { 'timelogs { nodes { timeSpent } }' }
+
+ before do
+ create_list(:issue_timelog, 2, issue: issue_a)
+ create(:issue_timelog, issue: issue_b)
+ end
+
+ include_examples 'N+1 query check'
+ end
+
+ context 'when requesting `closed_as_duplicate_of`' do
+ let(:requested_fields) { 'closedAsDuplicateOf { id }' }
+ let(:issue_a_dup) { create(:issue, project: issue_a.project) }
+ let(:issue_b_dup) { create(:issue, project: issue_b.project) }
+
+ before do
+ issue_a.update!(duplicated_to_id: issue_a_dup)
+ issue_b.update!(duplicated_to_id: issue_a_dup)
+ end
+
+ include_examples 'N+1 query check'
+ end
+
+ context 'when award emoji votes' do
+ let(:requested_fields) { 'upvotes downvotes' }
+
+ before do
+ create_list(:award_emoji, 2, name: 'thumbsup', awardable: issue_a)
+ create_list(:award_emoji, 2, name: 'thumbsdown', awardable: issue_b)
+ end
+
+ include_examples 'N+1 query check'
+ end
+
+ context 'when requesting participants' do
+ let(:search_params) { { iids: [issue_a.iid.to_s, issue_c.iid.to_s] } }
+ let(:requested_fields) { 'participants { nodes { name } }' }
+
+ before do
+ create(:award_emoji, :upvote, awardable: issue_a)
+ create(:award_emoji, :upvote, awardable: issue_b)
+ create(:award_emoji, :upvote, awardable: issue_c)
+
+ note_with_emoji_a = create(:note_on_issue, noteable: issue_a, project: issue_a.project)
+ note_with_emoji_b = create(:note_on_issue, noteable: issue_b, project: issue_b.project)
+ note_with_emoji_c = create(:note_on_issue, noteable: issue_c, project: issue_c.project)
+
+ create(:award_emoji, :upvote, awardable: note_with_emoji_a)
+ create(:award_emoji, :upvote, awardable: note_with_emoji_b)
+ create(:award_emoji, :upvote, awardable: note_with_emoji_c)
+ end
+
+ # Executes 3 extra queries to fetch participant_attrs
+ include_examples 'N+1 query check', threshold: 3
+ end
+
+ context 'when requesting labels', :use_sql_query_cache do
+ let(:requested_fields) { 'labels { nodes { id } }' }
+ let(:extra_iid_for_second_query) { same_project_issue2.iid.to_s }
+ let(:search_params) { { iids: [same_project_issue1.iid.to_s] } }
+
+ before do
+ current_project = same_project_issue1.project
+ project_labels = create_list(:label, 2, project: current_project)
+ group_labels = create_list(:group_label, 2, group: current_project.group)
+
+ same_project_issue1.update!(labels: [project_labels.first, group_labels.first].flatten)
+ same_project_issue2.update!(labels: [project_labels, group_labels].flatten)
+ end
+
+ include_examples 'N+1 query check', skip_cached: false
+ end
+ end
+
+ context 'when confidential issues exist' do
+ context 'when user can see confidential issues' do
+ it 'includes confidential issues' do
+ post_query
+
+ all_issues = confidential_issues + non_confidential_issues
+
+ expect(issue_ids).to match_array(to_gid_list(all_issues))
+ expect(issues_data.pluck('confidential')).to match_array(all_issues.map(&:confidential))
+ end
+ end
+
+ context 'when user cannot see confidential issues' do
+ let(:current_user) { external_user }
+
+ it 'does not include confidential issues' do
+ post_query
+
+ expect(issue_ids).to match_array(to_gid_list(public_non_confidential_issues))
+ end
+ end
+ end
+
+ context 'when limiting the number of results' do
+ let(:issue_limit) { 1 }
+ let(:issue_filter_params) { { first: issue_limit } }
+
+ it_behaves_like 'a working graphql query' do
+ before do
+ post_query
+ end
+
+ it 'only returns N issues' do
+ expect(issues_data.size).to eq(issue_limit)
+ end
+ end
+
+ context 'when no limit is provided' do
+ let(:issue_limit) { nil }
+
+ it 'returns all issues' do
+ post_query
+
+ expect(issues_data.size).to be > 1
+ end
+ end
+
+ it 'is expected to check permissions on the first issue only' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ # Newest first, we only want to see the newest checked
+ expect(Ability).not_to receive(:allowed?).with(current_user, :read_issue, issues.first)
+
+ post_query
+ end
+ end
+
+ context 'when the user does not have access to the issue' do
+ let(:current_user) { external_user }
+
+ it 'returns no issues' do
+ public_projects.each do |public_project|
+ public_project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
+ end
+
+ post_query
+
+ expect(issues_data).to eq([])
+ end
+ end
+
+ context 'when fetching escalation status' do
+ let_it_be(:escalation_status) { create(:incident_management_issuable_escalation_status, issue: issue_a) }
+
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ id
+ escalationStatus
+ }
+ QUERY
+ end
+
+ before do
+ issue_a.update_columns(issue_type: Issue.issue_types[:incident])
+ end
+
+ it 'returns the escalation status values' do
+ post_query
+
+ statuses = issues_data.pluck('escalationStatus')
+
+ expect(statuses).to contain_exactly(escalation_status.status_name.upcase.to_s, nil, nil, nil, nil)
+ end
+
+ it 'avoids N+1 queries', :aggregate_failures do
+ control = ActiveRecord::QueryRecorder.new { run_with_clean_state(query, context: { current_user: current_user }) }
+
+ new_incident = create(:incident, project: public_projects.first)
+ create(:incident_management_issuable_escalation_status, issue: new_incident)
+
+ expect { run_with_clean_state(query, context: { current_user: current_user }) }.not_to exceed_query_limit(control)
+ end
+ end
+
+ context 'when fetching alert management alert' do
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ iid
+ alertManagementAlert {
+ title
+ }
+ alertManagementAlerts {
+ nodes {
+ title
+ }
+ }
+ }
+ QUERY
+ end
+
+ it 'avoids N+1 queries' do
+ control = ActiveRecord::QueryRecorder.new { post_query }
+
+ create(:alert_management_alert, :with_incident, project: public_projects.first)
+
+ expect { post_query }.not_to exceed_query_limit(control)
+ end
+
+ it 'returns the alert data' do
+ post_query
+
+ alert_titles = issues_data.map { |issue| issue.dig('alertManagementAlert', 'title') }
+ expected_titles = issues.map { |issue| issue.alert_management_alerts.first&.title }
+
+ expect(alert_titles).to contain_exactly(*expected_titles)
+ end
+
+ it 'returns the alerts data' do
+ post_query
+
+ alert_titles = issues_data.map { |issue| issue.dig('alertManagementAlerts', 'nodes') }
+ expected_titles = issues.map do |issue|
+ issue.alert_management_alerts.map { |alert| { 'title' => alert.title } }
+ end
+
+ expect(alert_titles).to contain_exactly(*expected_titles)
+ end
+ end
+
+ context 'when fetching customer_relations_contacts' do
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ id
+ customerRelationsContacts {
+ nodes {
+ firstName
+ }
+ }
+ }
+ QUERY
+ end
+
+ def clean_state_query
+ run_with_clean_state(query, context: { current_user: current_user })
+ end
+
+ it 'avoids N+1 queries' do
+ create(:issue_customer_relations_contact, :for_issue, issue: issue_a)
+
+ control = ActiveRecord::QueryRecorder.new(skip_cached: false) { clean_state_query }
+
+ create(:issue_customer_relations_contact, :for_issue, issue: issue_a)
+
+ expect { clean_state_query }.not_to exceed_all_query_limit(control)
+ end
+ end
+
+ context 'when fetching labels' do
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ id
+ labels {
+ nodes {
+ id
+ }
+ }
+ }
+ QUERY
+ end
+
+ before do
+ issues.each do |issue|
+ # create a label for each issue we have to properly test N+1
+ label = create(:label, project: issue.project)
+ issue.update!(labels: [label])
+ end
+ end
+
+ def response_label_ids(response_data)
+ response_data.map do |node|
+ node['labels']['nodes'].pluck('id')
+ end.flatten
+ end
+
+ def labels_as_global_ids(issues)
+ issues.map(&:labels).flatten.map(&:to_global_id).map(&:to_s)
+ end
+
+ it 'avoids N+1 queries', :aggregate_failures do
+ control = ActiveRecord::QueryRecorder.new { post_query }
+ expect(issues_data.count).to eq(5)
+ expect(response_label_ids(issues_data)).to match_array(labels_as_global_ids(issues))
+
+ public_project = public_projects.first
+ new_issues = issues + [
+ create(:issue, project: public_project, labels: [create(:label, project: public_project)])
+ ]
+
+ expect { post_query }.not_to exceed_query_limit(control)
+
+ expect(issues_data.count).to eq(6)
+ expect(response_label_ids(issues_data)).to match_array(labels_as_global_ids(new_issues))
+ end
+ end
+
+ context 'when fetching assignees' do
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ id
+ assignees {
+ nodes {
+ id
+ }
+ }
+ }
+ QUERY
+ end
+
+ before do
+ issues.each do |issue|
+ # create an assignee for each issue we have to properly test N+1
+ assignee = create(:user)
+ issue.update!(assignees: [assignee])
+ end
+ end
+
+ def response_assignee_ids(response_data)
+ response_data.map do |node|
+ node['assignees']['nodes'].pluck('id')
+ end.flatten
+ end
+
+ def assignees_as_global_ids(issues)
+ issues.map(&:assignees).flatten.map(&:to_global_id).map(&:to_s)
+ end
+
+ it 'avoids N+1 queries', :aggregate_failures do
+ control = ActiveRecord::QueryRecorder.new { post_query }
+ expect(issues_data.count).to eq(5)
+ expect(response_assignee_ids(issues_data)).to match_array(assignees_as_global_ids(issues))
+
+ public_project = public_projects.first
+ new_issues = issues + [create(:issue, project: public_project, assignees: [create(:user)])]
+
+ expect { post_query }.not_to exceed_query_limit(control)
+
+ expect(issues_data.count).to eq(6)
+ expect(response_assignee_ids(issues_data)).to match_array(assignees_as_global_ids(new_issues))
+ end
end
it 'includes a web_url' do
@@ -167,4 +676,8 @@ RSpec.shared_examples 'graphql issue list request spec' do
def to_gid_list(instance_list)
instance_list.map { |instance| instance.to_gid.to_s }
end
+
+ def issues_data
+ graphql_data.dig(*issue_nodes_path)
+ 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 fb4aacfd7a9..f5835460a77 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
@@ -62,6 +62,21 @@ RSpec.shared_examples 'group and project packages query' do
it 'returns the count of the packages' do
expect(packages_count).to eq(4)
end
+
+ context '_links' do
+ let_it_be(:errored_package) { create(:maven_package, :error, project: project1) }
+
+ let(:package_web_paths) { graphql_data_at(resource_type, :packages, :nodes, :_links, :web_path) }
+
+ it 'does not contain the web path of errored package' do
+ expect(package_web_paths.compact).to contain_exactly(
+ "/#{project1.full_path}/-/packages/#{npm_package.id}",
+ "/#{project1.full_path}/-/packages/#{maven_package.id}",
+ "/#{project2.full_path}/-/packages/#{debian_package.id}",
+ "/#{project2.full_path}/-/packages/#{composer_package.id}"
+ )
+ end
+ end
end
context 'when the user does not have access to the resource' do
@@ -139,7 +154,7 @@ RSpec.shared_examples 'group and project packages query' do
end
it 'throws an error' do
- expect_graphql_errors_to_include(/Argument \'sort\' on Field \'packages\' has an invalid value/)
+ expect_graphql_errors_to_include(/Argument 'sort' on Field 'packages' has an invalid value/)
end
end
diff --git a/spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb b/spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb
index 54cc13fac94..6b4d8cae2ce 100644
--- a/spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb
@@ -1,13 +1,13 @@
# frozen_string_literal: true
-RSpec.shared_examples 'perform graphql requests for AccessLevel type objects' do |access_level_kind|
+RSpec.shared_examples 'a GraphQL query for access levels' do |access_level_kind|
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:current_user) { create(:user, maintainer_projects: [project]) }
let_it_be(:variables) { { path: project.full_path } }
- let(:fields) { all_graphql_fields_for("#{access_level_kind.to_s.classify}AccessLevel", max_depth: 2) }
+ let(:fields) { all_graphql_fields_for("#{access_level_kind.to_s.classify}AccessLevel") }
let(:access_levels) { protected_branch.public_send("#{access_level_kind}_access_levels") }
let(:access_levels_count) { access_levels.size }
let(:maintainer_access_level) { access_levels.for_role.first }
@@ -61,17 +61,35 @@ RSpec.shared_examples 'perform graphql requests for AccessLevel type objects' do
create(:protected_branch, "maintainers_can_#{access_level_kind}", project: project)
end
- before do
- post_graphql(query, current_user: current_user, variables: variables)
+ describe 'query' do
+ it 'avoids N+1 queries' do
+ control = ActiveRecord::QueryRecorder.new do
+ post_graphql(query, current_user: current_user, variables: variables)
+ end
+ expect_graphql_errors_to_be_empty
+
+ create("protected_branch_#{access_level_kind}_access_level", protected_branch: protected_branch)
+
+ expect do
+ post_graphql(query, current_user: current_user, variables: variables)
+ end.not_to exceed_all_query_limit(control)
+ expect_graphql_errors_to_be_empty
+ end
end
- it_behaves_like 'a working graphql query'
+ describe 'response' do
+ before do
+ post_graphql(query, current_user: current_user, variables: variables)
+ end
+
+ it_behaves_like 'a working graphql query'
- it 'returns all the access level attributes' do
- expect(maintainer_access_level_data['accessLevel']).to eq(maintainer_access_level.access_level)
- expect(maintainer_access_level_data['accessLevelDescription']).to eq(maintainer_access_level.humanize)
- expect(maintainer_access_level_data.dig('group', 'name')).to be_nil
- expect(maintainer_access_level_data.dig('user', 'name')).to be_nil
+ it 'returns all the access level attributes' do
+ expect(maintainer_access_level_data['accessLevel']).to eq(maintainer_access_level.access_level)
+ expect(maintainer_access_level_data['accessLevelDescription']).to eq(maintainer_access_level.humanize)
+ expect(maintainer_access_level_data.dig('group', 'name')).to be_nil
+ expect(maintainer_access_level_data.dig('user', 'name')).to be_nil
+ end
end
end
end
diff --git a/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb
index 8bf6b162508..7803f0ff04d 100644
--- a/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb
@@ -156,25 +156,13 @@ RSpec.shared_examples 'process helm upload' do |user_type, status|
end
context 'and direct upload disabled' do
- context 'and background upload disabled' do
- let(:fog_connection) do
- stub_package_file_object_storage(direct_upload: false, background_upload: false)
- end
-
- it_behaves_like 'creates helm package files'
+ let(:fog_connection) do
+ stub_package_file_object_storage(direct_upload: false)
end
- context 'and background upload enabled' do
- let(:fog_connection) do
- stub_package_file_object_storage(direct_upload: false, background_upload: true)
- end
-
- it_behaves_like 'creates helm package files'
- end
+ it_behaves_like 'creates helm package files'
end
end
-
- it_behaves_like 'background upload schedules a file migration'
end
end
diff --git a/spec/support/shared_examples/requests/api/notes_shared_examples.rb b/spec/support/shared_examples/requests/api/notes_shared_examples.rb
index 11f9565989f..efe5ed3bcf9 100644
--- a/spec/support/shared_examples/requests/api/notes_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/notes_shared_examples.rb
@@ -159,7 +159,7 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
expect do
post api(uri, user), params: { body: 'hi!' }
- end.to change(Event, :count).by(1)
+ end.to change { Event.count }.by(1)
end
context 'setting created_at' do
diff --git a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
index 85ac2b5e1ea..b55639a6b82 100644
--- a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
@@ -323,6 +323,171 @@ RSpec.shared_examples 'handling get metadata requests' do |scope: :project|
end
end
+RSpec.shared_examples 'handling audit request' do |path:, scope: :project|
+ using RSpec::Parameterized::TableSyntax
+
+ let(:headers) { {} }
+ let(:params) do
+ ActiveSupport::Gzip.compress(
+ Gitlab::Json.dump({
+ '@gitlab-org/npm-test': ['1.0.6'],
+ 'call-bind': ['1.0.2']
+ })
+ )
+ end
+
+ let(:default_headers) do
+ { 'HTTP_CONTENT_ENCODING' => 'gzip', 'CONTENT_TYPE' => 'application/json' }
+ end
+
+ subject { post(url, headers: headers.merge(default_headers), params: params) }
+
+ shared_examples 'accept audit request' do |status:|
+ it 'accepts the audit request' do
+ subject
+
+ expect(response).to have_gitlab_http_status(status)
+ expect(response.media_type).to eq('application/json')
+ expect(json_response).to eq([])
+ end
+ end
+
+ shared_examples 'reject audit request' do |status:|
+ it 'rejects the audit request' do
+ subject
+
+ expect(response).to have_gitlab_http_status(status)
+ end
+ end
+
+ shared_examples 'redirect audit request' do |status:|
+ it 'redirects audit request' do
+ subject
+
+ expect(response).to have_gitlab_http_status(status)
+ expect(response.headers['Location']).to eq("https://registry.npmjs.org/-/npm/v1/security/#{path}")
+ end
+ end
+
+ shared_examples 'handling all conditions' do
+ include_context 'dependency proxy helpers context'
+
+ where(:auth, :request_forward, :visibility, :user_role, :expected_result, :expected_status) do
+ nil | true | :public | nil | :reject | :unauthorized
+ nil | false | :public | nil | :reject | :unauthorized
+ nil | true | :private | nil | :reject | :unauthorized
+ nil | false | :private | nil | :reject | :unauthorized
+ nil | true | :internal | nil | :reject | :unauthorized
+ nil | false | :internal | nil | :reject | :unauthorized
+
+ :oauth | true | :public | :guest | :redirect | :temporary_redirect
+ :oauth | true | :public | :reporter | :redirect | :temporary_redirect
+ :oauth | false | :public | :guest | :accept | :ok
+ :oauth | false | :public | :reporter | :accept | :ok
+ :oauth | true | :private | :reporter | :redirect | :temporary_redirect
+ :oauth | false | :private | :guest | :reject | :forbidden
+ :oauth | false | :private | :reporter | :accept | :ok
+ :oauth | true | :private | :guest | :redirect | :temporary_redirect
+ :oauth | true | :internal | :guest | :redirect | :temporary_redirect
+ :oauth | true | :internal | :reporter | :redirect | :temporary_redirect
+ :oauth | false | :internal | :guest | :accept | :ok
+ :oauth | false | :internal | :reporter | :accept | :ok
+
+ :personal_access_token | true | :public | :guest | :redirect | :temporary_redirect
+ :personal_access_token | true | :public | :reporter | :redirect | :temporary_redirect
+ :personal_access_token | false | :public | :guest | :accept | :ok
+ :personal_access_token | false | :public | :reporter | :accept | :ok
+ :personal_access_token | true | :private | :guest | :redirect | :temporary_redirect
+ :personal_access_token | true | :private | :reporter | :redirect | :temporary_redirect
+ :personal_access_token | false | :private | :guest | :reject | :forbidden # instance might fail
+ :personal_access_token | false | :private | :reporter | :accept | :ok
+ :personal_access_token | true | :internal | :guest | :redirect | :temporary_redirect
+ :personal_access_token | true | :internal | :reporter | :redirect | :temporary_redirect
+ :personal_access_token | false | :internal | :guest | :accept | :ok
+ :personal_access_token | false | :internal | :reporter | :accept | :ok
+
+ :job_token | true | :public | :developer | :redirect | :temporary_redirect
+ :job_token | false | :public | :developer | :accept | :ok
+ :job_token | true | :private | :developer | :redirect | :temporary_redirect
+ :job_token | false | :private | :developer | :accept | :ok
+ :job_token | true | :internal | :developer | :redirect | :temporary_redirect
+ :job_token | false | :internal | :developer | :accept | :ok
+
+ :deploy_token | true | :public | nil | :redirect | :temporary_redirect
+ :deploy_token | false | :public | nil | :accept | :ok
+ :deploy_token | true | :private | nil | :redirect | :temporary_redirect
+ :deploy_token | false | :private | nil | :accept | :ok
+ :deploy_token | true | :internal | nil | :redirect | :temporary_redirect
+ :deploy_token | false | :internal | nil | :accept | :ok
+ end
+
+ with_them do
+ let(:headers) do
+ case auth
+ when :oauth
+ build_token_auth_header(token.plaintext_token)
+ when :personal_access_token
+ build_token_auth_header(personal_access_token.token)
+ when :job_token
+ build_token_auth_header(job.token)
+ when :deploy_token
+ build_token_auth_header(deploy_token.token)
+ else
+ {}
+ end
+ end
+
+ before do
+ project.send("add_#{user_role}", user) if user_role
+ project.update!(visibility: visibility.to_s)
+
+ if scope == :instance
+ allow_fetch_application_setting(attribute: "npm_package_requests_forwarding", return_value: request_forward)
+ else
+ allow_fetch_cascade_application_setting(attribute: "npm_package_requests_forwarding", return_value: request_forward)
+ end
+ end
+
+ example_name = "#{params[:expected_result]} audit request"
+ status = params[:expected_status]
+
+ if scope == :instance && params[:expected_status] != :unauthorized
+ if params[:request_forward]
+ example_name = 'redirect audit request'
+ status = :temporary_redirect
+ else
+ example_name = 'reject audit request'
+ status = :not_found
+ end
+ end
+
+ it_behaves_like example_name, status: status
+ end
+ end
+
+ context 'with a group namespace' do
+ it_behaves_like 'handling all conditions'
+ end
+
+ context 'with a developer' do
+ let(:headers) { build_token_auth_header(personal_access_token.token) }
+
+ before do
+ project.add_developer(user)
+ end
+
+ context 'with a job token' do
+ let(:headers) { build_token_auth_header(job.token) }
+
+ before do
+ job.update!(status: :success)
+ end
+
+ it_behaves_like 'reject audit request', status: :unauthorized
+ end
+ end
+end
+
RSpec.shared_examples 'handling get dist tags requests' do |scope: :project|
using RSpec::Parameterized::TableSyntax
include_context 'set package name from package name type'
diff --git a/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
index fdd55893deb..bace570e47a 100644
--- a/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
@@ -224,25 +224,13 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
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 nuget package files'
+ let(:fog_connection) do
+ stub_package_file_object_storage(direct_upload: false)
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 nuget package files'
- end
+ it_behaves_like 'creates nuget package files'
end
end
-
- it_behaves_like 'background upload schedules a file migration'
end
end
@@ -507,7 +495,7 @@ RSpec.shared_examples 'nuget upload endpoint' do |symbol_package: false|
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
let(:headers) { user_headers.merge(workhorse_headers) }
- let(:snowplow_gitlab_standard_context) { { project: project, user: user, namespace: project.namespace } }
+ let(:snowplow_gitlab_standard_context) { { project: project, user: user, namespace: project.namespace, property: 'i_package_nuget_user' } }
before do
update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false))
diff --git a/spec/support/shared_examples/requests/api/packages_shared_examples.rb b/spec/support/shared_examples/requests/api/packages_shared_examples.rb
index 860cb1b1d86..98264baa61d 100644
--- a/spec/support/shared_examples/requests/api/packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/packages_shared_examples.rb
@@ -142,15 +142,26 @@ RSpec.shared_examples 'job token for package uploads' do |authorize_endpoint: fa
end
end
-RSpec.shared_examples 'a package tracking event' do |category, action|
+RSpec.shared_examples 'a package tracking event' do |category, action, service_ping_context = true|
before do
stub_feature_flags(collect_package_events: true)
end
+ let(:context) do
+ [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll,
+ event: snowplow_gitlab_standard_context[:property]).to_h]
+ end
+
it "creates a gitlab tracking event #{action}", :snowplow, :aggregate_failures do
expect { subject }.to change { Packages::Event.count }.by(1)
- expect_snowplow_event(category: category, action: action, **snowplow_gitlab_standard_context)
+ if service_ping_context
+ expect_snowplow_event(category: category, action: action,
+ label: "redis_hll_counters.user_packages.user_packages_total_unique_counts_monthly",
+ context: context, **snowplow_gitlab_standard_context)
+ else
+ expect_snowplow_event(category: category, action: action, **snowplow_gitlab_standard_context)
+ end
end
end
diff --git a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
index a9b44015206..a267476b7cb 100644
--- a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
@@ -95,25 +95,13 @@ RSpec.shared_examples 'PyPI package creation' do |user_type, status, add_member
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 'creating pypi package files'
+ let(:fog_connection) do
+ stub_package_file_object_storage(direct_upload: false)
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 'creating pypi package files'
- end
+ it_behaves_like 'creating pypi package files'
end
end
-
- it_behaves_like 'background upload schedules a file migration'
end
end
@@ -285,7 +273,7 @@ RSpec.shared_examples 'pypi simple API endpoint' do
let(:url) { "/projects/#{project.id}/packages/pypi/simple/my-package" }
let(:headers) { basic_auth_header(user.username, personal_access_token.token) }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: group, property: 'i_package_pypi_user' } }
it_behaves_like 'PyPI package versions', :developer, :success
end
diff --git a/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb b/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb
index 2d036cb2aa3..2154a76d765 100644
--- a/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb
@@ -71,11 +71,7 @@ RSpec.shared_examples 'repository_storage_moves API' do |container_type|
get_container_repository_storage_moves
json_ids = json_response.map { |storage_move| storage_move['id'] }
- expect(json_ids).to eq([
- storage_move.id,
- storage_move_middle.id,
- storage_move_oldest.id
- ])
+ expect(json_ids).to eq([storage_move.id, storage_move_middle.id, storage_move_oldest.id])
end
describe 'permissions' do
diff --git a/spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb
index f075927e7bf..da09d70c777 100644
--- a/spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb
@@ -122,21 +122,11 @@ RSpec.shared_examples 'process rubygems upload' do |user_type, status, add_membe
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 rubygems package files'
+ let(:fog_connection) do
+ stub_package_file_object_storage(direct_upload: false)
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 rubygems package files'
- end
+ it_behaves_like 'creates rubygems package files'
end
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
index bdff2c65691..ae2855083f6 100644
--- 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
@@ -264,21 +264,11 @@ RSpec.shared_examples 'process terraform module upload' do |user_type, status, a
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'
+ let(:fog_connection) do
+ stub_package_file_object_storage(direct_upload: false)
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
+ it_behaves_like 'creates terraform module package files'
end
end
end
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 11759b6671f..82ed6eb4c95 100644
--- a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
+++ b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
@@ -68,6 +68,7 @@ RSpec.shared_examples 'rate-limited token requests' do
# Set low limits
settings_to_set[:"#{throttle_setting_prefix}_requests_per_period"] = requests_per_period
settings_to_set[:"#{throttle_setting_prefix}_period_in_seconds"] = period_in_seconds
+ travel_back
end
after do
@@ -220,6 +221,7 @@ RSpec.shared_examples 'rate-limited web authenticated requests' do
# Set low limits
settings_to_set[:"#{throttle_setting_prefix}_requests_per_period"] = requests_per_period
settings_to_set[:"#{throttle_setting_prefix}_period_in_seconds"] = period_in_seconds
+ travel_back
end
after do
@@ -436,6 +438,7 @@ RSpec.shared_examples 'rate-limited unauthenticated requests' do
# Set low limits
settings_to_set[:"#{throttle_setting_prefix}_requests_per_period"] = requests_per_period
settings_to_set[:"#{throttle_setting_prefix}_period_in_seconds"] = period_in_seconds
+ travel_back
end
context 'when the throttle is enabled' do
diff --git a/spec/support/shared_examples/security_training_providers_importer.rb b/spec/support/shared_examples/security_training_providers_importer.rb
index 568e3e1a4f2..69d92964270 100644
--- a/spec/support/shared_examples/security_training_providers_importer.rb
+++ b/spec/support/shared_examples/security_training_providers_importer.rb
@@ -8,7 +8,7 @@ RSpec.shared_examples 'security training providers importer' do
end
it 'upserts security training providers' do
- expect { 2.times { subject } }.to change(security_training_providers, :count).from(0).to(2)
+ expect { 2.times { subject } }.to change { security_training_providers.count }.from(0).to(2)
expect(security_training_providers.all.map(&:name)).to match_array(['Kontra', 'Secure Code Warrior'])
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
index 0db9519f760..6a9da91eaa7 100644
--- a/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb
+++ b/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb
@@ -11,7 +11,7 @@ RSpec.shared_examples 'creates an alert management alert or errors' do
it 'creates AlertManagement::Alert' do
expect(Gitlab::AppLogger).not_to receive(:warn)
- expect { subject }.to change(AlertManagement::Alert, :count).by(1)
+ expect { subject }.to change { AlertManagement::Alert.count }.by(1)
end
it 'executes the alert service hooks' do
@@ -118,7 +118,7 @@ end
RSpec.shared_examples 'does not create an alert management alert' do
specify do
- expect { subject }.not_to change(AlertManagement::Alert, :count)
+ expect { subject }.not_to change { AlertManagement::Alert.count }
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
index 1973577d742..2740b6bf59d 100644
--- 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
@@ -14,7 +14,7 @@ RSpec.shared_examples 'closes related incident if enabled' do
specify do
expect { Sidekiq::Testing.inline! { subject } }
.to change { alert.issue.reload.closed? }.from(false).to(true)
- .and change(ResourceStateEvent, :count).by(1)
+ .and change { ResourceStateEvent.count }.by(1)
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
index 57d598c0259..2d0815ba27c 100644
--- 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
@@ -19,7 +19,7 @@ RSpec.shared_examples 'creates expected system notes for alert' do |*notes|
end
it "for #{notes.join(', ')}" do
- expect { subject }.to change(Note, :count).by(expected_note_count)
+ 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)
@@ -29,6 +29,6 @@ end
RSpec.shared_examples 'does not create a system note for alert' do
specify do
- expect { subject }.not_to change(Note, :count)
+ 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 b46ace1824a..b8fc2eb5475 100644
--- a/spec/support/shared_examples/services/alert_management_shared_examples.rb
+++ b/spec/support/shared_examples/services/alert_management_shared_examples.rb
@@ -68,8 +68,8 @@ RSpec.shared_examples 'processes one firing and one resolved prometheus alerts'
expect(Gitlab::AppLogger).not_to receive(:warn)
expect { subject }
- .to change(AlertManagement::Alert, :count).by(1)
- .and change(Note, :count).by(1)
+ .to change { AlertManagement::Alert.count }.by(1)
+ .and change { Note.count }.by(1)
expect(subject).to be_success
expect(subject.payload).to eq({})
diff --git a/spec/support/shared_examples/services/approval_state_updated_trigger_shared_examples.rb b/spec/support/shared_examples/services/approval_state_updated_trigger_shared_examples.rb
new file mode 100644
index 00000000000..455fd308be8
--- /dev/null
+++ b/spec/support/shared_examples/services/approval_state_updated_trigger_shared_examples.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'triggers GraphQL subscription mergeRequestApprovalStateUpdated' do
+ specify do
+ expect(GraphqlTriggers).to receive(:merge_request_approval_state_updated).with(merge_request)
+
+ action
+ end
+end
+
+RSpec.shared_examples 'does not trigger GraphQL subscription mergeRequestApprovalStateUpdated' do
+ specify do
+ expect(GraphqlTriggers).not_to receive(:merge_request_approval_state_updated)
+
+ action
+ end
+end
diff --git a/spec/support/shared_examples/services/boards/boards_create_service_shared_examples.rb b/spec/support/shared_examples/services/boards/boards_create_service_shared_examples.rb
index f28c78aec97..67a5af587e9 100644
--- a/spec/support/shared_examples/services/boards/boards_create_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/boards/boards_create_service_shared_examples.rb
@@ -3,7 +3,7 @@
RSpec.shared_examples 'boards create service' do
context 'when parent does not have a board' do
it 'creates a new board' do
- expect { service.execute }.to change(Board, :count).by(1)
+ expect { service.execute }.to change { Board.count }.by(1)
end
it 'creates the default lists' do
@@ -23,7 +23,7 @@ RSpec.shared_examples 'boards create service' do
it 'does not create a new board' do
expect(service).to receive(:can_create_board?) { false }
- expect { service.execute }.not_to change(parent.boards, :count)
+ expect { service.execute }.not_to change { parent.boards.count }
end
end
end
diff --git a/spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb b/spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb
index fd832d4484d..80d5c771abd 100644
--- a/spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb
@@ -2,7 +2,7 @@
RSpec.shared_examples 'boards list service' do
it 'does not create a new board' do
- expect { service.execute }.not_to change(parent.boards, :count)
+ expect { service.execute }.not_to change { parent.boards.count }
end
it 'returns parent boards' do
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
index 68ea460dabc..8bf01ad84ff 100644
--- 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
@@ -5,7 +5,7 @@ RSpec.shared_examples 'boards recent visit' do
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)
+ expect { described_class.visited!(user, board) }.to change { described_class.count }.by(1)
end
shared_examples 'was visited previously' do
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 af88644ced7..52d427d6684 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
@@ -5,7 +5,7 @@ RSpec.shared_examples 'lists destroy service' do
it 'removes list from board' do
service = described_class.new(parent, user)
- expect { service.execute(list) }.to change(board.lists, :count).by(-1)
+ expect { service.execute(list) }.to change { board.lists.count }.by(-1)
end
it 'decrements position of higher lists' do
@@ -24,6 +24,6 @@ RSpec.shared_examples 'lists destroy service' do
it 'does not remove list from board when list type is closed' do
service = described_class.new(parent, user)
- expect { service.execute(closed_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/boards/lists_list_service_shared_examples.rb b/spec/support/shared_examples/services/boards/lists_list_service_shared_examples.rb
index e1143562661..b9f28fab558 100644
--- a/spec/support/shared_examples/services/boards/lists_list_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/boards/lists_list_service_shared_examples.rb
@@ -5,7 +5,7 @@ RSpec.shared_examples 'lists list service' do
let!(:backlog_list) { create_backlog_list(board) }
it 'does not create a backlog list' do
- expect { service.execute(board) }.not_to change(board.lists, :count)
+ expect { service.execute(board) }.not_to change { board.lists.count }
end
it "returns board's lists" do
@@ -35,11 +35,11 @@ RSpec.shared_examples 'lists list service' do
context 'when the board does not have a backlog list' do
it 'creates a backlog list' do
- expect { service.execute(board) }.to change(board.lists, :count).by(1)
+ expect { service.execute(board) }.to change { board.lists.count }.by(1)
end
it 'does not create a backlog list when create_default_lists is false' do
- expect { service.execute(board, create_default_lists: false) }.not_to change(board.lists, :count)
+ expect { service.execute(board, create_default_lists: false) }.not_to change { board.lists.count }
end
it "returns board's lists" do
diff --git a/spec/support/shared_examples/services/container_expiration_policy_shared_examples.rb b/spec/support/shared_examples/services/container_expiration_policy_shared_examples.rb
index 28bf46a57d5..38e19e58706 100644
--- a/spec/support/shared_examples/services/container_expiration_policy_shared_examples.rb
+++ b/spec/support/shared_examples/services/container_expiration_policy_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples 'updating the container expiration policy attributes' do |mode:, from: {}, to:|
+RSpec.shared_examples 'updating the container expiration policy attributes' do |mode:, to:, from: {}|
if mode == :create
it 'creates a new container expiration policy' do
expect { subject }
diff --git a/spec/support/shared_examples/services/dependency_proxy_ttl_policies_shared_examples.rb b/spec/support/shared_examples/services/dependency_proxy_ttl_policies_shared_examples.rb
index f6692646ca8..dcc9c3d898f 100644
--- a/spec/support/shared_examples/services/dependency_proxy_ttl_policies_shared_examples.rb
+++ b/spec/support/shared_examples/services/dependency_proxy_ttl_policies_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples 'updating the dependency proxy image ttl policy attributes' do |from: {}, to:|
+RSpec.shared_examples 'updating the dependency proxy image ttl policy attributes' do |to:, from: {}|
it_behaves_like 'not creating the dependency proxy image ttl policy'
it 'updates the dependency proxy image ttl policy' do
diff --git a/spec/support/shared_examples/services/incident_shared_examples.rb b/spec/support/shared_examples/services/incident_shared_examples.rb
index b533b095aac..a87e7c1f801 100644
--- a/spec/support/shared_examples/services/incident_shared_examples.rb
+++ b/spec/support/shared_examples/services/incident_shared_examples.rb
@@ -55,7 +55,7 @@ RSpec.shared_examples 'incident management label service' do
shared_examples 'existing label' do
it 'returns the existing label' do
- expect { execute }.not_to change(Label, :count)
+ expect { execute }.not_to change { Label.count }
expect(execute).to be_success
expect(execute.payload).to eq(label: label)
@@ -64,7 +64,7 @@ RSpec.shared_examples 'incident management label service' do
shared_examples 'new label' do
it 'creates a new label' do
- expect { execute }.to change(Label, :count).by(1)
+ expect { execute }.to change { Label.count }.by(1)
label = project.reload.labels.last
expect(execute).to be_success
diff --git a/spec/support/shared_examples/services/issuable/update_service_shared_examples.rb b/spec/support/shared_examples/services/issuable/update_service_shared_examples.rb
index 3d90885dd6f..ff7acc7e907 100644
--- a/spec/support/shared_examples/services/issuable/update_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable/update_service_shared_examples.rb
@@ -5,7 +5,7 @@ RSpec.shared_examples_for 'issuable update service updating last_edited_at value
let(:update_params) { { title: 'updated title' } }
it 'does not update last_edited values' do
- expect { update_issuable }.to change(issuable, :title).from(issuable.title).to('updated title').and(
+ expect { update_issuable }.to change { issuable.title }.from(issuable.title).to('updated title').and(
not_change(issuable, :last_edited_at)
).and(
not_change(issuable, :last_edited_by)
@@ -19,10 +19,10 @@ RSpec.shared_examples_for 'issuable update service updating last_edited_at value
it 'updates last_edited values' do
expect do
update_issuable
- end.to change(issuable, :description).from(issuable.description).to('updated description').and(
- change(issuable, :last_edited_at)
+ end.to change { issuable.description }.from(issuable.description).to('updated description').and(
+ change { issuable.last_edited_at }
).and(
- change(issuable, :last_edited_by)
+ change { issuable.last_edited_by }
)
end
end
diff --git a/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb b/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb
index 65351ac94ab..12f2b5d78a5 100644
--- a/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb
@@ -24,7 +24,7 @@ RSpec.shared_examples 'issuable link creation' do
end
it 'no relationship is created' do
- expect { subject }.not_to change(issuable_link_class, :count)
+ expect { subject }.not_to change { issuable_link_class.count }
end
end
@@ -38,7 +38,7 @@ RSpec.shared_examples 'issuable link creation' do
end
it 'no relationship is created' do
- expect { subject }.not_to change(issuable_link_class, :count)
+ expect { subject }.not_to change { issuable_link_class.count }
end
end
@@ -54,7 +54,7 @@ RSpec.shared_examples 'issuable link creation' do
end
it 'no relationship is created' do
- expect { subject }.not_to change(issuable_link_class, :count)
+ expect { subject }.not_to change { issuable_link_class.count }
end
end
@@ -64,7 +64,7 @@ RSpec.shared_examples 'issuable link creation' do
end
it 'creates relationships' do
- expect { subject }.to change(issuable_link_class, :count).by(2)
+ expect { subject }.to change { issuable_link_class.count }.by(2)
expect(issuable_link_class.find_by!(target: issuable2)).to have_attributes(source: issuable, link_type: 'relates_to')
expect(issuable_link_class.find_by!(target: issuable3)).to have_attributes(source: issuable, link_type: 'relates_to')
diff --git a/spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb b/spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb
index 5e80014da1d..cc170c6544d 100644
--- a/spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb
@@ -8,7 +8,7 @@ RSpec.shared_examples 'a destroyable issuable link' do
end
it 'removes related issue' do
- expect { subject }.to change(issuable_link.class, :count).by(-1)
+ expect { subject }.to change { issuable_link.class.count }.by(-1)
end
it 'creates notes' do
@@ -28,7 +28,7 @@ RSpec.shared_examples 'a destroyable issuable link' do
context 'when failing to remove an issuable link' do
it 'does not remove relation' do
- expect { subject }.not_to change(issuable_link.class, :count).from(1)
+ expect { subject }.not_to change { issuable_link.class.count }.from(1)
end
it 'does not create notes' do
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 f7a6bd3676a..11a786fdefb 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
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples 'updating the namespace package setting attributes' do |from: {}, to:|
+RSpec.shared_examples 'updating the namespace package setting attributes' do |to:, from: {}|
it_behaves_like 'not creating the namespace package setting'
it 'updates the namespace package setting' do
diff --git a/spec/support/shared_examples/services/packages_shared_examples.rb b/spec/support/shared_examples/services/packages_shared_examples.rb
index ca4dea90c55..e0dd08ec50e 100644
--- a/spec/support/shared_examples/services/packages_shared_examples.rb
+++ b/spec/support/shared_examples/services/packages_shared_examples.rb
@@ -188,20 +188,6 @@ RSpec.shared_examples 'returns paginated packages' do
end
end
-RSpec.shared_examples 'background upload schedules a file migration' do
- context 'background upload enabled' do
- before do
- stub_package_file_object_storage(background_upload: true)
- end
-
- it 'schedules migration of file to object storage' do
- expect(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async).with('Packages::PackageFileUploader', 'Packages::PackageFile', :file, kind_of(Numeric))
-
- subject
- end
- end
-end
-
RSpec.shared_context 'package filter context' do
def package_filter_url(filter, param)
"/projects/#{project.id}/packages?package_#{filter}=#{param}"
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 14af35e58b7..9f940d27341 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
@@ -71,7 +71,7 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
it 'does not enqueue a GC run' do
expect { subject.execute }
- .not_to change(Projects::GitGarbageCollectWorker.jobs, :count)
+ .not_to change { Projects::GitGarbageCollectWorker.jobs.count }
end
end
@@ -84,12 +84,12 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
stub_application_setting(housekeeping_enabled: false)
expect { subject.execute }
- .not_to change(Projects::GitGarbageCollectWorker.jobs, :count)
+ .not_to change { Projects::GitGarbageCollectWorker.jobs.count }
end
it 'enqueues a GC run' do
expect { subject.execute }
- .to change(Projects::GitGarbageCollectWorker.jobs, :count).by(1)
+ .to change { Projects::GitGarbageCollectWorker.jobs.count }.by(1)
end
end
end
diff --git a/spec/support/shared_examples/services/repositories/housekeeping_shared_examples.rb b/spec/support/shared_examples/services/repositories/housekeeping_shared_examples.rb
index 4c00faee56b..8a937303711 100644
--- a/spec/support/shared_examples/services/repositories/housekeeping_shared_examples.rb
+++ b/spec/support/shared_examples/services/repositories/housekeeping_shared_examples.rb
@@ -12,7 +12,7 @@ RSpec.shared_examples 'housekeeps repository' do
expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :incremental_repack, :the_lease_key, :the_uuid).and_call_original
Sidekiq::Testing.fake! do
- expect { subject.execute }.to change(resource.git_garbage_collect_worker_klass.jobs, :size).by(1)
+ expect { subject.execute }.to change { resource.git_garbage_collect_worker_klass.jobs.size }.by(1)
end
end
@@ -71,9 +71,6 @@ RSpec.shared_examples 'housekeeps repository' do
# At push 10, 20, ... (except those above)
expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :incremental_repack, :the_lease_key, :the_uuid)
.exactly(16).times
- # At push 6, 12, 18, ... (except those above)
- expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :pack_refs, :the_lease_key, :the_uuid)
- .exactly(27).times
201.times do
subject.increment!
@@ -82,6 +79,37 @@ RSpec.shared_examples 'housekeeps repository' do
expect(resource.pushes_since_gc).to eq(1)
end
+
+ context 'when optimized_repository feature flag is disabled' do
+ before do
+ stub_feature_flags(optimized_housekeeping: false)
+ end
+
+ it 'calls also the garbage collect worker with pack_refs every 6 commits' do
+ allow(subject).to receive(:try_obtain_lease).and_return(:the_uuid)
+ allow(subject).to receive(:lease_key).and_return(:the_lease_key)
+
+ # At push 200
+ expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :gc, :the_lease_key, :the_uuid)
+ .once
+ # At push 50, 100, 150
+ expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :full_repack, :the_lease_key, :the_uuid)
+ .exactly(3).times
+ # At push 10, 20, ... (except those above)
+ expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :incremental_repack, :the_lease_key, :the_uuid)
+ .exactly(16).times
+ # At push 6, 12, 18, ... (except those above)
+ expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :pack_refs, :the_lease_key, :the_uuid)
+ .exactly(27).times
+
+ 201.times do
+ subject.increment!
+ subject.execute if subject.needed?
+ end
+
+ expect(resource.pushes_since_gc).to eq(1)
+ end
+ end
end
it 'runs the task specifically requested' do
@@ -107,6 +135,17 @@ RSpec.shared_examples 'housekeeps repository' do
allow(resource).to receive(:pushes_since_gc).and_return(10)
expect(subject.needed?).to eq(true)
end
+
+ context 'when optimized_housekeeping is disabled' do
+ before do
+ stub_feature_flags(optimized_housekeeping: false)
+ end
+
+ it 'returns true pack refs is needed' do
+ allow(resource).to receive(:pushes_since_gc).and_return(described_class::PACK_REFS_PERIOD)
+ expect(subject.needed?).to eq(true)
+ end
+ end
end
describe '#increment!' do
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 97304680316..acf15730180 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
@@ -11,7 +11,7 @@ RSpec.shared_examples 'moves repository shard in bulk' do
describe '#execute' do
it 'schedules container repository storage moves' do
expect { subject.execute(source_storage_name, destination_storage_name) }
- .to change(move_service_klass, :count).by(1)
+ .to change { move_service_klass.count }.by(1)
storage_move = container.repository_storage_moves.last!
@@ -29,7 +29,7 @@ RSpec.shared_examples 'moves repository shard in bulk' do
expect(subject).to receive(:log_info)
.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)
+ .to change { move_service_klass.count }.by(0)
end
end
end
diff --git a/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb b/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb
index 31919a4263d..e72e8e79411 100644
--- a/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb
+++ b/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb
@@ -7,5 +7,5 @@ RSpec.shared_examples 'issue_edit snowplow tracking' do
let(:namespace) { project.namespace }
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
- it_behaves_like 'Snowplow event tracking'
+ it_behaves_like 'Snowplow event tracking with RedisHLL context'
end
diff --git a/spec/support/shared_examples/services/timelogs/create_service_shared_examples.rb b/spec/support/shared_examples/services/timelogs/create_service_shared_examples.rb
index 53c42ec0e00..00d4224f021 100644
--- a/spec/support/shared_examples/services/timelogs/create_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/timelogs/create_service_shared_examples.rb
@@ -1,6 +1,12 @@
# frozen_string_literal: true
RSpec.shared_examples 'issuable supports timelog creation service' do
+ let_it_be(:time_spent) { 3600 }
+ let_it_be(:spent_at) { Time.now }
+ let_it_be(:summary) { "Test summary" }
+
+ let(:service) { described_class.new(issuable, time_spent, spent_at, summary, user) }
+
shared_examples 'success_response' do
it 'sucessfully saves the timelog' do
is_expected.to be_success
@@ -9,7 +15,7 @@ RSpec.shared_examples 'issuable supports timelog creation service' do
expect(timelog).to be_persisted
expect(timelog.time_spent).to eq(time_spent)
- expect(timelog.spent_at).to eq('Fri, 08 Jul 2022 00:00:00.000000000 UTC +00:00')
+ expect(timelog.spent_at).to eq(spent_at)
expect(timelog.summary).to eq(summary)
expect(timelog.issuable).to eq(issuable)
end
@@ -34,6 +40,39 @@ RSpec.shared_examples 'issuable supports timelog creation service' do
users_container.add_reporter(user)
end
+ context 'when spent_at is in the future' do
+ let_it_be(:spent_at) { Time.now + 2.hours }
+
+ it 'returns an error' do
+ is_expected.to be_error
+
+ expect(subject.message).to eq("Spent at can't be a future date and time.")
+ expect(subject.http_status).to eq(404)
+ end
+ end
+
+ context 'when time_spent is zero' do
+ let_it_be(:time_spent) { 0 }
+
+ it 'returns an error' do
+ is_expected.to be_error
+
+ expect(subject.message).to eq("Time spent can't be zero.")
+ expect(subject.http_status).to eq(404)
+ end
+ end
+
+ context 'when time_spent is nil' do
+ let_it_be(:time_spent) { nil }
+
+ it 'returns an error' do
+ is_expected.to be_error
+
+ expect(subject.message).to eq("Time spent can't be blank")
+ expect(subject.http_status).to eq(404)
+ end
+ end
+
context 'when the timelog save fails' do
before do
allow_next_instance_of(Timelog) do |timelog|
@@ -54,6 +93,12 @@ RSpec.shared_examples 'issuable supports timelog creation service' do
end
RSpec.shared_examples 'issuable does not support timelog creation service' do
+ let_it_be(:time_spent) { 3600 }
+ let_it_be(:spent_at) { Time.now }
+ let_it_be(:summary) { "Test summary" }
+
+ let(:service) { described_class.new(issuable, time_spent, spent_at, summary, user) }
+
shared_examples 'error_response' do
it 'returns an error' do
is_expected.to be_error
diff --git a/spec/support/shared_examples/services/users/build_service_shared_examples.rb b/spec/support/shared_examples/services/users/build_service_shared_examples.rb
index 6a8695e1786..e448f2f874b 100644
--- a/spec/support/shared_examples/services/users/build_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/users/build_service_shared_examples.rb
@@ -84,9 +84,10 @@ RSpec.shared_examples_for 'current user not admin build items' do
end
end
- context 'when "send_user_confirmation_email" application setting is true' do
+ context 'when "email_confirmation_setting" application setting is set to `hard`' do
before do
- stub_application_setting(send_user_confirmation_email: true, signup_enabled?: true)
+ stub_application_setting_enum('email_confirmation_setting', 'hard')
+ stub_application_setting(signup_enabled?: true)
end
it 'does not confirm the user' do
@@ -94,9 +95,10 @@ RSpec.shared_examples_for 'current user not admin build items' do
end
end
- context 'when "send_user_confirmation_email" application setting is false' do
+ context 'when "email_confirmation_setting" application setting is set to `off`' do
before do
- stub_application_setting(send_user_confirmation_email: false, signup_enabled?: true)
+ stub_application_setting_enum('email_confirmation_setting', 'off')
+ stub_application_setting(signup_enabled?: true)
end
it 'confirms the user' do
diff --git a/spec/support/shared_examples/services/wiki_pages/create_service_shared_examples.rb b/spec/support/shared_examples/services/wiki_pages/create_service_shared_examples.rb
index 980a752cf86..ced49b3e481 100644
--- a/spec/support/shared_examples/services/wiki_pages/create_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/wiki_pages/create_service_shared_examples.rb
@@ -75,7 +75,7 @@ RSpec.shared_examples 'WikiPages::CreateService#execute' do |container_type|
end
it 'does not record the activity' do
- expect { service.execute }.not_to change(Event, :count)
+ expect { service.execute }.not_to change { Event.count }
end
it 'reports the error' do
diff --git a/spec/support/shared_examples/services/wiki_pages/update_service_shared_examples.rb b/spec/support/shared_examples/services/wiki_pages/update_service_shared_examples.rb
index fd10dd4367e..5511843e681 100644
--- a/spec/support/shared_examples/services/wiki_pages/update_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/wiki_pages/update_service_shared_examples.rb
@@ -79,7 +79,7 @@ RSpec.shared_examples 'WikiPages::UpdateService#execute' do |container_type|
end
it 'does not record the activity' do
- expect { service.execute page }.not_to change(Event, :count)
+ expect { service.execute page }.not_to change { Event.count }
end
it 'reports the error' do
diff --git a/spec/support/shared_examples/services/work_items/widgets/milestone_service_shared_examples.rb b/spec/support/shared_examples/services/work_items/widgets/milestone_service_shared_examples.rb
index ac17915c15a..ac064ed4c33 100644
--- a/spec/support/shared_examples/services/work_items/widgets/milestone_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/work_items/widgets/milestone_service_shared_examples.rb
@@ -23,7 +23,7 @@ RSpec.shared_examples "setting work item's milestone" do
it "sets the work item's milestone" do
expect { execute_callback }
- .to change(work_item, :milestone)
+ .to change { work_item.milestone }
.from(nil)
.to(group_milestone)
end
@@ -34,7 +34,7 @@ RSpec.shared_examples "setting work item's milestone" do
it "sets the work item's milestone" do
expect { execute_callback }
- .to change(work_item, :milestone)
+ .to change { work_item.milestone }
.from(nil)
.to(project_milestone)
end
diff --git a/spec/support/shared_examples/work_item_base_types_importer.rb b/spec/support/shared_examples/work_item_base_types_importer.rb
index 593670ac4b8..b1011037584 100644
--- a/spec/support/shared_examples/work_item_base_types_importer.rb
+++ b/spec/support/shared_examples/work_item_base_types_importer.rb
@@ -4,7 +4,7 @@ RSpec.shared_examples 'work item base types importer' do
it "creates all base work item types if they don't exist" do
WorkItems::Type.delete_all
- expect { subject }.to change(WorkItems::Type, :count).from(0).to(WorkItems::Type::BASE_TYPES.count)
+ expect { subject }.to change { WorkItems::Type.count }.from(0).to(WorkItems::Type::BASE_TYPES.count)
types_in_db = WorkItems::Type.all.map { |type| type.slice(:base_type, :icon_name, :name).symbolize_keys }
expected_types = WorkItems::Type::BASE_TYPES.map do |type, attributes|
@@ -25,7 +25,7 @@ RSpec.shared_examples 'work item base types importer' do
subject
first_type.reload
end.to not_change(WorkItems::Type, :count).and(
- change(first_type, :name).from(original_name.upcase).to(original_name)
+ change { first_type.name }.from(original_name.upcase).to(original_name)
)
end
@@ -40,7 +40,7 @@ RSpec.shared_examples 'work item base types importer' do
it 'inserts all types and does nothing if some already existed' do
expect { subject }.to make_queries_matching(/INSERT/, 1).and(
- change(WorkItems::Type, :count).by(1)
+ change { WorkItems::Type.count }.by(1)
)
expect(WorkItems::Type.count).to eq(WorkItems::Type::BASE_TYPES.count)
end
diff --git a/spec/support/shared_examples/work_item_hierarchy_restrictions_importer.rb b/spec/support/shared_examples/work_item_hierarchy_restrictions_importer.rb
new file mode 100644
index 00000000000..b75aa27b2b7
--- /dev/null
+++ b/spec/support/shared_examples/work_item_hierarchy_restrictions_importer.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'work item hierarchy restrictions importer' do
+ shared_examples_for 'adds restrictions' do
+ it "adds all restrictions if they don't exist" do
+ expect { subject }.to change { WorkItems::HierarchyRestriction.count }.from(0).to(4)
+ end
+ end
+
+ context 'when restrictions are missing' do
+ before do
+ WorkItems::HierarchyRestriction.delete_all
+ end
+
+ it_behaves_like 'adds restrictions'
+ end
+
+ context 'when base types are missing' do
+ before do
+ WorkItems::Type.delete_all
+ end
+
+ it_behaves_like 'adds restrictions'
+ end
+
+ context 'when restrictions already exist' do
+ before do
+ Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
+ end
+
+ it 'upserts restrictions' do
+ restriction = WorkItems::HierarchyRestriction.first
+ depth = restriction.maximum_depth
+
+ restriction.update!(maximum_depth: depth + 1)
+
+ expect do
+ subject
+ restriction.reload
+ end.to not_change { WorkItems::HierarchyRestriction.count }.and(
+ change { restriction.maximum_depth }.from(depth + 1).to(depth)
+ )
+ end
+ end
+
+ context 'when some restrictions are missing' do
+ before do
+ Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
+ WorkItems::HierarchyRestriction.limit(1).delete_all
+ end
+
+ it 'inserts missing restrictions and does nothing if some already existed' do
+ expect { subject }.to make_queries_matching(/INSERT/, 1).and(
+ change { WorkItems::HierarchyRestriction.count }.by(1)
+ )
+ expect(WorkItems::HierarchyRestriction.count).to eq(4)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb b/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb
new file mode 100644
index 00000000000..ae29b76ee87
--- /dev/null
+++ b/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb
@@ -0,0 +1,203 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'batched background migrations execution worker' do
+ include ExclusiveLeaseHelpers
+
+ it 'is a limited capacity worker' do
+ expect(described_class.new).to be_a(LimitedCapacity::Worker)
+ end
+
+ describe 'defining the job attributes' do
+ it 'defines the data_consistency as always' do
+ expect(described_class.get_data_consistency).to eq(:always)
+ end
+
+ it 'defines the feature_category as database' do
+ expect(described_class.get_feature_category).to eq(:database)
+ end
+
+ it 'defines the idempotency as false' do
+ expect(described_class).not_to be_idempotent
+ end
+
+ it 'does not retry failed jobs' do
+ expect(described_class.sidekiq_options['retry']).to eq(0)
+ end
+
+ it 'does not deduplicate jobs' do
+ expect(described_class.get_deduplicate_strategy).to eq(:none)
+ end
+
+ it 'defines the queue namespace' do
+ expect(described_class.queue_namespace).to eq('batched_background_migrations')
+ end
+ end
+
+ describe '.perform_with_capacity' do
+ it 'enqueues jobs without modifying provided arguments' do
+ expect_next_instance_of(described_class) do |instance|
+ expect(instance).to receive(:remove_failed_jobs)
+ end
+
+ args = [['main', 123]]
+
+ expect(described_class)
+ .to receive(:bulk_perform_async)
+ .with(args)
+
+ described_class.perform_with_capacity(args)
+ end
+ end
+
+ describe '.max_running_jobs' do
+ it 'returns MAX_RUNNING_MIGRATIONS' do
+ expect(described_class.max_running_jobs).to eq(described_class::MAX_RUNNING_MIGRATIONS)
+ end
+ end
+
+ describe '#max_running_jobs' do
+ it 'returns MAX_RUNNING_MIGRATIONS' do
+ expect(described_class.new.max_running_jobs).to eq(described_class::MAX_RUNNING_MIGRATIONS)
+ end
+ end
+
+ describe '#remaining_work_count' do
+ it 'returns 0' do
+ expect(described_class.new.remaining_work_count).to eq(0)
+ end
+ end
+
+ describe '#perform_work' do
+ let(:database_name) { Gitlab::Database::MAIN_DATABASE_NAME.to_sym }
+ let(:base_model) { Gitlab::Database.database_base_models[database_name] }
+ let(:table_name) { :events }
+ let(:job_interval) { 5.minutes }
+ let(:lease_timeout) { job_interval * described_class::LEASE_TIMEOUT_MULTIPLIER }
+ let(:interval_variance) { described_class::INTERVAL_VARIANCE }
+
+ subject(:worker) { described_class.new }
+
+ context 'when the feature flag is disabled' do
+ let(:migration) do
+ create(:batched_background_migration, :active, interval: job_interval, table_name: table_name)
+ end
+
+ before do
+ stub_feature_flags(execute_batched_migrations_on_schedule: false)
+ end
+
+ it 'does nothing' do
+ expect(Gitlab::Database::BackgroundMigration::BatchedMigration).not_to receive(:find_executable)
+ expect(worker).not_to receive(:run_migration_job)
+
+ worker.perform_work(database_name, migration.id)
+ end
+ end
+
+ context 'when the feature flag is enabled' do
+ before do
+ stub_feature_flags(execute_batched_migrations_on_schedule: true)
+ end
+
+ context 'when the provided database is sharing config' do
+ before do
+ skip_if_multiple_databases_not_setup
+ end
+
+ it 'does nothing' do
+ ci_model = Gitlab::Database.database_base_models['ci']
+ expect(Gitlab::Database).to receive(:db_config_share_with)
+ .with(ci_model.connection_db_config).and_return('main')
+
+ expect(Gitlab::Database::BackgroundMigration::BatchedMigration).not_to receive(:find_executable)
+ expect(worker).not_to receive(:run_migration_job)
+
+ worker.perform_work(:ci, 123)
+ end
+ end
+
+ context 'when migration does not exist' do
+ it 'does nothing' do
+ expect(worker).not_to receive(:run_migration_job)
+
+ worker.perform_work(database_name, non_existing_record_id)
+ end
+ end
+
+ context 'when migration exist' do
+ let(:migration) do
+ create(:batched_background_migration, :active, interval: job_interval, table_name: table_name)
+ end
+
+ before do
+ allow(Gitlab::Database::BackgroundMigration::BatchedMigration).to receive(:find_executable)
+ .with(migration.id, connection: base_model.connection)
+ .and_return(migration)
+ end
+
+ context 'when the migration is no longer active' do
+ it 'does not run the migration' do
+ expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
+
+ expect(migration).to receive(:active?).and_return(false)
+
+ expect(worker).not_to receive(:run_migration_job)
+
+ worker.perform_work(database_name, migration.id)
+ end
+ end
+
+ context 'when the interval has not elapsed' do
+ it 'does not run the migration' do
+ expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
+ expect(migration).to receive(:interval_elapsed?).with(variance: interval_variance).and_return(false)
+ expect(worker).not_to receive(:run_migration_job)
+
+ worker.perform_work(database_name, migration.id)
+ end
+ end
+
+ context 'when the migration is still active and the interval has elapsed' do
+ let(:table_name_lease_key) do
+ "#{described_class.name.underscore}:database_name:#{database_name}:" \
+ "table_name:#{table_name}"
+ end
+
+ context 'when can not obtain lease on the table name' do
+ it 'does nothing' do
+ stub_exclusive_lease_taken(table_name_lease_key, timeout: lease_timeout)
+
+ expect(worker).not_to receive(:run_migration_job)
+
+ worker.perform_work(database_name, migration.id)
+ end
+ end
+
+ it 'always cleans up the exclusive lease' do
+ expect_to_obtain_exclusive_lease(table_name_lease_key, 'uuid-table-name', timeout: lease_timeout)
+ expect_to_cancel_exclusive_lease(table_name_lease_key, 'uuid-table-name')
+
+ expect(worker).to receive(:run_migration_job).and_raise(RuntimeError, 'I broke')
+
+ expect { worker.perform_work(database_name, migration.id) }.to raise_error(RuntimeError, 'I broke')
+ end
+
+ it 'runs the migration' do
+ expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
+
+ expect_next_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigrationRunner) do |instance|
+ expect(instance).to receive(:run_migration_job).with(migration)
+ end
+
+ expect_to_obtain_exclusive_lease(table_name_lease_key, 'uuid-table-name', timeout: lease_timeout)
+ expect_to_cancel_exclusive_lease(table_name_lease_key, 'uuid-table-name')
+
+ expect(worker).to receive(:run_migration_job).and_call_original
+
+ worker.perform_work(database_name, migration.id)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb b/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb
index 0be55fd2a3e..09ebc495e61 100644
--- a/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb
+++ b/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb
@@ -125,10 +125,28 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
end
context 'when no active migrations exist' do
- it 'does nothing' do
- expect(worker).not_to receive(:run_active_migration)
+ context 'when parallel execution is disabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: false)
+ end
- worker.perform
+ it 'does nothing' do
+ expect(worker).not_to receive(:run_active_migration)
+
+ worker.perform
+ end
+ end
+
+ context 'when parallel execution is enabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: true)
+ end
+
+ it 'does nothing' do
+ expect(worker).not_to receive(:queue_migrations_for_execution)
+
+ worker.perform
+ end
end
end
@@ -136,7 +154,6 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
let(:job_interval) { 5.minutes }
let(:lease_timeout) { 15.minutes }
let(:lease_key) { described_class.name.demodulize.underscore }
- let(:interval_variance) { described_class::INTERVAL_VARIANCE }
let(:migration_id) { 123 }
let(:migration) do
build(
@@ -145,52 +162,86 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
)
end
+ let(:execution_worker_class) do
+ case tracking_database
+ when :main
+ Database::BatchedBackgroundMigration::MainExecutionWorker
+ when :ci
+ Database::BatchedBackgroundMigration::CiExecutionWorker
+ end
+ end
+
before do
allow(Gitlab::Database::BackgroundMigration::BatchedMigration).to receive(:active_migration)
.with(connection: base_model.connection)
.and_return(migration)
-
- allow(migration).to receive(:interval_elapsed?).with(variance: interval_variance).and_return(true)
- allow(migration).to receive(:reload)
end
- context 'when the calculated timeout is less than the minimum allowed' do
- let(:minimum_timeout) { described_class::MINIMUM_LEASE_TIMEOUT }
- let(:job_interval) { 2.minutes }
+ context 'when parallel execution is disabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: false)
+ end
+
+ let(:execution_worker) { instance_double(execution_worker_class) }
- it 'sets the lease timeout to the minimum value' do
- expect_to_obtain_exclusive_lease(lease_key, timeout: minimum_timeout)
+ context 'when the calculated timeout is less than the minimum allowed' do
+ let(:minimum_timeout) { described_class::MINIMUM_LEASE_TIMEOUT }
+ let(:job_interval) { 2.minutes }
- expect_next_instance_of(Database::BatchedBackgroundMigration::ExecutionWorker) do |worker|
- expect(worker).to receive(:perform).with(tracking_database, migration_id)
+ it 'sets the lease timeout to the minimum value' do
+ expect_to_obtain_exclusive_lease(lease_key, timeout: minimum_timeout)
+
+ expect(execution_worker_class).to receive(:new).and_return(execution_worker)
+ expect(execution_worker).to receive(:perform_work).with(tracking_database, migration_id)
+
+ expect(worker).to receive(:run_active_migration).and_call_original
+
+ worker.perform
end
+ end
- expect(worker).to receive(:run_active_migration).and_call_original
+ it 'always cleans up the exclusive lease' do
+ lease = stub_exclusive_lease_taken(lease_key, timeout: lease_timeout)
+
+ expect(lease).to receive(:try_obtain).and_return(true)
+
+ expect(worker).to receive(:run_active_migration).and_raise(RuntimeError, 'I broke')
+ expect(lease).to receive(:cancel)
+
+ expect { worker.perform }.to raise_error(RuntimeError, 'I broke')
+ end
+
+ it 'delegetes the execution to ExecutionWorker' do
+ base_model = Gitlab::Database.database_base_models[tracking_database]
+
+ expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
+ expect(execution_worker_class).to receive(:new).and_return(execution_worker)
+ expect(execution_worker).to receive(:perform_work).with(tracking_database, migration_id)
worker.perform
end
end
- it 'always cleans up the exclusive lease' do
- lease = stub_exclusive_lease_taken(lease_key, timeout: lease_timeout)
+ context 'when parallel execution is enabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: true)
+ end
- expect(lease).to receive(:try_obtain).and_return(true)
+ it 'delegetes the execution to ExecutionWorker' do
+ expect(Gitlab::Database::BackgroundMigration::BatchedMigration)
+ .to receive(:active_migrations_distinct_on_table).with(
+ connection: base_model.connection,
+ limit: execution_worker_class.max_running_jobs
+ ).and_return([migration])
- expect(worker).to receive(:run_active_migration).and_raise(RuntimeError, 'I broke')
- expect(lease).to receive(:cancel)
+ expected_arguments = [
+ [tracking_database.to_s, migration_id]
+ ]
- expect { worker.perform }.to raise_error(RuntimeError, 'I broke')
- end
+ expect(execution_worker_class).to receive(:perform_with_capacity).with(expected_arguments)
- it 'delegetes the execution to ExecutionWorker' do
- base_model = Gitlab::Database.database_base_models[tracking_database]
-
- expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
- expect_next_instance_of(Database::BatchedBackgroundMigration::ExecutionWorker) do |worker|
- expect(worker).to receive(:perform).with(tracking_database, migration_id)
+ worker.perform
end
-
- worker.perform
end
end
end
@@ -249,6 +300,8 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
end
before do
+ stub_feature_flags(execute_batched_migrations_on_schedule: true)
+
# Create example table populated with test data to migrate.
#
# Test data should have two records that won't be updated:
@@ -269,80 +322,96 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
WHERE some_column = #{migration_records - 5};
SQL
- stub_feature_flags(execute_batched_migrations_on_schedule: true)
-
stub_const('Gitlab::BackgroundMigration::ExampleDataMigration', migration_class)
end
- subject(:full_migration_run) do
- # process all batches, then do an extra execution to mark the job as finished
- (number_of_batches + 1).times do
- described_class.new.perform
+ shared_examples 'batched background migration execution' do
+ subject(:full_migration_run) do
+ # process all batches, then do an extra execution to mark the job as finished
+ (number_of_batches + 1).times do
+ described_class.new.perform
- travel_to((migration.interval + described_class::INTERVAL_VARIANCE).seconds.from_now)
+ travel_to((migration.interval + described_class::INTERVAL_VARIANCE).seconds.from_now)
+ end
end
- end
- it 'marks the migration record as finished' do
- expect { full_migration_run }.to change { migration.reload.status }.from(1).to(3) # active -> finished
- end
+ it 'marks the migration record as finished' do
+ expect { full_migration_run }.to change { migration.reload.status }.from(1).to(3) # active -> finished
+ end
- it 'creates job records for each processed batch', :aggregate_failures do
- expect { full_migration_run }.to change { migration.reload.batched_jobs.count }.from(0)
+ it 'creates job records for each processed batch', :aggregate_failures do
+ expect { full_migration_run }.to change { migration.reload.batched_jobs.count }.from(0)
- final_min_value = migration.batched_jobs.reduce(1) do |next_min_value, batched_job|
- expect(batched_job.min_value).to eq(next_min_value)
+ final_min_value = migration.batched_jobs.order(id: :asc).reduce(1) do |next_min_value, batched_job|
+ expect(batched_job.min_value).to eq(next_min_value)
- batched_job.max_value + 1
+ batched_job.max_value + 1
+ end
+
+ final_max_value = final_min_value - 1
+ expect(final_max_value).to eq(migration_records)
end
- final_max_value = final_min_value - 1
- expect(final_max_value).to eq(migration_records)
- end
+ it 'marks all job records as succeeded', :aggregate_failures do
+ expect { full_migration_run }.to change { migration.reload.batched_jobs.count }.from(0)
- it 'marks all job records as succeeded', :aggregate_failures do
- expect { full_migration_run }.to change { migration.reload.batched_jobs.count }.from(0)
+ expect(migration.batched_jobs).to all(be_succeeded)
+ end
- expect(migration.batched_jobs).to all(be_succeeded)
- end
+ it 'updates matching records in the range', :aggregate_failures do
+ expect { full_migration_run }
+ .to change { example_data.where('status = 1 AND some_column <> 0').count }
+ .from(migration_records).to(1)
- it 'updates matching records in the range', :aggregate_failures do
- expect { full_migration_run }
- .to change { example_data.where('status = 1 AND some_column <> 0').count }
- .from(migration_records).to(1)
+ record_outside_range = example_data.last
- record_outside_range = example_data.last
+ expect(record_outside_range.status).to eq(1)
+ expect(record_outside_range.some_column).not_to eq(0)
+ end
- expect(record_outside_range.status).to eq(1)
- expect(record_outside_range.some_column).not_to eq(0)
- end
+ it 'does not update non-matching records in the range' do
+ expect { full_migration_run }.not_to change { example_data.where('status <> 1 AND some_column <> 0').count }
+ end
- it 'does not update non-matching records in the range' do
- expect { full_migration_run }.not_to change { example_data.where('status <> 1 AND some_column <> 0').count }
- end
+ context 'health status' do
+ subject(:migration_run) { described_class.new.perform }
+
+ it 'puts migration on hold when there is autovaccum activity on related tables' do
+ swapout_view_for_table(:postgres_autovacuum_activity, connection: connection)
+ create(
+ :postgres_autovacuum_activity,
+ table: migration.table_name,
+ table_identifier: "public.#{migration.table_name}"
+ )
+
+ expect { migration_run }.to change { migration.reload.on_hold? }.from(false).to(true)
+ end
- context 'health status' do
- subject(:migration_run) { described_class.new.perform }
+ it 'puts migration on hold when the pending WAL count is above the limit' do
+ sql = Gitlab::Database::BackgroundMigration::HealthStatus::Indicators::WriteAheadLog::PENDING_WAL_COUNT_SQL
+ limit = Gitlab::Database::BackgroundMigration::HealthStatus::Indicators::WriteAheadLog::LIMIT
- it 'puts migration on hold when there is autovaccum activity on related tables' do
- swapout_view_for_table(:postgres_autovacuum_activity, connection: connection)
- create(
- :postgres_autovacuum_activity,
- table: migration.table_name,
- table_identifier: "public.#{migration.table_name}"
- )
+ expect(connection).to receive(:execute).with(sql).and_return([{ 'pending_wal_count' => limit + 1 }])
- expect { migration_run }.to change { migration.reload.on_hold? }.from(false).to(true)
+ expect { migration_run }.to change { migration.reload.on_hold? }.from(false).to(true)
+ end
end
+ end
- it 'puts migration on hold when the pending WAL count is above the limit' do
- sql = Gitlab::Database::BackgroundMigration::HealthStatus::Indicators::WriteAheadLog::PENDING_WAL_COUNT_SQL
- limit = Gitlab::Database::BackgroundMigration::HealthStatus::Indicators::WriteAheadLog::LIMIT
+ context 'when parallel execution is disabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: false)
+ end
- expect(connection).to receive(:execute).with(sql).and_return([{ 'pending_wal_count' => limit + 1 }])
+ it_behaves_like 'batched background migration execution'
+ end
- expect { migration_run }.to change { migration.reload.on_hold? }.from(false).to(true)
+ context 'when parallel execution is enabled', :sidekiq_inline do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: true)
end
+
+ it_behaves_like 'batched background migration execution'
end
end
end
diff --git a/spec/support/shared_examples/workers/schedule_bulk_repository_shard_moves_shared_examples.rb b/spec/support/shared_examples/workers/schedule_bulk_repository_shard_moves_shared_examples.rb
index 465aca63148..6707f65eb69 100644
--- a/spec/support/shared_examples/workers/schedule_bulk_repository_shard_moves_shared_examples.rb
+++ b/spec/support/shared_examples/workers/schedule_bulk_repository_shard_moves_shared_examples.rb
@@ -15,7 +15,7 @@ RSpec.shared_examples 'schedules bulk repository shard moves' do
let(:job_args) { [source_storage_name, destination_storage_name] }
it 'schedules container repository storage moves' do
- expect { subject }.to change(move_service_klass, :count).by(1)
+ expect { subject }.to change { move_service_klass.count }.by(1)
storage_move = container.repository_storage_moves.last!
diff --git a/spec/support/shared_examples/workers/update_repository_move_shared_examples.rb b/spec/support/shared_examples/workers/update_repository_move_shared_examples.rb
index babd7cfbbeb..c50dc6d5372 100644
--- a/spec/support/shared_examples/workers/update_repository_move_shared_examples.rb
+++ b/spec/support/shared_examples/workers/update_repository_move_shared_examples.rb
@@ -15,7 +15,7 @@ RSpec.shared_examples 'an update storage move worker' do
expect do
subject.perform(container.id, 'test_second_storage')
- end.to change(repository_storage_move_klass, :count).by(1)
+ end.to change { repository_storage_move_klass.count }.by(1)
storage_move = container.repository_storage_moves.last
expect(storage_move).to have_attributes(
@@ -32,7 +32,7 @@ RSpec.shared_examples 'an update storage move worker' do
expect do
subject.perform(nil, nil, repository_storage_move.id)
- end.not_to change(repository_storage_move_klass, :count)
+ end.not_to change { repository_storage_move_klass.count }
end
end
end