diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-07 03:10:26 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-07 03:10:26 +0300 |
commit | 1e19d757e8a088e9d4aa67cc092fda87aba1cd35 (patch) | |
tree | b183775b7355dae057ab0d2e5076df86cdc8a77d /spec | |
parent | b04f912deb494b6dc0d0dba36776a5e53f622b43 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
11 files changed, 289 insertions, 68 deletions
diff --git a/spec/frontend/ci/ci_variable_list/components/ci_variable_modal_spec.js b/spec/frontend/ci/ci_variable_list/components/ci_variable_modal_spec.js index b6ffde9b33f..40b04ca3e00 100644 --- a/spec/frontend/ci/ci_variable_list/components/ci_variable_modal_spec.js +++ b/spec/frontend/ci/ci_variable_list/components/ci_variable_modal_spec.js @@ -458,7 +458,8 @@ describe('Ci variable modal', () => { }); describe('Validations', () => { - const maskError = 'This variable can not be masked.'; + const maskError = 'This variable value does not meet the masking requirements.'; + const helpText = 'Value must meet regular expression requirements to be masked.'; describe('when the variable is raw', () => { const [variable] = mockVariables; @@ -488,6 +489,10 @@ describe('Ci variable modal', () => { expect(findModal().text()).toContain(maskError); }); + + it('does not show the masked variable help text', () => { + expect(findModal().text()).not.toContain(helpText); + }); }); describe('when the mask state is invalid', () => { @@ -510,8 +515,9 @@ describe('Ci variable modal', () => { expect(findAddorUpdateButton().attributes('disabled')).toBeDefined(); }); - it('shows the correct error text', () => { + it('shows the correct error text and help text', () => { expect(findModal().text()).toContain(maskError); + expect(findModal().text()).toContain(helpText); }); it('sends the correct tracking event', () => { @@ -578,6 +584,10 @@ describe('Ci variable modal', () => { }); }); + it('shows the help text', () => { + expect(findModal().text()).toContain(helpText); + }); + it('does not disable the submit button', () => { expect(findAddorUpdateButton().attributes('disabled')).toBeUndefined(); }); diff --git a/spec/lib/gitlab/ci/secure_files/migration_helper_spec.rb b/spec/lib/gitlab/ci/secure_files/migration_helper_spec.rb new file mode 100644 index 00000000000..8f1b300ae98 --- /dev/null +++ b/spec/lib/gitlab/ci/secure_files/migration_helper_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::SecureFiles::MigrationHelper, feature_category: :mobile_devops do + before do + stub_ci_secure_file_object_storage + end + + describe '.migrate_to_remote_storage' do + let!(:local_file) { create(:ci_secure_file) } + + subject { described_class.migrate_to_remote_storage } + + it 'migrates remote files to remote storage' do + subject + + expect(local_file.reload.file_store).to eq(Ci::SecureFileUploader::Store::REMOTE) + end + end + + describe '.migrate_in_batches' do + let!(:local_file) { create(:ci_secure_file) } + let!(:storage) { Ci::SecureFileUploader::Store::REMOTE } + + subject { described_class.migrate_to_remote_storage } + + it 'migrates the given file to the given storage backend' do + expect_next_found_instance_of(Ci::SecureFile) do |instance| + expect(instance).to receive_message_chain(:file, :migrate!).with(storage) + end + + described_class.send(:migrate_in_batches, Ci::SecureFile.all, storage) + end + + it 'calls the given block for each migrated file' do + expect_next_found_instance_of(Ci::SecureFile) do |instance| + expect(instance).to receive(:metadata) + end + + described_class.send(:migrate_in_batches, Ci::SecureFile.all, storage, &:metadata) + end + end +end diff --git a/spec/lib/gitlab/database/health_status_spec.rb b/spec/lib/gitlab/database/health_status_spec.rb index 8d7717beaff..bc923635b1d 100644 --- a/spec/lib/gitlab/database/health_status_spec.rb +++ b/spec/lib/gitlab/database/health_status_spec.rb @@ -12,9 +12,10 @@ RSpec.describe Gitlab::Database::HealthStatus, feature_category: :database do end describe '.evaluate' do - subject(:evaluate) { described_class.evaluate(migration.health_context, [autovacuum_indicator_class]) } + subject(:evaluate) { described_class.evaluate(health_context, [autovacuum_indicator_class]) } let(:migration) { build(:batched_background_migration, :active) } + let(:health_context) { migration.health_context } let(:health_status) { described_class } let(:autovacuum_indicator_class) { health_status::Indicators::AutovacuumActiveOnTable } @@ -25,20 +26,20 @@ RSpec.describe Gitlab::Database::HealthStatus, feature_category: :database do let(:patroni_apdex_indicator) { instance_double(patroni_apdex_indicator_class) } before do - allow(autovacuum_indicator_class).to receive(:new).with(migration.health_context).and_return(autovacuum_indicator) + allow(autovacuum_indicator_class).to receive(:new).with(health_context).and_return(autovacuum_indicator) end context 'with default indicators' do - subject(:evaluate) { described_class.evaluate(migration.health_context) } + subject(:evaluate) { described_class.evaluate(health_context) } it 'returns a collection of signals' do normal_signal = instance_double("#{health_status}::Signals::Normal", log_info?: false) not_available_signal = instance_double("#{health_status}::Signals::NotAvailable", log_info?: false) expect(autovacuum_indicator).to receive(:evaluate).and_return(normal_signal) - expect(wal_indicator_class).to receive(:new).with(migration.health_context).and_return(wal_indicator) + expect(wal_indicator_class).to receive(:new).with(health_context).and_return(wal_indicator) expect(wal_indicator).to receive(:evaluate).and_return(not_available_signal) - expect(patroni_apdex_indicator_class).to receive(:new).with(migration.health_context) + expect(patroni_apdex_indicator_class).to receive(:new).with(health_context) .and_return(patroni_apdex_indicator) expect(patroni_apdex_indicator).to receive(:evaluate).and_return(not_available_signal) @@ -46,7 +47,7 @@ RSpec.describe Gitlab::Database::HealthStatus, feature_category: :database do end end - it 'returns a collection of signals' do + it 'returns the signal of the given indicator' do signal = instance_double("#{health_status}::Signals::Normal", log_info?: false) expect(autovacuum_indicator).to receive(:evaluate).and_return(signal) @@ -54,28 +55,78 @@ RSpec.describe Gitlab::Database::HealthStatus, feature_category: :database do expect(evaluate).to contain_exactly(signal) end - it 'logs interesting signals' do - signal = instance_double( - "#{health_status}::Signals::Stop", - log_info?: true, - indicator_class: autovacuum_indicator_class, - short_name: 'Stop', - reason: 'Test Exception' - ) + context 'with stop signals' do + let(:stop_signal) do + instance_double( + "#{health_status}::Signals::Stop", + log_info?: true, + indicator_class: autovacuum_indicator_class, + short_name: 'Stop', + reason: 'Test Exception' + ) + end - expect(autovacuum_indicator).to receive(:evaluate).and_return(signal) + before do + allow(autovacuum_indicator).to receive(:evaluate).and_return(stop_signal) + end - expect(Gitlab::Database::HealthStatus::Logger).to receive(:info).with( - status_checker_id: migration.id, - status_checker_type: 'Gitlab::Database::BackgroundMigration::BatchedMigration', - job_class_name: migration.job_class_name, - health_status_indicator: autovacuum_indicator_class.to_s, - indicator_signal: 'Stop', - signal_reason: 'Test Exception', - message: "#{migration} signaled: #{signal}" - ) + context 'with batched migrations as the status checker' do + it 'captures BatchedMigration class name in the log' do + expect(Gitlab::Database::HealthStatus::Logger).to receive(:info).with( + status_checker_id: migration.id, + status_checker_type: 'Gitlab::Database::BackgroundMigration::BatchedMigration', + job_class_name: migration.job_class_name, + health_status_indicator: autovacuum_indicator_class.to_s, + indicator_signal: 'Stop', + signal_reason: 'Test Exception', + message: "#{migration} signaled: #{stop_signal}" + ) - evaluate + evaluate + end + end + + context 'with sidekiq deferred job as the status checker' do + let(:deferred_worker) do + Class.new do + def self.name + 'TestDeferredWorker' + end + + include ApplicationWorker + end + end + + let(:deferred_worker_health_checker) do + Gitlab::SidekiqMiddleware::DeferJobs::DatabaseHealthStatusChecker.new( + 123, + deferred_worker.name + ) + end + + let(:health_context) do + Gitlab::Database::HealthStatus::Context.new( + deferred_worker_health_checker, + ActiveRecord::Base.connection, + :gitlab_main, + [:users] + ) + end + + it 'captures sidekiq job class in the log' do + expect(Gitlab::Database::HealthStatus::Logger).to receive(:info).with( + status_checker_id: deferred_worker_health_checker.id, + status_checker_type: 'Gitlab::SidekiqMiddleware::DeferJobs::DatabaseHealthStatusChecker', + job_class_name: deferred_worker_health_checker.job_class_name, + health_status_indicator: autovacuum_indicator_class.to_s, + indicator_signal: 'Stop', + signal_reason: 'Test Exception', + message: "#{deferred_worker_health_checker} signaled: #{stop_signal}" + ) + + evaluate + end + end end it 'does not log signals of no interest' do diff --git a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb index 96897cea9c0..1c23a619b38 100644 --- a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb +++ b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb @@ -435,6 +435,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do call_subject(job, 'test_queue') do job['deferred'] = true + job['deferred_by'] = :feature_flag end end end diff --git a/spec/lib/gitlab/sidekiq_middleware/defer_jobs_spec.rb b/spec/lib/gitlab/sidekiq_middleware/defer_jobs_spec.rb index 6dcf9aaeb63..195a79c22ec 100644 --- a/spec/lib/gitlab/sidekiq_middleware/defer_jobs_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/defer_jobs_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe Gitlab::SidekiqMiddleware::DeferJobs, feature_category: :scalability do let(:job) { { 'jid' => 123, 'args' => [456] } } let(:queue) { 'test_queue' } - let(:worker) do + let(:deferred_worker) do Class.new do def self.name 'TestDeferredWorker' @@ -14,7 +14,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::DeferJobs, feature_category: :scalabil end end - let(:worker2) do + let(:undeferred_worker) do Class.new do def self.name 'UndeferredWorker' @@ -26,21 +26,29 @@ RSpec.describe Gitlab::SidekiqMiddleware::DeferJobs, feature_category: :scalabil subject { described_class.new } before do - stub_const('TestDeferredWorker', worker) - stub_const('UndeferredWorker', worker2) + stub_const('TestDeferredWorker', deferred_worker) + stub_const('UndeferredWorker', undeferred_worker) end describe '#call' do - context 'when sidekiq_defer_jobs feature flag is enabled for a worker' do - before do - stub_feature_flags("defer_sidekiq_jobs_#{TestDeferredWorker.name}": true) - stub_feature_flags("defer_sidekiq_jobs_#{UndeferredWorker.name}": false) - end + context 'with worker not opted for database health check' do + context 'when sidekiq_defer_jobs feature flag is enabled for a worker' do + before do + stub_feature_flags("defer_sidekiq_jobs_#{TestDeferredWorker.name}": true) + stub_feature_flags("defer_sidekiq_jobs_#{UndeferredWorker.name}": false) + end + + context 'for the affected worker' do + it 'defers the job' do + expect(TestDeferredWorker).to receive(:perform_in).with(described_class::DELAY, *job['args']) + expect { |b| subject.call(TestDeferredWorker.new, job, queue, &b) }.not_to yield_control + end + end - context 'for the affected worker' do - it 'defers the job' do - expect(TestDeferredWorker).to receive(:perform_in).with(described_class::DELAY, *job['args']) - expect { |b| subject.call(TestDeferredWorker.new, job, queue, &b) }.not_to yield_control + context 'for other workers' do + it 'runs the job normally' do + expect { |b| subject.call(UndeferredWorker.new, job, queue, &b) }.to yield_control + end end it 'increments the counter' do @@ -51,22 +59,52 @@ RSpec.describe Gitlab::SidekiqMiddleware::DeferJobs, feature_category: :scalabil end end - context 'for other workers' do + context 'when sidekiq_defer_jobs feature flag is disabled' do + before do + stub_feature_flags("defer_sidekiq_jobs_#{TestDeferredWorker.name}": false) + stub_feature_flags("defer_sidekiq_jobs_#{UndeferredWorker.name}": false) + end + it 'runs the job normally' do + expect { |b| subject.call(TestDeferredWorker.new, job, queue, &b) }.to yield_control expect { |b| subject.call(UndeferredWorker.new, job, queue, &b) }.to yield_control end end end - context 'when sidekiq_defer_jobs feature flag is disabled' do + context 'with worker opted for database health check' do + let(:health_signal_attrs) { { gitlab_schema: :gitlab_main, delay: 1.minute, tables: [:users] } } + + around do |example| + with_sidekiq_server_middleware do |chain| + chain.add described_class + Sidekiq::Testing.inline! { example.run } + end + end + before do stub_feature_flags("defer_sidekiq_jobs_#{TestDeferredWorker.name}": false) - stub_feature_flags("defer_sidekiq_jobs_#{UndeferredWorker.name}": false) + + TestDeferredWorker.defer_on_database_health_signal(*health_signal_attrs.values) + end + + context 'without any stop signal from database health check' do + it 'runs the job normally' do + expect { |b| subject.call(TestDeferredWorker.new, job, queue, &b) }.to yield_control + end end - it 'runs the job normally' do - expect { |b| subject.call(TestDeferredWorker.new, job, queue, &b) }.to yield_control - expect { |b| subject.call(UndeferredWorker.new, job, queue, &b) }.to yield_control + context 'with stop signal from database health check' do + before do + stop_signal = instance_double("Gitlab::Database::HealthStatus::Signals::Stop", stop?: true) + allow(Gitlab::Database::HealthStatus).to receive(:evaluate).and_return([stop_signal]) + end + + it 'defers the job by set time' do + expect(TestDeferredWorker).to receive(:perform_in).with(health_signal_attrs[:delay], *job['args']) + + TestDeferredWorker.perform_async(*job['args']) + end end end end diff --git a/spec/models/namespace/root_storage_statistics_spec.rb b/spec/models/namespace/root_storage_statistics_spec.rb index 3b6062c0d8a..a7f21e3a07f 100644 --- a/spec/models/namespace/root_storage_statistics_spec.rb +++ b/spec/models/namespace/root_storage_statistics_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do it { is_expected.to belong_to :namespace } it { is_expected.to have_one(:route).through(:namespace) } - it { is_expected.to delegate_method(:all_projects).to(:namespace) } + it { is_expected.to delegate_method(:all_projects_except_soft_deleted).to(:namespace) } context 'scopes' do describe '.for_namespace_ids' do @@ -28,9 +28,10 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do let(:project1) { create(:project, namespace: namespace) } let(:project2) { create(:project, namespace: namespace) } + let(:project3) { create(:project, namespace: namespace, marked_for_deletion_at: 1.day.ago, pending_delete: true) } shared_examples 'project data refresh' do - it 'aggregates project statistics' do + it 'aggregates eligible project statistics' do root_storage_statistics.recalculate! root_storage_statistics.reload @@ -97,6 +98,7 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do context 'with project statistics' do let!(:project_stat1) { create(:project_statistics, project: project1, with_data: true, size_multiplier: 100) } let!(:project_stat2) { create(:project_statistics, project: project2, with_data: true, size_multiplier: 200) } + let!(:project_stat3) { create(:project_statistics, project: project3, with_data: true, size_multiplier: 300) } it_behaves_like 'project data refresh' it_behaves_like 'does not include personal snippets' diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index c9995122bb2..aea7a3b52c9 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -1745,6 +1745,52 @@ RSpec.describe Namespace, feature_category: :groups_and_projects do end end + describe '#all_projects_except_soft_deleted' do + context 'when namespace is a group' do + let_it_be(:namespace) { create(:group) } + let_it_be(:child) { create(:group, parent: namespace) } + let_it_be(:project1) { create(:project_empty_repo, namespace: namespace) } + let_it_be(:project2) { create(:project_empty_repo, namespace: child) } + let_it_be(:other_project) { create(:project_empty_repo) } + + before do + reload_models(namespace, child) + end + + it { expect(namespace.all_projects_except_soft_deleted.to_a).to match_array([project2, project1]) } + it { expect(child.all_projects_except_soft_deleted.to_a).to match_array([project2]) } + + context 'with soft deleted projects' do + let_it_be(:delayed_deletion_project) { create(:project, namespace: child, marked_for_deletion_at: Date.current) } + + it 'skips delayed deletion project' do + expect(namespace.all_projects_except_soft_deleted.to_a).to match_array([project2, project1]) + end + end + end + + context 'when namespace is a user namespace' do + let_it_be(:user) { create(:user) } + let_it_be(:user_namespace) { create(:namespace, owner: user) } + let_it_be(:project) { create(:project, namespace: user_namespace) } + let_it_be(:other_project) { create(:project_empty_repo) } + + before do + reload_models(user_namespace) + end + + it { expect(user_namespace.all_projects_except_soft_deleted.to_a).to match_array([project]) } + + context 'with soft deleted projects' do + let_it_be(:delayed_deletion_project) { create(:project, namespace: user_namespace, marked_for_deletion_at: Date.current) } + + it 'skips delayed deletion project' do + expect(user_namespace.all_projects_except_soft_deleted.to_a).to match_array([project]) + end + end + end + end + describe '#all_projects' do context 'with use_traversal_ids feature flag enabled' do before do diff --git a/spec/support/shared_contexts/lib/gitlab/sidekiq_logging/structured_logger_shared_context.rb b/spec/support/shared_contexts/lib/gitlab/sidekiq_logging/structured_logger_shared_context.rb index 88339df3475..1b50ef3fcff 100644 --- a/spec/support/shared_contexts/lib/gitlab/sidekiq_logging/structured_logger_shared_context.rb +++ b/spec/support/shared_contexts/lib/gitlab/sidekiq_logging/structured_logger_shared_context.rb @@ -68,7 +68,8 @@ RSpec.shared_context 'structured_logger' do let(:deferred_payload) do end_payload.merge( 'message' => 'TestWorker JID-da883554ee4fe414012f5f42: deferred: 0.0 sec', - 'job_status' => 'deferred' + 'job_status' => 'deferred', + 'job_deferred_by' => :feature_flag ) end diff --git a/spec/support/shared_examples/features/variable_list_shared_examples.rb b/spec/support/shared_examples/features/variable_list_shared_examples.rb index 1211c9d19e6..d206f669b3e 100644 --- a/spec/support/shared_examples/features/variable_list_shared_examples.rb +++ b/spec/support/shared_examples/features/variable_list_shared_examples.rb @@ -186,7 +186,7 @@ RSpec.shared_examples 'variable list' do click_button('Add variable') fill_variable('empty_mask_key', '???', protected: true, masked: true) do - expect(page).to have_content('This variable can not be masked') + expect(page).to have_content('This variable value does not meet the masking requirements.') expect(find_button('Add variable', disabled: true)).to be_present end end diff --git a/spec/tasks/gitlab/ci_secure_files/migrate_rake_spec.rb b/spec/tasks/gitlab/ci_secure_files/migrate_rake_spec.rb new file mode 100644 index 00000000000..ed6b5914f3e --- /dev/null +++ b/spec/tasks/gitlab/ci_secure_files/migrate_rake_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'rake_helper' + +RSpec.describe 'gitlab:ci_secure_files', feature_category: :mobile_devops do + let_it_be(:local_file) { create(:ci_secure_file) } + + let(:logger) { instance_double(Logger) } + let(:helper) { double } + + before(:all) do + Rake.application.rake_require 'tasks/gitlab/ci_secure_files/migrate' + end + + before do + allow(Logger).to receive(:new).with($stdout).and_return(logger) + end + + describe 'gitlab:ci_secure_files:migrate' do + subject { run_rake_task('gitlab:ci_secure_files:migrate') } + + it 'invokes the migration helper to move files to object storage' do + expect(Gitlab::Ci::SecureFiles::MigrationHelper).to receive(:migrate_to_remote_storage).and_yield(local_file) + expect(logger).to receive(:info).with('Starting transfer of Secure Files to object storage') + expect(logger).to receive(:info).with(/Transferred Secure File ID #{local_file.id}/) + + subject + end + + context 'when an error is raised while migrating' do + let(:error_message) { 'Something went wrong' } + + before do + allow(Gitlab::Ci::SecureFiles::MigrationHelper).to receive(:migrate_to_remote_storage).and_raise(StandardError, + error_message) + end + + it 'logs the error' do + expect(logger).to receive(:info).with('Starting transfer of Secure Files to object storage') + expect(logger).to receive(:error).with("Failed to migrate: #{error_message}") + + subject + end + end + end +end diff --git a/spec/workers/concerns/worker_attributes_spec.rb b/spec/workers/concerns/worker_attributes_spec.rb index cee8e0fb0ad..959cb62c6fb 100644 --- a/spec/workers/concerns/worker_attributes_spec.rb +++ b/spec/workers/concerns/worker_attributes_spec.rb @@ -157,23 +157,5 @@ RSpec.describe WorkerAttributes, feature_category: :shared do context 'when defer_on_database_health_signal is not set' do it { is_expected.to be(false) } end - - context 'when FF `defer_sidekiq_workers_on_database_health_signal` is disabled' do - before do - stub_feature_flags(defer_sidekiq_workers_on_database_health_signal: false) - end - - context 'when defer_on_database_health_signal is set' do - before do - worker.defer_on_database_health_signal(:gitlab_main, 1.minute, [:users]) - end - - it { is_expected.to be(false) } - end - - context 'when defer_on_database_health_signal is not set' do - it { is_expected.to be(false) } - end - end end end |