From 256c5ea115fccfa52f1eb4cac8bf9530eecc1751 Mon Sep 17 00:00:00 2001
From: GitLab Bot
Date: Fri, 10 Mar 2023 21:12:47 +0000
Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master
---
.gitlab/CODEOWNERS | 4 +-
.../merge_request_templates/Stable Branch.md | 8 +-
GITALY_SERVER_VERSION | 2 +-
.../batch_comments/components/draft_note.vue | 1 +
.../components/group_select.vue | 1 +
.../components/self_monitor_form.vue | 34 +++-
app/graphql/mutations/projects/sync_fork.rb | 3 +
app/helpers/nav_helper.rb | 2 +-
app/helpers/sidebars_helper.rb | 3 +
.../layouts/nav/sidebar/_profile.html.haml | 170 +-----------------
.../nav/_user_settings_scope_header.html.haml | 4 +
.../reduce_sub_batch_size_on_timeouts.yml | 8 +
.../development/synchronize_fork.yml | 8 +
doc/update/index.md | 9 +-
lib/api/merge_requests.rb | 4 +
.../batched_migration_job.rb | 15 +-
.../background_migration/batched_job.rb | 83 +++++++--
.../batched_migration_wrapper.rb | 13 +-
.../sub_batch_timeout_error.rb | 17 ++
.../database/schema_validation/database.rb | 50 +++++-
.../database/schema_validation/index.rb | 25 ---
.../database/schema_validation/indexes.rb | 37 ----
.../schema_validation/schema_objects/base.rb | 27 +++
.../schema_validation/schema_objects/index.rb | 15 ++
.../schema_objects/trigger.rb | 15 ++
.../schema_validation/structure_sql.rb | 39 +++-
.../validators/base_validator.rb | 5 +-
.../different_definition_triggers.rb | 22 +++
.../validators/extra_triggers.rb | 19 ++
.../validators/missing_triggers.rb | 19 ++
lib/sidebars/concerns/render_if_logged_in.rb | 11 ++
.../user_settings/menus/access_tokens_menu.rb | 39 ++++
.../user_settings/menus/account_menu.rb | 36 ++++
.../menus/active_sessions_menu.rb | 31 ++++
.../user_settings/menus/applications_menu.rb | 31 ++++
.../menus/authentication_log_menu.rb | 31 ++++
lib/sidebars/user_settings/menus/chat_menu.rb | 31 ++++
.../user_settings/menus/emails_menu.rb | 36 ++++
.../user_settings/menus/gpg_keys_menu.rb | 31 ++++
.../user_settings/menus/notifications_menu.rb | 31 ++++
.../user_settings/menus/password_menu.rb | 39 ++++
.../user_settings/menus/preferences_menu.rb | 31 ++++
.../user_settings/menus/profile_menu.rb | 31 ++++
.../user_settings/menus/saved_replies_menu.rb | 42 +++++
.../user_settings/menus/ssh_keys_menu.rb | 36 ++++
lib/sidebars/user_settings/panel.rb | 51 ++++++
lib/tasks/gitlab/tw/codeowners.rake | 2 +-
locale/gitlab.pot | 9 +-
qa/qa/page/profile/menu.rb | 24 ++-
..._visits_profile_authentication_log_spec.rb | 2 +-
spec/features/signed_commits_spec.rb | 2 +-
spec/fixtures/structure.sql | 8 +
.../components/group_select_spec.js | 1 +
.../self_monitor_form_spec.js.snap | 35 ++++
spec/helpers/sidebars_helper_spec.rb | 4 +
.../batched_migration_job_spec.rb | 22 +++
.../background_migration/batched_job_spec.rb | 165 +++++++++++++++++
.../batched_migration_wrapper_spec.rb | 15 +-
.../schema_validation/database_spec.rb | 119 ++++++++----
.../database/schema_validation/index_spec.rb | 22 ---
.../database/schema_validation/runner_spec.rb | 2 +-
.../schema_objects/index_spec.rb | 10 ++
.../schema_objects/trigger_spec.rb | 10 ++
.../schema_validation/structure_sql_spec.rb | 83 ++++++---
.../validators/base_validator_spec.rb | 5 +-
.../different_definition_triggers_spec.rb | 8 +
.../validators/extra_triggers_spec.rb | 7 +
.../validators/missing_triggers_spec.rb | 9 +
.../menus/access_tokens_menu_spec.rb | 65 +++++++
.../user_settings/menus/account_menu_spec.rb | 13 ++
.../menus/active_sessions_menu_spec.rb | 13 ++
.../menus/applications_menu_spec.rb | 13 ++
.../menus/authentication_log_menu_spec.rb | 13 ++
.../user_settings/menus/chat_menu_spec.rb | 13 ++
.../user_settings/menus/emails_menu_spec.rb | 13 ++
.../user_settings/menus/gpg_keys_menu_spec.rb | 13 ++
.../menus/notifications_menu_spec.rb | 13 ++
.../user_settings/menus/password_menu_spec.rb | 38 ++++
.../menus/preferences_menu_spec.rb | 13 ++
.../user_settings/menus/profile_menu_spec.rb | 13 ++
.../menus/saved_replies_menu_spec.rb | 65 +++++++
.../user_settings/menus/ssh_keys_menu_spec.rb | 13 ++
spec/lib/sidebars/user_settings/panel_spec.rb | 15 ++
...ill_is_finished_for_gitlab_dot_com_spec.rb | 2 +-
.../mutations/projects/sync_fork_spec.rb | 18 ++
spec/requests/api/merge_requests_spec.rb | 11 ++
.../background_migrations_matchers.rb | 2 +-
.../index_validators_shared_examples.rb | 6 +-
.../schema_objects_shared_examples.rb | 20 +++
.../trigger_validators_shared_examples.rb | 33 ++++
.../user_settings_menus_shared_examples.rb | 52 ++++++
91 files changed, 1856 insertions(+), 378 deletions(-)
create mode 100644 app/views/shared/nav/_user_settings_scope_header.html.haml
create mode 100644 config/feature_flags/development/reduce_sub_batch_size_on_timeouts.yml
create mode 100644 config/feature_flags/development/synchronize_fork.yml
create mode 100644 lib/gitlab/database/background_migration/sub_batch_timeout_error.rb
delete mode 100644 lib/gitlab/database/schema_validation/index.rb
delete mode 100644 lib/gitlab/database/schema_validation/indexes.rb
create mode 100644 lib/gitlab/database/schema_validation/schema_objects/base.rb
create mode 100644 lib/gitlab/database/schema_validation/schema_objects/index.rb
create mode 100644 lib/gitlab/database/schema_validation/schema_objects/trigger.rb
create mode 100644 lib/gitlab/database/schema_validation/validators/different_definition_triggers.rb
create mode 100644 lib/gitlab/database/schema_validation/validators/extra_triggers.rb
create mode 100644 lib/gitlab/database/schema_validation/validators/missing_triggers.rb
create mode 100644 lib/sidebars/concerns/render_if_logged_in.rb
create mode 100644 lib/sidebars/user_settings/menus/access_tokens_menu.rb
create mode 100644 lib/sidebars/user_settings/menus/account_menu.rb
create mode 100644 lib/sidebars/user_settings/menus/active_sessions_menu.rb
create mode 100644 lib/sidebars/user_settings/menus/applications_menu.rb
create mode 100644 lib/sidebars/user_settings/menus/authentication_log_menu.rb
create mode 100644 lib/sidebars/user_settings/menus/chat_menu.rb
create mode 100644 lib/sidebars/user_settings/menus/emails_menu.rb
create mode 100644 lib/sidebars/user_settings/menus/gpg_keys_menu.rb
create mode 100644 lib/sidebars/user_settings/menus/notifications_menu.rb
create mode 100644 lib/sidebars/user_settings/menus/password_menu.rb
create mode 100644 lib/sidebars/user_settings/menus/preferences_menu.rb
create mode 100644 lib/sidebars/user_settings/menus/profile_menu.rb
create mode 100644 lib/sidebars/user_settings/menus/saved_replies_menu.rb
create mode 100644 lib/sidebars/user_settings/menus/ssh_keys_menu.rb
create mode 100644 lib/sidebars/user_settings/panel.rb
delete mode 100644 spec/lib/gitlab/database/schema_validation/index_spec.rb
create mode 100644 spec/lib/gitlab/database/schema_validation/schema_objects/index_spec.rb
create mode 100644 spec/lib/gitlab/database/schema_validation/schema_objects/trigger_spec.rb
create mode 100644 spec/lib/gitlab/database/schema_validation/validators/different_definition_triggers_spec.rb
create mode 100644 spec/lib/gitlab/database/schema_validation/validators/extra_triggers_spec.rb
create mode 100644 spec/lib/gitlab/database/schema_validation/validators/missing_triggers_spec.rb
create mode 100644 spec/lib/sidebars/user_settings/menus/access_tokens_menu_spec.rb
create mode 100644 spec/lib/sidebars/user_settings/menus/account_menu_spec.rb
create mode 100644 spec/lib/sidebars/user_settings/menus/active_sessions_menu_spec.rb
create mode 100644 spec/lib/sidebars/user_settings/menus/applications_menu_spec.rb
create mode 100644 spec/lib/sidebars/user_settings/menus/authentication_log_menu_spec.rb
create mode 100644 spec/lib/sidebars/user_settings/menus/chat_menu_spec.rb
create mode 100644 spec/lib/sidebars/user_settings/menus/emails_menu_spec.rb
create mode 100644 spec/lib/sidebars/user_settings/menus/gpg_keys_menu_spec.rb
create mode 100644 spec/lib/sidebars/user_settings/menus/notifications_menu_spec.rb
create mode 100644 spec/lib/sidebars/user_settings/menus/password_menu_spec.rb
create mode 100644 spec/lib/sidebars/user_settings/menus/preferences_menu_spec.rb
create mode 100644 spec/lib/sidebars/user_settings/menus/profile_menu_spec.rb
create mode 100644 spec/lib/sidebars/user_settings/menus/saved_replies_menu_spec.rb
create mode 100644 spec/lib/sidebars/user_settings/menus/ssh_keys_menu_spec.rb
create mode 100644 spec/lib/sidebars/user_settings/panel_spec.rb
create mode 100644 spec/support/shared_examples/lib/gitlab/database/schema_objects_shared_examples.rb
create mode 100644 spec/support/shared_examples/lib/gitlab/database/trigger_validators_shared_examples.rb
create mode 100644 spec/support/shared_examples/lib/sidebars/user_settings/menus/user_settings_menus_shared_examples.rb
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index 5b8c4f201ca..e6f9fe9767b 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -463,7 +463,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/administration/pages/ @ashrafkhamis
/doc/administration/polling.md @axil
/doc/administration/postgresql/ @aqualls
-/doc/administration/postgresql/multiple_databases.md @jglassman1
+/doc/administration/postgresql/multiple_databases.md @lciutacu
/doc/administration/raketasks/ @axil
/doc/administration/raketasks/ldap.md @jglassman1
/doc/administration/raketasks/praefect.md @eread
@@ -710,7 +710,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/development/contributing/ @sselhorn
/doc/development/database/ @aqualls
/doc/development/database/filtering_by_label.md @msedlakjakubowski
-/doc/development/database/multiple_databases.md @jglassman1
+/doc/development/database/multiple_databases.md @lciutacu
/doc/development/database_review.md @aqualls
/doc/development/developing_with_solargraph.md @aqualls
/doc/development/development_processes.md @sselhorn
diff --git a/.gitlab/merge_request_templates/Stable Branch.md b/.gitlab/merge_request_templates/Stable Branch.md
index e584296cbb1..9f1408e06bc 100644
--- a/.gitlab/merge_request_templates/Stable Branch.md
+++ b/.gitlab/merge_request_templates/Stable Branch.md
@@ -1,6 +1,10 @@
## What does this MR do and why?
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 5687640cd8a..1a6d66d7a86 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-37fd51c54395a06dc39322606f9a3e4ba2dafa3d
+ac7304c0ce981bf97dca39ac1017b31860c043b5
diff --git a/app/assets/javascripts/batch_comments/components/draft_note.vue b/app/assets/javascripts/batch_comments/components/draft_note.vue
index 9afe8181873..b78874d372c 100644
--- a/app/assets/javascripts/batch_comments/components/draft_note.vue
+++ b/app/assets/javascripts/batch_comments/components/draft_note.vue
@@ -99,6 +99,7 @@ export default {
diff --git a/app/assets/javascripts/invite_members/components/group_select.vue b/app/assets/javascripts/invite_members/components/group_select.vue
index e7f5211dc25..0e9781d77fe 100644
--- a/app/assets/javascripts/invite_members/components/group_select.vue
+++ b/app/assets/javascripts/invite_members/components/group_select.vue
@@ -114,6 +114,7 @@ export default {
defaultFetchOptions: {
exclude_internal: true,
active: true,
+ order_by: 'similarity',
},
};
diff --git a/app/assets/javascripts/self_monitor/components/self_monitor_form.vue b/app/assets/javascripts/self_monitor/components/self_monitor_form.vue
index d9e969e2278..e5a11487c90 100644
--- a/app/assets/javascripts/self_monitor/components/self_monitor_form.vue
+++ b/app/assets/javascripts/self_monitor/components/self_monitor_form.vue
@@ -1,5 +1,5 @@
@@ -140,6 +162,16 @@ export default {
{{ __('Learn more.') }}
+
+
+
+
+
+
+
diff --git a/spec/helpers/sidebars_helper_spec.rb b/spec/helpers/sidebars_helper_spec.rb
index d03c97442ad..f23de35877b 100644
--- a/spec/helpers/sidebars_helper_spec.rb
+++ b/spec/helpers/sidebars_helper_spec.rb
@@ -214,6 +214,10 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do
expect(helper.super_sidebar_nav_panel(nav: 'group')).to be_a(Sidebars::Groups::SuperSidebarPanel)
end
+ it 'returns User Settings Panel for profile nav' do
+ expect(helper.super_sidebar_nav_panel(nav: 'profile')).to be_a(Sidebars::UserSettings::Panel)
+ end
+
it 'returns "Your Work" Panel for your_work nav', :use_clean_rails_memory_store_caching do
expect(helper.super_sidebar_nav_panel(nav: 'your_work', user: user)).to be_a(Sidebars::YourWork::Panel)
end
diff --git a/spec/lib/gitlab/background_migration/batched_migration_job_spec.rb b/spec/lib/gitlab/background_migration/batched_migration_job_spec.rb
index faaaccfdfaf..781bf93dd85 100644
--- a/spec/lib/gitlab/background_migration/batched_migration_job_spec.rb
+++ b/spec/lib/gitlab/background_migration/batched_migration_job_spec.rb
@@ -301,6 +301,28 @@ RSpec.describe Gitlab::BackgroundMigration::BatchedMigrationJob do
perform_job
end
+ context 'when using a sub batch exception for timeouts' do
+ let(:job_class) do
+ Class.new(described_class) do
+ operation_name :update
+
+ def perform(*_)
+ each_sub_batch { raise ActiveRecord::StatementTimeout } # rubocop:disable Lint/UnreachableLoop
+ end
+ end
+ end
+
+ let(:job_instance) do
+ job_class.new(start_id: 1, end_id: 10, batch_table: '_test_table', batch_column: 'id',
+ sub_batch_size: 2, pause_ms: 1000, connection: connection,
+ sub_batch_exception: StandardError)
+ end
+
+ it 'raises the expected error type' do
+ expect { job_instance.perform }.to raise_error(StandardError)
+ end
+ end
+
context 'when batching_arguments are given' do
it 'forwards them for batching' do
expect(job_instance).to receive(:base_relation).and_return(test_table)
diff --git a/spec/lib/gitlab/database/background_migration/batched_job_spec.rb b/spec/lib/gitlab/database/background_migration/batched_job_spec.rb
index cc9f3d5b7f1..073a30e7839 100644
--- a/spec/lib/gitlab/database/background_migration/batched_job_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_job_spec.rb
@@ -184,6 +184,35 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
expect(transition_log.exception_message).to eq('RuntimeError')
end
end
+
+ context 'when job fails during sub batch processing' do
+ let(:args) { { error: ActiveRecord::StatementTimeout.new, from_sub_batch: true } }
+ let(:attempts) { 0 }
+ let(:failure) { job.failure!(**args) }
+ let(:job) do
+ create(:batched_background_migration_job, :running, batch_size: 20, sub_batch_size: 10, attempts: attempts)
+ end
+
+ context 'when sub batch size can be reduced in 25%' do
+ it { expect { failure }.to change { job.sub_batch_size }.to 7 }
+ end
+
+ context 'when retries exceeds 2 attempts' do
+ let(:attempts) { 3 }
+
+ before do
+ allow(job).to receive(:split_and_retry!)
+ end
+
+ it 'calls split_and_retry! once sub_batch_size cannot be decreased anymore' do
+ failure
+
+ expect(job).to have_received(:split_and_retry!).once
+ end
+
+ it { expect { failure }.not_to change { job.sub_batch_size } }
+ end
+ end
end
describe 'scopes' do
@@ -271,6 +300,24 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
end
end
+ describe '.extract_transition_options' do
+ let(:perform) { subject.class.extract_transition_options(args) }
+
+ where(:args, :expected_result) do
+ [
+ [[], []],
+ [[{ error: StandardError }], [StandardError, nil]],
+ [[{ error: StandardError, from_sub_batch: true }], [StandardError, true]]
+ ]
+ end
+
+ with_them do
+ it 'matches expected keys and result' do
+ expect(perform).to match_array(expected_result)
+ end
+ end
+ end
+
describe '#can_split?' do
subject { job.can_split?(exception) }
@@ -327,6 +374,48 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
end
end
+ describe '#can_reduce_sub_batch_size?' do
+ let(:attempts) { 0 }
+ let(:batch_size) { 10 }
+ let(:sub_batch_size) { 6 }
+ let(:feature_flag) { :reduce_sub_batch_size_on_timeouts }
+ let(:job) do
+ create(:batched_background_migration_job, attempts: attempts,
+ batch_size: batch_size, sub_batch_size: sub_batch_size)
+ end
+
+ where(:feature_flag_state, :within_boundaries, :outside_boundaries, :limit_reached) do
+ [
+ [true, true, false, false],
+ [false, false, false, false]
+ ]
+ end
+
+ with_them do
+ before do
+ stub_feature_flags(feature_flag => feature_flag_state)
+ end
+
+ context 'when the number of attempts is lower than the limit and batch size are within boundaries' do
+ let(:attempts) { 1 }
+
+ it { expect(job.can_reduce_sub_batch_size?).to be(within_boundaries) }
+ end
+
+ context 'when the number of attempts is lower than the limit and batch size are outside boundaries' do
+ let(:batch_size) { 1 }
+
+ it { expect(job.can_reduce_sub_batch_size?).to be(outside_boundaries) }
+ end
+
+ context 'when the number of attempts is greater than the limit and batch size are within boundaries' do
+ let(:attempts) { 3 }
+
+ it { expect(job.can_reduce_sub_batch_size?).to be(limit_reached) }
+ end
+ end
+ end
+
describe '#time_efficiency' do
subject { job.time_efficiency }
@@ -465,4 +554,80 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
end
end
end
+
+ describe '#reduce_sub_batch_size!' do
+ let(:migration_batch_size) { 20 }
+ let(:migration_sub_batch_size) { 10 }
+ let(:job_batch_size) { 20 }
+ let(:job_sub_batch_size) { 10 }
+ let(:status) { :failed }
+
+ let(:migration) do
+ create(:batched_background_migration, :active, batch_size: migration_batch_size,
+ sub_batch_size: migration_sub_batch_size)
+ end
+
+ let(:job) do
+ create(:batched_background_migration_job, status, sub_batch_size: job_sub_batch_size,
+ batch_size: job_batch_size, batched_migration: migration)
+ end
+
+ context 'when the job sub batch size can be reduced' do
+ let(:expected_sub_batch_size) { 7 }
+
+ it 'reduces sub batch size in 25%' do
+ expect { job.reduce_sub_batch_size! }.to change { job.sub_batch_size }.to(expected_sub_batch_size)
+ end
+
+ it 'log the changes' do
+ expect(Gitlab::AppLogger).to receive(:warn).with(
+ message: 'Sub batch size reduced due to timeout',
+ batched_job_id: job.id,
+ sub_batch_size: job_sub_batch_size,
+ reduced_sub_batch_size: expected_sub_batch_size,
+ attempts: job.attempts,
+ batched_migration_id: migration.id,
+ job_class_name: job.migration_job_class_name,
+ job_arguments: job.migration_job_arguments
+ )
+
+ job.reduce_sub_batch_size!
+ end
+ end
+
+ context 'when reduced sub_batch_size is greater than sub_batch' do
+ let(:job_batch_size) { 5 }
+
+ it "doesn't allow sub_batch_size to greater than sub_batch" do
+ expect { job.reduce_sub_batch_size! }.to change { job.sub_batch_size }.to 5
+ end
+ end
+
+ context 'when sub_batch_size is already 1' do
+ let(:job_sub_batch_size) { 1 }
+
+ it "updates sub_batch_size to it's minimum value" do
+ expect { job.reduce_sub_batch_size! }.not_to change { job.sub_batch_size }
+ end
+ end
+
+ context 'when job has not failed' do
+ let(:status) { :succeeded }
+ let(:error) { Gitlab::Database::BackgroundMigration::ReduceSubBatchSizeError }
+
+ it 'raises an exception' do
+ expect { job.reduce_sub_batch_size! }.to raise_error(error)
+ end
+ end
+
+ context 'when the amount to be reduced exceeds the threshold' do
+ let(:migration_batch_size) { 150 }
+ let(:migration_sub_batch_size) { 100 }
+ let(:job_sub_batch_size) { 30 }
+
+ it 'prevents sub batch size to be reduced' do
+ expect { job.reduce_sub_batch_size! }.not_to change { job.sub_batch_size }
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
index f3a292abbae..8d74d16f4e5 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
let(:connection) { Gitlab::Database.database_base_models[:main].connection }
let(:metrics_tracker) { instance_double('::Gitlab::Database::BackgroundMigration::PrometheusMetrics', track: nil) }
let(:job_class) { Class.new(Gitlab::BackgroundMigration::BatchedMigrationJob) }
+ let(:sub_batch_exception) { Gitlab::Database::BackgroundMigration::SubBatchTimeoutError }
let_it_be(:pause_ms) { 250 }
let_it_be(:active_migration) { create(:batched_background_migration, :active, job_arguments: [:id, :other_id]) }
@@ -39,7 +40,8 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
sub_batch_size: 1,
pause_ms: pause_ms,
job_arguments: active_migration.job_arguments,
- connection: connection)
+ connection: connection,
+ sub_batch_exception: sub_batch_exception)
.and_return(job_instance)
expect(job_instance).to receive(:perform).with(no_args)
@@ -119,12 +121,14 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
end
context 'when the migration job raises an error' do
- shared_examples 'an error is raised' do |error_class|
+ shared_examples 'an error is raised' do |error_class, cause|
+ let(:expected_to_raise) { cause || error_class }
+
it 'marks the tracking record as failed' do
expect(job_instance).to receive(:perform).with(no_args).and_raise(error_class)
freeze_time do
- expect { perform }.to raise_error(error_class)
+ expect { perform }.to raise_error(expected_to_raise)
reloaded_job_record = job_record.reload
@@ -137,13 +141,16 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
expect(job_instance).to receive(:perform).with(no_args).and_raise(error_class)
expect(metrics_tracker).to receive(:track).with(job_record)
- expect { perform }.to raise_error(error_class)
+ expect { perform }.to raise_error(expected_to_raise)
end
end
it_behaves_like 'an error is raised', RuntimeError.new('Something broke!')
it_behaves_like 'an error is raised', SignalException.new('SIGTERM')
it_behaves_like 'an error is raised', ActiveRecord::StatementTimeout.new('Timeout!')
+
+ error = StandardError.new
+ it_behaves_like('an error is raised', Gitlab::Database::BackgroundMigration::SubBatchTimeoutError.new(error), error)
end
context 'when the batched background migration does not inherit from BatchedMigrationJob' do
diff --git a/spec/lib/gitlab/database/schema_validation/database_spec.rb b/spec/lib/gitlab/database/schema_validation/database_spec.rb
index 68e12c90819..eadaf683a29 100644
--- a/spec/lib/gitlab/database/schema_validation/database_spec.rb
+++ b/spec/lib/gitlab/database/schema_validation/database_spec.rb
@@ -3,61 +3,108 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::SchemaValidation::Database, feature_category: :database do
- let(:database_name) { 'main' }
- let(:database_indexes) do
- [['index', 'CREATE UNIQUE INDEX "index" ON public.achievements USING btree (namespace_id, lower(name))']]
- end
-
- let(:query_result) { instance_double('ActiveRecord::Result', rows: database_indexes) }
- let(:database_model) { Gitlab::Database.database_base_models[database_name] }
- let(:connection) { database_model.connection }
-
subject(:database) { described_class.new(connection) }
- before do
- allow(connection).to receive(:exec_query).and_return(query_result)
- end
+ let(:database_model) { Gitlab::Database.database_base_models['main'] }
+ let(:connection) { database_model.connection }
- describe '#fetch_index_by_name' do
- context 'when index does not exist' do
- it 'returns nil' do
- index = database.fetch_index_by_name('non_existing_index')
+ context 'when having indexes' do
+ let(:schema_object) { Gitlab::Database::SchemaValidation::SchemaObjects::Index }
+ let(:results) do
+ [['index', 'CREATE UNIQUE INDEX "index" ON public.achievements USING btree (namespace_id, lower(name))']]
+ end
- expect(index).to be_nil
+ before do
+ allow(connection).to receive(:select_rows).and_return(results)
+ end
+
+ describe '#fetch_index_by_name' do
+ context 'when index does not exist' do
+ it 'returns nil' do
+ index = database.fetch_index_by_name('non_existing_index')
+
+ expect(index).to be_nil
+ end
+ end
+
+ it 'returns index by name' do
+ index = database.fetch_index_by_name('index')
+
+ expect(index.name).to eq('index')
end
end
- it 'returns index by name' do
- index = database.fetch_index_by_name('index')
+ describe '#index_exists?' do
+ context 'when index exists' do
+ it 'returns true' do
+ index_exists = database.index_exists?('index')
- expect(index.name).to eq('index')
- end
- end
+ expect(index_exists).to be_truthy
+ end
+ end
- describe '#index_exists?' do
- context 'when index exists' do
- it 'returns true' do
- index_exists = database.index_exists?('index')
+ context 'when index does not exist' do
+ it 'returns false' do
+ index_exists = database.index_exists?('non_existing_index')
- expect(index_exists).to be_truthy
+ expect(index_exists).to be_falsey
+ end
end
end
- context 'when index does not exist' do
- it 'returns false' do
- index_exists = database.index_exists?('non_existing_index')
+ describe '#indexes' do
+ it 'returns indexes' do
+ indexes = database.indexes
- expect(index_exists).to be_falsey
+ expect(indexes).to all(be_a(schema_object))
+ expect(indexes.map(&:name)).to eq(['index'])
end
end
end
- describe '#indexes' do
- it 'returns indexes' do
- indexes = database.indexes
+ context 'when having triggers' do
+ let(:schema_object) { Gitlab::Database::SchemaValidation::SchemaObjects::Trigger }
+ let(:results) do
+ { 'my_trigger' => 'CREATE TRIGGER my_trigger BEFORE INSERT ON todos FOR EACH ROW EXECUTE FUNCTION trigger()' }
+ end
- expect(indexes).to all(be_a(Gitlab::Database::SchemaValidation::Index))
- expect(indexes.map(&:name)).to eq(['index'])
+ before do
+ allow(database).to receive(:fetch_triggers).and_return(results)
+ end
+
+ describe '#fetch_trigger_by_name' do
+ context 'when trigger does not exist' do
+ it 'returns nil' do
+ expect(database.fetch_trigger_by_name('non_existing_trigger')).to be_nil
+ end
+ end
+
+ it 'returns trigger by name' do
+ expect(database.fetch_trigger_by_name('my_trigger').name).to eq('my_trigger')
+ end
+ end
+
+ describe '#trigger_exists?' do
+ context 'when trigger exists' do
+ it 'returns true' do
+ expect(database.trigger_exists?('my_trigger')).to be_truthy
+ end
+ end
+
+ context 'when trigger does not exist' do
+ it 'returns false' do
+ expect(database.trigger_exists?('non_existing_trigger')).to be_falsey
+ end
+ end
+ end
+
+ describe '#triggers' do
+ it 'returns triggers' do
+ triggers = database.triggers
+
+ expect(triggers).to all(be_a(schema_object))
+ expect(triggers.map(&:name)).to eq(['my_trigger'])
+ end
end
end
end
diff --git a/spec/lib/gitlab/database/schema_validation/index_spec.rb b/spec/lib/gitlab/database/schema_validation/index_spec.rb
deleted file mode 100644
index 297211d79ed..00000000000
--- a/spec/lib/gitlab/database/schema_validation/index_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-require 'spec_helper'
-
-RSpec.describe Gitlab::Database::SchemaValidation::Index, feature_category: :database do
- let(:index_statement) { 'CREATE INDEX index_name ON public.achievements USING btree (namespace_id)' }
-
- let(:stmt) { PgQuery.parse(index_statement).tree.stmts.first.stmt.index_stmt }
-
- let(:index) { described_class.new(stmt) }
-
- describe '#name' do
- it 'returns index name' do
- expect(index.name).to eq('index_name')
- end
- end
-
- describe '#statement' do
- it 'returns index statement' do
- expect(index.statement).to eq(index_statement)
- end
- end
-end
diff --git a/spec/lib/gitlab/database/schema_validation/runner_spec.rb b/spec/lib/gitlab/database/schema_validation/runner_spec.rb
index 13980cb148b..ddbdedcd8b4 100644
--- a/spec/lib/gitlab/database/schema_validation/runner_spec.rb
+++ b/spec/lib/gitlab/database/schema_validation/runner_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Gitlab::Database::SchemaValidation::Runner, feature_category: :da
let(:connection) { ActiveRecord::Base.connection }
let(:database) { Gitlab::Database::SchemaValidation::Database.new(connection) }
- let(:structure_sql) { Gitlab::Database::SchemaValidation::StructureSql.new(structure_file_path) }
+ let(:structure_sql) { Gitlab::Database::SchemaValidation::StructureSql.new(structure_file_path, 'public') }
describe '#execute' do
subject(:inconsistencies) { described_class.new(structure_sql, database).execute }
diff --git a/spec/lib/gitlab/database/schema_validation/schema_objects/index_spec.rb b/spec/lib/gitlab/database/schema_validation/schema_objects/index_spec.rb
new file mode 100644
index 00000000000..1aaa994e3bb
--- /dev/null
+++ b/spec/lib/gitlab/database/schema_validation/schema_objects/index_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::SchemaValidation::SchemaObjects::Index, feature_category: :database do
+ let(:statement) { 'CREATE INDEX index_name ON public.achievements USING btree (namespace_id)' }
+ let(:name) { 'index_name' }
+
+ include_examples 'schema objects assertions for', 'index_stmt'
+end
diff --git a/spec/lib/gitlab/database/schema_validation/schema_objects/trigger_spec.rb b/spec/lib/gitlab/database/schema_validation/schema_objects/trigger_spec.rb
new file mode 100644
index 00000000000..8000a54ee27
--- /dev/null
+++ b/spec/lib/gitlab/database/schema_validation/schema_objects/trigger_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::SchemaValidation::SchemaObjects::Trigger, feature_category: :database do
+ let(:statement) { 'CREATE TRIGGER my_trigger BEFORE INSERT ON todos FOR EACH ROW EXECUTE FUNCTION trigger()' }
+ let(:name) { 'my_trigger' }
+
+ include_examples 'schema objects assertions for', 'create_trig_stmt'
+end
diff --git a/spec/lib/gitlab/database/schema_validation/structure_sql_spec.rb b/spec/lib/gitlab/database/schema_validation/structure_sql_spec.rb
index 00566d884cf..cc0bd4125ef 100644
--- a/spec/lib/gitlab/database/schema_validation/structure_sql_spec.rb
+++ b/spec/lib/gitlab/database/schema_validation/structure_sql_spec.rb
@@ -4,44 +4,79 @@ require 'spec_helper'
RSpec.describe Gitlab::Database::SchemaValidation::StructureSql, feature_category: :database do
let(:structure_file_path) { Rails.root.join('spec/fixtures/structure.sql') }
+ let(:schema_name) { 'public' }
- subject(:structure_sql) { described_class.new(structure_file_path) }
+ subject(:structure_sql) { described_class.new(structure_file_path, schema_name) }
- describe '#index_exists?' do
- subject(:index_exists) { structure_sql.index_exists?(index_name) }
+ context 'when having indexes' do
+ describe '#index_exists?' do
+ subject(:index_exists) { structure_sql.index_exists?(index_name) }
- context 'when the index does not exist' do
- let(:index_name) { 'non-existent-index' }
+ context 'when the index does not exist' do
+ let(:index_name) { 'non-existent-index' }
- it 'returns false' do
- expect(index_exists).to be_falsey
+ it 'returns false' do
+ expect(index_exists).to be_falsey
+ end
+ end
+
+ context 'when the index exists' do
+ let(:index_name) { 'index' }
+
+ it 'returns true' do
+ expect(index_exists).to be_truthy
+ end
end
end
- context 'when the index exists' do
- let(:index_name) { 'index' }
+ describe '#indexes' do
+ it 'returns indexes' do
+ indexes = structure_sql.indexes
- it 'returns true' do
- expect(index_exists).to be_truthy
+ expected_indexes = %w[
+ missing_index
+ wrong_index
+ index
+ index_namespaces_public_groups_name_id
+ index_on_deploy_keys_id_and_type_and_public
+ index_users_on_public_email_excluding_null_and_empty
+ ]
+
+ expect(indexes).to all(be_a(Gitlab::Database::SchemaValidation::SchemaObjects::Index))
+ expect(indexes.map(&:name)).to eq(expected_indexes)
end
end
end
- describe '#indexes' do
- it 'returns indexes' do
- indexes = structure_sql.indexes
+ context 'when having triggers' do
+ describe '#trigger_exists?' do
+ subject(:trigger_exists) { structure_sql.trigger_exists?(name) }
- expected_indexes = %w[
- missing_index
- wrong_index
- index
- index_namespaces_public_groups_name_id
- index_on_deploy_keys_id_and_type_and_public
- index_users_on_public_email_excluding_null_and_empty
- ]
+ context 'when the trigger does not exist' do
+ let(:name) { 'non-existent-trigger' }
- expect(indexes).to all(be_a(Gitlab::Database::SchemaValidation::Index))
- expect(indexes.map(&:name)).to eq(expected_indexes)
+ it 'returns false' do
+ expect(trigger_exists).to be_falsey
+ end
+ end
+
+ context 'when the trigger exists' do
+ let(:name) { 'trigger' }
+
+ it 'returns true' do
+ expect(trigger_exists).to be_truthy
+ end
+ end
+ end
+
+ describe '#triggers' do
+ it 'returns triggers' do
+ triggers = structure_sql.triggers
+ expected_triggers = %w[trigger wrong_trigger missing_trigger_1 projects_loose_fk_trigger]
+
+ expect(triggers).to all(be_a(Gitlab::Database::SchemaValidation::SchemaObjects::Trigger))
+ expect(triggers.map(&:name)).to eq(expected_triggers)
+ end
end
end
end
diff --git a/spec/lib/gitlab/database/schema_validation/validators/base_validator_spec.rb b/spec/lib/gitlab/database/schema_validation/validators/base_validator_spec.rb
index cf5207aee95..2f38c25cf68 100644
--- a/spec/lib/gitlab/database/schema_validation/validators/base_validator_spec.rb
+++ b/spec/lib/gitlab/database/schema_validation/validators/base_validator_spec.rb
@@ -9,8 +9,11 @@ RSpec.describe Gitlab::Database::SchemaValidation::Validators::BaseValidator, fe
it 'returns an array of all validators' do
expect(all_validators).to eq([
Gitlab::Database::SchemaValidation::Validators::ExtraIndexes,
+ Gitlab::Database::SchemaValidation::Validators::ExtraTriggers,
Gitlab::Database::SchemaValidation::Validators::MissingIndexes,
- Gitlab::Database::SchemaValidation::Validators::DifferentDefinitionIndexes
+ Gitlab::Database::SchemaValidation::Validators::MissingTriggers,
+ Gitlab::Database::SchemaValidation::Validators::DifferentDefinitionIndexes,
+ Gitlab::Database::SchemaValidation::Validators::DifferentDefinitionTriggers
])
end
end
diff --git a/spec/lib/gitlab/database/schema_validation/validators/different_definition_triggers_spec.rb b/spec/lib/gitlab/database/schema_validation/validators/different_definition_triggers_spec.rb
new file mode 100644
index 00000000000..4d065929708
--- /dev/null
+++ b/spec/lib/gitlab/database/schema_validation/validators/different_definition_triggers_spec.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::SchemaValidation::Validators::DifferentDefinitionTriggers,
+ feature_category: :database do
+ include_examples 'trigger validators', described_class, ['wrong_trigger']
+end
diff --git a/spec/lib/gitlab/database/schema_validation/validators/extra_triggers_spec.rb b/spec/lib/gitlab/database/schema_validation/validators/extra_triggers_spec.rb
new file mode 100644
index 00000000000..d2e1c18a1ab
--- /dev/null
+++ b/spec/lib/gitlab/database/schema_validation/validators/extra_triggers_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::SchemaValidation::Validators::ExtraTriggers, feature_category: :database do
+ include_examples 'trigger validators', described_class, ['extra_trigger']
+end
diff --git a/spec/lib/gitlab/database/schema_validation/validators/missing_triggers_spec.rb b/spec/lib/gitlab/database/schema_validation/validators/missing_triggers_spec.rb
new file mode 100644
index 00000000000..87bc3ded808
--- /dev/null
+++ b/spec/lib/gitlab/database/schema_validation/validators/missing_triggers_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::SchemaValidation::Validators::MissingTriggers, feature_category: :database do
+ missing_triggers = %w[missing_trigger_1 projects_loose_fk_trigger]
+
+ include_examples 'trigger validators', described_class, missing_triggers
+end
diff --git a/spec/lib/sidebars/user_settings/menus/access_tokens_menu_spec.rb b/spec/lib/sidebars/user_settings/menus/access_tokens_menu_spec.rb
new file mode 100644
index 00000000000..fa33e7bedfb
--- /dev/null
+++ b/spec/lib/sidebars/user_settings/menus/access_tokens_menu_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::UserSettings::Menus::AccessTokensMenu, feature_category: :navigation do
+ it_behaves_like 'User settings menu',
+ link: '/-/profile/personal_access_tokens',
+ title: _('Access Tokens'),
+ icon: 'token',
+ active_routes: { controller: :personal_access_tokens }
+
+ describe '#render?' do
+ subject { described_class.new(context) }
+
+ let_it_be(:user) { build(:user) }
+
+ context 'when personal access tokens are disabled' do
+ before do
+ allow(::Gitlab::CurrentSettings).to receive_messages(personal_access_tokens_disabled?: true)
+ end
+
+ context 'when user is logged in' do
+ let(:context) { Sidebars::Context.new(current_user: user, container: nil) }
+
+ it 'does not render' do
+ expect(subject.render?).to be false
+ end
+ end
+
+ context 'when user is not logged in' do
+ let(:context) { Sidebars::Context.new(current_user: nil, container: nil) }
+
+ subject { described_class.new(context) }
+
+ it 'does not render' do
+ expect(subject.render?).to be false
+ end
+ end
+ end
+
+ context 'when personal access tokens are enabled' do
+ before do
+ allow(::Gitlab::CurrentSettings).to receive_messages(personal_access_tokens_disabled?: false)
+ end
+
+ context 'when user is logged in' do
+ let(:context) { Sidebars::Context.new(current_user: user, container: nil) }
+
+ it 'renders' do
+ expect(subject.render?).to be true
+ end
+ end
+
+ context 'when user is not logged in' do
+ let(:context) { Sidebars::Context.new(current_user: nil, container: nil) }
+
+ subject { described_class.new(context) }
+
+ it 'does not render' do
+ expect(subject.render?).to be false
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/sidebars/user_settings/menus/account_menu_spec.rb b/spec/lib/sidebars/user_settings/menus/account_menu_spec.rb
new file mode 100644
index 00000000000..d5810d9c5ae
--- /dev/null
+++ b/spec/lib/sidebars/user_settings/menus/account_menu_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::UserSettings::Menus::AccountMenu, feature_category: :navigation do
+ it_behaves_like 'User settings menu',
+ link: '/-/profile/account',
+ title: _('Account'),
+ icon: 'account',
+ active_routes: { controller: [:accounts, :two_factor_auths] }
+
+ it_behaves_like 'User settings menu #render? method'
+end
diff --git a/spec/lib/sidebars/user_settings/menus/active_sessions_menu_spec.rb b/spec/lib/sidebars/user_settings/menus/active_sessions_menu_spec.rb
new file mode 100644
index 00000000000..be5f826ee58
--- /dev/null
+++ b/spec/lib/sidebars/user_settings/menus/active_sessions_menu_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::UserSettings::Menus::ActiveSessionsMenu, feature_category: :navigation do
+ it_behaves_like 'User settings menu',
+ link: '/-/profile/active_sessions',
+ title: _('Active Sessions'),
+ icon: 'monitor-lines',
+ active_routes: { controller: :active_sessions }
+
+ it_behaves_like 'User settings menu #render? method'
+end
diff --git a/spec/lib/sidebars/user_settings/menus/applications_menu_spec.rb b/spec/lib/sidebars/user_settings/menus/applications_menu_spec.rb
new file mode 100644
index 00000000000..eeda4fb844c
--- /dev/null
+++ b/spec/lib/sidebars/user_settings/menus/applications_menu_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::UserSettings::Menus::ApplicationsMenu, feature_category: :navigation do
+ it_behaves_like 'User settings menu',
+ link: '/-/profile/applications',
+ title: _('Applications'),
+ icon: 'applications',
+ active_routes: { controller: 'oauth/applications' }
+
+ it_behaves_like 'User settings menu #render? method'
+end
diff --git a/spec/lib/sidebars/user_settings/menus/authentication_log_menu_spec.rb b/spec/lib/sidebars/user_settings/menus/authentication_log_menu_spec.rb
new file mode 100644
index 00000000000..33be5050c37
--- /dev/null
+++ b/spec/lib/sidebars/user_settings/menus/authentication_log_menu_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::UserSettings::Menus::AuthenticationLogMenu, feature_category: :navigation do
+ it_behaves_like 'User settings menu',
+ link: '/-/profile/audit_log',
+ title: _('Authentication Log'),
+ icon: 'log',
+ active_routes: { path: 'profiles#audit_log' }
+
+ it_behaves_like 'User settings menu #render? method'
+end
diff --git a/spec/lib/sidebars/user_settings/menus/chat_menu_spec.rb b/spec/lib/sidebars/user_settings/menus/chat_menu_spec.rb
new file mode 100644
index 00000000000..2a0587e2504
--- /dev/null
+++ b/spec/lib/sidebars/user_settings/menus/chat_menu_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::UserSettings::Menus::ChatMenu, feature_category: :navigation do
+ it_behaves_like 'User settings menu',
+ link: '/-/profile/chat',
+ title: _('Chat'),
+ icon: 'comment',
+ active_routes: { controller: :chat_names }
+
+ it_behaves_like 'User settings menu #render? method'
+end
diff --git a/spec/lib/sidebars/user_settings/menus/emails_menu_spec.rb b/spec/lib/sidebars/user_settings/menus/emails_menu_spec.rb
new file mode 100644
index 00000000000..2f16c68e601
--- /dev/null
+++ b/spec/lib/sidebars/user_settings/menus/emails_menu_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::UserSettings::Menus::EmailsMenu, feature_category: :navigation do
+ it_behaves_like 'User settings menu',
+ link: '/-/profile/emails',
+ title: _('Emails'),
+ icon: 'mail',
+ active_routes: { controller: :emails }
+
+ it_behaves_like 'User settings menu #render? method'
+end
diff --git a/spec/lib/sidebars/user_settings/menus/gpg_keys_menu_spec.rb b/spec/lib/sidebars/user_settings/menus/gpg_keys_menu_spec.rb
new file mode 100644
index 00000000000..1f4340ad29c
--- /dev/null
+++ b/spec/lib/sidebars/user_settings/menus/gpg_keys_menu_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::UserSettings::Menus::GpgKeysMenu, feature_category: :navigation do
+ it_behaves_like 'User settings menu',
+ link: '/-/profile/gpg_keys',
+ title: _('GPG Keys'),
+ icon: 'key',
+ active_routes: { controller: :gpg_keys }
+
+ it_behaves_like 'User settings menu #render? method'
+end
diff --git a/spec/lib/sidebars/user_settings/menus/notifications_menu_spec.rb b/spec/lib/sidebars/user_settings/menus/notifications_menu_spec.rb
new file mode 100644
index 00000000000..282324056d4
--- /dev/null
+++ b/spec/lib/sidebars/user_settings/menus/notifications_menu_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::UserSettings::Menus::NotificationsMenu, feature_category: :navigation do
+ it_behaves_like 'User settings menu',
+ link: '/-/profile/notifications',
+ title: _('Notifications'),
+ icon: 'notifications',
+ active_routes: { controller: :notifications }
+
+ it_behaves_like 'User settings menu #render? method'
+end
diff --git a/spec/lib/sidebars/user_settings/menus/password_menu_spec.rb b/spec/lib/sidebars/user_settings/menus/password_menu_spec.rb
new file mode 100644
index 00000000000..168019fea5d
--- /dev/null
+++ b/spec/lib/sidebars/user_settings/menus/password_menu_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::UserSettings::Menus::PasswordMenu, feature_category: :navigation do
+ it_behaves_like 'User settings menu',
+ link: '/-/profile/password',
+ title: _('Password'),
+ icon: 'lock',
+ active_routes: { controller: :passwords }
+
+ describe '#render?' do
+ subject { described_class.new(context) }
+
+ let_it_be(:user) { build(:user) }
+ let(:context) { Sidebars::Context.new(current_user: user, container: nil) }
+
+ context 'when password authentication is enabled' do
+ before do
+ allow(user).to receive(:allow_password_authentication?).and_return(true)
+ end
+
+ it 'renders' do
+ expect(subject.render?).to be true
+ end
+ end
+
+ context 'when password authentication is disabled' do
+ before do
+ allow(user).to receive(:allow_password_authentication?).and_return(false)
+ end
+
+ it 'renders' do
+ expect(subject.render?).to be false
+ end
+ end
+ end
+end
diff --git a/spec/lib/sidebars/user_settings/menus/preferences_menu_spec.rb b/spec/lib/sidebars/user_settings/menus/preferences_menu_spec.rb
new file mode 100644
index 00000000000..83a67a40081
--- /dev/null
+++ b/spec/lib/sidebars/user_settings/menus/preferences_menu_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::UserSettings::Menus::PreferencesMenu, feature_category: :navigation do
+ it_behaves_like 'User settings menu',
+ link: '/-/profile/preferences',
+ title: _('Preferences'),
+ icon: 'preferences',
+ active_routes: { controller: :preferences }
+
+ it_behaves_like 'User settings menu #render? method'
+end
diff --git a/spec/lib/sidebars/user_settings/menus/profile_menu_spec.rb b/spec/lib/sidebars/user_settings/menus/profile_menu_spec.rb
new file mode 100644
index 00000000000..8410ba7cfcd
--- /dev/null
+++ b/spec/lib/sidebars/user_settings/menus/profile_menu_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::UserSettings::Menus::ProfileMenu, feature_category: :navigation do
+ it_behaves_like 'User settings menu',
+ link: '/-/profile',
+ title: _('Profile'),
+ icon: 'profile',
+ active_routes: { path: 'profiles#show' }
+
+ it_behaves_like 'User settings menu #render? method'
+end
diff --git a/spec/lib/sidebars/user_settings/menus/saved_replies_menu_spec.rb b/spec/lib/sidebars/user_settings/menus/saved_replies_menu_spec.rb
new file mode 100644
index 00000000000..ea1a2a3539f
--- /dev/null
+++ b/spec/lib/sidebars/user_settings/menus/saved_replies_menu_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::UserSettings::Menus::SavedRepliesMenu, feature_category: :navigation do
+ it_behaves_like 'User settings menu',
+ link: '/-/profile/saved_replies',
+ title: _('Saved Replies'),
+ icon: 'symlink',
+ active_routes: { controller: :saved_replies }
+
+ describe '#render?' do
+ subject { described_class.new(context) }
+
+ let_it_be(:user) { build(:user) }
+
+ context 'when saved replies are enabled' do
+ before do
+ allow(subject).to receive(:saved_replies_enabled?).and_return(true)
+ end
+
+ context 'when user is logged in' do
+ let(:context) { Sidebars::Context.new(current_user: user, container: nil) }
+
+ it 'does not render' do
+ expect(subject.render?).to be true
+ end
+ end
+
+ context 'when user is not logged in' do
+ let(:context) { Sidebars::Context.new(current_user: nil, container: nil) }
+
+ subject { described_class.new(context) }
+
+ it 'does not render' do
+ expect(subject.render?).to be false
+ end
+ end
+ end
+
+ context 'when saved replies are disabled' do
+ before do
+ allow(subject).to receive(:saved_replies_enabled?).and_return(false)
+ end
+
+ context 'when user is logged in' do
+ let(:context) { Sidebars::Context.new(current_user: user, container: nil) }
+
+ it 'renders' do
+ expect(subject.render?).to be false
+ end
+ end
+
+ context 'when user is not logged in' do
+ let(:context) { Sidebars::Context.new(current_user: nil, container: nil) }
+
+ subject { described_class.new(context) }
+
+ it 'does not render' do
+ expect(subject.render?).to be false
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/sidebars/user_settings/menus/ssh_keys_menu_spec.rb b/spec/lib/sidebars/user_settings/menus/ssh_keys_menu_spec.rb
new file mode 100644
index 00000000000..8c781cc743b
--- /dev/null
+++ b/spec/lib/sidebars/user_settings/menus/ssh_keys_menu_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::UserSettings::Menus::SshKeysMenu, feature_category: :navigation do
+ it_behaves_like 'User settings menu',
+ link: '/-/profile/keys',
+ title: _('SSH Keys'),
+ icon: 'key',
+ active_routes: { controller: :keys }
+
+ it_behaves_like 'User settings menu #render? method'
+end
diff --git a/spec/lib/sidebars/user_settings/panel_spec.rb b/spec/lib/sidebars/user_settings/panel_spec.rb
new file mode 100644
index 00000000000..aa05d99912a
--- /dev/null
+++ b/spec/lib/sidebars/user_settings/panel_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::UserSettings::Panel, feature_category: :navigation do
+ let_it_be(:user) { create(:user) }
+
+ let(:context) { Sidebars::Context.new(current_user: user, container: nil) }
+
+ subject { described_class.new(context) }
+
+ it 'implements #super_sidebar_context_header' do
+ expect(subject.super_sidebar_context_header).to eq({ title: _('User settings'), avatar: user.avatar_url })
+ end
+end
diff --git a/spec/migrations/ensure_timelogs_note_id_bigint_backfill_is_finished_for_gitlab_dot_com_spec.rb b/spec/migrations/ensure_timelogs_note_id_bigint_backfill_is_finished_for_gitlab_dot_com_spec.rb
index 41864c25357..9fc04f711a0 100644
--- a/spec/migrations/ensure_timelogs_note_id_bigint_backfill_is_finished_for_gitlab_dot_com_spec.rb
+++ b/spec/migrations/ensure_timelogs_note_id_bigint_backfill_is_finished_for_gitlab_dot_com_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe EnsureTimelogsNoteIdBigintBackfillIsFinishedForGitlabDotCom, feat
expect(described_class).send(
expectation,
- ensure_bacthed_background_migration_is_finished_for(migration_arguments)
+ ensure_batched_background_migration_is_finished_for(migration_arguments)
)
migrate!
diff --git a/spec/requests/api/graphql/mutations/projects/sync_fork_spec.rb b/spec/requests/api/graphql/mutations/projects/sync_fork_spec.rb
index 33074029a69..a77c026dd06 100644
--- a/spec/requests/api/graphql/mutations/projects/sync_fork_spec.rb
+++ b/spec/requests/api/graphql/mutations/projects/sync_fork_spec.rb
@@ -32,6 +32,24 @@ RSpec.describe "Sync project fork", feature_category: :source_code_management do
source_project.change_head('feature')
end
+ context 'when synchronize_fork feature flag is disabled' do
+ before do
+ stub_feature_flags(synchronize_fork: false)
+ end
+
+ it 'does not call the sync service' do
+ expect(::Projects::Forks::SyncWorker).not_to receive(:perform_async)
+
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(graphql_mutation_response(:project_sync_fork)).to eq(
+ {
+ 'details' => nil,
+ 'errors' => ['Feature flag is disabled']
+ })
+ end
+ end
+
context 'when the user does not have permission' do
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 19a630e5218..81815fdab62 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -168,6 +168,17 @@ RSpec.describe API::MergeRequests, feature_category: :source_code_management do
end
end
+ context 'when DB timeouts occur' do
+ it 'returns a :request_timeout status' do
+ allow(MergeRequestsFinder).to receive(:new).and_raise(ActiveRecord::QueryCanceled)
+
+ path = endpoint_path + '?view=simple'
+ get api(path, user)
+
+ expect(response).to have_gitlab_http_status(:request_timeout)
+ end
+ end
+
it 'returns an array of all merge_requests using simple mode' do
path = endpoint_path + '?view=simple'
diff --git a/spec/support/matchers/background_migrations_matchers.rb b/spec/support/matchers/background_migrations_matchers.rb
index 041958deeea..97993b158c8 100644
--- a/spec/support/matchers/background_migrations_matchers.rb
+++ b/spec/support/matchers/background_migrations_matchers.rb
@@ -101,7 +101,7 @@ RSpec::Matchers.define :be_finalize_background_migration_of do |migration|
end
end
-RSpec::Matchers.define :ensure_bacthed_background_migration_is_finished_for do |migration_arguments|
+RSpec::Matchers.define :ensure_batched_background_migration_is_finished_for do |migration_arguments|
define_method :matches? do |klass|
expect_next_instance_of(klass) do |instance|
expect(instance).to receive(:ensure_batched_background_migration_is_finished).with(migration_arguments)
diff --git a/spec/support/shared_examples/lib/gitlab/database/index_validators_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/index_validators_shared_examples.rb
index 6b0ea9421de..6f0cede7130 100644
--- a/spec/support/shared_examples/lib/gitlab/database/index_validators_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/database/index_validators_shared_examples.rb
@@ -20,15 +20,15 @@ RSpec.shared_examples "index validators" do |validator, expected_result|
let(:connection) { database_model.connection }
- let(:query_result) { instance_double('ActiveRecord::Result', rows: database_indexes) }
+ let(:schema) { connection.current_schema }
let(:database) { Gitlab::Database::SchemaValidation::Database.new(connection) }
- let(:structure_file) { Gitlab::Database::SchemaValidation::StructureSql.new(structure_file_path) }
+ let(:structure_file) { Gitlab::Database::SchemaValidation::StructureSql.new(structure_file_path, schema) }
subject(:result) { validator.new(structure_file, database).execute }
before do
- allow(connection).to receive(:exec_query).and_return(query_result)
+ allow(connection).to receive(:select_rows).and_return(database_indexes)
end
it 'returns index inconsistencies' do
diff --git a/spec/support/shared_examples/lib/gitlab/database/schema_objects_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/schema_objects_shared_examples.rb
new file mode 100644
index 00000000000..d5ecab0cb6b
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/database/schema_objects_shared_examples.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.shared_examples "schema objects assertions for" do |stmt_name|
+ let(:stmt) { PgQuery.parse(statement).tree.stmts.first.stmt }
+ let(:schema_object) { described_class.new(stmt.public_send(stmt_name)) }
+
+ describe '#name' do
+ it 'returns schema object name' do
+ expect(schema_object.name).to eq(name)
+ end
+ end
+
+ describe '#statement' do
+ it 'returns schema object statement' do
+ expect(schema_object.statement).to eq(statement)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/database/trigger_validators_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/trigger_validators_shared_examples.rb
new file mode 100644
index 00000000000..13a112275c2
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/database/trigger_validators_shared_examples.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.shared_examples 'trigger validators' do |validator, expected_result|
+ subject(:result) { validator.new(structure_file, database).execute }
+
+ let(:structure_file_path) { Rails.root.join('spec/fixtures/structure.sql') }
+ let(:structure_file) { Gitlab::Database::SchemaValidation::StructureSql.new(structure_file_path, schema) }
+ let(:inconsistency_type) { validator.name.demodulize.underscore }
+ let(:database_name) { 'main' }
+ let(:schema) { 'public' }
+ let(:database_model) { Gitlab::Database.database_base_models[database_name] }
+ let(:connection) { database_model.connection }
+ let(:database) { Gitlab::Database::SchemaValidation::Database.new(connection) }
+
+ let(:database_triggers) do
+ [
+ ['trigger', 'CREATE TRIGGER trigger AFTER INSERT ON public.t1 FOR EACH ROW EXECUTE FUNCTION t1()'],
+ ['wrong_trigger', 'CREATE TRIGGER wrong_trigger BEFORE UPDATE ON public.t2 FOR EACH ROW EXECUTE FUNCTION t2()'],
+ ['extra_trigger', 'CREATE TRIGGER extra_trigger BEFORE INSERT ON public.t4 FOR EACH ROW EXECUTE FUNCTION t4()']
+ ]
+ end
+
+ before do
+ allow(connection).to receive(:select_rows).and_return(database_triggers)
+ end
+
+ it 'returns trigger inconsistencies' do
+ expect(result.map(&:object_name)).to match_array(expected_result)
+ expect(result.map(&:type)).to all(eql inconsistency_type)
+ end
+end
diff --git a/spec/support/shared_examples/lib/sidebars/user_settings/menus/user_settings_menus_shared_examples.rb b/spec/support/shared_examples/lib/sidebars/user_settings/menus/user_settings_menus_shared_examples.rb
new file mode 100644
index 00000000000..b91386d1935
--- /dev/null
+++ b/spec/support/shared_examples/lib/sidebars/user_settings/menus/user_settings_menus_shared_examples.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'User settings menu' do |link:, title:, icon:, active_routes:|
+ let_it_be(:user) { create(:user) }
+
+ let(:context) { Sidebars::Context.new(current_user: user, container: nil) }
+
+ subject { described_class.new(context) }
+
+ it 'does not contain any sub menu' do
+ expect(subject.has_items?).to be false
+ end
+
+ it 'renders the correct link' do
+ expect(subject.link).to match link
+ end
+
+ it 'renders the correct title' do
+ expect(subject.title).to eq title
+ end
+
+ it 'renders the correct icon' do
+ expect(subject.sprite_icon).to be icon
+ end
+
+ it 'defines correct active route' do
+ expect(subject.active_routes).to eq active_routes
+ end
+end
+
+RSpec.shared_examples 'User settings menu #render? method' do
+ describe '#render?' do
+ subject { described_class.new(context) }
+
+ context 'when user is logged in' do
+ let_it_be(:user) { build(:user) }
+ let(:context) { Sidebars::Context.new(current_user: user, container: nil) }
+
+ it 'renders' do
+ expect(subject.render?).to be true
+ end
+ end
+
+ context 'when user is not logged in' do
+ let(:context) { Sidebars::Context.new(current_user: nil, container: nil) }
+
+ it 'does not render' do
+ expect(subject.render?).to be false
+ end
+ end
+ end
+end