#{node.content}
)
else
%(#{node.content}
)
diff --git a/lib/gitlab/ci/status/bridge/common.rb b/lib/gitlab/ci/status/bridge/common.rb
index d66d4b20bba..eaa87157716 100644
--- a/lib/gitlab/ci/status/bridge/common.rb
+++ b/lib/gitlab/ci/status/bridge/common.rb
@@ -16,7 +16,11 @@ module Gitlab
def details_path
return unless can?(user, :read_pipeline, downstream_pipeline)
- project_pipeline_path(downstream_project, downstream_pipeline)
+ if Feature.enabled?(:ci_retry_downstream_pipeline, subject.project, default_enabled: :yaml)
+ project_job_path(subject.project, subject)
+ else
+ project_pipeline_path(downstream_project, downstream_pipeline)
+ end
end
def has_action?
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 1a464555278..f9c346a272f 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -72,6 +72,10 @@ module Gitlab
}.with_indifferent_access.freeze
end
+ def self.all_database_names
+ DATABASE_NAMES
+ end
+
# We configure the database connection pool size automatically based on the
# configured concurrency. We also add some headroom, to make sure we don't
# run out of connections when more threads besides the 'user-facing' ones
diff --git a/lib/gitlab/database/gitlab_schemas.yml b/lib/gitlab/database/gitlab_schemas.yml
index ddad56061a4..2469c5dd44b 100644
--- a/lib/gitlab/database/gitlab_schemas.yml
+++ b/lib/gitlab/database/gitlab_schemas.yml
@@ -287,6 +287,7 @@ ldap_group_links: :gitlab_main
lfs_file_locks: :gitlab_main
lfs_objects: :gitlab_main
lfs_objects_projects: :gitlab_main
+lfs_object_states: :gitlab_main
licenses: :gitlab_main
lists: :gitlab_main
list_user_preferences: :gitlab_main
diff --git a/lib/gitlab/diff/custom_diff.rb b/lib/gitlab/diff/custom_diff.rb
new file mode 100644
index 00000000000..e1d3cea4306
--- /dev/null
+++ b/lib/gitlab/diff/custom_diff.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+module Gitlab
+ module Diff
+ module CustomDiff
+ class << self
+ def preprocess_before_diff(path, old_blob, new_blob)
+ return unless path.ends_with? '.ipynb'
+
+ transformed_diff(old_blob&.data, new_blob&.data)&.tap do
+ transformed_for_diff(new_blob, old_blob)
+ Gitlab::AppLogger.info({ message: 'IPYNB_DIFF_GENERATED' })
+ end
+ rescue IpynbDiff::InvalidNotebookError => e
+ Gitlab::ErrorTracking.log_exception(e)
+ nil
+ end
+
+ def transformed_diff(before, after)
+ transformed_diff = IpynbDiff.diff(before, after,
+ diff_opts: { context: 5, include_diff_info: true },
+ transform_options: { cell_decorator: :percent },
+ raise_if_invalid_notebook: true)
+ strip_diff_frontmatter(transformed_diff)
+ end
+
+ def transformed_blob_language(blob)
+ 'md' if transformed_for_diff?(blob)
+ end
+
+ def transformed_blob_data(blob)
+ if transformed_for_diff?(blob)
+ IpynbDiff.transform(blob.data,
+ raise_errors: true,
+ options: { include_metadata: false, cell_decorator: :percent })
+ end
+ end
+
+ def strip_diff_frontmatter(diff_content)
+ diff_content.scan(/.*\n/)[2..-1]&.join('') if diff_content.present?
+ end
+
+ def blobs_with_transformed_diffs
+ @blobs_with_transformed_diffs ||= {}
+ end
+
+ def transformed_for_diff?(blob)
+ blobs_with_transformed_diffs[blob]
+ end
+
+ def transformed_for_diff(*blobs)
+ blobs.each do |b|
+ blobs_with_transformed_diffs[b] = true if b
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb
index 83f242ff902..d9860d9fb86 100644
--- a/lib/gitlab/diff/file.rb
+++ b/lib/gitlab/diff/file.rb
@@ -44,7 +44,11 @@ module Gitlab
new_blob_lazy
old_blob_lazy
- preprocess_before_diff(diff) if Feature.enabled?(:jupyter_clean_diffs, repository.project, default_enabled: true)
+ diff.diff = Gitlab::Diff::CustomDiff.preprocess_before_diff(diff.new_path, old_blob_lazy, new_blob_lazy) || diff.diff if use_custom_diff?
+ end
+
+ def use_custom_diff?
+ strong_memoize(:_custom_diff_enabled) { Feature.enabled?(:jupyter_clean_diffs, repository.project, default_enabled: true) }
end
def position(position_marker, position_type: :text)
@@ -450,33 +454,6 @@ module Gitlab
find_renderable_viewer_class(classes)
end
- def preprocess_before_diff(diff)
- return unless diff.new_path.ends_with? '.ipynb'
-
- from = old_blob_lazy&.data
- to = new_blob_lazy&.data
-
- transformed_diff = IpynbDiff.diff(from, to,
- diff_opts: { context: 5, include_diff_info: true },
- transform_options: { cell_decorator: :percent },
- raise_if_invalid_notebook: true)
- new_diff = strip_diff_frontmatter(transformed_diff)
-
- if new_diff
- diff.diff = new_diff
- new_blob_lazy.transformed_for_diff = true if new_blob_lazy
- old_blob_lazy.transformed_for_diff = true if old_blob_lazy
- end
-
- Gitlab::AppLogger.info({ message: new_diff ? 'IPYNB_DIFF_GENERATED' : 'IPYNB_DIFF_NIL' })
- rescue IpynbDiff::InvalidNotebookError => e
- Gitlab::ErrorTracking.log_exception(e)
- end
-
- def strip_diff_frontmatter(diff_content)
- diff_content.scan(/.*\n/)[2..-1]&.join('') if diff_content.present?
- end
-
def alternate_viewer_class
return unless viewer.instance_of?(DiffViewer::Renamed)
diff --git a/lib/gitlab/diff/highlight.rb b/lib/gitlab/diff/highlight.rb
index 7ee9b862876..47f3324752d 100644
--- a/lib/gitlab/diff/highlight.rb
+++ b/lib/gitlab/diff/highlight.rb
@@ -153,8 +153,6 @@ module Gitlab
blob.load_all_data!
- return blob.present.highlight_transformed.lines if Feature.enabled?(:jupyter_clean_diffs, @project, default_enabled: true)
-
blob.present.highlight.lines
end
diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb
index b0d194f309a..f72217dedde 100644
--- a/lib/gitlab/git/blob.rb
+++ b/lib/gitlab/git/blob.rb
@@ -24,7 +24,7 @@ module Gitlab
LFS_POINTER_MIN_SIZE = 120.bytes
LFS_POINTER_MAX_SIZE = 200.bytes
- attr_accessor :size, :mode, :id, :commit_id, :loaded_size, :binary, :transformed_for_diff
+ attr_accessor :size, :mode, :id, :commit_id, :loaded_size, :binary
attr_writer :name, :path, :data
def self.gitlab_blob_truncated_true
@@ -127,7 +127,6 @@ module Gitlab
# Retain the actual size before it is encoded
@loaded_size = @data.bytesize if @data
@loaded_all_data = @loaded_size == size
- @transformed_for_diff = false
record_metric_blob_size
record_metric_truncated(truncated?)
diff --git a/lib/gitlab/github_import/importer/diff_note_importer.rb b/lib/gitlab/github_import/importer/diff_note_importer.rb
index 0aa0896aa57..8a8d23401c1 100644
--- a/lib/gitlab/github_import/importer/diff_note_importer.rb
+++ b/lib/gitlab/github_import/importer/diff_note_importer.rb
@@ -31,6 +31,10 @@ module Gitlab
else
import_with_legacy_diff_note
end
+ rescue ::DiffNote::NoteDiffFileCreationError => e
+ Logger.warn(message: e.message, 'error.class': e.class.name)
+
+ import_with_legacy_diff_note
rescue ActiveRecord::InvalidForeignKey => e
# It's possible the project and the issue have been deleted since
# scheduling this job. In this case we'll just skip creating the note
diff --git a/lib/gitlab/github_import/importer/note_importer.rb b/lib/gitlab/github_import/importer/note_importer.rb
index 2cc3a82dd9b..673f56b5753 100644
--- a/lib/gitlab/github_import/importer/note_importer.rb
+++ b/lib/gitlab/github_import/importer/note_importer.rb
@@ -29,6 +29,7 @@ module Gitlab
project_id: project.id,
author_id: author_id,
note: note_body,
+ discussion_id: note.discussion_id,
system: false,
created_at: note.created_at,
updated_at: note.updated_at
diff --git a/lib/gitlab/github_import/representation/diff_note.rb b/lib/gitlab/github_import/representation/diff_note.rb
index fecff0644c2..04f53accfeb 100644
--- a/lib/gitlab/github_import/representation/diff_note.rb
+++ b/lib/gitlab/github_import/representation/diff_note.rb
@@ -4,6 +4,7 @@ module Gitlab
module GithubImport
module Representation
class DiffNote
+ include Gitlab::Utils::StrongMemoize
include ToHash
include ExposeAttribute
@@ -127,15 +128,17 @@ module Gitlab
end
def discussion_id
- if in_reply_to_id.present?
- current_discussion_id
- else
- Discussion.discussion_id(
- Struct
- .new(:noteable_id, :noteable_type)
- .new(merge_request.id, NOTEABLE_TYPE)
- ).tap do |discussion_id|
- cache_discussion_id(discussion_id)
+ strong_memoize(:discussion_id) do
+ if in_reply_to_id.present?
+ current_discussion_id
+ else
+ Discussion.discussion_id(
+ Struct
+ .new(:noteable_id, :noteable_type)
+ .new(merge_request.id, NOTEABLE_TYPE)
+ ).tap do |discussion_id|
+ cache_discussion_id(discussion_id)
+ end
end
end
end
diff --git a/lib/gitlab/github_import/representation/note.rb b/lib/gitlab/github_import/representation/note.rb
index bcdb1a5459b..bbf20b7e9e6 100644
--- a/lib/gitlab/github_import/representation/note.rb
+++ b/lib/gitlab/github_import/representation/note.rb
@@ -63,6 +63,14 @@ module Gitlab
@attributes = attributes
end
+ def discussion_id
+ Discussion.discussion_id(
+ Struct
+ .new(:noteable_id, :noteable_type)
+ .new(noteable_id, noteable_type)
+ )
+ end
+
alias_method :issuable_type, :noteable_type
def github_identifiers
diff --git a/lib/gitlab/metrics/samplers/database_sampler.rb b/lib/gitlab/metrics/samplers/database_sampler.rb
index 44304b5891e..965d85e20e5 100644
--- a/lib/gitlab/metrics/samplers/database_sampler.rb
+++ b/lib/gitlab/metrics/samplers/database_sampler.rb
@@ -38,6 +38,10 @@ module Gitlab
end
def host_stats
+ connection_class_stats + replica_host_stats
+ end
+
+ def connection_class_stats
Gitlab::Database.database_base_models.each_value.with_object([]) do |base_model, stats|
next unless base_model.connected?
@@ -45,6 +49,16 @@ module Gitlab
end
end
+ def replica_host_stats
+ Gitlab::Database::LoadBalancing.each_load_balancer.with_object([]) do |load_balancer, stats|
+ next if load_balancer.primary_only?
+
+ load_balancer.host_list.hosts.each do |host|
+ stats << { labels: labels_for_replica_host(load_balancer, host), stats: host.connection.pool.stat }
+ end
+ end
+ end
+
def labels_for_class(klass)
{
host: klass.connection_db_config.host,
@@ -53,6 +67,15 @@ module Gitlab
db_config_name: klass.connection_db_config.name
}
end
+
+ def labels_for_replica_host(load_balancer, host)
+ {
+ host: host.host,
+ port: host.port,
+ class: load_balancer.configuration.primary_connection_specification_name,
+ db_config_name: Gitlab::Database.db_config_name(host.connection)
+ }
+ end
end
end
end
diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb
index df0582149a9..715dd86d93c 100644
--- a/lib/gitlab/metrics/subscribers/active_record.rb
+++ b/lib/gitlab/metrics/subscribers/active_record.rb
@@ -5,6 +5,8 @@ module Gitlab
module Subscribers
# Class for tracking the total query duration of a transaction.
class ActiveRecord < ActiveSupport::Subscriber
+ extend Gitlab::Utils::StrongMemoize
+
attach_to :active_record
IGNORABLE_SQL = %w{BEGIN COMMIT}.freeze
@@ -107,7 +109,7 @@ module Gitlab
# Per database metrics
db_config_name = db_config_name(event.payload)
- duration_key = compose_metric_key(:duration_s, db_role, db_config_name)
+ duration_key = compose_metric_key(:duration_s, nil, db_config_name)
::Gitlab::SafeRequestStore[duration_key] = (::Gitlab::SafeRequestStore[duration_key].presence || 0) + duration
end
@@ -144,7 +146,7 @@ module Gitlab
# when we are also logging the db_role. Otherwise it will be hard to
# tell if the log key is referring to a db_role OR a db_config_name.
if db_role.present? && db_config_name.present?
- log_key = compose_metric_key(counter, db_role, db_config_name)
+ log_key = compose_metric_key(counter, nil, db_config_name)
Gitlab::SafeRequestStore[log_key] = Gitlab::SafeRequestStore[log_key].to_i + 1
end
end
@@ -172,26 +174,34 @@ module Gitlab
end
def self.load_balancing_metric_counter_keys
- load_balancing_metric_keys(DB_LOAD_BALANCING_COUNTERS)
+ strong_memoize(:load_balancing_metric_counter_keys) do
+ load_balancing_metric_keys(DB_LOAD_BALANCING_COUNTERS)
+ end
end
def self.load_balancing_metric_duration_keys
- load_balancing_metric_keys(DB_LOAD_BALANCING_DURATIONS)
+ strong_memoize(:load_balancing_metric_duration_keys) do
+ load_balancing_metric_keys(DB_LOAD_BALANCING_DURATIONS)
+ end
end
def self.load_balancing_metric_keys(metrics)
- [].tap do |counters|
- DB_LOAD_BALANCING_ROLES.each do |role|
- metrics.each do |metric|
- counters << compose_metric_key(metric, role)
- next unless ENV['GITLAB_MULTIPLE_DATABASE_METRICS']
+ counters = []
- ::Gitlab::Database.db_config_names.each do |config_name|
- counters << compose_metric_key(metric, role, config_name)
- end
+ metrics.each do |metric|
+ DB_LOAD_BALANCING_ROLES.each do |role|
+ counters << compose_metric_key(metric, role)
+ end
+
+ if ENV['GITLAB_MULTIPLE_DATABASE_METRICS']
+ ::Gitlab::Database.db_config_names.each do |config_name|
+ counters << compose_metric_key(metric, nil, config_name) # main
+ counters << compose_metric_key(metric, nil, config_name + ::Gitlab::Database::LoadBalancing::LoadBalancer::REPLICA_SUFFIX) # main_replica
end
end
end
+
+ counters
end
def compose_metric_key(metric, db_role = nil, db_config_name = nil)
diff --git a/lib/gitlab/patch/legacy_database_config.rb b/lib/gitlab/patch/legacy_database_config.rb
index a7d4fdf7490..6040f737c75 100644
--- a/lib/gitlab/patch/legacy_database_config.rb
+++ b/lib/gitlab/patch/legacy_database_config.rb
@@ -35,6 +35,40 @@ module Gitlab
attr_reader :uses_legacy_database_config
end
+ def load_database_yaml
+ return super unless Gitlab.ee?
+
+ super.deep_merge(load_geo_database_yaml)
+ end
+
+ # This method is taken from Rails to load a database YAML file without
+ # evaluating ERB. This allows us to create the rake tasks for the Geo
+ # tracking database without filling in the configuration values or
+ # loading the environment. To be removed when we start configure Geo
+ # tracking database in database.yml instead of custom database_geo.yml
+ #
+ # https://github.com/rails/rails/blob/v6.1.4/railties/lib/rails/application/configuration.rb#L255
+ def load_geo_database_yaml
+ path = Rails.root.join("config/database_geo.yml")
+ return {} unless File.exist?(path)
+
+ require "rails/application/dummy_erb_compiler"
+
+ yaml = DummyERB.new(Pathname.new(path).read).result
+ config = YAML.load(yaml) || {} # rubocop:disable Security/YAMLLoad
+
+ config.to_h do |env, configs|
+ # This check is taken from Rails where the transformation
+ # of a flat database.yml is done into `primary:`
+ # https://github.com/rails/rails/blob/v6.1.4/activerecord/lib/active_record/database_configurations.rb#L169
+ if configs.is_a?(Hash) && !configs.all? { |_, v| v.is_a?(Hash) }
+ configs = { "geo" => configs }
+ end
+
+ [env, configs]
+ end
+ end
+
def database_configuration
@uses_legacy_database_config = false # rubocop:disable Gitlab/ModuleWithInstanceVariables
@@ -48,6 +82,16 @@ module Gitlab
@uses_legacy_database_config = true # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
+ if Gitlab.ee? && File.exist?(Rails.root.join("config/database_geo.yml"))
+ migrations_paths = ["ee/db/geo/migrate"]
+ migrations_paths << "ee/db/geo/post_migrate" unless ENV['SKIP_POST_DEPLOYMENT_MIGRATIONS']
+
+ configs["geo"] =
+ Rails.application.config_for(:database_geo)
+ .merge(migrations_paths: migrations_paths, schema_migrations_path: "ee/db/geo/schema_migrations")
+ .stringify_keys
+ end
+
[env, configs]
end
end
diff --git a/lib/sidebars/groups/menus/packages_registries_menu.rb b/lib/sidebars/groups/menus/packages_registries_menu.rb
index 46fcec9f7b8..60d91c8fd10 100644
--- a/lib/sidebars/groups/menus/packages_registries_menu.rb
+++ b/lib/sidebars/groups/menus/packages_registries_menu.rb
@@ -26,9 +26,7 @@ module Sidebars
private
def packages_registry_menu_item
- unless context.group.packages_feature_enabled?
- return ::Sidebars::NilMenuItem.new(item_id: :packages_registry)
- end
+ return nil_menu_item(:packages_registry) unless context.group.packages_feature_enabled?
::Sidebars::MenuItem.new(
title: _('Package Registry'),
@@ -40,7 +38,7 @@ module Sidebars
def container_registry_menu_item
if !::Gitlab.config.registry.enabled || !can?(context.current_user, :read_container_image, context.group)
- return ::Sidebars::NilMenuItem.new(item_id: :container_registry)
+ return nil_menu_item(:container_registry)
end
::Sidebars::MenuItem.new(
@@ -52,9 +50,11 @@ module Sidebars
end
def dependency_proxy_menu_item
- unless can?(context.current_user, :read_dependency_proxy, context.group)
- return ::Sidebars::NilMenuItem.new(item_id: :dependency_proxy)
- end
+ setting_does_not_exist_or_is_enabled = !context.group.dependency_proxy_setting ||
+ context.group.dependency_proxy_setting.enabled
+
+ return nil_menu_item(:dependency_proxy) unless can?(context.current_user, :read_dependency_proxy, context.group)
+ return nil_menu_item(:dependency_proxy) unless setting_does_not_exist_or_is_enabled
::Sidebars::MenuItem.new(
title: _('Dependency Proxy'),
@@ -63,6 +63,10 @@ module Sidebars
item_id: :dependency_proxy
)
end
+
+ def nil_menu_item(item_id)
+ ::Sidebars::NilMenuItem.new(item_id: item_id)
+ end
end
end
end
diff --git a/lib/sidebars/projects/menus/infrastructure_menu.rb b/lib/sidebars/projects/menus/infrastructure_menu.rb
index 3a08aeb9116..1018bdd545b 100644
--- a/lib/sidebars/projects/menus/infrastructure_menu.rb
+++ b/lib/sidebars/projects/menus/infrastructure_menu.rb
@@ -57,9 +57,9 @@ module Sidebars
data: { trigger: 'manual',
container: 'body',
placement: 'right',
- highlight: UserCalloutsHelper::GKE_CLUSTER_INTEGRATION,
- highlight_priority: UserCallout.feature_names[:GKE_CLUSTER_INTEGRATION],
- dismiss_endpoint: user_callouts_path,
+ highlight: Users::CalloutsHelper::GKE_CLUSTER_INTEGRATION,
+ highlight_priority: Users::Callout.feature_names[:GKE_CLUSTER_INTEGRATION],
+ dismiss_endpoint: callouts_path,
auto_devops_help_path: help_page_path('topics/autodevops/index.md') } }
end
diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake
index 6f4eeb23d3b..71cc1c47a1a 100644
--- a/lib/tasks/gitlab/db.rake
+++ b/lib/tasks/gitlab/db.rake
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+databases = ActiveRecord::Tasks::DatabaseTasks.setup_initial_database_yaml
+
namespace :gitlab do
namespace :db do
desc 'GitLab | DB | Manually insert schema migration version'
@@ -83,7 +85,7 @@ namespace :gitlab do
desc 'GitLab | DB | Sets up EE specific database functionality'
if Gitlab.ee?
- task setup_ee: %w[geo:db:drop geo:db:create geo:db:schema:load geo:db:migrate]
+ task setup_ee: %w[db:drop:geo db:create:geo db:schema:load:geo db:migrate:geo]
else
task :setup_ee
end
@@ -116,6 +118,19 @@ namespace :gitlab do
Rake::Task['gitlab:db:clean_structure_sql'].invoke
end
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
+ # Inform Rake that custom tasks should be run every time rake db:structure:dump is run
+ #
+ # Rails 6.1 deprecates db:structure:dump in favor of db:schema:dump
+ Rake::Task["db:structure:dump:#{name}"].enhance do
+ Rake::Task['gitlab:db:clean_structure_sql'].invoke
+ end
+
+ Rake::Task["db:schema:dump:#{name}"].enhance do
+ Rake::Task['gitlab:db:clean_structure_sql'].invoke
+ end
+ end
+
desc 'Create missing dynamic database partitions'
task create_dynamic_partitions: :environment do
Gitlab::Database::Partitioning.sync_partitions
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 30b4c4ddf29..14ebf429067 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -5421,9 +5421,6 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
-msgid "BillingPlan|Contact sales"
-msgstr ""
-
msgid "BillingPlan|Upgrade"
msgstr ""
@@ -11087,6 +11084,9 @@ msgstr ""
msgid "Date"
msgstr ""
+msgid "Date merged"
+msgstr ""
+
msgid "Date picker"
msgstr ""
@@ -11570,9 +11570,6 @@ msgstr ""
msgid "DependencyProxy|Storage settings"
msgstr ""
-msgid "DependencyProxy|The Dependency Proxy is disabled. %{docLinkStart}Learn how to enable it%{docLinkEnd}."
-msgstr ""
-
msgid "DependencyProxy|There are no images in the cache"
msgstr ""
@@ -20187,7 +20184,7 @@ msgstr ""
msgid "Job|Download"
msgstr ""
-msgid "Job|Erase job log"
+msgid "Job|Erase job log and artifacts"
msgstr ""
msgid "Job|Job artifacts"
@@ -29916,6 +29913,9 @@ msgstr ""
msgid "Resync"
msgstr ""
+msgid "Retrieving the compliance report failed. Please refresh the page and try again."
+msgstr ""
+
msgid "Retry"
msgstr ""
@@ -29925,6 +29925,12 @@ msgstr ""
msgid "Retry migration"
msgstr ""
+msgid "Retry the downstream pipeline"
+msgstr ""
+
+msgid "Retry the trigger job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -34764,6 +34770,9 @@ msgstr ""
msgid "The compliance report captures merged changes that violate compliance best practices."
msgstr ""
+msgid "The compliance report shows the merge request violations merged in protected environments."
+msgstr ""
+
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
@@ -35894,6 +35903,9 @@ msgstr ""
msgid "This job requires manual intervention to start. Before starting this job, you can add variables below for last-minute configuration changes."
msgstr ""
+msgid "This job triggers a downstream pipeline"
+msgstr ""
+
msgid "This job will automatically run after its timer finishes. Often they are used for incremental roll-out deploys to production environments. When unscheduled it converts into a manual action."
msgstr ""
@@ -38599,6 +38611,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View downstream pipeline"
+msgstr ""
+
msgid "View eligible approvers"
msgstr ""
@@ -38726,6 +38741,9 @@ msgstr ""
msgid "Viewing commit"
msgstr ""
+msgid "Violation"
+msgstr ""
+
msgid "Visibility"
msgstr ""
diff --git a/spec/controllers/groups/dependency_proxies_controller_spec.rb b/spec/controllers/groups/dependency_proxies_controller_spec.rb
index 35bd7d47aed..67847936a80 100644
--- a/spec/controllers/groups/dependency_proxies_controller_spec.rb
+++ b/spec/controllers/groups/dependency_proxies_controller_spec.rb
@@ -3,8 +3,9 @@
require 'spec_helper'
RSpec.describe Groups::DependencyProxiesController do
- let(:group) { create(:group) }
- let(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be_with_reload(:dependency_proxy_group_setting) { create(:dependency_proxy_group_setting, group: group) }
+ let_it_be(:user) { create(:user) }
before do
group.add_owner(user)
@@ -12,62 +13,37 @@ RSpec.describe Groups::DependencyProxiesController do
end
describe 'GET #show' do
- context 'feature enabled' do
- before do
- enable_dependency_proxy
+ subject { get :show, params: { group_id: group.to_param } }
+
+ before do
+ stub_config(dependency_proxy: { enabled: config_enabled })
+ end
+
+ context 'with global config enabled' do
+ let(:config_enabled) { true }
+
+ context 'with the setting enabled' do
+ it 'returns 200 and renders the view' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template('groups/dependency_proxies/show')
+ end
end
- it 'returns 200 and renders the view' do
- get :show, params: { group_id: group.to_param }
+ context 'with the setting disabled' do
+ before do
+ dependency_proxy_group_setting.update!(enabled: false)
+ end
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to render_template('groups/dependency_proxies/show')
+ it_behaves_like 'returning response status', :not_found
end
end
- it 'returns 404 when feature is disabled' do
- disable_dependency_proxy
+ context 'with global config disabled' do
+ let(:config_enabled) { false }
- get :show, params: { group_id: group.to_param }
-
- expect(response).to have_gitlab_http_status(:not_found)
+ it_behaves_like 'returning response status', :not_found
end
end
-
- describe 'PUT #update' do
- context 'feature enabled' do
- before do
- enable_dependency_proxy
- end
-
- it 'redirects back to show page' do
- put :update, params: update_params
-
- expect(response).to have_gitlab_http_status(:found)
- end
- end
-
- it 'returns 404 when feature is disabled' do
- put :update, params: update_params
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
-
- def update_params
- {
- group_id: group.to_param,
- dependency_proxy_group_setting: { enabled: true }
- }
- end
- end
-
- def enable_dependency_proxy
- stub_config(dependency_proxy: { enabled: true })
-
- group.create_dependency_proxy_setting!(enabled: true)
- end
-
- def disable_dependency_proxy
- group.create_dependency_proxy_setting!(enabled: false)
- end
end
diff --git a/spec/controllers/root_controller_spec.rb b/spec/controllers/root_controller_spec.rb
index dbf1b3baf25..38f8d267a2c 100644
--- a/spec/controllers/root_controller_spec.rb
+++ b/spec/controllers/root_controller_spec.rb
@@ -142,8 +142,8 @@ RSpec.describe RootController do
context 'without customize homepage banner' do
before do
- Users::DismissUserCalloutService.new(
- container: nil, current_user: user, params: { feature_name: UserCalloutsHelper::CUSTOMIZE_HOMEPAGE }
+ Users::DismissCalloutService.new(
+ container: nil, current_user: user, params: { feature_name: Users::CalloutsHelper::CUSTOMIZE_HOMEPAGE }
).execute
end
diff --git a/spec/controllers/user_callouts_controller_spec.rb b/spec/controllers/users/callouts_controller_spec.rb
similarity index 73%
rename from spec/controllers/user_callouts_controller_spec.rb
rename to spec/controllers/users/callouts_controller_spec.rb
index 3bb8d78a6b0..13dc565b4ad 100644
--- a/spec/controllers/user_callouts_controller_spec.rb
+++ b/spec/controllers/users/callouts_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe UserCalloutsController do
+RSpec.describe Users::CalloutsController do
let_it_be(:user) { create(:user) }
before do
@@ -15,11 +15,11 @@ RSpec.describe UserCalloutsController do
subject { post :create, params: params, format: :json }
context 'with valid feature name' do
- let(:feature_name) { UserCallout.feature_names.each_key.first }
+ let(:feature_name) { Users::Callout.feature_names.each_key.first }
context 'when callout entry does not exist' do
it 'creates a callout entry with dismissed state' do
- expect { subject }.to change { UserCallout.count }.by(1)
+ expect { subject }.to change { Users::Callout.count }.by(1)
end
it 'returns success' do
@@ -30,10 +30,10 @@ RSpec.describe UserCalloutsController do
end
context 'when callout entry already exists' do
- let!(:callout) { create(:user_callout, feature_name: UserCallout.feature_names.each_key.first, user: user) }
+ let!(:callout) { create(:callout, feature_name: Users::Callout.feature_names.each_key.first, user: user) }
it 'returns success', :aggregate_failures do
- expect { subject }.not_to change { UserCallout.count }
+ expect { subject }.not_to change { Users::Callout.count }
expect(response).to have_gitlab_http_status(:ok)
end
end
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
index a8b28b32bd7..94957020bcf 100644
--- a/spec/db/schema_spec.rb
+++ b/spec/db/schema_spec.rb
@@ -169,7 +169,7 @@ RSpec.describe 'Database schema' do
'PrometheusMetric' => %w[group],
'ResourceLabelEvent' => %w[action],
'User' => %w[layout dashboard project_view],
- 'UserCallout' => %w[feature_name],
+ 'Users::Callout' => %w[feature_name],
'PrometheusAlert' => %w[operator]
}.freeze
diff --git a/spec/factories/user_callouts.rb b/spec/factories/users/callouts.rb
similarity index 71%
rename from spec/factories/user_callouts.rb
rename to spec/factories/users/callouts.rb
index cedc6efd8d7..d9f142fee6f 100644
--- a/spec/factories/user_callouts.rb
+++ b/spec/factories/users/callouts.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
FactoryBot.define do
- factory :user_callout do
+ factory :callout, class: 'Users::Callout' do
feature_name { :gke_cluster_integration }
user
diff --git a/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb b/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb
index e885c0c4413..211576a93f3 100644
--- a/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb
+++ b/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb
@@ -10,6 +10,9 @@ RSpec.describe 'User uploads new design', :js do
let(:issue) { create(:issue, project: project) }
before do
+ # Cause of raising query limiting threshold https://gitlab.com/gitlab-org/gitlab/-/issues/347334
+ stub_const("Gitlab::QueryLimiting::Transaction::THRESHOLD", 102)
+
sign_in(user)
enable_design_management(feature_enabled)
visit project_issue_path(project, issue)
diff --git a/spec/features/projects/milestones/milestone_spec.rb b/spec/features/projects/milestones/milestone_spec.rb
index 9ffb1746f3e..6bd139c0ebe 100644
--- a/spec/features/projects/milestones/milestone_spec.rb
+++ b/spec/features/projects/milestones/milestone_spec.rb
@@ -2,10 +2,11 @@
require 'spec_helper'
-RSpec.describe 'Project milestone' do
+RSpec.describe 'Project milestone', :js do
let(:user) { create(:user) }
let(:project) { create(:project, name: 'test', namespace: user.namespace) }
let(:milestone) { create(:milestone, project: project) }
+ let(:active_tab_selector) { '[role="tab"][aria-selected="true"]' }
def toggle_sidebar
find('.milestone-sidebar .gutter-toggle').click
@@ -31,8 +32,9 @@ RSpec.describe 'Project milestone' do
it 'shows issues tab' do
within('#content-body') do
expect(page).to have_link 'Issues', href: '#tab-issues'
- expect(page).to have_selector '.nav-links li a.active', count: 1
- expect(find('.nav-links li a.active')).to have_content 'Issues'
+ expect(page).to have_selector active_tab_selector, count: 1
+ expect(find(active_tab_selector)).to have_content 'Issues'
+ expect(page).to have_text('Unstarted Issues')
end
end
@@ -49,6 +51,35 @@ RSpec.describe 'Project milestone' do
end
end
+ context 'when clicking on other tabs' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:tab_text, :href, :panel_content) do
+ 'Merge requests' | '#tab-merge-requests' | 'Work in progress'
+ 'Participants' | '#tab-participants' | nil
+ 'Labels' | '#tab-labels' | nil
+ end
+
+ with_them do
+ before do
+ visit project_milestone_path(project, milestone)
+ click_link(tab_text, href: href)
+ end
+
+ it 'shows the merge requests tab and panel' do
+ within('#content-body') do
+ expect(find(active_tab_selector)).to have_content tab_text
+ expect(find(href)).to be_visible
+ expect(page).to have_text(panel_content) if panel_content
+ end
+ end
+
+ it 'sets the location hash' do
+ expect(current_url).to end_with(href)
+ end
+ end
+ end
+
context 'when project has disabled issues' do
before do
create(:issue, project: project, milestone: milestone)
@@ -59,7 +90,7 @@ RSpec.describe 'Project milestone' do
it 'does not show any issues under the issues tab' do
within('#content-body') do
- expect(find('.nav-links li a.active')).to have_content 'Issues'
+ expect(find(active_tab_selector)).to have_content 'Issues'
expect(page).not_to have_selector '.issuable-row'
end
end
diff --git a/spec/frontend/code_navigation/components/__snapshots__/popover_spec.js.snap b/spec/frontend/code_navigation/components/__snapshots__/popover_spec.js.snap
index 118d8ceceb9..97d9be110c8 100644
--- a/spec/frontend/code_navigation/components/__snapshots__/popover_spec.js.snap
+++ b/spec/frontend/code_navigation/components/__snapshots__/popover_spec.js.snap
@@ -42,6 +42,8 @@ exports[`Code navigation popover component renders popover 1`] = `
main() {
+
+ ')
else
expect(result).to start_with('')
@@ -49,7 +49,7 @@ RSpec.describe Banzai::Filter::MarkdownFilter do
it 'works with utf8 chars in language' do
result = filter("```日\nsome code\n```", no_sourcepos: true)
- if Feature.enabled?(:use_cmark_renderer)
+ if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
expect(result).to start_with('')
else
expect(result).to start_with('')
@@ -59,7 +59,7 @@ RSpec.describe Banzai::Filter::MarkdownFilter do
it 'works with additional language parameters' do
result = filter("```ruby:red gem foo\nsome code\n```", no_sourcepos: true)
- if Feature.enabled?(:use_cmark_renderer)
+ if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
expect(result).to start_with('')
else
expect(result).to start_with('')
@@ -102,7 +102,7 @@ RSpec.describe Banzai::Filter::MarkdownFilter do
expect(result).to include('foot ')
else
expect(result).to include('')
diff --git a/spec/lib/banzai/filter/plantuml_filter_spec.rb b/spec/lib/banzai/filter/plantuml_filter_spec.rb
index d1a3b5689a8..e1e02c09fbe 100644
--- a/spec/lib/banzai/filter/plantuml_filter_spec.rb
+++ b/spec/lib/banzai/filter/plantuml_filter_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Banzai::Filter::PlantumlFilter do
it 'replaces plantuml pre tag with img tag' do
stub_application_setting(plantuml_enabled: true, plantuml_url: "http://localhost:8080")
- input = if Feature.enabled?(:use_cmark_renderer)
+ input = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
'Bob -> Sara : Hello
'
else
'Bob -> Sara : Hello
'
@@ -24,7 +24,7 @@ RSpec.describe Banzai::Filter::PlantumlFilter do
it 'does not replace plantuml pre tag with img tag if disabled' do
stub_application_setting(plantuml_enabled: false)
- if Feature.enabled?(:use_cmark_renderer)
+ if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
input = 'Bob -> Sara : Hello
'
output = 'Bob -> Sara : Hello
'
else
@@ -40,7 +40,7 @@ RSpec.describe Banzai::Filter::PlantumlFilter do
it 'does not replace plantuml pre tag with img tag if url is invalid' do
stub_application_setting(plantuml_enabled: true, plantuml_url: "invalid")
- input = if Feature.enabled?(:use_cmark_renderer)
+ input = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
'Bob -> Sara : Hello
'
else
'Bob -> Sara : Hello
'
diff --git a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
index dfe022b51d2..62e93cb1653 100644
--- a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
+++ b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
@@ -40,7 +40,7 @@ RSpec.describe Banzai::Filter::SyntaxHighlightFilter do
context "when a valid language is specified" do
it "highlights as that language" do
- result = if Feature.enabled?(:use_cmark_renderer)
+ result = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
filter('def fun end
')
else
filter('def fun end
')
@@ -54,7 +54,7 @@ RSpec.describe Banzai::Filter::SyntaxHighlightFilter do
context "when an invalid language is specified" do
it "highlights as plaintext" do
- result = if Feature.enabled?(:use_cmark_renderer)
+ result = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
filter('This is a test
')
else
filter('This is a test
')
@@ -73,7 +73,7 @@ RSpec.describe Banzai::Filter::SyntaxHighlightFilter do
%w(math mermaid plantuml suggestion).each do |lang|
context "when #{lang} is specified" do
it "highlights as plaintext but with the correct language attribute and class" do
- result = if Feature.enabled?(:use_cmark_renderer)
+ result = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
filter(%{This is a test
})
else
filter(%{This is a test
})
@@ -89,7 +89,7 @@ RSpec.describe Banzai::Filter::SyntaxHighlightFilter do
let(:lang_params) { 'foo-bar-kux' }
let(:xss_lang) do
- if Feature.enabled?(:use_cmark_renderer)
+ if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
"#{lang} data-meta=\"foo-bar-kux\"<script>alert(1)</script>"
else
"#{lang}#{described_class::LANG_PARAMS_DELIMITER}<script>alert(1)</script>"
@@ -97,7 +97,7 @@ RSpec.describe Banzai::Filter::SyntaxHighlightFilter do
end
it "includes data-lang-params tag with extra information" do
- result = if Feature.enabled?(:use_cmark_renderer)
+ result = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
filter(%{This is a test
})
else
filter(%{This is a test
})
@@ -108,7 +108,7 @@ RSpec.describe Banzai::Filter::SyntaxHighlightFilter do
include_examples "XSS prevention", lang
- if Feature.enabled?(:use_cmark_renderer)
+ if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
include_examples "XSS prevention",
"#{lang} data-meta=\"foo-bar-kux\"<script>alert(1)</script>"
else
@@ -131,7 +131,7 @@ RSpec.describe Banzai::Filter::SyntaxHighlightFilter do
context 'when delimiter is space' do
it 'delimits on the first appearance' do
- if Feature.enabled?(:use_cmark_renderer)
+ if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
result = filter(%{This is a test
})
expect(result.to_html).to eq(expected_result)
@@ -147,7 +147,7 @@ RSpec.describe Banzai::Filter::SyntaxHighlightFilter do
it 'delimits on the first appearance' do
result = filter(%{This is a test
})
- if Feature.enabled?(:use_cmark_renderer)
+ if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
expect(result.to_html).to eq(expected_result)
else
expect(result.to_html).to eq(%{This is a test
})
@@ -173,7 +173,7 @@ RSpec.describe Banzai::Filter::SyntaxHighlightFilter do
end
it "highlights as plaintext" do
- result = if Feature.enabled?(:use_cmark_renderer)
+ result = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
filter('This is a test
')
else
filter('This is a test
')
diff --git a/spec/lib/banzai/pipeline/plain_markdown_pipeline_spec.rb b/spec/lib/banzai/pipeline/plain_markdown_pipeline_spec.rb
index 394fcc06eba..c8cd9d4fcac 100644
--- a/spec/lib/banzai/pipeline/plain_markdown_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/plain_markdown_pipeline_spec.rb
@@ -71,7 +71,7 @@ RSpec.describe Banzai::Pipeline::PlainMarkdownPipeline do
let(:markdown) { %Q(``` foo\\@bar\nfoo\n```) }
it 'renders correct html' do
- if Feature.enabled?(:use_cmark_renderer)
+ if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
correct_html_included(markdown, %Q(foo\n
))
else
correct_html_included(markdown, %Q(foo\n
))
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index ac29bb22865..87874c73e75 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -96,7 +96,7 @@ module Gitlab
it "does not convert dangerous fenced code with inline script into HTML" do
input = '```mypre">'
output =
- if Feature.enabled?(:use_cmark_renderer)
+ if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
"\n\n
\n\n"
else
"\n\n\">
\n\n"
diff --git a/spec/lib/gitlab/ci/status/bridge/common_spec.rb b/spec/lib/gitlab/ci/status/bridge/common_spec.rb
index 37524afc83d..30e6ad234a0 100644
--- a/spec/lib/gitlab/ci/status/bridge/common_spec.rb
+++ b/spec/lib/gitlab/ci/status/bridge/common_spec.rb
@@ -29,7 +29,15 @@ RSpec.describe Gitlab::Ci::Status::Bridge::Common do
end
it { expect(subject).to have_details }
- it { expect(subject.details_path).to include "pipelines/#{downstream_pipeline.id}" }
+ it { expect(subject.details_path).to include "jobs/#{bridge.id}" }
+
+ context 'with ci_retry_downstream_pipeline ff disabled' do
+ before do
+ stub_feature_flags(ci_retry_downstream_pipeline: false)
+ end
+
+ it { expect(subject.details_path).to include "pipelines/#{downstream_pipeline.id}" }
+ end
end
context 'when user does not have access to read downstream pipeline' do
diff --git a/spec/lib/gitlab/diff/custom_diff_spec.rb b/spec/lib/gitlab/diff/custom_diff_spec.rb
new file mode 100644
index 00000000000..246508d2e1e
--- /dev/null
+++ b/spec/lib/gitlab/diff/custom_diff_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Diff::CustomDiff do
+ include RepoHelpers
+
+ let(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
+ let(:ipynb_blob) { repository.blob_at('f6b7a707', 'files/ipython/markdown-table.ipynb') }
+ let(:blob) { repository.blob_at('HEAD', 'files/ruby/regex.rb') }
+
+ describe '#preprocess_before_diff' do
+ context 'for ipynb files' do
+ it 'transforms the diff' do
+ expect(described_class.preprocess_before_diff(ipynb_blob.path, nil, ipynb_blob)).not_to include('cells')
+ end
+
+ it 'adds the blob to the list of transformed blobs' do
+ described_class.preprocess_before_diff(ipynb_blob.path, nil, ipynb_blob)
+
+ expect(described_class.transformed_for_diff?(ipynb_blob)).to be_truthy
+ end
+ end
+
+ context 'for other files' do
+ it 'returns nil' do
+ expect(described_class.preprocess_before_diff(blob.path, nil, blob)).to be_nil
+ end
+
+ it 'does not add the blob to the list of transformed blobs' do
+ described_class.preprocess_before_diff(blob.path, nil, blob)
+
+ expect(described_class.transformed_for_diff?(blob)).to be_falsey
+ end
+ end
+ end
+
+ describe '#transformed_blob_data' do
+ it 'transforms blob data if file was processed' do
+ described_class.preprocess_before_diff(ipynb_blob.path, nil, ipynb_blob)
+
+ expect(described_class.transformed_blob_data(ipynb_blob)).not_to include('cells')
+ end
+
+ it 'does not transform blob data if file was not processed' do
+ expect(described_class.transformed_blob_data(ipynb_blob)).to be_nil
+ end
+ end
+
+ describe '#transformed_blob_language' do
+ it 'is md when file was preprocessed' do
+ described_class.preprocess_before_diff(ipynb_blob.path, nil, ipynb_blob)
+
+ expect(described_class.transformed_blob_language(ipynb_blob)).to eq('md')
+ end
+
+ it 'is nil for a .ipynb blob that was not preprocessed' do
+ expect(described_class.transformed_blob_language(ipynb_blob)).to be_nil
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb b/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
index 0448ada6bca..a0e78186caa 100644
--- a/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
@@ -173,9 +173,11 @@ RSpec.describe Gitlab::GithubImport::Importer::DiffNoteImporter, :aggregate_fail
EOB
end
- it 'imports the note as diff note' do
+ before do
stub_user_finder(user.id, true)
+ end
+ it 'imports the note as diff note' do
expect { subject.execute }
.to change(DiffNote, :count)
.by(1)
@@ -212,6 +214,29 @@ RSpec.describe Gitlab::GithubImport::Importer::DiffNoteImporter, :aggregate_fail
```
NOTE
end
+
+ context 'when the note diff file creation fails' do
+ it 'falls back to the LegacyDiffNote' do
+ exception = ::DiffNote::NoteDiffFileCreationError.new('Failed to create diff note file')
+
+ expect_next_instance_of(::Import::Github::Notes::CreateService) do |service|
+ expect(service)
+ .to receive(:execute)
+ .and_raise(exception)
+ end
+
+ expect(Gitlab::GithubImport::Logger)
+ .to receive(:warn)
+ .with(
+ message: 'Failed to create diff note file',
+ 'error.class': 'DiffNote::NoteDiffFileCreationError'
+ )
+
+ expect { subject.execute }
+ .to change(LegacyDiffNote, :count)
+ .and not_change(DiffNote, :count)
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/github_import/importer/note_importer_spec.rb b/spec/lib/gitlab/github_import/importer/note_importer_spec.rb
index 96d8acbd3de..165f543525d 100644
--- a/spec/lib/gitlab/github_import/importer/note_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/note_importer_spec.rb
@@ -52,6 +52,7 @@ RSpec.describe Gitlab::GithubImport::Importer::NoteImporter do
project_id: project.id,
author_id: user.id,
note: 'This is my note',
+ discussion_id: match(/\A[0-9a-f]{40}\z/),
system: false,
created_at: created_at,
updated_at: updated_at
@@ -82,6 +83,7 @@ RSpec.describe Gitlab::GithubImport::Importer::NoteImporter do
project_id: project.id,
author_id: project.creator_id,
note: "*Created by: alice*\n\nThis is my note",
+ discussion_id: match(/\A[0-9a-f]{40}\z/),
system: false,
created_at: created_at,
updated_at: updated_at
diff --git a/spec/lib/gitlab/metrics/samplers/database_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/database_sampler_spec.rb
index 6127a52b14f..e8f8947c9e8 100644
--- a/spec/lib/gitlab/metrics/samplers/database_sampler_spec.rb
+++ b/spec/lib/gitlab/metrics/samplers/database_sampler_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe Gitlab::Metrics::Samplers::DatabaseSampler do
it_behaves_like 'metrics sampler', 'DATABASE_SAMPLER'
describe '#sample' do
- let(:active_record_labels) do
+ let(:main_labels) do
{
class: 'ActiveRecord::Base',
host: ApplicationRecord.database.config['host'],
@@ -17,7 +17,7 @@ RSpec.describe Gitlab::Metrics::Samplers::DatabaseSampler do
}
end
- let(:ci_application_record_labels) do
+ let(:ci_labels) do
{
class: 'Ci::ApplicationRecord',
host: Ci::ApplicationRecord.database.config['host'],
@@ -26,6 +26,24 @@ RSpec.describe Gitlab::Metrics::Samplers::DatabaseSampler do
}
end
+ let(:main_replica_labels) do
+ {
+ class: 'ActiveRecord::Base',
+ host: 'main-replica-host',
+ port: 2345,
+ db_config_name: 'main_replica'
+ }
+ end
+
+ let(:ci_replica_labels) do
+ {
+ class: 'Ci::ApplicationRecord',
+ host: 'ci-replica-host',
+ port: 3456,
+ db_config_name: 'ci_replica'
+ }
+ end
+
before do
described_class::METRIC_DESCRIPTIONS.each_key do |metric|
allow(subject.metrics[metric]).to receive(:set)
@@ -35,35 +53,124 @@ RSpec.describe Gitlab::Metrics::Samplers::DatabaseSampler do
.and_return({ main: ActiveRecord::Base, ci: Ci::ApplicationRecord })
end
- context 'when the database is connected', :add_ci_connection do
- it 'samples connection pool statistics' do
- expect(subject.metrics[:size]).to receive(:set).with(active_record_labels, a_value >= 1)
- expect(subject.metrics[:connections]).to receive(:set).with(active_record_labels, a_value >= 1)
- expect(subject.metrics[:busy]).to receive(:set).with(active_record_labels, a_value >= 1)
- expect(subject.metrics[:dead]).to receive(:set).with(active_record_labels, a_value >= 0)
- expect(subject.metrics[:waiting]).to receive(:set).with(active_record_labels, a_value >= 0)
-
- expect(subject.metrics[:size]).to receive(:set).with(ci_application_record_labels, a_value >= 1)
- expect(subject.metrics[:connections]).to receive(:set).with(ci_application_record_labels, a_value >= 1)
- expect(subject.metrics[:busy]).to receive(:set).with(ci_application_record_labels, a_value >= 1)
- expect(subject.metrics[:dead]).to receive(:set).with(ci_application_record_labels, a_value >= 0)
- expect(subject.metrics[:waiting]).to receive(:set).with(ci_application_record_labels, a_value >= 0)
+ context 'when all base models are connected', :add_ci_connection do
+ it 'samples connection pool statistics for all primaries' do
+ expect_metrics_with_labels(main_labels)
+ expect_metrics_with_labels(ci_labels)
subject.sample
end
+
+ context 'when replica hosts are configured' do
+ let(:main_load_balancer) { ActiveRecord::Base.load_balancer } # rubocop:disable Database/MultipleDatabases
+ let(:main_replica_host) { main_load_balancer.host }
+
+ let(:ci_load_balancer) { double(:load_balancer, host_list: ci_host_list, configuration: configuration) }
+ let(:configuration) { double(:configuration, primary_connection_specification_name: 'Ci::ApplicationRecord') }
+ let(:ci_host_list) { double(:host_list, hosts: [ci_replica_host]) }
+ let(:ci_replica_host) { double(:host, connection: ci_connection) }
+ let(:ci_connection) { double(:connection, pool: Ci::ApplicationRecord.connection_pool) }
+
+ before do
+ allow(Gitlab::Database::LoadBalancing).to receive(:each_load_balancer)
+ .and_return([main_load_balancer, ci_load_balancer].to_enum)
+
+ allow(main_load_balancer).to receive(:primary_only?).and_return(false)
+ allow(ci_load_balancer).to receive(:primary_only?).and_return(false)
+
+ allow(main_replica_host).to receive(:host).and_return('main-replica-host')
+ allow(ci_replica_host).to receive(:host).and_return('ci-replica-host')
+
+ allow(main_replica_host).to receive(:port).and_return(2345)
+ allow(ci_replica_host).to receive(:port).and_return(3456)
+
+ allow(Gitlab::Database).to receive(:db_config_name)
+ .with(main_replica_host.connection)
+ .and_return('main_replica')
+
+ allow(Gitlab::Database).to receive(:db_config_name)
+ .with(ci_replica_host.connection)
+ .and_return('ci_replica')
+ end
+
+ it 'samples connection pool statistics for primaries and replicas' do
+ expect_metrics_with_labels(main_labels)
+ expect_metrics_with_labels(ci_labels)
+ expect_metrics_with_labels(main_replica_labels)
+ expect_metrics_with_labels(ci_replica_labels)
+
+ subject.sample
+ end
+ end
end
- context 'when a database is not connected', :add_ci_connection do
+ context 'when a base model is not connected', :add_ci_connection do
before do
allow(Ci::ApplicationRecord).to receive(:connected?).and_return(false)
end
- it 'records no samples for that database' do
- expect(subject.metrics[:size]).to receive(:set).with(active_record_labels, anything)
- expect(subject.metrics[:size]).not_to receive(:set).with(ci_application_record_labels, anything)
+ it 'records no samples for that primary' do
+ expect_metrics_with_labels(main_labels)
+ expect_no_metrics_with_labels(ci_labels)
subject.sample
end
+
+ context 'when the base model has replica connections' do
+ let(:main_load_balancer) { ActiveRecord::Base.load_balancer } # rubocop:disable Database/MultipleDatabases
+ let(:main_replica_host) { main_load_balancer.host }
+
+ let(:ci_load_balancer) { double(:load_balancer, host_list: ci_host_list, configuration: configuration) }
+ let(:configuration) { double(:configuration, primary_connection_specification_name: 'Ci::ApplicationRecord') }
+ let(:ci_host_list) { double(:host_list, hosts: [ci_replica_host]) }
+ let(:ci_replica_host) { double(:host, connection: ci_connection) }
+ let(:ci_connection) { double(:connection, pool: Ci::ApplicationRecord.connection_pool) }
+
+ before do
+ allow(Gitlab::Database::LoadBalancing).to receive(:each_load_balancer)
+ .and_return([main_load_balancer, ci_load_balancer].to_enum)
+
+ allow(main_load_balancer).to receive(:primary_only?).and_return(false)
+ allow(ci_load_balancer).to receive(:primary_only?).and_return(false)
+
+ allow(main_replica_host).to receive(:host).and_return('main-replica-host')
+ allow(ci_replica_host).to receive(:host).and_return('ci-replica-host')
+
+ allow(main_replica_host).to receive(:port).and_return(2345)
+ allow(ci_replica_host).to receive(:port).and_return(3456)
+
+ allow(Gitlab::Database).to receive(:db_config_name)
+ .with(main_replica_host.connection)
+ .and_return('main_replica')
+
+ allow(Gitlab::Database).to receive(:db_config_name)
+ .with(ci_replica_host.connection)
+ .and_return('ci_replica')
+ end
+
+ it 'still records the replica metrics' do
+ expect_metrics_with_labels(main_labels)
+ expect_metrics_with_labels(main_replica_labels)
+ expect_no_metrics_with_labels(ci_labels)
+ expect_metrics_with_labels(ci_replica_labels)
+
+ subject.sample
+ end
+ end
+ end
+
+ def expect_metrics_with_labels(labels)
+ expect(subject.metrics[:size]).to receive(:set).with(labels, a_value >= 1)
+ expect(subject.metrics[:connections]).to receive(:set).with(labels, a_value >= 1)
+ expect(subject.metrics[:busy]).to receive(:set).with(labels, a_value >= 1)
+ expect(subject.metrics[:dead]).to receive(:set).with(labels, a_value >= 0)
+ expect(subject.metrics[:waiting]).to receive(:set).with(labels, a_value >= 0)
+ end
+
+ def expect_no_metrics_with_labels(labels)
+ described_class::METRIC_DESCRIPTIONS.each_key do |metric|
+ expect(subject.metrics[metric]).not_to receive(:set).with(labels, anything)
+ end
end
end
end
diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
index a8e4f039da4..389b0ef1044 100644
--- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
@@ -198,6 +198,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
context 'query using a connection to a replica' do
before do
allow(Gitlab::Database::LoadBalancing).to receive(:db_role_for_connection).and_return(:replica)
+ allow(connection).to receive_message_chain(:pool, :db_config, :name).and_return(db_config_name)
end
it 'queries connection db role' do
diff --git a/spec/lib/gitlab/patch/legacy_database_config_spec.rb b/spec/lib/gitlab/patch/legacy_database_config_spec.rb
index e6c0bdbf360..b87e16f31ae 100644
--- a/spec/lib/gitlab/patch/legacy_database_config_spec.rb
+++ b/spec/lib/gitlab/patch/legacy_database_config_spec.rb
@@ -11,6 +11,9 @@ RSpec.describe Gitlab::Patch::LegacyDatabaseConfig do
let(:configuration) { Rails::Application::Configuration.new(Rails.root) }
before do
+ allow(File).to receive(:exist?).and_call_original
+ allow(File).to receive(:exist?).with(Rails.root.join("config/database_geo.yml")).and_return(false)
+
# The `AS::ConfigurationFile` calls `read` in `def initialize`
# thus we cannot use `expect_next_instance_of`
# rubocop:disable RSpec/AnyInstanceOf
diff --git a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb
index d801b84775b..210b9162be0 100644
--- a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb
+++ b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb
@@ -272,12 +272,12 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
expected_end_payload.merge(
'db_duration_s' => a_value >= 0.1,
'db_count' => a_value >= 1,
- "db_replica_#{db_config_name}_count" => 0,
+ "db_#{db_config_name}_replica_count" => 0,
'db_replica_duration_s' => a_value >= 0,
'db_primary_count' => a_value >= 1,
- "db_primary_#{db_config_name}_count" => a_value >= 1,
+ "db_#{db_config_name}_count" => a_value >= 1,
'db_primary_duration_s' => a_value > 0,
- "db_primary_#{db_config_name}_duration_s" => a_value > 0
+ "db_#{db_config_name}_duration_s" => a_value > 0
)
end
diff --git a/spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb b/spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb
index e954d7a44ba..bc1fa3e88ff 100644
--- a/spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb
+++ b/spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Groups::Menus::PackagesRegistriesMenu do
let_it_be(:owner) { create(:user) }
- let_it_be(:group) do
+ let_it_be_with_reload(:group) do
build(:group, :private).tap do |g|
g.add_owner(owner)
end
@@ -70,6 +70,18 @@ RSpec.describe Sidebars::Groups::Menus::PackagesRegistriesMenu do
describe 'Menu items' do
subject { find_menu(menu, item_id) }
+ shared_examples 'the menu entry is available' do
+ it 'the menu item is added to list of menu items' do
+ is_expected.not_to be_nil
+ end
+ end
+
+ shared_examples 'the menu entry is not available' do
+ it 'the menu item is not added to list of menu items' do
+ is_expected.to be_nil
+ end
+ end
+
describe 'Packages Registry' do
let(:item_id) { :packages_registry }
@@ -81,17 +93,13 @@ RSpec.describe Sidebars::Groups::Menus::PackagesRegistriesMenu do
context 'when config package setting is disabled' do
let(:packages_enabled) { false }
- it 'the menu item is not added to list of menu items' do
- is_expected.to be_nil
- end
+ it_behaves_like 'the menu entry is not available'
end
context 'when config package setting is enabled' do
let(:packages_enabled) { true }
- it 'the menu item is added to list of menu items' do
- is_expected.not_to be_nil
- end
+ it_behaves_like 'the menu entry is available'
end
end
end
@@ -107,24 +115,18 @@ RSpec.describe Sidebars::Groups::Menus::PackagesRegistriesMenu do
context 'when config registry setting is disabled' do
let(:container_enabled) { false }
- it 'the menu item is not added to list of menu items' do
- is_expected.to be_nil
- end
+ it_behaves_like 'the menu entry is not available'
end
context 'when config registry setting is enabled' do
let(:container_enabled) { true }
- it 'the menu item is added to list of menu items' do
- is_expected.not_to be_nil
- end
+ it_behaves_like 'the menu entry is available'
context 'when user cannot read container images' do
let(:user) { nil }
- it 'the menu item is not added to list of menu items' do
- is_expected.to be_nil
- end
+ it_behaves_like 'the menu entry is not available'
end
end
end
@@ -141,17 +143,28 @@ RSpec.describe Sidebars::Groups::Menus::PackagesRegistriesMenu do
context 'when config dependency_proxy is enabled' do
let(:dependency_enabled) { true }
- it 'the menu item is added to list of menu items' do
- is_expected.not_to be_nil
+ it_behaves_like 'the menu entry is available'
+
+ context 'when the group settings exist' do
+ let_it_be(:dependency_proxy_group_setting) { create(:dependency_proxy_group_setting, group: group) }
+
+ it_behaves_like 'the menu entry is available'
+
+ context 'when the proxy is disabled at the group level' do
+ before do
+ dependency_proxy_group_setting.enabled = false
+ dependency_proxy_group_setting.save!
+ end
+
+ it_behaves_like 'the menu entry is not available'
+ end
end
end
context 'when config dependency_proxy is not enabled' do
let(:dependency_enabled) { false }
- it 'the menu item is not added to list of menu items' do
- is_expected.to be_nil
- end
+ it_behaves_like 'the menu entry is not available'
end
end
@@ -159,9 +172,7 @@ RSpec.describe Sidebars::Groups::Menus::PackagesRegistriesMenu do
let(:user) { nil }
let(:dependency_enabled) { true }
- it 'the menu item is not added to list of menu items' do
- is_expected.to be_nil
- end
+ it_behaves_like 'the menu entry is not available'
end
end
end
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 20d8016fae2..af810572106 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -753,7 +753,7 @@ RSpec.describe Ci::Runner do
runner.created_at = 1.day.ago
end
- it { is_expected.to eq(:not_connected) }
+ it { is_expected.to eq(:never_contacted) }
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index d10f1405a7b..3f9c3bc6858 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -124,7 +124,7 @@ RSpec.describe User do
it { is_expected.to have_many(:created_custom_emoji).inverse_of(:creator) }
it { is_expected.to have_many(:in_product_marketing_emails) }
it { is_expected.to have_many(:timelogs) }
- it { is_expected.to have_many(:callouts).class_name('UserCallout') }
+ it { is_expected.to have_many(:callouts).class_name('Users::Callout') }
it { is_expected.to have_many(:group_callouts).class_name('Users::GroupCallout') }
describe '#user_detail' do
@@ -5589,7 +5589,7 @@ RSpec.describe User do
describe '#dismissed_callout?' do
let_it_be(:user, refind: true) { create(:user) }
- let_it_be(:feature_name) { UserCallout.feature_names.each_key.first }
+ let_it_be(:feature_name) { Users::Callout.feature_names.each_key.first }
context 'when no callout dismissal record exists' do
it 'returns false when no ignore_dismissal_earlier_than provided' do
@@ -5599,7 +5599,7 @@ RSpec.describe User do
context 'when dismissed callout exists' do
before_all do
- create(:user_callout, user: user, feature_name: feature_name, dismissed_at: 4.months.ago)
+ create(:callout, user: user, feature_name: feature_name, dismissed_at: 4.months.ago)
end
it 'returns true when no ignore_dismissal_earlier_than provided' do
@@ -5618,12 +5618,12 @@ RSpec.describe User do
describe '#find_or_initialize_callout' do
let_it_be(:user, refind: true) { create(:user) }
- let_it_be(:feature_name) { UserCallout.feature_names.each_key.first }
+ let_it_be(:feature_name) { Users::Callout.feature_names.each_key.first }
subject(:find_or_initialize_callout) { user.find_or_initialize_callout(feature_name) }
context 'when callout exists' do
- let!(:callout) { create(:user_callout, user: user, feature_name: feature_name) }
+ let!(:callout) { create(:callout, user: user, feature_name: feature_name) }
it 'returns existing callout' do
expect(find_or_initialize_callout).to eq(callout)
@@ -5633,7 +5633,7 @@ RSpec.describe User do
context 'when callout does not exist' do
context 'when feature name is valid' do
it 'initializes a new callout' do
- expect(find_or_initialize_callout).to be_a_new(UserCallout)
+ expect(find_or_initialize_callout).to be_a_new(Users::Callout)
end
it 'is valid' do
@@ -5645,7 +5645,7 @@ RSpec.describe User do
let(:feature_name) { 'notvalid' }
it 'initializes a new callout' do
- expect(find_or_initialize_callout).to be_a_new(UserCallout)
+ expect(find_or_initialize_callout).to be_a_new(Users::Callout)
end
it 'is not valid' do
diff --git a/spec/models/user_callout_spec.rb b/spec/models/users/callout_spec.rb
similarity index 80%
rename from spec/models/user_callout_spec.rb
rename to spec/models/users/callout_spec.rb
index 5b36c8450ea..293f0279e79 100644
--- a/spec/models/user_callout_spec.rb
+++ b/spec/models/users/callout_spec.rb
@@ -2,8 +2,8 @@
require 'spec_helper'
-RSpec.describe UserCallout do
- let_it_be(:callout) { create(:user_callout) }
+RSpec.describe Users::Callout do
+ let_it_be(:callout) { create(:callout) }
it_behaves_like 'having unique enum values'
diff --git a/spec/models/concerns/calloutable_spec.rb b/spec/models/users/calloutable_spec.rb
similarity index 57%
rename from spec/models/concerns/calloutable_spec.rb
rename to spec/models/users/calloutable_spec.rb
index d847413de88..01603d8bbd6 100644
--- a/spec/models/concerns/calloutable_spec.rb
+++ b/spec/models/users/calloutable_spec.rb
@@ -2,8 +2,8 @@
require 'spec_helper'
-RSpec.describe Calloutable do
- subject { build(:user_callout) }
+RSpec.describe Users::Calloutable do
+ subject { build(:callout) }
describe "Associations" do
it { is_expected.to belong_to(:user) }
@@ -14,9 +14,9 @@ RSpec.describe Calloutable do
end
describe '#dismissed_after?' do
- let(:some_feature_name) { UserCallout.feature_names.keys.second }
- let(:callout_dismissed_month_ago) { create(:user_callout, feature_name: some_feature_name, dismissed_at: 1.month.ago )}
- let(:callout_dismissed_day_ago) { create(:user_callout, feature_name: some_feature_name, dismissed_at: 1.day.ago )}
+ let(:some_feature_name) { Users::Callout.feature_names.keys.second }
+ let(:callout_dismissed_month_ago) { create(:callout, feature_name: some_feature_name, dismissed_at: 1.month.ago )}
+ let(:callout_dismissed_day_ago) { create(:callout, feature_name: some_feature_name, dismissed_at: 1.day.ago )}
it 'returns whether a callout dismissed after specified date' do
expect(callout_dismissed_month_ago.dismissed_after?(15.days.ago)).to eq(false)
diff --git a/spec/presenters/blob_presenter_spec.rb b/spec/presenters/blob_presenter_spec.rb
index 6dbcb5cace7..8c0347b3c8d 100644
--- a/spec/presenters/blob_presenter_spec.rb
+++ b/spec/presenters/blob_presenter_spec.rb
@@ -159,27 +159,25 @@ RSpec.describe BlobPresenter do
presenter.highlight
end
end
- end
- describe '#highlight_transformed' do
context 'when blob is ipynb' do
let(:blob) { repository.blob_at('f6b7a707', 'files/ipython/markdown-table.ipynb') }
let(:git_blob) { blob.__getobj__ }
before do
- allow(git_blob).to receive(:transformed_for_diff).and_return(true)
+ allow(Gitlab::Diff::CustomDiff).to receive(:transformed_for_diff?).and_return(true)
end
it 'uses md as the transformed language' do
expect(Gitlab::Highlight).to receive(:highlight).with('files/ipython/markdown-table.ipynb', anything, plain: nil, language: 'md')
- presenter.highlight_transformed
+ presenter.highlight
end
it 'transforms the blob' do
expect(Gitlab::Highlight).to receive(:highlight).with('files/ipython/markdown-table.ipynb', include("%%"), plain: nil, language: 'md')
- presenter.highlight_transformed
+ presenter.highlight
end
end
@@ -197,7 +195,7 @@ RSpec.describe BlobPresenter do
it 'does not transform the file' do
expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: nil, language: 'ruby')
- presenter.highlight_transformed
+ presenter.highlight
end
end
end
diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb
index 66601c0e810..98d3a3b1c51 100644
--- a/spec/requests/api/graphql/ci/runner_spec.rb
+++ b/spec/requests/api/graphql/ci/runner_spec.rb
@@ -223,20 +223,22 @@ RSpec.describe 'Query.runner(id)' do
describe 'for runner with status' do
let_it_be(:stale_runner) { create(:ci_runner, description: 'Stale runner 1', created_at: 3.months.ago) }
+ let_it_be(:never_contacted_instance_runner) { create(:ci_runner, description: 'Missing runner 1', created_at: 1.month.ago, contacted_at: nil) }
+
+ let(:status_fragment) do
+ %(
+ status
+ legacyStatusWithExplicitVersion: status(legacyMode: "14.5")
+ newStatus: status(legacyMode: null)
+ )
+ end
let(:query) do
%(
query {
- staleRunner: runner(id: "#{stale_runner.to_global_id}") {
- status
- legacyStatusWithExplicitVersion: status(legacyMode: "14.5")
- newStatus: status(legacyMode: null)
- }
- pausedRunner: runner(id: "#{inactive_instance_runner.to_global_id}") {
- status
- legacyStatusWithExplicitVersion: status(legacyMode: "14.5")
- newStatus: status(legacyMode: null)
- }
+ staleRunner: runner(id: "#{stale_runner.to_global_id}") { #{status_fragment} }
+ pausedRunner: runner(id: "#{inactive_instance_runner.to_global_id}") { #{status_fragment} }
+ neverContactedInstanceRunner: runner(id: "#{never_contacted_instance_runner.to_global_id}") { #{status_fragment} }
}
)
end
@@ -257,6 +259,13 @@ RSpec.describe 'Query.runner(id)' do
'legacyStatusWithExplicitVersion' => 'PAUSED',
'newStatus' => 'OFFLINE'
)
+
+ never_contacted_instance_runner_data = graphql_data_at(:never_contacted_instance_runner)
+ expect(never_contacted_instance_runner_data).to match a_hash_including(
+ 'status' => 'NOT_CONNECTED',
+ 'legacyStatusWithExplicitVersion' => 'NOT_CONNECTED',
+ 'newStatus' => 'NEVER_CONTACTED'
+ )
end
end
diff --git a/spec/requests/api/graphql/ci/runners_spec.rb b/spec/requests/api/graphql/ci/runners_spec.rb
index 51a07e60e15..267dd1b5e6f 100644
--- a/spec/requests/api/graphql/ci/runners_spec.rb
+++ b/spec/requests/api/graphql/ci/runners_spec.rb
@@ -62,6 +62,15 @@ RSpec.describe 'Query.runners' do
it_behaves_like 'a working graphql query returning expected runner'
end
+
+ context 'runner_type is PROJECT_TYPE and status is NEVER_CONTACTED' do
+ let(:runner_type) { 'PROJECT_TYPE' }
+ let(:status) { 'NEVER_CONTACTED' }
+
+ let!(:expected_runner) { project_runner }
+
+ it_behaves_like 'a working graphql query returning expected runner'
+ end
end
describe 'pagination' do
diff --git a/spec/requests/api/graphql/mutations/user_callouts/create_spec.rb b/spec/requests/api/graphql/mutations/user_callouts/create_spec.rb
index 716983f01d2..28a46583d2a 100644
--- a/spec/requests/api/graphql/mutations/user_callouts/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/user_callouts/create_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe 'Create a user callout' do
let_it_be(:current_user) { create(:user) }
- let(:feature_name) { ::UserCallout.feature_names.each_key.first }
+ let(:feature_name) { ::Users::Callout.feature_names.each_key.first }
let(:input) do
{
diff --git a/spec/serializers/merge_request_widget_entity_spec.rb b/spec/serializers/merge_request_widget_entity_spec.rb
index 3e0c61a26c0..1712df6266c 100644
--- a/spec/serializers/merge_request_widget_entity_spec.rb
+++ b/spec/serializers/merge_request_widget_entity_spec.rb
@@ -252,7 +252,7 @@ RSpec.describe MergeRequestWidgetEntity do
subject { described_class.new(resource, request: request).as_json }
it 'provides a valid path value for user callout path' do
- expect(subject[:user_callouts_path]).to eq '/-/user_callouts'
+ expect(subject[:user_callouts_path]).to eq '/-/users/callouts'
end
it 'provides a valid value for suggest pipeline feature id' do
@@ -362,7 +362,7 @@ RSpec.describe MergeRequestWidgetEntity do
context 'when suggest pipeline has been dismissed' do
before do
- create(:user_callout, user: user, feature_name: described_class::SUGGEST_PIPELINE)
+ create(:callout, user: user, feature_name: described_class::SUGGEST_PIPELINE)
end
it 'is true' do
diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb
index e3e2f5b59da..5d56084faa8 100644
--- a/spec/services/ci/retry_build_service_spec.rb
+++ b/spec/services/ci/retry_build_service_spec.rb
@@ -317,7 +317,7 @@ RSpec.describe Ci::RetryBuildService do
expect(build).to be_processed
end
- context 'when build with deployment is retried' do
+ shared_examples_for 'when build with deployment is retried' do
let!(:build) do
create(:ci_build, :with_deployment, :deploy_to_production,
pipeline: pipeline, stage_id: stage.id, project: project)
@@ -336,7 +336,7 @@ RSpec.describe Ci::RetryBuildService do
end
end
- context 'when build with dynamic environment is retried' do
+ shared_examples_for 'when build with dynamic environment is retried' do
let_it_be(:other_developer) { create(:user).tap { |u| project.add_developer(other_developer) } }
let(:environment_name) { 'review/$CI_COMMIT_REF_SLUG-$GITLAB_USER_ID' }
@@ -363,6 +363,18 @@ RSpec.describe Ci::RetryBuildService do
end
end
+ it_behaves_like 'when build with deployment is retried'
+ it_behaves_like 'when build with dynamic environment is retried'
+
+ context 'when create_deployment_in_separate_transaction feature flag is disabled' do
+ before do
+ stub_feature_flags(create_deployment_in_separate_transaction: false)
+ end
+
+ it_behaves_like 'when build with deployment is retried'
+ it_behaves_like 'when build with dynamic environment is retried'
+ end
+
context 'when build has needs' do
before do
create(:ci_build_need, build: build, name: 'build1')
diff --git a/spec/services/users/dismiss_user_callout_service_spec.rb b/spec/services/users/dismiss_callout_service_spec.rb
similarity index 63%
rename from spec/services/users/dismiss_user_callout_service_spec.rb
rename to spec/services/users/dismiss_callout_service_spec.rb
index 6bf9961eb74..6ba9f180444 100644
--- a/spec/services/users/dismiss_user_callout_service_spec.rb
+++ b/spec/services/users/dismiss_callout_service_spec.rb
@@ -2,12 +2,12 @@
require 'spec_helper'
-RSpec.describe Users::DismissUserCalloutService do
+RSpec.describe Users::DismissCalloutService do
describe '#execute' do
let_it_be(:user) { create(:user) }
let(:params) { { feature_name: feature_name } }
- let(:feature_name) { UserCallout.feature_names.each_key.first }
+ let(:feature_name) { Users::Callout.feature_names.each_key.first }
subject(:execute) do
described_class.new(
@@ -15,6 +15,6 @@ RSpec.describe Users::DismissUserCalloutService do
).execute
end
- it_behaves_like 'dismissing user callout', UserCallout
+ it_behaves_like 'dismissing user callout', Users::Callout
end
end
diff --git a/spec/support/helpers/stub_gitlab_calls.rb b/spec/support/helpers/stub_gitlab_calls.rb
index ef3c39c83c2..ae031f58bd4 100644
--- a/spec/support/helpers/stub_gitlab_calls.rb
+++ b/spec/support/helpers/stub_gitlab_calls.rb
@@ -93,7 +93,7 @@ module StubGitlabCalls
def stub_commonmark_sourcepos_disabled
render_options =
- if Feature.enabled?(:use_cmark_renderer)
+ if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
Banzai::Filter::MarkdownEngines::CommonMark::RENDER_OPTIONS_C
else
Banzai::Filter::MarkdownEngines::CommonMark::RENDER_OPTIONS_RUBY
diff --git a/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb b/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb
index c06083ba952..6e8c340582a 100644
--- a/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb
+++ b/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb
@@ -1,7 +1,11 @@
# frozen_string_literal: true
RSpec.shared_examples 'store ActiveRecord info in RequestStore' do |db_role|
- let(:db_config_name) { ::Gitlab::Database.db_config_names.first }
+ let(:db_config_name) do
+ db_config_name = ::Gitlab::Database.db_config_names.first
+ db_config_name += "_replica" if db_role == :secondary
+ db_config_name
+ end
let(:expected_payload_defaults) do
result = {}
@@ -39,15 +43,15 @@ RSpec.shared_examples 'store ActiveRecord info in RequestStore' do |db_role|
db_write_count: record_write_query ? 1 : 0,
db_cached_count: record_cached_query ? 1 : 0,
db_primary_cached_count: record_cached_query ? 1 : 0,
- "db_primary_#{db_config_name}_cached_count": record_cached_query ? 1 : 0,
+ "db_#{db_config_name}_cached_count": record_cached_query ? 1 : 0,
db_primary_count: record_query ? 1 : 0,
- "db_primary_#{db_config_name}_count": record_query ? 1 : 0,
+ "db_#{db_config_name}_count": record_query ? 1 : 0,
db_primary_duration_s: record_query ? 0.002 : 0.0,
- "db_primary_#{db_config_name}_duration_s": record_query ? 0.002 : 0.0,
+ "db_#{db_config_name}_duration_s": record_query ? 0.002 : 0.0,
db_primary_wal_count: record_wal_query ? 1 : 0,
- "db_primary_#{db_config_name}_wal_count": record_wal_query ? 1 : 0,
+ "db_#{db_config_name}_wal_count": record_wal_query ? 1 : 0,
db_primary_wal_cached_count: record_wal_query && record_cached_query ? 1 : 0,
- "db_primary_#{db_config_name}_wal_cached_count": record_wal_query && record_cached_query ? 1 : 0
+ "db_#{db_config_name}_wal_cached_count": record_wal_query && record_cached_query ? 1 : 0
})
elsif db_role == :replica
transform_hash(expected_payload_defaults, {
@@ -55,15 +59,15 @@ RSpec.shared_examples 'store ActiveRecord info in RequestStore' do |db_role|
db_write_count: record_write_query ? 1 : 0,
db_cached_count: record_cached_query ? 1 : 0,
db_replica_cached_count: record_cached_query ? 1 : 0,
- "db_replica_#{db_config_name}_cached_count": record_cached_query ? 1 : 0,
+ "db_#{db_config_name}_cached_count": record_cached_query ? 1 : 0,
db_replica_count: record_query ? 1 : 0,
- "db_replica_#{db_config_name}_count": record_query ? 1 : 0,
+ "db_#{db_config_name}_count": record_query ? 1 : 0,
db_replica_duration_s: record_query ? 0.002 : 0.0,
- "db_replica_#{db_config_name}_duration_s": record_query ? 0.002 : 0.0,
+ "db_#{db_config_name}_duration_s": record_query ? 0.002 : 0.0,
db_replica_wal_count: record_wal_query ? 1 : 0,
- "db_replica_#{db_config_name}_wal_count": record_wal_query ? 1 : 0,
+ "db_#{db_config_name}_wal_count": record_wal_query ? 1 : 0,
db_replica_wal_cached_count: record_wal_query && record_cached_query ? 1 : 0,
- "db_replica_#{db_config_name}_wal_cached_count": record_wal_query && record_cached_query ? 1 : 0
+ "db_#{db_config_name}_wal_cached_count": record_wal_query && record_cached_query ? 1 : 0
})
else
transform_hash(expected_payload_defaults, {
@@ -71,15 +75,15 @@ RSpec.shared_examples 'store ActiveRecord info in RequestStore' do |db_role|
db_write_count: record_write_query ? 1 : 0,
db_cached_count: record_cached_query ? 1 : 0,
db_primary_cached_count: 0,
- "db_primary_#{db_config_name}_cached_count": 0,
+ "db_#{db_config_name}_cached_count": 0,
db_primary_count: 0,
- "db_primary_#{db_config_name}_count": 0,
+ "db_#{db_config_name}_count": 0,
db_primary_duration_s: 0.0,
- "db_primary_#{db_config_name}_duration_s": 0.0,
+ "db_#{db_config_name}_duration_s": 0.0,
db_primary_wal_count: 0,
- "db_primary_#{db_config_name}_wal_count": 0,
+ "db_#{db_config_name}_wal_count": 0,
db_primary_wal_cached_count: 0,
- "db_primary_#{db_config_name}_wal_cached_count": 0
+ "db_#{db_config_name}_wal_cached_count": 0
})
end
@@ -105,7 +109,11 @@ RSpec.shared_examples 'store ActiveRecord info in RequestStore' do |db_role|
end
RSpec.shared_examples 'record ActiveRecord metrics in a metrics transaction' do |db_role|
- let(:db_config_name) { ::Gitlab::Database.db_config_name(ApplicationRecord.retrieve_connection) }
+ let(:db_config_name) do
+ db_config_name = ::Gitlab::Database.db_config_names.first
+ db_config_name += "_replica" if db_role == :secondary
+ db_config_name
+ end
it 'increments only db counters' do
if record_query
diff --git a/spec/tasks/gitlab/db_rake_spec.rb b/spec/tasks/gitlab/db_rake_spec.rb
index 17e6af2aedb..48b879fea26 100644
--- a/spec/tasks/gitlab/db_rake_spec.rb
+++ b/spec/tasks/gitlab/db_rake_spec.rb
@@ -138,6 +138,10 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do
stub_file_read(structure_file, content: input)
allow(File).to receive(:open).with(structure_file.to_s, any_args).and_yield(output)
end
+
+ if Gitlab.ee?
+ allow(File).to receive(:open).with(Rails.root.join(Gitlab::Database::GEO_DATABASE_DIR, 'structure.sql').to_s, any_args).and_yield(output)
+ end
end
after do
@@ -328,6 +332,32 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do
end
end
+ context 'with multiple databases', :reestablished_active_record_base do
+ before do
+ allow(ActiveRecord::Tasks::DatabaseTasks).to receive(:setup_initial_database_yaml).and_return([:main, :geo])
+ end
+
+ describe 'db:structure:dump' do
+ it 'invokes gitlab:db:clean_structure_sql' do
+ skip unless Gitlab.ee?
+
+ expect(Rake::Task['gitlab:db:clean_structure_sql']).to receive(:invoke).twice.and_return(true)
+
+ expect { run_rake_task('db:structure:dump:main') }.not_to raise_error
+ end
+ end
+
+ describe 'db:schema:dump' do
+ it 'invokes gitlab:db:clean_structure_sql' do
+ skip unless Gitlab.ee?
+
+ expect(Rake::Task['gitlab:db:clean_structure_sql']).to receive(:invoke).once.and_return(true)
+
+ expect { run_rake_task('db:schema:dump:main') }.not_to raise_error
+ end
+ end
+ end
+
def run_rake_task(task_name, arguments = '')
Rake::Task[task_name].reenable
Rake.application.invoke_task("#{task_name}#{arguments}")
diff --git a/spec/views/projects/jobs/show.html.haml_spec.rb b/spec/views/projects/jobs/show.html.haml_spec.rb
index 83a00135629..8242d20a9e7 100644
--- a/spec/views/projects/jobs/show.html.haml_spec.rb
+++ b/spec/views/projects/jobs/show.html.haml_spec.rb
@@ -13,26 +13,47 @@ RSpec.describe 'projects/jobs/show' do
end
before do
- assign(:build, build.present)
assign(:project, project)
assign(:builds, builds)
allow(view).to receive(:can?).and_return(true)
end
- context 'when job is running' do
- let(:build) { create(:ci_build, :trace_live, :running, pipeline: pipeline) }
-
+ context 'when showing a CI build' do
before do
+ assign(:build, build.present)
render
end
- it 'does not show retry button' do
- expect(rendered).not_to have_link('Retry')
+ it 'shows job vue app' do
+ expect(rendered).to have_css('#js-job-page')
+ expect(rendered).not_to have_css('#js-bridge-page')
end
- it 'does not show New issue button' do
- expect(rendered).not_to have_link('New issue')
+ context 'when job is running' do
+ let(:build) { create(:ci_build, :trace_live, :running, pipeline: pipeline) }
+
+ it 'does not show retry button' do
+ expect(rendered).not_to have_link('Retry')
+ end
+
+ it 'does not show New issue button' do
+ expect(rendered).not_to have_link('New issue')
+ end
+ end
+ end
+
+ context 'when showing a bridge job' do
+ let(:bridge) { create(:ci_bridge, status: :pending) }
+
+ before do
+ assign(:build, bridge)
+ render
+ end
+
+ it 'shows bridge vue app' do
+ expect(rendered).to have_css('#js-bridge-page')
+ expect(rendered).not_to have_css('#js-job-page')
end
end
end