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>2023-02-20 16:49:51 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-02-20 16:49:51 +0300
commit71786ddc8e28fbd3cb3fcc4b3ff15e5962a1c82e (patch)
tree6a2d93ef3fb2d353bb7739e4b57e6541f51cdd71 /spec/support
parenta7253423e3403b8c08f8a161e5937e1488f5f407 (diff)
Add latest changes from gitlab-org/gitlab@15-9-stable-eev15.9.0-rc42
Diffstat (limited to 'spec/support')
-rw-r--r--spec/support/database/prevent_cross_database_modification.rb4
-rw-r--r--spec/support/database/prevent_cross_joins.rb4
-rw-r--r--spec/support/db_cleaner.rb2
-rw-r--r--spec/support/graphql/fake_tracer.rb4
-rw-r--r--spec/support/graphql/resolver_factories.rb4
-rw-r--r--spec/support/graphql/subscriptions/action_cable/mock_action_cable.rb100
-rw-r--r--spec/support/graphql/subscriptions/action_cable/mock_gitlab_schema.rb41
-rw-r--r--spec/support/graphql/subscriptions/notes/helper.rb94
-rw-r--r--spec/support/helpers/ci/job_token_scope_helpers.rb61
-rw-r--r--spec/support/helpers/ci/template_helpers.rb23
-rw-r--r--spec/support/helpers/cycle_analytics_helpers.rb2
-rw-r--r--spec/support/helpers/database/multiple_databases_helpers.rb32
-rw-r--r--spec/support/helpers/email_helpers.rb20
-rw-r--r--spec/support/helpers/features/branches_helpers.rb9
-rw-r--r--spec/support/helpers/features/releases_helpers.rb7
-rw-r--r--spec/support/helpers/features/sorting_helpers.rb4
-rw-r--r--spec/support/helpers/gitaly_setup.rb1
-rw-r--r--spec/support/helpers/javascript_fixtures_helpers.rb4
-rw-r--r--spec/support/helpers/kubernetes_helpers.rb4
-rw-r--r--spec/support/helpers/listbox_helpers.rb8
-rw-r--r--spec/support/helpers/reload_helpers.rb4
-rw-r--r--spec/support/helpers/select2_helper.rb57
-rw-r--r--spec/support/helpers/stub_env.rb18
-rw-r--r--spec/support/helpers/stubbed_member.rb6
-rw-r--r--spec/support/helpers/test_env.rb2
-rw-r--r--spec/support/helpers/trial_status_widget_test_helper.rb9
-rw-r--r--spec/support/helpers/usage_data_helpers.rb2
-rw-r--r--spec/support/import_export/common_util.rb14
-rw-r--r--spec/support/import_export/project_tree_expectations.rb2
-rw-r--r--spec/support/matchers/not_enqueue_mail_matcher.rb3
-rw-r--r--spec/support/matchers/schema_matcher.rb16
-rw-r--r--spec/support/models/ci/partitioning_testing/schema_helpers.rb19
-rw-r--r--spec/support/redis.rb6
-rw-r--r--spec/support/redis/redis_new_instance_shared_examples.rb50
-rw-r--r--spec/support/redis/redis_shared_examples.rb69
-rw-r--r--spec/support/rspec_order_todo.yml25
-rw-r--r--spec/support/services/issuable_update_service_shared_examples.rb4
-rw-r--r--spec/support/shared_contexts/graphql/types/query_type_shared_context.rb45
-rw-r--r--spec/support/shared_contexts/mailers/emails/service_desk_shared_context.rb40
-rw-r--r--spec/support/shared_contexts/models/ci/job_token_scope.rb34
-rw-r--r--spec/support/shared_contexts/navbar_structure_context.rb8
-rw-r--r--spec/support/shared_contexts/policies/group_policy_shared_context.rb3
-rw-r--r--spec/support/shared_contexts/policies/project_policy_shared_context.rb10
-rw-r--r--spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb2
-rw-r--r--spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb3
-rw-r--r--spec/support/shared_contexts/requests/api/graphql/releases_and_group_releases_shared_context.rb60
-rw-r--r--spec/support/shared_examples/bulk_imports/visibility_level_examples.rb124
-rw-r--r--spec/support/shared_examples/controllers/issuable_anonymous_search_disabled_examples.rb55
-rw-r--r--spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb11
-rw-r--r--spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb9
-rw-r--r--spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb11
-rw-r--r--spec/support/shared_examples/features/incident_details_routing_shared_examples.rb28
-rw-r--r--spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/features/reportable_note_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/features/runners_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/sidebar_shared_examples.rb20
-rw-r--r--spec/support/shared_examples/features/work_items_shared_examples.rb141
-rw-r--r--spec/support/shared_examples/finders/issues_finder_shared_examples.rb127
-rw-r--r--spec/support/shared_examples/finders/packages/debian/distributions_finder_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/graphql/mutations/work_items/update_description_widget_shared_examples.rb84
-rw-r--r--spec/support/shared_examples/graphql/projects/services_resolver_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/graphql/resolvers/issuable_resolvers_shared_examples.rb35
-rw-r--r--spec/support/shared_examples/graphql/resolvers/releases_resolvers_shared_examples.rb41
-rw-r--r--spec/support/shared_examples/graphql/subscriptions/notes/notes_subscription_shared_examples.rb58
-rw-r--r--spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb1
-rw-r--r--spec/support/shared_examples/integrations/integration_settings_form.rb17
-rw-r--r--spec/support/shared_examples/lib/cache_helpers_shared_examples.rb12
-rw-r--r--spec/support/shared_examples/mailers/notify_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/models/chat_integration_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/concerns/auto_disabling_hooks_shared_examples.rb272
-rw-r--r--spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb64
-rw-r--r--spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/models/concerns/unstoppable_hooks_shared_examples.rb177
-rw-r--r--spec/support/shared_examples/models/concerns/web_hooks/has_web_hooks_shared_examples.rb107
-rw-r--r--spec/support/shared_examples/models/exportable_shared_examples.rb73
-rw-r--r--spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/resource_event_shared_examples.rb20
-rw-r--r--spec/support/shared_examples/namespaces/traversal_examples.rb5
-rw-r--r--spec/support/shared_examples/policies/project_policy_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/quick_actions/issuable/max_issuable_examples.rb40
-rw-r--r--spec/support/shared_examples/requests/admin_mode_shared_examples.rb98
-rw-r--r--spec/support/shared_examples/requests/api/debian_common_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/requests/api/debian_distributions_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb20
-rw-r--r--spec/support/shared_examples/requests/api/graphql/ci/sorted_paginated_variables_shared_examples.rb26
-rw-r--r--spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb24
-rw-r--r--spec/support/shared_examples/requests/api/graphql/releases_and_group_releases_shared_examples.rb164
-rw-r--r--spec/support/shared_examples/requests/api/hooks_shared_examples.rb14
-rw-r--r--spec/support/shared_examples/requests/api/issuable_search_shared_examples.rb51
-rw-r--r--spec/support/shared_examples/requests/api/time_tracking_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/rack_attack_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/serializers/note_entity_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/export_csv/export_csv_invalid_fields_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/services/issuable/discussions_list_shared_examples.rb14
-rw-r--r--spec/support/shared_examples/services/issuable_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb15
-rw-r--r--spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb50
-rw-r--r--spec/support/shared_examples/services/packages_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb15
-rw-r--r--spec/support/shared_examples/services/updating_mentions_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/views/themed_layout_examples.rb2
-rw-r--r--spec/support/shared_examples/work_item_base_types_importer.rb60
-rw-r--r--spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb2
-rw-r--r--spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb14
-rw-r--r--spec/support/webmock.rb14
108 files changed, 2477 insertions, 683 deletions
diff --git a/spec/support/database/prevent_cross_database_modification.rb b/spec/support/database/prevent_cross_database_modification.rb
index 759e8316cc5..cd0cbe733d1 100644
--- a/spec/support/database/prevent_cross_database_modification.rb
+++ b/spec/support/database/prevent_cross_database_modification.rb
@@ -28,8 +28,6 @@ RSpec.configure do |config|
config.after do |example_file|
::Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification.suppress_in_rspec = true
- [::ApplicationRecord, ::Ci::ApplicationRecord].each do |base_class|
- base_class.gitlab_transactions_stack.clear if base_class.respond_to?(:gitlab_transactions_stack)
- end
+ ::ApplicationRecord.gitlab_transactions_stack.clear
end
end
diff --git a/spec/support/database/prevent_cross_joins.rb b/spec/support/database/prevent_cross_joins.rb
index 42c69a26788..8e08824c464 100644
--- a/spec/support/database/prevent_cross_joins.rb
+++ b/spec/support/database/prevent_cross_joins.rb
@@ -23,6 +23,7 @@ module Database
ALLOW_THREAD_KEY = :allow_cross_joins_across_databases
ALLOW_ANNOTATE_KEY = ALLOW_THREAD_KEY.to_s.freeze
+ IGNORED_SCHEMAS = %i[gitlab_shared gitlab_internal].freeze
def self.validate_cross_joins!(sql)
return if Thread.current[ALLOW_THREAD_KEY] || sql.include?(ALLOW_ANNOTATE_KEY)
@@ -40,8 +41,9 @@ module Database
end
schemas = ::Gitlab::Database::GitlabSchema.table_schemas(tables)
+ schemas.subtract(IGNORED_SCHEMAS)
- if schemas.include?(:gitlab_ci) && schemas.include?(:gitlab_main)
+ if schemas.many?
Thread.current[:has_cross_join_exception] = true
raise CrossJoinAcrossUnsupportedTablesError,
"Unsupported cross-join across '#{tables.join(", ")}' querying '#{schemas.to_a.join(", ")}' discovered " \
diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb
index 588fe466a42..b9a99eff413 100644
--- a/spec/support/db_cleaner.rb
+++ b/spec/support/db_cleaner.rb
@@ -12,7 +12,7 @@ module DbCleaner
end
def deletion_except_tables
- %w[work_item_types work_item_hierarchy_restrictions]
+ %w[work_item_types work_item_hierarchy_restrictions work_item_widget_definitions]
end
def setup_database_cleaner
diff --git a/spec/support/graphql/fake_tracer.rb b/spec/support/graphql/fake_tracer.rb
index c2fb7ed12d8..58688c9abd0 100644
--- a/spec/support/graphql/fake_tracer.rb
+++ b/spec/support/graphql/fake_tracer.rb
@@ -6,8 +6,8 @@ module Graphql
@trace_callback = trace_callback
end
- def trace(*args)
- @trace_callback.call(*args)
+ def trace(...)
+ @trace_callback.call(...)
yield
end
diff --git a/spec/support/graphql/resolver_factories.rb b/spec/support/graphql/resolver_factories.rb
index 3c5aad34e8b..76df4b58943 100644
--- a/spec/support/graphql/resolver_factories.rb
+++ b/spec/support/graphql/resolver_factories.rb
@@ -27,8 +27,8 @@ module Graphql
Class.new(Resolvers::BaseResolver) do
include ::Gitlab::Graphql::Authorize::AuthorizeResource
- def resolve(**args)
- authorized_find!(**args)
+ def resolve(...)
+ authorized_find!(...)
end
define_method :find_object do |**_args|
diff --git a/spec/support/graphql/subscriptions/action_cable/mock_action_cable.rb b/spec/support/graphql/subscriptions/action_cable/mock_action_cable.rb
new file mode 100644
index 00000000000..5467564a79e
--- /dev/null
+++ b/spec/support/graphql/subscriptions/action_cable/mock_action_cable.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+# A stub implementation of ActionCable.
+# Any methods to support the mock backend have `mock` in the name.
+module Graphql
+ module Subscriptions
+ module ActionCable
+ class MockActionCable
+ class MockChannel
+ def initialize
+ @mock_broadcasted_messages = []
+ end
+
+ attr_reader :mock_broadcasted_messages
+
+ def stream_from(stream_name, coder: nil, &block)
+ # Rails uses `coder`, we don't
+ block ||= ->(msg) { @mock_broadcasted_messages << msg }
+ MockActionCable.mock_stream_for(stream_name).add_mock_channel(self, block)
+ end
+ end
+
+ class MockStream
+ def initialize
+ @mock_channels = {}
+ end
+
+ def add_mock_channel(channel, handler)
+ @mock_channels[channel] = handler
+ end
+
+ def mock_broadcast(message)
+ @mock_channels.each do |channel, handler|
+ handler && handler.call(message)
+ end
+ end
+ end
+
+ class << self
+ def clear_mocks
+ @mock_streams = {}
+ end
+
+ def server
+ self
+ end
+
+ def broadcast(stream_name, message)
+ stream = @mock_streams[stream_name]
+ stream && stream.mock_broadcast(message)
+ end
+
+ def mock_stream_for(stream_name)
+ @mock_streams[stream_name] ||= MockStream.new
+ end
+
+ def get_mock_channel
+ MockChannel.new
+ end
+
+ def mock_stream_names
+ @mock_streams.keys
+ end
+ end
+ end
+
+ class MockSchema < GraphQL::Schema
+ class << self
+ def find_by_gid(gid)
+ return unless gid
+
+ if gid.model_class < ApplicationRecord
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(gid.model_class, gid.model_id).find
+ elsif gid.model_class.respond_to?(:lazy_find)
+ gid.model_class.lazy_find(gid.model_id)
+ else
+ gid.find
+ end
+ end
+
+ def id_from_object(object, _type = nil, _ctx = nil)
+ unless object.respond_to?(:to_global_id)
+ # This is an error in our schema and needs to be solved. So raise a
+ # more meaningful error message
+ raise "#{object} does not implement `to_global_id`. " \
+ "Include `GlobalID::Identification` into `#{object.class}"
+ end
+
+ object.to_global_id
+ end
+ end
+
+ query(::Types::QueryType)
+ subscription(::Types::SubscriptionType)
+
+ use GraphQL::Subscriptions::ActionCableSubscriptions, action_cable: MockActionCable, action_cable_coder: JSON
+ end
+ end
+ end
+end
diff --git a/spec/support/graphql/subscriptions/action_cable/mock_gitlab_schema.rb b/spec/support/graphql/subscriptions/action_cable/mock_gitlab_schema.rb
new file mode 100644
index 00000000000..cd5d78cc78b
--- /dev/null
+++ b/spec/support/graphql/subscriptions/action_cable/mock_gitlab_schema.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+# A stub implementation of ActionCable.
+# Any methods to support the mock backend have `mock` in the name.
+module Graphql
+ module Subscriptions
+ module ActionCable
+ class MockGitlabSchema < GraphQL::Schema
+ class << self
+ def find_by_gid(gid)
+ return unless gid
+
+ if gid.model_class < ApplicationRecord
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(gid.model_class, gid.model_id).find
+ elsif gid.model_class.respond_to?(:lazy_find)
+ gid.model_class.lazy_find(gid.model_id)
+ else
+ gid.find
+ end
+ end
+
+ def id_from_object(object, _type = nil, _ctx = nil)
+ unless object.respond_to?(:to_global_id)
+ # This is an error in our schema and needs to be solved. So raise a
+ # more meaningful error message
+ raise "#{object} does not implement `to_global_id`. " \
+ "Include `GlobalID::Identification` into `#{object.class}"
+ end
+
+ object.to_global_id
+ end
+ end
+
+ query(::Types::QueryType)
+ subscription(::Types::SubscriptionType)
+
+ use GraphQL::Subscriptions::ActionCableSubscriptions, action_cable: MockActionCable, action_cable_coder: JSON
+ end
+ end
+ end
+end
diff --git a/spec/support/graphql/subscriptions/notes/helper.rb b/spec/support/graphql/subscriptions/notes/helper.rb
new file mode 100644
index 00000000000..9a552f9879e
--- /dev/null
+++ b/spec/support/graphql/subscriptions/notes/helper.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+module Graphql
+ module Subscriptions
+ module Notes
+ module Helper
+ def subscription_response
+ subscription_channel = subscribe
+ yield
+ subscription_channel.mock_broadcasted_messages.first
+ end
+
+ def notes_subscription(name, noteable, current_user)
+ mock_channel = Graphql::Subscriptions::ActionCable::MockActionCable.get_mock_channel
+
+ query = case name
+ when 'workItemNoteDeleted'
+ note_deleted_subscription_query(name, noteable)
+ when 'workItemNoteUpdated'
+ note_updated_subscription_query(name, noteable)
+ when 'workItemNoteCreated'
+ note_created_subscription_query(name, noteable)
+ else
+ raise "Subscription query unknown: #{name}"
+ end
+
+ GitlabSchema.execute(query, context: { current_user: current_user, channel: mock_channel })
+
+ mock_channel
+ end
+
+ def note_subscription(name, noteable, current_user)
+ mock_channel = Graphql::Subscriptions::ActionCable::MockActionCable.get_mock_channel
+
+ query = <<~SUBSCRIPTION
+ subscription {
+ #{name}(noteableId: \"#{noteable.to_gid}\") {
+ id
+ body
+ }
+ }
+ SUBSCRIPTION
+
+ GitlabSchema.execute(query, context: { current_user: current_user, channel: mock_channel })
+
+ mock_channel
+ end
+
+ private
+
+ def note_deleted_subscription_query(name, noteable)
+ <<~SUBSCRIPTION
+ subscription {
+ #{name}(noteableId: \"#{noteable.to_gid}\") {
+ id
+ discussionId
+ lastDiscussionNote
+ }
+ }
+ SUBSCRIPTION
+ end
+
+ def note_created_subscription_query(name, noteable)
+ <<~SUBSCRIPTION
+ subscription {
+ #{name}(noteableId: \"#{noteable.to_gid}\") {
+ id
+ discussion {
+ id
+ notes {
+ nodes {
+ id
+ }
+ }
+ }
+ }
+ }
+ SUBSCRIPTION
+ end
+
+ def note_updated_subscription_query(name, noteable)
+ <<~SUBSCRIPTION
+ subscription {
+ #{name}(noteableId: \"#{noteable.to_gid}\") {
+ id
+ body
+ }
+ }
+ SUBSCRIPTION
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/helpers/ci/job_token_scope_helpers.rb b/spec/support/helpers/ci/job_token_scope_helpers.rb
new file mode 100644
index 00000000000..1d71356917e
--- /dev/null
+++ b/spec/support/helpers/ci/job_token_scope_helpers.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+module Ci
+ module JobTokenScopeHelpers
+ def create_project_in_allowlist(root_project, direction:, target_project: nil)
+ included_project = target_project || create(:project,
+ ci_outbound_job_token_scope_enabled: true,
+ ci_inbound_job_token_scope_enabled: true
+ )
+ create(
+ :ci_job_token_project_scope_link,
+ source_project: root_project,
+ target_project: included_project,
+ direction: direction
+ )
+
+ included_project
+ end
+
+ def create_project_in_both_allowlists(root_project)
+ create_project_in_allowlist(root_project, direction: :outbound).tap do |new_project|
+ create_project_in_allowlist(root_project, target_project: new_project, direction: :inbound)
+ end
+ end
+
+ def create_inbound_accessible_project(project)
+ create(:project).tap do |accessible_project|
+ add_inbound_accessible_linkage(project, accessible_project)
+ end
+ end
+
+ def create_inbound_and_outbound_accessible_project(project)
+ create(:project).tap do |accessible_project|
+ make_project_fully_accessible(project, accessible_project)
+ end
+ end
+
+ def make_project_fully_accessible(project, accessible_project)
+ add_outbound_accessible_linkage(project, accessible_project)
+ add_inbound_accessible_linkage(project, accessible_project)
+ end
+
+ def add_outbound_accessible_linkage(project, accessible_project)
+ create(
+ :ci_job_token_project_scope_link,
+ source_project: project,
+ target_project: accessible_project,
+ direction: :outbound
+ )
+ end
+
+ def add_inbound_accessible_linkage(project, accessible_project)
+ create(
+ :ci_job_token_project_scope_link,
+ source_project: accessible_project,
+ target_project: project,
+ direction: :inbound
+ )
+ end
+ end
+end
diff --git a/spec/support/helpers/ci/template_helpers.rb b/spec/support/helpers/ci/template_helpers.rb
index 2cdd242ac22..cd3ab4bd82d 100644
--- a/spec/support/helpers/ci/template_helpers.rb
+++ b/spec/support/helpers/ci/template_helpers.rb
@@ -13,14 +13,21 @@ module Ci
def public_image_manifest(registry, repository, reference)
token = public_image_repository_token(registry, repository)
+ headers = {
+ 'Authorization' => "Bearer #{token}",
+ 'Accept' => 'application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.index.v1+json'
+ }
response = with_net_connect_allowed do
- Gitlab::HTTP.get(image_manifest_url(registry, repository, reference),
- headers: { 'Authorization' => "Bearer #{token}" })
+ Gitlab::HTTP.get(image_manifest_url(registry, repository, reference), headers: headers)
end
- return unless response.success?
-
- Gitlab::Json.parse(response.body)
+ if response.success?
+ Gitlab::Json.parse(response.body)
+ elsif response.not_found?
+ nil
+ else
+ raise "Could not retrieve manifest: #{response.body}"
+ end
end
def public_image_repository_token(registry, repository)
@@ -31,17 +38,17 @@ module Ci
Gitlab::HTTP.get(image_manifest_url(registry, repository, 'latest'))
end
- return unless response.unauthorized?
+ raise 'Unauthorized' unless response.unauthorized?
www_authenticate = response.headers['www-authenticate']
- return unless www_authenticate
+ raise 'Missing www-authenticate' unless www_authenticate
realm, service, scope = www_authenticate.split(',').map { |s| s[/\w+="(.*)"/, 1] }
token_response = with_net_connect_allowed do
Gitlab::HTTP.get(realm, query: { service: service, scope: scope })
end
- return unless token_response.success?
+ raise "Could not get token: #{token_response.body}" unless token_response.success?
token_response['token']
end
diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb
index 632f3ea28ee..eba5771e062 100644
--- a/spec/support/helpers/cycle_analytics_helpers.rb
+++ b/spec/support/helpers/cycle_analytics_helpers.rb
@@ -185,7 +185,7 @@ module CycleAnalyticsHelpers
def merge_merge_requests_closing_issue(user, project, issue)
merge_requests = Issues::ReferencedMergeRequestsService
- .new(project: project, current_user: user)
+ .new(container: project, current_user: user)
.closed_by_merge_requests(issue)
merge_requests.each { |merge_request| MergeRequests::MergeService.new(project: project, current_user: user, params: { sha: merge_request.diff_head_sha }).execute(merge_request) }
diff --git a/spec/support/helpers/database/multiple_databases_helpers.rb b/spec/support/helpers/database/multiple_databases_helpers.rb
index 16f5168ca29..5083ea1ff53 100644
--- a/spec/support/helpers/database/multiple_databases_helpers.rb
+++ b/spec/support/helpers/database/multiple_databases_helpers.rb
@@ -2,12 +2,28 @@
module Database
module MultipleDatabasesHelpers
- def skip_if_multiple_databases_not_setup
- skip 'Skipping because multiple databases not set up' unless Gitlab::Database.has_config?(:ci)
+ EXTRA_DBS = ::Gitlab::Database::DATABASE_NAMES.map(&:to_sym) - [:main]
+
+ def skip_if_multiple_databases_not_setup(*databases)
+ unless (databases - EXTRA_DBS).empty?
+ raise "Unsupported database in #{databases}. It must be one of #{EXTRA_DBS}."
+ end
+
+ databases = EXTRA_DBS if databases.empty?
+ return if databases.any? { |db| Gitlab::Database.has_config?(db) }
+
+ skip "Skipping because none of the extra databases #{databases} are setup"
end
- def skip_if_multiple_databases_are_setup
- skip 'Skipping because multiple databases are set up' if Gitlab::Database.has_config?(:ci)
+ def skip_if_multiple_databases_are_setup(*databases)
+ unless (databases - EXTRA_DBS).empty?
+ raise "Unsupported database in #{databases}. It must be one of #{EXTRA_DBS}."
+ end
+
+ databases = EXTRA_DBS if databases.empty?
+ return if databases.none? { |db| Gitlab::Database.has_config?(db) }
+
+ skip "Skipping because some of the extra databases #{databases} are setup"
end
def reconfigure_db_connection(name: nil, config_hash: {}, model: ActiveRecord::Base, config_model: nil)
@@ -71,6 +87,14 @@ module Database
end
# rubocop:enable Database/MultipleDatabases
+ def with_db_configs(test: test_config)
+ current_configurations = ActiveRecord::Base.configurations # rubocop:disable Database/MultipleDatabases
+ ActiveRecord::Base.configurations = { test: test_config }
+ yield
+ ensure
+ ActiveRecord::Base.configurations = current_configurations
+ end
+
def with_added_ci_connection
if Gitlab::Database.has_config?(:ci)
# No need to add a ci: connection if we already have one
diff --git a/spec/support/helpers/email_helpers.rb b/spec/support/helpers/email_helpers.rb
index d0f6fd466d0..f4bdaa7e425 100644
--- a/spec/support/helpers/email_helpers.rb
+++ b/spec/support/helpers/email_helpers.rb
@@ -56,4 +56,24 @@ module EmailHelpers
have_subject [prefix, suffix].compact.join
end
+
+ def enqueue_mail_with(mailer_class, mail_method_name, *args)
+ args.map! { |arg| arg.is_a?(ActiveRecord::Base) ? arg.id : arg }
+ have_enqueued_mail(mailer_class, mail_method_name).with(*args)
+ end
+
+ def not_enqueue_mail_with(mailer_class, mail_method_name, *args)
+ args.map! { |arg| arg.is_a?(ActiveRecord::Base) ? arg.id : arg }
+ not_enqueue_mail(mailer_class, mail_method_name).with(*args)
+ end
+
+ def have_only_enqueued_mail_with_args(mailer_class, mailer_method, *args)
+ raise ArgumentError, 'You must provide at least one array of mailer arguments' if args.empty?
+
+ count_expectation = have_enqueued_mail(mailer_class, mailer_method).exactly(args.size).times
+
+ args.inject(count_expectation) do |composed_expectation, arguments|
+ composed_expectation.and(have_enqueued_mail(mailer_class, mailer_method).with(*arguments))
+ end
+ end
end
diff --git a/spec/support/helpers/features/branches_helpers.rb b/spec/support/helpers/features/branches_helpers.rb
index d4f96718cc0..dc4fa448167 100644
--- a/spec/support/helpers/features/branches_helpers.rb
+++ b/spec/support/helpers/features/branches_helpers.rb
@@ -22,15 +22,10 @@ module Spec
end
def select_branch(branch_name)
- ref_selector = '.ref-selector'
- find(ref_selector).click
wait_for_requests
- page.within(ref_selector) do
- fill_in _('Search by Git revision'), with: branch_name
- wait_for_requests
- find('li', text: branch_name, match: :prefer_exact).click
- end
+ click_button branch_name
+ send_keys branch_name
end
end
end
diff --git a/spec/support/helpers/features/releases_helpers.rb b/spec/support/helpers/features/releases_helpers.rb
index a24b99bbe61..545e12341ef 100644
--- a/spec/support/helpers/features/releases_helpers.rb
+++ b/spec/support/helpers/features/releases_helpers.rb
@@ -15,17 +15,18 @@ module Spec
module Helpers
module Features
module ReleasesHelpers
+ include ListboxHelpers
+
def select_new_tag_name(tag_name)
page.within '[data-testid="tag-name-field"]' do
find('button').click
-
wait_for_all_requests
find('input[aria-label="Search or create tag"]').set(tag_name)
-
wait_for_all_requests
click_button("Create tag #{tag_name}")
+ click_button tag_name
end
end
@@ -39,7 +40,7 @@ module Spec
wait_for_all_requests
- click_button(branch_name.to_s)
+ select_listbox_item(branch_name.to_s, exact_text: true)
end
end
diff --git a/spec/support/helpers/features/sorting_helpers.rb b/spec/support/helpers/features/sorting_helpers.rb
index 50b8083ebb3..504a9b764cf 100644
--- a/spec/support/helpers/features/sorting_helpers.rb
+++ b/spec/support/helpers/features/sorting_helpers.rb
@@ -26,8 +26,8 @@ module Spec
# all of the dropdowns are converted, pajamas_sort_by can be renamed to sort_by
# https://gitlab.com/groups/gitlab-org/-/epics/7551
def pajamas_sort_by(value)
- find('.filter-dropdown-container .dropdown').click
- find('.dropdown-item', text: value).click
+ find('.filter-dropdown-container .gl-new-dropdown').click
+ find('.gl-new-dropdown-item', text: value).click
end
end
end
diff --git a/spec/support/helpers/gitaly_setup.rb b/spec/support/helpers/gitaly_setup.rb
index 20c104cd85c..398a2a20f2f 100644
--- a/spec/support/helpers/gitaly_setup.rb
+++ b/spec/support/helpers/gitaly_setup.rb
@@ -65,6 +65,7 @@ module GitalySetup
def env
{
'GEM_PATH' => Gem.path.join(':'),
+ 'BUNDLER_SETUP' => nil,
'BUNDLE_INSTALL_FLAGS' => nil,
'BUNDLE_IGNORE_CONFIG' => '1',
'BUNDLE_PATH' => bundle_path,
diff --git a/spec/support/helpers/javascript_fixtures_helpers.rb b/spec/support/helpers/javascript_fixtures_helpers.rb
index 40eb46878ad..403456fa48e 100644
--- a/spec/support/helpers/javascript_fixtures_helpers.rb
+++ b/spec/support/helpers/javascript_fixtures_helpers.rb
@@ -24,7 +24,9 @@ module JavaScriptFixturesHelpers
# pick an arbitrary date from the past, so tests are not time dependent
# Also see spec/frontend/__helpers__/fake_date/jest.js
- travel_to(Time.utc(2015, 7, 3, 10)) { example.run }
+ travel_to Time.utc(2015, 7, 3, 10)
+ example.run
+ travel_back
raise NoMethodError.new('You need to set `response` for the fixture generator! This will automatically happen with `type: :controller` or `type: :request`.', 'response') unless respond_to?(:response)
diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb
index 72524453f34..c3076a2c359 100644
--- a/spec/support/helpers/kubernetes_helpers.rb
+++ b/spec/support/helpers/kubernetes_helpers.rb
@@ -534,10 +534,10 @@ module KubernetesHelpers
}
end
- def kube_knative_services_body(**options)
+ def kube_knative_services_body(...)
{
"kind" => "List",
- "items" => [knative_09_service(**options)]
+ "items" => [knative_09_service(...)]
}
end
diff --git a/spec/support/helpers/listbox_helpers.rb b/spec/support/helpers/listbox_helpers.rb
index 5fcd05f31fb..e943790fc65 100644
--- a/spec/support/helpers/listbox_helpers.rb
+++ b/spec/support/helpers/listbox_helpers.rb
@@ -7,18 +7,18 @@ module ListboxHelpers
end
def select_listbox_item(text, exact_text: false)
- find('.gl-listbox-item[role="option"]', text: text, exact_text: exact_text).click
+ find('.gl-new-dropdown-item[role="option"]', text: text, exact_text: exact_text).click
end
def expect_listbox_item(text)
- expect(page).to have_css('.gl-listbox-item[role="option"]', text: text)
+ expect(page).to have_css('.gl-new-dropdown-item[role="option"]', text: text)
end
def expect_no_listbox_item(text)
- expect(page).not_to have_css('.gl-listbox-item[role="option"]', text: text)
+ expect(page).not_to have_css('.gl-new-dropdown-item[role="option"]', text: text)
end
def expect_listbox_items(items)
- expect(find_all('.gl-listbox-item[role="option"]').map(&:text)).to eq(items)
+ expect(find_all('.gl-new-dropdown-item[role="option"]').map(&:text)).to eq(items)
end
end
diff --git a/spec/support/helpers/reload_helpers.rb b/spec/support/helpers/reload_helpers.rb
index 368ebaaba8a..71becd535b0 100644
--- a/spec/support/helpers/reload_helpers.rb
+++ b/spec/support/helpers/reload_helpers.rb
@@ -5,8 +5,8 @@ module ReloadHelpers
models.compact.map(&:reload)
end
- def subject_and_reload(*models)
+ def subject_and_reload(...)
subject
- reload_models(*models)
+ reload_models(...)
end
end
diff --git a/spec/support/helpers/select2_helper.rb b/spec/support/helpers/select2_helper.rb
deleted file mode 100644
index 38bf34bdd61..00000000000
--- a/spec/support/helpers/select2_helper.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'wait_for_requests'
-
-# Select2 ajax programmatic helper
-# It allows you to select value from select2
-#
-# Params
-# value - real value of selected item
-# opts - options containing css selector
-#
-# Usage:
-#
-# select2(2, from: '#user_ids')
-#
-
-module Select2Helper
- include WaitForRequests
-
- def select2(value, options = {})
- raise ArgumentError, 'options must be a Hash' unless options.is_a?(Hash)
-
- wait_for_requests unless options[:async]
-
- selector = options.fetch(:from)
-
- ensure_select2_loaded(selector)
-
- if options[:multiple]
- execute_script("$('#{selector}').select2('val', ['#{value}']).trigger('change');")
- else
- execute_script("$('#{selector}').select2('val', '#{value}').trigger('change');")
- end
- end
-
- def open_select2(selector)
- ensure_select2_loaded(selector)
-
- execute_script("$('#{selector}').select2('open');")
- end
-
- def close_select2(selector)
- ensure_select2_loaded(selector)
-
- execute_script("$('#{selector}').select2('close');")
- end
-
- def scroll_select2_to_bottom(selector)
- evaluate_script "$('#{selector}').scrollTop($('#{selector}')[0].scrollHeight); $('#{selector}');"
- end
-
- private
-
- def ensure_select2_loaded(selector)
- first(selector, visible: :all).sibling('.select2-container')
- end
-end
diff --git a/spec/support/helpers/stub_env.rb b/spec/support/helpers/stub_env.rb
index 5f344f8fb52..afa501d6279 100644
--- a/spec/support/helpers/stub_env.rb
+++ b/spec/support/helpers/stub_env.rb
@@ -2,13 +2,23 @@
# Inspired by https://github.com/ljkbennett/stub_env/blob/master/lib/stub_env/helpers.rb
module StubENV
+ # Stub ENV variables
+ #
+ # You can provide either a key and value as separate params or both in a Hash format
+ #
+ # Keys and values will always be converted to String, to comply with how ENV behaves
+ #
+ # @param key_or_hash [String, Hash<String,String>]
+ # @param value [String]
def stub_env(key_or_hash, value = nil)
init_stub unless env_stubbed?
if key_or_hash.is_a? Hash
- key_or_hash.each { |k, v| add_stubbed_value(k, v) }
+ key_or_hash.each do |key, value|
+ add_stubbed_value(key, ensure_env_type(value))
+ end
else
- add_stubbed_value key_or_hash, value
+ add_stubbed_value key_or_hash, ensure_env_type(value)
end
end
@@ -35,4 +45,8 @@ module StubENV
allow(ENV).to receive(:fetch).and_call_original
add_stubbed_value(STUBBED_KEY, true)
end
+
+ def ensure_env_type(value)
+ value.nil? ? value : value.to_s
+ end
end
diff --git a/spec/support/helpers/stubbed_member.rb b/spec/support/helpers/stubbed_member.rb
index 27420c9b709..d61cdea5354 100644
--- a/spec/support/helpers/stubbed_member.rb
+++ b/spec/support/helpers/stubbed_member.rb
@@ -11,9 +11,7 @@ module StubbedMember
module Member
private
- def refresh_member_authorized_projects(blocking:)
- return super unless blocking
-
+ def refresh_member_authorized_projects
AuthorizedProjectsWorker.new.perform(user_id)
end
end
@@ -21,7 +19,7 @@ module StubbedMember
module ProjectMember
private
- def blocking_project_authorizations_refresh
+ def execute_project_authorizations_refresh
AuthorizedProjectUpdate::ProjectRecalculatePerUserWorker.new.perform(project.id, user.id)
end
end
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index 3530d1b1a39..3403064bf0b 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -423,7 +423,7 @@ module TestEnv
return if File.exist?(install_dir) && ci?
if component_needs_update?(install_dir, version)
- puts "==> Starting #{component} set up...\n"
+ puts "==> Starting #{component} (#{version}) set up...\n"
# Cleanup the component entirely to ensure we start fresh
FileUtils.rm_rf(install_dir) if fresh_install
diff --git a/spec/support/helpers/trial_status_widget_test_helper.rb b/spec/support/helpers/trial_status_widget_test_helper.rb
deleted file mode 100644
index d75620d17ee..00000000000
--- a/spec/support/helpers/trial_status_widget_test_helper.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-module TrialStatusWidgetTestHelper
- def purchase_href(group)
- new_subscriptions_path(namespace_id: group.id, plan_id: 'ultimate-plan-id')
- end
-end
-
-TrialStatusWidgetTestHelper.prepend_mod
diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb
index 438f0d129b9..2bec945fbc8 100644
--- a/spec/support/helpers/usage_data_helpers.rb
+++ b/spec/support/helpers/usage_data_helpers.rb
@@ -4,7 +4,6 @@ module UsageDataHelpers
COUNTS_KEYS = %i(
assignee_lists
ci_builds
- ci_internal_pipelines
ci_external_pipelines
ci_pipeline_config_auto_devops
ci_pipeline_config_repository
@@ -110,7 +109,6 @@ module UsageDataHelpers
gitaly
database
prometheus_metrics_enabled
- web_ide_clientside_preview_enabled
object_store
topology
).freeze
diff --git a/spec/support/import_export/common_util.rb b/spec/support/import_export/common_util.rb
index 3d7a0d29e71..f8f32fa59d1 100644
--- a/spec/support/import_export/common_util.rb
+++ b/spec/support/import_export/common_util.rb
@@ -51,22 +51,22 @@ module ImportExport
json
end
- def restore_then_save_project(project, import_path:, export_path:)
- project_restorer = get_project_restorer(project, import_path)
- project_saver = get_project_saver(project, export_path)
+ def restore_then_save_project(project, user, import_path:, export_path:)
+ project_restorer = get_project_restorer(project, user, import_path)
+ project_saver = get_project_saver(project, user, export_path)
project_restorer.restore && project_saver.save
end
- def get_project_restorer(project, import_path)
+ def get_project_restorer(project, user, import_path)
Gitlab::ImportExport::Project::TreeRestorer.new(
- user: project.creator, shared: get_shared_env(path: import_path), project: project
+ user: user, shared: get_shared_env(path: import_path), project: project
)
end
- def get_project_saver(project, export_path)
+ def get_project_saver(project, user, export_path)
Gitlab::ImportExport::Project::TreeSaver.new(
- project: project, current_user: project.creator, shared: get_shared_env(path: export_path)
+ project: project, current_user: user, shared: get_shared_env(path: export_path)
)
end
diff --git a/spec/support/import_export/project_tree_expectations.rb b/spec/support/import_export/project_tree_expectations.rb
index 2423a58a3e6..0049d0fbd06 100644
--- a/spec/support/import_export/project_tree_expectations.rb
+++ b/spec/support/import_export/project_tree_expectations.rb
@@ -59,7 +59,7 @@ module ImportExport
end
def match_arrays(left_node, right_node, stats, location_stack, failures)
- has_simple_elements = left_node.none? { |el| Enumerable === el }
+ has_simple_elements = left_node.none?(Enumerable)
# for simple types, we can do a direct order-less set comparison
if has_simple_elements && left_node.to_set != right_node.to_set
stats[:arrays][:direct] += 1
diff --git a/spec/support/matchers/not_enqueue_mail_matcher.rb b/spec/support/matchers/not_enqueue_mail_matcher.rb
new file mode 100644
index 00000000000..0975c038252
--- /dev/null
+++ b/spec/support/matchers/not_enqueue_mail_matcher.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+RSpec::Matchers.define_negated_matcher :not_enqueue_mail, :have_enqueued_mail
diff --git a/spec/support/matchers/schema_matcher.rb b/spec/support/matchers/schema_matcher.rb
index d2f32b60464..d5a07f200dd 100644
--- a/spec/support/matchers/schema_matcher.rb
+++ b/spec/support/matchers/schema_matcher.rb
@@ -16,20 +16,8 @@ module SchemaPath
end
def self.validator(schema_path)
- unless @schema_cache.key?(schema_path)
- @schema_cache[schema_path] = JSONSchemer.schema(schema_path, ref_resolver: SchemaPath.file_ref_resolver)
- end
-
- @schema_cache[schema_path]
- end
-
- def self.file_ref_resolver
- proc do |uri|
- file = Rails.root.join(uri.path)
- raise StandardError, "Ref file #{uri.path} must be json" unless uri.path.ends_with?('.json')
- raise StandardError, "File #{file.to_path} doesn't exists" unless file.exist?
-
- Gitlab::Json.parse(File.read(file))
+ @schema_cache.fetch(schema_path) do
+ @schema_cache[schema_path] = JSONSchemer.schema(schema_path)
end
end
end
diff --git a/spec/support/models/ci/partitioning_testing/schema_helpers.rb b/spec/support/models/ci/partitioning_testing/schema_helpers.rb
index 3a79ed1b5a9..4107bbcb976 100644
--- a/spec/support/models/ci/partitioning_testing/schema_helpers.rb
+++ b/spec/support/models/ci/partitioning_testing/schema_helpers.rb
@@ -24,13 +24,13 @@ module Ci
each_partitionable_table do |table_name|
change_column_default(table_name, from: DEFAULT_PARTITION, to: nil, connection: connection)
change_column_default("p_#{table_name}", from: DEFAULT_PARTITION, to: nil, connection: connection)
- create_test_partition(table_name, connection: connection)
+ create_test_partition("p_#{table_name}", connection: connection)
end
end
def teardown(connection: Ci::ApplicationRecord.connection)
each_partitionable_table do |table_name|
- drop_test_partition(table_name, connection: connection)
+ drop_test_partition("p_#{table_name}", connection: connection)
change_column_default(table_name, from: nil, to: DEFAULT_PARTITION, connection: connection)
change_column_default("p_#{table_name}", from: nil, to: DEFAULT_PARTITION, connection: connection)
end
@@ -54,13 +54,13 @@ module Ci
end
def create_test_partition(table_name, connection:)
- return unless table_available?("p_#{table_name}", connection: connection)
+ return unless table_available?(table_name, connection: connection)
drop_test_partition(table_name, connection: connection)
- connection.execute(<<~SQL)
+ connection.execute(<<~SQL.squish)
CREATE TABLE #{full_partition_name(table_name)}
- PARTITION OF p_#{table_name}
+ PARTITION OF #{table_name}
FOR VALUES IN (#{PartitioningTesting::PartitionIdentifiers.ci_testing_partition_id});
SQL
end
@@ -68,7 +68,7 @@ module Ci
def drop_test_partition(table_name, connection:)
return unless table_available?(table_name, connection: connection)
- connection.execute(<<~SQL)
+ connection.execute(<<~SQL.squish)
DROP TABLE IF EXISTS #{full_partition_name(table_name)};
SQL
end
@@ -79,7 +79,12 @@ module Ci
end
def full_partition_name(table_name)
- "#{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}._test_gitlab_#{table_name}_partition"
+ [
+ Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA,
+ '._test_gitlab_',
+ table_name.delete_prefix('p_'),
+ '_partition'
+ ].join('')
end
end
end
diff --git a/spec/support/redis.rb b/spec/support/redis.rb
index 6d313c8aa16..d5ae0bf1582 100644
--- a/spec/support/redis.rb
+++ b/spec/support/redis.rb
@@ -25,4 +25,10 @@ RSpec.configure do |config|
instance_class.with(&:flushdb)
end
end
+
+ config.before(:each, :use_null_store_as_repository_cache) do |example|
+ null_store = ActiveSupport::Cache::NullStore.new
+
+ allow(Gitlab::Redis::RepositoryCache).to receive(:cache_store).and_return(null_store)
+ end
end
diff --git a/spec/support/redis/redis_new_instance_shared_examples.rb b/spec/support/redis/redis_new_instance_shared_examples.rb
index 0f2de78b2cb..435d342fcca 100644
--- a/spec/support/redis/redis_new_instance_shared_examples.rb
+++ b/spec/support/redis/redis_new_instance_shared_examples.rb
@@ -27,38 +27,34 @@ RSpec.shared_examples "redis_new_instance_shared_examples" do |name, fallback_cl
FileUtils.mkdir_p(File.join(rails_root, 'config'))
end
- context 'when there is only a resque.yml' do
+ context 'and there is a global env override' do
before do
- FileUtils.touch(File.join(rails_root, 'config/resque.yml'))
+ stub_env('GITLAB_REDIS_CONFIG_FILE', 'global override')
end
- it { expect(subject).to eq("#{rails_root}/config/resque.yml") }
+ it { expect(subject).to eq('global override') }
- context 'and there is a global env override' do
- before do
- stub_env('GITLAB_REDIS_CONFIG_FILE', 'global override')
- end
-
- it { expect(subject).to eq('global override') }
+ context "and #{fallback_class.name.demodulize} has a different config file" do
+ let(:fallback_config_file) { 'fallback config file' }
- context "and #{fallback_class.name.demodulize} has a different config file" do
- let(:fallback_config_file) { 'fallback config file' }
-
- it { expect(subject).to eq('fallback config file') }
- end
+ it { expect(subject).to eq('fallback config file') }
end
end
end
describe '#fetch_config' do
- context 'when redis.yml exists' do
- subject { described_class.new('test').send(:fetch_config) }
+ subject { described_class.new('test').send(:fetch_config) }
+
+ before do
+ FileUtils.mkdir_p(File.join(rails_root, 'config'))
+
+ allow(described_class).to receive(:rails_root).and_return(rails_root)
+ end
+ context 'when redis.yml exists' do
before do
allow(described_class).to receive(:config_file_name).and_call_original
allow(described_class).to receive(:redis_yml_path).and_call_original
- allow(described_class).to receive(:rails_root).and_return(rails_root)
- FileUtils.mkdir_p(File.join(rails_root, 'config'))
end
context 'when the fallback has a redis.yml entry' do
@@ -93,5 +89,23 @@ RSpec.shared_examples "redis_new_instance_shared_examples" do |name, fallback_cl
end
end
end
+
+ context 'when no redis config file exsits' do
+ it 'returns nil' do
+ expect(subject).to eq(nil)
+ end
+
+ context 'when resque.yml exists' do
+ before do
+ File.write(File.join(rails_root, 'config/resque.yml'), {
+ 'test' => { 'foobar' => 123 }
+ }.to_json)
+ end
+
+ it 'returns the config from resque.yml' do
+ expect(subject).to eq({ 'foobar' => 123 })
+ end
+ end
+ end
end
end
diff --git a/spec/support/redis/redis_shared_examples.rb b/spec/support/redis/redis_shared_examples.rb
index 43c118a362d..8c195a9dbeb 100644
--- a/spec/support/redis/redis_shared_examples.rb
+++ b/spec/support/redis/redis_shared_examples.rb
@@ -40,42 +40,30 @@ RSpec.shared_examples "redis_shared_examples" do
context 'when there is no config file anywhere' do
it { expect(subject).to be_nil }
- context 'but resque.yml exists' do
+ context 'and there is a global env override' do
before do
- FileUtils.touch(File.join(rails_root, 'config', 'resque.yml'))
+ stub_env('GITLAB_REDIS_CONFIG_FILE', 'global override')
end
- it { expect(subject).to eq("#{rails_root}/config/resque.yml") }
-
- it 'returns a path that exists' do
- expect(File.file?(subject)).to eq(true)
- end
+ it { expect(subject).to eq('global override') }
- context 'and there is a global env override' do
+ context 'and there is an instance specific config file' do
before do
- stub_env('GITLAB_REDIS_CONFIG_FILE', 'global override')
+ FileUtils.touch(File.join(rails_root, instance_specific_config_file))
end
- it { expect(subject).to eq('global override') }
-
- context 'and there is an instance specific config file' do
- before do
- FileUtils.touch(File.join(rails_root, instance_specific_config_file))
- end
+ it { expect(subject).to eq("#{rails_root}/#{instance_specific_config_file}") }
- it { expect(subject).to eq("#{rails_root}/#{instance_specific_config_file}") }
+ it 'returns a path that exists' do
+ expect(File.file?(subject)).to eq(true)
+ end
- it 'returns a path that exists' do
- expect(File.file?(subject)).to eq(true)
+ context 'and there is a specific env override' do
+ before do
+ stub_env(environment_config_file_name, 'instance specific override')
end
- context 'and there is a specific env override' do
- before do
- stub_env(environment_config_file_name, 'instance specific override')
- end
-
- it { expect(subject).to eq('instance specific override') }
- end
+ it { expect(subject).to eq('instance specific override') }
end
end
end
@@ -402,6 +390,13 @@ RSpec.shared_examples "redis_shared_examples" do
end
describe '#fetch_config' do
+ before do
+ FileUtils.mkdir_p(File.join(rails_root, 'config'))
+ # Undo top-level stub of config_file_name because we are testing that method now.
+ allow(described_class).to receive(:config_file_name).and_call_original
+ allow(described_class).to receive(:rails_root).and_return(rails_root)
+ end
+
it 'raises an exception when the config file contains invalid yaml' do
Tempfile.open('bad.yml') do |file|
file.write('{"not":"yaml"')
@@ -422,10 +417,7 @@ RSpec.shared_examples "redis_shared_examples" do
subject { described_class.new('test').send(:fetch_config) }
before do
- allow(described_class).to receive(:config_file_name).and_call_original
allow(described_class).to receive(:redis_yml_path).and_call_original
- allow(described_class).to receive(:rails_root).and_return(rails_root)
- FileUtils.mkdir_p(File.join(rails_root, 'config'))
end
it 'uses config/redis.yml' do
@@ -436,6 +428,27 @@ RSpec.shared_examples "redis_shared_examples" do
expect(subject).to eq({ 'foobar' => 123 })
end
end
+
+ context 'when no config file exsits' do
+ subject { described_class.new('test').send(:fetch_config) }
+
+ it 'returns nil' do
+ expect(subject).to eq(nil)
+ end
+
+ context 'but resque.yml exists' do
+ before do
+ FileUtils.mkdir_p(File.join(rails_root, 'config'))
+ File.write(File.join(rails_root, 'config/resque.yml'), {
+ 'test' => { 'foobar' => 123 }
+ }.to_json)
+ end
+
+ it 'returns the config from resque.yml' do
+ expect(subject).to eq({ 'foobar' => 123 })
+ end
+ end
+ end
end
def clear_pool
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index 2f3f0feb87e..7aa7d8e8abd 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -165,7 +165,6 @@
- './ee/spec/controllers/projects/vulnerability_feedback_controller_spec.rb'
- './ee/spec/controllers/registrations/company_controller_spec.rb'
- './ee/spec/controllers/registrations/groups_projects_controller_spec.rb'
-- './ee/spec/controllers/registrations/verification_controller_spec.rb'
- './ee/spec/controllers/repositories/git_http_controller_spec.rb'
- './ee/spec/controllers/security/dashboard_controller_spec.rb'
- './ee/spec/controllers/security/projects_controller_spec.rb'
@@ -341,7 +340,6 @@
- './ee/spec/features/issues/form_spec.rb'
- './ee/spec/features/issues/gfm_autocomplete_ee_spec.rb'
- './ee/spec/features/issues/issue_actions_spec.rb'
-- './ee/spec/features/issues/issue_sidebar_spec.rb'
- './ee/spec/features/issues/move_issue_resource_weight_events_spec.rb'
- './ee/spec/features/issues/related_issues_spec.rb'
- './ee/spec/features/issues/resource_weight_events_spec.rb'
@@ -428,7 +426,6 @@
- './ee/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb'
- './ee/spec/features/projects/milestones/milestone_spec.rb'
- './ee/spec/features/projects/mirror_spec.rb'
-- './ee/spec/features/projects/navbar_spec.rb'
- './ee/spec/features/projects/new_project_from_template_spec.rb'
- './ee/spec/features/projects/new_project_spec.rb'
- './ee/spec/features/projects/path_locks_spec.rb'
@@ -966,11 +963,9 @@
- './ee/spec/helpers/ee/groups/settings_helper_spec.rb'
- './ee/spec/helpers/ee/hooks_helper_spec.rb'
- './ee/spec/helpers/ee/integrations_helper_spec.rb'
-- './ee/spec/helpers/ee/invite_members_helper_spec.rb'
- './ee/spec/helpers/ee/issuables_helper_spec.rb'
- './ee/spec/helpers/ee/issues_helper_spec.rb'
- './ee/spec/helpers/ee/labels_helper_spec.rb'
-- './ee/spec/helpers/ee/learn_gitlab_helper_spec.rb'
- './ee/spec/helpers/ee/lock_helper_spec.rb'
- './ee/spec/helpers/ee/namespaces_helper_spec.rb'
- './ee/spec/helpers/ee/namespace_user_cap_reached_alert_helper_spec.rb'
@@ -1006,10 +1001,7 @@
- './ee/spec/helpers/manual_quarterly_co_term_banner_helper_spec.rb'
- './ee/spec/helpers/markup_helper_spec.rb'
- './ee/spec/helpers/merge_requests_helper_spec.rb'
-- './ee/spec/helpers/nav/new_dropdown_helper_spec.rb'
-- './ee/spec/helpers/nav/top_nav_helper_spec.rb'
- './ee/spec/helpers/notes_helper_spec.rb'
-- './ee/spec/helpers/paid_feature_callout_helper_spec.rb'
- './ee/spec/helpers/path_locks_helper_spec.rb'
- './ee/spec/helpers/preferences_helper_spec.rb'
- './ee/spec/helpers/prevent_forking_helper_spec.rb'
@@ -1640,7 +1632,6 @@
- './ee/spec/models/allowed_email_domain_spec.rb'
- './ee/spec/models/analytics/cycle_analytics/aggregation_context_spec.rb'
- './ee/spec/models/analytics/cycle_analytics/group_level_spec.rb'
-- './ee/spec/models/analytics/cycle_analytics/project_stage_spec.rb'
- './ee/spec/models/analytics/cycle_analytics/runtime_limiter_spec.rb'
- './ee/spec/models/analytics/devops_adoption/enabled_namespace_spec.rb'
- './ee/spec/models/analytics/devops_adoption/snapshot_spec.rb'
@@ -1674,7 +1665,6 @@
- './ee/spec/models/broadcast_message_spec.rb'
- './ee/spec/models/burndown_spec.rb'
- './ee/spec/models/ci/bridge_spec.rb'
-- './ee/spec/models/ci/build_spec.rb'
- './ee/spec/models/ci/daily_build_group_report_result_spec.rb'
- './ee/spec/models/ci/minutes/additional_pack_spec.rb'
- './ee/spec/models/ci/minutes/context_spec.rb'
@@ -1782,6 +1772,7 @@
- './ee/spec/models/ee/pages_deployment_spec.rb'
- './ee/spec/models/ee/personal_access_token_spec.rb'
- './ee/spec/models/ee/preloaders/group_policy_preloader_spec.rb'
+- './ee/spec/models/ee/project_spec.rb'
- './ee/spec/models/ee/project_authorization_spec.rb'
- './ee/spec/models/ee/project_group_link_spec.rb'
- './ee/spec/models/ee/project_setting_spec.rb'
@@ -1905,7 +1896,6 @@
- './ee/spec/models/project_member_spec.rb'
- './ee/spec/models/project_repository_state_spec.rb'
- './ee/spec/models/project_security_setting_spec.rb'
-- './ee/spec/models/project_spec.rb'
- './ee/spec/models/project_team_spec.rb'
- './ee/spec/models/protected_branch/required_code_owners_section_spec.rb'
- './ee/spec/models/protected_branch/unprotect_access_level_spec.rb'
@@ -3117,7 +3107,6 @@
- './ee/spec/views/groups/compliance_frameworks/new.html.haml_spec.rb'
- './ee/spec/views/groups/edit.html.haml_spec.rb'
- './ee/spec/views/groups/feature_discovery_moments/advanced_features_dashboard.html.haml_spec.rb'
-- './ee/spec/views/groups/group_members/index.html.haml_spec.rb'
- './ee/spec/views/groups/hook_logs/show.html.haml_spec.rb'
- './ee/spec/views/groups/hooks/edit.html.haml_spec.rb'
- './ee/spec/views/groups/security/discover/show.html.haml_spec.rb'
@@ -3128,7 +3117,6 @@
- './ee/spec/views/layouts/header/_current_user_dropdown.html.haml_spec.rb'
- './ee/spec/views/layouts/header/_ee_subscribable_banner.html.haml_spec.rb'
- './ee/spec/views/layouts/header/help_dropdown/_cross_stage_fdm.html.haml_spec.rb'
-- './ee/spec/views/layouts/header/_new_dropdown.haml_spec.rb'
- './ee/spec/views/layouts/header/_read_only_banner.html.haml_spec.rb'
- './ee/spec/views/layouts/nav/sidebar/_admin.html.haml_spec.rb'
- './ee/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb'
@@ -3142,7 +3130,6 @@
- './ee/spec/views/projects/issues/show.html.haml_spec.rb'
- './ee/spec/views/projects/_merge_request_status_checks_settings.html.haml_spec.rb'
- './ee/spec/views/projects/on_demand_scans/index.html.haml_spec.rb'
-- './ee/spec/views/projects/project_members/index.html.haml_spec.rb'
- './ee/spec/views/projects/security/corpus_management/show.html.haml_spec.rb'
- './ee/spec/views/projects/security/dast_profiles/show.html.haml_spec.rb'
- './ee/spec/views/projects/security/dast_scanner_profiles/edit.html.haml_spec.rb'
@@ -3154,7 +3141,6 @@
- './ee/spec/views/projects/security/sast_configuration/show.html.haml_spec.rb'
- './ee/spec/views/projects/settings/subscriptions/_index.html.haml_spec.rb'
- './ee/spec/views/registrations/groups_projects/new.html.haml_spec.rb'
-- './ee/spec/views/registrations/welcome/continuous_onboarding_getting_started.html.haml_spec.rb'
- './ee/spec/views/search/_category.html.haml_spec.rb'
- './ee/spec/views/shared/billings/_billing_plan_actions.html.haml_spec.rb'
- './ee/spec/views/shared/billings/_billing_plan.html.haml_spec.rb'
@@ -3549,7 +3535,6 @@
- './spec/controllers/projects/issues_controller_spec.rb'
- './spec/controllers/projects/jobs_controller_spec.rb'
- './spec/controllers/projects/labels_controller_spec.rb'
-- './spec/controllers/projects/learn_gitlab_controller_spec.rb'
- './spec/controllers/projects/mattermosts_controller_spec.rb'
- './spec/controllers/projects/merge_requests/conflicts_controller_spec.rb'
- './spec/controllers/projects/merge_requests/content_controller_spec.rb'
@@ -3635,7 +3620,6 @@
- './spec/experiments/ios_specific_templates_experiment_spec.rb'
- './spec/experiments/require_verification_for_namespace_creation_experiment_spec.rb'
- './spec/experiments/security_reports_mr_widget_prompt_experiment_spec.rb'
-- './spec/experiments/video_tutorials_continuous_onboarding_experiment_spec.rb'
- './spec/features/abuse_report_spec.rb'
- './spec/features/action_cable_logging_spec.rb'
- './spec/features/admin/admin_abuse_reports_spec.rb'
@@ -5162,7 +5146,6 @@
- './spec/helpers/import_helper_spec.rb'
- './spec/helpers/instance_configuration_helper_spec.rb'
- './spec/helpers/integrations_helper_spec.rb'
-- './spec/helpers/invite_members_helper_spec.rb'
- './spec/helpers/issuables_description_templates_helper_spec.rb'
- './spec/helpers/issuables_helper_spec.rb'
- './spec/helpers/issues_helper_spec.rb'
@@ -5237,9 +5220,7 @@
- './spec/helpers/wiki_helper_spec.rb'
- './spec/helpers/wiki_page_version_helper_spec.rb'
- './spec/helpers/x509_helper_spec.rb'
-- './spec/initializers/00_deprecations_spec.rb'
- './spec/initializers/00_rails_disable_joins_spec.rb'
-- './spec/initializers/0_log_deprecations_spec.rb'
- './spec/initializers/0_postgresql_types_spec.rb'
- './spec/initializers/100_patch_omniauth_oauth2_spec.rb'
- './spec/initializers/100_patch_omniauth_saml_spec.rb'
@@ -6130,7 +6111,6 @@
- './spec/lib/gitlab/ci/status/success_warning_spec.rb'
- './spec/lib/gitlab/ci/status/waiting_for_resource_spec.rb'
- './spec/lib/gitlab/ci/tags/bulk_insert_spec.rb'
-- './spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb'
- './spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb'
- './spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb'
- './spec/lib/gitlab/ci/templates/flutter_gitlab_ci_yaml_spec.rb'
@@ -7476,7 +7456,6 @@
- './spec/lib/sidebars/menu_spec.rb'
- './spec/lib/sidebars/panel_spec.rb'
- './spec/lib/sidebars/projects/context_spec.rb'
-- './spec/lib/sidebars/projects/menus/analytics_menu_spec.rb'
- './spec/lib/sidebars/projects/menus/ci_cd_menu_spec.rb'
- './spec/lib/sidebars/projects/menus/confluence_menu_spec.rb'
- './spec/lib/sidebars/projects/menus/deployments_menu_spec.rb'
@@ -7739,7 +7718,6 @@
- './spec/models/analytics/cycle_analytics/aggregation_spec.rb'
- './spec/models/analytics/cycle_analytics/issue_stage_event_spec.rb'
- './spec/models/analytics/cycle_analytics/merge_request_stage_event_spec.rb'
-- './spec/models/analytics/cycle_analytics/project_stage_spec.rb'
- './spec/models/analytics/cycle_analytics/project_value_stream_spec.rb'
- './spec/models/analytics/cycle_analytics/stage_event_hash_spec.rb'
- './spec/models/analytics/usage_trends/measurement_spec.rb'
@@ -10309,7 +10287,6 @@
- './spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb'
- './spec/views/projects/pipelines/show.html.haml_spec.rb'
- './spec/views/projects/project_members/index.html.haml_spec.rb'
-- './spec/views/projects/runners/_specific_runners.html.haml_spec.rb'
- './spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb'
- './spec/views/projects/settings/integrations/edit.html.haml_spec.rb'
- './spec/views/projects/settings/operations/show.html.haml_spec.rb'
diff --git a/spec/support/services/issuable_update_service_shared_examples.rb b/spec/support/services/issuable_update_service_shared_examples.rb
index b85c3904127..feea21be428 100644
--- a/spec/support/services/issuable_update_service_shared_examples.rb
+++ b/spec/support/services/issuable_update_service_shared_examples.rb
@@ -26,7 +26,7 @@ RSpec.shared_examples 'issuable update service' do
expect(project).to receive(:execute_hooks).with(expected_payload, hook_event)
expect(project).to receive(:execute_integrations).with(expected_payload, hook_event)
- described_class.new(project: project, current_user: user, params: { state_event: 'reopen' }).execute(closed_issuable)
+ described_class.new(**described_class.constructor_container_arg(project), current_user: user, params: { state_event: 'reopen' }).execute(closed_issuable)
end
end
@@ -48,7 +48,7 @@ RSpec.shared_examples 'issuable update service' do
expect(project).to receive(:execute_hooks).with(expected_payload, hook_event)
expect(project).to receive(:execute_integrations).with(expected_payload, hook_event)
- described_class.new(project: project, current_user: user, params: { state_event: 'close' }).execute(open_issuable)
+ described_class.new(**described_class.constructor_container_arg(project), current_user: user, params: { state_event: 'close' }).execute(open_issuable)
end
end
end
diff --git a/spec/support/shared_contexts/graphql/types/query_type_shared_context.rb b/spec/support/shared_contexts/graphql/types/query_type_shared_context.rb
new file mode 100644
index 00000000000..1585ef0e7fc
--- /dev/null
+++ b/spec/support/shared_contexts/graphql/types/query_type_shared_context.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'with FOSS query type fields' do
+ # extracted these fields into a shared variable so that we can define FOSS fields once and use them on EE spec as well
+ let(:expected_foss_fields) do
+ [
+ :board_list,
+ :ci_application_settings,
+ :ci_config,
+ :ci_variables,
+ :container_repository,
+ :current_user,
+ :design_management,
+ :echo,
+ :gitpod_enabled,
+ :group,
+ :groups,
+ :issue,
+ :issues,
+ :jobs,
+ :merge_request,
+ :metadata,
+ :milestone,
+ :namespace,
+ :note,
+ :package,
+ :project,
+ :projects,
+ :query_complexity,
+ :runner,
+ :runner_platforms,
+ :runner_setup,
+ :runners,
+ :snippets,
+ :synthetic_note,
+ :timelogs,
+ :todo,
+ :topics,
+ :usage_trends_measurements,
+ :user,
+ :users,
+ :work_item
+ ]
+ end
+end
diff --git a/spec/support/shared_contexts/mailers/emails/service_desk_shared_context.rb b/spec/support/shared_contexts/mailers/emails/service_desk_shared_context.rb
new file mode 100644
index 00000000000..4aa4d500f5c
--- /dev/null
+++ b/spec/support/shared_contexts/mailers/emails/service_desk_shared_context.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'with service desk mailer' do
+ before do
+ stub_const('ServiceEmailClass', Class.new(ApplicationMailer))
+
+ ServiceEmailClass.class_eval do
+ include GitlabRoutingHelper
+ include EmailsHelper
+ include Emails::ServiceDesk
+
+ helper GitlabRoutingHelper
+ helper EmailsHelper
+
+ # this method is implemented in Notify class, we don't need to test it
+ def reply_key
+ 'b7721fc7e8419911a8bea145236a0519'
+ end
+
+ # this method is implemented in Notify class, we don't need to test it
+ def sender(author_id, params = {})
+ author_id
+ end
+
+ # this method is implemented in Notify class
+ #
+ # We do not need to test the Notify method, it is already tested in notify_spec
+ def mail_new_thread(issue, options)
+ # we need to rewrite this in order to look up templates in the correct directory
+ self.class.mailer_name = 'notify'
+
+ # this is needed for default layout
+ @unsubscribe_url = 'http://unsubscribe.example.com'
+
+ mail(options)
+ end
+ alias_method :mail_answer_thread, :mail_new_thread
+ end
+ end
+end
diff --git a/spec/support/shared_contexts/models/ci/job_token_scope.rb b/spec/support/shared_contexts/models/ci/job_token_scope.rb
index 51f671b139d..d0fee23b57c 100644
--- a/spec/support/shared_contexts/models/ci/job_token_scope.rb
+++ b/spec/support/shared_contexts/models/ci/job_token_scope.rb
@@ -1,21 +1,27 @@
# frozen_string_literal: true
-RSpec.shared_context 'with scoped projects' do
- let_it_be(:inbound_scoped_project) { create_scoped_project(source_project, direction: :inbound) }
- let_it_be(:outbound_scoped_project) { create_scoped_project(source_project, direction: :outbound) }
+RSpec.shared_context 'with a project in each allowlist' do
+ let_it_be(:outbound_allowlist_project) { create_project_in_allowlist(source_project, direction: :outbound) }
+
+ include_context 'with inaccessible projects'
+end
+
+RSpec.shared_context 'with accessible and inaccessible projects' do
+ let_it_be(:outbound_allowlist_project) { create_project_in_allowlist(source_project, direction: :outbound) }
+ let_it_be(:inbound_accessible_project) { create_inbound_accessible_project(source_project) }
+ let_it_be(:fully_accessible_project) { create_inbound_and_outbound_accessible_project(source_project) }
+
+ include_context 'with inaccessible projects'
+end
+
+RSpec.shared_context 'with inaccessible projects' do
+ let_it_be(:inbound_allowlist_project) { create_project_in_allowlist(source_project, direction: :inbound) }
+ include_context 'with unscoped projects'
+end
+
+RSpec.shared_context 'with unscoped projects' do
let_it_be(:unscoped_project1) { create(:project) }
let_it_be(:unscoped_project2) { create(:project) }
let_it_be(:link_out_of_scope) { create(:ci_job_token_project_scope_link, target_project: unscoped_project1) }
-
- def create_scoped_project(source_project, direction:)
- create(:project).tap do |scoped_project|
- create(
- :ci_job_token_project_scope_link,
- source_project: source_project,
- target_project: scoped_project,
- direction: direction
- )
- end
- end
end
diff --git a/spec/support/shared_contexts/navbar_structure_context.rb b/spec/support/shared_contexts/navbar_structure_context.rb
index 9c7cf831241..b74819d2ac7 100644
--- a/spec/support/shared_contexts/navbar_structure_context.rb
+++ b/spec/support/shared_contexts/navbar_structure_context.rb
@@ -85,7 +85,8 @@ RSpec.shared_context 'project navbar structure' do
_('Metrics'),
_('Error Tracking'),
_('Alerts'),
- _('Incidents')
+ _('Incidents'),
+ _('Airflow')
]
},
{
@@ -243,7 +244,10 @@ RSpec.shared_context 'dashboard navbar structure' do
},
{
nav_item: _("Merge requests"),
- nav_sub_items: []
+ nav_sub_items: [
+ _('Assigned 0'),
+ _('Review requests 0')
+ ]
},
{
nav_item: _("To-Do List"),
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 fddcecbe125..4c081c8464e 100644
--- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
@@ -36,6 +36,7 @@ RSpec.shared_context 'GroupPolicy context' do
read_prometheus
read_crm_contact
read_crm_organization
+ read_internal_note
]
end
@@ -81,7 +82,7 @@ RSpec.shared_context 'GroupPolicy context' do
]
end
- let(:admin_permissions) { %i[read_confidential_issues] }
+ let(:admin_permissions) { %i[read_confidential_issues read_internal_note] }
before_all do
group.add_guest(guest)
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 6e2caa853f8..afc7fc8766f 100644
--- a/spec/support/shared_contexts/policies/project_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
@@ -38,7 +38,8 @@ RSpec.shared_context 'ProjectPolicy context' do
read_commit_status read_confidential_issues read_container_image
read_harbor_registry read_deployment read_environment read_merge_request
read_metrics_dashboard_annotation read_pipeline read_prometheus
- read_sentry_issue update_issue create_merge_request_in
+ read_sentry_issue update_issue create_merge_request_in read_external_emails
+ read_internal_note
]
end
@@ -89,6 +90,13 @@ RSpec.shared_context 'ProjectPolicy context' do
]
end
+ let(:admin_permissions) do
+ %i[
+ read_project_for_iids update_max_artifacts_size read_storage_disk_path
+ owner_access admin_remote_mirror read_internal_note
+ ]
+ end
+
# Used in EE specs
let(:additional_guest_permissions) { [] }
let(:additional_reporter_permissions) { [] }
diff --git a/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb b/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb
index 7c37e5189f1..f6e10543c84 100644
--- a/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb
+++ b/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb
@@ -11,7 +11,7 @@ RSpec.shared_context 'conan api setup' do
let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) }
let(:project) { package.project }
- let(:job) { create(:ci_build, :running, user: user) }
+ let(:job) { create(:ci_build, :running, user: user, project: project) }
let(:job_token) { job.token }
let(:auth_token) { personal_access_token.token }
let(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) }
diff --git a/spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb b/spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb
index cf090c7a185..57967fb9414 100644
--- a/spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb
+++ b/spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb
@@ -75,7 +75,8 @@ RSpec.shared_context 'Debian repository shared context' do |container_type, can_
end
end
- let(:api_params) { workhorse_params }
+ let(:extra_params) { {} }
+ let(:api_params) { workhorse_params.merge(extra_params) }
let(:auth_headers) { {} }
let(:wh_headers) do
diff --git a/spec/support/shared_contexts/requests/api/graphql/releases_and_group_releases_shared_context.rb b/spec/support/shared_contexts/requests/api/graphql/releases_and_group_releases_shared_context.rb
new file mode 100644
index 00000000000..81076ea6fdc
--- /dev/null
+++ b/spec/support/shared_contexts/requests/api/graphql/releases_and_group_releases_shared_context.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'when releases and group releases shared context' do
+ let_it_be(:stranger) { create(:user) }
+ let_it_be(:guest) { create(:user) }
+ let_it_be(:reporter) { create(:user) }
+ let_it_be(:developer) { create(:user) }
+
+ let(:base_url_params) { { scope: 'all', release_tag: release.tag } }
+ let(:opened_url_params) { { state: 'opened', **base_url_params } }
+ let(:merged_url_params) { { state: 'merged', **base_url_params } }
+ let(:closed_url_params) { { state: 'closed', **base_url_params } }
+
+ let(:query) do
+ graphql_query_for(resource_type, { fullPath: resource.full_path },
+ %(
+ releases {
+ count
+ nodes {
+ tagName
+ tagPath
+ name
+ commit {
+ sha
+ }
+ assets {
+ count
+ sources {
+ nodes {
+ url
+ }
+ }
+ }
+ evidences {
+ nodes {
+ sha
+ }
+ }
+ links {
+ selfUrl
+ openedMergeRequestsUrl
+ mergedMergeRequestsUrl
+ closedMergeRequestsUrl
+ openedIssuesUrl
+ closedIssuesUrl
+ }
+ }
+ }
+ ))
+ end
+
+ let(:params_for_issues_and_mrs) { { scope: 'all', state: 'opened', release_tag: release.tag } }
+ let(:post_query) { post_graphql(query, current_user: current_user) }
+
+ let(:data) { graphql_data.dig(resource_type.to_s, 'releases', 'nodes', 0) }
+
+ before do
+ stub_default_url_options(host: 'www.example.com')
+ end
+end
diff --git a/spec/support/shared_examples/bulk_imports/visibility_level_examples.rb b/spec/support/shared_examples/bulk_imports/visibility_level_examples.rb
new file mode 100644
index 00000000000..40e9726f89c
--- /dev/null
+++ b/spec/support/shared_examples/bulk_imports/visibility_level_examples.rb
@@ -0,0 +1,124 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'visibility level settings' do
+ context 'when public' do
+ let(:data) { { 'visibility' => 'public' } }
+
+ context 'when destination is a public group' do
+ let(:destination_group) { create(:group, :public) }
+
+ it 'sets visibility level to public' do
+ expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::PUBLIC)
+ end
+ end
+
+ context 'when destination is a internal group' do
+ let(:destination_group) { create(:group, :internal) }
+
+ it 'sets visibility level to internal' do
+ expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::INTERNAL)
+ end
+ end
+
+ context 'when destination is a private group' do
+ let(:destination_group) { create(:group, :private) }
+
+ it 'sets visibility level to private' do
+ expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+ end
+
+ context 'when destination is blank' do
+ let(:destination_namespace) { '' }
+
+ it 'sets visibility level to public' do
+ expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::PUBLIC)
+ end
+ end
+ end
+
+ context 'when internal' do
+ let(:data) { { 'visibility' => 'internal' } }
+
+ context 'when destination is a public group' do
+ let(:destination_group) { create(:group, :public) }
+
+ it 'sets visibility level to internal' do
+ expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::INTERNAL)
+ end
+ end
+
+ context 'when destination is a internal group' do
+ let(:destination_group) { create(:group, :internal) }
+
+ it 'sets visibility level to internal' do
+ expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::INTERNAL)
+ end
+ end
+
+ context 'when destination is a private group' do
+ let(:destination_group) { create(:group, :private) }
+
+ it 'sets visibility level to private' do
+ expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+ end
+
+ context 'when destination is blank' do
+ let(:destination_namespace) { '' }
+
+ it 'sets visibility level to internal' do
+ expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::INTERNAL)
+ end
+
+ context 'when visibility level is restricted' do
+ it 'sets visibility level to private' do
+ stub_application_setting(
+ restricted_visibility_levels: [
+ Gitlab::VisibilityLevel::INTERNAL,
+ Gitlab::VisibilityLevel::PUBLIC
+ ]
+ )
+
+ expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+ end
+ end
+ end
+
+ context 'when private' do
+ let(:data) { { 'visibility' => 'private' } }
+
+ context 'when destination is a public group' do
+ let(:destination_group) { create(:group, :public) }
+
+ it 'sets visibility level to private' do
+ expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+ end
+
+ context 'when destination is a internal group' do
+ let(:destination_group) { create(:group, :internal) }
+
+ it 'sets visibility level to private' do
+ expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+ end
+
+ context 'when destination is a private group' do
+ let(:destination_group) { create(:group, :private) }
+
+ it 'sets visibility level to private' do
+ expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+ end
+
+ context 'when destination is blank' do
+ let(:destination_namespace) { '' }
+
+ it 'sets visibility level to private' do
+ expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/controllers/issuable_anonymous_search_disabled_examples.rb b/spec/support/shared_examples/controllers/issuable_anonymous_search_disabled_examples.rb
deleted file mode 100644
index e77acb93798..00000000000
--- a/spec/support/shared_examples/controllers/issuable_anonymous_search_disabled_examples.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples 'issuable list with anonymous search disabled' do |action|
- let(:controller_action) { :index }
- let(:params_with_search) { params.merge(search: 'some search term') }
-
- context 'when disable_anonymous_search is enabled' do
- before do
- stub_feature_flags(disable_anonymous_search: true)
- end
-
- it 'shows a flash message' do
- get controller_action, params: params_with_search
-
- expect(flash.now[:notice]).to eq('You must sign in to search for specific terms.')
- end
-
- context 'when search param is not given' do
- it 'does not show a flash message' do
- get controller_action, params: params
-
- expect(flash.now[:notice]).to be_nil
- end
- end
-
- context 'when user is signed-in' do
- it 'does not show a flash message' do
- sign_in(create(:user))
- get controller_action, params: params_with_search
-
- expect(flash.now[:notice]).to be_nil
- end
- end
-
- context 'when format is not HTML' do
- it 'does not show a flash message' do
- get controller_action, params: params_with_search.merge(format: :atom)
-
- expect(flash.now[:notice]).to be_nil
- end
- end
- end
-
- context 'when disable_anonymous_search is disabled' do
- before do
- stub_feature_flags(disable_anonymous_search: false)
- end
-
- it 'does not show a flash message' do
- get controller_action, params: params_with_search
-
- expect(flash.now[:notice]).to be_nil
- end
- end
-end
diff --git a/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb b/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb
index 3a7588a5cc9..cc28a79b4ca 100644
--- a/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb
@@ -61,9 +61,14 @@ RSpec.shared_examples Repositories::GitHttpController do
end
it 'updates the user activity' do
- expect_next_instance_of(Users::ActivityService) do |activity_service|
- expect(activity_service).to receive(:execute)
- end
+ activity_project = container.is_a?(PersonalSnippet) ? nil : project
+
+ activity_service = instance_double(Users::ActivityService)
+
+ args = { author: user, project: activity_project, namespace: activity_project&.namespace }
+ expect(Users::ActivityService).to receive(:new).with(args).and_return(activity_service)
+
+ expect(activity_service).to receive(:execute)
get :info_refs, params: params
end
diff --git a/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb b/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb
index 7e99066110d..ba00e3e0610 100644
--- a/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb
+++ b/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb
@@ -52,3 +52,12 @@ RSpec.shared_examples 'Snowplow event tracking with RedisHLL context' do |overri
end
end
end
+
+RSpec.shared_examples 'Snowplow event tracking with Redis context' do |overrides: {}|
+ it_behaves_like 'Snowplow event tracking', overrides: overrides do
+ let(:context) do
+ key_path = try(:label) || action
+ [Gitlab::Tracking::ServicePingContext.new(data_source: :redis, key_path: key_path).to_context.to_json]
+ 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 7f31ea8f9be..96e57980c68 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
@@ -2,6 +2,7 @@
RSpec.shared_examples 'a creatable merge request' do
include WaitForRequests
+ include ListboxHelpers
it 'creates new merge request', :js do
find('.js-assignee-search').click
@@ -64,14 +65,16 @@ RSpec.shared_examples 'a creatable merge request' do
visit project_new_merge_request_path(source_project)
first('.js-target-project').click
- find('.dropdown-target-project li', text: target_project.full_path).click
+ select_listbox_item(target_project.full_path)
wait_for_requests
first('.js-target-branch').click
- within('.js-target-branch-dropdown .dropdown-content') do
- expect(page).to have_content('a-brand-new-branch-to-test')
- end
+ find('.gl-listbox-search-input').set('a-brand-new-branch-to-test')
+
+ wait_for_requests
+
+ expect_listbox_item('a-brand-new-branch-to-test')
end
end
diff --git a/spec/support/shared_examples/features/incident_details_routing_shared_examples.rb b/spec/support/shared_examples/features/incident_details_routing_shared_examples.rb
new file mode 100644
index 00000000000..dab125caa60
--- /dev/null
+++ b/spec/support/shared_examples/features/incident_details_routing_shared_examples.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'for each incident details route' do |example, tab_text:|
+ before do
+ sign_in(user)
+ visit incident_path
+ end
+
+ context 'for /-/issues/:id route' do
+ let(:incident_path) { project_issue_path(project, incident) }
+
+ before do
+ page.within('[data-testid="incident-tabs"]') { click_link tab_text }
+ end
+
+ it_behaves_like example
+ end
+
+ context 'for /-/issues/incident/:id route' do
+ let(:incident_path) { incident_project_issues_path(project, incident) }
+
+ before do
+ page.within('[data-testid="incident-tabs"]') { click_link tab_text }
+ end
+
+ it_behaves_like example
+ end
+end
diff --git a/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb b/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb
index 149486320ae..b6f7094e422 100644
--- a/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb
+++ b/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
RSpec.shared_examples 'issuable invite members' do
+ include Spec::Support::Helpers::Features::InviteMembersModalHelper
+
context 'when a privileged user can invite' do
before do
project.add_maintainer(user)
@@ -21,7 +23,9 @@ RSpec.shared_examples 'issuable invite members' do
click_link 'Invite Members'
- expect(page).to have_content("You're inviting members to the")
+ page.within invite_modal_selector do
+ expect(page).to have_content("You're inviting members to the #{project.name} project")
+ end
end
end
diff --git a/spec/support/shared_examples/features/reportable_note_shared_examples.rb b/spec/support/shared_examples/features/reportable_note_shared_examples.rb
index 9d859403465..bb3fab5b23e 100644
--- a/spec/support/shared_examples/features/reportable_note_shared_examples.rb
+++ b/spec/support/shared_examples/features/reportable_note_shared_examples.rb
@@ -20,7 +20,7 @@ RSpec.shared_examples 'reportable note' do |type|
dropdown = comment.find(more_actions_selector)
open_dropdown(dropdown)
- expect(dropdown).to have_link('Report abuse to administrator', href: abuse_report_path)
+ expect(dropdown).to have_button('Report abuse to administrator')
if type == 'issue' || type == 'merge_request'
expect(dropdown).to have_button('Delete comment')
@@ -33,10 +33,14 @@ RSpec.shared_examples 'reportable note' do |type|
dropdown = comment.find(more_actions_selector)
open_dropdown(dropdown)
- dropdown.click_link('Report abuse to administrator')
+ dropdown.click_button('Report abuse to administrator')
+
+ choose "They're posting spam."
+ click_button "Next"
expect(find('#user_name')['value']).to match(note.author.username)
expect(find('#abuse_report_reported_from_url')['value']).to match(noteable_note_url(note))
+ expect(find('#abuse_report_category', visible: false)['value']).to match('spam')
end
def open_dropdown(dropdown)
diff --git a/spec/support/shared_examples/features/runners_shared_examples.rb b/spec/support/shared_examples/features/runners_shared_examples.rb
index 20078243cfb..63a0832117d 100644
--- a/spec/support/shared_examples/features/runners_shared_examples.rb
+++ b/spec/support/shared_examples/features/runners_shared_examples.rb
@@ -178,6 +178,22 @@ RSpec.shared_examples 'filters by tag' do
end
end
+RSpec.shared_examples 'shows runner jobs tab' do
+ context 'when clicking on jobs tab' do
+ before do
+ click_on("#{s_('Runners|Jobs')} #{job_count}")
+
+ wait_for_requests
+ end
+
+ it 'shows job in list' do
+ within "[data-testid='job-row-#{job.id}']" do
+ expect(page).to have_link("##{job.id}")
+ end
+ end
+ end
+end
+
RSpec.shared_examples 'submits edit runner form' do
it 'breadcrumb contains runner id and token' do
page.within '[data-testid="breadcrumb-links"]' do
diff --git a/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb b/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb
index 95b306fdaaa..a332fdec963 100644
--- a/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb
+++ b/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb
@@ -95,7 +95,7 @@ RSpec.shared_examples 'labels sidebar widget' do
end
end
- it 'creates new label' do
+ it 'creates new label', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/391240' do
page.within(labels_widget) do
fill_in 'Name new label', with: 'wontfix'
page.find('.suggest-colors a', match: :first).click
diff --git a/spec/support/shared_examples/features/sidebar_shared_examples.rb b/spec/support/shared_examples/features/sidebar_shared_examples.rb
index 77334db6a36..c2c50e8762f 100644
--- a/spec/support/shared_examples/features/sidebar_shared_examples.rb
+++ b/spec/support/shared_examples/features/sidebar_shared_examples.rb
@@ -42,6 +42,26 @@ RSpec.shared_examples 'issue boards sidebar' do
end
end
+ context 'editing issue title' do
+ it 'edits issue title' do
+ page.within('[data-testid="sidebar-title"]') do
+ click_button 'Edit'
+
+ wait_for_requests
+
+ find('input').set('Test title')
+
+ click_button 'Save changes'
+
+ wait_for_requests
+
+ expect(page).to have_content('Test title')
+ end
+
+ expect(first_card).to have_content('Test title')
+ end
+ end
+
context 'editing issue milestone', :js do
it_behaves_like 'milestone sidebar widget'
end
diff --git a/spec/support/shared_examples/features/work_items_shared_examples.rb b/spec/support/shared_examples/features/work_items_shared_examples.rb
new file mode 100644
index 00000000000..4f3d957ad71
--- /dev/null
+++ b/spec/support/shared_examples/features/work_items_shared_examples.rb
@@ -0,0 +1,141 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'work items status' do
+ let(:state_selector) { '[data-testid="work-item-state-select"]' }
+
+ it 'successfully shows and changes the status of the work item' do
+ expect(find(state_selector)).to have_content 'Open'
+
+ find(state_selector).select("Closed")
+
+ wait_for_requests
+
+ expect(find(state_selector)).to have_content 'Closed'
+ expect(work_item.reload.state).to eq('closed')
+ end
+end
+
+RSpec.shared_examples 'work items comments' do
+ let(:form_selector) { '[data-testid="work-item-add-comment"]' }
+
+ it 'successfully creates and shows comments' do
+ click_button 'Add a comment'
+
+ find(form_selector).fill_in(with: "Test comment")
+ click_button "Comment"
+
+ wait_for_requests
+
+ expect(page).to have_content "Test comment"
+ end
+end
+
+RSpec.shared_examples 'work items assignees' do
+ it 'successfully assigns the current user by searching' do
+ # The button is only when the mouse is over the input
+ find('[data-testid="work-item-assignees-input"]').fill_in(with: user.username)
+ wait_for_requests
+
+ # submit and simulate blur to save
+ send_keys(:enter)
+ find("body").click
+
+ wait_for_requests
+
+ expect(work_item.assignees).to include(user)
+ end
+end
+
+RSpec.shared_examples 'work items labels' do
+ it 'successfully assigns a label' do
+ label = create(:label, project: work_item.project, title: "testing-label")
+
+ find('[data-testid="work-item-labels-input"]').fill_in(with: label.title)
+ wait_for_requests
+
+ # submit and simulate blur to save
+ send_keys(:enter)
+ find("body").click
+
+ wait_for_requests
+
+ expect(work_item.labels).to include(label)
+ end
+end
+
+RSpec.shared_examples 'work items description' do
+ it 'shows GFM autocomplete', :aggregate_failures do
+ click_button "Edit description"
+
+ find('[aria-label="Description"]').send_keys("@#{user.username}")
+
+ wait_for_requests
+
+ page.within('.atwho-container') do
+ expect(page).to have_text(user.name)
+ end
+ end
+
+ it 'autocompletes available quick actions', :aggregate_failures do
+ click_button "Edit description"
+
+ find('[aria-label="Description"]').send_keys("/")
+
+ wait_for_requests
+
+ page.within('.atwho-container') do
+ expect(page).to have_text("title")
+ expect(page).to have_text("shrug")
+ expect(page).to have_text("tableflip")
+ expect(page).to have_text("close")
+ expect(page).to have_text("cc")
+ end
+ end
+
+ context 'on conflict' do
+ let_it_be(:other_user) { create(:user) }
+ let(:expected_warning) { 'Someone edited the description at the same time you did.' }
+
+ before do
+ project.add_developer(other_user)
+ end
+
+ it 'shows conflict message when description changes', :aggregate_failures do
+ click_button "Edit description"
+
+ wait_for_requests
+
+ ::WorkItems::UpdateService.new(
+ container: work_item.project,
+ current_user: other_user,
+ params: { description: "oh no!" }
+ ).execute(work_item)
+
+ wait_for_requests
+
+ find('[aria-label="Description"]').send_keys("oh yeah!")
+
+ expect(page.find('[data-testid="work-item-description-conflicts"]')).to have_text(expected_warning)
+
+ click_button "Save and overwrite"
+
+ expect(page.find('[data-testid="work-item-description"]')).to have_text("oh yeah!")
+ end
+ end
+end
+
+RSpec.shared_examples 'work items invite members' do
+ include Spec::Support::Helpers::Features::InviteMembersModalHelper
+
+ it 'successfully assigns the current user by searching' do
+ # The button is only when the mouse is over the input
+ find('[data-testid="work-item-assignees-input"]').fill_in(with: 'Invite members')
+ wait_for_requests
+
+ click_button('Invite members')
+
+ page.within invite_modal_selector do
+ expect(page).to have_content("You're inviting members to the #{work_item.project.name} project")
+ end
+ end
+end
diff --git a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb
index 6f4072ba762..93f9e42241b 100644
--- a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb
+++ b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb
@@ -670,35 +670,6 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
expect(items).to contain_exactly(english)
end
end
-
- context 'with anonymous user' do
- let_it_be(:public_project) { create(:project, :public, group: subgroup) }
- let_it_be(:item6) { create(factory, project: public_project, title: 'tanuki') }
- let_it_be(:item7) { create(factory, project: public_project, title: 'ikunat') }
-
- let(:search_user) { nil }
- let(:params) { { search: 'tanuki' } }
-
- context 'with disable_anonymous_search feature flag enabled' do
- before do
- stub_feature_flags(disable_anonymous_search: true)
- end
-
- it 'does not perform search' do
- expect(items).to contain_exactly(item6, item7)
- end
- end
-
- context 'with disable_anonymous_search feature flag disabled' do
- before do
- stub_feature_flags(disable_anonymous_search: false)
- end
-
- it 'finds one public item' do
- expect(items).to contain_exactly(item6)
- end
- end
- end
end
context 'filtering by item term in title' do
@@ -1003,7 +974,7 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
let(:params) { { issue_types: ['nonsense'] } }
it 'returns no items' do
- expect(items).to eq(items_model.none)
+ expect(items.none?).to eq(true)
end
end
end
@@ -1285,28 +1256,12 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
subject { described_class.new(nil, params).with_confidentiality_access_check }
it_behaves_like 'returns public, does not return hidden or confidential'
-
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(ban_user_feature_flag: false)
- end
-
- it_behaves_like 'returns public and hidden, does not return confidential'
- end
end
context 'for a user without project membership' do
subject { described_class.new(user, params).with_confidentiality_access_check }
it_behaves_like 'returns public, does not return hidden or confidential'
-
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(ban_user_feature_flag: false)
- end
-
- it_behaves_like 'returns public and hidden, does not return confidential'
- end
end
context 'for a guest user' do
@@ -1317,28 +1272,12 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
end
it_behaves_like 'returns public, does not return hidden or confidential'
-
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(ban_user_feature_flag: false)
- end
-
- it_behaves_like 'returns public and hidden, does not return confidential'
- end
end
context 'for a project member with access to view confidential items' do
subject { described_class.new(authorized_user, params).with_confidentiality_access_check }
it_behaves_like 'returns public and confidential, does not return hidden'
-
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(ban_user_feature_flag: false)
- end
-
- it_behaves_like 'returns public, confidential, and hidden'
- end
end
context 'for an admin' do
@@ -1348,26 +1287,10 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
context 'when admin mode is enabled', :enable_admin_mode do
it_behaves_like 'returns public, confidential, and hidden'
-
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(ban_user_feature_flag: false)
- end
-
- it_behaves_like 'returns public, confidential, and hidden'
- end
end
context 'when admin mode is disabled' do
it_behaves_like 'returns public, does not return hidden or confidential'
-
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(ban_user_feature_flag: false)
- end
-
- it_behaves_like 'returns public and hidden, does not return confidential'
- end
end
end
end
@@ -1380,14 +1303,6 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
it_behaves_like 'returns public, does not return hidden or confidential'
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(ban_user_feature_flag: false)
- end
-
- it_behaves_like 'returns public and hidden, does not return confidential'
- end
-
it 'does not filter by confidentiality' do
expect(items_model).not_to receive(:where).with(a_string_matching('confidential'), anything)
subject
@@ -1399,14 +1314,6 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
it_behaves_like 'returns public, does not return hidden or confidential'
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(ban_user_feature_flag: false)
- end
-
- it_behaves_like 'returns public and hidden, does not return confidential'
- end
-
it 'filters by confidentiality' do
expect(subject.to_sql).to match("issues.confidential")
end
@@ -1421,14 +1328,6 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
it_behaves_like 'returns public, does not return hidden or confidential'
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(ban_user_feature_flag: false)
- end
-
- it_behaves_like 'returns public and hidden, does not return confidential'
- end
-
it 'filters by confidentiality' do
expect(subject.to_sql).to match("issues.confidential")
end
@@ -1439,14 +1338,6 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
it_behaves_like 'returns public and confidential, does not return hidden'
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(ban_user_feature_flag: false)
- end
-
- it_behaves_like 'returns public, confidential, and hidden'
- end
-
it 'does not filter by confidentiality' do
expect(items_model).not_to receive(:where).with(a_string_matching('confidential'), anything)
@@ -1462,14 +1353,6 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
context 'when admin mode is enabled', :enable_admin_mode do
it_behaves_like 'returns public, confidential, and hidden'
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(ban_user_feature_flag: false)
- end
-
- it_behaves_like 'returns public, confidential, and hidden'
- end
-
it 'does not filter by confidentiality' do
expect(items_model).not_to receive(:where).with(a_string_matching('confidential'), anything)
@@ -1480,14 +1363,6 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
context 'when admin mode is disabled' do
it_behaves_like 'returns public, does not return hidden or confidential'
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(ban_user_feature_flag: false)
- end
-
- it_behaves_like 'returns public and hidden, does not return confidential'
- end
-
it 'filters by confidentiality' do
expect(subject.to_sql).to match("issues.confidential")
end
diff --git a/spec/support/shared_examples/finders/packages/debian/distributions_finder_shared_examples.rb b/spec/support/shared_examples/finders/packages/debian/distributions_finder_shared_examples.rb
index 2700d29bf0e..5e7a2b77797 100644
--- a/spec/support/shared_examples/finders/packages/debian/distributions_finder_shared_examples.rb
+++ b/spec/support/shared_examples/finders/packages/debian/distributions_finder_shared_examples.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.shared_examples 'Debian Distributions Finder' do |factory, can_freeze|
- let_it_be(:distribution_with_suite, freeze: can_freeze) { create(factory, suite: 'mysuite') }
+ let_it_be(:distribution_with_suite, freeze: can_freeze) { create(factory, :with_suite) }
let_it_be(:container) { distribution_with_suite.container }
let_it_be(:distribution_with_same_container, freeze: can_freeze) { create(factory, container: container ) }
let_it_be(:distribution_with_same_codename, freeze: can_freeze) { create(factory, codename: distribution_with_suite.codename ) }
@@ -35,7 +35,7 @@ RSpec.shared_examples 'Debian Distributions Finder' do |factory, can_freeze|
context 'by suite' do
context 'with existing suite' do
- let(:params) { { suite: 'mysuite' } }
+ let(:params) { { suite: distribution_with_suite.suite } }
it 'finds distribution by suite' do
is_expected.to contain_exactly(distribution_with_suite)
@@ -61,7 +61,7 @@ RSpec.shared_examples 'Debian Distributions Finder' do |factory, can_freeze|
end
context 'with existing suite' do
- let(:params) { { codename_or_suite: 'mysuite' } }
+ let(:params) { { codename_or_suite: distribution_with_suite.suite } }
it 'finds distribution by suite' do
is_expected.to contain_exactly(distribution_with_suite)
diff --git a/spec/support/shared_examples/graphql/mutations/work_items/update_description_widget_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/work_items/update_description_widget_shared_examples.rb
index f672ec7f5ac..2ec48aa405b 100644
--- a/spec/support/shared_examples/graphql/mutations/work_items/update_description_widget_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/work_items/update_description_widget_shared_examples.rb
@@ -31,4 +31,88 @@ RSpec.shared_examples 'update work item description widget' do
expect(mutation_response['errors']).to match_array(['Description error message'])
end
end
+
+ context 'when the edited description includes quick action(s)' do
+ let(:input) { { 'descriptionWidget' => { 'description' => new_description } } }
+
+ shared_examples 'quick action is applied' do
+ before do
+ post_graphql_mutation(mutation, current_user: current_user)
+ end
+
+ it 'applies the quick action(s)' do
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['workItem']).to include(expected_response)
+ end
+ end
+
+ context 'with /title quick action' do
+ it_behaves_like 'quick action is applied' do
+ let(:new_description) { "updated description\n/title updated title" }
+ let(:filtered_description) { "updated description" }
+
+ let(:expected_response) do
+ {
+ 'title' => 'updated title',
+ 'widgets' => include({
+ 'description' => filtered_description,
+ 'type' => 'DESCRIPTION'
+ })
+ }
+ end
+ end
+ end
+
+ context 'with /shrug, /tableflip and /cc quick action' do
+ it_behaves_like 'quick action is applied' do
+ let(:new_description) { "/tableflip updated description\n/shrug\n/cc @#{developer.username}" }
+ # note: \cc performs no action since 15.0
+ let(:filtered_description) { "updated description (╯°□°)╯︵ ┻━┻\n ¯\\_(ツ)_/¯\n/cc @#{developer.username}" }
+ let(:expected_response) do
+ {
+ 'widgets' => include({
+ 'description' => filtered_description,
+ 'type' => 'DESCRIPTION'
+ })
+ }
+ end
+ end
+ end
+
+ context 'with /close' do
+ it_behaves_like 'quick action is applied' do
+ let(:new_description) { "Resolved work item.\n/close" }
+ let(:filtered_description) { "Resolved work item." }
+ let(:expected_response) do
+ {
+ 'state' => 'CLOSED',
+ 'widgets' => include({
+ 'description' => filtered_description,
+ 'type' => 'DESCRIPTION'
+ })
+ }
+ end
+ end
+ end
+
+ context 'with /reopen' do
+ before do
+ work_item.close!
+ end
+
+ it_behaves_like 'quick action is applied' do
+ let(:new_description) { "Re-opening this work item.\n/reopen" }
+ let(:filtered_description) { "Re-opening this work item." }
+ let(:expected_response) do
+ {
+ 'state' => 'OPEN',
+ 'widgets' => include({
+ 'description' => filtered_description,
+ 'type' => 'DESCRIPTION'
+ })
+ }
+ end
+ end
+ end
+ end
end
diff --git a/spec/support/shared_examples/graphql/projects/services_resolver_shared_examples.rb b/spec/support/shared_examples/graphql/projects/services_resolver_shared_examples.rb
index 16c2ab07f3a..d9a9400cb4e 100644
--- a/spec/support/shared_examples/graphql/projects/services_resolver_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/projects/services_resolver_shared_examples.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-
+# TODO: Remove in 17.0, see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108418
RSpec.shared_examples 'no project services' do
it 'returns empty collection' do
expect(resolve_services).to be_empty
diff --git a/spec/support/shared_examples/graphql/resolvers/issuable_resolvers_shared_examples.rb b/spec/support/shared_examples/graphql/resolvers/issuable_resolvers_shared_examples.rb
index 25008bca619..6b2b45d28e8 100644
--- a/spec/support/shared_examples/graphql/resolvers/issuable_resolvers_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/resolvers/issuable_resolvers_shared_examples.rb
@@ -58,41 +58,6 @@ RSpec.shared_examples 'graphql query for searching issuables' do
end
end
- context 'with anonymous user' do
- let_it_be(:current_user) { nil }
-
- context 'with disable_anonymous_search as `true`' do
- before do
- stub_feature_flags(disable_anonymous_search: true)
- end
-
- it 'returns an error' do
- error_message = "User must be authenticated to include the `search` argument."
-
- expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, error_message) do
- resolve_issuables(search: 'created')
- end
- end
-
- it 'does not return error if search term is not present' do
- expect(resolve_issuables).not_to be_instance_of(Gitlab::Graphql::Errors::ArgumentError)
- end
- end
-
- context 'with disable_anonymous_search as `false`' do
- before do
- stub_feature_flags(disable_anonymous_search: false)
- parent.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
- end
-
- it 'filters issuables by search term' do
- issuables = resolve_issuables(search: 'created')
-
- expect(issuables).to contain_exactly(issuable1, issuable2)
- end
- end
- end
-
def resolve_issuables(args = {}, obj = parent, context = { current_user: current_user })
resolve(described_class, obj: obj, args: args, ctx: context, arg_style: :internal)
end
diff --git a/spec/support/shared_examples/graphql/resolvers/releases_resolvers_shared_examples.rb b/spec/support/shared_examples/graphql/resolvers/releases_resolvers_shared_examples.rb
new file mode 100644
index 00000000000..0e09a9d9e66
--- /dev/null
+++ b/spec/support/shared_examples/graphql/resolvers/releases_resolvers_shared_examples.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'releases and group releases resolver' do
+ context 'when the user does not have access to the project' do
+ let(:current_user) { public_user }
+
+ it 'returns an empty array' do
+ expect(resolve_releases).to be_empty
+ end
+ end
+
+ context "when the user has full access to the project's releases" do
+ let(:current_user) { developer }
+
+ it 'returns all releases associated to the project' do
+ expect(resolve_releases).to match_array(all_releases)
+ end
+
+ describe 'when order_by is released_at' do
+ context 'with sort: desc' do
+ let(:args) { { sort: :released_at_desc } }
+
+ it 'returns the releases ordered by released_at in descending order' do
+ expect(resolve_releases.to_a)
+ .to match_array(all_releases)
+ .and be_sorted(:released_at, :desc)
+ end
+ end
+
+ context 'with sort: asc' do
+ let(:args) { { sort: :released_at_asc } }
+
+ it 'returns the releases ordered by released_at in ascending order' do
+ expect(resolve_releases.to_a)
+ .to match_array(all_releases)
+ .and be_sorted(:released_at, :asc)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/graphql/subscriptions/notes/notes_subscription_shared_examples.rb b/spec/support/shared_examples/graphql/subscriptions/notes/notes_subscription_shared_examples.rb
new file mode 100644
index 00000000000..949eb4fb643
--- /dev/null
+++ b/spec/support/shared_examples/graphql/subscriptions/notes/notes_subscription_shared_examples.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'graphql notes subscriptions' do
+ describe '#resolve' do
+ let_it_be(:unauthorized_user) { create(:user) }
+ let_it_be(:work_item) { create(:work_item, :task) }
+ let_it_be(:note) { create(:note, noteable: work_item, project: work_item.project) }
+ let_it_be(:current_user) { work_item.author }
+ let_it_be(:noteable_id) { work_item.to_gid }
+
+ subject { resolver.resolve_with_support(noteable_id: noteable_id) }
+
+ context 'on initial subscription' do
+ let(:resolver) do
+ resolver_instance(described_class, ctx: { current_user: current_user }, subscription_update: false)
+ end
+
+ it 'returns nil' do
+ expect(subject).to eq(nil)
+ end
+
+ context 'when user is unauthorized' do
+ let(:current_user) { unauthorized_user }
+
+ it 'raises an exception' do
+ expect { subject }.to raise_error(GraphQL::ExecutionError)
+ end
+ end
+
+ context 'when work_item does not exist' do
+ let(:noteable_id) { GlobalID.parse("gid://gitlab/WorkItem/#{non_existing_record_id}") }
+
+ it 'raises an exception' do
+ expect { subject }.to raise_error(GraphQL::ExecutionError)
+ end
+ end
+ end
+
+ context 'on subscription updates' do
+ let(:resolver) do
+ resolver_instance(described_class, obj: note, ctx: { current_user: current_user }, subscription_update: true)
+ end
+
+ it 'returns the resolved object' do
+ expect(subject).to eq(note)
+ end
+
+ context 'when user is unauthorized' do
+ let(:current_user) { unauthorized_user }
+
+ it 'unsubscribes the user' do
+ # GraphQL::Execution::Execute::Skip is returned when unsubscribed
+ expect(subject).to be_an(GraphQL::Execution::Execute::Skip)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb b/spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb
index 67576a18c80..4dc2ce61c4d 100644
--- a/spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb
@@ -5,8 +5,7 @@ RSpec.shared_examples 'Gitlab-style deprecations' do
it 'raises an informative error if `deprecation_reason` is used' do
expect { subject(deprecation_reason: 'foo') }.to raise_error(
ArgumentError,
- 'Use `deprecated` property instead of `deprecation_reason`. ' \
- 'See https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#deprecating-schema-items'
+ start_with('Use `deprecated` property instead of `deprecation_reason`.')
)
end
diff --git a/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb b/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb
index 19ceb465383..bb33a7559dc 100644
--- a/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb
@@ -41,6 +41,7 @@ RSpec.shared_examples "a user type with merge request interaction type" do
preferencesGitpodPath
profileEnableGitpodPath
savedReplies
+ savedReply
]
expect(described_class).to have_graphql_fields(*expected_fields)
diff --git a/spec/support/shared_examples/integrations/integration_settings_form.rb b/spec/support/shared_examples/integrations/integration_settings_form.rb
index 5041ac4a660..aeb4e0feb12 100644
--- a/spec/support/shared_examples/integrations/integration_settings_form.rb
+++ b/spec/support/shared_examples/integrations/integration_settings_form.rb
@@ -4,9 +4,10 @@ RSpec.shared_examples 'integration settings form' do
include IntegrationsHelper
# Note: these specs don't validate channel fields
# which are present on a few integrations
- it 'displays all the integrations' do
+ it 'displays all the integrations', feature_category: :integrations do
aggregate_failures do
integrations.each do |integration|
+ stub_feature_flags(integration_slack_app_notifications: false)
navigate_to_integration(integration)
page.within('form.integration-settings-form') do
@@ -53,12 +54,14 @@ RSpec.shared_examples 'integration settings form' do
# Should match `integrationTriggerEventTitles` in app/assets/javascripts/integrations/constants.js
event_titles = {
push_events: s_('IntegrationEvents|A push is made to the repository'),
- issues_events: s_('IntegrationEvents|IntegrationEvents|An issue is created, updated, or closed'),
- confidential_issues_events: s_('IntegrationEvents|A confidential issue is created, updated, or closed'),
- merge_requests_events: s_('IntegrationEvents|A merge request is created, updated, or merged'),
- note_events: s_('IntegrationEvents|A comment is added on an issue'),
- confidential_note_events: s_('IntegrationEvents|A comment is added on a confidential issue'),
- tag_push_events: s_('IntegrationEvents|A tag is pushed to the repository'),
+ issues_events: s_('IntegrationEvents|An issue is created, closed, or reopened'),
+ confidential_issues_events: s_('A confidential issue is created, closed, or reopened'),
+ merge_requests_events: s_('IntegrationEvents|A merge request is created, merged, closed, or reopened'),
+ note_events: s_('IntegrationEvents|A comment is added'),
+ confidential_note_events: s_(
+ 'IntegrationEvents|An internal note or comment on a confidential issue is added'
+ ),
+ tag_push_events: s_('IntegrationEvents|A tag is pushed to the repository or removed'),
pipeline_events: s_('IntegrationEvents|A pipeline status changes'),
wiki_page_events: s_('IntegrationEvents|A wiki page is created or updated')
}.with_indifferent_access
diff --git a/spec/support/shared_examples/lib/cache_helpers_shared_examples.rb b/spec/support/shared_examples/lib/cache_helpers_shared_examples.rb
index 6cdd7954b5f..42e8fa3d51f 100644
--- a/spec/support/shared_examples/lib/cache_helpers_shared_examples.rb
+++ b/spec/support/shared_examples/lib/cache_helpers_shared_examples.rb
@@ -17,7 +17,7 @@ RSpec.shared_examples_for 'object cache helper' do
end
it "fetches from the cache" do
- expect(instance.cache).to receive(:fetch).with("#{presenter.class.name}:#{presentable.cache_key}:#{user.cache_key}", expires_in: described_class::DEFAULT_EXPIRY).once
+ expect(instance.cache).to receive(:fetch).with("#{expected_cache_key_prefix}:#{presentable.cache_key}:#{user.cache_key}", expires_in: described_class::DEFAULT_EXPIRY).once
subject
end
@@ -28,7 +28,7 @@ RSpec.shared_examples_for 'object cache helper' do
end
it "uses the context to augment the cache key" do
- expect(instance.cache).to receive(:fetch).with("#{presenter.class.name}:#{presentable.cache_key}:#{project.cache_key}", expires_in: described_class::DEFAULT_EXPIRY).once
+ expect(instance.cache).to receive(:fetch).with("#{expected_cache_key_prefix}:#{presentable.cache_key}:#{project.cache_key}", expires_in: described_class::DEFAULT_EXPIRY).once
subject
end
@@ -38,7 +38,7 @@ RSpec.shared_examples_for 'object cache helper' do
it "sets the expiry when accessing the cache" do
kwargs[:expires_in] = 7.days
- expect(instance.cache).to receive(:fetch).with("#{presenter.class.name}:#{presentable.cache_key}:#{user.cache_key}", expires_in: 7.days).once
+ expect(instance.cache).to receive(:fetch).with("#{expected_cache_key_prefix}:#{presentable.cache_key}:#{user.cache_key}", expires_in: 7.days).once
subject
end
@@ -90,7 +90,7 @@ RSpec.shared_examples_for 'collection cache helper' do
end
it "fetches from the cache" do
- keys = presentable.map { |item| "#{presenter.class.name}:#{item.cache_key}:#{user.cache_key}" }
+ keys = presentable.map { |item| "#{expected_cache_key_prefix}:#{item.cache_key}:#{user.cache_key}" }
expect(instance.cache).to receive(:fetch_multi).with(*keys, expires_in: described_class::DEFAULT_EXPIRY).once.and_call_original
@@ -103,7 +103,7 @@ RSpec.shared_examples_for 'collection cache helper' do
end
it "uses the context to augment the cache key" do
- keys = presentable.map { |item| "#{presenter.class.name}:#{item.cache_key}:#{project.cache_key}" }
+ keys = presentable.map { |item| "#{expected_cache_key_prefix}:#{item.cache_key}:#{project.cache_key}" }
expect(instance.cache).to receive(:fetch_multi).with(*keys, expires_in: described_class::DEFAULT_EXPIRY).once.and_call_original
@@ -113,7 +113,7 @@ RSpec.shared_examples_for 'collection cache helper' do
context "expires_in is supplied" do
it "sets the expiry when accessing the cache" do
- keys = presentable.map { |item| "#{presenter.class.name}:#{item.cache_key}:#{user.cache_key}" }
+ keys = presentable.map { |item| "#{expected_cache_key_prefix}:#{item.cache_key}:#{user.cache_key}" }
kwargs[:expires_in] = 7.days
expect(instance.cache).to receive(:fetch_multi).with(*keys, expires_in: 7.days).once.and_call_original
diff --git a/spec/support/shared_examples/mailers/notify_shared_examples.rb b/spec/support/shared_examples/mailers/notify_shared_examples.rb
index b0cbf0b0d65..2e182fb399d 100644
--- a/spec/support/shared_examples/mailers/notify_shared_examples.rb
+++ b/spec/support/shared_examples/mailers/notify_shared_examples.rb
@@ -280,6 +280,12 @@ RSpec.shared_examples 'no email is sent' do
end
end
+RSpec.shared_examples 'a mail with default delivery method' do
+ it 'uses mailer default delivery method' do
+ expect(subject.delivery_method).to be_a ActionMailer::Base.delivery_methods[described_class.delivery_method]
+ end
+end
+
RSpec.shared_examples 'does not render a manage notifications link' do
it do
aggregate_failures do
diff --git a/spec/support/shared_examples/models/chat_integration_shared_examples.rb b/spec/support/shared_examples/models/chat_integration_shared_examples.rb
index 6d0462a9ee8..085fec6ff1e 100644
--- a/spec/support/shared_examples/models/chat_integration_shared_examples.rb
+++ b/spec/support/shared_examples/models/chat_integration_shared_examples.rb
@@ -33,7 +33,7 @@ RSpec.shared_examples "chat integration" do |integration_name|
describe "#execute" do
let_it_be(:user) { create(:user) }
- let_it_be_with_reload(:project) { create(:project, :repository) }
+ let_it_be_with_refind(:project) { create(:project, :repository) }
let(:webhook_url) { "https://example.gitlab.com/" }
let(:webhook_url_regex) { /\A#{webhook_url}.*/ }
@@ -165,7 +165,7 @@ RSpec.shared_examples "chat integration" do |integration_name|
context "with issue events" do
let(:opts) { { title: "Awesome issue", description: "please fix" } }
let(:sample_data) do
- service = Issues::CreateService.new(project: project, current_user: user, params: opts, spam_params: nil)
+ service = Issues::CreateService.new(container: project, current_user: user, params: opts, spam_params: nil)
issue = service.execute[:issue]
service.hook_data(issue, "open")
end
diff --git a/spec/support/shared_examples/models/concerns/auto_disabling_hooks_shared_examples.rb b/spec/support/shared_examples/models/concerns/auto_disabling_hooks_shared_examples.rb
new file mode 100644
index 00000000000..122774a9028
--- /dev/null
+++ b/spec/support/shared_examples/models/concerns/auto_disabling_hooks_shared_examples.rb
@@ -0,0 +1,272 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'a hook that gets automatically disabled on failure' do
+ shared_examples 'is tolerant of invalid records' do
+ specify do
+ hook.url = nil
+
+ expect(hook).to be_invalid
+ run_expectation
+ end
+ end
+
+ describe '.executable/.disabled', :freeze_time do
+ let!(:not_executable) do
+ [
+ [4, nil], # Exceeded the grace period, set by #fail!
+ [4, 1.second.from_now], # Exceeded the grace period, set by #backoff!
+ [4, Time.current] # Exceeded the grace period, set by #backoff!, edge-case
+ ].map do |(recent_failures, disabled_until)|
+ create(hook_factory, **default_factory_arguments, recent_failures: recent_failures,
+disabled_until: disabled_until)
+ end
+ end
+
+ let!(:executables) do
+ expired = 1.second.ago
+ borderline = Time.current
+ suspended = 1.second.from_now
+
+ [
+ # Most of these are impossible states, but are included for completeness
+ [0, nil],
+ [1, nil],
+ [3, nil],
+ [4, expired],
+
+ # Impossible cases:
+ [3, suspended],
+ [3, expired],
+ [3, borderline],
+ [1, suspended],
+ [1, expired],
+ [1, borderline],
+ [0, borderline],
+ [0, suspended],
+ [0, expired]
+ ].map do |(recent_failures, disabled_until)|
+ create(hook_factory, **default_factory_arguments, recent_failures: recent_failures,
+disabled_until: disabled_until)
+ end
+ end
+
+ it 'finds the correct set of project hooks' do
+ expect(find_hooks.executable).to match_array executables
+ expect(find_hooks.executable).to all(be_executable)
+
+ # As expected, and consistent
+ expect(find_hooks.disabled).to match_array not_executable
+ expect(find_hooks.disabled.map(&:executable?)).not_to include(true)
+
+ # Nothing is missing
+ expect(find_hooks.executable.to_a + find_hooks.disabled.to_a).to match_array(find_hooks.to_a)
+ end
+ end
+
+ describe '#executable?', :freeze_time do
+ let(:web_hook) { create(hook_factory, **default_factory_arguments) }
+
+ where(:recent_failures, :not_until, :executable) do
+ [
+ [0, :not_set, true],
+ [0, :past, true],
+ [0, :future, true],
+ [0, :now, true],
+ [1, :not_set, true],
+ [1, :past, true],
+ [1, :future, true],
+ [3, :not_set, true],
+ [3, :past, true],
+ [3, :future, true],
+ [4, :not_set, false],
+ [4, :past, true], # expired suspension
+ [4, :now, false], # active suspension
+ [4, :future, false] # active suspension
+ ]
+ end
+
+ with_them do
+ # Phasing means we cannot put these values in the where block,
+ # which is not subject to the frozen time context.
+ let(:disabled_until) do
+ case not_until
+ when :not_set
+ nil
+ when :past
+ 1.minute.ago
+ when :future
+ 1.minute.from_now
+ when :now
+ Time.current
+ end
+ end
+
+ before do
+ web_hook.update!(recent_failures: recent_failures, disabled_until: disabled_until)
+ end
+
+ it 'has the correct state' do
+ expect(web_hook.executable?).to eq(executable)
+ end
+ end
+ end
+
+ describe '#enable!' do
+ it 'makes a hook executable if it was marked as failed' do
+ hook.recent_failures = 1000
+
+ expect { hook.enable! }.to change { hook.executable? }.from(false).to(true)
+ end
+
+ it 'makes a hook executable if it is currently backed off' do
+ hook.recent_failures = 1000
+ hook.disabled_until = 1.hour.from_now
+
+ expect { hook.enable! }.to change { hook.executable? }.from(false).to(true)
+ end
+
+ it 'does not update hooks unless necessary' do
+ hook
+
+ sql_count = ActiveRecord::QueryRecorder.new { hook.enable! }.count
+
+ expect(sql_count).to eq(0)
+ end
+
+ include_examples 'is tolerant of invalid records' do
+ def run_expectation
+ hook.recent_failures = 1000
+
+ expect { hook.enable! }.to change { hook.executable? }.from(false).to(true)
+ end
+ end
+ end
+
+ describe '#backoff!' do
+ context 'when we have not backed off before' do
+ it 'does not disable the hook' do
+ expect { hook.backoff! }.not_to change { hook.executable? }.from(true)
+ end
+ end
+
+ context 'when we have exhausted the grace period' do
+ before do
+ hook.update!(recent_failures: WebHook::FAILURE_THRESHOLD)
+ end
+
+ context 'when the hook is permanently disabled' do
+ before do
+ allow(hook).to receive(:permanently_disabled?).and_return(true)
+ end
+
+ it 'does not set disabled_until' do
+ expect { hook.backoff! }.not_to change { hook.disabled_until }
+ end
+
+ it 'does not increment the backoff count' do
+ expect { hook.backoff! }.not_to change { hook.backoff_count }
+ end
+ end
+
+ include_examples 'is tolerant of invalid records' do
+ def run_expectation
+ expect { hook.backoff! }.to change { hook.backoff_count }.by(1)
+ end
+ end
+ end
+ end
+
+ describe '#failed!' do
+ include_examples 'is tolerant of invalid records' do
+ def run_expectation
+ expect { hook.failed! }.to change { hook.recent_failures }.by(1)
+ end
+ end
+ end
+
+ describe '#disable!' do
+ it 'disables a hook' do
+ expect { hook.disable! }.to change { hook.executable? }.from(true).to(false)
+ end
+
+ it 'does nothing if the hook is already disabled' do
+ allow(hook).to receive(:permanently_disabled?).and_return(true)
+
+ sql_count = ActiveRecord::QueryRecorder.new { hook.disable! }.count
+
+ expect(sql_count).to eq(0)
+ end
+
+ include_examples 'is tolerant of invalid records' do
+ def run_expectation
+ expect { hook.disable! }.to change { hook.executable? }.from(true).to(false)
+ end
+ end
+ end
+
+ describe '#temporarily_disabled?' do
+ it 'is false when not temporarily disabled' do
+ expect(hook).not_to be_temporarily_disabled
+ end
+
+ it 'allows FAILURE_THRESHOLD initial failures before we back-off' do
+ WebHook::FAILURE_THRESHOLD.times do
+ hook.backoff!
+ expect(hook).not_to be_temporarily_disabled
+ end
+
+ hook.backoff!
+ expect(hook).to be_temporarily_disabled
+ end
+
+ context 'when hook has been told to back off' do
+ before do
+ hook.update!(recent_failures: WebHook::FAILURE_THRESHOLD)
+ hook.backoff!
+ end
+
+ it 'is true' do
+ expect(hook).to be_temporarily_disabled
+ end
+ end
+ end
+
+ describe '#permanently_disabled?' do
+ it 'is false when not disabled' do
+ expect(hook).not_to be_permanently_disabled
+ end
+
+ context 'when hook has been disabled' do
+ before do
+ hook.disable!
+ end
+
+ it 'is true' do
+ expect(hook).to be_permanently_disabled
+ end
+ end
+ end
+
+ describe '#alert_status' do
+ subject(:status) { hook.alert_status }
+
+ it { is_expected.to eq :executable }
+
+ context 'when hook has been disabled' do
+ before do
+ hook.disable!
+ end
+
+ it { is_expected.to eq :disabled }
+ end
+
+ context 'when hook has been backed off' do
+ before do
+ hook.update!(recent_failures: WebHook::FAILURE_THRESHOLD + 1)
+ hook.disabled_until = 1.hour.from_now
+ end
+
+ it { is_expected.to eq :temporarily_disabled }
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb b/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb
index f98be12523d..5755b9a56b1 100644
--- a/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb
@@ -15,7 +15,7 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes|
describe attribute do
describe '#increment_counter', :redis do
let(:amount) { 10 }
- let(:increment) { Gitlab::Counters::Increment.new(amount: amount) }
+ let(:increment) { Gitlab::Counters::Increment.new(amount: amount, ref: 3) }
let(:counter_key) { model.counter(attribute).key }
subject { model.increment_counter(attribute, increment) }
@@ -31,6 +31,7 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes|
attribute: attribute,
project_id: model.project_id,
increment: amount,
+ ref: increment.ref,
new_counter_value: 0 + amount,
current_db_value: model.read_attribute(attribute),
'correlation_id' => an_instance_of(String),
@@ -74,27 +75,36 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes|
end
describe '#bulk_increment_counter', :redis do
- let(:increments) { [Gitlab::Counters::Increment.new(amount: 10), Gitlab::Counters::Increment.new(amount: 5)] }
+ let(:increments) do
+ [
+ Gitlab::Counters::Increment.new(amount: 10, ref: 1),
+ Gitlab::Counters::Increment.new(amount: 5, ref: 2)
+ ]
+ end
+
let(:total_amount) { increments.sum(&:amount) }
let(:counter_key) { model.counter(attribute).key }
subject { model.bulk_increment_counter(attribute, increments) }
context 'when attribute is a counter attribute' do
- it 'increments the counter in Redis and logs it' do
- expect(Gitlab::AppLogger).to receive(:info).with(
- hash_including(
- message: 'Increment counter attribute',
- attribute: attribute,
- project_id: model.project_id,
- increment: total_amount,
- new_counter_value: 0 + total_amount,
- current_db_value: model.read_attribute(attribute),
- 'correlation_id' => an_instance_of(String),
- 'meta.feature_category' => 'test',
- 'meta.caller_id' => 'caller'
+ it 'increments the counter in Redis and logs each increment' do
+ increments.each do |increment|
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ hash_including(
+ message: 'Increment counter attribute',
+ attribute: attribute,
+ project_id: model.project_id,
+ increment: increment.amount,
+ ref: increment.ref,
+ new_counter_value: 0 + total_amount,
+ current_db_value: model.read_attribute(attribute),
+ 'correlation_id' => an_instance_of(String),
+ 'meta.feature_category' => 'test',
+ 'meta.caller_id' => 'caller'
+ )
)
- )
+ end
subject
@@ -104,6 +114,30 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes|
end
end
+ context 'when feature flag split_log_bulk_increment_counter is disabled' do
+ before do
+ stub_feature_flags(split_log_bulk_increment_counter: false)
+ end
+
+ it 'logs a single total increment' do
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ hash_including(
+ message: 'Increment counter attribute',
+ attribute: attribute,
+ project_id: model.project_id,
+ increment: increments.sum(&:amount),
+ new_counter_value: 0 + total_amount,
+ current_db_value: model.read_attribute(attribute),
+ 'correlation_id' => an_instance_of(String),
+ 'meta.feature_category' => 'test',
+ 'meta.caller_id' => 'caller'
+ )
+ )
+
+ subject
+ end
+ end
+
it 'does not increment the counter for the record' do
expect { subject }.not_to change { model.reset.read_attribute(attribute) }
end
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 974fc8f402a..0ef9ab25505 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
@@ -276,7 +276,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |integration_name
describe 'Push events' do
let_it_be(:user) { create(:user) }
- let_it_be_with_reload(:project) { create(:project, :repository, creator: user) }
+ let_it_be_with_refind(:project) { create(:project, :repository, creator: user) }
before do
allow(chat_integration).to receive_messages(
@@ -520,7 +520,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |integration_name
describe 'Pipeline events' do
let_it_be(:user) { create(:user) }
- let_it_be_with_reload(:project) { create(:project, :repository, creator: user) }
+ let_it_be_with_refind(:project) { create(:project, :repository, creator: user) }
let(:pipeline) do
create(:ci_pipeline,
project: project, status: status,
@@ -671,7 +671,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |integration_name
describe 'Deployment events' do
let_it_be(:user) { create(:user) }
- let_it_be_with_reload(:project) { create(:project, :repository, creator: user) }
+ let_it_be_with_refind(:project) { create(:project, :repository, creator: user) }
let_it_be(:deployment) do
create(:deployment, :success, project: project, sha: project.commit.sha, ref: project.default_branch)
diff --git a/spec/support/shared_examples/models/concerns/unstoppable_hooks_shared_examples.rb b/spec/support/shared_examples/models/concerns/unstoppable_hooks_shared_examples.rb
new file mode 100644
index 00000000000..848840ee297
--- /dev/null
+++ b/spec/support/shared_examples/models/concerns/unstoppable_hooks_shared_examples.rb
@@ -0,0 +1,177 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'a hook that does not get automatically disabled on failure' do
+ describe '.executable/.disabled', :freeze_time do
+ let!(:executables) do
+ [
+ [0, Time.current],
+ [0, 1.minute.from_now],
+ [1, 1.minute.from_now],
+ [3, 1.minute.from_now],
+ [4, nil],
+ [4, 1.day.ago],
+ [4, 1.minute.from_now],
+ [0, nil],
+ [0, 1.day.ago],
+ [1, nil],
+ [1, 1.day.ago],
+ [3, nil],
+ [3, 1.day.ago]
+ ].map do |(recent_failures, disabled_until)|
+ create(hook_factory, **default_factory_arguments, recent_failures: recent_failures,
+disabled_until: disabled_until)
+ end
+ end
+
+ it 'finds the correct set of project hooks' do
+ expect(find_hooks).to all(be_executable)
+ expect(find_hooks.executable).to match_array executables
+ expect(find_hooks.disabled).to be_empty
+ end
+ end
+
+ describe '#executable?', :freeze_time do
+ let(:web_hook) { create(hook_factory, **default_factory_arguments) }
+
+ where(:recent_failures, :not_until) do
+ [
+ [0, :not_set],
+ [0, :past],
+ [0, :future],
+ [0, :now],
+ [1, :not_set],
+ [1, :past],
+ [1, :future],
+ [3, :not_set],
+ [3, :past],
+ [3, :future],
+ [4, :not_set],
+ [4, :past], # expired suspension
+ [4, :now], # active suspension
+ [4, :future] # active suspension
+ ]
+ end
+
+ with_them do
+ # Phasing means we cannot put these values in the where block,
+ # which is not subject to the frozen time context.
+ let(:disabled_until) do
+ case not_until
+ when :not_set
+ nil
+ when :past
+ 1.minute.ago
+ when :future
+ 1.minute.from_now
+ when :now
+ Time.current
+ end
+ end
+
+ before do
+ web_hook.update!(recent_failures: recent_failures, disabled_until: disabled_until)
+ end
+
+ it 'has the correct state' do
+ expect(web_hook).to be_executable
+ end
+ end
+ end
+
+ describe '#enable!' do
+ it 'makes a hook executable if it was marked as failed' do
+ hook.recent_failures = 1000
+
+ expect { hook.enable! }.not_to change { hook.executable? }.from(true)
+ end
+
+ it 'makes a hook executable if it is currently backed off' do
+ hook.recent_failures = 1000
+ hook.disabled_until = 1.hour.from_now
+
+ expect { hook.enable! }.not_to change { hook.executable? }.from(true)
+ end
+
+ it 'does not update hooks unless necessary' do
+ hook
+
+ sql_count = ActiveRecord::QueryRecorder.new { hook.enable! }.count
+
+ expect(sql_count).to eq(0)
+ end
+ end
+
+ describe '#backoff!' do
+ context 'when we have not backed off before' do
+ it 'does not disable the hook' do
+ expect { hook.backoff! }.not_to change { hook.executable? }.from(true)
+ end
+ end
+
+ context 'when we have exhausted the grace period' do
+ before do
+ hook.update!(recent_failures: WebHook::FAILURE_THRESHOLD)
+ end
+
+ it 'does not disable the hook' do
+ expect { hook.backoff! }.not_to change { hook.executable? }.from(true)
+ end
+ end
+ end
+
+ describe '#disable!' do
+ it 'does not disable a group hook' do
+ expect { hook.disable! }.not_to change { hook.executable? }.from(true)
+ end
+ end
+
+ describe '#temporarily_disabled?' do
+ it 'is false' do
+ # Initially
+ expect(hook).not_to be_temporarily_disabled
+
+ # Backing off
+ WebHook::FAILURE_THRESHOLD.times do
+ hook.backoff!
+ expect(hook).not_to be_temporarily_disabled
+ end
+
+ hook.backoff!
+ expect(hook).not_to be_temporarily_disabled
+ end
+ end
+
+ describe '#permanently_disabled?' do
+ it 'is false' do
+ # Initially
+ expect(hook).not_to be_permanently_disabled
+
+ hook.disable!
+
+ expect(hook).not_to be_permanently_disabled
+ end
+ end
+
+ describe '#alert_status' do
+ subject(:status) { hook.alert_status }
+
+ it { is_expected.to eq :executable }
+
+ context 'when hook has been disabled' do
+ before do
+ hook.disable!
+ end
+
+ it { is_expected.to eq :executable }
+ end
+
+ context 'when hook has been backed off' do
+ before do
+ hook.update!(recent_failures: WebHook::FAILURE_THRESHOLD + 1)
+ hook.disabled_until = 1.hour.from_now
+ end
+
+ it { is_expected.to eq :executable }
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/concerns/web_hooks/has_web_hooks_shared_examples.rb b/spec/support/shared_examples/models/concerns/web_hooks/has_web_hooks_shared_examples.rb
new file mode 100644
index 00000000000..cd6eb8c77fa
--- /dev/null
+++ b/spec/support/shared_examples/models/concerns/web_hooks/has_web_hooks_shared_examples.rb
@@ -0,0 +1,107 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'something that has web-hooks' do
+ describe '#any_hook_failed?', :clean_gitlab_redis_shared_state do
+ subject { object.any_hook_failed? }
+
+ context 'when there are no hooks' do
+ it { is_expected.to eq(false) }
+ end
+
+ context 'when there are hooks' do
+ before do
+ create_hook
+ create_hook
+ end
+
+ it { is_expected.to eq(false) }
+
+ context 'when there is a failed hook' do
+ before do
+ hook = create_hook
+ hook.update!(recent_failures: WebHook::FAILURE_THRESHOLD + 1)
+ end
+
+ it { is_expected.to eq(true) }
+ end
+ end
+ end
+
+ describe '#cache_web_hook_failure', :clean_gitlab_redis_shared_state do
+ context 'when no value is passed' do
+ it 'stores the return value of #any_hook_failed? and passes it back' do
+ allow(object).to receive(:any_hook_failed?).and_return(true)
+
+ Gitlab::Redis::SharedState.with do |r|
+ expect(r).to receive(:set)
+ .with(object.web_hook_failure_redis_key, 'true', ex: 1.hour)
+ .and_call_original
+ end
+
+ expect(object.cache_web_hook_failure).to eq(true)
+ end
+ end
+
+ context 'when a value is passed' do
+ it 'stores the value and passes it back' do
+ expect(object).not_to receive(:any_hook_failed?)
+
+ Gitlab::Redis::SharedState.with do |r|
+ expect(r).to receive(:set)
+ .with(object.web_hook_failure_redis_key, 'foo', ex: 1.hour)
+ .and_call_original
+ end
+
+ expect(object.cache_web_hook_failure(:foo)).to eq(:foo)
+ end
+ end
+ end
+
+ describe '#get_web_hook_failure', :clean_gitlab_redis_shared_state do
+ subject { object.get_web_hook_failure }
+
+ context 'when no value is stored' do
+ it { is_expected.to be_nil }
+ end
+
+ context 'when stored as true' do
+ before do
+ object.cache_web_hook_failure(true)
+ end
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'when stored as false' do
+ before do
+ object.cache_web_hook_failure(false)
+ end
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ describe '#fetch_web_hook_failure', :clean_gitlab_redis_shared_state do
+ context 'when a value has not been stored' do
+ it 'does not call #any_hook_failed?' do
+ expect(object.get_web_hook_failure).to be_nil
+ expect(object).to receive(:any_hook_failed?).and_return(true)
+
+ expect(object.fetch_web_hook_failure).to eq(true)
+ expect(object.get_web_hook_failure).to eq(true)
+ end
+ end
+
+ context 'when a value has been stored' do
+ before do
+ object.cache_web_hook_failure(true)
+ end
+
+ it 'does not call #any_hook_failed?' do
+ expect(object).not_to receive(:any_hook_failed?)
+
+ expect(object.fetch_web_hook_failure).to eq(true)
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/exportable_shared_examples.rb b/spec/support/shared_examples/models/exportable_shared_examples.rb
new file mode 100644
index 00000000000..37c3e68fd5f
--- /dev/null
+++ b/spec/support/shared_examples/models/exportable_shared_examples.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'resource with exportable associations' do
+ before do
+ stub_licensed_features(stubbed_features) if stubbed_features.any?
+ end
+
+ describe '#exportable_association?' do
+ let(:association) { single_association }
+
+ subject { resource.exportable_association?(association, current_user: user) }
+
+ it { is_expected.to be_falsey }
+
+ context 'when user can read resource' do
+ before do
+ group.add_developer(user)
+ end
+
+ it { is_expected.to be_falsey }
+
+ context "when user can read resource's association" do
+ before do
+ other_group.add_developer(user)
+ end
+
+ it { is_expected.to be_truthy }
+
+ context 'for an unknown association' do
+ let(:association) { :foo }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'for an unauthenticated user' do
+ let(:user) { nil }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+ end
+ end
+
+ describe '#readable_records' do
+ subject { resource.readable_records(association, current_user: user) }
+
+ before do
+ group.add_developer(user)
+ end
+
+ context 'when association not supported' do
+ let(:association) { :foo }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when association is `:notes`' do
+ let(:association) { :notes }
+
+ it { is_expected.to match_array([readable_note]) }
+
+ context 'when user have access' do
+ before do
+ other_group.add_developer(user)
+ end
+
+ it 'returns all records' do
+ is_expected.to match_array([readable_note, restricted_note])
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
index 3d7d97bbeae..ac4ad4525aa 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
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze|
- let_it_be(:distribution_with_suite, freeze: can_freeze) { create(factory, suite: 'mysuite') }
+ let_it_be(:distribution_with_suite, freeze: can_freeze) { create(factory, :with_suite) }
let_it_be(:distribution_with_same_container, freeze: can_freeze) { create(factory, container: distribution_with_suite.container ) }
let_it_be(:distribution_with_same_codename, freeze: can_freeze) { create(factory, codename: distribution_with_suite.codename ) }
let_it_be(:distribution_with_same_suite, freeze: can_freeze) { create(factory, suite: distribution_with_suite.suite ) }
diff --git a/spec/support/shared_examples/models/resource_event_shared_examples.rb b/spec/support/shared_examples/models/resource_event_shared_examples.rb
index 8cab2de076d..038ff33c68a 100644
--- a/spec/support/shared_examples/models/resource_event_shared_examples.rb
+++ b/spec/support/shared_examples/models/resource_event_shared_examples.rb
@@ -160,6 +160,16 @@ RSpec.shared_examples 'a resource event for merge requests' do
end
end
end
+
+ context 'on callbacks' do
+ it 'does not trigger note created subscription' do
+ event = build(described_class.name.underscore.to_sym, merge_request: merge_request1)
+
+ expect(GraphqlTriggers).not_to receive(:work_item_note_created)
+ expect(event).not_to receive(:trigger_note_subscription_create)
+ event.save!
+ end
+ end
end
RSpec.shared_examples 'a note for work item resource event' do
@@ -172,4 +182,14 @@ RSpec.shared_examples 'a note for work item resource event' do
expect(event.work_item_synthetic_system_note.class.name).to eq(event.synthetic_note_class.name)
end
+
+ context 'on callbacks' do
+ it 'triggers note created subscription' do
+ event = build(described_class.name.underscore.to_sym, issue: work_item)
+
+ expect(GraphqlTriggers).to receive(:work_item_note_created)
+ expect(event).to receive(:trigger_note_subscription_create).and_call_original
+ event.save!
+ end
+ end
end
diff --git a/spec/support/shared_examples/namespaces/traversal_examples.rb b/spec/support/shared_examples/namespaces/traversal_examples.rb
index 73e22b97abc..600539f7d0a 100644
--- a/spec/support/shared_examples/namespaces/traversal_examples.rb
+++ b/spec/support/shared_examples/namespaces/traversal_examples.rb
@@ -57,11 +57,6 @@ RSpec.shared_examples 'namespace traversal' do
end
describe '#ancestors' do
- before do
- # #reload is called to make sure traversal_ids are reloaded
- reload_models(group, nested_group, deep_nested_group, very_deep_nested_group)
- end
-
it 'returns the correct ancestors' do
expect(very_deep_nested_group.ancestors).to contain_exactly(group, nested_group, deep_nested_group)
expect(deep_nested_group.ancestors).to contain_exactly(group, nested_group)
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 15d56c402d1..9ec1b8b3f5a 100644
--- a/spec/support/shared_examples/policies/project_policy_shared_examples.rb
+++ b/spec/support/shared_examples/policies/project_policy_shared_examples.rb
@@ -333,6 +333,7 @@ RSpec.shared_examples 'project policies as admin with admin mode' do
expect_disallowed(*team_member_reporter_permissions)
expect_allowed(*developer_permissions)
expect_allowed(*maintainer_permissions)
+ expect_allowed(*admin_permissions)
expect_allowed(*owner_permissions)
end
@@ -354,6 +355,7 @@ RSpec.shared_examples 'project policies as admin with admin mode' do
expect_disallowed(*team_member_reporter_permissions)
expect_disallowed(*developer_permissions)
expect_disallowed(*maintainer_permissions)
+ expect_disallowed(*admin_permissions)
expect_disallowed(*owner_permissions)
end
end
diff --git a/spec/support/shared_examples/quick_actions/issuable/max_issuable_examples.rb b/spec/support/shared_examples/quick_actions/issuable/max_issuable_examples.rb
index f5431b29ee2..b5704ad8f17 100644
--- a/spec/support/shared_examples/quick_actions/issuable/max_issuable_examples.rb
+++ b/spec/support/shared_examples/quick_actions/issuable/max_issuable_examples.rb
@@ -54,43 +54,3 @@ RSpec.shared_examples 'does not exceed the issuable size limit' do
end
end
end
-
-RSpec.shared_examples 'does not exceed the issuable size limit with ff off' do
- let(:user1) { create(:user) }
- let(:user2) { create(:user) }
- let(:user3) { create(:user) }
-
- before do
- project.add_maintainer(user)
- project.add_maintainer(user1)
- project.add_maintainer(user2)
- project.add_maintainer(user3)
- end
-
- context 'when feature flag is off' do
- before do
- stub_feature_flags(feature_flag_hash)
- end
-
- context "when the number of users of issuable does exceed the limit" do
- before do
- stub_const("Issuable::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS", 2)
- end
-
- it 'will not add more than the allowed number of users' do
- allow_next_instance_of(MergeRequests::UpdateService) do |service|
- expect(service).to receive(:execute).and_call_original
- end
-
- note = described_class.new(project, user, opts.merge(
- note: note_text,
- noteable_type: 'MergeRequest',
- noteable_id: issuable.id,
- confidential: false
- )).execute
-
- expect(note.errors[:validation]).to be_empty
- end
- end
- end
-end
diff --git a/spec/support/shared_examples/requests/admin_mode_shared_examples.rb b/spec/support/shared_examples/requests/admin_mode_shared_examples.rb
new file mode 100644
index 00000000000..07fde7d3f35
--- /dev/null
+++ b/spec/support/shared_examples/requests/admin_mode_shared_examples.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+RSpec.shared_examples 'GET request permissions for admin mode' do
+ it_behaves_like 'GET request permissions for admin mode when user'
+ it_behaves_like 'GET request permissions for admin mode when admin'
+end
+
+RSpec.shared_examples 'PUT request permissions for admin mode' do |params|
+ it_behaves_like 'PUT request permissions for admin mode when user', params
+ it_behaves_like 'PUT request permissions for admin mode when admin', params
+end
+
+RSpec.shared_examples 'POST request permissions for admin mode' do |params|
+ it_behaves_like 'POST request permissions for admin mode when user', params
+ it_behaves_like 'POST request permissions for admin mode when admin', params
+end
+
+RSpec.shared_examples 'DELETE request permissions for admin mode' do
+ it_behaves_like 'DELETE request permissions for admin mode when user'
+ it_behaves_like 'DELETE request permissions for admin mode when admin'
+end
+
+RSpec.shared_examples 'GET request permissions for admin mode when user' do
+ subject { get api(path, current_user, admin_mode: admin_mode) }
+
+ let_it_be(:current_user) { create(:user) }
+
+ it_behaves_like 'admin mode on', true, :forbidden
+ it_behaves_like 'admin mode on', false, :forbidden
+end
+
+RSpec.shared_examples 'GET request permissions for admin mode when admin' do
+ subject { get api(path, current_user, admin_mode: admin_mode) }
+
+ let_it_be(:current_user) { create(:admin) }
+
+ it_behaves_like 'admin mode on', true, :ok
+ it_behaves_like 'admin mode on', false, :forbidden
+end
+
+RSpec.shared_examples 'PUT request permissions for admin mode when user' do |params|
+ subject { put api(path, current_user, admin_mode: admin_mode), params: params }
+
+ let_it_be(:current_user) { create(:user) }
+
+ it_behaves_like 'admin mode on', true, :forbidden
+ it_behaves_like 'admin mode on', false, :forbidden
+end
+
+RSpec.shared_examples 'PUT request permissions for admin mode when admin' do |params|
+ subject { put api(path, current_user, admin_mode: admin_mode), params: params }
+
+ let_it_be(:current_user) { create(:admin) }
+
+ it_behaves_like 'admin mode on', true, :ok
+ it_behaves_like 'admin mode on', false, :forbidden
+end
+
+RSpec.shared_examples 'POST request permissions for admin mode when user' do |params|
+ subject { post api(path, current_user, admin_mode: admin_mode), params: params }
+
+ let_it_be(:current_user) { create(:user) }
+
+ it_behaves_like 'admin mode on', true, :forbidden
+ it_behaves_like 'admin mode on', false, :forbidden
+end
+
+RSpec.shared_examples 'POST request permissions for admin mode when admin' do |params|
+ subject { post api(path, current_user, admin_mode: admin_mode), params: params }
+
+ let_it_be(:current_user) { create(:admin) }
+
+ it_behaves_like 'admin mode on', true, :created
+ it_behaves_like 'admin mode on', false, :forbidden
+end
+
+RSpec.shared_examples 'DELETE request permissions for admin mode when user' do
+ subject { delete api(path, current_user, admin_mode: admin_mode) }
+
+ let_it_be(:current_user) { create(:user) }
+
+ it_behaves_like 'admin mode on', true, :forbidden
+ it_behaves_like 'admin mode on', false, :forbidden
+end
+
+RSpec.shared_examples 'DELETE request permissions for admin mode when admin' do
+ subject { delete api(path, current_user, admin_mode: admin_mode) }
+
+ let_it_be(:current_user) { create(:admin) }
+
+ it_behaves_like 'admin mode on', true, :no_content
+ it_behaves_like 'admin mode on', false, :forbidden
+end
+
+RSpec.shared_examples "admin mode on" do |admin_mode, status|
+ let_it_be(:admin_mode) { admin_mode }
+
+ it_behaves_like 'returning response status', status
+end
diff --git a/spec/support/shared_examples/requests/api/debian_common_shared_examples.rb b/spec/support/shared_examples/requests/api/debian_common_shared_examples.rb
index 2ba42b8e8fa..e0225070986 100644
--- a/spec/support/shared_examples/requests/api/debian_common_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/debian_common_shared_examples.rb
@@ -15,9 +15,3 @@ RSpec.shared_examples 'rejects Debian access with unknown container id' do |anon
end
end
end
-
-RSpec.shared_examples 'Debian API FIPS mode' do
- context 'when FIPS mode is enabled', :fips_mode do
- it_behaves_like 'returning response status', :not_found
- end
-end
diff --git a/spec/support/shared_examples/requests/api/debian_distributions_shared_examples.rb b/spec/support/shared_examples/requests/api/debian_distributions_shared_examples.rb
index f13ac05591c..5cd63c33936 100644
--- a/spec/support/shared_examples/requests/api/debian_distributions_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/debian_distributions_shared_examples.rb
@@ -3,8 +3,6 @@
RSpec.shared_examples 'Debian distributions GET request' do |status, body = nil|
and_body = body.nil? ? '' : ' and expected body'
- it_behaves_like 'Debian API FIPS mode'
-
it "returns #{status}#{and_body}" do
subject
@@ -19,8 +17,6 @@ end
RSpec.shared_examples 'Debian distributions PUT request' do |status, body|
and_body = body.nil? ? '' : ' and expected body'
- it_behaves_like 'Debian API FIPS mode'
-
if status == :success
it 'updates distribution', :aggregate_failures do
expect(::Packages::Debian::UpdateDistributionService).to receive(:new).with(distribution, api_params.except(:codename)).and_call_original
@@ -53,8 +49,6 @@ end
RSpec.shared_examples 'Debian distributions DELETE request' do |status, body|
and_body = body.nil? ? '' : ' and expected body'
- it_behaves_like 'Debian API FIPS mode'
-
if status == :success
it 'updates distribution', :aggregate_failures do
expect { subject }
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 14a83d2889b..6d29076da0f 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
@@ -3,8 +3,6 @@
RSpec.shared_examples 'Debian packages GET request' do |status, body = nil|
and_body = body.nil? ? '' : ' and expected body'
- it_behaves_like 'Debian API FIPS mode'
-
it "returns #{status}#{and_body}" do
subject
@@ -19,11 +17,8 @@ end
RSpec.shared_examples 'Debian packages upload request' do |status, body = nil|
and_body = body.nil? ? '' : ' and expected body'
- it_behaves_like 'Debian API FIPS mode'
-
if status == :created
it 'creates package files', :aggregate_failures do
- expect(::Packages::Debian::FindOrCreateIncomingService).to receive(:new).with(container, user).and_call_original
expect(::Packages::Debian::CreatePackageFileService).to receive(:new).with(package: be_a(Packages::Package), current_user: be_an(User), params: be_an(Hash)).and_call_original
if file_name.end_with? '.changes'
@@ -32,10 +27,23 @@ RSpec.shared_examples 'Debian packages upload request' do |status, body = nil|
expect(::Packages::Debian::ProcessChangesWorker).not_to receive(:perform_async)
end
- expect { subject }
+ if extra_params[:distribution]
+ expect(::Packages::Debian::FindOrCreateIncomingService).not_to receive(:new)
+ expect(::Packages::Debian::ProcessPackageFileWorker).to receive(:perform_async)
+
+ expect { subject }
+ .to change { container.packages.debian.count }.by(1)
+ .and not_change { container.packages.debian.where(name: 'incoming').count }
+ .and change { container.package_files.count }.by(1)
+ else
+ expect(::Packages::Debian::FindOrCreateIncomingService).to receive(:new).with(container, user).and_call_original
+ expect(::Packages::Debian::ProcessPackageFileWorker).not_to receive(:perform_async)
+
+ expect { subject }
.to change { container.packages.debian.count }.by(1)
.and change { container.packages.debian.where(name: 'incoming').count }.by(1)
.and change { container.package_files.count }.by(1)
+ end
expect(response).to have_gitlab_http_status(status)
expect(response.media_type).to eq('text/plain')
diff --git a/spec/support/shared_examples/requests/api/graphql/ci/sorted_paginated_variables_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/ci/sorted_paginated_variables_shared_examples.rb
new file mode 100644
index 00000000000..306310c9e9c
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/graphql/ci/sorted_paginated_variables_shared_examples.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+# Requires `current_user`, `data_path`, `variables`, and `pagination_query(params)` bindings
+RSpec.shared_examples 'sorted paginated variables' do
+ subject(:expected_ordered_variables) { ordered_variables.map { |var| var.to_global_id.to_s } }
+
+ context 'when sorted by key ascending' do
+ let(:ordered_variables) { variables.sort_by(&:key) }
+
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :KEY_ASC }
+ let(:first_param) { 2 }
+ let(:all_records) { expected_ordered_variables }
+ end
+ end
+
+ context 'when sorted by key descending' do
+ let(:ordered_variables) { variables.sort_by(&:key).reverse }
+
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :KEY_DESC }
+ let(:first_param) { 2 }
+ let(:all_records) { expected_ordered_variables }
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb
index d4af9e570d1..6c8b792bf92 100644
--- a/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb
@@ -5,7 +5,7 @@ RSpec.shared_examples 'graphql issue list request spec' do
let(:fields) do
<<~QUERY
nodes {
- #{all_graphql_fields_for('issues'.classify)}
+ #{all_graphql_fields_for('issues'.classify, excluded: ['relatedMergeRequests'])}
}
QUERY
end
@@ -683,6 +683,28 @@ RSpec.shared_examples 'graphql issue list request spec' do
end
end
+ context 'when selecting `related_merge_requests`' do
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ relatedMergeRequests {
+ nodes {
+ id
+ }
+ }
+ }
+ QUERY
+ end
+
+ it 'limits the field to 1 execution' do
+ post_query
+
+ expect_graphql_errors_to_include(
+ '"relatedMergeRequests" field can be requested only for 1 Issue(s) at a time.'
+ )
+ end
+ end
+
it 'includes a web_url' do
post_query
diff --git a/spec/support/shared_examples/requests/api/graphql/releases_and_group_releases_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/releases_and_group_releases_shared_examples.rb
new file mode 100644
index 00000000000..b40cf6daea9
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/graphql/releases_and_group_releases_shared_examples.rb
@@ -0,0 +1,164 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'correct total count' do
+ let(:data) { graphql_data.dig(resource_type.to_s, 'releases') }
+
+ before do
+ create_list(:release, 2, project: project)
+
+ post_query
+ end
+
+ it 'returns the total count' do
+ expect(data['count']).to eq(project.releases.count)
+ end
+end
+
+RSpec.shared_examples 'full access to all repository-related fields' do
+ describe 'repository-related fields' do
+ before do
+ post_query
+ end
+
+ it 'returns data for fields that are protected in private projects' do
+ expected_sources = release.sources.map do |s|
+ { 'url' => s.url }
+ end
+
+ expected_evidences = release.evidences.map do |e|
+ { 'sha' => e.sha }
+ end
+ expect(data).to eq(
+ 'tagName' => release.tag,
+ 'tagPath' => project_tag_path(project, release.tag),
+ 'name' => release.name,
+ 'commit' => {
+ 'sha' => release.commit.sha
+ },
+ 'assets' => {
+ 'count' => release.assets_count,
+ 'sources' => {
+ 'nodes' => expected_sources
+ }
+ },
+ 'evidences' => {
+ 'nodes' => expected_evidences
+ },
+ 'links' => {
+ 'selfUrl' => project_release_url(project, release),
+ 'openedMergeRequestsUrl' => project_merge_requests_url(project, opened_url_params),
+ 'mergedMergeRequestsUrl' => project_merge_requests_url(project, merged_url_params),
+ 'closedMergeRequestsUrl' => project_merge_requests_url(project, closed_url_params),
+ 'openedIssuesUrl' => project_issues_url(project, opened_url_params),
+ 'closedIssuesUrl' => project_issues_url(project, closed_url_params)
+ }
+ )
+ end
+ end
+
+ it_behaves_like 'correct total count'
+end
+
+RSpec.shared_examples 'no access to any repository-related fields' do
+ describe 'repository-related fields' do
+ before do
+ post_query
+ end
+
+ it 'does not return data for fields that expose repository information' do
+ tag_name = release.tag
+ release_name = release.name
+ expect(data).to eq(
+ 'tagName' => tag_name,
+ 'tagPath' => nil,
+ 'name' => release_name,
+ 'commit' => nil,
+ 'assets' => {
+ 'count' => release.assets_count(except: [:sources]),
+ 'sources' => {
+ 'nodes' => []
+ }
+ },
+ 'evidences' => {
+ 'nodes' => []
+ },
+ 'links' => {
+ 'closedIssuesUrl' => nil,
+ 'closedMergeRequestsUrl' => nil,
+ 'mergedMergeRequestsUrl' => nil,
+ 'openedIssuesUrl' => nil,
+ 'openedMergeRequestsUrl' => nil,
+ 'selfUrl' => project_release_url(project, release)
+ }
+ )
+ end
+ end
+
+ it_behaves_like 'correct total count'
+end
+
+RSpec.shared_examples 'access to editUrl' do
+ # editUrl is tested separately because its permissions
+ # are slightly different than other release fields
+ let(:query) do
+ graphql_query_for(resource_type, { fullPath: resource.full_path },
+ %(
+ releases {
+ nodes {
+ links {
+ editUrl
+ }
+ }
+ }
+ ))
+ end
+
+ before do
+ post_query
+ end
+
+ it 'returns editUrl' do
+ expect(data).to eq(
+ 'links' => {
+ 'editUrl' => edit_project_release_url(project, release)
+ }
+ )
+ end
+end
+
+RSpec.shared_examples 'no access to editUrl' do
+ let(:query) do
+ graphql_query_for(resource_type, { fullPath: resource.full_path },
+ %(
+ releases {
+ nodes {
+ links {
+ editUrl
+ }
+ }
+ }
+ ))
+ end
+
+ before do
+ post_query
+ end
+
+ it 'does not return editUrl' do
+ expect(data).to eq(
+ 'links' => {
+ 'editUrl' => nil
+ }
+ )
+ end
+end
+
+RSpec.shared_examples 'no access to any release data' do
+ before do
+ post_query
+ end
+
+ it 'returns nil' do
+ expect(data).to eq(nil)
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/hooks_shared_examples.rb b/spec/support/shared_examples/requests/api/hooks_shared_examples.rb
index d666a754d9f..f2002de4b55 100644
--- a/spec/support/shared_examples/requests/api/hooks_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/hooks_shared_examples.rb
@@ -128,7 +128,8 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
get api(hook_uri, user)
expect(response).to have_gitlab_http_status(:ok)
- expect(json_response).to include('alert_status' => 'disabled')
+
+ expect(json_response).to include('alert_status' => 'disabled') unless hook.executable?
end
end
@@ -142,10 +143,13 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
get api(hook_uri, user)
expect(response).to have_gitlab_http_status(:ok)
- expect(json_response).to include(
- 'alert_status' => 'temporarily_disabled',
- 'disabled_until' => hook.disabled_until.iso8601(3)
- )
+
+ unless hook.executable?
+ expect(json_response).to include(
+ 'alert_status' => 'temporarily_disabled',
+ 'disabled_until' => hook.disabled_until.iso8601(3)
+ )
+ end
end
end
end
diff --git a/spec/support/shared_examples/requests/api/issuable_search_shared_examples.rb b/spec/support/shared_examples/requests/api/issuable_search_shared_examples.rb
index fcde3b65b4f..f06a80375e8 100644
--- a/spec/support/shared_examples/requests/api/issuable_search_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/issuable_search_shared_examples.rb
@@ -1,40 +1,5 @@
# frozen_string_literal: true
-RSpec.shared_examples 'issuable anonymous search' do
- context 'with anonymous user' do
- context 'with disable_anonymous_search disabled' do
- before do
- stub_feature_flags(disable_anonymous_search: false)
- end
-
- it 'returns issuables matching given search string for title' do
- get api(url), params: { scope: 'all', search: issuable.title }
-
- expect_paginated_array_response(result)
- end
-
- it 'returns issuables matching given search string for description' do
- get api(url), params: { scope: 'all', search: issuable.description }
-
- expect_paginated_array_response(result)
- end
- end
-
- context 'with disable_anonymous_search enabled' do
- before do
- stub_feature_flags(disable_anonymous_search: true)
- end
-
- it "returns 422 error" do
- get api(url), params: { scope: 'all', search: issuable.title }
-
- expect(response).to have_gitlab_http_status(:unprocessable_entity)
- expect(json_response['message']).to eq('User must be authenticated to use search')
- end
- end
- end
-end
-
RSpec.shared_examples 'issuable API rate-limited search' do
it_behaves_like 'rate limited endpoint', rate_limit_key: :search_rate_limit do
let(:current_user) { user }
@@ -49,20 +14,4 @@ RSpec.shared_examples 'issuable API rate-limited search' do
get api(url), params: { scope: 'all', search: issuable.title }
end
end
-
- context 'when rate_limit_issuable_searches is disabled', :freeze_time, :clean_gitlab_redis_rate_limiting do
- before do
- stub_feature_flags(rate_limit_issuable_searches: false)
-
- allow(Gitlab::ApplicationRateLimiter).to receive(:threshold)
- .with(:search_rate_limit_unauthenticated).and_return(1)
- end
-
- it 'does not enforce the rate limit' do
- get api(url), params: { scope: 'all', search: issuable.title }
- get api(url), params: { scope: 'all', search: issuable.title }
-
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
end
diff --git a/spec/support/shared_examples/requests/api/time_tracking_shared_examples.rb b/spec/support/shared_examples/requests/api/time_tracking_shared_examples.rb
index 381583ff2a9..86a1fd76d09 100644
--- a/spec/support/shared_examples/requests/api/time_tracking_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/time_tracking_shared_examples.rb
@@ -142,7 +142,7 @@ RSpec.shared_examples 'time tracking endpoints' do |issuable_name|
if issuable_name == 'issue'
it 'calls update service without :use_specialized_service param' do
expect(::Issues::UpdateService).to receive(:new).with(
- project: project,
+ container: project,
current_user: user,
params: { spend_time: { duration: 3600, summary: 'summary', user_id: user.id } })
diff --git a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
index 82ed6eb4c95..3f457890f35 100644
--- a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
+++ b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
@@ -382,7 +382,7 @@ RSpec.shared_examples 'tracking when dry-run mode is set' do
end
def reset_rack_attack
- Rack::Attack.reset!
+ Gitlab::Redis::RateLimiting.with(&:flushdb)
Rack::Attack.clear_configuration
Gitlab::RackAttack.configure(Rack::Attack)
end
diff --git a/spec/support/shared_examples/serializers/note_entity_shared_examples.rb b/spec/support/shared_examples/serializers/note_entity_shared_examples.rb
index 2e557ca090c..b5e3a407b53 100644
--- a/spec/support/shared_examples/serializers/note_entity_shared_examples.rb
+++ b/spec/support/shared_examples/serializers/note_entity_shared_examples.rb
@@ -38,6 +38,10 @@ RSpec.shared_examples 'note entity' do
expect(subject[:current_user]).to include(:can_edit, :can_award_emoji, :can_resolve, :can_resolve_discussion)
end
+ it 'exposes the report_abuse_path' do
+ expect(subject[:report_abuse_path]).to eq(add_category_abuse_reports_path)
+ end
+
describe ':can_resolve_discussion' do
context 'discussion is resolvable' do
before do
diff --git a/spec/support/shared_examples/services/export_csv/export_csv_invalid_fields_shared_examples.rb b/spec/support/shared_examples/services/export_csv/export_csv_invalid_fields_shared_examples.rb
new file mode 100644
index 00000000000..25899f93914
--- /dev/null
+++ b/spec/support/shared_examples/services/export_csv/export_csv_invalid_fields_shared_examples.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'a service that returns invalid fields from selection' do
+ describe '#invalid_fields' do
+ it 'returns invalid fields from selection' do
+ fields = %w[title invalid_1 invalid_2]
+
+ service = described_class.new(WorkItem.all, project, fields)
+
+ expect(service.invalid_fields).to eq(%w[invalid_1 invalid_2])
+ end
+ end
+end
diff --git a/spec/support/shared_examples/services/issuable/discussions_list_shared_examples.rb b/spec/support/shared_examples/services/issuable/discussions_list_shared_examples.rb
index c38ca6a3bf0..4188c4aa56c 100644
--- a/spec/support/shared_examples/services/issuable/discussions_list_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable/discussions_list_shared_examples.rb
@@ -26,7 +26,9 @@ RSpec.shared_examples 'listing issuable discussions' do |user_role, internal_dis
discussions = next_page_discussions_service.execute
expect(discussions.count).to eq(2)
- expect(discussions.first.notes.map(&:note)).to match_array(["added #{label.to_reference} label"])
+ expect(discussions.first.notes.map(&:note)).to match_array(
+ ["added #{label.to_reference} #{label_2.to_reference} labels"]
+ )
expect(discussions.second.notes.map(&:note)).to match_array(["removed #{label.to_reference} label"])
end
end
@@ -93,7 +95,15 @@ def create_notes(issuable, note_body)
discussion_id: first_discussion.discussion_id, noteable: issuable,
project: issuable.project, note: "reply on #{note_body}")
- create(:resource_label_event, user: current_user, "#{assoc_name}": issuable, label: label, action: 'add')
+ now = Time.current
+ create(
+ :resource_label_event,
+ user: current_user, "#{assoc_name}": issuable, label: label, action: 'add', created_at: now
+ )
+ create(
+ :resource_label_event,
+ user: current_user, "#{assoc_name}": issuable, label: label_2, action: 'add', created_at: now
+ )
create(:resource_label_event, user: current_user, "#{assoc_name}": issuable, label: label, action: 'remove')
unless issuable.is_a?(Epic)
diff --git a/spec/support/shared_examples/services/issuable_shared_examples.rb b/spec/support/shared_examples/services/issuable_shared_examples.rb
index 142d4ae8531..fe868d494d2 100644
--- a/spec/support/shared_examples/services/issuable_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable_shared_examples.rb
@@ -11,7 +11,8 @@ end
RSpec.shared_examples 'updating a single task' do
def update_issuable(opts)
issuable = try(:issue) || try(:merge_request)
- described_class.new(project: project, current_user: user, params: opts).execute(issuable)
+ described_class.new(**described_class.constructor_container_arg(project), current_user: user, params: opts)
+ .execute(issuable)
end
before do
diff --git a/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb b/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb
index 4df12f7849b..bdb01b12607 100644
--- a/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb
+++ b/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb
@@ -12,27 +12,20 @@ RSpec.shared_examples 'misconfigured dashboard service response' do |status_code
end
RSpec.shared_examples 'valid dashboard service response for schema' do
- file_ref_resolver = proc do |uri|
- file = Rails.root.join(uri.path)
- raise StandardError, "Ref file #{uri.path} must be json" unless uri.path.ends_with?('.json')
- raise StandardError, "File #{file.to_path} doesn't exists" unless file.exist?
-
- Gitlab::Json.parse(File.read(file))
- end
-
it 'returns a json representation of the dashboard' do
result = service_call
expect(result.keys).to contain_exactly(:dashboard, :status)
expect(result[:status]).to eq(:success)
- validator = JSONSchemer.schema(dashboard_schema, ref_resolver: file_ref_resolver)
+ schema_path = Rails.root.join('spec/fixtures', dashboard_schema)
+ validator = JSONSchemer.schema(schema_path)
expect(validator.valid?(result[:dashboard].with_indifferent_access)).to be true
end
end
RSpec.shared_examples 'valid dashboard service response' do
- let(:dashboard_schema) { Gitlab::Json.parse(fixture_file('lib/gitlab/metrics/dashboard/schemas/dashboard.json')) }
+ let(:dashboard_schema) { 'lib/gitlab/metrics/dashboard/schemas/dashboard.json' }
it_behaves_like 'valid dashboard service response for schema'
end
@@ -76,7 +69,7 @@ RSpec.shared_examples 'dashboard_version contains SHA256 hash of dashboard file
end
RSpec.shared_examples 'valid embedded dashboard service response' do
- let(:dashboard_schema) { Gitlab::Json.parse(fixture_file('lib/gitlab/metrics/dashboard/schemas/embedded_dashboard.json')) }
+ let(:dashboard_schema) { 'lib/gitlab/metrics/dashboard/schemas/embedded_dashboard.json' }
it_behaves_like 'valid dashboard service response for schema'
end
diff --git a/spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb b/spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb
index ea79dc674a1..a3042ac2e26 100644
--- a/spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb
+++ b/spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb
@@ -4,6 +4,8 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
def check_release_files(expected_release_content)
distribution.reload
+ expect(expected_release_content).not_to include('MD5')
+
distribution.file.use_file do |file_path|
expect(File.read(file_path)).to eq(expected_release_content)
end
@@ -12,8 +14,10 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
expect(distribution.file_signature).to end_with("\n-----END PGP SIGNATURE-----\n")
distribution.signed_file.use_file do |file_path|
- expect(File.read(file_path)).to start_with("-----BEGIN PGP SIGNED MESSAGE-----\nHash: SHA512\n\n#{expected_release_content}-----BEGIN PGP SIGNATURE-----\n")
- expect(File.read(file_path)).to end_with("\n-----END PGP SIGNATURE-----\n")
+ signed_file_content = File.read(file_path)
+ expect(signed_file_content).to start_with("-----BEGIN PGP SIGNED MESSAGE-----\nHash:")
+ expect(signed_file_content).to include("\n\n#{expected_release_content}-----BEGIN PGP SIGNATURE-----\n")
+ expect(signed_file_content).to end_with("\n-----END PGP SIGNATURE-----\n")
end
end
@@ -45,6 +49,7 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
expect(component_file.updated_at).to eq(release_date)
unless expected_content.nil?
+ expect(expected_content).not_to include('MD5')
component_file.file.use_file do |file_path|
expect(File.read(file_path)).to eq(expected_content)
end
@@ -73,9 +78,9 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
.and change { component_file1.reload.updated_at }.to(current_time.round)
package_files = package.package_files.order(id: :asc).preload_debian_file_metadata.to_a
- pool_prefix = 'pool/unstable'
+ pool_prefix = "pool/#{distribution.codename}"
pool_prefix += "/#{project.id}" if container_type == :group
- pool_prefix += "/p/#{package.name}/#{package.version}"
+ pool_prefix += "/#{package.name[0]}/#{package.name}/#{package.version}"
expected_main_amd64_content = <<~EOF
Package: libsample0
Source: #{package.name}
@@ -93,7 +98,6 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
Priority: optional
Filename: #{pool_prefix}/libsample0_1.2.3~alpha2_amd64.deb
Size: 409600
- MD5sum: #{package_files[2].file_md5}
SHA256: #{package_files[2].file_sha256}
Package: sample-dev
@@ -113,7 +117,6 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
Priority: optional
Filename: #{pool_prefix}/sample-dev_1.2.3~binary_amd64.deb
Size: 409600
- MD5sum: #{package_files[3].file_md5}
SHA256: #{package_files[3].file_sha256}
EOF
@@ -122,7 +125,6 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
Priority: extra
Filename: #{pool_prefix}/sample-udeb_1.2.3~alpha2_amd64.udeb
Size: 409600
- MD5sum: #{package_files[4].file_md5}
SHA256: #{package_files[4].file_sha256}
EOF
@@ -171,45 +173,26 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
check_component_file(current_time.round, 'contrib', :sources, nil, nil)
main_amd64_size = expected_main_amd64_content.length
- main_amd64_md5sum = Digest::MD5.hexdigest(expected_main_amd64_content)
main_amd64_sha256 = Digest::SHA256.hexdigest(expected_main_amd64_content)
contrib_all_size = component_file1.size
- contrib_all_md5sum = component_file1.file_md5
contrib_all_sha256 = component_file1.file_sha256
main_amd64_di_size = expected_main_amd64_di_content.length
- main_amd64_di_md5sum = Digest::MD5.hexdigest(expected_main_amd64_di_content)
main_amd64_di_sha256 = Digest::SHA256.hexdigest(expected_main_amd64_di_content)
main_sources_size = expected_main_sources_content.length
- main_sources_md5sum = Digest::MD5.hexdigest(expected_main_sources_content)
main_sources_sha256 = Digest::SHA256.hexdigest(expected_main_sources_content)
expected_release_content = <<~EOF
- Codename: unstable
+ Codename: #{distribution.codename}
Date: Sat, 25 Jan 2020 15:17:18 +0000
Valid-Until: Mon, 27 Jan 2020 15:17:18 +0000
Acquire-By-Hash: yes
Architectures: all amd64 arm64
Components: contrib main
- MD5Sum:
- #{contrib_all_md5sum} #{contrib_all_size} contrib/binary-all/Packages
- d41d8cd98f00b204e9800998ecf8427e 0 contrib/debian-installer/binary-all/Packages
- d41d8cd98f00b204e9800998ecf8427e 0 contrib/binary-amd64/Packages
- d41d8cd98f00b204e9800998ecf8427e 0 contrib/debian-installer/binary-amd64/Packages
- d41d8cd98f00b204e9800998ecf8427e 0 contrib/binary-arm64/Packages
- d41d8cd98f00b204e9800998ecf8427e 0 contrib/debian-installer/binary-arm64/Packages
- d41d8cd98f00b204e9800998ecf8427e 0 contrib/source/Sources
- d41d8cd98f00b204e9800998ecf8427e 0 main/binary-all/Packages
- d41d8cd98f00b204e9800998ecf8427e 0 main/debian-installer/binary-all/Packages
- #{main_amd64_md5sum} #{main_amd64_size} main/binary-amd64/Packages
- #{main_amd64_di_md5sum} #{main_amd64_di_size} main/debian-installer/binary-amd64/Packages
- d41d8cd98f00b204e9800998ecf8427e 0 main/binary-arm64/Packages
- d41d8cd98f00b204e9800998ecf8427e 0 main/debian-installer/binary-arm64/Packages
- #{main_sources_md5sum} #{main_sources_size} main/source/Sources
SHA256:
- #{contrib_all_sha256} #{contrib_all_size} contrib/binary-all/Packages
+ #{contrib_all_sha256} #{contrib_all_size.to_s.rjust(8)} contrib/binary-all/Packages
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-all/Packages
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/binary-amd64/Packages
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-amd64/Packages
@@ -218,12 +201,13 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/source/Sources
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-all/Packages
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/debian-installer/binary-all/Packages
- #{main_amd64_sha256} #{main_amd64_size} main/binary-amd64/Packages
- #{main_amd64_di_sha256} #{main_amd64_di_size} main/debian-installer/binary-amd64/Packages
+ #{main_amd64_sha256} #{main_amd64_size.to_s.rjust(8)} main/binary-amd64/Packages
+ #{main_amd64_di_sha256} #{main_amd64_di_size.to_s.rjust(8)} main/debian-installer/binary-amd64/Packages
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-arm64/Packages
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/debian-installer/binary-arm64/Packages
- #{main_sources_sha256} #{main_sources_size} main/source/Sources
+ #{main_sources_sha256} #{main_sources_size.to_s.rjust(8)} main/source/Sources
EOF
+ expected_release_content = "Suite: #{distribution.suite}\n#{expected_release_content}" if distribution.suite
check_release_files(expected_release_content)
end
@@ -247,13 +231,13 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
.and not_change { distribution.component_files.reset.count }
expected_release_content = <<~EOF
- Codename: unstable
+ Codename: #{distribution.codename}
Date: Sat, 25 Jan 2020 15:17:18 +0000
Valid-Until: Mon, 27 Jan 2020 15:17:18 +0000
Acquire-By-Hash: yes
- MD5Sum:
SHA256:
EOF
+ expected_release_content = "Suite: #{distribution.suite}\n#{expected_release_content}" if distribution.suite
check_release_files(expected_release_content)
end
diff --git a/spec/support/shared_examples/services/packages_shared_examples.rb b/spec/support/shared_examples/services/packages_shared_examples.rb
index e0dd08ec50e..f63693dbf26 100644
--- a/spec/support/shared_examples/services/packages_shared_examples.rb
+++ b/spec/support/shared_examples/services/packages_shared_examples.rb
@@ -2,7 +2,7 @@
RSpec.shared_examples 'assigns build to package' do
context 'with build info' do
- let(:job) { create(:ci_build, user: user) }
+ let(:job) { create(:ci_build, user: user, project: project) }
let(:params) { super().merge(build: job) }
it 'assigns the pipeline to the package' do
diff --git a/spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb b/spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb
index a7e51408032..1383346644a 100644
--- a/spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb
+++ b/spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb
@@ -8,7 +8,7 @@ RSpec.shared_examples 'filters by paginated notes' do |event_type|
end
it 'only returns given notes' do
- paginated_notes = { event_type.to_s.pluralize => [double(id: event.id)] }
+ paginated_notes = { event_type.to_s.pluralize => [double(ids: [event.id])] }
notes = described_class.new(event.issue, user, paginated_notes: paginated_notes).execute
expect(notes.size).to eq(1)
diff --git a/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb b/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb
index 716be8c6210..209be09c807 100644
--- a/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb
@@ -160,6 +160,21 @@ RSpec.shared_examples_for 'services security ci configuration create service' do
end
end
end
+
+ context 'when the project is empty' do
+ let(:params) { nil }
+ let_it_be(:project) { create(:project_empty_repo) }
+
+ it 'returns an error' do
+ expect { result }.to raise_error { |error|
+ expect(error).to be_a(Gitlab::Graphql::Errors::MutationError)
+ expect(error.message).to eq('You must <a target="_blank" rel="noopener noreferrer" ' \
+ 'href="http://localhost/help/user/project/repository/index.md' \
+ '#add-files-to-a-repository">add at least one file to the repository' \
+ '</a> before using Security features.')
+ }
+ end
+ end
end
end
end
diff --git a/spec/support/shared_examples/services/updating_mentions_shared_examples.rb b/spec/support/shared_examples/services/updating_mentions_shared_examples.rb
index 13a2aa9ddac..0f649173683 100644
--- a/spec/support/shared_examples/services/updating_mentions_shared_examples.rb
+++ b/spec/support/shared_examples/services/updating_mentions_shared_examples.rb
@@ -15,7 +15,8 @@ RSpec.shared_examples 'updating mentions' do |service_class|
def update_mentionable(opts)
perform_enqueued_jobs do
- service_class.new(project: project, current_user: user, params: opts).execute(mentionable)
+ service_class.new(**service_class.constructor_container_arg(project),
+ current_user: user, params: opts).execute(mentionable)
end
mentionable.reload
diff --git a/spec/support/shared_examples/views/themed_layout_examples.rb b/spec/support/shared_examples/views/themed_layout_examples.rb
index b6c53dce4cb..599fd141dd7 100644
--- a/spec/support/shared_examples/views/themed_layout_examples.rb
+++ b/spec/support/shared_examples/views/themed_layout_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples "a layout which reflects the application theme setting", :themed_layout do
+RSpec.shared_examples "a layout which reflects the application theme setting" do
context 'as a themed layout' do
let(:default_theme_class) { ::Gitlab::Themes.default.css_class }
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 b1011037584..1703d400aea 100644
--- a/spec/support/shared_examples/work_item_base_types_importer.rb
+++ b/spec/support/shared_examples/work_item_base_types_importer.rb
@@ -15,6 +15,22 @@ RSpec.shared_examples 'work item base types importer' do
expect(WorkItems::Type.all).to all(be_valid)
end
+ it 'creates all default widget definitions' do
+ WorkItems::WidgetDefinition.delete_all
+ widget_mapping = ::Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter::WIDGETS_FOR_TYPE
+
+ expect { subject }.to change { WorkItems::WidgetDefinition.count }.from(0).to(widget_mapping.values.flatten.count)
+
+ created_widgets = WorkItems::WidgetDefinition.global.map do |widget|
+ { name: widget.work_item_type.name, type: widget.widget_type }
+ end
+ expected_widgets = widget_mapping.flat_map do |type_sym, widget_types|
+ widget_types.map { |type| { name: ::WorkItems::Type::TYPE_NAMES[type_sym], type: type.to_s } }
+ end
+
+ expect(created_widgets).to match_array(expected_widgets)
+ end
+
it 'upserts base work item types if they already exist' do
first_type = WorkItems::Type.first
original_name = first_type.name
@@ -29,8 +45,34 @@ RSpec.shared_examples 'work item base types importer' do
)
end
- it 'executes a single INSERT query' do
- expect { subject }.to make_queries_matching(/INSERT/, 1)
+ it 'upserts default widget definitions if they already exist and type changes' do
+ widget = WorkItems::WidgetDefinition.global.find_by_widget_type(:labels)
+
+ widget.update!(widget_type: :weight)
+
+ expect do
+ subject
+ widget.reload
+ end.to not_change(WorkItems::WidgetDefinition, :count).and(
+ change { widget.widget_type }.from('weight').to('labels')
+ )
+ end
+
+ it 'does not change default widget definitions if they already exist with changed disabled status' do
+ widget = WorkItems::WidgetDefinition.global.find_by_widget_type(:labels)
+
+ widget.update!(disabled: true)
+
+ expect do
+ subject
+ widget.reload
+ end.to not_change(WorkItems::WidgetDefinition, :count).and(
+ not_change { widget.disabled }
+ )
+ end
+
+ it 'executes single INSERT query per types and widget definitions' do
+ expect { subject }.to make_queries_matching(/INSERT/, 2)
end
context 'when some base types exist' do
@@ -39,10 +81,22 @@ RSpec.shared_examples 'work item base types importer' do
end
it 'inserts all types and does nothing if some already existed' do
- expect { subject }.to make_queries_matching(/INSERT/, 1).and(
+ expect { subject }.to make_queries_matching(/INSERT/, 2).and(
change { WorkItems::Type.count }.by(1)
)
expect(WorkItems::Type.count).to eq(WorkItems::Type::BASE_TYPES.count)
end
end
+
+ context 'when some widget definitions exist' do
+ before do
+ WorkItems::WidgetDefinition.limit(1).delete_all
+ end
+
+ it 'inserts all widget definitions and does nothing if some already existed' do
+ expect { subject }.to make_queries_matching(/INSERT/, 2).and(
+ change { WorkItems::WidgetDefinition.count }.by(1)
+ )
+ end
+ end
end
diff --git a/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb b/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb
index ae29b76ee87..e224b71da91 100644
--- a/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb
+++ b/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb
@@ -101,7 +101,7 @@ RSpec.shared_examples 'batched background migrations execution worker' do
context 'when the provided database is sharing config' do
before do
- skip_if_multiple_databases_not_setup
+ skip_if_multiple_databases_not_setup(:ci)
end
it 'does nothing' do
diff --git a/spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb b/spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb
index ba1bdfa7aa8..abb00efdee8 100644
--- a/spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb
+++ b/spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb
@@ -147,5 +147,19 @@ RSpec.shared_examples 'can collect git garbage' do |update_statistics: true|
subject.perform(resource.id, 'prune', lease_key, lease_uuid)
end
end
+
+ context 'eager' do
+ before do
+ expect(subject).to receive(:get_lease_uuid).and_return(lease_uuid)
+ end
+
+ specify do
+ expect_next_instance_of(Gitlab::GitalyClient::RepositoryService, repository.raw_repository) do |instance|
+ expect(instance).to receive(:optimize_repository).with(eager: true).and_call_original
+ end
+
+ subject.perform(resource.id, 'eager', lease_key, lease_uuid)
+ end
+ end
end
end
diff --git a/spec/support/webmock.rb b/spec/support/webmock.rb
index b9bd3f82f65..171c7ace2d2 100644
--- a/spec/support/webmock.rb
+++ b/spec/support/webmock.rb
@@ -9,12 +9,26 @@ def webmock_allowed_hosts
hosts << URI.parse(ENV['ELASTIC_URL']).host
end
+ if ENV.key?('ZOEKT_INDEX_BASE_URL')
+ hosts.concat(allowed_host_and_ip(ENV['ZOEKT_INDEX_BASE_URL']))
+ end
+
+ if ENV.key?('ZOEKT_SEARCH_BASE_URL')
+ hosts.concat(allowed_host_and_ip(ENV['ZOEKT_SEARCH_BASE_URL']))
+ end
+
if Gitlab.config.webpack&.dev_server&.enabled
hosts << Gitlab.config.webpack.dev_server.host
end
end.compact.uniq
end
+def allowed_host_and_ip(url)
+ host = URI.parse(url).host
+ ip_address = Addrinfo.ip(host).ip_address
+ [host, ip_address]
+end
+
def with_net_connect_allowed
WebMock.allow_net_connect!
yield