diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-12 09:09:55 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-12 09:09:55 +0000 |
commit | 18f7828977b74bf6e5153594a098ef90e773b3b7 (patch) | |
tree | 49cb1e16d5341d773807ee583357ae6eb167d61f /spec | |
parent | 8191b1571c017378eac33b3ed296ad5216d0a410 (diff) | |
download | gitlab-ce-18f7828977b74bf6e5153594a098ef90e773b3b7.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
19 files changed, 512 insertions, 70 deletions
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 67e24841dee..53a57937e9b 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -1140,7 +1140,7 @@ describe ProjectsController do end it 'prevents requesting project export' do - get action, params: { namespace_id: project.namespace, id: project } + post action, params: { namespace_id: project.namespace, id: project } expect(flash[:alert]).to eq('This endpoint has been requested too many times. Try again later.') expect(response).to have_gitlab_http_status(:found) @@ -1152,7 +1152,7 @@ describe ProjectsController do context 'when project export is enabled' do it 'returns 302' do - get action, params: { namespace_id: project.namespace, id: project } + post action, params: { namespace_id: project.namespace, id: project } expect(response).to have_gitlab_http_status(:found) end @@ -1164,7 +1164,7 @@ describe ProjectsController do end it 'returns 404' do - get action, params: { namespace_id: project.namespace, id: project } + post action, params: { namespace_id: project.namespace, id: project } expect(response).to have_gitlab_http_status(:not_found) end diff --git a/spec/factories/project_export_jobs.rb b/spec/factories/project_export_jobs.rb new file mode 100644 index 00000000000..b2666555ea8 --- /dev/null +++ b/spec/factories/project_export_jobs.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :project_export_job do + project + jid { SecureRandom.hex(8) } + end +end diff --git a/spec/features/markdown/mermaid_spec.rb b/spec/features/markdown/mermaid_spec.rb index 542caccb18d..1cd5760c30e 100644 --- a/spec/features/markdown/mermaid_spec.rb +++ b/spec/features/markdown/mermaid_spec.rb @@ -94,8 +94,31 @@ describe 'Mermaid rendering', :js do page.find('summary').click svg = page.find('svg.mermaid') - expect(svg[:width].to_i).to be_within(5).of(120) - expect(svg[:height].to_i).to be_within(5).of(220) + expect(svg[:style]).to match(/max-width/) + expect(svg[:width].to_i).to eq(100) + expect(svg[:height].to_i).to eq(0) end end + + it 'correctly sizes mermaid diagram block', :js do + description = <<~MERMAID + ```mermaid + graph TD; + A-->B; + A-->C; + B-->D; + C-->D; + ``` + MERMAID + + project = create(:project, :public) + issue = create(:issue, project: project, description: description) + + visit project_issue_path(project, issue) + + svg = page.find('svg.mermaid') + expect(svg[:style]).to match(/max-width/) + expect(svg[:width].to_i).to eq(100) + expect(svg[:height].to_i).to eq(0) + end end diff --git a/spec/finders/projects/export_job_finder_spec.rb b/spec/finders/projects/export_job_finder_spec.rb new file mode 100644 index 00000000000..31b68717d13 --- /dev/null +++ b/spec/finders/projects/export_job_finder_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Projects::ExportJobFinder do + let(:project) { create(:project) } + let(:project_export_job1) { create(:project_export_job, project: project) } + let(:project_export_job2) { create(:project_export_job, project: project) } + + describe '#execute' do + subject { described_class.new(project, params).execute } + + context 'when queried for a project' do + let(:params) { {} } + + it 'scopes to the project' do + expect(subject).to contain_exactly( + project_export_job1, project_export_job2 + ) + end + end + + context 'when queried by job id' do + let(:params) { { jid: project_export_job1.jid } } + + it 'filters records' do + expect(subject).to contain_exactly(project_export_job1) + end + end + + context 'when queried by status' do + let(:params) { { status: :started } } + + before do + project_export_job2.start! + end + + it 'filters records' do + expect(subject).to contain_exactly(project_export_job2) + end + end + + context 'when queried by invalid status' do + let(:params) { { status: '1234ad' } } + + it 'raises exception' do + expect { subject }.to raise_error(described_class::InvalidExportJobStatusError, 'Invalid export job status') + end + end + end +end diff --git a/spec/fixtures/api/schemas/public_api/v4/project/export_status.json b/spec/fixtures/api/schemas/public_api/v4/project/export_status.json index 81c8815caf6..fd35ba34b49 100644 --- a/spec/fixtures/api/schemas/public_api/v4/project/export_status.json +++ b/spec/fixtures/api/schemas/public_api/v4/project/export_status.json @@ -13,9 +13,10 @@ "type": "string", "enum": [ "none", + "queued", "started", "finished", - "after_export_action" + "regeneration_in_progress" ] } } diff --git a/spec/frontend/blob/blob_file_dropzone_spec.js b/spec/frontend/blob/blob_file_dropzone_spec.js new file mode 100644 index 00000000000..4e9a05418df --- /dev/null +++ b/spec/frontend/blob/blob_file_dropzone_spec.js @@ -0,0 +1,50 @@ +import $ from 'jquery'; +import BlobFileDropzone from '~/blob/blob_file_dropzone'; + +describe('BlobFileDropzone', () => { + preloadFixtures('blob/show.html'); + let dropzone; + let replaceFileButton; + const jQueryMock = { + enable: jest.fn(), + disable: jest.fn(), + }; + + beforeEach(() => { + loadFixtures('blob/show.html'); + const form = $('.js-upload-blob-form'); + // eslint-disable-next-line no-new + new BlobFileDropzone(form, 'POST'); + dropzone = $('.js-upload-blob-form .dropzone').get(0).dropzone; + dropzone.processQueue = jest.fn(); + replaceFileButton = $('#submit-all'); + $.fn.extend(jQueryMock); + }); + + describe('submit button', () => { + it('requires file', () => { + jest.spyOn(window, 'alert').mockImplementation(() => {}); + + replaceFileButton.click(); + + expect(window.alert).toHaveBeenCalled(); + }); + + it('is disabled while uploading', () => { + jest.spyOn(window, 'alert').mockImplementation(() => {}); + + const file = new File([], 'some-file.jpg'); + const fakeEvent = $.Event('drop', { + dataTransfer: { files: [file] }, + }); + + dropzone.listeners[0].events.drop(fakeEvent); + + replaceFileButton.click(); + + expect(window.alert).not.toHaveBeenCalled(); + expect(jQueryMock.enable).toHaveBeenCalled(); + expect(dropzone.processQueue).toHaveBeenCalled(); + }); + }); +}); diff --git a/spec/javascripts/blob/blob_file_dropzone_spec.js b/spec/javascripts/blob/blob_file_dropzone_spec.js deleted file mode 100644 index fe03775ec4d..00000000000 --- a/spec/javascripts/blob/blob_file_dropzone_spec.js +++ /dev/null @@ -1,39 +0,0 @@ -import $ from 'jquery'; -import BlobFileDropzone from '~/blob/blob_file_dropzone'; - -describe('BlobFileDropzone', function() { - preloadFixtures('blob/show.html'); - - beforeEach(() => { - loadFixtures('blob/show.html'); - const form = $('.js-upload-blob-form'); - this.blobFileDropzone = new BlobFileDropzone(form, 'POST'); - this.dropzone = $('.js-upload-blob-form .dropzone').get(0).dropzone; - this.replaceFileButton = $('#submit-all'); - }); - - describe('submit button', () => { - it('requires file', () => { - spyOn(window, 'alert'); - - this.replaceFileButton.click(); - - expect(window.alert).toHaveBeenCalled(); - }); - - it('is disabled while uploading', () => { - spyOn(window, 'alert'); - - const file = new File([], 'some-file.jpg'); - const fakeEvent = $.Event('drop', { - dataTransfer: { files: [file] }, - }); - - this.dropzone.listeners[0].events.drop(fakeEvent); - this.replaceFileButton.click(); - - expect(window.alert).not.toHaveBeenCalled(); - expect(this.replaceFileButton.is(':disabled')).toEqual(true); - }); - }); -}); diff --git a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb index 86ceb97b250..1631de393b5 100644 --- a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb +++ b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb @@ -3,6 +3,12 @@ require 'spec_helper' describe Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy do + before do + allow_next_instance_of(ProjectExportWorker) do |job| + allow(job).to receive(:jid).and_return(SecureRandom.hex(8)) + end + end + let!(:service) { described_class.new } let!(:project) { create(:project, :with_export) } let(:shared) { project.import_export_shared } diff --git a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb index 95c47d15f8f..7792daed99c 100644 --- a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb +++ b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb @@ -5,6 +5,12 @@ require 'spec_helper' describe Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy do include StubRequests + before do + allow_next_instance_of(ProjectExportWorker) do |job| + allow(job).to receive(:jid).and_return(SecureRandom.hex(8)) + end + end + let(:example_url) { 'http://www.example.com' } let(:strategy) { subject.new(url: example_url, http_method: 'post') } let!(:project) { create(:project, :with_export) } diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index e579c8474b7..37b3e4a4a22 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -469,6 +469,7 @@ project: - autoclose_referenced_issues - status_page_setting - requirements +- export_jobs award_emoji: - awardable - user diff --git a/spec/lib/gitlab/quick_actions/extractor_spec.rb b/spec/lib/gitlab/quick_actions/extractor_spec.rb index 1843acb0cc0..6ea597bf01e 100644 --- a/spec/lib/gitlab/quick_actions/extractor_spec.rb +++ b/spec/lib/gitlab/quick_actions/extractor_spec.rb @@ -291,6 +291,33 @@ describe Gitlab::QuickActions::Extractor do expect(msg).to eq expected end + it 'does not extract commands in multiline inline code on seperated rows' do + msg = "Hello\r\n`\r\nThis is some text\r\n/close\r\n/assign @user\r\n`\r\n\r\nWorld" + expected = msg.delete("\r") + msg, commands = extractor.extract_commands(msg) + + expect(commands).to be_empty + expect(msg).to eq expected + end + + it 'does not extract commands in multiline inline code starting from text' do + msg = "Hello `This is some text\r\n/close\r\n/assign @user\r\n`\r\n\r\nWorld" + expected = msg.delete("\r") + msg, commands = extractor.extract_commands(msg) + + expect(commands).to be_empty + expect(msg).to eq expected + end + + it 'does not extract commands in inline code' do + msg = "`This is some text\r\n/close\r\n/assign @user\r\n`\r\n\r\nWorld" + expected = msg.delete("\r") + msg, commands = extractor.extract_commands(msg) + + expect(commands).to be_empty + expect(msg).to eq expected + end + it 'limits to passed commands when they are passed' do msg = <<~MSG.strip Hello, we should only extract the commands passed diff --git a/spec/models/project_export_job_spec.rb b/spec/models/project_export_job_spec.rb new file mode 100644 index 00000000000..dc39d0e401d --- /dev/null +++ b/spec/models/project_export_job_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe ProjectExportJob, type: :model do + let(:project) { create(:project) } + let!(:job1) { create(:project_export_job, project: project, status: 0) } + let!(:job2) { create(:project_export_job, project: project, status: 2) } + + describe 'associations' do + it { expect(job1).to belong_to(:project) } + end + + describe 'validations' do + it { expect(job1).to validate_presence_of(:project) } + it { expect(job1).to validate_presence_of(:jid) } + it { expect(job1).to validate_presence_of(:status) } + end +end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 782e526b69d..6d9b46c9941 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -3957,6 +3957,12 @@ describe Project do describe '#remove_export' do let(:project) { create(:project, :with_export) } + before do + allow_next_instance_of(ProjectExportWorker) do |job| + allow(job).to receive(:jid).and_return(SecureRandom.hex(8)) + end + end + it 'removes the export' do project.remove_exports @@ -5813,6 +5819,86 @@ describe Project do end end + describe '#add_export_job' do + context 'if not already present' do + it 'starts project export job' do + user = create(:user) + project = build(:project) + + expect(ProjectExportWorker).to receive(:perform_async).with(user.id, project.id, nil, {}) + + project.add_export_job(current_user: user) + end + end + end + + describe '#export_in_progress?' do + let(:project) { build(:project) } + let!(:project_export_job ) { create(:project_export_job, project: project) } + + context 'when project export is enqueued' do + it { expect(project.export_in_progress?).to be false } + end + + context 'when project export is in progress' do + before do + project_export_job.start! + end + + it { expect(project.export_in_progress?).to be true } + end + + context 'when project export is completed' do + before do + finish_job(project_export_job) + end + + it { expect(project.export_in_progress?).to be false } + end + end + + describe '#export_status' do + let(:project) { build(:project) } + let!(:project_export_job ) { create(:project_export_job, project: project) } + + context 'when project export is enqueued' do + it { expect(project.export_status).to eq :queued } + end + + context 'when project export is in progress' do + before do + project_export_job.start! + end + + it { expect(project.export_status).to eq :started } + end + + context 'when project export is completed' do + before do + finish_job(project_export_job) + allow(project).to receive(:export_file).and_return(double(ImportExportUploader, file: 'exists.zip')) + end + + it { expect(project.export_status).to eq :finished } + end + + context 'when project export is being regenerated' do + let!(:new_project_export_job ) { create(:project_export_job, project: project) } + + before do + finish_job(project_export_job) + allow(project).to receive(:export_file).and_return(double(ImportExportUploader, file: 'exists.zip')) + end + + it { expect(project.export_status).to eq :regeneration_in_progress } + end + end + + def finish_job(export_job) + export_job.start + export_job.finish + end + def rugged_config rugged_repo(project.repository).config end diff --git a/spec/requests/api/project_export_spec.rb b/spec/requests/api/project_export_spec.rb index d5c822385da..859a3cca44f 100644 --- a/spec/requests/api/project_export_spec.rb +++ b/spec/requests/api/project_export_spec.rb @@ -27,12 +27,9 @@ describe API::ProjectExport, :clean_gitlab_redis_cache do before do allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) - - # simulate exporting work directory - FileUtils.mkdir_p File.join(project_started.export_path, 'securerandom-hex') - - # simulate in after export action - FileUtils.touch File.join(project_after_export.import_export_shared.lock_files_path, SecureRandom.hex) + allow_next_instance_of(ProjectExportWorker) do |job| + allow(job).to receive(:jid).and_return(SecureRandom.hex(8)) + end end after do @@ -82,28 +79,42 @@ describe API::ProjectExport, :clean_gitlab_redis_cache do expect(json_response['export_status']).to eq('none') end - it 'is started' do - get api(path_started, user) + context 'when project export has started' do + before do + create(:project_export_job, project: project_started, status: 1) + end - expect(response).to have_gitlab_http_status(:ok) - expect(response).to match_response_schema('public_api/v4/project/export_status') - expect(json_response['export_status']).to eq('started') + it 'returns status started' do + get api(path_started, user) + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('public_api/v4/project/export_status') + expect(json_response['export_status']).to eq('started') + end end - it 'is after_export' do - get api(path_after_export, user) + context 'when project export has finished' do + it 'returns status finished' do + get api(path_finished, user) - expect(response).to have_gitlab_http_status(:ok) - expect(response).to match_response_schema('public_api/v4/project/export_status') - expect(json_response['export_status']).to eq('after_export_action') + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('public_api/v4/project/export_status') + expect(json_response['export_status']).to eq('finished') + end end - it 'is finished' do - get api(path_finished, user) + context 'when project export is being regenerated' do + before do + create(:project_export_job, project: project_finished, status: 1) + end + + it 'returns status regeneration_in_progress' do + get api(path_finished, user) - expect(response).to have_gitlab_http_status(:ok) - expect(response).to match_response_schema('public_api/v4/project/export_status') - expect(json_response['export_status']).to eq('finished') + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('public_api/v4/project/export_status') + expect(json_response['export_status']).to eq('regeneration_in_progress') + end end end diff --git a/spec/services/ci/create_cross_project_pipeline_service_spec.rb b/spec/services/ci/create_cross_project_pipeline_service_spec.rb index 667ad532fb0..99c44c3aa17 100644 --- a/spec/services/ci/create_cross_project_pipeline_service_spec.rb +++ b/spec/services/ci/create_cross_project_pipeline_service_spec.rb @@ -362,6 +362,26 @@ describe Ci::CreateCrossProjectPipelineService, '#execute' do end end + context 'when bridge job status update raises state machine errors' do + let(:stub_config) { false } + + before do + stub_ci_pipeline_yaml_file(YAML.dump(invalid: { yaml: 'error' })) + bridge.drop! + end + + it 'tracks the exception' do + expect(Gitlab::ErrorTracking) + .to receive(:track_exception) + .with( + instance_of(Ci::Bridge::InvalidTransitionError), + bridge_id: bridge.id, + downstream_pipeline_id: kind_of(Numeric)) + + service.execute(bridge) + end + end + context 'when bridge job has YAML variables defined' do before do bridge.yaml_variables = [{ key: 'BRIDGE', value: 'var', public: true }] diff --git a/spec/services/ci/pipeline_bridge_status_service_spec.rb b/spec/services/ci/pipeline_bridge_status_service_spec.rb index 95f16af3af9..0b6ae976d97 100644 --- a/spec/services/ci/pipeline_bridge_status_service_spec.rb +++ b/spec/services/ci/pipeline_bridge_status_service_spec.rb @@ -22,6 +22,24 @@ describe Ci::PipelineBridgeStatusService do subject end + + context 'when bridge job status raises state machine errors' do + before do + pipeline.drop! + bridge.drop! + end + + it 'tracks the exception' do + expect(Gitlab::ErrorTracking) + .to receive(:track_exception) + .with( + instance_of(Ci::Bridge::InvalidTransitionError), + bridge_id: bridge.id, + downstream_pipeline_id: pipeline.id) + + subject + end + end end end end diff --git a/spec/workers/concerns/project_export_options_spec.rb b/spec/workers/concerns/project_export_options_spec.rb new file mode 100644 index 00000000000..985afaaf11e --- /dev/null +++ b/spec/workers/concerns/project_export_options_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe ProjectExportOptions do + let(:project) { create(:project) } + let(:project_export_job) { create(:project_export_job, project: project, jid: '123', status: 1) } + let(:job) { { 'args' => [project.owner.id, project.id, nil, nil], 'jid' => '123' } } + let(:worker_class) do + Class.new do + include Sidekiq::Worker + include ProjectExportOptions + end + end + + it 'sets default retry limit' do + expect(worker_class.sidekiq_options['retry']).to eq(ProjectExportOptions::EXPORT_RETRY_COUNT) + end + + it 'sets default status expiration' do + expect(worker_class.sidekiq_options['status_expiration']).to eq(StuckExportJobsWorker::EXPORT_JOBS_EXPIRATION) + end + + describe '.sidekiq_retries_exhausted' do + it 'marks status as failed' do + expect { worker_class.sidekiq_retries_exhausted_block.call(job) }.to change { project_export_job.reload.status }.from(1).to(3) + end + + context 'when status update fails' do + before do + project_export_job.update(status: 2) + end + + it 'logs an error' do + expect(Sidekiq.logger).to receive(:error).with("Failed to set Job #{job['jid']} for project #{project.id} to failed state") + + worker_class.sidekiq_retries_exhausted_block.call(job) + end + end + end +end diff --git a/spec/workers/project_export_worker_spec.rb b/spec/workers/project_export_worker_spec.rb index 8065087796c..d0d52e0df2d 100644 --- a/spec/workers/project_export_worker_spec.rb +++ b/spec/workers/project_export_worker_spec.rb @@ -9,21 +9,59 @@ describe ProjectExportWorker do subject { described_class.new } describe '#perform' do + before do + allow_next_instance_of(described_class) do |job| + allow(job).to receive(:jid).and_return(SecureRandom.hex(8)) + end + end + context 'when it succeeds' do it 'calls the ExportService' do expect_any_instance_of(::Projects::ImportExport::ExportService).to receive(:execute) subject.perform(user.id, project.id, { 'klass' => 'Gitlab::ImportExport::AfterExportStrategies::DownloadNotificationStrategy' }) end + + context 'export job' do + before do + allow_any_instance_of(::Projects::ImportExport::ExportService).to receive(:execute) + end + + it 'creates an export job record for the project' do + expect { subject.perform(user.id, project.id, {}) }.to change { project.export_jobs.count }.from(0).to(1) + end + + it 'sets the export job status to started' do + expect_next_instance_of(ProjectExportJob) do |job| + expect(job).to receive(:start) + end + + subject.perform(user.id, project.id, {}) + end + + it 'sets the export job status to finished' do + expect_next_instance_of(ProjectExportJob) do |job| + expect(job).to receive(:finish) + end + + subject.perform(user.id, project.id, {}) + end + end end context 'when it fails' do - it 'raises an exception when params are invalid' do + it 'does not raise an exception when strategy is invalid' do expect_any_instance_of(::Projects::ImportExport::ExportService).not_to receive(:execute) - expect { subject.perform(1234, project.id, {}) }.to raise_exception(ActiveRecord::RecordNotFound) - expect { subject.perform(user.id, 1234, {}) }.to raise_exception(ActiveRecord::RecordNotFound) - expect { subject.perform(user.id, project.id, { 'klass' => 'Whatever' }) }.to raise_exception(Gitlab::ImportExport::AfterExportStrategyBuilder::StrategyNotFoundError) + expect { subject.perform(user.id, project.id, { 'klass' => 'Whatever' }) }.not_to raise_error + end + + it 'does not raise error when project cannot be found' do + expect { subject.perform(user.id, -234, {}) }.not_to raise_error + end + + it 'does not raise error when user cannot be found' do + expect { subject.perform(-863, project.id, {}) }.not_to raise_error end end end diff --git a/spec/workers/stuck_export_jobs_worker_spec.rb b/spec/workers/stuck_export_jobs_worker_spec.rb new file mode 100644 index 00000000000..fc5758fdadf --- /dev/null +++ b/spec/workers/stuck_export_jobs_worker_spec.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe StuckExportJobsWorker do + let(:worker) { described_class.new } + + shared_examples 'project export job detection' do + context 'when the job has completed' do + context 'when the export status was already updated' do + before do + allow(Gitlab::SidekiqStatus).to receive(:completed_jids) do + project_export_job.start + project_export_job.finish + + [project_export_job.jid] + end + end + + it 'does not mark the export as failed' do + worker.perform + + expect(project_export_job.reload.finished?).to be true + end + end + + context 'when the export status was not updated' do + before do + allow(Gitlab::SidekiqStatus).to receive(:completed_jids) do + project_export_job.start + + [project_export_job.jid] + end + end + + it 'marks the project as failed' do + worker.perform + + expect(project_export_job.reload.failed?).to be true + end + end + + context 'when the job is not in queue and db record in queued state' do + before do + allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return([project_export_job.jid]) + end + + it 'marks the project as failed' do + expect(project_export_job.queued?).to be true + + worker.perform + + expect(project_export_job.reload.failed?).to be true + end + end + end + + context 'when the job is running in Sidekiq' do + before do + allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return([]) + end + + it 'does not mark the project export as failed' do + expect { worker.perform }.not_to change { project_export_job.reload.status } + end + end + end + + describe 'with started export status' do + it_behaves_like 'project export job detection' do + let(:project) { create(:project) } + let!(:project_export_job) { create(:project_export_job, project: project, jid: '123') } + end + end +end |