diff options
Diffstat (limited to 'spec/support')
35 files changed, 857 insertions, 117 deletions
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb index 2bd4750dffa..5ae042e4148 100644 --- a/spec/support/capybara.rb +++ b/spec/support/capybara.rb @@ -16,6 +16,7 @@ JSConsoleError = Class.new(StandardError) JS_CONSOLE_FILTER = Regexp.union([ '"[HMR] Waiting for update signal from WDS..."', '"[WDS] Hot Module Replacement enabled."', + '"[WDS] Live Reloading enabled."', "Download the Vue Devtools extension" ]) diff --git a/spec/support/database_cleaner.rb b/spec/support/database_cleaner.rb index 25260a56578..aaf408f6143 100644 --- a/spec/support/database_cleaner.rb +++ b/spec/support/database_cleaner.rb @@ -1,21 +1,7 @@ # frozen_string_literal: true -require 'database_cleaner/active_record/deletion' require_relative 'db_cleaner' -module FakeInformationSchema - # Work around a bug in DatabaseCleaner when using the deletion strategy: - # https://github.com/DatabaseCleaner/database_cleaner/issues/347 - # - # On MySQL, if the information schema is said to exist, we use an inaccurate - # row count leading to some tables not being cleaned when they should - def information_schema_exists?(_connection) - false - end -end - -DatabaseCleaner::ActiveRecord::Deletion.prepend(FakeInformationSchema) - RSpec.configure do |config| include DbCleaner diff --git a/spec/support/helpers/admin_mode_helpers.rb b/spec/support/helpers/admin_mode_helpers.rb index de8ffe40536..e995a7d4f5e 100644 --- a/spec/support/helpers/admin_mode_helpers.rb +++ b/spec/support/helpers/admin_mode_helpers.rb @@ -3,7 +3,7 @@ # Helper for enabling admin mode in tests module AdminModeHelper - # Users are logged in by default in user mode and have to switch to admin + # Administrators are logged in by default in user mode and have to switch to admin # mode for accessing any administrative functionality. This helper lets a user # be in admin mode without requiring a second authentication step (provided # the user is an admin) diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb index 80a3f7df05f..e21b3aea3da 100644 --- a/spec/support/helpers/graphql_helpers.rb +++ b/spec/support/helpers/graphql_helpers.rb @@ -297,6 +297,10 @@ module GraphqlHelpers extract_attribute ? item['node'][extract_attribute] : item['node'] end end + + def global_id_of(model) + model.to_global_id.to_s + end end # This warms our schema, doing this as part of loading the helpers to avoid diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb index 677aef57661..ad4ae93a027 100644 --- a/spec/support/helpers/kubernetes_helpers.rb +++ b/spec/support/helpers/kubernetes_helpers.rb @@ -84,7 +84,7 @@ module KubernetesHelpers end logs_url = service.api_url + "/api/v1/namespaces/#{namespace}/pods/#{pod_name}" \ - "/log?#{container_query_param}tailLines=#{Clusters::Platforms::Kubernetes::LOGS_LIMIT}" + "/log?#{container_query_param}tailLines=#{Clusters::Platforms::Kubernetes::LOGS_LIMIT}×tamps=true" if status response = { status: status } @@ -194,6 +194,11 @@ module KubernetesHelpers .to_return(kube_response({})) end + def stub_kubeclient_put_cluster_role_binding(api_url, name) + WebMock.stub_request(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/clusterrolebindings/#{name}") + .to_return(kube_response({})) + end + def stub_kubeclient_get_role_binding(api_url, name, namespace: 'default') WebMock.stub_request(:get, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings/#{name}") .to_return(kube_response({})) @@ -219,11 +224,21 @@ module KubernetesHelpers .to_return(kube_response({})) end + def stub_kubeclient_get_namespaces(api_url) + WebMock.stub_request(:get, api_url + '/api/v1/namespaces') + .to_return(kube_response(kube_v1_namespace_list_body)) + end + def stub_kubeclient_get_namespace(api_url, namespace: 'default') WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}") .to_return(kube_response({})) end + def stub_kubeclient_put_cluster_role(api_url, name) + WebMock.stub_request(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/clusterroles/#{name}") + .to_return(kube_response({})) + end + def stub_kubeclient_put_role(api_url, name, namespace: 'default') WebMock.stub_request(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/roles/#{name}") .to_return(kube_response({})) @@ -257,6 +272,20 @@ module KubernetesHelpers } end + def kube_v1_namespace_list_body + { + "kind" => "NamespaceList", + "apiVersion" => "v1", + "items" => [ + { + "metadata" => { + "name" => "knative-serving" + } + } + ] + } + end + def kube_v1beta1_discovery_body { "kind" => "APIResourceList", @@ -302,7 +331,7 @@ module KubernetesHelpers end def kube_logs_body - "Log 1\nLog 2\nLog 3" + "2019-12-13T14:04:22.123456Z Log 1\n2019-12-13T14:04:23.123456Z Log 2\n2019-12-13T14:04:24.123456Z Log 3" end def kube_deployments_body @@ -322,7 +351,7 @@ module KubernetesHelpers def kube_knative_services_body(**options) { "kind" => "List", - "items" => [knative_07_service(options)] + "items" => [knative_09_service(options)] } end @@ -511,6 +540,58 @@ module KubernetesHelpers end # noinspection RubyStringKeysInHashInspection + def knative_09_service(name: 'kubetest', namespace: 'default', domain: 'example.com', description: 'a knative service', environment: 'production') + { "apiVersion" => "serving.knative.dev/v1alpha1", + "kind" => "Service", + "metadata" => + { "annotations" => + { "serving.knative.dev/creator" => "system:serviceaccount:#{namespace}:#{namespace}-service-account", + "serving.knative.dev/lastModifier" => "system:serviceaccount:#{namespace}:#{namespace}-service-account" }, + "creationTimestamp" => "2019-10-22T21:19:13Z", + "generation" => 1, + "labels" => { "service" => name }, + "name" => name, + "namespace" => namespace, + "resourceVersion" => "289726", + "selfLink" => "/apis/serving.knative.dev/v1alpha1/namespaces/#{namespace}/services/#{name}", + "uid" => "988349fa-f511-11e9-9ea1-42010a80005e" }, + "spec" => { + "template" => { + "metadata" => { + "annotations" => { "Description" => description }, + "creationTimestamp" => "2019-10-22T21:19:12Z", + "labels" => { "service" => name } + }, + "spec" => { + "containers" => [{ + "env" => + [{ "name" => "timestamp", "value" => "2019-10-22 21:19:12" }], + "image" => "image_name", + "name" => "user-container", + "resources" => {} + }], + "timeoutSeconds" => 300 + } + }, + "traffic" => [{ "latestRevision" => true, "percent" => 100 }] + }, + "status" => + { "address" => { "url" => "http://#{name}.#{namespace}.svc.cluster.local" }, + "conditions" => + [{ "lastTransitionTime" => "2019-10-22T21:20:15Z", "status" => "True", "type" => "ConfigurationsReady" }, + { "lastTransitionTime" => "2019-10-22T21:20:15Z", "status" => "True", "type" => "Ready" }, + { "lastTransitionTime" => "2019-10-22T21:20:15Z", "status" => "True", "type" => "RoutesReady" }], + "latestCreatedRevisionName" => "#{name}-92tsj", + "latestReadyRevisionName" => "#{name}-92tsj", + "observedGeneration" => 1, + "traffic" => [{ "latestRevision" => true, "percent" => 100, "revisionName" => "#{name}-92tsj" }], + "url" => "http://#{name}.#{namespace}.#{domain}" }, + "environment_scope" => environment, + "cluster_id" => 5, + "podcount" => 0 } + end + + # noinspection RubyStringKeysInHashInspection def knative_05_service(name: 'kubetest', namespace: 'default', domain: 'example.com', description: 'a knative service', environment: 'production') { "apiVersion" => "serving.knative.dev/v1alpha1", "kind" => "Service", diff --git a/spec/support/helpers/live_debugger.rb b/spec/support/helpers/live_debugger.rb index d6091035b59..cdb068760f4 100644 --- a/spec/support/helpers/live_debugger.rb +++ b/spec/support/helpers/live_debugger.rb @@ -6,11 +6,17 @@ module LiveDebugger def live_debug puts puts "Current example is paused for live debugging." - puts "Opening #{current_url} in your default browser..." + + if ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i + puts "Switch to the Chrome window that was automatically opened to run the test in order to view current page" + else + puts "Opening #{current_url} in your default browser..." + end + puts "The current user credentials are: #{@current_user.username} / #{@current_user.password}" if @current_user puts "Press any key to resume the execution of the example!!" - `open #{current_url}` + `open #{current_url}` if ENV['CHROME_HEADLESS'] !~ /^(false|no|0)$/i loop until $stdin.getch diff --git a/spec/support/helpers/metrics_dashboard_helpers.rb b/spec/support/helpers/metrics_dashboard_helpers.rb index 0e86b6dfda7..5b425d0964d 100644 --- a/spec/support/helpers/metrics_dashboard_helpers.rb +++ b/spec/support/helpers/metrics_dashboard_helpers.rb @@ -19,7 +19,11 @@ module MetricsDashboardHelpers end def system_dashboard_path - Metrics::Dashboard::SystemDashboardService::SYSTEM_DASHBOARD_PATH + Metrics::Dashboard::SystemDashboardService::DASHBOARD_PATH + end + + def pod_dashboard_path + Metrics::Dashboard::PodDashboardService::DASHBOARD_PATH end def business_metric_title @@ -53,6 +57,15 @@ module MetricsDashboardHelpers it_behaves_like 'valid dashboard service response for schema' end + shared_examples_for 'caches the unprocessed dashboard for subsequent calls' do + it do + expect(YAML).to receive(:safe_load).once.and_call_original + + described_class.new(*service_params).get_dashboard + described_class.new(*service_params).get_dashboard + end + end + shared_examples_for 'valid embedded dashboard service response' do let(:dashboard_schema) { JSON.parse(fixture_file('lib/gitlab/metrics/dashboard/schemas/embedded_dashboard.json')) } diff --git a/spec/support/helpers/position_tracer_helpers.rb b/spec/support/helpers/position_tracer_helpers.rb index bbf6e06dd40..7516694d4fe 100644 --- a/spec/support/helpers/position_tracer_helpers.rb +++ b/spec/support/helpers/position_tracer_helpers.rb @@ -50,7 +50,7 @@ module PositionTracerHelpers end def create_branch(new_name, branch_name) - CreateBranchService.new(project, current_user).execute(new_name, branch_name) + ::Branches::CreateService.new(project, current_user).execute(new_name, branch_name) end def create_file(branch_name, file_name, content) diff --git a/spec/support/helpers/sentry_client_helpers.rb b/spec/support/helpers/sentry_client_helpers.rb new file mode 100644 index 00000000000..7476b5fb249 --- /dev/null +++ b/spec/support/helpers/sentry_client_helpers.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module SentryClientHelpers + private + + def stub_sentry_request(url, body: {}, status: 200, headers: {}) + stub_request(:get, url) + .to_return( + status: status, + headers: { 'Content-Type' => 'application/json' }.merge(headers), + body: body.to_json + ) + end +end diff --git a/spec/support/helpers/stub_gitlab_calls.rb b/spec/support/helpers/stub_gitlab_calls.rb index fe343da7838..ff4b9db8ad9 100644 --- a/spec/support/helpers/stub_gitlab_calls.rb +++ b/spec/support/helpers/stub_gitlab_calls.rb @@ -19,24 +19,28 @@ module StubGitlabCalls end def stub_ci_pipeline_yaml_file(ci_yaml_content) - allow_any_instance_of(Repository).to receive(:gitlab_ci_yml_for).and_return(ci_yaml_content) + allow_any_instance_of(Repository) + .to receive(:gitlab_ci_yml_for) + .and_return(ci_yaml_content) # Ensure we don't hit auto-devops when config not found in repository unless ci_yaml_content allow_any_instance_of(Project).to receive(:auto_devops_enabled?).and_return(false) end + + # Stub the first call to `include:[local: .gitlab-ci.yml]` when + # evaluating the CI root config content. + if Feature.enabled?(:ci_root_config_content, default_enabled: true) + allow_any_instance_of(Gitlab::Ci::Config::External::File::Local) + .to receive(:content) + .and_return(ci_yaml_content) + end end def stub_pipeline_modified_paths(pipeline, modified_paths) allow(pipeline).to receive(:modified_paths).and_return(modified_paths) end - def stub_repository_ci_yaml_file(sha:, path: '.gitlab-ci.yml') - allow_any_instance_of(Repository) - .to receive(:gitlab_ci_yml_for).with(sha, path) - .and_return(gitlab_ci_yaml) - end - def stub_ci_builds_disabled allow_any_instance_of(Project).to receive(:builds_enabled?).and_return(false) end diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb index 3f7002b8768..392300a4436 100644 --- a/spec/support/helpers/stub_object_storage.rb +++ b/spec/support/helpers/stub_object_storage.rb @@ -56,6 +56,13 @@ module StubObjectStorage **params) end + def stub_package_file_object_storage(**params) + stub_object_storage_uploader(config: Gitlab.config.packages.object_store, + uploader: ::Packages::PackageFileUploader, + remote_directory: 'packages', + **params) + end + def stub_uploads_object_storage(uploader = described_class, **params) stub_object_storage_uploader(config: Gitlab.config.uploads.object_store, uploader: uploader, diff --git a/spec/support/import_export/configuration_helper.rb b/spec/support/import_export/configuration_helper.rb index 122df7f27f0..2e5a99bb8b2 100644 --- a/spec/support/import_export/configuration_helper.rb +++ b/spec/support/import_export/configuration_helper.rb @@ -10,21 +10,54 @@ module ConfigurationHelper end end + def all_relations(tree, tree_path = []) + tree.flat_map do |relation_name, relations| + relation_path = tree_path + [relation_name] + [relation_path] + all_relations(relations, relation_path) + end + end + + def config_hash(config = Gitlab::ImportExport.config_file) + Gitlab::ImportExport::Config.new(config: config).to_h + end + + def relation_paths_for(key, config: Gitlab::ImportExport.config_file) + # - project is not part of the tree, so it has to be added manually. + all_relations({ project: config_hash(config).dig(:tree, key) }) + end + + def relation_names_for(key, config: Gitlab::ImportExport.config_file) + names = names_from_tree(config_hash(config).dig(:tree, key)) + # Remove duplicated or add missing models + # - project is not part of the tree, so it has to be added manually. + # - milestone, labels, merge_request have both singular and plural versions in the tree, so remove the duplicates. + # - User, Author... Models we do not care about for checking models + names.flatten.uniq - %w(milestones labels user author merge_request design) + [key.to_s] + end + def relation_class_for_name(relation_name) relation_name = Gitlab::ImportExport::RelationFactory.overrides[relation_name.to_sym] || relation_name Gitlab::ImportExport::RelationFactory.relation_class(relation_name) end - def parsed_attributes(relation_name, attributes) - excluded_attributes = config_hash['excluded_attributes'][relation_name] - included_attributes = config_hash['included_attributes'][relation_name] - + def parsed_attributes(relation_name, attributes, config: Gitlab::ImportExport.config_file) + import_export_config = config_hash(config) + excluded_attributes = import_export_config[:excluded_attributes][relation_name.to_sym] + included_attributes = import_export_config[:included_attributes][relation_name.to_sym] attributes = attributes - JSON[excluded_attributes.to_json] if excluded_attributes attributes = attributes & JSON[included_attributes.to_json] if included_attributes attributes end + def prohibited_key?(key) + key =~ Gitlab::ImportExport::AttributeCleaner::PROHIBITED_REFERENCES && !permitted_key?(key) + end + + def permitted_key?(key) + Gitlab::ImportExport::AttributeCleaner::ALLOWED_REFERENCES.include?(key) + end + def associations_for(safe_model) safe_model.reflect_on_all_associations.map { |assoc| assoc.name.to_s } end diff --git a/spec/support/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb index d735c10f698..dbf457a9200 100644 --- a/spec/support/matchers/graphql_matchers.rb +++ b/spec/support/matchers/graphql_matchers.rb @@ -28,6 +28,19 @@ RSpec::Matchers.define :have_graphql_fields do |*expected| end end +RSpec::Matchers.define :include_graphql_fields do |*expected| + expected_field_names = expected.map { |name| GraphqlHelpers.fieldnamerize(name) } + + match do |kls| + expect(kls.fields.keys).to include(*expected_field_names) + end + + failure_message do |kls| + missing = expected_field_names - kls.fields.keys + "is missing fields: <#{missing.inspect}>" if missing.any? + end +end + RSpec::Matchers.define :have_graphql_field do |field_name, args = {}| match do |kls| field = kls.fields[GraphqlHelpers.fieldnamerize(field_name)] @@ -64,6 +77,12 @@ RSpec::Matchers.define :have_graphql_type do |expected| end end +RSpec::Matchers.define :have_non_null_graphql_type do |expected| + match do |field| + expect(field.type).to eq(!expected.to_graphql) + end +end + RSpec::Matchers.define :have_graphql_resolver do |expected| match do |field| case expected diff --git a/spec/support/matchers/navigation_matcher.rb b/spec/support/matchers/navigation_matcher.rb index ad73c96031e..a0beecbfb2c 100644 --- a/spec/support/matchers/navigation_matcher.rb +++ b/spec/support/matchers/navigation_matcher.rb @@ -9,6 +9,6 @@ end RSpec::Matchers.define :have_active_sub_navigation do |expected| match do |page| - expect(page.find('.sidebar-sub-level-items > li.active:not(.fly-out-top-item)')).to have_content(expected) + expect(page).to have_css('.sidebar-sub-level-items > li.active:not(.fly-out-top-item)', text: expected) end end 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 65398c13d90..480c5a0fda0 100644 --- a/spec/support/shared_contexts/policies/project_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/project_policy_shared_context.rb @@ -39,12 +39,13 @@ RSpec.shared_context 'ProjectPolicy context' do update_pipeline create_merge_request_from create_wiki push_code resolve_note create_container_image update_container_image create_environment create_deployment update_deployment create_release update_release + update_environment ] end let(:base_maintainer_permissions) do %i[ - push_to_delete_protected_branch update_project_snippet update_environment + push_to_delete_protected_branch update_project_snippet admin_project_snippet admin_project_member admin_note admin_wiki admin_project admin_commit_status admin_build admin_container_image admin_pipeline admin_environment admin_deployment destroy_release add_cluster diff --git a/spec/support/shared_examples/controllers/environments_controller_shared_examples.rb b/spec/support/shared_examples/controllers/environments_controller_shared_examples.rb new file mode 100644 index 00000000000..3540f60bf1b --- /dev/null +++ b/spec/support/shared_examples/controllers/environments_controller_shared_examples.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +shared_examples_for 'successful response for #cancel_auto_stop' do + include GitlabRoutingHelper + + context 'when request is html' do + let(:params) { environment_params(format: :html) } + + it 'redirects to show page' do + subject + + expect(response).to redirect_to(environment_path(environment)) + expect(flash[:notice]).to eq('Auto stop successfully canceled.') + end + + it 'expires etag caching' do + expect_next_instance_of(Gitlab::EtagCaching::Store) do |etag_caching| + expect(etag_caching).to receive(:touch).with(project_environments_path(project, format: :json)) + end + + subject + end + end + + context 'when request is js' do + let(:params) { environment_params(format: :json) } + + it 'responds as ok' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['message']).to eq('Auto stop successfully canceled.') + end + + it 'expires etag caching' do + expect_next_instance_of(Gitlab::EtagCaching::Store) do |etag_caching| + expect(etag_caching).to receive(:touch).with(project_environments_path(project, format: :json)) + end + + subject + end + end +end + +shared_examples_for 'failed response for #cancel_auto_stop' do + context 'when request is html' do + let(:params) { environment_params(format: :html) } + + it 'redirects to show page' do + subject + + expect(response).to redirect_to(environment_path(environment)) + expect(flash[:alert]).to eq("Failed to cancel auto stop because #{message}.") + end + end + + context 'when request is js' do + let(:params) { environment_params(format: :json) } + + it 'responds as unprocessable entity' do + subject + + expect(response).to have_gitlab_http_status(:unprocessable_entity) + expect(json_response['message']).to eq("Failed to cancel auto stop because #{message}.") + end + end +end diff --git a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb index c24418b2f90..8962d98218a 100644 --- a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb +++ b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb @@ -74,7 +74,7 @@ shared_examples 'handle uploads' do end before do - expect(FileUploader).to receive(:generate_secret).and_return(secret) + allow(FileUploader).to receive(:generate_secret).and_return(secret) UploadService.new(model, jpg, uploader_class).execute end @@ -88,6 +88,18 @@ shared_examples 'handle uploads' do end end + context 'when the upload does not have a MIME type that Rails knows' do + let(:po) { fixture_file_upload('spec/fixtures/missing_metadata.po', 'text/plain') } + + it 'falls back to the null type' do + UploadService.new(model, po, uploader_class).execute + + get :show, params: params.merge(secret: secret, filename: 'missing_metadata.po') + + expect(response.headers['Content-Type']).to eq('application/octet-stream') + end + end + context "when the model is public" do before do model.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PUBLIC) diff --git a/spec/support/shared_examples/diff_file_collections.rb b/spec/support/shared_examples/diff_file_collections.rb index 4c64abd2a97..c8bd137bf84 100644 --- a/spec/support/shared_examples/diff_file_collections.rb +++ b/spec/support/shared_examples/diff_file_collections.rb @@ -57,3 +57,45 @@ shared_examples 'unfoldable diff' do subject.unfold_diff_files([position]) end end + +shared_examples 'cacheable diff collection' do + let(:cache) { instance_double(Gitlab::Diff::HighlightCache) } + + before do + expect(Gitlab::Diff::HighlightCache).to receive(:new).with(subject) { cache } + end + + describe '#write_cache' do + it 'calls Gitlab::Diff::HighlightCache#write_if_empty' do + expect(cache).to receive(:write_if_empty).once + + subject.write_cache + end + end + + describe '#clear_cache' do + it 'calls Gitlab::Diff::HighlightCache#clear' do + expect(cache).to receive(:clear).once + + subject.clear_cache + end + end + + describe '#cache_key' do + it 'calls Gitlab::Diff::HighlightCache#key' do + expect(cache).to receive(:key).once + + subject.cache_key + end + end + + describe '#diff_files' do + it 'calls Gitlab::Diff::HighlightCache#decorate' do + expect(cache).to receive(:decorate) + .with(instance_of(Gitlab::Diff::File)) + .exactly(cacheable_files_count).times + + subject.diff_files + end + end +end diff --git a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb index c0db4cdde72..da966fd2200 100644 --- a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb +++ b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb @@ -8,11 +8,14 @@ RSpec.shared_examples 'a creatable merge request' do page.within '.dropdown-menu-user' do click_link user2.name end + expect(find('input[name="merge_request[assignee_ids][]"]', visible: false).value).to match(user2.id.to_s) page.within '.js-assignee-search' do expect(page).to have_content user2.name end + click_link 'Assign to me' + expect(find('input[name="merge_request[assignee_ids][]"]', visible: false).value).to match(user.id.to_s) page.within '.js-assignee-search' do expect(page).to have_content user.name @@ -22,6 +25,7 @@ RSpec.shared_examples 'a creatable merge request' do page.within '.issue-milestone' do click_link milestone.title end + expect(find('input[name="merge_request[milestone_id]"]', visible: false).value).to match(milestone.id.to_s) page.within '.js-milestone-select' do expect(page).to have_content milestone.title @@ -32,6 +36,7 @@ RSpec.shared_examples 'a creatable merge request' do click_link label.title click_link label2.title end + page.within '.js-label-select' do expect(page).to have_content label.title end @@ -58,8 +63,9 @@ RSpec.shared_examples 'a creatable merge request' do it 'updates the branches when selecting a new target project', :js do target_project_member = target_project.owner - CreateBranchService.new(target_project, target_project_member) - .execute('a-brand-new-branch-to-test', 'master') + ::Branches::CreateService.new(target_project, target_project_member) + .execute('a-brand-new-branch-to-test', 'master') + visit project_new_merge_request_path(source_project) first('.js-target-project').click diff --git a/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb b/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb index f2e1a95345b..522211340ea 100644 --- a/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb +++ b/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb @@ -26,6 +26,7 @@ end RSpec.shared_examples 'a Note mutation when there are active record validation errors' do |model: Note| before do expect_next_instance_of(model) do |note| + allow(note).to receive_message_chain(:errors, :empty?).and_return(true) expect(note).to receive(:valid?).at_least(:once).and_return(false) expect(note).to receive_message_chain( :errors, diff --git a/spec/support/shared_examples/lib/banzai/filters/sanitization_filter_shared_examples.rb b/spec/support/shared_examples/lib/banzai/filters/sanitization_filter_shared_examples.rb new file mode 100644 index 00000000000..134e38833cf --- /dev/null +++ b/spec/support/shared_examples/lib/banzai/filters/sanitization_filter_shared_examples.rb @@ -0,0 +1,182 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'default whitelist' do + it 'sanitizes tags that are not whitelisted' do + act = %q{<textarea>no inputs</textarea> and <blink>no blinks</blink>} + exp = 'no inputs and no blinks' + expect(filter(act).to_html).to eq exp + end + + it 'sanitizes tag attributes' do + act = %q{<a href="http://example.com/bar.html" onclick="bar">Text</a>} + exp = %q{<a href="http://example.com/bar.html">Text</a>} + expect(filter(act).to_html).to eq exp + end + + it 'sanitizes javascript in attributes' do + act = %q(<a href="javascript:alert('foo')">Text</a>) + exp = '<a>Text</a>' + expect(filter(act).to_html).to eq exp + end + + it 'sanitizes mixed-cased javascript in attributes' do + act = %q(<a href="javaScript:alert('foo')">Text</a>) + exp = '<a>Text</a>' + expect(filter(act).to_html).to eq exp + end + + it 'allows whitelisted HTML tags from the user' do + exp = act = "<dl>\n<dt>Term</dt>\n<dd>Definition</dd>\n</dl>" + expect(filter(act).to_html).to eq exp + end + + it 'sanitizes `class` attribute on any element' do + act = %q{<strong class="foo">Strong</strong>} + expect(filter(act).to_html).to eq %q{<strong>Strong</strong>} + end + + it 'sanitizes `id` attribute on any element' do + act = %q{<em id="foo">Emphasis</em>} + expect(filter(act).to_html).to eq %q{<em>Emphasis</em>} + end +end + +RSpec.shared_examples 'XSS prevention' do + # Adapted from the Sanitize test suite: http://git.io/vczrM + protocols = { + 'protocol-based JS injection: simple, no spaces' => { + input: '<a href="javascript:alert(\'XSS\');">foo</a>', + output: '<a>foo</a>' + }, + + 'protocol-based JS injection: simple, spaces before' => { + input: '<a href="javascript :alert(\'XSS\');">foo</a>', + output: '<a>foo</a>' + }, + + 'protocol-based JS injection: simple, spaces after' => { + input: '<a href="javascript: alert(\'XSS\');">foo</a>', + output: '<a>foo</a>' + }, + + 'protocol-based JS injection: simple, spaces before and after' => { + input: '<a href="javascript : alert(\'XSS\');">foo</a>', + output: '<a>foo</a>' + }, + + 'protocol-based JS injection: preceding colon' => { + input: '<a href=":javascript:alert(\'XSS\');">foo</a>', + output: '<a>foo</a>' + }, + + 'protocol-based JS injection: UTF-8 encoding' => { + input: '<a href="javascript:">foo</a>', + output: '<a>foo</a>' + }, + + 'protocol-based JS injection: long UTF-8 encoding' => { + input: '<a href="javascript:">foo</a>', + output: '<a>foo</a>' + }, + + 'protocol-based JS injection: long UTF-8 encoding without semicolons' => { + input: '<a href=javascript:alert('XSS')>foo</a>', + output: '<a>foo</a>' + }, + + 'protocol-based JS injection: hex encoding' => { + input: '<a href="javascript:">foo</a>', + output: '<a>foo</a>' + }, + + 'protocol-based JS injection: long hex encoding' => { + input: '<a href="javascript:">foo</a>', + output: '<a>foo</a>' + }, + + 'protocol-based JS injection: hex encoding without semicolons' => { + input: '<a href=javascript:alert('XSS')>foo</a>', + output: '<a>foo</a>' + }, + + 'protocol-based JS injection: null char' => { + input: "<a href=java\0script:alert(\"XSS\")>foo</a>", + output: '<a href="java"></a>' + }, + + 'protocol-based JS injection: invalid URL char' => { + input: '<img src=java\script:alert("XSS")>', + output: '<img>' + }, + + 'protocol-based JS injection: Unicode' => { + input: %Q(<a href="\u0001java\u0003script:alert('XSS')">foo</a>), + output: '<a>foo</a>' + }, + + 'protocol-based JS injection: spaces and entities' => { + input: '<a href="  javascript:alert(\'XSS\');">foo</a>', + output: '<a href="">foo</a>' + }, + + 'protocol whitespace' => { + input: '<a href=" http://example.com/"></a>', + output: '<a href="http://example.com/"></a>' + } + } + + protocols.each do |name, data| + it "disallows #{name}" do + doc = filter(data[:input]) + + expect(doc.to_html).to eq data[:output] + end + end + + it 'disallows data links' do + input = '<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">XSS</a>' + output = filter(input) + + expect(output.to_html).to eq '<a>XSS</a>' + end + + it 'disallows vbscript links' do + input = '<a href="vbscript:alert(document.domain)">XSS</a>' + output = filter(input) + + expect(output.to_html).to eq '<a>XSS</a>' + end +end + +RSpec.shared_examples 'sanitize link' do + it 'removes `rel` attribute from `a` elements' do + act = %q{<a href="#" rel="nofollow">Link</a>} + exp = %q{<a href="#">Link</a>} + + expect(filter(act).to_html).to eq exp + end + + it 'disallows invalid URIs' do + expect(Addressable::URI).to receive(:parse).with('foo://example.com') + .and_raise(Addressable::URI::InvalidURIError) + + input = '<a href="foo://example.com">Foo</a>' + output = filter(input) + + expect(output.to_html).to eq '<a>Foo</a>' + end + + it 'allows non-standard anchor schemes' do + exp = %q{<a href="irc://irc.freenode.net/git">IRC</a>} + act = filter(exp) + + expect(act.to_html).to eq exp + end + + it 'allows relative links' do + exp = %q{<a href="foo/bar.md">foo/bar.md</a>} + act = filter(exp) + + expect(act.to_html).to eq exp + end +end diff --git a/spec/support/shared_examples/lib/gitlab/import_export/project_tree_restorer_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/import_export/project_tree_restorer_shared_examples.rb index f26a8554055..4ef9a9930f7 100644 --- a/spec/support/shared_examples/lib/gitlab/import_export/project_tree_restorer_shared_examples.rb +++ b/spec/support/shared_examples/lib/gitlab/import_export/project_tree_restorer_shared_examples.rb @@ -2,7 +2,7 @@ # Shared examples for ProjectTreeRestorer (shared to allow the testing # of EE-specific features) -RSpec.shared_examples 'restores project correctly' do |**results| +RSpec.shared_examples 'restores project successfully' do |**results| it 'restores the project' do expect(shared.errors).to be_empty expect(restored_project_json).to be_truthy @@ -32,6 +32,10 @@ RSpec.shared_examples 'restores project correctly' do |**results| it 'does not set params that are excluded from import_export settings' do expect(project.import_type).to be_nil - expect(project.creator_id).not_to eq 123 + expect(project.creator_id).not_to eq 999 + end + + it 'records exact number of import failures' do + expect(project.import_failures.size).to eq(results.fetch(:import_failures, 0)) end end diff --git a/spec/support/shared_examples/lib/sentry/client_shared_examples.rb b/spec/support/shared_examples/lib/sentry/client_shared_examples.rb new file mode 100644 index 00000000000..76b71ebd3c5 --- /dev/null +++ b/spec/support/shared_examples/lib/sentry/client_shared_examples.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +# Requires sentry_api_request and subject to be defined +RSpec.shared_examples 'calls sentry api' do + it 'calls sentry api' do + subject + + expect(sentry_api_request).to have_been_requested + end +end + +# Requires sentry_api_url and subject to be defined +RSpec.shared_examples 'no Sentry redirects' do + let(:redirect_to) { 'https://redirected.example.com' } + let(:other_url) { 'https://other.example.org' } + + let!(:redirected_req_stub) { stub_sentry_request(other_url) } + + let!(:redirect_req_stub) do + stub_sentry_request( + sentry_api_url, + status: 302, + headers: { location: redirect_to } + ) + end + + it 'does not follow redirects' do + expect { subject }.to raise_exception(Sentry::Client::Error, 'Sentry response status code: 302') + expect(redirect_req_stub).to have_been_requested + expect(redirected_req_stub).not_to have_been_requested + end +end + +RSpec.shared_examples 'maps Sentry exceptions' do + exceptions = { + Gitlab::HTTP::Error => 'Error when connecting to Sentry', + Net::OpenTimeout => 'Connection to Sentry timed out', + SocketError => 'Received SocketError when trying to connect to Sentry', + OpenSSL::SSL::SSLError => 'Sentry returned invalid SSL data', + Errno::ECONNREFUSED => 'Connection refused', + StandardError => 'Sentry request failed due to StandardError' + } + + exceptions.each do |exception, message| + context "#{exception}" do + before do + stub_request(:get, sentry_request_url).to_raise(exception) + end + + it do + expect { subject } + .to raise_exception(Sentry::Client::Error, message) + end + end + end +end diff --git a/spec/support/shared_examples/mail_room_shared_examples.rb b/spec/support/shared_examples/mail_room_shared_examples.rb new file mode 100644 index 00000000000..4cca29250e2 --- /dev/null +++ b/spec/support/shared_examples/mail_room_shared_examples.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +shared_examples_for 'only truthy if both enabled and address are truthy' do |target_proc| + context 'with both enabled and address as truthy values' do + it 'is truthy' do + stub_config(enabled: true, address: 'localhost') + + expect(target_proc.call).to be_truthy + end + end + + context 'with address only as truthy' do + it 'is falsey' do + stub_config(enabled: false, address: 'localhost') + + expect(target_proc.call).to be_falsey + end + end + + context 'with enabled only as truthy' do + it 'is falsey' do + stub_config(enabled: true, address: nil) + + expect(target_proc.call).to be_falsey + end + end + + context 'with neither address nor enabled as truthy' do + it 'is falsey' do + stub_config(enabled: false, address: nil) + + expect(target_proc.call).to be_falsey + end + end +end diff --git a/spec/support/shared_examples/mentionable_shared_examples.rb b/spec/support/shared_examples/mentionable_shared_examples.rb index 93a8c4709a6..6efc471ce75 100644 --- a/spec/support/shared_examples/mentionable_shared_examples.rb +++ b/spec/support/shared_examples/mentionable_shared_examples.rb @@ -195,3 +195,153 @@ shared_examples 'an editable mentionable' do subject.create_new_cross_references!(author) end end + +shared_examples_for 'mentions in description' do |mentionable_type| + describe 'when store_mentioned_users_to_db feature disabled' do + before do + stub_feature_flags(store_mentioned_users_to_db: false) + mentionable.store_mentions! + end + + context 'when mentionable description contains mentions' do + let(:user) { create(:user) } + let(:mentionable) { create(mentionable_type, description: "#{user.to_reference} some description") } + + it 'stores no mentions' do + expect(mentionable.user_mentions.count).to eq 0 + end + end + end + + describe 'when store_mentioned_users_to_db feature enabled' do + before do + stub_feature_flags(store_mentioned_users_to_db: true) + mentionable.store_mentions! + end + + context 'when mentionable description has no mentions' do + let(:mentionable) { create(mentionable_type, description: "just some description") } + + it 'stores no mentions' do + expect(mentionable.user_mentions.count).to eq 0 + end + end + + context 'when mentionable description contains mentions' do + let(:user) { create(:user) } + let(:group) { create(:group) } + + let(:mentionable_desc) { "#{user.to_reference} some description #{group.to_reference(full: true)} and @all" } + let(:mentionable) { create(mentionable_type, description: mentionable_desc) } + + it 'stores mentions' do + add_member(user) + + expect(mentionable.user_mentions.count).to eq 1 + expect(mentionable.referenced_users).to match_array([user]) + expect(mentionable.referenced_projects(user)).to match_array([mentionable.project].compact) # epic.project is nil, and we want empty [] + expect(mentionable.referenced_groups(user)).to match_array([group]) + end + end + end +end + +shared_examples_for 'mentions in notes' do |mentionable_type| + context 'when mentionable notes contain mentions' do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:note_desc) { "#{user.to_reference} and #{group.to_reference(full: true)} and @all" } + let!(:mentionable) { note.noteable } + + before do + note.update(note: note_desc) + note.store_mentions! + add_member(user) + end + + it 'returns all mentionable mentions' do + expect(mentionable.user_mentions.count).to eq 1 + expect(mentionable.referenced_users).to eq [user] + expect(mentionable.referenced_projects(user)).to eq [mentionable.project].compact # epic.project is nil, and we want empty [] + expect(mentionable.referenced_groups(user)).to eq [group] + end + end +end + +shared_examples_for 'load mentions from DB' do |mentionable_type| + context 'load stored mentions' do + let_it_be(:user) { create(:user) } + let_it_be(:mentioned_user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:note_desc) { "#{mentioned_user.to_reference} and #{group.to_reference(full: true)} and @all" } + + before do + note.update(note: note_desc) + note.store_mentions! + add_member(user) + end + + context 'when stored user mention contains ids of inexistent records' do + before do + user_mention = note.send(:model_user_mention) + mention_ids = { + mentioned_users_ids: user_mention.mentioned_users_ids.to_a << User.maximum(:id).to_i.succ, + mentioned_projects_ids: user_mention.mentioned_projects_ids.to_a << Project.maximum(:id).to_i.succ, + mentioned_groups_ids: user_mention.mentioned_groups_ids.to_a << Group.maximum(:id).to_i.succ + } + user_mention.update(mention_ids) + end + + it 'filters out inexistent mentions' do + expect(mentionable.referenced_users).to match_array([mentioned_user]) + expect(mentionable.referenced_projects(user)).to match_array([mentionable.project].compact) # epic.project is nil, and we want empty [] + expect(mentionable.referenced_groups(user)).to match_array([group]) + end + end + + context 'when private projects and groups are mentioned' do + let(:mega_user) { create(:user) } + let(:private_project) { create(:project, :private) } + let(:project_member) { create(:project_member, user: create(:user), project: private_project) } + let(:private_group) { create(:group, :private) } + let(:group_member) { create(:group_member, user: create(:user), group: private_group) } + + before do + user_mention = note.send(:model_user_mention) + mention_ids = { + mentioned_projects_ids: user_mention.mentioned_projects_ids.to_a << private_project.id, + mentioned_groups_ids: user_mention.mentioned_groups_ids.to_a << private_group.id + } + user_mention.update(mention_ids) + + add_member(mega_user) + private_project.add_developer(mega_user) + private_group.add_developer(mega_user) + end + + context 'when user has no access to some mentions' do + it 'filters out inaccessible mentions' do + expect(mentionable.referenced_projects(user)).to match_array([mentionable.project].compact) # epic.project is nil, and we want empty [] + expect(mentionable.referenced_groups(user)).to match_array([group]) + end + end + + context 'when user has access to all mentions' do + it 'returns all mentions' do + expect(mentionable.referenced_projects(mega_user)).to match_array([mentionable.project, private_project].compact) # epic.project is nil, and we want empty [] + expect(mentionable.referenced_groups(mega_user)).to match_array([group, private_group]) + end + end + end + end +end + +def add_member(user) + issuable_parent = if mentionable.is_a?(Epic) + mentionable.group + else + mentionable.project + end + + issuable_parent&.add_developer(user) +end diff --git a/spec/support/shared_examples/merge_requests_rendering_a_single_diff_version.rb b/spec/support/shared_examples/merge_requests_rendering_a_single_diff_version.rb index 80120629a32..18d025a4b07 100644 --- a/spec/support/shared_examples/merge_requests_rendering_a_single_diff_version.rb +++ b/spec/support/shared_examples/merge_requests_rendering_a_single_diff_version.rb @@ -3,6 +3,10 @@ # This pending test can be removed when `single_mr_diff_view` is enabled by default # disabling the feature flag above is then not needed anymore. RSpec.shared_examples 'rendering a single diff version' do |attribute| + before do + stub_feature_flags(diffs_batch_load: false) + end + pending 'allows editing diff settings single_mr_diff_view is enabled' do project = create(:project, :repository) user = project.creator diff --git a/spec/support/shared_examples/models/chat_service_shared_examples.rb b/spec/support/shared_examples/models/chat_service_shared_examples.rb index 98bf647a9bc..7936a8eb974 100644 --- a/spec/support/shared_examples/models/chat_service_shared_examples.rb +++ b/spec/support/shared_examples/models/chat_service_shared_examples.rb @@ -80,7 +80,7 @@ shared_examples_for "chat service" do |service_name| it_behaves_like "triggered #{service_name} service" - it "specifies the webhook when it is configured" do + it "specifies the webhook when it is configured", if: defined?(client) do expect(client).to receive(:new).with(client_arguments).and_return(double(:chat_service).as_null_object) subject.execute(sample_data) diff --git a/spec/support/shared_examples/models/cluster_cleanup_worker_base_shared_examples.rb b/spec/support/shared_examples/models/cluster_cleanup_worker_base_shared_examples.rb new file mode 100644 index 00000000000..66bbd908ea8 --- /dev/null +++ b/spec/support/shared_examples/models/cluster_cleanup_worker_base_shared_examples.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +shared_examples 'cluster cleanup worker base specs' do + it 'transitions to errored if sidekiq retries exhausted' do + job = { 'args' => [cluster.id, 0], 'jid' => '123' } + + described_class.sidekiq_retries_exhausted_block.call(job) + + expect(cluster.reload.cleanup_status_name).to eq(:cleanup_errored) + end +end diff --git a/spec/support/shared_examples/models/user_mentions_shared_examples.rb b/spec/support/shared_examples/models/user_mentions_shared_examples.rb new file mode 100644 index 00000000000..b94994ea712 --- /dev/null +++ b/spec/support/shared_examples/models/user_mentions_shared_examples.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'spec_helper' + +shared_examples_for 'has user mentions' do + describe '#has_mentions?' do + context 'when no mentions' do + it 'returns false' do + expect(subject.mentioned_users_ids).to be nil + expect(subject.mentioned_projects_ids).to be nil + expect(subject.mentioned_groups_ids).to be nil + expect(subject.has_mentions?).to be false + end + end + + context 'when mentioned_users_ids not null' do + subject { described_class.new(mentioned_users_ids: [1, 2, 3]) } + + it 'returns true' do + expect(subject.has_mentions?).to be true + end + end + + context 'when mentioned projects' do + subject { described_class.new(mentioned_projects_ids: [1, 2, 3]) } + + it 'returns true' do + expect(subject.has_mentions?).to be true + end + end + + context 'when mentioned groups' do + subject { described_class.new(mentioned_groups_ids: [1, 2, 3]) } + + it 'returns true' do + expect(subject.has_mentions?).to be true + end + end + end +end diff --git a/spec/support/shared_examples/policies/project_policy_shared_examples.rb b/spec/support/shared_examples/policies/project_policy_shared_examples.rb index 13b7ade658b..1831fc10628 100644 --- a/spec/support/shared_examples/policies/project_policy_shared_examples.rb +++ b/spec/support/shared_examples/policies/project_policy_shared_examples.rb @@ -4,7 +4,7 @@ RSpec.shared_examples 'archived project policies' do let(:feature_write_abilities) do described_class::READONLY_FEATURES_WHEN_ARCHIVED.flat_map do |feature| described_class.create_update_admin_destroy(feature) - end + additional_reporter_permissions + additional_maintainer_permissions + end + additional_maintainer_permissions end let(:other_write_abilities) do @@ -18,6 +18,7 @@ RSpec.shared_examples 'archived project policies' do resolve_note award_emoji admin_tag + admin_issue_link ] end @@ -151,6 +152,7 @@ end RSpec.shared_examples 'project policies as developer' do context 'abilities for non-public projects' do let(:project) { create(:project, namespace: owner.namespace) } + subject { described_class.new(developer, project) } it do diff --git a/spec/support/shared_examples/quick_actions/issue/duplicate_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issue/duplicate_quick_action_shared_examples.rb deleted file mode 100644 index 3834b8b2b87..00000000000 --- a/spec/support/shared_examples/quick_actions/issue/duplicate_quick_action_shared_examples.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -shared_examples 'duplicate quick action' do - context 'mark issue as duplicate' do - let(:original_issue) { create(:issue, project: project) } - - context 'when the current user can update issues' do - it 'does not create a note, and marks the issue as a duplicate' do - add_note("/duplicate ##{original_issue.to_reference}") - - expect(page).not_to have_content "/duplicate #{original_issue.to_reference}" - expect(page).to have_content "marked this issue as a duplicate of #{original_issue.to_reference}" - - expect(issue.reload).to be_closed - end - end - - context 'when the current user cannot update the issue' do - let(:guest) { create(:user) } - before do - project.add_guest(guest) - gitlab_sign_out - sign_in(guest) - visit project_issue_path(project, issue) - end - - it 'does not create a note, and does not mark the issue as a duplicate' do - add_note("/duplicate ##{original_issue.to_reference}") - - expect(page).not_to have_content "marked this issue as a duplicate of #{original_issue.to_reference}" - - expect(issue.reload).to be_open - end - end - end -end diff --git a/spec/support/shared_examples/services/base_helm_service_shared_examples.rb b/spec/support/shared_examples/services/base_helm_service_shared_examples.rb index fa76b95f768..19f5334b4b2 100644 --- a/spec/support/shared_examples/services/base_helm_service_shared_examples.rb +++ b/spec/support/shared_examples/services/base_helm_service_shared_examples.rb @@ -11,20 +11,10 @@ shared_examples 'logs kubernetes errors' do } end - let(:logger_hash) do - error_hash.merge( - exception: error_name, - message: error_message, - backtrace: instance_of(Array) - ) - end - it 'logs into kubernetes.log and Sentry' do - expect(service.send(:logger)).to receive(:error).with(hash_including(logger_hash)) - - expect(Gitlab::Sentry).to receive(:track_acceptable_exception).with( + expect(Gitlab::ErrorTracking).to receive(:track_exception).with( error, - extra: hash_including(error_hash) + hash_including(error_hash) ) service.execute diff --git a/spec/support/sidekiq.rb b/spec/support/sidekiq.rb index 246efedc7e5..a6d6d5fc6e1 100644 --- a/spec/support/sidekiq.rb +++ b/spec/support/sidekiq.rb @@ -1,31 +1,5 @@ # frozen_string_literal: true -require 'sidekiq/testing' - -# If Sidekiq::Testing.inline! is used, SQL transactions done inside -# Sidekiq worker are included in the SQL query limit (in a real -# deployment sidekiq worker is executed separately). To avoid -# increasing SQL limit counter, the request is marked as whitelisted -# during Sidekiq block -class DisableQueryLimit - def call(worker_instance, msg, queue) - transaction = Gitlab::QueryLimiting::Transaction.current - - if !transaction.respond_to?(:whitelisted) || transaction.whitelisted - yield - else - transaction.whitelisted = true - yield - transaction.whitelisted = false - end - end -end - -Sidekiq::Testing.server_middleware do |chain| - chain.add Gitlab::SidekiqStatus::ServerMiddleware - chain.add DisableQueryLimit -end - RSpec.configure do |config| config.around(:each, :sidekiq) do |example| Sidekiq::Worker.clear_all diff --git a/spec/support/sidekiq_middleware.rb b/spec/support/sidekiq_middleware.rb new file mode 100644 index 00000000000..f6694713101 --- /dev/null +++ b/spec/support/sidekiq_middleware.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'sidekiq/testing' + +# If Sidekiq::Testing.inline! is used, SQL transactions done inside +# Sidekiq worker are included in the SQL query limit (in a real +# deployment sidekiq worker is executed separately). To avoid +# increasing SQL limit counter, the request is marked as whitelisted +# during Sidekiq block +class DisableQueryLimit + def call(worker_instance, msg, queue) + transaction = Gitlab::QueryLimiting::Transaction.current + + if !transaction.respond_to?(:whitelisted) || transaction.whitelisted + yield + else + transaction.whitelisted = true + yield + transaction.whitelisted = false + end + end +end + +Sidekiq::Testing.server_middleware do |chain| + chain.add Gitlab::SidekiqStatus::ServerMiddleware + chain.add DisableQueryLimit +end diff --git a/spec/support/webmock.rb b/spec/support/webmock.rb index 32b88edc2df..57acc3b63b1 100644 --- a/spec/support/webmock.rb +++ b/spec/support/webmock.rb @@ -8,7 +8,11 @@ def webmock_allowed_hosts if ENV.key?('ELASTIC_URL') hosts << URI.parse(ENV['ELASTIC_URL']).host end - end.uniq + + if Gitlab.config.webpack&.dev_server&.enabled + hosts << Gitlab.config.webpack.dev_server.host + end + end.compact.uniq end WebMock.disable_net_connect!(allow_localhost: true, allow: webmock_allowed_hosts) |