diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-19 15:44:42 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-19 15:44:42 +0000 |
commit | 4555e1b21c365ed8303ffb7a3325d773c9b8bf31 (patch) | |
tree | 5423a1c7516cffe36384133ade12572cf709398d /spec/models/ci | |
parent | e570267f2f6b326480d284e0164a6464ba4081bc (diff) | |
download | gitlab-ce-4555e1b21c365ed8303ffb7a3325d773c9b8bf31.tar.gz |
Add latest changes from gitlab-org/gitlab@13-12-stable-eev13.12.0-rc42
Diffstat (limited to 'spec/models/ci')
-rw-r--r-- | spec/models/ci/build_dependencies_spec.rb | 22 | ||||
-rw-r--r-- | spec/models/ci/build_spec.rb | 82 | ||||
-rw-r--r-- | spec/models/ci/commit_with_pipeline_spec.rb | 40 | ||||
-rw-r--r-- | spec/models/ci/job_artifact_spec.rb | 28 | ||||
-rw-r--r-- | spec/models/ci/pipeline_artifact_spec.rb | 24 | ||||
-rw-r--r-- | spec/models/ci/pipeline_schedule_spec.rb | 30 | ||||
-rw-r--r-- | spec/models/ci/pipeline_spec.rb | 123 | ||||
-rw-r--r-- | spec/models/ci/runner_namespace_spec.rb | 9 | ||||
-rw-r--r-- | spec/models/ci/runner_project_spec.rb | 9 | ||||
-rw-r--r-- | spec/models/ci/stage_spec.rb | 12 |
10 files changed, 260 insertions, 119 deletions
diff --git a/spec/models/ci/build_dependencies_spec.rb b/spec/models/ci/build_dependencies_spec.rb index e343ec0e698..d00d88ae397 100644 --- a/spec/models/ci/build_dependencies_spec.rb +++ b/spec/models/ci/build_dependencies_spec.rb @@ -18,12 +18,8 @@ RSpec.describe Ci::BuildDependencies do let!(:rubocop_test) { create(:ci_build, pipeline: pipeline, name: 'rubocop', stage_idx: 1, stage: 'test') } let!(:staging) { create(:ci_build, pipeline: pipeline, name: 'staging', stage_idx: 2, stage: 'deploy') } - before do - stub_feature_flags(ci_validate_build_dependencies_override: false) - end - - describe '#local' do - subject { described_class.new(job).local } + context 'for local dependencies' do + subject { described_class.new(job).all } describe 'jobs from previous stages' do context 'when job is in the first stage' do @@ -52,7 +48,7 @@ RSpec.describe Ci::BuildDependencies do project.add_developer(user) end - let(:retried_job) { Ci::Build.retry(rspec_test, user) } + let!(:retried_job) { Ci::Build.retry(rspec_test, user) } it 'contains the retried job instead of the original one' do is_expected.to contain_exactly(build, retried_job, rubocop_test) @@ -150,7 +146,7 @@ RSpec.describe Ci::BuildDependencies do end end - describe '#cross_pipeline' do + context 'for cross_pipeline dependencies' do let!(:job) do create(:ci_build, pipeline: pipeline, @@ -160,7 +156,7 @@ RSpec.describe Ci::BuildDependencies do subject { described_class.new(job) } - let(:cross_pipeline_deps) { subject.cross_pipeline } + let(:cross_pipeline_deps) { subject.all } context 'when dependency specifications are valid' do context 'when pipeline exists in the hierarchy' do @@ -378,14 +374,6 @@ RSpec.describe Ci::BuildDependencies do end it { is_expected.to eq(false) } - - context 'when ci_validate_build_dependencies_override feature flag is enabled' do - before do - stub_feature_flags(ci_validate_build_dependencies_override: job.project) - end - - it { is_expected.to eq(true) } - end end end end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 339dffa507f..66d2f5f4ee9 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -1132,7 +1132,7 @@ RSpec.describe Ci::Build do it "executes UPDATE query" do recorded = ActiveRecord::QueryRecorder.new { subject } - expect(recorded.log.select { |l| l.match?(/UPDATE.*ci_builds/) }.count).to eq(1) + expect(recorded.log.count { |l| l.match?(/UPDATE.*ci_builds/) }).to eq(1) end end @@ -1140,7 +1140,7 @@ RSpec.describe Ci::Build do it 'does not execute UPDATE query' do recorded = ActiveRecord::QueryRecorder.new { subject } - expect(recorded.log.select { |l| l.match?(/UPDATE.*ci_builds/) }.count).to eq(0) + expect(recorded.log.count { |l| l.match?(/UPDATE.*ci_builds/) }).to eq(0) end end end @@ -1205,7 +1205,7 @@ RSpec.describe Ci::Build do before do allow(Deployments::LinkMergeRequestWorker).to receive(:perform_async) - allow(Deployments::ExecuteHooksWorker).to receive(:perform_async) + allow(Deployments::HooksWorker).to receive(:perform_async) end it 'has deployments record with created status' do @@ -1241,7 +1241,7 @@ RSpec.describe Ci::Build do before do allow(Deployments::UpdateEnvironmentWorker).to receive(:perform_async) - allow(Deployments::ExecuteHooksWorker).to receive(:perform_async) + allow(Deployments::HooksWorker).to receive(:perform_async) end it_behaves_like 'avoid deadlock' @@ -3631,46 +3631,29 @@ RSpec.describe Ci::Build do end let!(:job) { create(:ci_build, :pending, pipeline: pipeline, stage_idx: 1, options: options) } + let!(:pre_stage_job) { create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0) } - context 'when validates for dependencies is enabled' do - before do - stub_feature_flags(ci_validate_build_dependencies_override: false) - end - - let!(:pre_stage_job) { create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0) } - - context 'when "dependencies" keyword is not defined' do - let(:options) { {} } - - it { expect(job).to have_valid_build_dependencies } - end - - context 'when "dependencies" keyword is empty' do - let(:options) { { dependencies: [] } } + context 'when "dependencies" keyword is not defined' do + let(:options) { {} } - it { expect(job).to have_valid_build_dependencies } - end + it { expect(job).to have_valid_build_dependencies } + end - context 'when "dependencies" keyword is specified' do - let(:options) { { dependencies: ['test'] } } + context 'when "dependencies" keyword is empty' do + let(:options) { { dependencies: [] } } - it_behaves_like 'validation is active' - end + it { expect(job).to have_valid_build_dependencies } end - context 'when validates for dependencies is disabled' do + context 'when "dependencies" keyword is specified' do let(:options) { { dependencies: ['test'] } } - before do - stub_feature_flags(ci_validate_build_dependencies_override: true) - end - - it_behaves_like 'validation is not active' + it_behaves_like 'validation is active' end end describe 'state transition when build fails' do - let(:service) { ::MergeRequests::AddTodoWhenBuildFailsService.new(project, user) } + let(:service) { ::MergeRequests::AddTodoWhenBuildFailsService.new(project: project, current_user: user) } before do allow(::MergeRequests::AddTodoWhenBuildFailsService).to receive(:new).and_return(service) @@ -4679,25 +4662,30 @@ RSpec.describe Ci::Build do end describe '#execute_hooks' do + before do + build.clear_memoization(:build_data) + end + context 'with project hooks' do + let(:build_data) { double(:BuildData, dup: double(:DupedData)) } + before do create(:project_hook, project: project, job_events: true) end - it 'execute hooks' do - expect_any_instance_of(ProjectHook).to receive(:async_execute) + it 'calls project.execute_hooks(build_data, :job_hooks)' do + expect(::Gitlab::DataBuilder::Build) + .to receive(:build).with(build).and_return(build_data) + expect(build.project) + .to receive(:execute_hooks).with(build_data.dup, :job_hooks) build.execute_hooks end end - context 'without relevant project hooks' do - before do - create(:project_hook, project: project, job_events: false) - end - - it 'does not execute a hook' do - expect_any_instance_of(ProjectHook).not_to receive(:async_execute) + context 'without project hooks' do + it 'does not call project.execute_hooks' do + expect(build.project).not_to receive(:execute_hooks) build.execute_hooks end @@ -4708,8 +4696,10 @@ RSpec.describe Ci::Build do create(:service, active: true, job_events: true, project: project) end - it 'execute services' do - expect_any_instance_of(Service).to receive(:async_execute) + it 'executes services' do + allow_next_found_instance_of(Integration) do |integration| + expect(integration).to receive(:async_execute) + end build.execute_hooks end @@ -4720,8 +4710,10 @@ RSpec.describe Ci::Build do create(:service, active: true, job_events: false, project: project) end - it 'execute services' do - expect_any_instance_of(Service).not_to receive(:async_execute) + it 'does not execute services' do + allow_next_found_instance_of(Integration) do |integration| + expect(integration).not_to receive(:async_execute) + end build.execute_hooks end diff --git a/spec/models/ci/commit_with_pipeline_spec.rb b/spec/models/ci/commit_with_pipeline_spec.rb index 4dd288bde62..320143535e2 100644 --- a/spec/models/ci/commit_with_pipeline_spec.rb +++ b/spec/models/ci/commit_with_pipeline_spec.rb @@ -26,15 +26,47 @@ RSpec.describe Ci::CommitWithPipeline do end end + describe '#lazy_latest_pipeline' do + let(:commit_1) do + described_class.new(Commit.new(RepoHelpers.sample_commit, project)) + end + + let(:commit_2) do + described_class.new(Commit.new(RepoHelpers.another_sample_commit, project)) + end + + let!(:commits) { [commit_1, commit_2] } + + it 'executes only 1 SQL query' do + recorder = ActiveRecord::QueryRecorder.new do + # Running this first ensures we don't run one query for every + # commit. + commits.each(&:lazy_latest_pipeline) + + # This forces the execution of the SQL queries necessary to load the + # data. + commits.each { |c| c.latest_pipeline.try(:id) } + end + + expect(recorder.count).to eq(1) + end + end + describe '#latest_pipeline' do let(:pipeline) { double } shared_examples_for 'fetching latest pipeline' do |ref| it 'returns the latest pipeline for the project' do - expect(commit) - .to receive(:latest_pipeline_for_project) - .with(ref, project) - .and_return(pipeline) + if ref + expect(commit) + .to receive(:latest_pipeline_for_project) + .with(ref, project) + .and_return(pipeline) + else + expect(commit) + .to receive(:lazy_latest_pipeline) + .and_return(pipeline) + end expect(result).to eq(pipeline) end diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb index cdb123573f1..3c4769764d5 100644 --- a/spec/models/ci/job_artifact_spec.rb +++ b/spec/models/ci/job_artifact_spec.rb @@ -602,6 +602,34 @@ RSpec.describe Ci::JobArtifact do end end + context 'FastDestroyAll' do + let_it_be(:project) { create(:project) } + let_it_be(:pipeline) { create(:ci_pipeline, project: project) } + let_it_be(:job) { create(:ci_build, pipeline: pipeline, project: project) } + + let!(:job_artifact) { create(:ci_job_artifact, :archive, job: job) } + let(:subjects) { pipeline.job_artifacts } + + describe '.use_fast_destroy' do + it 'performs cascading delete with fast_destroy_all' do + expect(Ci::DeletedObject.count).to eq(0) + expect(subjects.count).to be > 0 + + expect { pipeline.destroy! }.not_to raise_error + + expect(subjects.count).to eq(0) + expect(Ci::DeletedObject.count).to be > 0 + end + + it 'updates project statistics' do + expect(ProjectStatistics).to receive(:increment_statistic).once + .with(project, :build_artifacts_size, -job_artifact.file.size) + + pipeline.destroy! + end + end + end + def file_type_limit_failure_message(type, limit_name) <<~MSG The artifact type `#{type}` is missing its counterpart plan limit which is expected to be named `#{limit_name}`. diff --git a/spec/models/ci/pipeline_artifact_spec.rb b/spec/models/ci/pipeline_artifact_spec.rb index 3fe09f05cab..f65483d2290 100644 --- a/spec/models/ci/pipeline_artifact_spec.rb +++ b/spec/models/ci/pipeline_artifact_spec.rb @@ -50,6 +50,30 @@ RSpec.describe Ci::PipelineArtifact, type: :model do end end + describe 'scopes' do + describe '.unlocked' do + subject(:pipeline_artifacts) { described_class.unlocked } + + context 'when pipeline is locked' do + it 'returns an empty collection' do + expect(pipeline_artifacts).to be_empty + end + end + + context 'when pipeline is unlocked' do + before do + create(:ci_pipeline_artifact, :with_coverage_report) + end + + it 'returns unlocked artifacts' do + codequality_report = create(:ci_pipeline_artifact, :with_codequality_mr_diff_report, :unlocked) + + expect(pipeline_artifacts).to eq([codequality_report]) + end + end + end + end + describe 'file is being stored' do subject { create(:ci_pipeline_artifact, :with_coverage_report) } diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb index 3e5fbbfe823..d5560edbbfd 100644 --- a/spec/models/ci/pipeline_schedule_spec.rb +++ b/spec/models/ci/pipeline_schedule_spec.rb @@ -126,16 +126,6 @@ RSpec.describe Ci::PipelineSchedule do end end - context 'when pipeline schedule runs every minute' do - let(:pipeline_schedule) { create(:ci_pipeline_schedule, :every_minute) } - - it "updates next_run_at to the sidekiq worker's execution time" do - travel_to(Time.zone.parse("2019-06-01 12:18:00+0000")) do - expect(pipeline_schedule.next_run_at).to eq(cron_worker_next_run_at) - end - end - end - context 'when there are two different pipeline schedules in different time zones' do let(:pipeline_schedule_1) { create(:ci_pipeline_schedule, :weekly, cron_timezone: 'Eastern Time (US & Canada)') } let(:pipeline_schedule_2) { create(:ci_pipeline_schedule, :weekly, cron_timezone: 'UTC') } @@ -144,24 +134,6 @@ RSpec.describe Ci::PipelineSchedule do expect(pipeline_schedule_1.next_run_at).not_to eq(pipeline_schedule_2.next_run_at) end end - - context 'when there are two different pipeline schedules in the same time zones' do - let(:pipeline_schedule_1) { create(:ci_pipeline_schedule, :weekly, cron_timezone: 'UTC') } - let(:pipeline_schedule_2) { create(:ci_pipeline_schedule, :weekly, cron_timezone: 'UTC') } - - it 'sets the sames next_run_at' do - expect(pipeline_schedule_1.next_run_at).to eq(pipeline_schedule_2.next_run_at) - end - end - - context 'when updates cron of exsisted pipeline schedule' do - let(:new_cron) { '0 0 1 1 *' } - - it 'updates next_run_at automatically' do - expect { pipeline_schedule.update!(cron: new_cron) } - .to change { pipeline_schedule.next_run_at } - end - end end describe '#schedule_next_run!' do @@ -178,7 +150,7 @@ RSpec.describe Ci::PipelineSchedule do context 'when record is invalid' do before do - allow(pipeline_schedule).to receive(:save!) { raise ActiveRecord::RecordInvalid.new(pipeline_schedule) } + allow(pipeline_schedule).to receive(:save!) { raise ActiveRecord::RecordInvalid, pipeline_schedule } end it 'nullifies the next run at' do diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index b7f5811e945..b9457055a18 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -68,14 +68,23 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do end describe '#downloadable_artifacts' do - let(:build) { create(:ci_build, pipeline: pipeline) } + let_it_be(:build) { create(:ci_build, pipeline: pipeline) } + let_it_be(:downloadable_artifact) { create(:ci_job_artifact, :codequality, job: build) } + let_it_be(:expired_artifact) { create(:ci_job_artifact, :junit, :expired, job: build) } + let_it_be(:undownloadable_artifact) { create(:ci_job_artifact, :trace, job: build) } + + context 'when artifacts are locked' do + it 'returns downloadable artifacts including locked artifacts' do + expect(pipeline.downloadable_artifacts).to contain_exactly(downloadable_artifact, expired_artifact) + end + end - it 'returns downloadable artifacts that have not expired' do - downloadable_artifact = create(:ci_job_artifact, :codequality, job: build) - _expired_artifact = create(:ci_job_artifact, :junit, :expired, job: build) - _undownloadable_artifact = create(:ci_job_artifact, :trace, job: build) + context 'when artifacts are unlocked' do + it 'returns only downloadable artifacts not expired' do + expired_artifact.job.pipeline.unlocked! - expect(pipeline.downloadable_artifacts).to contain_exactly(downloadable_artifact) + expect(pipeline.reload.downloadable_artifacts).to contain_exactly(downloadable_artifact) + end end end end @@ -1939,6 +1948,30 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do expect(pipeline.modified_paths).to match(merge_request.modified_paths) end end + + context 'when source is an external pull request' do + let(:pipeline) do + create(:ci_pipeline, source: :external_pull_request_event, external_pull_request: external_pull_request) + end + + let(:external_pull_request) do + create(:external_pull_request, project: project, target_sha: '281d3a7', source_sha: '498214d') + end + + it 'returns external pull request modified paths' do + expect(pipeline.modified_paths).to match(external_pull_request.modified_paths) + end + + context 'when the FF ci_modified_paths_of_external_prs is disabled' do + before do + stub_feature_flags(ci_modified_paths_of_external_prs: false) + end + + it 'returns nil' do + expect(pipeline.modified_paths).to be_nil + end + end + end end describe '#all_worktree_paths' do @@ -3201,18 +3234,6 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do expect(pipeline.messages.map(&:content)).to contain_exactly('The error message') end - - context 'when feature flag ci_store_pipeline_messages is disabled' do - before do - stub_feature_flags(ci_store_pipeline_messages: false) - end - - it 'does not add pipeline error message' do - pipeline.add_error_message('The error message') - - expect(pipeline.messages).to be_empty - end - end end describe '#has_yaml_errors?' do @@ -4303,26 +4324,80 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do end end - describe 'reset_ancestor_bridges!' do - let_it_be(:pipeline) { create(:ci_pipeline, :created) } + describe '#reset_source_bridge!' do + let(:pipeline) { create(:ci_pipeline, :created, project: project) } + + subject(:reset_bridge) { pipeline.reset_source_bridge!(project.owner) } + + # This whole block will be removed by https://gitlab.com/gitlab-org/gitlab/-/issues/329194 + # It contains some duplicate checks. + context 'when the FF ci_reset_bridge_with_subsequent_jobs is disabled' do + before do + stub_feature_flags(ci_reset_bridge_with_subsequent_jobs: false) + end + + context 'when the pipeline is a child pipeline and the bridge is depended' do + let!(:parent_pipeline) { create(:ci_pipeline) } + let!(:bridge) { create_bridge(parent_pipeline, pipeline, true) } + + it 'marks source bridge as pending' do + reset_bridge + + expect(bridge.reload).to be_pending + end + + context 'when the parent pipeline has subsequent jobs after the bridge' do + let!(:after_bridge_job) { create(:ci_build, :skipped, pipeline: parent_pipeline, stage_idx: bridge.stage_idx + 1) } + + it 'does not touch subsequent jobs of the bridge' do + reset_bridge + + expect(after_bridge_job.reload).to be_skipped + end + end + + context 'when the parent pipeline has a dependent upstream pipeline' do + let!(:upstream_bridge) do + create_bridge(create(:ci_pipeline, project: create(:project)), parent_pipeline, true) + end + + it 'marks all source bridges as pending' do + reset_bridge + + expect(bridge.reload).to be_pending + expect(upstream_bridge.reload).to be_pending + end + end + end + end context 'when the pipeline is a child pipeline and the bridge is depended' do let!(:parent_pipeline) { create(:ci_pipeline) } let!(:bridge) { create_bridge(parent_pipeline, pipeline, true) } it 'marks source bridge as pending' do - pipeline.reset_ancestor_bridges! + reset_bridge expect(bridge.reload).to be_pending end + context 'when the parent pipeline has subsequent jobs after the bridge' do + let!(:after_bridge_job) { create(:ci_build, :skipped, pipeline: parent_pipeline, stage_idx: bridge.stage_idx + 1) } + + it 'marks subsequent jobs of the bridge as processable' do + reset_bridge + + expect(after_bridge_job.reload).to be_created + end + end + context 'when the parent pipeline has a dependent upstream pipeline' do let!(:upstream_bridge) do create_bridge(create(:ci_pipeline, project: create(:project)), parent_pipeline, true) end it 'marks all source bridges as pending' do - pipeline.reset_ancestor_bridges! + reset_bridge expect(bridge.reload).to be_pending expect(upstream_bridge.reload).to be_pending @@ -4335,7 +4410,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do let!(:bridge) { create_bridge(parent_pipeline, pipeline, false) } it 'does not touch source bridge' do - pipeline.reset_ancestor_bridges! + reset_bridge expect(bridge.reload).to be_success end @@ -4346,7 +4421,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do end it 'does not touch any source bridge' do - pipeline.reset_ancestor_bridges! + reset_bridge expect(bridge.reload).to be_success expect(upstream_bridge.reload).to be_success diff --git a/spec/models/ci/runner_namespace_spec.rb b/spec/models/ci/runner_namespace_spec.rb new file mode 100644 index 00000000000..41d805adb9f --- /dev/null +++ b/spec/models/ci/runner_namespace_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::RunnerNamespace do + it_behaves_like 'includes Limitable concern' do + subject { build(:ci_runner_namespace, group: create(:group, :nested), runner: create(:ci_runner, :group)) } + end +end diff --git a/spec/models/ci/runner_project_spec.rb b/spec/models/ci/runner_project_spec.rb new file mode 100644 index 00000000000..13369dba2cf --- /dev/null +++ b/spec/models/ci/runner_project_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::RunnerProject do + it_behaves_like 'includes Limitable concern' do + subject { build(:ci_runner_project, project: create(:project), runner: create(:ci_runner, :project)) } + end +end diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb index e46d9189c86..5e0fcb4882f 100644 --- a/spec/models/ci/stage_spec.rb +++ b/spec/models/ci/stage_spec.rb @@ -286,6 +286,18 @@ RSpec.describe Ci::Stage, :models do end end + context 'when stage has statuses with nil idx' do + before do + create(:ci_build, :running, stage_id: stage.id, stage_idx: nil) + create(:ci_build, :running, stage_id: stage.id, stage_idx: 10) + create(:ci_build, :running, stage_id: stage.id, stage_idx: nil) + end + + it 'sets index to a non-empty value' do + expect { stage.update_legacy_status }.to change { stage.reload.position }.from(nil).to(10) + end + end + context 'when stage does not have statuses' do it 'fallbacks to zero' do expect(stage.reload.position).to be_nil |