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:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-09-19 04:45:44 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-09-19 04:45:44 +0300
commit85dc423f7090da0a52c73eb66faf22ddb20efff9 (patch)
tree9160f299afd8c80c038f08e1545be119f5e3f1e1 /spec/models/ci
parent15c2c8c66dbe422588e5411eee7e68f1fa440bb8 (diff)
Add latest changes from gitlab-org/gitlab@13-4-stable-ee
Diffstat (limited to 'spec/models/ci')
-rw-r--r--spec/models/ci/bridge_spec.rb1
-rw-r--r--spec/models/ci/build_metadata_spec.rb2
-rw-r--r--spec/models/ci/build_spec.rb145
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb123
-rw-r--r--spec/models/ci/build_trace_chunks/fog_spec.rb12
-rw-r--r--spec/models/ci/instance_variable_spec.rb6
-rw-r--r--spec/models/ci/job_artifact_spec.rb40
-rw-r--r--spec/models/ci/legacy_stage_spec.rb2
-rw-r--r--spec/models/ci/persistent_ref_spec.rb8
-rw-r--r--spec/models/ci/pipeline_artifact_spec.rb78
-rw-r--r--spec/models/ci/pipeline_spec.rb473
-rw-r--r--spec/models/ci/ref_spec.rb72
-rw-r--r--spec/models/ci/runner_spec.rb6
13 files changed, 752 insertions, 216 deletions
diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb
index 3a459e5897a..850fc1ec6e6 100644
--- a/spec/models/ci/bridge_spec.rb
+++ b/spec/models/ci/bridge_spec.rb
@@ -50,6 +50,7 @@ RSpec.describe Ci::Bridge do
CI_PROJECT_PATH_SLUG CI_PROJECT_NAMESPACE CI_PROJECT_ROOT_NAMESPACE
CI_PIPELINE_IID CI_CONFIG_PATH CI_PIPELINE_SOURCE CI_COMMIT_MESSAGE
CI_COMMIT_TITLE CI_COMMIT_DESCRIPTION CI_COMMIT_REF_PROTECTED
+ CI_COMMIT_TIMESTAMP
]
expect(bridge.scoped_variables_hash.keys).to include(*variables)
diff --git a/spec/models/ci/build_metadata_spec.rb b/spec/models/ci/build_metadata_spec.rb
index e4d71632957..069864fa765 100644
--- a/spec/models/ci/build_metadata_spec.rb
+++ b/spec/models/ci/build_metadata_spec.rb
@@ -73,7 +73,7 @@ RSpec.describe Ci::BuildMetadata do
context 'when both runner and job timeouts are set' do
before do
- build.update(runner: runner)
+ build.update!(runner: runner)
end
context 'when job timeout is higher than runner timeout' do
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 069ac23c5a4..1e551d9ee33 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -25,6 +25,7 @@ RSpec.describe Ci::Build do
it { is_expected.to have_many(:sourced_pipelines) }
it { is_expected.to have_many(:job_variables) }
it { is_expected.to have_many(:report_results) }
+ it { is_expected.to have_many(:pages_deployments) }
it { is_expected.to have_one(:deployment) }
it { is_expected.to have_one(:runner_session) }
@@ -448,7 +449,7 @@ RSpec.describe Ci::Build do
end
it 'schedules BuildScheduleWorker at the right time' do
- Timecop.freeze do
+ freeze_time do
expect(Ci::BuildScheduleWorker)
.to receive(:perform_at).with(be_like_time(1.minute.since), build.id)
@@ -496,7 +497,7 @@ RSpec.describe Ci::Build do
let(:option) { { start_in: '1 day' } }
it 'returns date after 1 day' do
- Timecop.freeze do
+ freeze_time do
is_expected.to eq(1.day.since)
end
end
@@ -506,7 +507,7 @@ RSpec.describe Ci::Build do
let(:option) { { start_in: '1 week' } }
it 'returns date after 1 week' do
- Timecop.freeze do
+ freeze_time do
is_expected.to eq(1.week.since)
end
end
@@ -566,18 +567,18 @@ RSpec.describe Ci::Build do
let(:runner) { create(:ci_runner, :project, projects: [build.project]) }
before do
- runner.update(contacted_at: 1.second.ago)
+ runner.update!(contacted_at: 1.second.ago)
end
it { is_expected.to be_truthy }
it 'that is inactive' do
- runner.update(active: false)
+ runner.update!(active: false)
is_expected.to be_falsey
end
it 'that is not online' do
- runner.update(contacted_at: nil)
+ runner.update!(contacted_at: nil)
is_expected.to be_falsey
end
@@ -612,6 +613,46 @@ RSpec.describe Ci::Build do
end
end
+ describe '#locked_artifacts?' do
+ subject(:locked_artifacts) { build.locked_artifacts? }
+
+ context 'when pipeline is artifacts_locked' do
+ before do
+ build.pipeline.artifacts_locked!
+ end
+
+ context 'artifacts archive does not exist' do
+ let(:build) { create(:ci_build) }
+
+ it { is_expected.to be_falsy }
+ end
+
+ context 'artifacts archive exists' do
+ let(:build) { create(:ci_build, :artifacts) }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ context 'when pipeline is unlocked' do
+ before do
+ build.pipeline.unlocked!
+ end
+
+ context 'artifacts archive does not exist' do
+ let(:build) { create(:ci_build) }
+
+ it { is_expected.to be_falsy }
+ end
+
+ context 'artifacts archive exists' do
+ let(:build) { create(:ci_build, :artifacts) }
+
+ it { is_expected.to be_falsy }
+ end
+ end
+ end
+
describe '#available_artifacts?' do
let(:build) { create(:ci_build) }
@@ -683,7 +724,7 @@ RSpec.describe Ci::Build do
context 'is expired' do
before do
- build.update(artifacts_expire_at: Time.current - 7.days)
+ build.update!(artifacts_expire_at: Time.current - 7.days)
end
it { is_expected.to be_truthy }
@@ -691,7 +732,7 @@ RSpec.describe Ci::Build do
context 'is not expired' do
before do
- build.update(artifacts_expire_at: Time.current + 7.days)
+ build.update!(artifacts_expire_at: Time.current + 7.days)
end
it { is_expected.to be_falsey }
@@ -1012,18 +1053,53 @@ RSpec.describe Ci::Build do
end
describe '#hide_secrets' do
+ let(:metrics) { spy('metrics') }
let(:subject) { build.hide_secrets(data) }
context 'hide runners token' do
let(:data) { "new #{project.runners_token} data"}
it { is_expected.to match(/^new x+ data$/) }
+
+ it 'increments trace mutation metric' do
+ build.hide_secrets(data, metrics)
+
+ expect(metrics)
+ .to have_received(:increment_trace_operation)
+ .with(operation: :mutated)
+ end
end
context 'hide build token' do
let(:data) { "new #{build.token} data"}
it { is_expected.to match(/^new x+ data$/) }
+
+ it 'increments trace mutation metric' do
+ build.hide_secrets(data, metrics)
+
+ expect(metrics)
+ .to have_received(:increment_trace_operation)
+ .with(operation: :mutated)
+ end
+ end
+
+ context 'when build does not include secrets' do
+ let(:data) { 'my build log' }
+
+ it 'does not mutate trace' do
+ trace = build.hide_secrets(data)
+
+ expect(trace).to eq data
+ end
+
+ it 'does not increment trace mutation metric' do
+ build.hide_secrets(data, metrics)
+
+ expect(metrics)
+ .not_to have_received(:increment_trace_operation)
+ .with(operation: :mutated)
+ end
end
end
@@ -1200,7 +1276,7 @@ RSpec.describe Ci::Build do
context 'when environment is defined' do
before do
- build.update(environment: 'review')
+ build.update!(environment: 'review')
end
it { is_expected.to be_truthy }
@@ -1208,7 +1284,7 @@ RSpec.describe Ci::Build do
context 'when environment is not defined' do
before do
- build.update(environment: nil)
+ build.update!(environment: nil)
end
it { is_expected.to be_falsey }
@@ -1316,7 +1392,7 @@ RSpec.describe Ci::Build do
context 'when environment is defined' do
before do
- build.update(environment: 'review')
+ build.update!(environment: 'review')
end
context 'no action is defined' do
@@ -1325,7 +1401,7 @@ RSpec.describe Ci::Build do
context 'and start action is defined' do
before do
- build.update(options: { environment: { action: 'start' } } )
+ build.update!(options: { environment: { action: 'start' } } )
end
it { is_expected.to be_truthy }
@@ -1334,7 +1410,7 @@ RSpec.describe Ci::Build do
context 'when environment is not defined' do
before do
- build.update(environment: nil)
+ build.update!(environment: nil)
end
it { is_expected.to be_falsey }
@@ -1346,7 +1422,7 @@ RSpec.describe Ci::Build do
context 'when environment is defined' do
before do
- build.update(environment: 'review')
+ build.update!(environment: 'review')
end
context 'no action is defined' do
@@ -1355,7 +1431,7 @@ RSpec.describe Ci::Build do
context 'and stop action is defined' do
before do
- build.update(options: { environment: { action: 'stop' } } )
+ build.update!(options: { environment: { action: 'stop' } } )
end
it { is_expected.to be_truthy }
@@ -1364,7 +1440,7 @@ RSpec.describe Ci::Build do
context 'when environment is not defined' do
before do
- build.update(environment: nil)
+ build.update!(environment: nil)
end
it { is_expected.to be_falsey }
@@ -1687,7 +1763,7 @@ RSpec.describe Ci::Build do
describe '#action?' do
before do
- build.update(when: value)
+ build.update!(when: value)
end
subject { build.action? }
@@ -2232,7 +2308,7 @@ RSpec.describe Ci::Build do
describe '#has_expiring_archive_artifacts?' do
context 'when artifacts have expiration date set' do
before do
- build.update(artifacts_expire_at: 1.day.from_now)
+ build.update!(artifacts_expire_at: 1.day.from_now)
end
context 'and job artifacts archive record exists' do
@@ -2252,7 +2328,7 @@ RSpec.describe Ci::Build do
context 'when artifacts do not have expiration date set' do
before do
- build.update(artifacts_expire_at: nil)
+ build.update!(artifacts_expire_at: nil)
end
it 'does not have expiring artifacts' do
@@ -2329,6 +2405,7 @@ RSpec.describe Ci::Build do
{ key: 'CI_COMMIT_TITLE', value: pipeline.git_commit_title, public: true, masked: false },
{ key: 'CI_COMMIT_DESCRIPTION', value: pipeline.git_commit_description, public: true, masked: false },
{ key: 'CI_COMMIT_REF_PROTECTED', value: (!!pipeline.protected_ref?).to_s, public: true, masked: false },
+ { key: 'CI_COMMIT_TIMESTAMP', value: pipeline.git_commit_timestamp, public: true, masked: false },
{ key: 'CI_BUILD_REF', value: build.sha, public: true, masked: false },
{ key: 'CI_BUILD_BEFORE_SHA', value: build.before_sha, public: true, masked: false },
{ key: 'CI_BUILD_REF_NAME', value: build.ref, public: true, masked: false },
@@ -2526,7 +2603,7 @@ RSpec.describe Ci::Build do
end
before do
- build.update(user: user)
+ build.update!(user: user)
end
it { user_variables.each { |v| is_expected.to include(v) } }
@@ -2562,7 +2639,7 @@ RSpec.describe Ci::Build do
end
before do
- build.update(environment: 'production')
+ build.update!(environment: 'production')
end
shared_examples 'containing environment variables' do
@@ -2589,7 +2666,7 @@ RSpec.describe Ci::Build do
context 'when the URL was set from the job' do
before do
- build.update(options: { environment: { url: url } })
+ build.update!(options: { environment: { url: url } })
end
it_behaves_like 'containing environment variables'
@@ -2607,7 +2684,7 @@ RSpec.describe Ci::Build do
context 'when the URL was not set from the job, but environment' do
before do
- environment.update(external_url: url)
+ environment.update!(external_url: url)
end
it_behaves_like 'containing environment variables'
@@ -2643,7 +2720,7 @@ RSpec.describe Ci::Build do
context 'when build started manually' do
before do
- build.update(when: :manual)
+ build.update!(when: :manual)
end
let(:manual_variable) do
@@ -2669,8 +2746,8 @@ RSpec.describe Ci::Build do
end
before do
- build.update(tag: false)
- pipeline.update(tag: false)
+ build.update!(tag: false)
+ pipeline.update!(tag: false)
end
it { is_expected.to include(branch_variable) }
@@ -2682,8 +2759,8 @@ RSpec.describe Ci::Build do
end
before do
- build.update(tag: true)
- pipeline.update(tag: true)
+ build.update!(tag: true)
+ pipeline.update!(tag: true)
end
it { is_expected.to include(tag_variable) }
@@ -2860,7 +2937,7 @@ RSpec.describe Ci::Build do
context 'and is disabled for project' do
before do
- project.update(container_registry_enabled: false)
+ project.update!(container_registry_enabled: false)
end
it { is_expected.to include(ci_registry) }
@@ -2869,7 +2946,7 @@ RSpec.describe Ci::Build do
context 'and is enabled for project' do
before do
- project.update(container_registry_enabled: true)
+ project.update!(container_registry_enabled: true)
end
it { is_expected.to include(ci_registry) }
@@ -2881,7 +2958,7 @@ RSpec.describe Ci::Build do
let(:runner) { create(:ci_runner, description: 'description', tag_list: %w(docker linux)) }
before do
- build.update(runner: runner)
+ build.update!(runner: runner)
end
it { is_expected.to include({ key: 'CI_RUNNER_ID', value: runner.id.to_s, public: true, masked: false }) }
@@ -3685,7 +3762,7 @@ RSpec.describe Ci::Build do
subject { described_class.where(id: build).matches_tag_ids(tag_ids) }
before do
- build.update(tag_list: build_tag_list)
+ build.update!(tag_list: build_tag_list)
end
context 'when have different tags' do
@@ -3731,7 +3808,7 @@ RSpec.describe Ci::Build do
subject { described_class.where(id: build).with_any_tags }
before do
- build.update(tag_list: tag_list)
+ build.update!(tag_list: tag_list)
end
context 'when does have tags' do
@@ -4087,7 +4164,7 @@ RSpec.describe Ci::Build do
let(:path) { 'other_artifacts_0.1.2/another-subdirectory/banana_sample.gif' }
around do |example|
- Timecop.freeze { example.run }
+ freeze_time { example.run }
end
before do
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index a6362d46449..fefe5e3bfca 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -21,6 +21,25 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
stub_artifacts_object_storage
end
+ describe 'chunk creation' do
+ let(:metrics) { spy('metrics') }
+
+ before do
+ allow(::Gitlab::Ci::Trace::Metrics)
+ .to receive(:new)
+ .and_return(metrics)
+ end
+
+ it 'increments trace operation chunked metric' do
+ build_trace_chunk.save!
+
+ expect(metrics)
+ .to have_received(:increment_trace_operation)
+ .with(operation: :chunked)
+ .once
+ end
+ end
+
context 'FastDestroyAll' do
let(:parent) { create(:project) }
let(:pipeline) { create(:ci_pipeline, project: parent) }
@@ -32,7 +51,7 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
expect(external_data_counter).to be > 0
expect(subjects.count).to be > 0
- expect { subjects.first.destroy }.to raise_error('`destroy` and `destroy_all` are forbidden. Please use `fast_destroy_all`')
+ expect { subjects.first.destroy! }.to raise_error('`destroy` and `destroy_all` are forbidden. Please use `fast_destroy_all`')
expect { subjects.destroy_all }.to raise_error('`destroy` and `destroy_all` are forbidden. Please use `fast_destroy_all`') # rubocop: disable Cop/DestroyAll
expect(subjects.count).to be > 0
@@ -57,7 +76,7 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
expect(external_data_counter).to be > 0
expect(subjects.count).to be > 0
- expect { parent.destroy }.not_to raise_error
+ expect { parent.destroy! }.not_to raise_error
expect(subjects.count).to eq(0)
expect(external_data_counter).to eq(0)
@@ -222,6 +241,8 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
subject
build_trace_chunk.reload
+
+ expect(build_trace_chunk.checksum).to be_present
expect(build_trace_chunk.fog?).to be_truthy
expect(build_trace_chunk.data).to eq(new_data)
end
@@ -344,6 +365,24 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
it_behaves_like 'Scheduling no sidekiq worker'
end
end
+
+ describe 'append metrics' do
+ let(:metrics) { spy('metrics') }
+
+ before do
+ allow(::Gitlab::Ci::Trace::Metrics)
+ .to receive(:new)
+ .and_return(metrics)
+ end
+
+ it 'increments trace operation appended metric' do
+ build_trace_chunk.append('123456', 0)
+
+ expect(metrics)
+ .to have_received(:increment_trace_operation)
+ .with(operation: :appended)
+ end
+ end
end
describe '#truncate' do
@@ -461,6 +500,8 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
describe '#persist_data!' do
+ let(:build) { create(:ci_build, :running) }
+
subject { build_trace_chunk.persist_data! }
shared_examples_for 'Atomic operation' do
@@ -502,6 +543,12 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to eq(data)
end
+ it 'calculates CRC32 checksum' do
+ subject
+
+ expect(build_trace_chunk.reload.checksum).to eq '3398914352'
+ end
+
it_behaves_like 'Atomic operation'
end
@@ -516,6 +563,18 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to be_nil
expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to be_nil
end
+
+ context 'when chunk is a final one' do
+ before do
+ create(:ci_build_pending_state, build: build)
+ end
+
+ it 'persists the data' do
+ subject
+
+ expect(build_trace_chunk.fog?).to be_truthy
+ end
+ end
end
end
@@ -565,6 +624,18 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to eq(data)
expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to be_nil
end
+
+ context 'when chunk is a final one' do
+ before do
+ create(:ci_build_pending_state, build: build)
+ end
+
+ it 'persists the data' do
+ subject
+
+ expect(build_trace_chunk.fog?).to be_truthy
+ end
+ end
end
end
@@ -614,6 +685,54 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
end
+ describe 'final?' do
+ let(:build) { create(:ci_build, :running) }
+
+ context 'when build pending state exists' do
+ before do
+ create(:ci_build_pending_state, build: build)
+ end
+
+ context 'when chunks is not the last one' do
+ before do
+ create(:ci_build_trace_chunk, chunk_index: 1, build: build)
+ end
+
+ it 'is not a final chunk' do
+ expect(build.reload.pending_state).to be_present
+ expect(build_trace_chunk).not_to be_final
+ end
+ end
+
+ context 'when chunks is the last one' do
+ it 'is a final chunk' do
+ expect(build.reload.pending_state).to be_present
+ expect(build_trace_chunk).to be_final
+ end
+ end
+ end
+
+ context 'when build pending state does not exist' do
+ context 'when chunks is not the last one' do
+ before do
+ create(:ci_build_trace_chunk, chunk_index: 1, build: build)
+ end
+
+ it 'is not a final chunk' do
+ expect(build.reload.pending_state).not_to be_present
+ expect(build_trace_chunk).not_to be_final
+ end
+ end
+
+ context 'when chunks is the last one' do
+ it 'is not a final chunk' do
+ expect(build.reload.pending_state).not_to be_present
+ expect(build_trace_chunk).not_to be_final
+ end
+ end
+ end
+ end
+
describe 'deletes data in redis after a parent record destroyed' do
let(:project) { create(:project) }
diff --git a/spec/models/ci/build_trace_chunks/fog_spec.rb b/spec/models/ci/build_trace_chunks/fog_spec.rb
index a44ae58dfd2..b7e9adab04a 100644
--- a/spec/models/ci/build_trace_chunks/fog_spec.rb
+++ b/spec/models/ci/build_trace_chunks/fog_spec.rb
@@ -46,9 +46,7 @@ RSpec.describe Ci::BuildTraceChunks::Fog do
end
describe '#set_data' do
- subject { data_store.set_data(model, data) }
-
- let(:data) { 'abc123' }
+ let(:new_data) { 'abc123' }
context 'when data exists' do
let(:model) { create(:ci_build_trace_chunk, :fog_with_data, initial_data: 'sample data in fog') }
@@ -56,9 +54,9 @@ RSpec.describe Ci::BuildTraceChunks::Fog do
it 'overwrites data' do
expect(data_store.data(model)).to eq('sample data in fog')
- subject
+ data_store.set_data(model, new_data)
- expect(data_store.data(model)).to eq('abc123')
+ expect(data_store.data(model)).to eq new_data
end
end
@@ -68,9 +66,9 @@ RSpec.describe Ci::BuildTraceChunks::Fog do
it 'sets new data' do
expect(data_store.data(model)).to be_nil
- subject
+ data_store.set_data(model, new_data)
- expect(data_store.data(model)).to eq('abc123')
+ expect(data_store.data(model)).to eq new_data
end
end
end
diff --git a/spec/models/ci/instance_variable_spec.rb b/spec/models/ci/instance_variable_spec.rb
index 15d0c911bc4..0ef1dfbd55c 100644
--- a/spec/models/ci/instance_variable_spec.rb
+++ b/spec/models/ci/instance_variable_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe Ci::InstanceVariable do
let(:value) { SecureRandom.alphanumeric(10_002) }
it 'raises a database level error' do
- expect { variable.save }.to raise_error(ActiveRecord::StatementInvalid)
+ expect { variable.save! }.to raise_error(ActiveRecord::StatementInvalid)
end
end
@@ -36,7 +36,7 @@ RSpec.describe Ci::InstanceVariable do
let(:value) { SecureRandom.alphanumeric(10_000) }
it 'does not raise database level error' do
- expect { variable.save }.not_to raise_error
+ expect { variable.save! }.not_to raise_error
end
end
end
@@ -85,7 +85,7 @@ RSpec.describe Ci::InstanceVariable do
it 'resets the cache when records are deleted' do
expect(described_class.all_cached).to contain_exactly(protected_variable, unprotected_variable)
- protected_variable.destroy
+ protected_variable.destroy!
expect(described_class.all_cached).to contain_exactly(unprotected_variable)
end
diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb
index 91a669aa3f4..779839df670 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -20,7 +20,9 @@ RSpec.describe Ci::JobArtifact do
it_behaves_like 'having unique enum values'
it_behaves_like 'UpdateProjectStatistics' do
- subject { build(:ci_job_artifact, :archive, size: 107464) }
+ let_it_be(:job, reload: true) { create(:ci_build) }
+
+ subject { build(:ci_job_artifact, :archive, job: job, size: 107464) }
end
describe '.not_expired' do
@@ -343,42 +345,6 @@ RSpec.describe Ci::JobArtifact do
end
end
- describe '#each_blob' do
- context 'when file format is gzip' do
- context 'when gzip file contains one file' do
- let(:artifact) { build(:ci_job_artifact, :junit) }
-
- it 'iterates blob once' do
- expect { |b| artifact.each_blob(&b) }.to yield_control.once
- end
- end
-
- context 'when gzip file contains three files' do
- let(:artifact) { build(:ci_job_artifact, :junit_with_three_testsuites) }
-
- it 'iterates blob three times' do
- expect { |b| artifact.each_blob(&b) }.to yield_control.exactly(3).times
- end
- end
- end
-
- context 'when file format is raw' do
- let(:artifact) { build(:ci_job_artifact, :codequality, file_format: :raw) }
-
- it 'iterates blob once' do
- expect { |b| artifact.each_blob(&b) }.to yield_control.once
- end
- end
-
- context 'when there are no adapters for the file format' do
- let(:artifact) { build(:ci_job_artifact, :junit, file_format: :zip) }
-
- it 'raises an error' do
- expect { |b| artifact.each_blob(&b) }.to raise_error(described_class::NotSupportedAdapterError)
- end
- end
- end
-
describe 'expired?' do
subject { artifact.expired? }
diff --git a/spec/models/ci/legacy_stage_spec.rb b/spec/models/ci/legacy_stage_spec.rb
index c53f6abb037..2487ad85889 100644
--- a/spec/models/ci/legacy_stage_spec.rb
+++ b/spec/models/ci/legacy_stage_spec.rb
@@ -116,7 +116,7 @@ RSpec.describe Ci::LegacyStage do
let!(:new_build) { create_job(:ci_build, status: :success) }
before do
- stage_build.update(retried: true)
+ stage_build.update!(retried: true)
end
it "returns status of latest build" do
diff --git a/spec/models/ci/persistent_ref_spec.rb b/spec/models/ci/persistent_ref_spec.rb
index 18552317025..e488580ae7b 100644
--- a/spec/models/ci/persistent_ref_spec.rb
+++ b/spec/models/ci/persistent_ref_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe Ci::PersistentRef do
context 'when a persistent ref exists' do
before do
- pipeline.persistent_ref.create
+ pipeline.persistent_ref.create # rubocop: disable Rails/SaveBang
end
it { is_expected.to eq(true) }
@@ -32,7 +32,7 @@ RSpec.describe Ci::PersistentRef do
end
describe '#create' do
- subject { pipeline.persistent_ref.create }
+ subject { pipeline.persistent_ref.create } # rubocop: disable Rails/SaveBang
let(:pipeline) { create(:ci_pipeline, sha: sha, project: project) }
let(:project) { create(:project, :repository) }
@@ -58,7 +58,7 @@ RSpec.describe Ci::PersistentRef do
context 'when a persistent ref already exists' do
before do
- pipeline.persistent_ref.create
+ pipeline.persistent_ref.create # rubocop: disable Rails/SaveBang
end
it 'overwrites a persistent ref' do
@@ -78,7 +78,7 @@ RSpec.describe Ci::PersistentRef do
context 'when a persistent ref exists' do
before do
- pipeline.persistent_ref.create
+ pipeline.persistent_ref.create # rubocop: disable Rails/SaveBang
end
it 'deletes the ref' do
diff --git a/spec/models/ci/pipeline_artifact_spec.rb b/spec/models/ci/pipeline_artifact_spec.rb
index 9d63d74a6cc..8cbace845a9 100644
--- a/spec/models/ci/pipeline_artifact_spec.rb
+++ b/spec/models/ci/pipeline_artifact_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Ci::PipelineArtifact, type: :model do
- let_it_be(:coverage_report) { create(:ci_pipeline_artifact) }
+ let(:coverage_report) { create(:ci_pipeline_artifact) }
describe 'associations' do
it { is_expected.to belong_to(:pipeline) }
@@ -12,6 +12,12 @@ RSpec.describe Ci::PipelineArtifact, type: :model do
it_behaves_like 'having unique enum values'
+ it_behaves_like 'UpdateProjectStatistics' do
+ let_it_be(:pipeline, reload: true) { create(:ci_pipeline) }
+
+ subject { build(:ci_pipeline_artifact, pipeline: pipeline) }
+ end
+
describe 'validations' do
it { is_expected.to validate_presence_of(:pipeline) }
it { is_expected.to validate_presence_of(:project) }
@@ -44,38 +50,74 @@ RSpec.describe Ci::PipelineArtifact, type: :model do
end
end
- describe '#set_size' do
+ describe 'file is being stored' do
subject { create(:ci_pipeline_artifact) }
- context 'when file is being created' do
- it 'sets the size' do
- expect(subject.size).to eq(85)
+ context 'when existing object has local store' do
+ it_behaves_like 'mounted file in local store'
+ end
+
+ context 'when direct upload is enabled' do
+ before do
+ stub_artifacts_object_storage(Ci::PipelineArtifactUploader, direct_upload: true)
+ end
+
+ context 'when file is stored' do
+ it_behaves_like 'mounted file in object store'
end
end
- context 'when file is being updated' do
- it 'updates the size' do
- subject.update!(file: fixture_file_upload('spec/fixtures/dk.png'))
+ context 'when file contains multi-byte characters' do
+ let(:coverage_report_multibyte) { create(:ci_pipeline_artifact, :with_multibyte_characters) }
- expect(subject.size).to eq(1062)
+ it 'sets the size in bytesize' do
+ expect(coverage_report_multibyte.size).to eq(14)
end
end
end
- describe 'file is being stored' do
- subject { create(:ci_pipeline_artifact) }
+ describe '.has_code_coverage?' do
+ subject { Ci::PipelineArtifact.has_code_coverage? }
- context 'when existing object has local store' do
- it_behaves_like 'mounted file in local store'
+ context 'when pipeline artifact has a code coverage' do
+ let!(:pipeline_artifact) { create(:ci_pipeline_artifact) }
+
+ it 'returns true' do
+ expect(subject).to be_truthy
+ end
end
- context 'when direct upload is enabled' do
- before do
- stub_artifacts_object_storage(Ci::PipelineArtifactUploader, direct_upload: true)
+ context 'when pipeline artifact does not have a code coverage' do
+ it 'returns false' do
+ expect(subject).to be_falsey
end
+ end
+ end
- context 'when file is stored' do
- it_behaves_like 'mounted file in object store'
+ describe '.find_with_code_coverage' do
+ subject { Ci::PipelineArtifact.find_with_code_coverage }
+
+ context 'when pipeline artifact has a coverage report' do
+ let!(:coverage_report) { create(:ci_pipeline_artifact) }
+
+ it 'returns a pipeline artifact with a code coverage' do
+ expect(subject.file_type).to eq('code_coverage')
+ end
+ end
+
+ context 'when pipeline artifact does not have a coverage report' do
+ it 'returns nil' do
+ expect(subject).to be_nil
+ end
+ end
+ end
+
+ describe '#present' do
+ subject { coverage_report.present }
+
+ context 'when file_type is code_coverage' do
+ it 'uses code coverage presenter' do
+ expect(subject.present).to be_kind_of(Ci::PipelineArtifacts::CodeCoveragePresenter)
end
end
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index b4e80fa7588..228a1e8f7a2 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -2,12 +2,14 @@
require 'spec_helper'
-RSpec.describe Ci::Pipeline, :mailer do
+RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
include ProjectForksHelper
include StubRequests
+ include Ci::SourcePipelineHelpers
- let(:user) { create(:user) }
- let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:namespace) { create_default(:namespace) }
+ let_it_be(:project) { create_default(:project, :repository) }
let(:pipeline) do
create(:ci_empty_pipeline, status: :created, project: project)
@@ -220,6 +222,26 @@ RSpec.describe Ci::Pipeline, :mailer do
end
end
+ describe '.ci_sources' do
+ subject { described_class.ci_sources }
+
+ let!(:push_pipeline) { create(:ci_pipeline, source: :push) }
+ let!(:web_pipeline) { create(:ci_pipeline, source: :web) }
+ let!(:api_pipeline) { create(:ci_pipeline, source: :api) }
+ let!(:webide_pipeline) { create(:ci_pipeline, source: :webide) }
+ let!(:child_pipeline) { create(:ci_pipeline, source: :parent_pipeline) }
+
+ it 'contains pipelines having CI only sources' do
+ expect(subject).to contain_exactly(push_pipeline, web_pipeline, api_pipeline)
+ end
+
+ it 'filters on expected sources' do
+ expect(::Enums::Ci::Pipeline.ci_sources.keys).to contain_exactly(
+ *%i[unknown push web trigger schedule api external pipeline chat
+ merge_request_event external_pull_request_event])
+ end
+ end
+
describe '.outside_pipeline_family' do
subject(:outside_pipeline_family) { described_class.outside_pipeline_family(upstream_pipeline) }
@@ -714,6 +736,7 @@ RSpec.describe Ci::Pipeline, :mailer do
CI_COMMIT_TITLE
CI_COMMIT_DESCRIPTION
CI_COMMIT_REF_PROTECTED
+ CI_COMMIT_TIMESTAMP
CI_BUILD_REF
CI_BUILD_BEFORE_SHA
CI_BUILD_REF_NAME
@@ -879,7 +902,7 @@ RSpec.describe Ci::Pipeline, :mailer do
context 'when there is auto_canceled_by' do
before do
- pipeline.update(auto_canceled_by: create(:ci_empty_pipeline))
+ pipeline.update!(auto_canceled_by: create(:ci_empty_pipeline))
end
it 'is auto canceled' do
@@ -1237,14 +1260,42 @@ RSpec.describe Ci::Pipeline, :mailer do
end
describe 'pipeline caching' do
- before do
- pipeline.config_source = 'repository_source'
+ context 'if pipeline is cacheable' do
+ before do
+ pipeline.source = 'push'
+ end
+
+ it 'performs ExpirePipelinesCacheWorker' do
+ expect(ExpirePipelineCacheWorker).to receive(:perform_async).with(pipeline.id)
+
+ pipeline.cancel
+ end
end
- it 'performs ExpirePipelinesCacheWorker' do
- expect(ExpirePipelineCacheWorker).to receive(:perform_async).with(pipeline.id)
+ context 'if pipeline is not cacheable' do
+ before do
+ pipeline.source = 'webide'
+ end
- pipeline.cancel
+ it 'deos not perform ExpirePipelinesCacheWorker' do
+ expect(ExpirePipelineCacheWorker).not_to receive(:perform_async)
+
+ pipeline.cancel
+ end
+ end
+ end
+
+ describe '#dangling?' do
+ it 'returns true if pipeline comes from any dangling sources' do
+ pipeline.source = Enums::Ci::Pipeline.dangling_sources.each_key.first
+
+ expect(pipeline).to be_dangling
+ end
+
+ it 'returns true if pipeline comes from any CI sources' do
+ pipeline.source = Enums::Ci::Pipeline.ci_sources.each_key.first
+
+ expect(pipeline).not_to be_dangling
end
end
@@ -1309,34 +1360,126 @@ RSpec.describe Ci::Pipeline, :mailer do
end
end
- context 'when pipeline is bridge triggered' do
- before do
- pipeline.source_bridge = create(:ci_bridge)
- end
+ def auto_devops_pipelines_completed_total(status)
+ Gitlab::Metrics.counter(:auto_devops_pipelines_completed_total, 'Number of completed auto devops pipelines').get(status: status)
+ end
+ end
+
+ describe 'bridge triggered pipeline' do
+ shared_examples 'upstream downstream pipeline' do
+ let!(:source_pipeline) { create(:ci_sources_pipeline, pipeline: downstream_pipeline, source_job: bridge) }
+ let!(:job) { downstream_pipeline.builds.first }
context 'when source bridge is dependent on pipeline status' do
- before do
- allow(pipeline.source_bridge).to receive(:dependent?).and_return(true)
- end
+ let!(:bridge) { create(:ci_bridge, :strategy_depend, pipeline: upstream_pipeline) }
it 'schedules the pipeline bridge worker' do
- expect(::Ci::PipelineBridgeStatusWorker).to receive(:perform_async)
+ expect(::Ci::PipelineBridgeStatusWorker).to receive(:perform_async).with(downstream_pipeline.id)
+
+ downstream_pipeline.succeed!
+ end
+
+ context 'when the downstream pipeline first fails then retries and succeeds' do
+ it 'makes the upstream pipeline successful' do
+ Sidekiq::Testing.inline! { job.drop! }
+
+ expect(downstream_pipeline.reload).to be_failed
+ expect(upstream_pipeline.reload).to be_failed
+
+ Sidekiq::Testing.inline! do
+ new_job = Ci::Build.retry(job, project.users.first)
+
+ expect(downstream_pipeline.reload).to be_running
+ expect(upstream_pipeline.reload).to be_running
+
+ new_job.success!
+ end
+
+ expect(downstream_pipeline.reload).to be_success
+ expect(upstream_pipeline.reload).to be_success
+ end
+ end
+
+ context 'when the downstream pipeline first succeeds then retries and fails' do
+ it 'makes the upstream pipeline failed' do
+ Sidekiq::Testing.inline! { job.success! }
+
+ expect(downstream_pipeline.reload).to be_success
+ expect(upstream_pipeline.reload).to be_success
+
+ Sidekiq::Testing.inline! do
+ new_job = Ci::Build.retry(job, project.users.first)
+
+ expect(downstream_pipeline.reload).to be_running
+ expect(upstream_pipeline.reload).to be_running
+
+ new_job.drop!
+ end
+
+ expect(downstream_pipeline.reload).to be_failed
+ expect(upstream_pipeline.reload).to be_failed
+ end
+ end
+
+ context 'when the upstream pipeline has another dependent upstream pipeline' do
+ let!(:upstream_of_upstream_pipeline) { create(:ci_pipeline) }
+
+ before do
+ upstream_bridge = create(:ci_bridge, :strategy_depend, pipeline: upstream_of_upstream_pipeline)
+ create(:ci_sources_pipeline, pipeline: upstream_pipeline,
+ source_job: upstream_bridge)
+ end
+
+ context 'when the downstream pipeline first fails then retries and succeeds' do
+ it 'makes upstream pipelines successful' do
+ Sidekiq::Testing.inline! { job.drop! }
+
+ expect(downstream_pipeline.reload).to be_failed
+ expect(upstream_pipeline.reload).to be_failed
+ expect(upstream_of_upstream_pipeline.reload).to be_failed
- pipeline.succeed!
+ Sidekiq::Testing.inline! do
+ new_job = Ci::Build.retry(job, project.users.first)
+
+ expect(downstream_pipeline.reload).to be_running
+ expect(upstream_pipeline.reload).to be_running
+ expect(upstream_of_upstream_pipeline.reload).to be_running
+
+ new_job.success!
+ end
+
+ expect(downstream_pipeline.reload).to be_success
+ expect(upstream_pipeline.reload).to be_success
+ expect(upstream_of_upstream_pipeline.reload).to be_success
+ end
+ end
end
end
context 'when source bridge is not dependent on pipeline status' do
+ let!(:bridge) { create(:ci_bridge, pipeline: upstream_pipeline) }
+
it 'does not schedule the pipeline bridge worker' do
expect(::Ci::PipelineBridgeStatusWorker).not_to receive(:perform_async)
- pipeline.succeed!
+ downstream_pipeline.succeed!
end
end
end
- def auto_devops_pipelines_completed_total(status)
- Gitlab::Metrics.counter(:auto_devops_pipelines_completed_total, 'Number of completed auto devops pipelines').get(status: status)
+ context 'multi-project pipelines' do
+ let!(:downstream_project) { create(:project, :repository) }
+ let!(:upstream_pipeline) { create(:ci_pipeline, project: project) }
+ let!(:downstream_pipeline) { create(:ci_pipeline, :with_job, project: downstream_project) }
+
+ it_behaves_like 'upstream downstream pipeline'
+ end
+
+ context 'parent-child pipelines' do
+ let!(:upstream_pipeline) { create(:ci_pipeline, project: project) }
+ let!(:downstream_pipeline) { create(:ci_pipeline, :with_job, project: project) }
+
+ it_behaves_like 'upstream downstream pipeline'
end
end
@@ -1436,8 +1579,6 @@ RSpec.describe Ci::Pipeline, :mailer do
context 'when repository exists' do
using RSpec::Parameterized::TableSyntax
- let(:project) { create(:project, :repository) }
-
where(:tag, :ref, :result) do
false | 'master' | true
false | 'non-existent-branch' | false
@@ -1457,6 +1598,7 @@ RSpec.describe Ci::Pipeline, :mailer do
end
context 'when repository does not exist' do
+ let(:project) { create(:project) }
let(:pipeline) do
create(:ci_empty_pipeline, project: project, ref: 'master')
end
@@ -1468,8 +1610,6 @@ RSpec.describe Ci::Pipeline, :mailer do
end
context 'with non-empty project' do
- let(:project) { create(:project, :repository) }
-
let(:pipeline) do
create(:ci_pipeline,
project: project,
@@ -1524,7 +1664,7 @@ RSpec.describe Ci::Pipeline, :mailer do
it 'looks up a commit for a tag' do
expect(project.repository.branch_names).not_to include 'v1.0.0'
- pipeline.update(sha: project.commit('v1.0.0').sha, ref: 'v1.0.0', tag: true)
+ pipeline.update!(sha: project.commit('v1.0.0').sha, ref: 'v1.0.0', tag: true)
expect(pipeline).to be_tag
expect(pipeline).to be_latest
@@ -1533,7 +1673,7 @@ RSpec.describe Ci::Pipeline, :mailer do
context 'with not latest sha' do
before do
- pipeline.update(sha: project.commit("#{project.default_branch}~1").sha)
+ pipeline.update!(sha: project.commit("#{project.default_branch}~1").sha)
end
it 'returns false' do
@@ -1561,7 +1701,7 @@ RSpec.describe Ci::Pipeline, :mailer do
let!(:manual2) { create(:ci_build, :manual, pipeline: pipeline, name: 'deploy') }
before do
- manual.update(retried: true)
+ manual.update!(retried: true)
end
it 'returns latest one' do
@@ -1596,10 +1736,8 @@ RSpec.describe Ci::Pipeline, :mailer do
describe '#modified_paths' do
context 'when old and new revisions are set' do
- let(:project) { create(:project, :repository) }
-
before do
- pipeline.update(before_sha: '1234abcd', sha: '2345bcde')
+ pipeline.update!(before_sha: '1234abcd', sha: '2345bcde')
end
it 'fetches stats for changes between commits' do
@@ -1866,8 +2004,6 @@ RSpec.describe Ci::Pipeline, :mailer do
end
describe '.latest_pipeline_per_commit' do
- let(:project) { create(:project) }
-
let!(:commit_123_ref_master) do
create(
:ci_empty_pipeline,
@@ -1962,15 +2098,14 @@ RSpec.describe Ci::Pipeline, :mailer do
end
describe '.last_finished_for_ref_id' do
- let(:project) { create(:project, :repository) }
let(:branch) { project.default_branch }
let(:ref) { project.ci_refs.take }
- let(:config_source) { Ci::PipelineEnums.config_sources[:parameter_source] }
+ let(:dangling_source) { Enums::Ci::Pipeline.sources[:ondemand_dast_scan] }
let!(:pipeline1) { create(:ci_pipeline, :success, project: project, ref: branch) }
let!(:pipeline2) { create(:ci_pipeline, :success, project: project, ref: branch) }
let!(:pipeline3) { create(:ci_pipeline, :failed, project: project, ref: branch) }
let!(:pipeline4) { create(:ci_pipeline, :success, project: project, ref: branch) }
- let!(:pipeline5) { create(:ci_pipeline, :success, project: project, ref: branch, config_source: config_source) }
+ let!(:pipeline5) { create(:ci_pipeline, :success, project: project, ref: branch, source: dangling_source) }
it 'returns the expected pipeline' do
result = described_class.last_finished_for_ref_id(ref.id)
@@ -2452,7 +2587,6 @@ RSpec.describe Ci::Pipeline, :mailer do
end
describe "#merge_requests_as_head_pipeline" do
- let(:project) { create(:project) }
let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: 'master', sha: 'a288a022a53a5a944fae87bcec6efc87b7061808') }
it "returns merge requests whose `diff_head_sha` matches the pipeline's SHA" do
@@ -2575,11 +2709,11 @@ RSpec.describe Ci::Pipeline, :mailer do
end
describe '#same_family_pipeline_ids' do
- subject(:same_family_pipeline_ids) { pipeline.same_family_pipeline_ids }
+ subject { pipeline.same_family_pipeline_ids.map(&:id) }
context 'when pipeline is not child nor parent' do
it 'returns just the pipeline id' do
- expect(same_family_pipeline_ids).to contain_exactly(pipeline.id)
+ expect(subject).to contain_exactly(pipeline.id)
end
end
@@ -2588,21 +2722,12 @@ RSpec.describe Ci::Pipeline, :mailer do
let(:sibling) { create(:ci_pipeline, project: pipeline.project) }
before do
- create(:ci_sources_pipeline,
- source_job: create(:ci_build, pipeline: parent),
- source_project: parent.project,
- pipeline: pipeline,
- project: pipeline.project)
-
- create(:ci_sources_pipeline,
- source_job: create(:ci_build, pipeline: parent),
- source_project: parent.project,
- pipeline: sibling,
- project: sibling.project)
+ create_source_pipeline(parent, pipeline)
+ create_source_pipeline(parent, sibling)
end
it 'returns parent sibling and self ids' do
- expect(same_family_pipeline_ids).to contain_exactly(parent.id, pipeline.id, sibling.id)
+ expect(subject).to contain_exactly(parent.id, pipeline.id, sibling.id)
end
end
@@ -2610,15 +2735,43 @@ RSpec.describe Ci::Pipeline, :mailer do
let(:child) { create(:ci_pipeline, project: pipeline.project) }
before do
- create(:ci_sources_pipeline,
- source_job: create(:ci_build, pipeline: pipeline),
- source_project: pipeline.project,
- pipeline: child,
- project: child.project)
+ create_source_pipeline(pipeline, child)
end
it 'returns self and child ids' do
- expect(same_family_pipeline_ids).to contain_exactly(pipeline.id, child.id)
+ expect(subject).to contain_exactly(pipeline.id, child.id)
+ end
+ end
+
+ context 'when pipeline is a child of a child pipeline' do
+ let(:ancestor) { create(:ci_pipeline, project: pipeline.project) }
+ let(:parent) { create(:ci_pipeline, project: pipeline.project) }
+ let(:cousin_parent) { create(:ci_pipeline, project: pipeline.project) }
+ let(:cousin) { create(:ci_pipeline, project: pipeline.project) }
+
+ before do
+ create_source_pipeline(ancestor, parent)
+ create_source_pipeline(ancestor, cousin_parent)
+ create_source_pipeline(parent, pipeline)
+ create_source_pipeline(cousin_parent, cousin)
+ end
+
+ it 'returns all family ids' do
+ expect(subject).to contain_exactly(
+ ancestor.id, parent.id, cousin_parent.id, cousin.id, pipeline.id
+ )
+ end
+ end
+
+ context 'when pipeline is a triggered pipeline' do
+ let(:upstream) { create(:ci_pipeline, project: create(:project)) }
+
+ before do
+ create_source_pipeline(upstream, pipeline)
+ end
+
+ it 'returns self id' do
+ expect(subject).to contain_exactly(pipeline.id)
end
end
end
@@ -2685,7 +2838,8 @@ RSpec.describe Ci::Pipeline, :mailer do
end
describe 'notifications when pipeline success or failed' do
- let(:project) { create(:project, :repository) }
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:project, :repository, namespace: namespace) }
let(:pipeline) do
create(:ci_pipeline,
@@ -2698,7 +2852,7 @@ RSpec.describe Ci::Pipeline, :mailer do
project.add_developer(pipeline.user)
pipeline.user.global_notification_setting
- .update(level: 'custom', failed_pipeline: true, success_pipeline: true)
+ .update!(level: 'custom', failed_pipeline: true, success_pipeline: true)
perform_enqueued_jobs do
pipeline.enqueue
@@ -2948,6 +3102,54 @@ RSpec.describe Ci::Pipeline, :mailer do
end
end
+ describe '#has_coverage_reports?' do
+ subject { pipeline.has_coverage_reports? }
+
+ context 'when pipeline has a code coverage artifact' do
+ let(:pipeline) { create(:ci_pipeline, :with_coverage_report_artifact, :running, project: project) }
+
+ it { expect(subject).to be_truthy }
+ end
+
+ context 'when pipeline does not have a code coverage artifact' do
+ let(:pipeline) { create(:ci_pipeline, :success, project: project) }
+
+ it { expect(subject).to be_falsey }
+ end
+ end
+
+ describe '#can_generate_coverage_reports?' do
+ subject { pipeline.can_generate_coverage_reports? }
+
+ context 'when pipeline has builds with coverage reports' do
+ before do
+ create(:ci_build, :coverage_reports, pipeline: pipeline, project: project)
+ end
+
+ context 'when pipeline status is running' do
+ let(:pipeline) { create(:ci_pipeline, :running, project: project) }
+
+ it { expect(subject).to be_falsey }
+ end
+
+ context 'when pipeline status is success' do
+ let(:pipeline) { create(:ci_pipeline, :success, project: project) }
+
+ it { expect(subject).to be_truthy }
+ end
+ end
+
+ context 'when pipeline does not have builds with coverage reports' do
+ before do
+ create(:ci_build, :artifacts, pipeline: pipeline, project: project)
+ end
+
+ let(:pipeline) { create(:ci_pipeline, :success, project: project) }
+
+ it { expect(subject).to be_falsey }
+ end
+ end
+
describe '#test_report_summary' do
subject { pipeline.test_report_summary }
@@ -3228,7 +3430,8 @@ RSpec.describe Ci::Pipeline, :mailer do
end
describe '#parent_pipeline' do
- let(:project) { create(:project) }
+ let_it_be(:project) { create(:project) }
+
let(:pipeline) { create(:ci_pipeline, project: project) }
context 'when pipeline is triggered by a pipeline from the same project' do
@@ -3283,7 +3486,7 @@ RSpec.describe Ci::Pipeline, :mailer do
end
describe '#child_pipelines' do
- let(:project) { create(:project) }
+ let_it_be(:project) { create(:project) }
let(:pipeline) { create(:ci_pipeline, project: project) }
context 'when pipeline triggered other pipelines on same project' do
@@ -3413,4 +3616,152 @@ RSpec.describe Ci::Pipeline, :mailer do
it { is_expected.to eq(Gitlab::Git::TAG_REF_PREFIX + pipeline.source_ref.to_s) }
end
end
+
+ describe "#builds_with_coverage" do
+ it 'returns builds with coverage only' do
+ rspec = create(:ci_build, name: 'rspec', coverage: 97.1, pipeline: pipeline)
+ jest = create(:ci_build, name: 'jest', coverage: 94.1, pipeline: pipeline)
+ karma = create(:ci_build, name: 'karma', coverage: nil, pipeline: pipeline)
+
+ builds = pipeline.builds_with_coverage
+
+ expect(builds).to include(rspec, jest)
+ expect(builds).not_to include(karma)
+ end
+ end
+
+ describe '#base_and_ancestors' do
+ let(:same_project) { false }
+
+ subject { pipeline.base_and_ancestors(same_project: same_project) }
+
+ context 'when pipeline is not child nor parent' do
+ it 'returns just the pipeline itself' do
+ expect(subject).to contain_exactly(pipeline)
+ end
+ end
+
+ context 'when pipeline is child' do
+ let(:parent) { create(:ci_pipeline, project: pipeline.project) }
+ let(:sibling) { create(:ci_pipeline, project: pipeline.project) }
+
+ before do
+ create_source_pipeline(parent, pipeline)
+ create_source_pipeline(parent, sibling)
+ end
+
+ it 'returns parent and self' do
+ expect(subject).to contain_exactly(parent, pipeline)
+ end
+ end
+
+ context 'when pipeline is parent' do
+ let(:child) { create(:ci_pipeline, project: pipeline.project) }
+
+ before do
+ create_source_pipeline(pipeline, child)
+ end
+
+ it 'returns self' do
+ expect(subject).to contain_exactly(pipeline)
+ end
+ end
+
+ context 'when pipeline is a child of a child pipeline' do
+ let(:ancestor) { create(:ci_pipeline, project: pipeline.project) }
+ let(:parent) { create(:ci_pipeline, project: pipeline.project) }
+
+ before do
+ create_source_pipeline(ancestor, parent)
+ create_source_pipeline(parent, pipeline)
+ end
+
+ it 'returns self, parent and ancestor' do
+ expect(subject).to contain_exactly(ancestor, parent, pipeline)
+ end
+ end
+
+ context 'when pipeline is a triggered pipeline' do
+ let(:upstream) { create(:ci_pipeline, project: create(:project)) }
+
+ before do
+ create_source_pipeline(upstream, pipeline)
+ end
+
+ context 'same_project: false' do
+ it 'returns upstream and self' do
+ expect(subject).to contain_exactly(pipeline, upstream)
+ end
+ end
+
+ context 'same_project: true' do
+ let(:same_project) { true }
+
+ it 'returns self' do
+ expect(subject).to contain_exactly(pipeline)
+ end
+ end
+ end
+ end
+
+ describe 'reset_ancestor_bridges!' do
+ context 'when the pipeline is a child pipeline and the bridge is depended' do
+ let!(:parent_pipeline) { create(:ci_pipeline, project: project) }
+ let!(:bridge) { create_bridge(parent_pipeline, pipeline, true) }
+
+ it 'marks source bridge as pending' do
+ pipeline.reset_ancestor_bridges!
+
+ expect(bridge.reload).to be_pending
+ 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!
+
+ expect(bridge.reload).to be_pending
+ expect(upstream_bridge.reload).to be_pending
+ end
+ end
+ end
+
+ context 'when the pipeline is a child pipeline and the bridge is not depended' do
+ let!(:parent_pipeline) { create(:ci_pipeline, project: project) }
+ let!(:bridge) { create_bridge(parent_pipeline, pipeline, false) }
+
+ it 'does not touch source bridge' do
+ pipeline.reset_ancestor_bridges!
+
+ expect(bridge.reload).to be_success
+ 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 'does not touch any source bridge' do
+ pipeline.reset_ancestor_bridges!
+
+ expect(bridge.reload).to be_success
+ expect(upstream_bridge.reload).to be_success
+ end
+ end
+ end
+
+ private
+
+ def create_bridge(upstream, downstream, depend = false)
+ options = depend ? { trigger: { strategy: 'depend' } } : {}
+
+ bridge = create(:ci_bridge, pipeline: upstream, status: 'success', options: options)
+ create(:ci_sources_pipeline, pipeline: downstream, source_job: bridge)
+
+ bridge
+ end
+ end
end
diff --git a/spec/models/ci/ref_spec.rb b/spec/models/ci/ref_spec.rb
index 8bce3c10d8c..cb62646532c 100644
--- a/spec/models/ci/ref_spec.rb
+++ b/spec/models/ci/ref_spec.rb
@@ -16,51 +16,33 @@ RSpec.describe Ci::Ref do
stub_const('Ci::PipelineSuccessUnlockArtifactsWorker', unlock_artifacts_worker_spy)
end
- context 'when keep latest artifact feature is enabled' do
- before do
- stub_feature_flags(keep_latest_artifacts_for_ref: true)
- end
-
- where(:initial_state, :action, :count) do
- :unknown | :succeed! | 1
- :unknown | :do_fail! | 0
- :success | :succeed! | 1
- :success | :do_fail! | 0
- :failed | :succeed! | 1
- :failed | :do_fail! | 0
- :fixed | :succeed! | 1
- :fixed | :do_fail! | 0
- :broken | :succeed! | 1
- :broken | :do_fail! | 0
- :still_failing | :succeed | 1
- :still_failing | :do_fail | 0
- end
-
- with_them do
- context "when transitioning states" do
- before do
- status_value = Ci::Ref.state_machines[:status].states[initial_state].value
- ci_ref.update!(status: status_value)
- end
-
- it 'calls unlock artifacts service' do
- ci_ref.send(action)
-
- expect(unlock_artifacts_worker_spy).to have_received(:perform_async).exactly(count).times
- end
- end
- end
+ where(:initial_state, :action, :count) do
+ :unknown | :succeed! | 1
+ :unknown | :do_fail! | 0
+ :success | :succeed! | 1
+ :success | :do_fail! | 0
+ :failed | :succeed! | 1
+ :failed | :do_fail! | 0
+ :fixed | :succeed! | 1
+ :fixed | :do_fail! | 0
+ :broken | :succeed! | 1
+ :broken | :do_fail! | 0
+ :still_failing | :succeed | 1
+ :still_failing | :do_fail | 0
end
- context 'when keep latest artifact feature is not enabled' do
- before do
- stub_feature_flags(keep_latest_artifacts_for_ref: false)
- end
+ with_them do
+ context "when transitioning states" do
+ before do
+ status_value = Ci::Ref.state_machines[:status].states[initial_state].value
+ ci_ref.update!(status: status_value)
+ end
- it 'does not call unlock artifacts service' do
- ci_ref.succeed!
+ it 'calls unlock artifacts service' do
+ ci_ref.send(action)
- expect(unlock_artifacts_worker_spy).not_to have_received(:perform_async)
+ expect(unlock_artifacts_worker_spy).to have_received(:perform_async).exactly(count).times
+ end
end
end
end
@@ -125,8 +107,8 @@ RSpec.describe Ci::Ref do
describe '#last_finished_pipeline_id' do
let(:pipeline_status) { :running }
- let(:config_source) { Ci::PipelineEnums.config_sources[:repository_source] }
- let(:pipeline) { create(:ci_pipeline, pipeline_status, config_source: config_source) }
+ let(:pipeline_source) { Enums::Ci::Pipeline.sources[:push] }
+ let(:pipeline) { create(:ci_pipeline, pipeline_status, source: pipeline_source) }
let(:ci_ref) { pipeline.ci_ref }
context 'when there are no finished pipelines' do
@@ -142,8 +124,8 @@ RSpec.describe Ci::Ref do
expect(ci_ref.last_finished_pipeline_id).to eq(pipeline.id)
end
- context 'when the pipeline is not a ci_source' do
- let(:config_source) { Ci::PipelineEnums.config_sources[:parameter_source] }
+ context 'when the pipeline a dangling pipeline' do
+ let(:pipeline_source) { Enums::Ci::Pipeline.sources[:ondemand_dast_scan] }
it 'returns nil' do
expect(ci_ref.last_finished_pipeline_id).to be_nil
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 8247ebf1144..3e5d068d780 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -570,7 +570,7 @@ RSpec.describe Ci::Runner do
let!(:last_update) { runner.ensure_runner_queue_value }
before do
- Ci::UpdateRunnerService.new(runner).update(description: 'new runner')
+ Ci::UpdateRunnerService.new(runner).update(description: 'new runner') # rubocop: disable Rails/SaveBang
end
it 'sets a new last_update value' do
@@ -660,7 +660,7 @@ RSpec.describe Ci::Runner do
before do
runner.tick_runner_queue
- runner.destroy
+ runner.destroy!
end
it 'cleans up the queue' do
@@ -878,7 +878,7 @@ RSpec.describe Ci::Runner do
it 'can be destroyed' do
subject
- expect { subject.destroy }.to change { described_class.count }.by(-1)
+ expect { subject.destroy! }.to change { described_class.count }.by(-1)
end
end