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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-01-20 12:16:11 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-01-20 12:16:11 +0300
commitedaa33dee2ff2f7ea3fac488d41558eb5f86d68c (patch)
tree11f143effbfeba52329fb7afbd05e6e2a3790241 /spec/support
parentd8a5691316400a0f7ec4f83832698f1988eb27c1 (diff)
Add latest changes from gitlab-org/gitlab@14-7-stable-eev14.7.0-rc42
Diffstat (limited to 'spec/support')
-rw-r--r--spec/support/database/cross-database-modification-allowlist.yml32
-rw-r--r--spec/support/db_cleaner.rb2
-rw-r--r--spec/support/flaky_tests.rb2
-rw-r--r--spec/support/gitlab_stubs/gitlab_ci.yml8
-rw-r--r--spec/support/helpers/cycle_analytics_helpers.rb2
-rw-r--r--spec/support/helpers/gitaly_setup.rb204
-rw-r--r--spec/support/helpers/login_helpers.rb2
-rw-r--r--spec/support/helpers/stub_gitlab_calls.rb7
-rw-r--r--spec/support/helpers/stub_object_storage.rb6
-rw-r--r--spec/support/helpers/test_env.rb132
-rw-r--r--spec/support/helpers/usage_data_helpers.rb4
-rw-r--r--spec/support/import_export/export_file_helper.rb2
-rw-r--r--spec/support/praefect.rb4
-rw-r--r--spec/support/shared_contexts/navbar_structure_context.rb1
-rw-r--r--spec/support/shared_contexts/policies/group_policy_shared_context.rb39
-rw-r--r--spec/support/shared_contexts/policies/project_policy_shared_context.rb2
-rw-r--r--spec/support/shared_examples/controllers/create_notes_rate_limit_shared_examples.rb43
-rw-r--r--spec/support/shared_examples/controllers/rate_limited_endpoint_shared_examples.rb57
-rw-r--r--spec/support/shared_examples/features/access_tokens_shared_examples.rb165
-rw-r--r--spec/support/shared_examples/features/packages_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb127
-rw-r--r--spec/support/shared_examples/features/sidebar_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/graphql/mutation_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/graphql/mutations/issues/permission_check_shared_examples.rb44
-rw-r--r--spec/support/shared_examples/graphql/mutations/merge_requests/permission_check_shared_examples.rb50
-rw-r--r--spec/support/shared_examples/lib/gitlab/database/background_migration_job_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/lib/gitlab/redis/multi_store_feature_flags_shared_examples.rb43
-rw-r--r--spec/support/shared_examples/lib/gitlab/unique_ip_check_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb10
-rw-r--r--spec/support/shared_examples/metrics/sampler_shared_examples.rb84
-rw-r--r--spec/support/shared_examples/models/application_setting_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb33
-rw-r--r--spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb231
-rw-r--r--spec/support/shared_examples/models/concerns/packages/destructible_shared_examples.rb18
-rw-r--r--spec/support/shared_examples/models/concerns/ttl_expirable_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/models/member_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb21
-rw-r--r--spec/support/shared_examples/models/update_project_statistics_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/namespaces/traversal_scope_examples.rb25
-rw-r--r--spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb (renamed from spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb)46
-rw-r--r--spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb1
-rw-r--r--spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb24
-rw-r--r--spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb1
-rw-r--r--spec/support/shared_examples/services/alert_management_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/incident_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/services/service_ping/service_ping_payload_with_all_expected_metrics_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/service_ping/service_ping_payload_without_restricted_metrics_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/work_item_base_types_importer.rb4
-rw-r--r--spec/support/shared_examples/workers/concerns/dependency_proxy/cleanup_worker_shared_examples.rb14
-rw-r--r--spec/support/system_exit_detected.rb15
52 files changed, 1142 insertions, 456 deletions
diff --git a/spec/support/database/cross-database-modification-allowlist.yml b/spec/support/database/cross-database-modification-allowlist.yml
index d6e74349069..fe51488c706 100644
--- a/spec/support/database/cross-database-modification-allowlist.yml
+++ b/spec/support/database/cross-database-modification-allowlist.yml
@@ -1,31 +1 @@
-- "./ee/spec/mailers/notify_spec.rb"
-- "./ee/spec/models/group_member_spec.rb"
-- "./ee/spec/replicators/geo/terraform_state_version_replicator_spec.rb"
-- "./ee/spec/services/ci/retry_build_service_spec.rb"
-- "./spec/controllers/abuse_reports_controller_spec.rb"
-- "./spec/controllers/omniauth_callbacks_controller_spec.rb"
-- "./spec/controllers/projects/issues_controller_spec.rb"
-- "./spec/features/issues/issue_detail_spec.rb"
-- "./spec/features/projects/pipelines/pipeline_spec.rb"
-- "./spec/features/signed_commits_spec.rb"
-- "./spec/helpers/issuables_helper_spec.rb"
-- "./spec/lib/gitlab/auth_spec.rb"
-- "./spec/lib/gitlab/ci/pipeline/chain/create_spec.rb"
-- "./spec/lib/gitlab/email/handler/create_issue_handler_spec.rb"
-- "./spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb"
-- "./spec/lib/gitlab/email/handler/create_note_handler_spec.rb"
-- "./spec/lib/gitlab/email/handler/create_note_on_issuable_handler_spec.rb"
-- "./spec/models/ci/build_trace_chunk_spec.rb"
-- "./spec/models/ci/job_artifact_spec.rb"
-- "./spec/models/ci/runner_spec.rb"
-- "./spec/models/clusters/applications/runner_spec.rb"
-- "./spec/models/design_management/version_spec.rb"
-- "./spec/models/hooks/system_hook_spec.rb"
-- "./spec/models/members/project_member_spec.rb"
-- "./spec/models/user_spec.rb"
-- "./spec/models/user_status_spec.rb"
-- "./spec/requests/api/commits_spec.rb"
-- "./spec/services/ci/retry_build_service_spec.rb"
-- "./spec/services/projects/overwrite_project_service_spec.rb"
-- "./spec/workers/merge_requests/create_pipeline_worker_spec.rb"
-- "./spec/workers/repository_cleanup_worker_spec.rb"
+[]
diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb
index 316d645f99f..fb70f82ef87 100644
--- a/spec/support/db_cleaner.rb
+++ b/spec/support/db_cleaner.rb
@@ -67,7 +67,7 @@ module DbCleaner
# Migrate each database individually
with_reestablished_active_record_base do
all_connection_classes.each do |connection_class|
- ActiveRecord::Base.establish_connection(connection_class.connection_db_config)
+ ActiveRecord::Base.establish_connection(connection_class.connection_db_config) # rubocop: disable Database/EstablishConnection
ActiveRecord::Tasks::DatabaseTasks.migrate
end
diff --git a/spec/support/flaky_tests.rb b/spec/support/flaky_tests.rb
index 0c211af695d..5ce55c47aab 100644
--- a/spec/support/flaky_tests.rb
+++ b/spec/support/flaky_tests.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
return unless ENV['CI']
-return unless ENV['SKIP_FLAKY_TESTS_AUTOMATICALLY'] == "true"
+return if ENV['SKIP_FLAKY_TESTS_AUTOMATICALLY'] == "false"
return if ENV['CI_MERGE_REQUEST_LABELS'].to_s.include?('pipeline:run-flaky-tests')
require_relative '../../tooling/rspec_flaky/report'
diff --git a/spec/support/gitlab_stubs/gitlab_ci.yml b/spec/support/gitlab_stubs/gitlab_ci.yml
index f3755e52b2c..52ae36229a6 100644
--- a/spec/support/gitlab_stubs/gitlab_ci.yml
+++ b/spec/support/gitlab_stubs/gitlab_ci.yml
@@ -9,7 +9,7 @@ before_script:
variables:
DB_NAME: postgres
-types:
+stages:
- test
- deploy
- notify
@@ -36,7 +36,7 @@ staging:
KEY1: value1
KEY2: value2
script: "cap deploy stating"
- type: deploy
+ stage: deploy
tags:
- ruby
- mysql
@@ -46,7 +46,7 @@ staging:
production:
variables:
DB_NAME: mysql
- type: deploy
+ stage: deploy
script:
- cap deploy production
- cap notify
@@ -58,7 +58,7 @@ production:
- /^deploy-.*$/
dockerhub:
- type: notify
+ stage: notify
script: "curl http://dockerhub/URL"
tags:
- ruby
diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb
index 722d484609c..70b794f7d82 100644
--- a/spec/support/helpers/cycle_analytics_helpers.rb
+++ b/spec/support/helpers/cycle_analytics_helpers.rb
@@ -59,7 +59,7 @@ module CycleAnalyticsHelpers
def save_value_stream(custom_value_stream_name)
fill_in 'create-value-stream-name', with: custom_value_stream_name
- page.find_button(s_('CreateValueStreamForm|Create Value Stream')).click
+ page.find_button(s_('CreateValueStreamForm|Create value stream')).click
wait_for_requests
end
diff --git a/spec/support/helpers/gitaly_setup.rb b/spec/support/helpers/gitaly_setup.rb
index 923051a2e04..905c439f4d9 100644
--- a/spec/support/helpers/gitaly_setup.rb
+++ b/spec/support/helpers/gitaly_setup.rb
@@ -9,8 +9,13 @@
require 'securerandom'
require 'socket'
require 'logger'
+require 'bundler'
module GitalySetup
+ extend self
+
+ REPOS_STORAGE = 'default'
+
LOGGER = begin
default_name = ENV['CI'] ? 'DEBUG' : 'WARN'
level_name = ENV['GITLAB_TESTING_LOG_LEVEL']&.upcase
@@ -52,11 +57,13 @@ module GitalySetup
def env
{
- 'HOME' => expand_path('tmp/tests'),
'GEM_PATH' => Gem.path.join(':'),
- 'BUNDLE_APP_CONFIG' => File.join(gemfile_dir, '.bundle'),
'BUNDLE_INSTALL_FLAGS' => nil,
+ 'BUNDLE_IGNORE_CONFIG' => '1',
+ 'BUNDLE_PATH' => bundle_path,
'BUNDLE_GEMFILE' => gemfile,
+ 'BUNDLE_JOBS' => '4',
+ 'BUNDLE_RETRY' => '3',
'RUBYOPT' => nil,
# Git hooks can't run during tests as the internal API is not running.
@@ -65,17 +72,20 @@ module GitalySetup
}
end
- # rubocop:disable GitlabSecurity/SystemCommandInjection
- def set_bundler_config
- system('bundle config set --local jobs 4', chdir: gemfile_dir)
- system('bundle config set --local retry 3', chdir: gemfile_dir)
+ def bundle_path
+ # Allow the user to override BUNDLE_PATH if they need to
+ return ENV['GITALY_TEST_BUNDLE_PATH'] if ENV['GITALY_TEST_BUNDLE_PATH']
if ENV['CI']
- bundle_path = expand_path('vendor/gitaly-ruby')
- system('bundle', 'config', 'set', '--local', 'path', bundle_path, chdir: gemfile_dir)
+ expand_path('vendor/gitaly-ruby')
+ else
+ explicit_path = Bundler.configured_bundle_path.explicit_path
+
+ return unless explicit_path
+
+ expand_path(explicit_path)
end
end
- # rubocop:enable GitlabSecurity/SystemCommandInjection
def config_path(service)
case service
@@ -88,6 +98,10 @@ module GitalySetup
end
end
+ def repos_path(storage = REPOS_STORAGE)
+ Gitlab.config.repositories.storages[REPOS_STORAGE].legacy_disk_path
+ end
+
def service_binary(service)
case service
when :gitaly, :gitaly2
@@ -97,16 +111,20 @@ module GitalySetup
end
end
+ def run_command(cmd, env: {})
+ system(env, *cmd, exception: true, chdir: tmp_tests_gitaly_dir)
+ end
+
def install_gitaly_gems
- system(env, "make #{tmp_tests_gitaly_dir}/.ruby-bundle", chdir: tmp_tests_gitaly_dir) # rubocop:disable GitlabSecurity/SystemCommandInjection
+ run_command(%W[make #{tmp_tests_gitaly_dir}/.ruby-bundle], env: env)
end
def build_gitaly
- system(env.merge({ 'GIT_VERSION' => nil }), 'make all git', chdir: tmp_tests_gitaly_dir) # rubocop:disable GitlabSecurity/SystemCommandInjection
+ run_command(%w[make all git], env: env.merge('GIT_VERSION' => nil))
end
- def start_gitaly
- start(:gitaly)
+ def start_gitaly(toml = nil)
+ start(:gitaly, toml)
end
def start_gitaly2
@@ -117,14 +135,20 @@ module GitalySetup
start(:praefect)
end
- def start(service)
+ def start(service, toml = nil)
+ toml ||= config_path(service)
args = ["#{tmp_tests_gitaly_bin_dir}/#{service_binary(service)}"]
args.push("-config") if service == :praefect
- args.push(config_path(service))
+ args.push(toml)
+
+ # Ensure user configuration does not affect Git
+ # Context: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/58776#note_547613780
+ env = self.env.merge('HOME' => nil, 'XDG_CONFIG_HOME' => nil)
+
pid = spawn(env, *args, [:out, :err] => "log/#{service}-test.log")
begin
- try_connect!(service)
+ try_connect!(service, toml)
rescue StandardError
Process.kill('TERM', pid)
raise
@@ -161,29 +185,37 @@ module GitalySetup
abort 'bundle check failed' unless system(env, 'bundle', 'check', out: out, chdir: gemfile_dir)
end
- def read_socket_path(service)
+ def connect_proc(toml)
# 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(config_path(service))
+ config_text = IO.read(toml)
config_text.lines.each do |line|
- match_data = line.match(/^\s*socket_path\s*=\s*"([^"]*)"$/)
+ match_data = line.match(/^\s*(socket_path|listen_addr)\s*=\s*"([^"]*)"$/)
- return match_data[1] if match_data
+ next unless match_data
+
+ case match_data[1]
+ when 'socket_path'
+ return -> { UNIXSocket.new(match_data[2]) }
+ when 'listen_addr'
+ addr, port = match_data[2].split(':')
+ return -> { TCPSocket.new(addr, port.to_i) }
+ end
end
- raise "failed to find socket_path in #{config_path(service)}"
+ raise "failed to find socket_path or listen_addr in #{toml}"
end
- def try_connect!(service)
+ def try_connect!(service, toml)
LOGGER.debug "Trying to connect to #{service}: "
timeout = 20
delay = 0.1
- socket = read_socket_path(service)
+ connect = connect_proc(toml)
Integer(timeout / delay).times do
- UNIXSocket.new(socket)
+ connect.call
LOGGER.debug " OK\n"
return
@@ -194,6 +226,128 @@ module GitalySetup
LOGGER.warn " FAILED to connect to #{service}\n"
- raise "could not connect to #{socket}"
+ raise "could not connect to #{service}"
+ end
+
+ def gitaly_socket_path
+ Gitlab::GitalyClient.address(REPOS_STORAGE).delete_prefix('unix:')
+ end
+
+ def gitaly_dir
+ socket_path = gitaly_socket_path
+ socket_path = File.expand_path(gitaly_socket_path) if expand_path_for_socket?
+
+ File.dirname(socket_path)
+ end
+
+ # Linux fails with "bind: invalid argument" if a UNIX socket path exceeds 108 characters:
+ # https://github.com/golang/go/issues/6895. We use absolute paths in CI to ensure
+ # that changes in the current working directory don't affect GRPC reconnections.
+ def expand_path_for_socket?
+ !!ENV['CI']
+ end
+
+ def setup_gitaly
+ unless ENV['CI']
+ # In CI Gitaly is built in the setup-test-env job and saved in the
+ # artifacts. So when tests are started, there's no need to build Gitaly.
+ build_gitaly
+ end
+
+ Gitlab::SetupHelper::Gitaly.create_configuration(
+ gitaly_dir,
+ { 'default' => repos_path },
+ force: true,
+ options: {
+ prometheus_listen_addr: 'localhost:9236'
+ }
+ )
+ Gitlab::SetupHelper::Gitaly.create_configuration(
+ gitaly_dir,
+ { 'default' => repos_path },
+ force: true,
+ options: {
+ internal_socket_dir: File.join(gitaly_dir, "internal_gitaly2"),
+ gitaly_socket: "gitaly2.socket",
+ config_filename: "gitaly2.config.toml"
+ }
+ )
+ Gitlab::SetupHelper::Praefect.create_configuration(gitaly_dir, { 'praefect' => repos_path }, force: true)
+ end
+
+ def socket_path(service)
+ File.join(tmp_tests_gitaly_dir, "#{service}.socket")
+ end
+
+ def praefect_socket_path
+ "unix:" + socket_path(:praefect)
+ end
+
+ def stop(pid)
+ Process.kill('KILL', pid)
+ rescue Errno::ESRCH
+ # The process can already be gone if the test run was INTerrupted.
+ end
+
+ def spawn_gitaly(toml = nil)
+ check_gitaly_config!
+
+ pids = []
+
+ if toml
+ pids << start_gitaly(toml)
+ else
+ pids << start_gitaly
+ pids << start_gitaly2
+ pids << start_praefect
+ end
+
+ Kernel.at_exit do
+ # In CI, this function is called by scripts/gitaly-test-spawn, triggered
+ # in a before_script. Gitaly needs to remain running until the container
+ # is stopped.
+ next if ENV['CI']
+ # In Workhorse tests (locally or in CI), this function is called by
+ # scripts/gitaly-test-spawn during `make test`. Gitaly needs to remain
+ # running until `make test` cleans it up.
+ next if ENV['GITALY_PID_FILE']
+
+ pids.each { |pid| stop(pid) }
+ end
+ rescue StandardError
+ raise gitaly_failure_message
+ end
+
+ def gitaly_failure_message
+ message = "gitaly spawn failed\n\n"
+
+ message += "- The `gitaly` binary does not exist: #{gitaly_binary}\n" unless File.exist?(gitaly_binary)
+ message += "- The `praefect` binary does not exist: #{praefect_binary}\n" unless File.exist?(praefect_binary)
+ message += "- The `git` binary does not exist: #{git_binary}\n" unless File.exist?(git_binary)
+
+ message += "\nCheck log/gitaly-test.log for errors.\n"
+
+ unless ci?
+ message += "\nIf binaries are missing, try running `make -C tmp/tests/gitaly build git.`\n"
+ message += "\nOtherwise, try running `rm -rf #{tmp_tests_gitaly_dir}`."
+ end
+
+ message
+ end
+
+ def git_binary
+ File.join(tmp_tests_gitaly_dir, "_build", "deps", "git", "install", "bin", "git")
+ end
+
+ def gitaly_binary
+ File.join(tmp_tests_gitaly_dir, "_build", "bin", "gitaly")
+ end
+
+ def praefect_binary
+ File.join(tmp_tests_gitaly_dir, "_build", "bin", "praefect")
+ end
+
+ def git_binary_exists?
+ File.exist?(git_binary)
end
end
diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb
index d9157fa7485..4e0e8dd96ee 100644
--- a/spec/support/helpers/login_helpers.rb
+++ b/spec/support/helpers/login_helpers.rb
@@ -95,7 +95,7 @@ module LoginHelpers
visit new_user_session_path
fill_in "user_login", with: user.email
- fill_in "user_password", with: "12345678"
+ fill_in "user_password", with: Gitlab::Password.test_default
check 'user_remember_me' if remember
click_button "Sign in"
diff --git a/spec/support/helpers/stub_gitlab_calls.rb b/spec/support/helpers/stub_gitlab_calls.rb
index ae031f58bd4..c3459f7bc81 100644
--- a/spec/support/helpers/stub_gitlab_calls.rb
+++ b/spec/support/helpers/stub_gitlab_calls.rb
@@ -92,12 +92,7 @@ module StubGitlabCalls
end
def stub_commonmark_sourcepos_disabled
- render_options =
- if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
- Banzai::Filter::MarkdownEngines::CommonMark::RENDER_OPTIONS_C
- else
- Banzai::Filter::MarkdownEngines::CommonMark::RENDER_OPTIONS_RUBY
- end
+ render_options = Banzai::Filter::MarkdownEngines::CommonMark::RENDER_OPTIONS
allow_any_instance_of(Banzai::Filter::MarkdownEngines::CommonMark)
.to receive(:render_options)
diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb
index 5e86b08aa45..d49a14f7f5b 100644
--- a/spec/support/helpers/stub_object_storage.rb
+++ b/spec/support/helpers/stub_object_storage.rb
@@ -91,6 +91,12 @@ module StubObjectStorage
**params)
end
+ def stub_ci_secure_file_object_storage(**params)
+ stub_object_storage_uploader(config: Gitlab.config.ci_secure_files.object_store,
+ uploader: Ci::SecureFileUploader,
+ **params)
+ end
+
def stub_terraform_state_object_storage(**params)
stub_object_storage_uploader(config: Gitlab.config.terraform_state.object_store,
uploader: Terraform::StateUploader,
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index d36bc4e3cb4..5c3ca92c4d0 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'parallel'
+require_relative 'gitaly_setup'
module TestEnv
extend self
@@ -93,7 +94,6 @@ module TestEnv
}.freeze
TMP_TEST_PATH = Rails.root.join('tmp', 'tests').freeze
- REPOS_STORAGE = 'default'
SECOND_STORAGE_PATH = Rails.root.join('tmp', 'tests', 'second_storage')
SETUP_METHODS = %i[setup_gitaly setup_gitlab_shell setup_workhorse setup_factory_repo setup_forked_repo].freeze
@@ -128,7 +128,7 @@ module TestEnv
# Can be overriden
def post_init
- start_gitaly(gitaly_dir)
+ start_gitaly
end
# Clean /tmp/tests
@@ -142,12 +142,15 @@ module TestEnv
end
FileUtils.mkdir_p(
- Gitlab::GitalyClient::StorageSettings.allow_disk_access { TestEnv.repos_path }
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access { GitalySetup.repos_path }
)
FileUtils.mkdir_p(SECOND_STORAGE_PATH)
FileUtils.mkdir_p(backup_path)
FileUtils.mkdir_p(pages_path)
FileUtils.mkdir_p(artifacts_path)
+ FileUtils.mkdir_p(lfs_path)
+ FileUtils.mkdir_p(terraform_state_path)
+ FileUtils.mkdir_p(packages_path)
end
def setup_gitlab_shell
@@ -156,111 +159,28 @@ module TestEnv
def setup_gitaly
component_timed_setup('Gitaly',
- install_dir: gitaly_dir,
+ install_dir: GitalySetup.gitaly_dir,
version: Gitlab::GitalyClient.expected_server_version,
- task: "gitlab:gitaly:test_install",
- task_args: [gitaly_dir, repos_path, gitaly_url].compact) do
- Gitlab::SetupHelper::Gitaly.create_configuration(
- gitaly_dir,
- { 'default' => repos_path },
- force: true,
- options: {
- prometheus_listen_addr: 'localhost:9236'
- }
- )
- Gitlab::SetupHelper::Gitaly.create_configuration(
- gitaly_dir,
- { 'default' => repos_path },
- force: true,
- options: {
- internal_socket_dir: File.join(gitaly_dir, "internal_gitaly2"),
- gitaly_socket: "gitaly2.socket",
- config_filename: "gitaly2.config.toml"
- }
- )
- Gitlab::SetupHelper::Praefect.create_configuration(gitaly_dir, { 'praefect' => repos_path }, force: true)
- end
- end
-
- def gitaly_socket_path
- Gitlab::GitalyClient.address('default').sub(/\Aunix:/, '')
- end
-
- def gitaly_dir
- socket_path = gitaly_socket_path
- socket_path = File.expand_path(gitaly_socket_path) if expand_path?
-
- File.dirname(socket_path)
- end
-
- # Linux fails with "bind: invalid argument" if a UNIX socket path exceeds 108 characters:
- # https://github.com/golang/go/issues/6895. We use absolute paths in CI to ensure
- # that changes in the current working directory don't affect GRPC reconnections.
- def expand_path?
- !!ENV['CI']
+ task: "gitlab:gitaly:clone",
+ fresh_install: ENV.key?('FORCE_GITALY_INSTALL'),
+ task_args: [GitalySetup.gitaly_dir, GitalySetup.repos_path, gitaly_url].compact) do
+ GitalySetup.setup_gitaly
+ end
end
- def start_gitaly(gitaly_dir)
+ def start_gitaly
if ci?
# Gitaly has been spawned outside this process already
return
end
- spawn_script = Rails.root.join('scripts/gitaly-test-spawn').to_s
- Bundler.with_original_env do
- unless system(spawn_script)
- message = 'gitaly spawn failed'
- message += " (try `rm -rf #{gitaly_dir}` ?)" unless ci?
- raise message
- end
- end
-
- gitaly_pid = Integer(File.read(TMP_TEST_PATH.join('gitaly.pid')))
- gitaly2_pid = Integer(File.read(TMP_TEST_PATH.join('gitaly2.pid')))
- praefect_pid = Integer(File.read(TMP_TEST_PATH.join('praefect.pid')))
-
- Kernel.at_exit do
- pids = [gitaly_pid, gitaly2_pid, praefect_pid]
- pids.each { |pid| stop(pid) }
- end
-
- wait('gitaly')
- wait('praefect')
- end
-
- def stop(pid)
- Process.kill('KILL', pid)
- rescue Errno::ESRCH
- # The process can already be gone if the test run was INTerrupted.
+ GitalySetup.spawn_gitaly
end
def gitaly_url
ENV.fetch('GITALY_REPO_URL', nil)
end
- def socket_path(service)
- TMP_TEST_PATH.join('gitaly', "#{service}.socket").to_s
- end
-
- def praefect_socket_path
- "unix:" + socket_path(:praefect)
- end
-
- def wait(service)
- sleep_time = 10
- sleep_interval = 0.1
- socket = socket_path(service)
-
- Integer(sleep_time / sleep_interval).times do
- Socket.unix(socket)
- return
- rescue StandardError
- sleep sleep_interval
- end
-
- raise "could not connect to #{service} at #{socket.inspect} after #{sleep_time} seconds"
- end
-
# Feature specs are run through Workhorse
def setup_workhorse
# Always rebuild the config file
@@ -376,8 +296,7 @@ module TestEnv
def rm_storage_dir(storage, dir)
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repos_path = Gitlab.config.repositories.storages[storage].legacy_disk_path
- target_repo_refs_path = File.join(repos_path, dir)
+ target_repo_refs_path = File.join(GitalySetup.repos_path(storage), dir)
FileUtils.remove_dir(target_repo_refs_path)
end
rescue Errno::ENOENT
@@ -385,8 +304,7 @@ module TestEnv
def storage_dir_exists?(storage, dir)
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repos_path = Gitlab.config.repositories.storages[storage].legacy_disk_path
- File.exist?(File.join(repos_path, dir))
+ File.exist?(File.join(GitalySetup.repos_path(storage), dir))
end
end
@@ -399,7 +317,7 @@ module TestEnv
end
def repos_path
- @repos_path ||= Gitlab.config.repositories.storages[REPOS_STORAGE].legacy_disk_path
+ @repos_path ||= GitalySetup.repos_path
end
def backup_path
@@ -414,6 +332,18 @@ module TestEnv
Gitlab.config.artifacts.storage_path
end
+ def lfs_path
+ Gitlab.config.lfs.storage_path
+ end
+
+ def terraform_state_path
+ Gitlab.config.terraform_state.storage_path
+ end
+
+ def packages_path
+ Gitlab.config.packages.storage_path
+ end
+
# When no cached assets exist, manually hit the root path to create them
#
# Otherwise they'd be created by the first test, often timing out and
@@ -512,7 +442,7 @@ module TestEnv
end
end
- def component_timed_setup(component, install_dir:, version:, task:, task_args: [])
+ def component_timed_setup(component, install_dir:, version:, task:, fresh_install: true, task_args: [])
start = Time.now
ensure_component_dir_name_is_correct!(component, install_dir)
@@ -522,7 +452,7 @@ module TestEnv
if component_needs_update?(install_dir, version)
# Cleanup the component entirely to ensure we start fresh
- FileUtils.rm_rf(install_dir)
+ FileUtils.rm_rf(install_dir) if fresh_install
if ENV['SKIP_RAILS_ENV_IN_RAKE']
# When we run `scripts/setup-test-env`, we take care of loading the necessary dependencies
diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb
index 5865bafd382..776ea37ffdc 100644
--- a/spec/support/helpers/usage_data_helpers.rb
+++ b/spec/support/helpers/usage_data_helpers.rb
@@ -183,6 +183,10 @@ module UsageDataHelpers
)
end
+ def stub_database_flavor_check(flavor = nil)
+ allow(ApplicationRecord.database).to receive(:flavor).and_return(flavor)
+ end
+
def clear_memoized_values(values)
values.each { |v| described_class.clear_memoization(v) }
end
diff --git a/spec/support/import_export/export_file_helper.rb b/spec/support/import_export/export_file_helper.rb
index f862a9bc1a4..3134e5c32a3 100644
--- a/spec/support/import_export/export_file_helper.rb
+++ b/spec/support/import_export/export_file_helper.rb
@@ -44,7 +44,7 @@ module ExportFileHelper
create(:ci_trigger, project: project)
key = create(:deploy_key)
key.projects << project
- create(:service, project: project)
+ create(:integration, project: project)
create(:project_hook, project: project, token: 'token')
create(:protected_branch, project: project)
diff --git a/spec/support/praefect.rb b/spec/support/praefect.rb
index 3218275c2aa..451b47cc83c 100644
--- a/spec/support/praefect.rb
+++ b/spec/support/praefect.rb
@@ -1,11 +1,11 @@
# frozen_string_literal: true
-require_relative 'helpers/test_env'
+require_relative 'helpers/gitaly_setup'
RSpec.configure do |config|
config.before(:each, :praefect) do
allow(Gitlab.config.repositories.storages['default']).to receive(:[]).and_call_original
allow(Gitlab.config.repositories.storages['default']).to receive(:[]).with('gitaly_address')
- .and_return(TestEnv.praefect_socket_path)
+ .and_return(GitalySetup.praefect_socket_path)
end
end
diff --git a/spec/support/shared_contexts/navbar_structure_context.rb b/spec/support/shared_contexts/navbar_structure_context.rb
index 085f1f13c2c..27967850389 100644
--- a/spec/support/shared_contexts/navbar_structure_context.rb
+++ b/spec/support/shared_contexts/navbar_structure_context.rb
@@ -142,6 +142,7 @@ RSpec.shared_context 'group navbar structure' do
nav_sub_items: [
_('General'),
_('Integrations'),
+ _('Access Tokens'),
_('Projects'),
_('Repository'),
_('CI/CD'),
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 ad6462dc367..0dfd76de79c 100644
--- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
@@ -8,7 +8,14 @@ RSpec.shared_context 'GroupPolicy context' do
let_it_be(:owner) { create(:user) }
let_it_be(:admin) { create(:admin) }
let_it_be(:non_group_member) { create(:user) }
- let_it_be(:group, refind: true) { create(:group, :private, :owner_subgroup_creation_only) }
+ let_it_be(:group, refind: true) { create(:group, :private, :owner_subgroup_creation_only, :crm_enabled) }
+
+ let(:public_permissions) do
+ %i[
+ read_group read_counts
+ read_label read_issue_board_list read_milestone read_issue_board
+ ]
+ end
let(:guest_permissions) do
%i[
@@ -18,8 +25,6 @@ RSpec.shared_context 'GroupPolicy context' do
]
end
- let(:read_group_permissions) { %i[read_label read_issue_board_list read_milestone read_issue_board] }
-
let(:reporter_permissions) do
%i[
admin_label
@@ -28,6 +33,8 @@ RSpec.shared_context 'GroupPolicy context' do
read_metrics_dashboard_annotation
read_prometheus
read_package_settings
+ read_crm_contact
+ read_crm_organization
]
end
@@ -48,22 +55,24 @@ RSpec.shared_context 'GroupPolicy context' do
destroy_package
create_projects
read_cluster create_cluster update_cluster admin_cluster add_cluster
- admin_group_runners
]
end
let(:owner_permissions) do
- [
- :owner_access,
- :admin_group,
- :admin_namespace,
- :admin_group_member,
- :change_visibility_level,
- :set_note_created_at,
- :create_subgroup,
- :read_statistics,
- :update_default_branch_protection
- ].compact
+ %i[
+ owner_access
+ admin_group
+ admin_namespace
+ admin_group_member
+ change_visibility_level
+ set_note_created_at
+ create_subgroup
+ read_statistics
+ update_default_branch_protection
+ read_group_runners
+ admin_group_runners
+ register_group_runners
+ ]
end
let(:admin_permissions) { %i[read_confidential_issues] }
diff --git a/spec/support/shared_contexts/policies/project_policy_shared_context.rb b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
index 8a90f887381..c39252cef13 100644
--- a/spec/support/shared_contexts/policies/project_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
@@ -50,7 +50,7 @@ RSpec.shared_context 'ProjectPolicy context' do
resolve_note update_build update_commit_status update_container_image
update_deployment update_environment update_merge_request
update_metrics_dashboard_annotation update_pipeline update_release destroy_release
- read_resource_group update_resource_group
+ read_resource_group update_resource_group update_escalation_status
]
end
diff --git a/spec/support/shared_examples/controllers/create_notes_rate_limit_shared_examples.rb b/spec/support/shared_examples/controllers/create_notes_rate_limit_shared_examples.rb
index 8affe4ac8f5..08d0be8c7ac 100644
--- a/spec/support/shared_examples/controllers/create_notes_rate_limit_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/create_notes_rate_limit_shared_examples.rb
@@ -3,44 +3,19 @@
# Requires a context containing:
# - user
# - params
-# - request_full_path
-RSpec.shared_examples 'request exceeding rate limit' do
- context 'with rate limiter', :freeze_time, :clean_gitlab_redis_rate_limiting do
- before do
- stub_application_setting(notes_create_limit: 2)
- 2.times { post :create, params: params }
- end
+RSpec.shared_examples 'create notes request exceeding rate limit' do
+ include_examples 'rate limited endpoint', rate_limit_key: :notes_create
- it 'prevents from creating more notes' do
- expect { post :create, params: params }
- .to change { Note.count }.by(0)
+ it 'allows user in allow-list to create notes, even if the case is different', :freeze_time, :clean_gitlab_redis_rate_limiting do
+ allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:notes_create).and_return(1)
- expect(response).to have_gitlab_http_status(:too_many_requests)
- expect(response.body).to eq(_('This endpoint has been requested too many times. Try again later.'))
- end
+ current_user.update_attribute(:username, current_user.username.titleize)
+ stub_application_setting(notes_create_limit_allowlist: [current_user.username.downcase])
- it 'logs the event in auth.log' do
- attributes = {
- message: 'Application_Rate_Limiter_Request',
- env: :notes_create_request_limit,
- remote_ip: '0.0.0.0',
- request_method: 'POST',
- path: request_full_path,
- user_id: user.id,
- username: user.username
- }
+ request
+ request
- expect(Gitlab::AuthLogger).to receive(:error).with(attributes).once
- post :create, params: params
- end
-
- it 'allows user in allow-list to create notes, even if the case is different' do
- user.update_attribute(:username, user.username.titleize)
- stub_application_setting(notes_create_limit_allowlist: ["#{user.username.downcase}"])
-
- post :create, params: params
- expect(response).to have_gitlab_http_status(:found)
- end
+ expect(response).to have_gitlab_http_status(:found)
end
end
diff --git a/spec/support/shared_examples/controllers/rate_limited_endpoint_shared_examples.rb b/spec/support/shared_examples/controllers/rate_limited_endpoint_shared_examples.rb
new file mode 100644
index 00000000000..bb2a4159071
--- /dev/null
+++ b/spec/support/shared_examples/controllers/rate_limited_endpoint_shared_examples.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+#
+# Requires a context containing:
+# - request (use method definition to avoid memoizing!)
+# - current_user
+# - error_message # optional
+
+RSpec.shared_examples 'rate limited endpoint' do |rate_limit_key:|
+ context 'when rate limiter enabled', :freeze_time, :clean_gitlab_redis_rate_limiting do
+ let(:expected_logger_attributes) do
+ {
+ message: 'Application_Rate_Limiter_Request',
+ env: :"#{rate_limit_key}_request_limit",
+ remote_ip: kind_of(String),
+ request_method: kind_of(String),
+ path: kind_of(String),
+ user_id: current_user.id,
+ username: current_user.username
+ }
+ end
+
+ let(:error_message) { _('This endpoint has been requested too many times. Try again later.') }
+
+ before do
+ allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(rate_limit_key).and_return(1)
+ end
+
+ it 'logs request and declines it when endpoint called more than the threshold' do |example|
+ expect(Gitlab::AuthLogger).to receive(:error).with(expected_logger_attributes).once
+
+ request
+ request
+
+ expect(response).to have_gitlab_http_status(:too_many_requests)
+
+ if example.metadata[:type] == :controller
+ expect(response.body).to eq(error_message)
+ else # it is API spec
+ expect(response.body).to eq({ message: { error: error_message } }.to_json)
+ end
+ end
+ end
+
+ context 'when rate limiter is disabled' do
+ before do
+ allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(rate_limit_key).and_return(0)
+ end
+
+ it 'does not log request and does not block the request' do
+ expect(Gitlab::AuthLogger).not_to receive(:error)
+
+ request
+
+ expect(response).not_to have_gitlab_http_status(:too_many_requests)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/features/access_tokens_shared_examples.rb b/spec/support/shared_examples/features/access_tokens_shared_examples.rb
new file mode 100644
index 00000000000..ae246a87bb6
--- /dev/null
+++ b/spec/support/shared_examples/features/access_tokens_shared_examples.rb
@@ -0,0 +1,165 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'resource access tokens missing access rights' do
+ it 'does not show access token page' do
+ visit resource_settings_access_tokens_path
+
+ expect(page).to have_content("Page Not Found")
+ end
+end
+
+RSpec.shared_examples 'resource access tokens creation' do |resource_type|
+ def active_resource_access_tokens
+ find('.table.active-tokens')
+ end
+
+ def created_resource_access_token
+ find('#created-personal-access-token').value
+ end
+
+ it 'allows creation of an access token', :aggregate_failures do
+ name = 'My access token'
+
+ visit resource_settings_access_tokens_path
+ fill_in 'Token name', with: name
+
+ # Set date to 1st of next month
+ find_field('Expiration date').click
+ find('.pika-next').click
+ click_on '1'
+
+ # Scopes
+ check 'api'
+ check 'read_api'
+
+ click_on "Create #{resource_type} access token"
+
+ expect(active_resource_access_tokens).to have_text(name)
+ expect(active_resource_access_tokens).to have_text('in')
+ expect(active_resource_access_tokens).to have_text('api')
+ expect(active_resource_access_tokens).to have_text('read_api')
+ expect(active_resource_access_tokens).to have_text('Maintainer')
+ expect(created_resource_access_token).not_to be_empty
+ end
+end
+
+RSpec.shared_examples 'resource access tokens creation disallowed' do |error_message|
+ before do
+ group.namespace_settings.update_column(:resource_access_token_creation_allowed, false)
+ end
+
+ it 'does not show access token creation form' do
+ visit resource_settings_access_tokens_path
+
+ expect(page).not_to have_selector('#new_resource_access_token')
+ end
+
+ it 'shows access token creation disabled text' do
+ visit resource_settings_access_tokens_path
+
+ expect(page).to have_text(error_message)
+ end
+
+ context 'group settings link' do
+ context 'when user is not a group owner' do
+ before do
+ group.add_developer(user)
+ end
+
+ it 'does not show group settings link' do
+ visit resource_settings_access_tokens_path
+
+ expect(page).not_to have_link('group settings', href: edit_group_path(group))
+ end
+ end
+
+ context 'with nested groups' do
+ let(:parent_group) { create(:group) }
+ let(:group) { create(:group, parent: parent_group) }
+
+ context 'when user is not a top level group owner' do
+ before do
+ group.add_owner(user)
+ end
+
+ it 'does not show group settings link' do
+ visit resource_settings_access_tokens_path
+
+ expect(page).not_to have_link('group settings', href: edit_group_path(group))
+ end
+ end
+ end
+
+ context 'when user is a group owner' do
+ before do
+ group.add_owner(user)
+ end
+
+ it 'shows group settings link' do
+ visit resource_settings_access_tokens_path
+
+ expect(page).to have_link('group settings', href: edit_group_path(group))
+ end
+ end
+ end
+end
+
+RSpec.shared_examples 'active resource access tokens' do
+ def active_resource_access_tokens
+ find('.table.active-tokens')
+ end
+
+ it 'shows active access tokens' do
+ visit resource_settings_access_tokens_path
+
+ expect(active_resource_access_tokens).to have_text(resource_access_token.name)
+ end
+
+ context 'when User#time_display_relative is false' do
+ before do
+ user.update!(time_display_relative: false)
+ end
+
+ it 'shows absolute times for expires_at' do
+ visit resource_settings_access_tokens_path
+
+ expect(active_resource_access_tokens).to have_text(PersonalAccessToken.last.expires_at.strftime('%b %-d'))
+ end
+ end
+end
+
+RSpec.shared_examples 'inactive resource access tokens' do |no_active_tokens_text|
+ def no_resource_access_tokens_message
+ find('.settings-message')
+ end
+
+ it 'allows revocation of an active token' do
+ visit resource_settings_access_tokens_path
+ accept_confirm { click_on 'Revoke' }
+
+ expect(page).to have_selector('.settings-message')
+ expect(no_resource_access_tokens_message).to have_text(no_active_tokens_text)
+ end
+
+ it 'removes expired tokens from active section' do
+ resource_access_token.update!(expires_at: 5.days.ago)
+ visit resource_settings_access_tokens_path
+
+ expect(page).to have_selector('.settings-message')
+ expect(no_resource_access_tokens_message).to have_text(no_active_tokens_text)
+ end
+
+ context 'when resource access token creation is not allowed' do
+ before do
+ group.namespace_settings.update_column(:resource_access_token_creation_allowed, false)
+ end
+
+ it 'allows revocation of an active token' do
+ visit resource_settings_access_tokens_path
+ accept_confirm { click_on 'Revoke' }
+
+ expect(page).to have_selector('.settings-message')
+ expect(no_resource_access_tokens_message).to have_text(no_active_tokens_text)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/features/packages_shared_examples.rb b/spec/support/shared_examples/features/packages_shared_examples.rb
index d14b4638ca5..ded30f32314 100644
--- a/spec/support/shared_examples/features/packages_shared_examples.rb
+++ b/spec/support/shared_examples/features/packages_shared_examples.rb
@@ -19,14 +19,12 @@ RSpec.shared_examples 'packages list' do |check_project_name: false|
end
RSpec.shared_examples 'package details link' do |property|
- let(:package) { packages.first }
-
it 'navigates to the correct url' do
page.within(packages_table_selector) do
click_link package.name
end
- expect(page).to have_current_path(project_package_path(package.project, package))
+ expect(page).to have_current_path(package_details_path)
expect(page).to have_css('.packages-app h2[data-testid="title"]', text: package.name)
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
new file mode 100644
index 00000000000..a9dac7a391f
--- /dev/null
+++ b/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb
@@ -0,0 +1,127 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'labels sidebar widget' do
+ context 'editing labels' do
+ let_it_be(:development) { create(:group_label, group: group, name: 'Development') }
+ let_it_be(:stretch) { create(:label, project: project, name: 'Stretch') }
+ let_it_be(:xss_label) { create(:label, project: project, title: '&lt;script&gt;alert("xss");&lt;&#x2F;script&gt;') }
+
+ let(:labels_widget) { find('[data-testid="sidebar-labels"]') }
+
+ before do
+ page.within(labels_widget) do
+ click_on 'Edit'
+ end
+
+ wait_for_all_requests
+ 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)
+ end
+
+ it 'adds a label' do
+ within(labels_widget) do
+ adds_label(stretch)
+
+ page.within('[data-testid="value-wrapper"]') do
+ expect(page).to have_content(stretch.name)
+ end
+ end
+ end
+
+ it 'removes a label' do
+ within(labels_widget) do
+ adds_label(stretch)
+ page.within('[data-testid="value-wrapper"]') do
+ expect(page).to have_content(stretch.name)
+ end
+
+ click_on 'Remove label'
+
+ wait_for_requests
+
+ page.within('[data-testid="value-wrapper"]') do
+ expect(page).not_to have_content(stretch.name)
+ end
+ end
+ end
+
+ it 'adds first label by pressing enter when search' do
+ within(labels_widget) do
+ page.within('[data-testid="value-wrapper"]') do
+ expect(page).not_to have_content(development.name)
+ end
+
+ fill_in 'Search', with: 'Devel'
+ sleep 1
+ expect(page.all(:css, '[data-testid="dropdown-content"] .gl-new-dropdown-item').length).to eq(1)
+
+ find_field('Search').native.send_keys(:enter)
+ click_button 'Close'
+ wait_for_requests
+
+ page.within('[data-testid="value-wrapper"]') do
+ expect(page).to have_content(development.name)
+ end
+ end
+ end
+
+ it 'escapes XSS when viewing issuable labels' do
+ page.within(labels_widget) do
+ expect(page).to have_content '<script>alert("xss");</script>'
+ end
+ end
+
+ it 'shows option to create a label' do
+ page.within(labels_widget) do
+ expect(page).to have_content 'Create'
+ end
+ end
+
+ context 'creating a label', :js do
+ before do
+ page.within(labels_widget) do
+ page.find('[data-testid="create-label-button"]').click
+ end
+ end
+
+ it 'shows dropdown switches to "create label" section' do
+ page.within(labels_widget) do
+ expect(page.find('[data-testid="dropdown-header"]')).to have_content 'Create'
+ end
+ end
+
+ it 'creates new label' do
+ page.within(labels_widget) do
+ fill_in 'Name new label', with: 'wontfix'
+ page.find('.suggest-colors a', match: :first).click
+ page.find('button', text: 'Create').click
+ wait_for_requests
+
+ expect(page).to have_content 'wontfix'
+ end
+ end
+
+ it 'shows error message if label title is taken' do
+ page.within(labels_widget) do
+ fill_in 'Name new label', with: development.title
+ page.find('.suggest-colors a', match: :first).click
+ page.find('button', text: 'Create').click
+ wait_for_requests
+
+ page.within('.dropdown-input') do
+ expect(page.find('.gl-alert')).to have_content 'Title'
+ end
+ end
+ end
+ end
+ end
+
+ def adds_label(label)
+ click_button label.name
+ click_button 'Close'
+
+ wait_for_requests
+ end
+end
diff --git a/spec/support/shared_examples/features/sidebar_shared_examples.rb b/spec/support/shared_examples/features/sidebar_shared_examples.rb
index 615f568420e..11d216ff4b6 100644
--- a/spec/support/shared_examples/features/sidebar_shared_examples.rb
+++ b/spec/support/shared_examples/features/sidebar_shared_examples.rb
@@ -50,6 +50,10 @@ RSpec.shared_examples 'issue boards sidebar' do
it_behaves_like 'date sidebar widget'
end
+ context 'editing issue labels', :js do
+ it_behaves_like 'labels sidebar widget'
+ end
+
context 'in notifications subscription' do
it 'displays notifications toggle', :aggregate_failures do
page.within('[data-testid="sidebar-notifications"]') do
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 a2c34cdd4a1..601a53ed913 100644
--- a/spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb
+++ b/spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb
@@ -233,7 +233,7 @@ RSpec.shared_examples 'snippet visibility' do
project.update!(visibility_level: Gitlab::VisibilityLevel.level_value(project_visibility.to_s), snippets_access_level: feature_visibility)
if user_type == :external
- member = project.project_member(external)
+ member = project.member(external)
if project.private?
project.add_developer(external) unless member
diff --git a/spec/support/shared_examples/graphql/mutation_shared_examples.rb b/spec/support/shared_examples/graphql/mutation_shared_examples.rb
index 51d52cbb901..dc590e23ace 100644
--- a/spec/support/shared_examples/graphql/mutation_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutation_shared_examples.rb
@@ -8,7 +8,7 @@
# There must be a method or let called `mutation` defined that executes
# the mutation.
RSpec.shared_examples 'a mutation that returns top-level errors' do |errors: []|
- let(:match_errors) { eq(errors) }
+ let(:match_errors) { match_array(errors) }
it do
post_graphql_mutation(mutation, current_user: current_user)
diff --git a/spec/support/shared_examples/graphql/mutations/issues/permission_check_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/issues/permission_check_shared_examples.rb
index 34c58f524cd..05fee45427a 100644
--- a/spec/support/shared_examples/graphql/mutations/issues/permission_check_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/issues/permission_check_shared_examples.rb
@@ -1,12 +1,34 @@
# frozen_string_literal: true
RSpec.shared_examples 'permission level for issue mutation is correctly verified' do |raises_for_all_errors = false|
- before do
- issue.assignees = []
- issue.author = user
+ let_it_be(:other_user_author) { create(:user) }
+
+ def issue_attributes(issue)
+ issue.attributes.except(
+ # Description and title can be updated by authors and assignees of the issues
+ 'description',
+ 'title',
+ # Those fields are calculated or expected to be modified during the mutations
+ 'author_id',
+ 'updated_at',
+ 'updated_by_id',
+ 'last_edited_at',
+ 'last_edited_by_id',
+ 'lock_version',
+ # There were spec failures due to nano-second comparisons
+ # this property isn't changed by any mutation so we don't have to verify it
+ 'created_at'
+ )
end
- shared_examples_for 'when the user does not have access to the resource' do |raise_for_assigned|
+ let(:expected) { issue_attributes(issue) }
+
+ shared_examples_for 'when the user does not have access to the resource' do |raise_for_assigned_and_author|
+ before do
+ issue.assignees = []
+ issue.update!(author: other_user_author)
+ end
+
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
@@ -17,21 +39,25 @@ RSpec.shared_examples 'permission level for issue mutation is correctly verified
end
it 'does not modify issue' do
- if raises_for_all_errors || raise_for_assigned
+ if raises_for_all_errors || raise_for_assigned_and_author
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
else
- expect(subject[:issue]).to eq issue
+ expect(issue_attributes(subject[:issue])).to eq expected
end
end
end
context 'even if author of the issue' do
before do
- issue.author = user
+ issue.update!(author: user)
end
- it 'raises an error' do
- expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ it 'does not modify issue' do
+ if raises_for_all_errors || raise_for_assigned_and_author
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ else
+ expect(issue_attributes(subject[:issue])).to eq expected
+ end
end
end
end
diff --git a/spec/support/shared_examples/graphql/mutations/merge_requests/permission_check_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/merge_requests/permission_check_shared_examples.rb
index 1ddbad1cea7..b0ac742079a 100644
--- a/spec/support/shared_examples/graphql/mutations/merge_requests/permission_check_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/merge_requests/permission_check_shared_examples.rb
@@ -1,13 +1,39 @@
# frozen_string_literal: true
RSpec.shared_examples 'permission level for merge request mutation is correctly verified' do
- before do
- merge_request.assignees = []
- merge_request.reviewers = []
- merge_request.author = nil
+ let(:other_user_author) { create(:user) }
+
+ def mr_attributes(mr)
+ mr.attributes.except(
+ # Authors and assignees can edit title, description, target branch and draft status
+ 'title',
+ 'description',
+ 'target_branch',
+ 'draft',
+ # Those fields are calculated or expected to be modified during the mutations
+ 'author_id',
+ 'latest_merge_request_diff_id',
+ 'last_edited_at',
+ 'last_edited_by_id',
+ 'lock_version',
+ 'updated_at',
+ 'updated_by_id',
+ 'merge_status',
+ # There were spec failures due to nano-second comparisons
+ # this property isn't changed by any mutation so we don't have to verify it
+ 'created_at'
+ )
end
- shared_examples_for 'when the user does not have access to the resource' do |raise_for_assigned|
+ let(:expected) { mr_attributes(merge_request) }
+
+ shared_examples_for 'when the user does not have access to the resource' do |raise_for_assigned_and_author|
+ before do
+ merge_request.assignees = []
+ merge_request.reviewers = []
+ merge_request.update!(author: other_user_author)
+ end
+
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
@@ -18,12 +44,12 @@ RSpec.shared_examples 'permission level for merge request mutation is correctly
end
it 'does not modify merge request' do
- if raise_for_assigned
+ if raise_for_assigned_and_author
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
else
# In some cases we simply do nothing instead of raising
# https://gitlab.com/gitlab-org/gitlab/-/issues/196241
- expect(subject[:merge_request]).to eq merge_request
+ expect(mr_attributes(subject[:merge_request])).to eq expected
end
end
end
@@ -40,11 +66,17 @@ RSpec.shared_examples 'permission level for merge request mutation is correctly
context 'even if author of the merge request' do
before do
- merge_request.author = user
+ merge_request.update!(author: user)
end
it 'raises an error' do
- expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ if raise_for_assigned_and_author
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ else
+ # In some cases we simply do nothing instead of raising
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/196241
+ expect(mr_attributes(subject[:merge_request])).to eq expected
+ end
end
end
end
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 7888ade56eb..213f084be17 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
@@ -22,19 +22,19 @@ RSpec.shared_examples 'marks background migration job records' do
end
end
-RSpec.shared_examples 'finalized background migration' do
+RSpec.shared_examples 'finalized background migration' do |worker_class|
it 'processed the scheduled sidekiq queue' do
queued = Sidekiq::ScheduledSet
.new
.select do |scheduled|
- scheduled.klass == 'BackgroundMigrationWorker' &&
+ scheduled.klass == worker_class.name &&
scheduled.args.first == job_class_name
end
expect(queued.size).to eq(0)
end
it 'processed the async sidekiq queue' do
- queued = Sidekiq::Queue.new('BackgroundMigrationWorker')
+ queued = Sidekiq::Queue.new(worker_class.name)
.select { |scheduled| scheduled.klass == job_class_name }
expect(queued.size).to eq(0)
end
@@ -42,8 +42,8 @@ RSpec.shared_examples 'finalized background migration' do
include_examples 'removed tracked jobs', 'pending'
end
-RSpec.shared_examples 'finalized tracked background migration' do
- include_examples 'finalized background migration'
+RSpec.shared_examples 'finalized tracked background migration' do |worker_class|
+ include_examples 'finalized background migration', worker_class
include_examples 'removed tracked jobs', 'succeeded'
end
diff --git a/spec/support/shared_examples/lib/gitlab/redis/multi_store_feature_flags_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/redis/multi_store_feature_flags_shared_examples.rb
deleted file mode 100644
index 046c70bf779..00000000000
--- a/spec/support/shared_examples/lib/gitlab/redis/multi_store_feature_flags_shared_examples.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples 'multi store feature flags' do |use_primary_and_secondary_stores, use_primary_store_as_default|
- context "with feature flag :#{use_primary_and_secondary_stores} is enabled" do
- before do
- stub_feature_flags(use_primary_and_secondary_stores => true)
- end
-
- it 'multi store is enabled' do
- expect(subject.use_primary_and_secondary_stores?).to be true
- end
- end
-
- context "with feature flag :#{use_primary_and_secondary_stores} is disabled" do
- before do
- stub_feature_flags(use_primary_and_secondary_stores => false)
- end
-
- it 'multi store is disabled' do
- expect(subject.use_primary_and_secondary_stores?).to be false
- end
- end
-
- context "with feature flag :#{use_primary_store_as_default} is enabled" do
- before do
- stub_feature_flags(use_primary_store_as_default => true)
- end
-
- it 'primary store is enabled' do
- expect(subject.use_primary_store_as_default?).to be true
- end
- end
-
- context "with feature flag :#{use_primary_store_as_default} is disabled" do
- before do
- stub_feature_flags(use_primary_store_as_default => false)
- end
-
- it 'primary store is disabled' do
- expect(subject.use_primary_store_as_default?).to be false
- end
- end
-end
diff --git a/spec/support/shared_examples/lib/gitlab/unique_ip_check_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/unique_ip_check_shared_examples.rb
index e42a927b5ba..c735b98aa23 100644
--- a/spec/support/shared_examples/lib/gitlab/unique_ip_check_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/unique_ip_check_shared_examples.rb
@@ -7,13 +7,13 @@ RSpec.shared_examples 'user login operation with unique ip limit' do
end
it 'allows user authenticating from the same ip' do
- expect { operation_from_ip('ip') }.not_to raise_error
- expect { operation_from_ip('ip') }.not_to raise_error
+ expect { operation_from_ip('111.221.4.3') }.not_to raise_error
+ expect { operation_from_ip('111.221.4.3') }.not_to raise_error
end
it 'blocks user authenticating from two distinct ips' do
- expect { operation_from_ip('ip') }.not_to raise_error
- expect { operation_from_ip('ip2') }.to raise_error(Gitlab::Auth::TooManyIps)
+ expect { operation_from_ip('111.221.4.3') }.not_to raise_error
+ expect { operation_from_ip('1.2.2.3') }.to raise_error(Gitlab::Auth::TooManyIps)
end
end
end
@@ -25,13 +25,13 @@ RSpec.shared_examples 'user login request with unique ip limit' do |success_stat
end
it 'allows user authenticating from the same ip' do
- expect(request_from_ip('ip')).to have_gitlab_http_status(success_status)
- expect(request_from_ip('ip')).to have_gitlab_http_status(success_status)
+ expect(request_from_ip('111.221.4.3')).to have_gitlab_http_status(success_status)
+ expect(request_from_ip('111.221.4.3')).to have_gitlab_http_status(success_status)
end
it 'blocks user authenticating from two distinct ips' do
- expect(request_from_ip('ip')).to have_gitlab_http_status(success_status)
- expect(request_from_ip('ip2')).to have_gitlab_http_status(:forbidden)
+ expect(request_from_ip('111.221.4.3')).to have_gitlab_http_status(success_status)
+ expect(request_from_ip('1.2.2.3')).to have_gitlab_http_status(:forbidden)
end
end
end
diff --git a/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb b/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb
index 8f3a93de509..42eec74e64f 100644
--- a/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb
+++ b/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb
@@ -55,10 +55,16 @@ RSpec.shared_examples 'cleanup by a loose foreign key' do
end
def find_model
- model.class.find_by(id: model.id)
+ query = model.class
+ # handle composite primary keys
+ connection = model.class.connection
+ connection.primary_keys(model.class.table_name).each do |primary_key|
+ query = query.where(primary_key => model.public_send(primary_key))
+ end
+ query.first
end
- it 'deletes the model' do
+ it 'cleans up (delete or nullify) the model' do
parent.delete
expect(find_model).to be_present
diff --git a/spec/support/shared_examples/metrics/sampler_shared_examples.rb b/spec/support/shared_examples/metrics/sampler_shared_examples.rb
index ebf199c3a8d..cec540cd120 100644
--- a/spec/support/shared_examples/metrics/sampler_shared_examples.rb
+++ b/spec/support/shared_examples/metrics/sampler_shared_examples.rb
@@ -2,26 +2,98 @@
RSpec.shared_examples 'metrics sampler' do |env_prefix|
context 'when sampling interval is passed explicitly' do
- subject { described_class.new(42) }
+ subject(:sampler) { described_class.new(interval: 42, logger: double) }
- specify { expect(subject.interval).to eq(42) }
+ specify { expect(sampler.interval).to eq(42) }
end
context 'when sampling interval is passed through the environment' do
- subject { described_class.new }
+ subject(:sampler) { described_class.new(logger: double) }
before do
stub_env("#{env_prefix}_INTERVAL_SECONDS", '42')
end
- specify { expect(subject.interval).to eq(42) }
+ specify { expect(sampler.interval).to eq(42) }
end
context 'when no sampling interval is passed anywhere' do
- subject { described_class.new }
+ subject(:sampler) { described_class.new(logger: double) }
it 'uses the hardcoded default' do
- expect(subject.interval).to eq(described_class::DEFAULT_SAMPLING_INTERVAL_SECONDS)
+ expect(sampler.interval).to eq(described_class::DEFAULT_SAMPLING_INTERVAL_SECONDS)
+ end
+ end
+
+ describe '#start' do
+ include WaitHelpers
+
+ subject(:sampler) { described_class.new(interval: 0.1) }
+
+ it 'calls the sample method on the sampler thread' do
+ sampling_threads = []
+ expect(sampler).to receive(:sample).at_least(:once) { sampling_threads << Thread.current }
+
+ sampler.start
+
+ wait_for('sampler has sampled', max_wait_time: 3) { sampling_threads.any? }
+ expect(sampling_threads.first.name).to eq(sampler.thread_name)
+
+ sampler.stop
+ end
+
+ context 'with warmup set to true' do
+ subject(:sampler) { described_class.new(interval: 0.1, warmup: true) }
+
+ it 'calls the sample method first on the caller thread' do
+ sampling_threads = []
+ current_thread = Thread.current
+ # Instead of sampling, we're keeping track of which thread the sampling happened on.
+ # We want the first sample to be on the spec thread, which would mean a blocking sample
+ # before the actual sampler thread starts.
+ expect(sampler).to receive(:sample).at_least(:once) { sampling_threads << Thread.current }
+
+ sampler.start
+
+ wait_for('sampler has sampled', max_wait_time: 3) { sampling_threads.size == 2 }
+
+ expect(sampling_threads.first).to be(current_thread)
+ expect(sampling_threads.last.name).to eq(sampler.thread_name)
+
+ sampler.stop
+ end
+ end
+ end
+
+ describe '#safe_sample' do
+ let(:logger) { Logger.new(File::NULL) }
+
+ subject(:sampler) { described_class.new(logger: logger) }
+
+ it 'calls #sample once' do
+ expect(sampler).to receive(:sample)
+
+ sampler.safe_sample
+ end
+
+ context 'when sampling fails with error' do
+ before do
+ expect(sampler).to receive(:sample).and_raise "something failed"
+ end
+
+ it 'recovers from errors' do
+ expect { sampler.safe_sample }.not_to raise_error
+ end
+
+ context 'with logger' do
+ let(:logger) { double('logger') }
+
+ it 'logs errors' do
+ expect(logger).to receive(:warn).with(an_instance_of(String))
+
+ expect { sampler.safe_sample }.not_to raise_error
+ end
+ end
end
end
end
diff --git a/spec/support/shared_examples/models/application_setting_shared_examples.rb b/spec/support/shared_examples/models/application_setting_shared_examples.rb
index 60a02d85a1e..38f5c7be393 100644
--- a/spec/support/shared_examples/models/application_setting_shared_examples.rb
+++ b/spec/support/shared_examples/models/application_setting_shared_examples.rb
@@ -94,7 +94,7 @@ RSpec.shared_examples 'application settings examples' do
'1:2:3:4:5::7:8',
'[1:2:3:4:5::7:8]',
'[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443',
- 'www.example2.com:8080',
+ 'www.example.org:8080',
'example.com:8080'
]
@@ -114,7 +114,7 @@ RSpec.shared_examples 'application settings examples' do
an_object_having_attributes(domain: 'example.com'),
an_object_having_attributes(domain: 'subdomain.example.com'),
an_object_having_attributes(domain: 'www.example.com'),
- an_object_having_attributes(domain: 'www.example2.com', port: 8080),
+ an_object_having_attributes(domain: 'www.example.org', port: 8080),
an_object_having_attributes(domain: 'example.com', port: 8080)
]
diff --git a/spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb b/spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb
index 7b33a95bfa1..8ee76efc896 100644
--- a/spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb
@@ -95,6 +95,12 @@ RSpec.shared_examples 'a model including Escalatable' do
it { is_expected.to eq([ignored_escalatable, resolved_escalatable, acknowledged_escalatable, triggered_escalatable]) }
end
end
+
+ describe '.open' do
+ subject { all_escalatables.open }
+
+ it { is_expected.to contain_exactly(acknowledged_escalatable, triggered_escalatable) }
+ end
end
describe '.status_value' do
@@ -133,6 +139,24 @@ RSpec.shared_examples 'a model including Escalatable' do
end
end
+ describe '.open_status?' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:status, :is_open_status) do
+ :triggered | true
+ :acknowledged | true
+ :resolved | false
+ :ignored | false
+ nil | false
+ end
+
+ with_them do
+ it 'returns true when the status is open status' do
+ expect(described_class.open_status?(status)).to eq(is_open_status)
+ end
+ end
+ end
+
describe '#trigger' do
subject { escalatable.trigger }
@@ -237,6 +261,15 @@ RSpec.shared_examples 'a model including Escalatable' do
end
end
+ describe '#open?' do
+ it 'returns true when the status is open status' do
+ expect(triggered_escalatable.open?).to be true
+ expect(acknowledged_escalatable.open?).to be true
+ expect(resolved_escalatable.open?).to be false
+ expect(ignored_escalatable.open?).to be false
+ end
+ end
+
private
def factory_from_class(klass)
diff --git a/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb b/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb
index ad15f82be5e..2a976fb7421 100644
--- a/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: true
-RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
+RSpec.shared_examples Integrations::SlackMattermostNotifier do |integration_name|
include StubRequests
- let(:chat_service) { described_class.new }
+ let(:chat_integration) { described_class.new }
let(:webhook_url) { 'https://example.gitlab.com' }
def execute_with_options(options)
@@ -17,7 +17,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
end
describe 'Validations' do
- context 'when service is active' do
+ context 'when integration is active' do
before do
subject.active = true
end
@@ -26,7 +26,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
it_behaves_like 'issue tracker integration URL attribute', :webhook
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
subject.active = false
end
@@ -35,9 +35,9 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
end
end
- shared_examples "triggered #{service_name} service" do |event_type: nil, branches_to_be_notified: nil|
+ shared_examples "triggered #{integration_name} integration" do |event_type: nil, branches_to_be_notified: nil|
before do
- chat_service.branches_to_be_notified = branches_to_be_notified if branches_to_be_notified
+ chat_integration.branches_to_be_notified = branches_to_be_notified if branches_to_be_notified
end
let!(:stubbed_resolved_hostname) do
@@ -45,14 +45,14 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
end
it "notifies about #{event_type} events" do
- chat_service.execute(data)
+ chat_integration.execute(data)
expect(WebMock).to have_requested(:post, stubbed_resolved_hostname)
end
end
- shared_examples "untriggered #{service_name} service" do |event_type: nil, branches_to_be_notified: nil|
+ shared_examples "untriggered #{integration_name} integration" do |event_type: nil, branches_to_be_notified: nil|
before do
- chat_service.branches_to_be_notified = branches_to_be_notified if branches_to_be_notified
+ chat_integration.branches_to_be_notified = branches_to_be_notified if branches_to_be_notified
end
let!(:stubbed_resolved_hostname) do
@@ -60,7 +60,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
end
it "notifies about #{event_type} events" do
- chat_service.execute(data)
+ chat_integration.execute(data)
expect(WebMock).not_to have_requested(:post, stubbed_resolved_hostname)
end
end
@@ -69,50 +69,50 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
let_it_be(:project) { create(:project, :repository, :wiki_repo) }
let_it_be(:user) { create(:user) }
- let(:chat_service) { described_class.new( { project: project, webhook: webhook_url, branches_to_be_notified: 'all' }.merge(chat_service_params)) }
- let(:chat_service_params) { {} }
+ let(:chat_integration) { described_class.new( { project: project, webhook: webhook_url, branches_to_be_notified: 'all' }.merge(chat_integration_params)) }
+ let(:chat_integration_params) { {} }
let(:data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
let!(:stubbed_resolved_hostname) do
stub_full_request(webhook_url, method: :post).request_pattern.uri_pattern.to_s
end
- subject(:execute_service) { chat_service.execute(data) }
+ subject(:execute_integration) { chat_integration.execute(data) }
- shared_examples 'calls the service API with the event message' do |event_message|
+ shared_examples 'calls the integration API with the event message' do |event_message|
specify do
expect_next_instance_of(::Slack::Messenger) do |messenger|
expect(messenger).to receive(:ping).with(event_message, anything).and_call_original
end
- execute_service
+ execute_integration
expect(WebMock).to have_requested(:post, stubbed_resolved_hostname).once
end
end
context 'with username for slack configured' do
- let(:chat_service_params) { { username: 'slack_username' } }
+ let(:chat_integration_params) { { username: 'slack_username' } }
it 'uses the username as an option' do
expect(::Slack::Messenger).to execute_with_options(username: 'slack_username')
- execute_service
+ execute_integration
end
end
context 'push events' do
let(:data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
- it_behaves_like 'calls the service API with the event message', /pushed to branch/
+ it_behaves_like 'calls the integration API with the event message', /pushed to branch/
context 'with event channel' do
- let(:chat_service_params) { { push_channel: 'random' } }
+ let(:chat_integration_params) { { push_channel: 'random' } }
it 'uses the right channel for push event' do
expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
- execute_service
+ execute_integration
end
end
end
@@ -123,7 +123,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
let(:ref) { 'refs/tags/v1.1.0' }
let(:data) { Git::TagHooksService.new(project, user, change: { oldrev: oldrev, newrev: newrev, ref: ref }).send(:push_data) }
- it_behaves_like 'calls the service API with the event message', /pushed new tag/
+ it_behaves_like 'calls the integration API with the event message', /pushed new tag/
end
context 'issue events' do
@@ -131,15 +131,15 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
let(:data) { issue.to_hook_data(user) }
- it_behaves_like 'calls the service API with the event message', /Issue (.*?) opened by/
+ it_behaves_like 'calls the integration API with the event message', /Issue (.*?) opened by/
context 'whith event channel' do
- let(:chat_service_params) { { issue_channel: 'random' } }
+ let(:chat_integration_params) { { issue_channel: 'random' } }
it 'uses the right channel for issue event' do
expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
- execute_service
+ execute_integration
end
context 'for confidential issues' do
@@ -150,16 +150,16 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
it 'falls back to issue channel' do
expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
- execute_service
+ execute_integration
end
context 'and confidential_issue_channel is defined' do
- let(:chat_service_params) { { issue_channel: 'random', confidential_issue_channel: 'confidential' } }
+ let(:chat_integration_params) { { issue_channel: 'random', confidential_issue_channel: 'confidential' } }
it 'uses the confidential issue channel when it is defined' do
expect(::Slack::Messenger).to execute_with_options(channel: ['confidential'])
- execute_service
+ execute_integration
end
end
end
@@ -171,15 +171,15 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
let(:data) { merge_request.to_hook_data(user) }
- it_behaves_like 'calls the service API with the event message', /opened merge request/
+ it_behaves_like 'calls the integration API with the event message', /opened merge request/
context 'with event channel' do
- let(:chat_service_params) { { merge_request_channel: 'random' } }
+ let(:chat_integration_params) { { merge_request_channel: 'random' } }
it 'uses the right channel for merge request event' do
expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
- execute_service
+ execute_integration
end
end
end
@@ -189,15 +189,15 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
let(:data) { Gitlab::DataBuilder::WikiPage.build(wiki_page, user, 'create') }
- it_behaves_like 'calls the service API with the event message', %r{ created (.*?)wikis/(.*?)|wiki page> in}
+ it_behaves_like 'calls the integration API with the event message', %r{ created (.*?)wikis/(.*?)|wiki page> in}
context 'with event channel' do
- let(:chat_service_params) { { wiki_page_channel: 'random' } }
+ let(:chat_integration_params) { { wiki_page_channel: 'random' } }
it 'uses the right channel for wiki event' do
expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
- execute_service
+ execute_integration
end
end
end
@@ -207,7 +207,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
let(:data) { Gitlab::DataBuilder::Deployment.build(deployment, Time.current) }
- it_behaves_like 'calls the service API with the event message', /Deploy to (.*?) created/
+ it_behaves_like 'calls the integration API with the event message', /Deploy to (.*?) created/
end
context 'note event' do
@@ -215,15 +215,15 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
let(:data) { Gitlab::DataBuilder::Note.build(issue_note, user) }
- it_behaves_like 'calls the service API with the event message', /commented on issue/
+ it_behaves_like 'calls the integration API with the event message', /commented on issue/
context 'with event channel' do
- let(:chat_service_params) { { note_channel: 'random' } }
+ let(:chat_integration_params) { { note_channel: 'random' } }
it 'uses the right channel' do
expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
- execute_service
+ execute_integration
end
context 'for confidential notes' do
@@ -234,16 +234,16 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
it 'falls back to note channel' do
expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
- execute_service
+ execute_integration
end
context 'and confidential_note_channel is defined' do
- let(:chat_service_params) { { note_channel: 'random', confidential_note_channel: 'confidential' } }
+ let(:chat_integration_params) { { note_channel: 'random', confidential_note_channel: 'confidential' } }
it 'uses confidential channel' do
expect(::Slack::Messenger).to execute_with_options(channel: ['confidential'])
- execute_service
+ execute_integration
end
end
end
@@ -256,7 +256,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
let(:project) { create(:project, :repository, creator: user) }
before do
- allow(chat_service).to receive_messages(
+ allow(chat_integration).to receive_messages(
project: project,
service_hook: true,
webhook: webhook_url
@@ -283,23 +283,23 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
)
end
- it_behaves_like "triggered #{service_name} service", event_type: "push"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "push"
end
context 'notification enabled only for default branch' do
- it_behaves_like "triggered #{service_name} service", event_type: "push", branches_to_be_notified: "default"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "push", branches_to_be_notified: "default"
end
context 'notification enabled only for protected branches' do
- it_behaves_like "untriggered #{service_name} service", event_type: "push", branches_to_be_notified: "protected"
+ it_behaves_like "untriggered #{integration_name} integration", event_type: "push", branches_to_be_notified: "protected"
end
context 'notification enabled only for default and protected branches' do
- it_behaves_like "triggered #{service_name} service", event_type: "push", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "push", branches_to_be_notified: "default_and_protected"
end
context 'notification enabled for all branches' do
- it_behaves_like "triggered #{service_name} service", event_type: "push", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "push", branches_to_be_notified: "all"
end
end
@@ -325,23 +325,23 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
)
end
- it_behaves_like "triggered #{service_name} service", event_type: "push"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "push"
end
context 'notification enabled only for default branch' do
- it_behaves_like "untriggered #{service_name} service", event_type: "push", branches_to_be_notified: "default"
+ it_behaves_like "untriggered #{integration_name} integration", event_type: "push", branches_to_be_notified: "default"
end
context 'notification enabled only for protected branches' do
- it_behaves_like "triggered #{service_name} service", event_type: "push", branches_to_be_notified: "protected"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "push", branches_to_be_notified: "protected"
end
context 'notification enabled only for default and protected branches' do
- it_behaves_like "triggered #{service_name} service", event_type: "push", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "push", branches_to_be_notified: "default_and_protected"
end
context 'notification enabled for all branches' do
- it_behaves_like "triggered #{service_name} service", event_type: "push", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "push", branches_to_be_notified: "all"
end
end
@@ -367,23 +367,23 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
)
end
- it_behaves_like "triggered #{service_name} service", event_type: "push"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "push"
end
context 'notification enabled only for default branch' do
- it_behaves_like "untriggered #{service_name} service", event_type: "push", branches_to_be_notified: "default"
+ it_behaves_like "untriggered #{integration_name} integration", event_type: "push", branches_to_be_notified: "default"
end
context 'notification enabled only for protected branches' do
- it_behaves_like "triggered #{service_name} service", event_type: "push", branches_to_be_notified: "protected"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "push", branches_to_be_notified: "protected"
end
context 'notification enabled only for default and protected branches' do
- it_behaves_like "triggered #{service_name} service", event_type: "push", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "push", branches_to_be_notified: "default_and_protected"
end
context 'notification enabled for all branches' do
- it_behaves_like "triggered #{service_name} service", event_type: "push", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "push", branches_to_be_notified: "all"
end
end
@@ -405,23 +405,23 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
)
end
- it_behaves_like "triggered #{service_name} service", event_type: "push"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "push"
end
context 'notification enabled only for default branch' do
- it_behaves_like "untriggered #{service_name} service", event_type: "push", branches_to_be_notified: "default"
+ it_behaves_like "untriggered #{integration_name} integration", event_type: "push", branches_to_be_notified: "default"
end
context 'notification enabled only for protected branches' do
- it_behaves_like "untriggered #{service_name} service", event_type: "push", branches_to_be_notified: "protected"
+ it_behaves_like "untriggered #{integration_name} integration", event_type: "push", branches_to_be_notified: "protected"
end
context 'notification enabled only for default and protected branches' do
- it_behaves_like "untriggered #{service_name} service", event_type: "push", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "untriggered #{integration_name} integration", event_type: "push", branches_to_be_notified: "default_and_protected"
end
context 'notification enabled for all branches' do
- it_behaves_like "triggered #{service_name} service", event_type: "push", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "push", branches_to_be_notified: "all"
end
end
end
@@ -431,7 +431,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
let(:project) { create(:project, :repository, creator: user) }
before do
- allow(chat_service).to receive_messages(
+ allow(chat_integration).to receive_messages(
project: project,
service_hook: true,
webhook: webhook_url
@@ -452,7 +452,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
Gitlab::DataBuilder::Note.build(commit_note, user)
end
- it_behaves_like "triggered #{service_name} service", event_type: "commit comment"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "commit comment"
end
context 'when merge request comment event executed' do
@@ -465,7 +465,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
Gitlab::DataBuilder::Note.build(merge_request_note, user)
end
- it_behaves_like "triggered #{service_name} service", event_type: "merge request comment"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "merge request comment"
end
context 'when issue comment event executed' do
@@ -478,7 +478,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
Gitlab::DataBuilder::Note.build(issue_note, user)
end
- it_behaves_like "triggered #{service_name} service", event_type: "issue comment"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "issue comment"
end
context 'when snippet comment event executed' do
@@ -491,7 +491,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
Gitlab::DataBuilder::Note.build(snippet_note, user)
end
- it_behaves_like "triggered #{service_name} service", event_type: "snippet comment"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "snippet comment"
end
end
@@ -505,7 +505,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
end
before do
- allow(chat_service).to receive_messages(
+ allow(chat_integration).to receive_messages(
project: project,
service_hook: true,
webhook: webhook_url
@@ -519,15 +519,15 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
let(:data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
context 'with default to notify_only_broken_pipelines' do
- it_behaves_like "untriggered #{service_name} service", event_type: "pipeline"
+ it_behaves_like "untriggered #{integration_name} integration", event_type: "pipeline"
end
context 'with setting notify_only_broken_pipelines to false' do
before do
- chat_service.notify_only_broken_pipelines = false
+ chat_integration.notify_only_broken_pipelines = false
end
- it_behaves_like "triggered #{service_name} service", event_type: "pipeline"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "pipeline"
end
end
@@ -542,19 +542,19 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
let(:data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
context 'notification enabled only for default branch' do
- it_behaves_like "triggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "default"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "default"
end
context 'notification enabled only for protected branches' do
- it_behaves_like "untriggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "protected"
+ it_behaves_like "untriggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "protected"
end
context 'notification enabled only for default and protected branches' do
- it_behaves_like "triggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "default_and_protected"
end
context 'notification enabled for all branches' do
- it_behaves_like "triggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "all"
end
end
@@ -572,19 +572,19 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
let(:data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
context 'notification enabled only for default branch' do
- it_behaves_like "untriggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "default"
+ it_behaves_like "untriggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "default"
end
context 'notification enabled only for protected branches' do
- it_behaves_like "triggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "protected"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "protected"
end
context 'notification enabled only for default and protected branches' do
- it_behaves_like "triggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "default_and_protected"
end
context 'notification enabled for all branches' do
- it_behaves_like "triggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "all"
end
end
@@ -602,19 +602,19 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
let(:data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
context 'notification enabled only for default branch' do
- it_behaves_like "untriggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "default"
+ it_behaves_like "untriggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "default"
end
context 'notification enabled only for protected branches' do
- it_behaves_like "triggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "protected"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "protected"
end
context 'notification enabled only for default and protected branches' do
- it_behaves_like "triggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "default_and_protected"
end
context 'notification enabled for all branches' do
- it_behaves_like "triggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "all"
end
end
@@ -628,19 +628,78 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |service_name|
let(:data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
context 'notification enabled only for default branch' do
- it_behaves_like "untriggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "default"
+ it_behaves_like "untriggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "default"
end
context 'notification enabled only for protected branches' do
- it_behaves_like "untriggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "protected"
+ it_behaves_like "untriggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "protected"
end
context 'notification enabled only for default and protected branches' do
- it_behaves_like "untriggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "untriggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "default_and_protected"
end
context 'notification enabled for all branches' do
- it_behaves_like "triggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "all"
+ end
+ end
+ end
+ end
+
+ describe 'Deployment events' do
+ let_it_be(:user) { create(:user) }
+ let_it_be_with_reload(:project) { create(:project, :repository, creator: user) }
+
+ let(:deployment) do
+ create(:deployment, :success, project: project, sha: project.commit.sha, ref: project.default_branch)
+ end
+
+ let(:data) { Gitlab::DataBuilder::Deployment.build(deployment, Time.now) }
+
+ before do
+ allow(chat_integration).to receive_messages(
+ project: project,
+ service_hook: true,
+ webhook: webhook_url
+ )
+
+ stub_full_request(webhook_url, method: :post)
+ end
+
+ it_behaves_like "triggered #{integration_name} integration", event_type: "deployment"
+
+ context 'on a protected branch' do
+ before do
+ create(:protected_branch, :create_branch_on_repository, project: project, name: 'a-protected-branch')
+ end
+
+ let(:deployment) do
+ create(:deployment, :success, project: project, sha: project.commit.sha, ref: 'a-protected-branch')
+ end
+
+ context 'notification enabled only for default branch' do
+ it_behaves_like "untriggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "default"
+ end
+
+ context 'notification enabled only for protected branches' do
+ it_behaves_like "triggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "protected"
+ end
+
+ context 'notification enabled only for default and protected branches' do
+ it_behaves_like "triggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "default_and_protected"
+ end
+
+ context 'notification enabled for all branches' do
+ it_behaves_like "triggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "all"
+ end
+
+ context 'when chat_notification_deployment_protected_branch_filter is disabled' do
+ before do
+ stub_feature_flags(chat_notification_deployment_protected_branch_filter: false)
+ end
+
+ context 'notification enabled only for default branch' do
+ it_behaves_like "triggered #{integration_name} integration", event_type: "pipeline", branches_to_be_notified: "default"
end
end
end
diff --git a/spec/support/shared_examples/models/concerns/packages/destructible_shared_examples.rb b/spec/support/shared_examples/models/concerns/packages/destructible_shared_examples.rb
new file mode 100644
index 00000000000..f974b46f881
--- /dev/null
+++ b/spec/support/shared_examples/models/concerns/packages/destructible_shared_examples.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'destructible' do |factory:|
+ let_it_be(:item1) { create(factory, created_at: 1.month.ago, updated_at: 1.day.ago) }
+ let_it_be(:item2) { create(factory, created_at: 1.year.ago, updated_at: 1.year.ago) }
+ let_it_be(:item3) { create(factory, :pending_destruction, created_at: 2.years.ago, updated_at: 1.month.ago) }
+ let_it_be(:item4) { create(factory, :pending_destruction, created_at: 3.years.ago, updated_at: 2.weeks.ago) }
+
+ describe '.next_pending_destruction' do
+ it 'returns the oldest item pending destruction based on updated_at' do
+ expect(described_class.next_pending_destruction(order_by: :updated_at)).to eq(item3)
+ end
+
+ it 'returns the oldest item pending destruction based on created_at' do
+ expect(described_class.next_pending_destruction(order_by: :created_at)).to eq(item4)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/concerns/ttl_expirable_shared_examples.rb b/spec/support/shared_examples/models/concerns/ttl_expirable_shared_examples.rb
index 2d08de297a3..174b8609337 100644
--- a/spec/support/shared_examples/models/concerns/ttl_expirable_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/ttl_expirable_shared_examples.rb
@@ -29,7 +29,7 @@ RSpec.shared_examples 'ttl_expirable' do
describe '.active' do
# rubocop:disable Rails/SaveBang
let_it_be(:item1) { create(class_symbol) }
- let_it_be(:item2) { create(class_symbol, :expired) }
+ let_it_be(:item2) { create(class_symbol, :pending_destruction) }
let_it_be(:item3) { create(class_symbol, status: :error) }
# rubocop:enable Rails/SaveBang
@@ -38,17 +38,6 @@ RSpec.shared_examples 'ttl_expirable' do
end
end
- describe '.lock_next_by' do
- let_it_be(:item1) { create(class_symbol, created_at: 1.month.ago, updated_at: 1.day.ago) }
- let_it_be(:item2) { create(class_symbol, created_at: 1.year.ago, updated_at: 1.year.ago) }
- let_it_be(:item3) { create(class_symbol, created_at: 2.years.ago, updated_at: 1.month.ago) }
-
- it 'returns the first item sorted by the argument' do
- expect(described_class.lock_next_by(:updated_at)).to contain_exactly(item2)
- expect(described_class.lock_next_by(:created_at)).to contain_exactly(item3)
- end
- end
-
describe '#read', :freeze_time do
let_it_be(:old_read_at) { 1.day.ago }
let_it_be(:item1) { create(class_symbol, read_at: old_read_at) }
diff --git a/spec/support/shared_examples/models/member_shared_examples.rb b/spec/support/shared_examples/models/member_shared_examples.rb
index d5d137922eb..5b4b8c8fcc1 100644
--- a/spec/support/shared_examples/models/member_shared_examples.rb
+++ b/spec/support/shared_examples/models/member_shared_examples.rb
@@ -3,7 +3,7 @@
RSpec.shared_examples 'inherited access level as a member of entity' do
let(:parent_entity) { create(:group) }
let(:user) { create(:user) }
- let(:member) { entity.is_a?(Group) ? entity.group_member(user) : entity.project_member(user) }
+ let(:member) { entity.member(user) }
context 'with root parent_entity developer member' do
before do
@@ -49,7 +49,7 @@ RSpec.shared_examples 'inherited access level as a member of entity' do
entity.add_maintainer(non_member_user)
- non_member = entity.is_a?(Group) ? entity.group_member(non_member_user) : entity.project_member(non_member_user)
+ non_member = entity.member(non_member_user)
expect { non_member.update!(access_level: Gitlab::Access::GUEST) }
.to change { non_member.reload.access_level }
diff --git a/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
index 750d3dd11e3..3f8c3b8960b 100644
--- a/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
+++ b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
@@ -198,7 +198,6 @@ RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze|
describe 'relationships' do
it { is_expected.to have_many(:publications).class_name('Packages::Debian::Publication').inverse_of(:distribution).with_foreign_key(:distribution_id) }
it { is_expected.to have_many(:packages).class_name('Packages::Package').through(:publications) }
- it { is_expected.to have_many(:package_files).class_name('Packages::PackageFile').through(:packages) }
end
end
else
@@ -229,6 +228,26 @@ RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze|
it 'returns only files from public packages with same codename' do
expect(subject.to_a).to contain_exactly(*public_package_with_same_codename.package_files)
end
+
+ context 'with pending destruction package files' do
+ let_it_be(:package_file_pending_destruction) { create(:package_file, :pending_destruction, package: public_package_with_same_codename) }
+
+ it 'does not return them' do
+ expect(subject.to_a).not_to include(package_file_pending_destruction)
+ end
+
+ context 'with packages_installable_package_files disabled' do
+ before do
+ stub_feature_flags(packages_installable_package_files: false)
+ end
+
+ it 'returns them' do
+ subject
+
+ expect(subject.to_a).to include(package_file_pending_destruction)
+ end
+ end
+ end
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 2e01de2ea84..06326ffac97 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
@@ -115,16 +115,14 @@ RSpec.shared_examples 'UpdateProjectStatistics' do |with_counter_attribute|
expect(ProjectStatistics)
.not_to receive(:increment_statistic)
- project.update!(pending_delete: true)
- project.destroy!
+ expect(Projects::DestroyService.new(project, project.owner).execute).to eq(true)
end
it 'does not schedule a namespace statistics worker' do
expect(Namespaces::ScheduleAggregationWorker)
.not_to receive(:perform_async)
- project.update!(pending_delete: true)
- project.destroy!
+ expect(Projects::DestroyService.new(project, project.owner).execute).to eq(true)
end
end
end
diff --git a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb
index 3d52ed30c62..b43b7946e69 100644
--- a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb
+++ b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb
@@ -124,6 +124,18 @@ RSpec.shared_examples 'namespace traversal scopes' do
it { expect(subject[0, 2]).to contain_exactly(group_1, group_2) }
it { expect(subject[2, 2]).to contain_exactly(nested_group_1, nested_group_2) }
end
+
+ context 'with offset and limit' do
+ subject { described_class.where(id: [deep_nested_group_1, deep_nested_group_2]).offset(1).limit(1).self_and_ancestors }
+
+ it { is_expected.to contain_exactly(group_2, nested_group_2, deep_nested_group_2) }
+ end
+
+ context 'with upto' do
+ subject { described_class.where(id: deep_nested_group_1).self_and_ancestors(upto: nested_group_1.id) }
+
+ it { is_expected.to contain_exactly(deep_nested_group_1) }
+ end
end
describe '.self_and_ancestors' do
@@ -168,6 +180,19 @@ RSpec.shared_examples 'namespace traversal scopes' do
it { is_expected.to contain_exactly(group_1.id, group_2.id) }
end
+
+ context 'with offset and limit' do
+ subject do
+ described_class
+ .where(id: [deep_nested_group_1, deep_nested_group_2])
+ .limit(1)
+ .offset(1)
+ .self_and_ancestor_ids
+ .pluck(:id)
+ end
+
+ it { is_expected.to contain_exactly(group_2.id, nested_group_2.id, deep_nested_group_2.id) }
+ end
end
describe '.self_and_ancestor_ids' do
diff --git a/spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb b/spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb
index 017e55309f7..6cd871d354c 100644
--- a/spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb
+++ b/spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb
@@ -1,19 +1,19 @@
# frozen_string_literal: true
-RSpec.shared_examples 'project access tokens available #index' do
- let_it_be(:active_project_access_token) { create(:personal_access_token, user: bot_user) }
- let_it_be(:inactive_project_access_token) { create(:personal_access_token, :revoked, user: bot_user) }
+RSpec.shared_examples 'GET resource access tokens available' do
+ let_it_be(:active_resource_access_token) { create(:personal_access_token, user: bot_user) }
+ let_it_be(:inactive_resource_access_token) { create(:personal_access_token, :revoked, user: bot_user) }
- it 'retrieves active project access tokens' do
+ it 'retrieves active resource access tokens' do
subject
- expect(assigns(:active_project_access_tokens)).to contain_exactly(active_project_access_token)
+ expect(assigns(:active_resource_access_tokens)).to contain_exactly(active_resource_access_token)
end
- it 'retrieves inactive project access tokens' do
+ it 'retrieves inactive resource access tokens' do
subject
- expect(assigns(:inactive_project_access_tokens)).to contain_exactly(inactive_project_access_token)
+ expect(assigns(:inactive_resource_access_tokens)).to contain_exactly(inactive_resource_access_token)
end
it 'lists all available scopes' do
@@ -24,15 +24,15 @@ RSpec.shared_examples 'project access tokens available #index' do
it 'retrieves newly created personal access token value' do
token_value = 'random-value'
- allow(PersonalAccessToken).to receive(:redis_getdel).with("#{user.id}:#{project.id}").and_return(token_value)
+ allow(PersonalAccessToken).to receive(:redis_getdel).with("#{user.id}:#{resource.id}").and_return(token_value)
subject
- expect(assigns(:new_project_access_token)).to eq(token_value)
+ expect(assigns(:new_resource_access_token)).to eq(token_value)
end
end
-RSpec.shared_examples 'project access tokens available #create' do
+RSpec.shared_examples 'POST resource access tokens available' do
def created_token
PersonalAccessToken.order(:created_at).last
end
@@ -40,17 +40,17 @@ RSpec.shared_examples 'project access tokens available #create' do
it 'returns success message' do
subject
- expect(controller).to set_flash[:notice].to match('Your new project access token has been created.')
+ expect(flash[:notice]).to match('Your new access token has been created.')
end
- it 'creates project access token' do
+ it 'creates resource access token' do
access_level = access_token_params[:access_level] || Gitlab::Access::MAINTAINER
subject
expect(created_token.name).to eq(access_token_params[:name])
expect(created_token.scopes).to eq(access_token_params[:scopes])
expect(created_token.expires_at).to eq(access_token_params[:expires_at])
- expect(project.project_member(created_token.user).access_level).to eq(access_level)
+ expect(resource.member(created_token.user).access_level).to eq(access_level)
end
it 'creates project bot user' do
@@ -90,12 +90,12 @@ RSpec.shared_examples 'project access tokens available #create' do
it 'shows a failure alert' do
subject
- expect(controller).to set_flash[:alert].to match("Failed to create new project access token: Failed!")
+ expect(flash[:alert]).to match("Failed to create new access token: Failed!")
end
end
end
-RSpec.shared_examples 'project access tokens available #revoke' do
+RSpec.shared_examples 'PUT resource access tokens available' do
it 'calls delete user worker' do
expect(DeleteUserWorker).to receive(:perform_async).with(user.id, bot_user.id, skip_authorization: true)
@@ -105,7 +105,7 @@ RSpec.shared_examples 'project access tokens available #revoke' do
it 'removes membership of bot user' do
subject
- expect(project.reload.bots).not_to include(bot_user)
+ expect(resource.reload.bots).not_to include(bot_user)
end
it 'converts issuables of the bot user to ghost user' do
@@ -121,4 +121,18 @@ RSpec.shared_examples 'project access tokens available #revoke' do
expect(User.exists?(bot_user.id)).to be_falsy
end
+
+ context 'when unsuccessful' do
+ before do
+ allow_next_instance_of(ResourceAccessTokens::RevokeService) do |service|
+ allow(service).to receive(:execute).and_return ServiceResponse.error(message: 'Failed!')
+ end
+ end
+
+ it 'shows a failure alert' do
+ subject
+
+ expect(flash[:alert]).to include("Could not revoke access token")
+ end
+ end
end
diff --git a/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb
index 2fd5e6a5f91..9f96cb2a164 100644
--- a/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb
@@ -40,7 +40,6 @@ RSpec.shared_examples 'Debian packages upload request' do |status, body = nil|
expect(response.body).to match(body)
end
end
- it_behaves_like 'a package tracking event', described_class.name, 'push_package'
else
it "returns #{status}#{and_body}", :aggregate_failures do
subject
diff --git a/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb
index d576a5874fd..9385706d991 100644
--- a/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb
@@ -38,4 +38,28 @@ RSpec.shared_examples 'a package with files' do
'fileSha256' => first_file.file_sha256
)
end
+
+ context 'with package files pending destruction' do
+ let_it_be(:package_file_pending_destruction) { create(:package_file, :pending_destruction, package: package) }
+
+ let(:response_package_file_ids) { package_files_response.map { |pf| pf['id'] } }
+
+ it 'does not return them' do
+ expect(package.reload.package_files).to include(package_file_pending_destruction)
+
+ expect(response_package_file_ids).not_to include(package_file_pending_destruction.to_global_id.to_s)
+ end
+
+ context 'with packages_installable_package_files disabled' do
+ before(:context) do
+ stub_feature_flags(packages_installable_package_files: false)
+ end
+
+ it 'returns them' do
+ expect(package.reload.package_files).to include(package_file_pending_destruction)
+
+ expect(response_package_file_ids).to include(package_file_pending_destruction.to_global_id.to_s)
+ end
+ end
+ end
end
diff --git a/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb b/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb
index db70bc75c63..290bf58fb6b 100644
--- a/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb
@@ -221,6 +221,7 @@ RSpec.shared_examples 'handling nuget search requests' do |anonymous_requests_ex
let_it_be(:packages_c) { create_list(:nuget_package, 5, name: 'Dummy.PackageC', project: project) }
let_it_be(:package_d) { create(:nuget_package, name: 'Dummy.PackageD', version: '5.0.5-alpha', project: project) }
let_it_be(:package_e) { create(:nuget_package, name: 'Foo.BarE', project: project) }
+
let(:search_term) { 'uMmy' }
let(:take) { 26 }
let(:skip) { 0 }
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 827ae42f970..23aee912d2d 100644
--- a/spec/support/shared_examples/services/alert_management_shared_examples.rb
+++ b/spec/support/shared_examples/services/alert_management_shared_examples.rb
@@ -64,12 +64,16 @@ RSpec.shared_examples 'processes never-before-seen recovery alert' do
end
RSpec.shared_examples 'processes one firing and one resolved prometheus alerts' do
- it 'creates AlertManagement::Alert' do
+ it 'creates alerts and returns them in the payload', :aggregate_failures do
expect(Gitlab::AppLogger).not_to receive(:warn)
expect { subject }
.to change(AlertManagement::Alert, :count).by(2)
.and change(Note, :count).by(4)
+
+ expect(subject).to be_success
+ expect(subject.payload[:alerts]).to all(be_a_kind_of(AlertManagement::Alert))
+ expect(subject.payload[:alerts].size).to eq(2)
end
it_behaves_like 'processes incident issues'
diff --git a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
index f6e25ee6647..87bf134eeb8 100644
--- a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
@@ -71,6 +71,7 @@ end
RSpec.shared_examples 'an accessible' do
before do
stub_feature_flags(container_registry_migration_phase1: false)
+ stub_feature_flags(container_registry_cdn_redirect: false)
end
let(:access) do
@@ -163,6 +164,7 @@ RSpec.shared_examples 'a container registry auth service' do
before do
stub_feature_flags(container_registry_migration_phase1: false)
+ stub_feature_flags(container_registry_cdn_redirect: false)
end
describe '#full_access_token' do
diff --git a/spec/support/shared_examples/services/incident_shared_examples.rb b/spec/support/shared_examples/services/incident_shared_examples.rb
index 0277cce975a..36b0acf5a51 100644
--- a/spec/support/shared_examples/services/incident_shared_examples.rb
+++ b/spec/support/shared_examples/services/incident_shared_examples.rb
@@ -17,16 +17,6 @@ RSpec.shared_examples 'incident issue' do
end
end
-RSpec.shared_examples 'has incident label' do
- let(:label_properties) { attributes_for(:label, :incident) }
-
- it 'has exactly one incident label' do
- expect(issue.labels).to be_one do |label|
- label.slice(*label_properties.keys).symbolize_keys == label_properties
- end
- end
-end
-
# This shared_example requires the following variables:
# - issue (required)
#
@@ -45,6 +35,12 @@ RSpec.shared_examples 'not an incident issue' do
expect(issue.work_item_type.base_type).not_to eq('incident')
end
+ it_behaves_like 'does not have incident label'
+end
+
+RSpec.shared_examples 'does not have incident label' do
+ let(:label_properties) { attributes_for(:label, :incident) }
+
it 'has not an incident label' do
expect(issue.labels).not_to include(have_attributes(label_properties))
end
diff --git a/spec/support/shared_examples/services/service_ping/service_ping_payload_with_all_expected_metrics_shared_examples.rb b/spec/support/shared_examples/services/service_ping/service_ping_payload_with_all_expected_metrics_shared_examples.rb
index 535e7291b7e..856810a4de1 100644
--- a/spec/support/shared_examples/services/service_ping/service_ping_payload_with_all_expected_metrics_shared_examples.rb
+++ b/spec/support/shared_examples/services/service_ping/service_ping_payload_with_all_expected_metrics_shared_examples.rb
@@ -2,6 +2,8 @@
RSpec.shared_examples 'service ping payload with all expected metrics' do
specify do
+ allow(ApplicationRecord.database).to receive(:flavor).and_return(nil)
+
aggregate_failures do
expected_metrics.each do |metric|
is_expected.to have_usage_metric metric['key_path']
diff --git a/spec/support/shared_examples/services/service_ping/service_ping_payload_without_restricted_metrics_shared_examples.rb b/spec/support/shared_examples/services/service_ping/service_ping_payload_without_restricted_metrics_shared_examples.rb
index 9f18174cbc7..e05239a9a36 100644
--- a/spec/support/shared_examples/services/service_ping/service_ping_payload_without_restricted_metrics_shared_examples.rb
+++ b/spec/support/shared_examples/services/service_ping/service_ping_payload_without_restricted_metrics_shared_examples.rb
@@ -2,6 +2,8 @@
RSpec.shared_examples 'service ping payload without restricted metrics' do
specify do
+ allow(ApplicationRecord.database).to receive(:flavor).and_return(nil)
+
aggregate_failures do
restricted_metrics.each do |metric|
is_expected.not_to have_usage_metric metric['key_path']
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 7d652be8d05..68e37171ea2 100644
--- a/spec/support/shared_examples/work_item_base_types_importer.rb
+++ b/spec/support/shared_examples/work_item_base_types_importer.rb
@@ -3,8 +3,8 @@
RSpec.shared_examples 'work item base types importer' do
it 'creates all base work item types' do
# Fixtures need to run on a pristine DB, but the test suite preloads the base types before(:suite)
- WorkItem::Type.delete_all
+ WorkItems::Type.delete_all
- expect { subject }.to change(WorkItem::Type, :count).from(0).to(WorkItem::Type::BASE_TYPES.count)
+ expect { subject }.to change(WorkItems::Type, :count).from(0).to(WorkItems::Type::BASE_TYPES.count)
end
end
diff --git a/spec/support/shared_examples/workers/concerns/dependency_proxy/cleanup_worker_shared_examples.rb b/spec/support/shared_examples/workers/concerns/dependency_proxy/cleanup_worker_shared_examples.rb
index c9014ad549c..26444437826 100644
--- a/spec/support/shared_examples/workers/concerns/dependency_proxy/cleanup_worker_shared_examples.rb
+++ b/spec/support/shared_examples/workers/concerns/dependency_proxy/cleanup_worker_shared_examples.rb
@@ -13,12 +13,12 @@ RSpec.shared_examples 'dependency_proxy_cleanup_worker' do
end
context 'with work to do' do
- let_it_be(:artifact1) { create(factory_type, :expired, group: group) }
- let_it_be(:artifact2) { create(factory_type, :expired, group: group, updated_at: 6.months.ago, created_at: 2.years.ago) }
- let_it_be_with_reload(:artifact3) { create(factory_type, :expired, group: group, updated_at: 1.year.ago, created_at: 1.year.ago) }
+ let_it_be(:artifact1) { create(factory_type, :pending_destruction, group: group) }
+ let_it_be(:artifact2) { create(factory_type, :pending_destruction, group: group, updated_at: 6.months.ago, created_at: 2.years.ago) }
+ let_it_be_with_reload(:artifact3) { create(factory_type, :pending_destruction, group: group, updated_at: 1.year.ago, created_at: 1.year.ago) }
let_it_be(:artifact4) { create(factory_type, group: group, updated_at: 2.years.ago, created_at: 2.years.ago) }
- it 'deletes the oldest expired artifact based on updated_at', :aggregate_failures do
+ it 'deletes the oldest artifact pending destruction based on updated_at', :aggregate_failures do
expect(worker).to receive(:log_extra_metadata_on_done).with("#{factory_type}_id".to_sym, artifact3.id)
expect(worker).to receive(:log_extra_metadata_on_done).with(:group_id, group.id)
@@ -40,10 +40,8 @@ RSpec.shared_examples 'dependency_proxy_cleanup_worker' do
end
describe '#remaining_work_count' do
- let_it_be(:expired_artifacts) do
- (1..3).map do |_|
- create(factory_type, :expired, group: group)
- end
+ before(:context) do
+ create_list(factory_type, 3, :pending_destruction, group: group)
end
subject { worker.remaining_work_count }
diff --git a/spec/support/system_exit_detected.rb b/spec/support/system_exit_detected.rb
new file mode 100644
index 00000000000..86c6af3ba8c
--- /dev/null
+++ b/spec/support/system_exit_detected.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+SystemExitDetected = Class.new(RuntimeError)
+
+RSpec.configure do |config|
+ config.around do |example|
+ example.run
+ rescue SystemExit
+ # In any cases, we cannot raise SystemExit in the tests,
+ # because it'll skip any following tests from running.
+ # Convert it to something that won't skip everything.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/350060
+ raise SystemExitDetected, "SystemExit should be rescued in the tests!"
+ end
+end