Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/workers')
-rw-r--r--spec/workers/bulk_import_worker_spec.rb169
-rw-r--r--spec/workers/bulk_imports/finish_project_import_worker_spec.rb28
-rw-r--r--spec/workers/bulk_imports/pipeline_batch_worker_spec.rb10
-rw-r--r--spec/workers/click_house/events_sync_worker_spec.rb145
-rw-r--r--spec/workers/concerns/gitlab/github_import/object_importer_spec.rb3
-rw-r--r--spec/workers/concerns/gitlab/github_import/rescheduling_methods_spec.rb2
-rw-r--r--spec/workers/concerns/gitlab/import/notify_upon_death_spec.rb (renamed from spec/workers/concerns/gitlab/notify_upon_death_spec.rb)10
-rw-r--r--spec/workers/concerns/limited_capacity/worker_spec.rb18
-rw-r--r--spec/workers/database/lock_tables_worker_spec.rb136
-rw-r--r--spec/workers/database/monitor_locked_tables_worker_spec.rb55
-rw-r--r--spec/workers/environments/stop_job_success_worker_spec.rb55
-rw-r--r--spec/workers/every_sidekiq_worker_spec.rb14
-rw-r--r--spec/workers/gitlab/bitbucket_import/advance_stage_worker_spec.rb115
-rw-r--r--spec/workers/gitlab/bitbucket_import/import_pull_request_worker_spec.rb9
-rw-r--r--spec/workers/gitlab/bitbucket_import/stage/finish_import_worker_spec.rb27
-rw-r--r--spec/workers/gitlab/bitbucket_import/stage/import_pull_requests_worker_spec.rb77
-rw-r--r--spec/workers/gitlab/bitbucket_import/stage/import_repository_worker_spec.rb21
-rw-r--r--spec/workers/gitlab/bitbucket_server_import/advance_stage_worker_spec.rb7
-rw-r--r--spec/workers/gitlab/bitbucket_server_import/import_pull_request_worker_spec.rb6
-rw-r--r--spec/workers/gitlab/github_gists_import/import_gist_worker_spec.rb8
-rw-r--r--spec/workers/gitlab/github_import/advance_stage_worker_spec.rb112
-rw-r--r--spec/workers/gitlab/jira_import/advance_stage_worker_spec.rb7
-rw-r--r--spec/workers/gitlab/jira_import/import_issue_worker_spec.rb2
-rw-r--r--spec/workers/incident_management/close_incident_worker_spec.rb2
-rw-r--r--spec/workers/incident_management/process_alert_worker_v2_spec.rb4
-rw-r--r--spec/workers/loose_foreign_keys/cleanup_worker_spec.rb62
-rw-r--r--spec/workers/merge_requests/ensure_prepared_worker_spec.rb59
-rw-r--r--spec/workers/metrics/global_metrics_update_worker_spec.rb30
-rw-r--r--spec/workers/namespaces/in_product_marketing_emails_worker_spec.rb32
-rw-r--r--spec/workers/new_merge_request_worker_spec.rb28
-rw-r--r--spec/workers/new_note_worker_spec.rb2
-rw-r--r--spec/workers/pages/invalidate_domain_cache_worker_spec.rb267
-rw-r--r--spec/workers/personal_access_tokens/expiring_worker_spec.rb27
-rw-r--r--spec/workers/post_receive_spec.rb2
-rw-r--r--spec/workers/projects/record_target_platforms_worker_spec.rb2
-rw-r--r--spec/workers/users/migrate_records_to_ghost_user_in_batches_worker_spec.rb4
-rw-r--r--spec/workers/users/track_namespace_visits_worker_spec.rb27
37 files changed, 1057 insertions, 527 deletions
diff --git a/spec/workers/bulk_import_worker_spec.rb b/spec/workers/bulk_import_worker_spec.rb
index ec8550bb3bc..c96e5ace124 100644
--- a/spec/workers/bulk_import_worker_spec.rb
+++ b/spec/workers/bulk_import_worker_spec.rb
@@ -137,5 +137,174 @@ RSpec.describe BulkImportWorker, feature_category: :importers do
end
end
end
+
+ context 'when importing a group' do
+ it 'creates trackers for group entity' do
+ bulk_import = create(:bulk_import)
+ entity = create(:bulk_import_entity, :group_entity, bulk_import: bulk_import)
+
+ subject.perform(bulk_import.id)
+
+ expect(entity.trackers.to_a).to include(
+ have_attributes(
+ stage: 0, status_name: :created, relation: BulkImports::Groups::Pipelines::GroupPipeline.to_s
+ ),
+ have_attributes(
+ stage: 1, status_name: :created, relation: BulkImports::Groups::Pipelines::GroupAttributesPipeline.to_s
+ )
+ )
+ end
+ end
+
+ context 'when importing a project' do
+ it 'creates trackers for project entity' do
+ bulk_import = create(:bulk_import)
+ entity = create(:bulk_import_entity, :project_entity, bulk_import: bulk_import)
+
+ subject.perform(bulk_import.id)
+
+ expect(entity.trackers.to_a).to include(
+ have_attributes(
+ stage: 0, status_name: :created, relation: BulkImports::Projects::Pipelines::ProjectPipeline.to_s
+ ),
+ have_attributes(
+ stage: 1, status_name: :created, relation: BulkImports::Projects::Pipelines::RepositoryPipeline.to_s
+ )
+ )
+ end
+ end
+
+ context 'when tracker configuration has a minimum version defined' do
+ before do
+ allow_next_instance_of(BulkImports::Groups::Stage) do |stage|
+ allow(stage).to receive(:config).and_return(
+ {
+ pipeline1: { pipeline: 'PipelineClass1', stage: 0 },
+ pipeline2: { pipeline: 'PipelineClass2', stage: 1, minimum_source_version: '14.10.0' },
+ pipeline3: { pipeline: 'PipelineClass3', stage: 1, minimum_source_version: '15.0.0' },
+ pipeline5: { pipeline: 'PipelineClass4', stage: 1, minimum_source_version: '15.1.0' },
+ pipeline6: { pipeline: 'PipelineClass5', stage: 1, minimum_source_version: '16.0.0' }
+ }
+ )
+ end
+ end
+
+ context 'when the source instance version is older than the tracker mininum version' do
+ let_it_be(:bulk_import) { create(:bulk_import, source_version: '15.0.0') }
+ let_it_be(:entity) { create(:bulk_import_entity, :group_entity, bulk_import: bulk_import) }
+
+ it 'creates trackers as skipped if version requirement does not meet' do
+ subject.perform(bulk_import.id)
+
+ expect(entity.trackers.collect { |tracker| [tracker.status_name, tracker.relation] }).to contain_exactly(
+ [:created, 'PipelineClass1'],
+ [:created, 'PipelineClass2'],
+ [:created, 'PipelineClass3'],
+ [:skipped, 'PipelineClass4'],
+ [:skipped, 'PipelineClass5']
+ )
+ end
+
+ it 'logs an info message for the skipped pipelines' do
+ expect_next_instance_of(Gitlab::Import::Logger) do |logger|
+ expect(logger).to receive(:info).with({
+ message: 'Pipeline skipped as source instance version not compatible with pipeline',
+ bulk_import_entity_id: entity.id,
+ bulk_import_id: entity.bulk_import_id,
+ bulk_import_entity_type: entity.source_type,
+ source_full_path: entity.source_full_path,
+ importer: 'gitlab_migration',
+ pipeline_name: 'PipelineClass4',
+ minimum_source_version: '15.1.0',
+ maximum_source_version: nil,
+ source_version: '15.0.0'
+ })
+
+ expect(logger).to receive(:info).with({
+ message: 'Pipeline skipped as source instance version not compatible with pipeline',
+ bulk_import_entity_id: entity.id,
+ bulk_import_id: entity.bulk_import_id,
+ bulk_import_entity_type: entity.source_type,
+ source_full_path: entity.source_full_path,
+ importer: 'gitlab_migration',
+ pipeline_name: 'PipelineClass5',
+ minimum_source_version: '16.0.0',
+ maximum_source_version: nil,
+ source_version: '15.0.0'
+ })
+ end
+
+ subject.perform(bulk_import.id)
+ end
+ end
+
+ context 'when the source instance version is undefined' do
+ it 'creates trackers as created' do
+ bulk_import = create(:bulk_import, source_version: nil)
+ entity = create(:bulk_import_entity, :group_entity, bulk_import: bulk_import)
+
+ subject.perform(bulk_import.id)
+
+ expect(entity.trackers.collect { |tracker| [tracker.status_name, tracker.relation] }).to contain_exactly(
+ [:created, 'PipelineClass1'],
+ [:created, 'PipelineClass2'],
+ [:created, 'PipelineClass3'],
+ [:created, 'PipelineClass4'],
+ [:created, 'PipelineClass5']
+ )
+ end
+ end
+ end
+
+ context 'when tracker configuration has a maximum version defined' do
+ before do
+ allow_next_instance_of(BulkImports::Groups::Stage) do |stage|
+ allow(stage).to receive(:config).and_return(
+ {
+ pipeline1: { pipeline: 'PipelineClass1', stage: 0 },
+ pipeline2: { pipeline: 'PipelineClass2', stage: 1, maximum_source_version: '14.10.0' },
+ pipeline3: { pipeline: 'PipelineClass3', stage: 1, maximum_source_version: '15.0.0' },
+ pipeline5: { pipeline: 'PipelineClass4', stage: 1, maximum_source_version: '15.1.0' },
+ pipeline6: { pipeline: 'PipelineClass5', stage: 1, maximum_source_version: '16.0.0' }
+ }
+ )
+ end
+ end
+
+ context 'when the source instance version is older than the tracker maximum version' do
+ it 'creates trackers as skipped if version requirement does not meet' do
+ bulk_import = create(:bulk_import, source_version: '15.0.0')
+ entity = create(:bulk_import_entity, :group_entity, bulk_import: bulk_import)
+
+ subject.perform(bulk_import.id)
+
+ expect(entity.trackers.collect { |tracker| [tracker.status_name, tracker.relation] }).to contain_exactly(
+ [:created, 'PipelineClass1'],
+ [:skipped, 'PipelineClass2'],
+ [:created, 'PipelineClass3'],
+ [:created, 'PipelineClass4'],
+ [:created, 'PipelineClass5']
+ )
+ end
+ end
+
+ context 'when the source instance version is a patch version' do
+ it 'creates trackers with the same status as the non-patch source version' do
+ bulk_import_1 = create(:bulk_import, source_version: '15.0.1')
+ entity_1 = create(:bulk_import_entity, :group_entity, bulk_import: bulk_import_1)
+
+ bulk_import_2 = create(:bulk_import, source_version: '15.0.0')
+ entity_2 = create(:bulk_import_entity, :group_entity, bulk_import: bulk_import_2)
+
+ described_class.perform_inline(bulk_import_1.id)
+ described_class.perform_inline(bulk_import_2.id)
+
+ trackers_1 = entity_1.trackers.collect { |tracker| [tracker.status_name, tracker.relation] }
+ trackers_2 = entity_2.trackers.collect { |tracker| [tracker.status_name, tracker.relation] }
+
+ expect(trackers_1).to eq(trackers_2)
+ end
+ end
+ end
end
end
diff --git a/spec/workers/bulk_imports/finish_project_import_worker_spec.rb b/spec/workers/bulk_imports/finish_project_import_worker_spec.rb
new file mode 100644
index 00000000000..3f5f8477667
--- /dev/null
+++ b/spec/workers/bulk_imports/finish_project_import_worker_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BulkImports::FinishProjectImportWorker, feature_category: :importers do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:job_args) { [project.id] }
+
+ describe '#perform' do
+ it_behaves_like 'an idempotent worker' do
+ it 'calls after_import for the project' do
+ expect_next_found_instance_of(Project) do |project|
+ expect(project).to receive(:after_import)
+ end
+
+ described_class.new.perform(project.id)
+ end
+
+ context 'when no project is found' do
+ let(:job_args) { nil }
+
+ it 'returns without error' do
+ expect { described_class.new.perform(project.id) }.not_to raise_error
+ end
+ end
+ end
+ end
+end
diff --git a/spec/workers/bulk_imports/pipeline_batch_worker_spec.rb b/spec/workers/bulk_imports/pipeline_batch_worker_spec.rb
index c10e1b486ab..3c33910b62c 100644
--- a/spec/workers/bulk_imports/pipeline_batch_worker_spec.rb
+++ b/spec/workers/bulk_imports/pipeline_batch_worker_spec.rb
@@ -73,6 +73,16 @@ RSpec.describe BulkImports::PipelineBatchWorker, feature_category: :importers do
end
end
+ context 'when batch status is started' do
+ let(:batch) { create(:bulk_import_batch_tracker, :started, tracker: tracker) }
+
+ it 'runs the given pipeline batch successfully' do
+ subject.perform(batch.id)
+
+ expect(batch.reload).to be_finished
+ end
+ end
+
context 'when exclusive lease cannot be obtained' do
it 'does not run the pipeline' do
expect(subject).to receive(:try_obtain_lease).and_return(false)
diff --git a/spec/workers/click_house/events_sync_worker_spec.rb b/spec/workers/click_house/events_sync_worker_spec.rb
index 8f328839cfd..01267db36a7 100644
--- a/spec/workers/click_house/events_sync_worker_spec.rb
+++ b/spec/workers/click_house/events_sync_worker_spec.rb
@@ -3,48 +3,125 @@
require 'spec_helper'
RSpec.describe ClickHouse::EventsSyncWorker, feature_category: :value_stream_management do
- let(:databases) { { main: :some_db } }
let(:worker) { described_class.new }
- before do
- allow(ClickHouse::Client.configuration).to receive(:databases).and_return(databases)
- end
-
- include_examples 'an idempotent worker' do
- context 'when the event_sync_worker_for_click_house feature flag is on' do
+ it_behaves_like 'an idempotent worker' do
+ context 'when the event_sync_worker_for_click_house feature flag is on', :click_house do
before do
stub_feature_flags(event_sync_worker_for_click_house: true)
end
- it 'returns true' do
- expect(worker).to receive(:log_extra_metadata_on_done).with(:result, { status: :processed })
+ context 'when there is nothing to sync' do
+ it 'adds metadata for the worker' do
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:result,
+ { status: :processed, records_inserted: 0, reached_end_of_table: true })
- worker.perform
+ worker.perform
+
+ events = ClickHouse::Client.select('SELECT * FROM events', :main)
+ expect(events).to be_empty
+ end
end
- context 'when no ClickHouse databases are configured' do
- let(:databases) { {} }
+ context 'when syncing records' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:issue) { create(:issue, project: project) }
+ let_it_be(:project_event2) { create(:event, :closed, project: project, target: issue) }
+ let_it_be(:event_without_parent) { create(:event, :joined, project: nil, group: nil) }
+ let_it_be(:group_event) { create(:event, :created, group: group, project: nil) }
+ let_it_be(:project_event1) { create(:event, :created, project: project, target: issue) }
+ # looks invalid but we have some records like this on PRD
- it 'skips execution' do
- expect(worker).to receive(:log_extra_metadata_on_done).with(:result, { status: :disabled })
+ it 'inserts all records' do
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:result,
+ { status: :processed, records_inserted: 4, reached_end_of_table: true })
worker.perform
+
+ expected_records = [
+ hash_including('id' => project_event2.id, 'path' => "#{group.id}/#{project.project_namespace.id}/",
+ 'target_type' => 'Issue'),
+ hash_including('id' => event_without_parent.id, 'path' => '', 'target_type' => ''),
+ hash_including('id' => group_event.id, 'path' => "#{group.id}/", 'target_type' => ''),
+ hash_including('id' => project_event1.id, 'path' => "#{group.id}/#{project.project_namespace.id}/",
+ 'target_type' => 'Issue')
+ ]
+
+ events = ClickHouse::Client.select('SELECT * FROM events ORDER BY id', :main)
+
+ expect(events).to match(expected_records)
+
+ last_processed_id = ClickHouse::SyncCursor.cursor_for(:events)
+ expect(last_processed_id).to eq(project_event1.id)
end
- end
- context 'when exclusive lease error happens' do
- it 'skips execution' do
- expect(worker).to receive(:in_lock).and_raise(Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError)
- expect(worker).to receive(:log_extra_metadata_on_done).with(:result, { status: :skipped })
+ context 'when multiple batches are needed' do
+ before do
+ stub_const("#{described_class}::BATCH_SIZE", 1)
+ stub_const("#{described_class}::INSERT_BATCH_SIZE", 1)
+ end
- worker.perform
+ it 'inserts all records' do
+ worker.perform
+
+ events = ClickHouse::Client.select('SELECT * FROM events', :main)
+ expect(events.size).to eq(4)
+ end
+ end
+
+ context 'when time limit is reached' do
+ before do
+ stub_const("#{described_class}::BATCH_SIZE", 1)
+ end
+
+ it 'stops the processing' do
+ allow_next_instance_of(Analytics::CycleAnalytics::RuntimeLimiter) do |runtime_limiter|
+ allow(runtime_limiter).to receive(:over_time?).and_return(false, true)
+ end
+
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:result,
+ { status: :processed, records_inserted: 2, reached_end_of_table: false })
+
+ worker.perform
+
+ last_processed_id = ClickHouse::SyncCursor.cursor_for(:events)
+ expect(last_processed_id).to eq(event_without_parent.id)
+ end
+ end
+
+ context 'when syncing from a certain point' do
+ before do
+ ClickHouse::SyncCursor.update_cursor_for(:events, project_event2.id)
+ end
+
+ it 'syncs records after the cursor' do
+ worker.perform
+
+ events = ClickHouse::Client.select('SELECT id FROM events ORDER BY id', :main)
+ expect(events).to eq([{ 'id' => event_without_parent.id }, { 'id' => group_event.id },
+ { 'id' => project_event1.id }])
+ end
+
+ context 'when there is nothing to sync' do
+ it 'does nothing' do
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:result,
+ { status: :processed, records_inserted: 0, reached_end_of_table: true })
+
+ ClickHouse::SyncCursor.update_cursor_for(:events, project_event1.id)
+ worker.perform
+
+ events = ClickHouse::Client.select('SELECT id FROM events ORDER BY id', :main)
+ expect(events).to be_empty
+ end
+ end
end
end
end
- context 'when the event_sync_worker_for_click_house feature flag is off' do
+ context 'when clickhouse is not configured' do
before do
- stub_feature_flags(event_sync_worker_for_click_house: false)
+ allow(ClickHouse::Client.configuration).to receive(:databases).and_return({})
end
it 'skips execution' do
@@ -54,4 +131,28 @@ RSpec.describe ClickHouse::EventsSyncWorker, feature_category: :value_stream_man
end
end
end
+
+ context 'when exclusive lease error happens' do
+ it 'skips execution' do
+ stub_feature_flags(event_sync_worker_for_click_house: true)
+ allow(ClickHouse::Client.configuration).to receive(:databases).and_return({ main: :some_db })
+
+ expect(worker).to receive(:in_lock).and_raise(Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError)
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:result, { status: :skipped })
+
+ worker.perform
+ end
+ end
+
+ context 'when the event_sync_worker_for_click_house feature flag is off' do
+ before do
+ stub_feature_flags(event_sync_worker_for_click_house: false)
+ end
+
+ it 'skips execution' do
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:result, { status: :disabled })
+
+ worker.perform
+ end
+ end
end
diff --git a/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb b/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
index 3b7bbfc8a7b..27e1077b138 100644
--- a/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
+++ b/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
@@ -273,7 +273,8 @@ RSpec.describe Gitlab::GithubImport::ObjectImporter, :aggregate_failures, featur
.to receive(:notify)
.with(
job['args'].last,
- job['jid']
+ job['jid'],
+ ttl: Gitlab::Import::JOB_WAITER_TTL
)
sidekiq_retries_exhausted
diff --git a/spec/workers/concerns/gitlab/github_import/rescheduling_methods_spec.rb b/spec/workers/concerns/gitlab/github_import/rescheduling_methods_spec.rb
index 6475be0243c..c76ce6b555f 100644
--- a/spec/workers/concerns/gitlab/github_import/rescheduling_methods_spec.rb
+++ b/spec/workers/concerns/gitlab/github_import/rescheduling_methods_spec.rb
@@ -110,7 +110,7 @@ RSpec.describe Gitlab::GithubImport::ReschedulingMethods, feature_category: :imp
expect(Gitlab::JobWaiter)
.to receive(:notify)
- .with('123', 'abc123')
+ .with('123', 'abc123', ttl: Gitlab::Import::JOB_WAITER_TTL)
worker.notify_waiter('123')
end
diff --git a/spec/workers/concerns/gitlab/notify_upon_death_spec.rb b/spec/workers/concerns/gitlab/import/notify_upon_death_spec.rb
index 36faf3ee296..1f760e8542b 100644
--- a/spec/workers/concerns/gitlab/notify_upon_death_spec.rb
+++ b/spec/workers/concerns/gitlab/import/notify_upon_death_spec.rb
@@ -2,11 +2,11 @@
require 'spec_helper'
-RSpec.describe Gitlab::NotifyUponDeath, feature_category: :shared do
+RSpec.describe Gitlab::Import::NotifyUponDeath, feature_category: :importers do
let(:worker_class) do
Class.new do
include Sidekiq::Worker
- include Gitlab::NotifyUponDeath
+ include Gitlab::Import::NotifyUponDeath
end
end
@@ -16,13 +16,13 @@ RSpec.describe Gitlab::NotifyUponDeath, feature_category: :shared do
expect(Gitlab::JobWaiter)
.to receive(:notify)
- .with('123abc', '123')
+ .with('123abc', '123', ttl: Gitlab::Import::JOB_WAITER_TTL)
worker_class.sidekiq_retries_exhausted_block.call(job)
end
it 'does not notify the JobWaiter when only 2 arguments are given' do
- job = { 'args' => [12, {}], 'jid' => '123' }
+ job = { 'args' => [12, '123abc'], 'jid' => '123' }
expect(Gitlab::JobWaiter)
.not_to receive(:notify)
@@ -31,7 +31,7 @@ RSpec.describe Gitlab::NotifyUponDeath, feature_category: :shared do
end
it 'does not notify the JobWaiter when only 1 argument is given' do
- job = { 'args' => [12], 'jid' => '123' }
+ job = { 'args' => ['123abc'], 'jid' => '123' }
expect(Gitlab::JobWaiter)
.not_to receive(:notify)
diff --git a/spec/workers/concerns/limited_capacity/worker_spec.rb b/spec/workers/concerns/limited_capacity/worker_spec.rb
index 65906eef0fa..8092adec3b9 100644
--- a/spec/workers/concerns/limited_capacity/worker_spec.rb
+++ b/spec/workers/concerns/limited_capacity/worker_spec.rb
@@ -57,10 +57,26 @@ RSpec.describe LimitedCapacity::Worker, :clean_gitlab_redis_queues, :aggregate_f
it 'enqueues jobs' do
expect(worker_class)
.to receive(:bulk_perform_async)
- .with([[:arg], [:arg], [:arg]])
+ .with([[:arg], [:arg], [:arg]]).and_call_original
+
+ expect(Sidekiq::Client).to receive(:push_bulk)
perform_with_capacity
end
+
+ context 'when max_running_jobs is 0' do
+ let(:max_running_jobs) { 0 }
+
+ it 'does not enqueue jobs' do
+ expect(worker_class)
+ .to receive(:bulk_perform_async)
+ .with([]).and_call_original
+
+ expect(Sidekiq::Client).not_to receive(:push_bulk)
+
+ perform_with_capacity
+ end
+ end
end
describe '#perform' do
diff --git a/spec/workers/database/lock_tables_worker_spec.rb b/spec/workers/database/lock_tables_worker_spec.rb
new file mode 100644
index 00000000000..cb720959db0
--- /dev/null
+++ b/spec/workers/database/lock_tables_worker_spec.rb
@@ -0,0 +1,136 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Database::LockTablesWorker, feature_category: :cell do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:worker) { described_class.new }
+ let(:exception_class) { described_class::TableShouldNotBeLocked }
+
+ describe '#perform' do
+ context 'when running with single database' do # this covers both single-db and single-db-ci-connection cases
+ before do
+ skip_if_database_exists(:ci)
+ end
+
+ it 'skips executing the job' do
+ expect do
+ worker.perform('ci', %w[ci_pipelines])
+ end.to raise_error(exception_class, 'GitLab is not running in multiple database mode')
+ end
+ end
+
+ context 'when running in decomposed database' do
+ before do
+ skip_if_shared_database(:ci)
+ end
+
+ context 'when the table is wrong' do
+ context 'when trying to lock tables on an unknown database' do
+ it 'raises an exception' do
+ expect do
+ worker.perform('foobar', %w[ci_pipelines])
+ end.to raise_error(exception_class, /does not support locking writes on tables/)
+ end
+ end
+
+ context 'when trying to lock tables on the database that does not support locking' do
+ it 'raises an exception' do
+ expect do
+ worker.perform('geo', %w[ci_pipelines]) # ci tables should be locked only on main
+ end.to raise_error(exception_class, /does not support locking writes on tables/)
+ end
+ end
+
+ context 'when trying to lock tables on the wrong database' do
+ it 'raises an exception' do
+ expect do
+ worker.perform('ci', %w[ci_pipelines]) # ci tables should be locked only on main
+ end.to raise_error(exception_class, "table 'ci_pipelines' should not be locked on the database 'ci'")
+ end
+ end
+
+ context 'when trying to lock shared tables on the database' do
+ it 'raises an exception' do
+ expect do
+ worker.perform('main', %w[loose_foreign_keys_deleted_records])
+ end.to raise_error(exception_class, /should not be locked on the database 'main'/)
+ end
+ end
+ end
+
+ context 'when the table is correct' do
+ context 'when the table is not locked for writes' do
+ where(:database_name, :tables) do
+ :ci | %w[users namespaces]
+ :main | %w[ci_pipelines ci_builds]
+ end
+
+ with_them do
+ it 'locks the tables on the corresponding database' do
+ tables.each do |table_name|
+ unlock_table(database_name, table_name)
+ expect(lock_writes_manager(database_name, table_name).table_locked_for_writes?).to eq(false)
+ end
+
+ expected_log_results = tables.map do |table_name|
+ { action: "locked", database: database_name, dry_run: false, table: table_name }
+ end
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:performed_actions, expected_log_results)
+
+ worker.perform(database_name, tables)
+ tables.each do |table_name|
+ expect(lock_writes_manager(database_name, table_name).table_locked_for_writes?).to eq(true)
+ end
+ end
+ end
+
+ context 'when the table is already locked for writes' do
+ where(:database_name, :tables) do
+ :ci | %w[users namespaces]
+ :main | %w[ci_pipelines ci_builds]
+ end
+
+ with_them do
+ it 'skips locking the tables on the corresponding database' do
+ tables.each do |table_name|
+ lock_table(database_name, table_name)
+ end
+
+ expected_log_results = tables.map do |table_name|
+ { action: 'skipped', database: database_name, dry_run: false, table: table_name }
+ end
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:performed_actions, expected_log_results)
+
+ worker.perform(database_name, tables)
+ tables.each do |table_name|
+ expect(lock_writes_manager(database_name, table_name).table_locked_for_writes?).to eq(true)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ def lock_table(database_name, table_name)
+ lock_writes_manager(database_name, table_name).lock_writes
+ end
+
+ def unlock_table(database_name, table_name)
+ lock_writes_manager(database_name, table_name).unlock_writes
+ end
+
+ def lock_writes_manager(database_name, table_name)
+ connection = Gitlab::Database.database_base_models_with_gitlab_shared[database_name].connection
+ Gitlab::Database::LockWritesManager.new(
+ table_name: table_name,
+ connection: connection,
+ database_name: database_name,
+ with_retries: false,
+ dry_run: false
+ )
+ end
+end
diff --git a/spec/workers/database/monitor_locked_tables_worker_spec.rb b/spec/workers/database/monitor_locked_tables_worker_spec.rb
index 47475a0ad4a..7e900259265 100644
--- a/spec/workers/database/monitor_locked_tables_worker_spec.rb
+++ b/spec/workers/database/monitor_locked_tables_worker_spec.rb
@@ -19,6 +19,10 @@ RSpec.describe Database::MonitorLockedTablesWorker, feature_category: :cell do
end
context 'when running in decomposed database' do
+ before do
+ skip_if_shared_database(:ci)
+ end
+
context 'when the feature flag is disabled' do
before do
stub_feature_flags(monitor_database_locked_tables: false)
@@ -32,7 +36,6 @@ RSpec.describe Database::MonitorLockedTablesWorker, feature_category: :cell do
context 'when the feature flag is enabled' do
before do
- skip_if_shared_database(:ci)
stub_feature_flags(monitor_database_locked_tables: true)
allow(Gitlab::Database::TablesLocker).to receive(:new).and_return(tables_locker)
end
@@ -73,6 +76,56 @@ RSpec.describe Database::MonitorLockedTablesWorker, feature_category: :cell do
worker.perform
end
+
+ context 'with automatically locking the unlocked tables' do
+ context 'when there are no tables to be locked' do
+ before do
+ stub_feature_flags(lock_tables_in_monitoring: true)
+ allow(tables_locker).to receive(:lock_writes).and_return([])
+ end
+
+ it 'does not call the Database::LockTablesWorker' do
+ expect(Database::LockTablesWorker).not_to receive(:perform_async)
+ end
+ end
+
+ context 'when there are tables to be locked' do
+ before do
+ lock_writes_results = [
+ { table: 'users', database: 'ci', action: 'needs_lock' },
+ { table: 'projects', database: 'ci', action: 'needs_lock' },
+ { table: 'ci_builds', database: 'main', action: 'needs_lock' },
+ { table: 'ci_pipelines', database: 'main', action: 'skipped' }
+ ]
+ allow(tables_locker).to receive(:lock_writes).and_return(lock_writes_results)
+ end
+
+ context 'when feature flag lock_tables_in_monitoring is enabled' do
+ before do
+ stub_feature_flags(lock_tables_in_monitoring: true)
+ end
+
+ it 'locks the tables that need to be locked' do
+ expect(Database::LockTablesWorker).to receive(:perform_async).once.with('ci', %w[users projects])
+ expect(Database::LockTablesWorker).to receive(:perform_async).once.with('main', %w[ci_builds])
+
+ worker.perform
+ end
+ end
+
+ context 'when feature flag lock_tables_in_monitoring is disabled' do
+ before do
+ stub_feature_flags(lock_tables_in_monitoring: false)
+ end
+
+ it 'does not lock the tables that need to be locked' do
+ expect(Database::LockTablesWorker).not_to receive(:perform_async)
+
+ worker.perform
+ end
+ end
+ end
+ end
end
end
end
diff --git a/spec/workers/environments/stop_job_success_worker_spec.rb b/spec/workers/environments/stop_job_success_worker_spec.rb
index 3a2db8cfb77..df0acf46bd9 100644
--- a/spec/workers/environments/stop_job_success_worker_spec.rb
+++ b/spec/workers/environments/stop_job_success_worker_spec.rb
@@ -4,39 +4,54 @@ require 'spec_helper'
RSpec.describe Environments::StopJobSuccessWorker, feature_category: :continuous_delivery do
describe '#perform' do
- subject { described_class.new.perform(build.id) }
+ let_it_be_with_refind(:environment) { create(:environment, state: :available) }
- context 'when build exists' do
- context 'when the build will stop an environment' do
- let!(:build) { create(:ci_build, :stop_review_app, environment: environment.name, project: environment.project, status: :success) } # rubocop:disable Layout/LineLength
- let(:environment) { create(:environment, state: :available) }
+ subject { described_class.new.perform(job.id) }
- it 'stops the environment' do
+ shared_examples_for 'stopping an associated environment' do
+ it 'stops the environment' do
+ expect(environment).to be_available
+
+ subject
+
+ expect(environment.reload).to be_stopped
+ end
+
+ context 'when the job fails' do
+ before do
+ job.update!(status: :failed)
+ environment.update!(state: :available)
+ end
+
+ it 'does not stop the environment' do
expect(environment).to be_available
subject
- expect(environment.reload).to be_stopped
+ expect(environment.reload).not_to be_stopped
end
+ end
+ end
- context 'when the build fails' do
- before do
- build.update!(status: :failed)
- environment.update!(state: :available)
- end
-
- it 'does not stop the environment' do
- expect(environment).to be_available
+ context 'with build job' do
+ let!(:job) do
+ create(:ci_build, :stop_review_app, environment: environment.name, project: environment.project,
+ status: :success)
+ end
- subject
+ it_behaves_like 'stopping an associated environment'
+ end
- expect(environment.reload).not_to be_stopped
- end
- end
+ context 'with bridge job' do
+ let!(:job) do
+ create(:ci_bridge, :stop_review_app, environment: environment.name, project: environment.project,
+ status: :success)
end
+
+ it_behaves_like 'stopping an associated environment'
end
- context 'when build does not exist' do
+ context 'when job does not exist' do
it 'does not raise exception' do
expect { described_class.new.perform(123) }
.not_to raise_error
diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb
index 3cd030e678d..9a94a836d60 100644
--- a/spec/workers/every_sidekiq_worker_spec.rb
+++ b/spec/workers/every_sidekiq_worker_spec.rb
@@ -142,6 +142,7 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do
'BulkImports::EntityWorker' => false,
'BulkImports::PipelineWorker' => false,
'BulkImports::PipelineBatchWorker' => false,
+ 'BulkImports::FinishProjectImportWorker' => 5,
'Chaos::CpuSpinWorker' => 3,
'Chaos::DbSpinWorker' => 3,
'Chaos::KillWorker' => false,
@@ -194,6 +195,7 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do
'CreateGithubWebhookWorker' => 3,
'CreateNoteDiffFileWorker' => 3,
'CreatePipelineWorker' => 3,
+ 'Database::LockTablesWorker' => false,
'Database::BatchedBackgroundMigration::CiExecutionWorker' => 0,
'Database::BatchedBackgroundMigration::MainExecutionWorker' => 0,
'DeleteContainerRepositoryWorker' => 3,
@@ -233,8 +235,6 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do
'Geo::Batch::ProjectRegistrySchedulerWorker' => 3,
'Geo::Batch::ProjectRegistryWorker' => 3,
'Geo::ContainerRepositorySyncWorker' => 1,
- 'Geo::DesignRepositoryShardSyncWorker' => false,
- 'Geo::DesignRepositorySyncWorker' => 1,
'Geo::DestroyWorker' => 3,
'Geo::EventWorker' => 3,
'Geo::FileRemovalWorker' => 3,
@@ -247,6 +247,7 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do
'Geo::RepositoryVerification::Secondary::SingleWorker' => false,
'Geo::ReverificationBatchWorker' => 0,
'Geo::BulkMarkPendingBatchWorker' => 0,
+ 'Geo::BulkMarkVerificationPendingBatchWorker' => 0,
'Geo::Scheduler::Primary::SchedulerWorker' => false,
'Geo::Scheduler::SchedulerWorker' => false,
'Geo::Scheduler::Secondary::SchedulerWorker' => false,
@@ -255,6 +256,10 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do
'Geo::VerificationTimeoutWorker' => false,
'Geo::VerificationWorker' => 3,
'GeoRepositoryDestroyWorker' => 3,
+ 'Gitlab::BitbucketImport::AdvanceStageWorker' => 3,
+ 'Gitlab::BitbucketImport::Stage::FinishImportWorker' => 3,
+ 'Gitlab::BitbucketImport::Stage::ImportPullRequestsWorker' => 3,
+ 'Gitlab::BitbucketImport::Stage::ImportRepositoryWorker' => 3,
'Gitlab::BitbucketServerImport::AdvanceStageWorker' => 3,
'Gitlab::BitbucketServerImport::Stage::FinishImportWorker' => 3,
'Gitlab::BitbucketServerImport::Stage::ImportLfsObjectsWorker' => 3,
@@ -342,6 +347,9 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do
'JiraConnect::SyncProjectWorker' => 3,
'LdapGroupSyncWorker' => 3,
'Licenses::ResetSubmitLicenseUsageDataBannerWorker' => 13,
+ 'Llm::Embedding::GitlabDocumentation::SetEmbeddingsOnTheRecordWorker' => 5,
+ 'Llm::Embedding::GitlabDocumentation::CreateEmptyEmbeddingsRecordsWorker' => 3,
+ 'Llm::Embedding::GitlabDocumentation::CreateDbEmbeddingsPerDocFileWorker' => 5,
'Llm::TanukiBot::UpdateWorker' => 1,
'Llm::TanukiBot::RecreateRecordsWorker' => 3,
'MailScheduler::IssueDueWorker' => 3,
@@ -366,10 +374,8 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do
'Onboarding::PipelineCreatedWorker' => 3,
'Onboarding::ProgressWorker' => 3,
'Onboarding::UserAddedWorker' => 3,
- 'Namespaces::FreeUserCap::OverLimitNotificationWorker' => false,
'Namespaces::RootStatisticsWorker' => 3,
'Namespaces::ScheduleAggregationWorker' => 3,
- 'Namespaces::FreeUserCap::NotificationClearingWorker' => false,
'NewEpicWorker' => 3,
'NewIssueWorker' => 3,
'NewMergeRequestWorker' => 3,
diff --git a/spec/workers/gitlab/bitbucket_import/advance_stage_worker_spec.rb b/spec/workers/gitlab/bitbucket_import/advance_stage_worker_spec.rb
new file mode 100644
index 00000000000..16e3a3dc481
--- /dev/null
+++ b/spec/workers/gitlab/bitbucket_import/advance_stage_worker_spec.rb
@@ -0,0 +1,115 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BitbucketImport::AdvanceStageWorker, :clean_gitlab_redis_shared_state, feature_category: :importers do
+ let(:project) { create(:project) }
+ let(:import_state) { create(:import_state, project: project, jid: '123') }
+ let(:worker) { described_class.new }
+
+ describe '#perform' do
+ context 'when the project no longer exists' do
+ it 'does not perform any work' do
+ expect(worker).not_to receive(:wait_for_jobs)
+
+ worker.perform(-1, { '123' => 2 }, :finish)
+ end
+ end
+
+ context 'when there are remaining jobs' do
+ before do
+ allow(worker)
+ .to receive(:find_import_state)
+ .and_return(import_state)
+ end
+
+ it 'reschedules itself' do
+ expect(worker)
+ .to receive(:wait_for_jobs)
+ .with({ '123' => 2 })
+ .and_return({ '123' => 1 })
+
+ expect(described_class)
+ .to receive(:perform_in)
+ .with(described_class::INTERVAL, project.id, { '123' => 1 }, :finish)
+
+ worker.perform(project.id, { '123' => 2 }, :finish)
+ end
+ end
+
+ context 'when there are no remaining jobs' do
+ before do
+ allow(worker)
+ .to receive(:find_import_state)
+ .and_return(import_state)
+
+ allow(worker)
+ .to receive(:wait_for_jobs)
+ .with({ '123' => 2 })
+ .and_return({})
+ end
+
+ it 'schedules the next stage' do
+ expect(import_state)
+ .to receive(:refresh_jid_expiration)
+
+ expect(Gitlab::BitbucketImport::Stage::FinishImportWorker)
+ .to receive(:perform_async)
+ .with(project.id)
+
+ worker.perform(project.id, { '123' => 2 }, :finish)
+ end
+
+ it 'raises KeyError when the stage name is invalid' do
+ expect { worker.perform(project.id, { '123' => 2 }, :kittens) }
+ .to raise_error(KeyError)
+ end
+ end
+ end
+
+ describe '#wait_for_jobs' do
+ it 'waits for jobs to complete and returns a new pair of keys to wait for' do
+ waiter1 = instance_double(Gitlab::JobWaiter, jobs_remaining: 1, key: '123')
+ waiter2 = instance_double(Gitlab::JobWaiter, jobs_remaining: 0, key: '456')
+
+ expect(Gitlab::JobWaiter)
+ .to receive(:new)
+ .ordered
+ .with(2, '123')
+ .and_return(waiter1)
+
+ expect(Gitlab::JobWaiter)
+ .to receive(:new)
+ .ordered
+ .with(1, '456')
+ .and_return(waiter2)
+
+ expect(waiter1)
+ .to receive(:wait)
+ .with(described_class::BLOCKING_WAIT_TIME)
+
+ expect(waiter2)
+ .to receive(:wait)
+ .with(described_class::BLOCKING_WAIT_TIME)
+
+ new_waiters = worker.wait_for_jobs({ '123' => 2, '456' => 1 })
+
+ expect(new_waiters).to eq({ '123' => 1 })
+ end
+ end
+
+ describe '#find_import_state' do
+ it 'returns a ProjectImportState' do
+ import_state.update_column(:status, 'started')
+
+ found = worker.find_import_state(project.id)
+
+ expect(found).to be_an_instance_of(ProjectImportState)
+ expect(found.attributes.keys).to match_array(%w[id jid])
+ end
+
+ it 'returns nil if the project import is not running' do
+ expect(worker.find_import_state(project.id)).to be_nil
+ end
+ end
+end
diff --git a/spec/workers/gitlab/bitbucket_import/import_pull_request_worker_spec.rb b/spec/workers/gitlab/bitbucket_import/import_pull_request_worker_spec.rb
new file mode 100644
index 00000000000..082499be515
--- /dev/null
+++ b/spec/workers/gitlab/bitbucket_import/import_pull_request_worker_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BitbucketImport::ImportPullRequestWorker, feature_category: :importers do
+ subject(:worker) { described_class.new }
+
+ it_behaves_like Gitlab::BitbucketImport::ObjectImporter
+end
diff --git a/spec/workers/gitlab/bitbucket_import/stage/finish_import_worker_spec.rb b/spec/workers/gitlab/bitbucket_import/stage/finish_import_worker_spec.rb
new file mode 100644
index 00000000000..11baa58f1ab
--- /dev/null
+++ b/spec/workers/gitlab/bitbucket_import/stage/finish_import_worker_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BitbucketImport::Stage::FinishImportWorker, feature_category: :importers do
+ let_it_be(:project) { create(:project, :import_started) }
+
+ subject(:worker) { described_class.new }
+
+ it_behaves_like Gitlab::BitbucketImport::StageMethods
+
+ it 'does not abort on failure' do
+ expect(worker.abort_on_failure).to be_falsey
+ end
+
+ describe '#perform' do
+ it 'finalises the import process' do
+ expect_next_instance_of(Gitlab::Import::Metrics, :bitbucket_importer, project) do |metric|
+ expect(metric).to receive(:track_finished_import)
+ end
+
+ worker.perform(project.id)
+
+ expect(project.import_state.reload).to be_finished
+ end
+ end
+end
diff --git a/spec/workers/gitlab/bitbucket_import/stage/import_pull_requests_worker_spec.rb b/spec/workers/gitlab/bitbucket_import/stage/import_pull_requests_worker_spec.rb
new file mode 100644
index 00000000000..8f425066160
--- /dev/null
+++ b/spec/workers/gitlab/bitbucket_import/stage/import_pull_requests_worker_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BitbucketImport::Stage::ImportPullRequestsWorker, feature_category: :importers do
+ let_it_be(:project) { create(:project, :import_started) }
+
+ subject(:worker) { described_class.new }
+
+ it_behaves_like Gitlab::BitbucketImport::StageMethods
+
+ describe '#perform' do
+ context 'when the import succeeds' do
+ before do
+ allow_next_instance_of(Gitlab::BitbucketImport::Importers::PullRequestsImporter) do |importer|
+ allow(importer).to receive(:execute).and_return(Gitlab::JobWaiter.new(2, '123'))
+ end
+ end
+
+ it 'schedules the next stage' do
+ expect(Gitlab::BitbucketImport::AdvanceStageWorker).to receive(:perform_async)
+ .with(project.id, { '123' => 2 }, :finish)
+
+ worker.perform(project.id)
+ end
+
+ it 'logs stage start and finish' do
+ expect(Gitlab::BitbucketImport::Logger)
+ .to receive(:info).with(hash_including(message: 'starting stage', project_id: project.id))
+ expect(Gitlab::BitbucketImport::Logger)
+ .to receive(:info).with(hash_including(message: 'stage finished', project_id: project.id))
+
+ worker.perform(project.id)
+ end
+ end
+
+ context 'when project does not exists' do
+ it 'does not call the importer' do
+ expect(Gitlab::BitbucketImport::Importers::PullRequestsImporter).not_to receive(:new)
+
+ worker.perform(-1)
+ end
+ end
+
+ context 'when project import state is not `started`' do
+ it 'does not call the importer' do
+ project = create(:project, :import_canceled)
+
+ expect(Gitlab::BitbucketImport::Importers::PullRequestsImporter).not_to receive(:new)
+
+ worker.perform(project.id)
+ end
+ end
+
+ context 'when the importer fails' do
+ it 'does not schedule the next stage and raises error' do
+ exception = StandardError.new('Error')
+
+ allow_next_instance_of(Gitlab::BitbucketImport::Importers::PullRequestsImporter) do |importer|
+ allow(importer).to receive(:execute).and_raise(exception)
+ end
+
+ expect(Gitlab::Import::ImportFailureService)
+ .to receive(:track).with(
+ project_id: project.id,
+ exception: exception,
+ error_source: described_class.name,
+ fail_import: false
+ ).and_call_original
+
+ expect { worker.perform(project.id) }
+ .to change { Gitlab::BitbucketImport::AdvanceStageWorker.jobs.size }.by(0)
+ .and raise_error(exception)
+ end
+ end
+ end
+end
diff --git a/spec/workers/gitlab/bitbucket_import/stage/import_repository_worker_spec.rb b/spec/workers/gitlab/bitbucket_import/stage/import_repository_worker_spec.rb
new file mode 100644
index 00000000000..2234a49d66c
--- /dev/null
+++ b/spec/workers/gitlab/bitbucket_import/stage/import_repository_worker_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BitbucketImport::Stage::ImportRepositoryWorker, feature_category: :importers do
+ let_it_be(:project) { create(:project, :import_started) }
+
+ let(:worker) { described_class.new }
+
+ it_behaves_like Gitlab::BitbucketImport::StageMethods
+
+ it 'executes the importer and enqueues ImportPullRequestsWorker' do
+ expect(Gitlab::BitbucketImport::Importers::RepositoryImporter).to receive_message_chain(:new, :execute)
+ .and_return(true)
+
+ expect(Gitlab::BitbucketImport::Stage::ImportPullRequestsWorker).to receive(:perform_async).with(project.id)
+ .and_return(true).once
+
+ worker.perform(project.id)
+ end
+end
diff --git a/spec/workers/gitlab/bitbucket_server_import/advance_stage_worker_spec.rb b/spec/workers/gitlab/bitbucket_server_import/advance_stage_worker_spec.rb
new file mode 100644
index 00000000000..14e93440422
--- /dev/null
+++ b/spec/workers/gitlab/bitbucket_server_import/advance_stage_worker_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BitbucketServerImport::AdvanceStageWorker, feature_category: :importers do
+ it_behaves_like Gitlab::Import::AdvanceStage, factory: :import_state
+end
diff --git a/spec/workers/gitlab/bitbucket_server_import/import_pull_request_worker_spec.rb b/spec/workers/gitlab/bitbucket_server_import/import_pull_request_worker_spec.rb
index dd3235f846c..376078532cd 100644
--- a/spec/workers/gitlab/bitbucket_server_import/import_pull_request_worker_spec.rb
+++ b/spec/workers/gitlab/bitbucket_server_import/import_pull_request_worker_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe Gitlab::BitbucketServerImport::ImportPullRequestWorker, feature_c
end
it 'notifies job waiter' do
- expect(Gitlab::JobWaiter).to receive(:notify).with(job_waiter_key, 'jid')
+ expect(Gitlab::JobWaiter).to receive(:notify).with(job_waiter_key, 'jid', ttl: Gitlab::Import::JOB_WAITER_TTL)
worker.perform(project.id, {}, job_waiter_key)
end
@@ -44,7 +44,7 @@ RSpec.describe Gitlab::BitbucketServerImport::ImportPullRequestWorker, feature_c
context 'when project does not exists' do
it 'does not call importer and notifies job waiter' do
expect(importer_class).not_to receive(:new)
- expect(Gitlab::JobWaiter).to receive(:notify).with(job_waiter_key, 'jid')
+ expect(Gitlab::JobWaiter).to receive(:notify).with(job_waiter_key, 'jid', ttl: Gitlab::Import::JOB_WAITER_TTL)
worker.perform(-1, {}, job_waiter_key)
end
@@ -55,7 +55,7 @@ RSpec.describe Gitlab::BitbucketServerImport::ImportPullRequestWorker, feature_c
project = create(:project, :import_canceled)
expect(importer_class).not_to receive(:new)
- expect(Gitlab::JobWaiter).to receive(:notify).with(job_waiter_key, 'jid')
+ expect(Gitlab::JobWaiter).to receive(:notify).with(job_waiter_key, 'jid', ttl: Gitlab::Import::JOB_WAITER_TTL)
worker.perform(project.id, {}, job_waiter_key)
end
diff --git a/spec/workers/gitlab/github_gists_import/import_gist_worker_spec.rb b/spec/workers/gitlab/github_gists_import/import_gist_worker_spec.rb
index 2e89263bcf3..dc715c3026b 100644
--- a/spec/workers/gitlab/github_gists_import/import_gist_worker_spec.rb
+++ b/spec/workers/gitlab/github_gists_import/import_gist_worker_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe Gitlab::GithubGistsImport::ImportGistWorker, feature_category: :i
.to receive(:info)
.with(log_attributes.merge('message' => 'start importer'))
expect(importer).to receive(:execute).and_return(importer_result)
- expect(Gitlab::JobWaiter).to receive(:notify).with('some_key', subject.jid)
+ expect(Gitlab::JobWaiter).to receive(:notify).with('some_key', subject.jid, ttl: Gitlab::Import::JOB_WAITER_TTL)
expect(Gitlab::GithubImport::Logger)
.to receive(:info)
.with(log_attributes.merge('message' => 'importer finished'))
@@ -114,7 +114,9 @@ RSpec.describe Gitlab::GithubGistsImport::ImportGistWorker, feature_category: :i
expect(Gitlab::GithubImport::Logger)
.to receive(:error)
.with(log_attributes.merge('message' => 'importer failed', 'error.message' => 'error_message'))
- expect(Gitlab::JobWaiter).to receive(:notify).with('some_key', subject.jid)
+ expect(Gitlab::JobWaiter)
+ .to receive(:notify)
+ .with('some_key', subject.jid, ttl: Gitlab::Import::JOB_WAITER_TTL)
subject.perform(user.id, gist_hash, 'some_key')
@@ -189,7 +191,7 @@ RSpec.describe Gitlab::GithubGistsImport::ImportGistWorker, feature_category: :i
it 'notifies the JobWaiter' do
expect(Gitlab::JobWaiter)
.to receive(:notify)
- .with(job['args'].last, job['jid'])
+ .with(job['args'].last, job['jid'], ttl: Gitlab::Import::JOB_WAITER_TTL)
sidekiq_retries_exhausted
end
diff --git a/spec/workers/gitlab/github_import/advance_stage_worker_spec.rb b/spec/workers/gitlab/github_import/advance_stage_worker_spec.rb
index 121f30ea9d5..60c117a2a90 100644
--- a/spec/workers/gitlab/github_import/advance_stage_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/advance_stage_worker_spec.rb
@@ -2,114 +2,6 @@
require 'spec_helper'
-RSpec.describe Gitlab::GithubImport::AdvanceStageWorker, :clean_gitlab_redis_shared_state, feature_category: :importers do
- let(:project) { create(:project) }
- let(:import_state) { create(:import_state, project: project, jid: '123') }
- let(:worker) { described_class.new }
-
- describe '#perform' do
- context 'when the project no longer exists' do
- it 'does not perform any work' do
- expect(worker).not_to receive(:wait_for_jobs)
-
- worker.perform(-1, { '123' => 2 }, :finish)
- end
- end
-
- context 'when there are remaining jobs' do
- before do
- allow(worker)
- .to receive(:find_import_state)
- .and_return(import_state)
- end
-
- it 'reschedules itself' do
- expect(worker)
- .to receive(:wait_for_jobs)
- .with({ '123' => 2 })
- .and_return({ '123' => 1 })
-
- expect(described_class)
- .to receive(:perform_in)
- .with(described_class::INTERVAL, project.id, { '123' => 1 }, :finish)
-
- worker.perform(project.id, { '123' => 2 }, :finish)
- end
- end
-
- context 'when there are no remaining jobs' do
- before do
- allow(worker)
- .to receive(:find_import_state)
- .and_return(import_state)
-
- allow(worker)
- .to receive(:wait_for_jobs)
- .with({ '123' => 2 })
- .and_return({})
- end
-
- it 'schedules the next stage' do
- expect(import_state)
- .to receive(:refresh_jid_expiration)
-
- expect(Gitlab::GithubImport::Stage::FinishImportWorker)
- .to receive(:perform_async)
- .with(project.id)
-
- worker.perform(project.id, { '123' => 2 }, :finish)
- end
-
- it 'raises KeyError when the stage name is invalid' do
- expect { worker.perform(project.id, { '123' => 2 }, :kittens) }
- .to raise_error(KeyError)
- end
- end
- end
-
- describe '#wait_for_jobs' do
- it 'waits for jobs to complete and returns a new pair of keys to wait for' do
- waiter1 = double(:waiter1, jobs_remaining: 1, key: '123')
- waiter2 = double(:waiter2, jobs_remaining: 0, key: '456')
-
- expect(Gitlab::JobWaiter)
- .to receive(:new)
- .ordered
- .with(2, '123')
- .and_return(waiter1)
-
- expect(Gitlab::JobWaiter)
- .to receive(:new)
- .ordered
- .with(1, '456')
- .and_return(waiter2)
-
- expect(waiter1)
- .to receive(:wait)
- .with(described_class::BLOCKING_WAIT_TIME)
-
- expect(waiter2)
- .to receive(:wait)
- .with(described_class::BLOCKING_WAIT_TIME)
-
- new_waiters = worker.wait_for_jobs({ '123' => 2, '456' => 1 })
-
- expect(new_waiters).to eq({ '123' => 1 })
- end
- end
-
- describe '#find_import_state' do
- it 'returns a ProjectImportState' do
- import_state.update_column(:status, 'started')
-
- found = worker.find_import_state(project.id)
-
- expect(found).to be_an_instance_of(ProjectImportState)
- expect(found.attributes.keys).to match_array(%w(id jid))
- end
-
- it 'returns nil if the project import is not running' do
- expect(worker.find_import_state(project.id)).to be_nil
- end
- end
+RSpec.describe Gitlab::GithubImport::AdvanceStageWorker, feature_category: :importers do
+ it_behaves_like Gitlab::Import::AdvanceStage, factory: :import_state
end
diff --git a/spec/workers/gitlab/jira_import/advance_stage_worker_spec.rb b/spec/workers/gitlab/jira_import/advance_stage_worker_spec.rb
new file mode 100644
index 00000000000..d7c5d8aba4d
--- /dev/null
+++ b/spec/workers/gitlab/jira_import/advance_stage_worker_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::JiraImport::AdvanceStageWorker, feature_category: :importers do
+ it_behaves_like Gitlab::Import::AdvanceStage, factory: :jira_import_state
+end
diff --git a/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb b/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb
index 5209395923f..6dfab44b228 100644
--- a/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb
+++ b/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb
@@ -12,9 +12,9 @@ RSpec.describe Gitlab::JiraImport::ImportIssueWorker, feature_category: :importe
describe 'modules' do
it { expect(described_class).to include_module(ApplicationWorker) }
- it { expect(described_class).to include_module(Gitlab::NotifyUponDeath) }
it { expect(described_class).to include_module(Gitlab::JiraImport::QueueOptions) }
it { expect(described_class).to include_module(Gitlab::Import::DatabaseHelpers) }
+ it { expect(described_class).to include_module(Gitlab::Import::NotifyUponDeath) }
end
subject { described_class.new }
diff --git a/spec/workers/incident_management/close_incident_worker_spec.rb b/spec/workers/incident_management/close_incident_worker_spec.rb
index 02ca5260fbd..b218bf4ced1 100644
--- a/spec/workers/incident_management/close_incident_worker_spec.rb
+++ b/spec/workers/incident_management/close_incident_worker_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe IncidentManagement::CloseIncidentWorker, feature_category: :incid
subject(:worker) { described_class.new }
describe '#perform' do
- let_it_be(:user) { User.alert_bot }
+ let_it_be(:user) { Users::Internal.alert_bot }
let_it_be(:project) { create(:project) }
let_it_be(:issue, reload: true) { create(:incident, project: project) }
diff --git a/spec/workers/incident_management/process_alert_worker_v2_spec.rb b/spec/workers/incident_management/process_alert_worker_v2_spec.rb
index 476b6f04942..b9d7cd9fee8 100644
--- a/spec/workers/incident_management/process_alert_worker_v2_spec.rb
+++ b/spec/workers/incident_management/process_alert_worker_v2_spec.rb
@@ -19,14 +19,14 @@ RSpec.describe IncidentManagement::ProcessAlertWorkerV2, feature_category: :inci
allow(Gitlab::AppLogger).to receive(:warn).and_call_original
allow(AlertManagement::CreateAlertIssueService)
- .to receive(:new).with(alert, User.alert_bot)
+ .to receive(:new).with(alert, Users::Internal.alert_bot)
.and_call_original
end
shared_examples 'creates issue successfully' do
it 'creates an issue' do
expect(AlertManagement::CreateAlertIssueService)
- .to receive(:new).with(alert, User.alert_bot)
+ .to receive(:new).with(alert, Users::Internal.alert_bot)
expect { perform_worker }.to change { Issue.count }.by(1)
end
diff --git a/spec/workers/loose_foreign_keys/cleanup_worker_spec.rb b/spec/workers/loose_foreign_keys/cleanup_worker_spec.rb
index 2e77f38e221..278efd3406c 100644
--- a/spec/workers/loose_foreign_keys/cleanup_worker_spec.rb
+++ b/spec/workers/loose_foreign_keys/cleanup_worker_spec.rb
@@ -166,4 +166,66 @@ RSpec.describe LooseForeignKeys::CleanupWorker, feature_category: :cell do
end
end
end
+
+ describe 'turbo mode' do
+ context 'when turbo mode is off' do
+ where(:database_name, :feature_flag) do
+ :main | :loose_foreign_keys_turbo_mode_main
+ :ci | :loose_foreign_keys_turbo_mode_ci
+ end
+
+ with_them do
+ before do
+ skip unless Gitlab::Database.has_config?(database_name)
+ stub_feature_flags(feature_flag => false)
+ end
+
+ it 'does not use TurboModificationTracker' do
+ allow_next_instance_of(LooseForeignKeys::TurboModificationTracker) do |instance|
+ expect(instance).not_to receive(:over_limit?)
+ end
+
+ perform_for(db: database_name)
+ end
+
+ it 'logs not using turbo mode' do
+ expect_next_instance_of(LooseForeignKeys::CleanupWorker) do |instance|
+ expect(instance).to receive(:log_extra_metadata_on_done).with(:stats, a_hash_including(turbo_mode: false))
+ end
+
+ perform_for(db: database_name)
+ end
+ end
+ end
+
+ context 'when turbo mode is on' do
+ where(:database_name, :feature_flag) do
+ :main | :loose_foreign_keys_turbo_mode_main
+ :ci | :loose_foreign_keys_turbo_mode_ci
+ end
+
+ with_them do
+ before do
+ skip unless Gitlab::Database.has_config?(database_name)
+ stub_feature_flags(feature_flag => true)
+ end
+
+ it 'does not use TurboModificationTracker' do
+ expect_next_instance_of(LooseForeignKeys::TurboModificationTracker) do |instance|
+ expect(instance).to receive(:over_limit?).at_least(:once)
+ end
+
+ perform_for(db: database_name)
+ end
+
+ it 'logs using turbo mode' do
+ expect_next_instance_of(LooseForeignKeys::CleanupWorker) do |instance|
+ expect(instance).to receive(:log_extra_metadata_on_done).with(:stats, a_hash_including(turbo_mode: true))
+ end
+
+ perform_for(db: database_name)
+ end
+ end
+ end
+ end
end
diff --git a/spec/workers/merge_requests/ensure_prepared_worker_spec.rb b/spec/workers/merge_requests/ensure_prepared_worker_spec.rb
new file mode 100644
index 00000000000..8f599ffe642
--- /dev/null
+++ b/spec/workers/merge_requests/ensure_prepared_worker_spec.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe MergeRequests::EnsurePreparedWorker, :sidekiq_inline, feature_category: :code_review_workflow do
+ subject(:worker) { described_class.new }
+
+ let_it_be(:merge_request_1, reload: true) { create(:merge_request, prepared_at: :nil) }
+ let_it_be(:merge_request_2, reload: true) { create(:merge_request, prepared_at: Time.current) }
+ let_it_be(:merge_request_3, reload: true) { create(:merge_request, prepared_at: :nil) }
+
+ describe '#perform' do
+ context 'when ensure_merge_requests_prepared is enabled' do
+ it 'creates the expected NewMergeRequestWorkers of the unprepared merge requests' do
+ expect(merge_request_1.prepared_at).to eq(nil)
+ expect(merge_request_2.prepared_at).to eq(merge_request_2.prepared_at)
+ expect(merge_request_3.prepared_at).to eq(nil)
+
+ worker.perform
+
+ expect(merge_request_1.reload.prepared_at).not_to eq(nil)
+ expect(merge_request_2.reload.prepared_at).to eq(merge_request_2.prepared_at)
+ expect(merge_request_3.reload.prepared_at).not_to eq(nil)
+ end
+ end
+
+ context 'when ensure_merge_requests_prepared is disabled' do
+ before do
+ stub_feature_flags(ensure_merge_requests_prepared: false)
+ end
+
+ it 'does not prepare any merge requests' do
+ expect(merge_request_1.prepared_at).to eq(nil)
+ expect(merge_request_2.prepared_at).to eq(merge_request_2.prepared_at)
+ expect(merge_request_3.prepared_at).to eq(nil)
+
+ worker.perform
+
+ expect(merge_request_1.prepared_at).to eq(nil)
+ expect(merge_request_2.prepared_at).to eq(merge_request_2.prepared_at)
+ expect(merge_request_3.prepared_at).to eq(nil)
+ end
+ end
+ end
+
+ it_behaves_like 'an idempotent worker' do
+ it 'creates the expected NewMergeRequestWorkers of the unprepared merge requests' do
+ expect(merge_request_1.prepared_at).to eq(nil)
+ expect(merge_request_2.prepared_at).to eq(merge_request_2.prepared_at)
+ expect(merge_request_3.prepared_at).to eq(nil)
+
+ subject
+
+ expect(merge_request_1.reload.prepared_at).not_to eq(nil)
+ expect(merge_request_2.reload.prepared_at).to eq(merge_request_2.prepared_at)
+ expect(merge_request_3.reload.prepared_at).not_to eq(nil)
+ end
+ end
+end
diff --git a/spec/workers/metrics/global_metrics_update_worker_spec.rb b/spec/workers/metrics/global_metrics_update_worker_spec.rb
deleted file mode 100644
index d5bfbcc928a..00000000000
--- a/spec/workers/metrics/global_metrics_update_worker_spec.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe ::Metrics::GlobalMetricsUpdateWorker, feature_category: :metrics do
- subject { described_class.new }
-
- describe '#perform' do
- let(:service) { ::Metrics::GlobalMetricsUpdateService.new }
-
- it 'delegates to ::Metrics::GlobalMetricsUpdateService' do
- expect(::Metrics::GlobalMetricsUpdateService).to receive(:new).and_return(service)
- expect(service).to receive(:execute)
-
- subject.perform
- end
-
- context 'for an idempotent worker' do
- include_examples 'an idempotent worker' do
- it 'exports metrics' do
- allow(Gitlab).to receive(:maintenance_mode?).and_return(true).at_least(1).time
-
- perform_multiple
-
- expect(service.maintenance_mode_metric.get).to eq(1)
- end
- end
- end
- end
-end
diff --git a/spec/workers/namespaces/in_product_marketing_emails_worker_spec.rb b/spec/workers/namespaces/in_product_marketing_emails_worker_spec.rb
deleted file mode 100644
index 237b5081bb1..00000000000
--- a/spec/workers/namespaces/in_product_marketing_emails_worker_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Namespaces::InProductMarketingEmailsWorker, '#perform', unless: Gitlab.ee?, feature_category: :experimentation_activation do
- # Running this in EE would call the overridden method, which can't be tested in CE.
- # The EE code is covered in a separate EE spec.
-
- context 'when the in_product_marketing_emails_enabled setting is disabled' do
- before do
- stub_application_setting(in_product_marketing_emails_enabled: false)
- end
-
- it 'does not execute the email service' do
- expect(Namespaces::InProductMarketingEmailsService).not_to receive(:send_for_all_tracks_and_intervals)
-
- subject.perform
- end
- end
-
- context 'when the in_product_marketing_emails_enabled setting is enabled' do
- before do
- stub_application_setting(in_product_marketing_emails_enabled: true)
- end
-
- it 'executes the email service' do
- expect(Namespaces::InProductMarketingEmailsService).to receive(:send_for_all_tracks_and_intervals)
-
- subject.perform
- end
- end
-end
diff --git a/spec/workers/new_merge_request_worker_spec.rb b/spec/workers/new_merge_request_worker_spec.rb
index 58f6792f9a0..4ed9b61a9d7 100644
--- a/spec/workers/new_merge_request_worker_spec.rb
+++ b/spec/workers/new_merge_request_worker_spec.rb
@@ -88,33 +88,25 @@ RSpec.describe NewMergeRequestWorker, feature_category: :code_review_workflow do
worker.perform(merge_request.id, user.id)
end
- context 'when add_prepared_state_to_mr feature flag is off' do
+ context 'when the merge request is prepared' do
before do
- stub_feature_flags(add_prepared_state_to_mr: false)
+ merge_request.update!(prepared_at: Time.current)
end
- it 'calls the create service' do
- expect_next_instance_of(MergeRequests::AfterCreateService, project: merge_request.target_project, current_user: user) do |service|
- expect(service).to receive(:execute).with(merge_request)
- end
+ it 'does not call the create service' do
+ expect(MergeRequests::AfterCreateService).not_to receive(:new)
worker.perform(merge_request.id, user.id)
end
end
- context 'when add_prepared_state_to_mr feature flag is on' do
- before do
- stub_feature_flags(add_prepared_state_to_mr: true)
- end
-
- context 'when the merge request is not prepared' do
- it 'calls the create service' do
- expect_next_instance_of(MergeRequests::AfterCreateService, project: merge_request.target_project, current_user: user) do |service|
- expect(service).to receive(:execute).with(merge_request)
- end
-
- worker.perform(merge_request.id, user.id)
+ context 'when the merge request is not prepared' do
+ it 'calls the create service' do
+ expect_next_instance_of(MergeRequests::AfterCreateService, project: merge_request.target_project, current_user: user) do |service|
+ expect(service).to receive(:execute).with(merge_request).and_call_original
end
+
+ worker.perform(merge_request.id, user.id)
end
end
end
diff --git a/spec/workers/new_note_worker_spec.rb b/spec/workers/new_note_worker_spec.rb
index 651b5742854..3465cffea2d 100644
--- a/spec/workers/new_note_worker_spec.rb
+++ b/spec/workers/new_note_worker_spec.rb
@@ -77,7 +77,7 @@ RSpec.describe NewNoteWorker, feature_category: :team_planning do
end
context 'when Note author has been deleted' do
- let_it_be(:note) { create(:note, author: User.ghost) }
+ let_it_be(:note) { create(:note, author: Users::Internal.ghost) }
it "does not call NotificationService" do
expect(NotificationService).not_to receive(:new)
diff --git a/spec/workers/pages/invalidate_domain_cache_worker_spec.rb b/spec/workers/pages/invalidate_domain_cache_worker_spec.rb
deleted file mode 100644
index b3c81b25a93..00000000000
--- a/spec/workers/pages/invalidate_domain_cache_worker_spec.rb
+++ /dev/null
@@ -1,267 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Pages::InvalidateDomainCacheWorker, feature_category: :pages do
- shared_examples 'clears caches with' do |event_class:, event_data:, caches:|
- include AfterNextHelpers
-
- let(:event) { event_class.new(data: event_data) }
-
- subject { consume_event(subscriber: described_class, event: event) }
-
- it_behaves_like 'subscribes to event'
-
- it 'clears the cache with Gitlab::Pages::CacheControl' do
- caches.each do |cache|
- expect_next(Gitlab::Pages::CacheControl, type: cache[:type], id: cache[:id])
- .to receive(:clear_cache)
- end
-
- subject
- end
- end
-
- context 'when a project have multiple domains' do
- include AfterNextHelpers
-
- let_it_be(:project) { create(:project) }
- let_it_be(:pages_domain) { create(:pages_domain, project: project) }
- let_it_be(:pages_domain2) { create(:pages_domain, project: project) }
-
- let(:event) do
- Pages::PageDeployedEvent.new(
- data: {
- project_id: project.id,
- namespace_id: project.namespace_id,
- root_namespace_id: project.root_ancestor.id
- }
- )
- end
-
- subject { consume_event(subscriber: described_class, event: event) }
-
- it 'clears the cache with Gitlab::Pages::CacheControl' do
- expect_next(Gitlab::Pages::CacheControl, type: :namespace, id: project.namespace_id)
- .to receive(:clear_cache)
- expect_next(Gitlab::Pages::CacheControl, type: :domain, id: pages_domain.id)
- .to receive(:clear_cache)
- expect_next(Gitlab::Pages::CacheControl, type: :domain, id: pages_domain2.id)
- .to receive(:clear_cache)
-
- subject
- end
- end
-
- it_behaves_like 'clears caches with',
- event_class: Pages::PageDeployedEvent,
- event_data: { project_id: 1, namespace_id: 2, root_namespace_id: 3 },
- caches: [
- { type: :namespace, id: 3 }
- ]
-
- it_behaves_like 'clears caches with',
- event_class: Pages::PageDeletedEvent,
- event_data: { project_id: 1, namespace_id: 2, root_namespace_id: 3 },
- caches: [
- { type: :namespace, id: 3 }
- ]
-
- it_behaves_like 'clears caches with',
- event_class: Projects::ProjectDeletedEvent,
- event_data: { project_id: 1, namespace_id: 2, root_namespace_id: 3 },
- caches: [
- { type: :namespace, id: 3 }
- ]
-
- it_behaves_like 'clears caches with',
- event_class: Projects::ProjectCreatedEvent,
- event_data: { project_id: 1, namespace_id: 2, root_namespace_id: 3 },
- caches: [
- { type: :namespace, id: 3 }
- ]
-
- it_behaves_like 'clears caches with',
- event_class: Projects::ProjectArchivedEvent,
- event_data: { project_id: 1, namespace_id: 2, root_namespace_id: 3 },
- caches: [
- { type: :namespace, id: 3 }
- ]
-
- it_behaves_like 'clears caches with',
- event_class: Projects::ProjectPathChangedEvent,
- event_data: {
- project_id: 1,
- namespace_id: 2,
- root_namespace_id: 3,
- old_path: 'old_path',
- new_path: 'new_path'
- },
- caches: [
- { type: :namespace, id: 3 }
- ]
-
- it_behaves_like 'clears caches with',
- event_class: Projects::ProjectTransferedEvent,
- event_data: {
- project_id: 1,
- old_namespace_id: 2,
- old_root_namespace_id: 3,
- new_namespace_id: 4,
- new_root_namespace_id: 5
- },
- caches: [
- { type: :namespace, id: 3 },
- { type: :namespace, id: 5 }
- ]
-
- it_behaves_like 'clears caches with',
- event_class: Groups::GroupTransferedEvent,
- event_data: {
- group_id: 1,
- old_root_namespace_id: 3,
- new_root_namespace_id: 5
- },
- caches: [
- { type: :namespace, id: 3 },
- { type: :namespace, id: 5 }
- ]
-
- it_behaves_like 'clears caches with',
- event_class: Groups::GroupPathChangedEvent,
- event_data: {
- group_id: 1,
- root_namespace_id: 2,
- old_path: 'old_path',
- new_path: 'new_path'
- },
- caches: [
- { type: :namespace, id: 2 }
- ]
-
- it_behaves_like 'clears caches with',
- event_class: Groups::GroupDeletedEvent,
- event_data: {
- group_id: 1,
- root_namespace_id: 3
- },
- caches: [
- { type: :namespace, id: 3 }
- ]
-
- it_behaves_like 'clears caches with',
- event_class: PagesDomains::PagesDomainDeletedEvent,
- event_data: {
- project_id: 1,
- namespace_id: 2,
- root_namespace_id: 3,
- domain_id: 4,
- domain: 'somedomain.com'
- },
- caches: [
- { type: :domain, id: 4 },
- { type: :namespace, id: 3 }
- ]
-
- it_behaves_like 'clears caches with',
- event_class: PagesDomains::PagesDomainUpdatedEvent,
- event_data: {
- project_id: 1,
- namespace_id: 2,
- root_namespace_id: 3,
- domain_id: 4,
- domain: 'somedomain.com'
- },
- caches: [
- { type: :domain, id: 4 },
- { type: :namespace, id: 3 }
- ]
-
- it_behaves_like 'clears caches with',
- event_class: PagesDomains::PagesDomainCreatedEvent,
- event_data: {
- project_id: 1,
- namespace_id: 2,
- root_namespace_id: 3,
- domain_id: 4,
- domain: 'somedomain.com'
- },
- caches: [
- { type: :domain, id: 4 },
- { type: :namespace, id: 3 }
- ]
-
- context 'when project attributes change' do
- Projects::ProjectAttributesChangedEvent::PAGES_RELATED_ATTRIBUTES.each do |attribute|
- it_behaves_like 'clears caches with',
- event_class: Projects::ProjectAttributesChangedEvent,
- event_data: {
- project_id: 1,
- namespace_id: 2,
- root_namespace_id: 3,
- domain_id: 4,
- attributes: [attribute]
- },
- caches: [
- { type: :domain, id: 4 },
- { type: :namespace, id: 3 }
- ]
- end
-
- it_behaves_like 'ignores the published event' do
- let(:event) do
- Projects::ProjectAttributesChangedEvent.new(
- data: {
- project_id: 1,
- namespace_id: 2,
- root_namespace_id: 3,
- attributes: ['unknown']
- }
- )
- end
- end
- end
-
- context 'when project features change' do
- it_behaves_like 'clears caches with',
- event_class: Projects::ProjectFeaturesChangedEvent,
- event_data: {
- project_id: 1,
- namespace_id: 2,
- root_namespace_id: 3,
- features: ['pages_access_level']
- },
- caches: [
- { type: :namespace, id: 3 }
- ]
-
- it_behaves_like 'ignores the published event' do
- let(:event) do
- Projects::ProjectFeaturesChangedEvent.new(
- data: {
- project_id: 1,
- namespace_id: 2,
- root_namespace_id: 3,
- features: ['unknown']
- }
- )
- end
- end
- end
-
- context 'when namespace based cache keys are duplicated' do
- # de-dups namespace cache keys
- it_behaves_like 'clears caches with',
- event_class: Projects::ProjectTransferedEvent,
- event_data: {
- project_id: 1,
- old_namespace_id: 2,
- old_root_namespace_id: 5,
- new_namespace_id: 4,
- new_root_namespace_id: 5
- },
- caches: [
- { type: :namespace, id: 5 }
- ]
- end
-end
diff --git a/spec/workers/personal_access_tokens/expiring_worker_spec.rb b/spec/workers/personal_access_tokens/expiring_worker_spec.rb
index 01ce4e85fe2..0cc63fdb85e 100644
--- a/spec/workers/personal_access_tokens/expiring_worker_spec.rb
+++ b/spec/workers/personal_access_tokens/expiring_worker_spec.rb
@@ -58,5 +58,32 @@ RSpec.describe PersonalAccessTokens::ExpiringWorker, type: :worker, feature_cate
expect { worker.perform }.not_to change { pat.reload.expire_notification_delivered }
end
end
+
+ context 'when a token is owned by a project bot' do
+ let_it_be(:maintainer1) { create(:user) }
+ let_it_be(:maintainer2) { create(:user) }
+ let_it_be(:project_bot) { create(:user, :project_bot) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:expiring_token) { create(:personal_access_token, user: project_bot, expires_at: 5.days.from_now) }
+
+ before_all do
+ project.add_developer(project_bot)
+ project.add_maintainer(maintainer1)
+ project.add_maintainer(maintainer2)
+ end
+
+ it 'uses notification service to send the email' do
+ expect_next_instance_of(NotificationService) do |notification_service|
+ expect(notification_service).to receive(:resource_access_tokens_about_to_expire)
+ .with(project_bot, match_array([expiring_token.name]))
+ end
+
+ worker.perform
+ end
+
+ it 'marks the notification as delivered' do
+ expect { worker.perform }.to change { expiring_token.reload.expire_notification_delivered }.from(false).to(true)
+ end
+ end
end
end
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index 5c8a75aca3f..2e0a2535453 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -282,7 +282,7 @@ RSpec.describe PostReceive, feature_category: :source_code_management do
let(:user) { project.creator }
let(:label) { 'counts.source_code_pushes' }
let(:property) { 'source_code_pushes' }
- let(:context) { [Gitlab::Tracking::ServicePingContext.new(data_source: :redis, key_path: label).to_h] }
+ let(:context) { [Gitlab::Usage::MetricDefinition.context_for(label).to_h] }
subject(:post_receive) { perform }
end
diff --git a/spec/workers/projects/record_target_platforms_worker_spec.rb b/spec/workers/projects/record_target_platforms_worker_spec.rb
index ecb6aab7349..116da404112 100644
--- a/spec/workers/projects/record_target_platforms_worker_spec.rb
+++ b/spec/workers/projects/record_target_platforms_worker_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::RecordTargetPlatformsWorker, feature_category: :groups_and_projects do
+RSpec.describe Projects::RecordTargetPlatformsWorker, feature_category: :experimentation_activation do
include ExclusiveLeaseHelpers
let_it_be(:swift) { create(:programming_language, name: 'Swift') }
diff --git a/spec/workers/users/migrate_records_to_ghost_user_in_batches_worker_spec.rb b/spec/workers/users/migrate_records_to_ghost_user_in_batches_worker_spec.rb
index 38ea7c43267..fbbfd44bba6 100644
--- a/spec/workers/users/migrate_records_to_ghost_user_in_batches_worker_spec.rb
+++ b/spec/workers/users/migrate_records_to_ghost_user_in_batches_worker_spec.rb
@@ -34,8 +34,8 @@ RSpec.describe Users::MigrateRecordsToGhostUserInBatchesWorker, feature_category
it 'migrates issue to ghost user' do
subject
- expect(issue.reload.author).to eq(User.ghost)
- expect(issue.last_edited_by).to eq(User.ghost)
+ expect(issue.reload.author).to eq(Users::Internal.ghost)
+ expect(issue.last_edited_by).to eq(Users::Internal.ghost)
end
end
end
diff --git a/spec/workers/users/track_namespace_visits_worker_spec.rb b/spec/workers/users/track_namespace_visits_worker_spec.rb
new file mode 100644
index 00000000000..cfb2b7ab5eb
--- /dev/null
+++ b/spec/workers/users/track_namespace_visits_worker_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Users::TrackNamespaceVisitsWorker, feature_category: :navigation do
+ describe '#perform' do
+ let_it_be(:user) { create(:user) }
+
+ context 'when tracking a group' do
+ let_it_be(:entity) { create(:group) }
+ let_it_be(:entity_type) { 'groups' }
+ let_it_be(:worker) { described_class.new }
+ let_it_be(:model) { ::Users::GroupVisit }
+
+ it_behaves_like 'namespace visits tracking worker'
+ end
+
+ context 'when tracking a project' do
+ let_it_be(:entity) { create(:project) }
+ let_it_be(:entity_type) { 'projects' }
+ let_it_be(:worker) { described_class.new }
+ let_it_be(:model) { ::Users::ProjectVisit }
+
+ it_behaves_like 'namespace visits tracking worker'
+ end
+ end
+end