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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models/ci/build_dependencies_spec.rb')
-rw-r--r--spec/models/ci/build_dependencies_spec.rb202
1 files changed, 200 insertions, 2 deletions
diff --git a/spec/models/ci/build_dependencies_spec.rb b/spec/models/ci/build_dependencies_spec.rb
index 4fa1b3eb5a5..c5f56dbe5bc 100644
--- a/spec/models/ci/build_dependencies_spec.rb
+++ b/spec/models/ci/build_dependencies_spec.rb
@@ -146,6 +146,204 @@ RSpec.describe Ci::BuildDependencies do
end
end
+ describe '#cross_pipeline' do
+ let!(:job) do
+ create(:ci_build,
+ pipeline: pipeline,
+ name: 'build_with_pipeline_dependency',
+ options: { cross_dependencies: dependencies })
+ end
+
+ subject { described_class.new(job) }
+
+ let(:cross_pipeline_deps) { subject.cross_pipeline }
+
+ context 'when dependency specifications are valid' do
+ context 'when pipeline exists in the hierarchy' do
+ let!(:pipeline) { create(:ci_pipeline, child_of: parent_pipeline) }
+ let!(:parent_pipeline) { create(:ci_pipeline, project: project) }
+
+ context 'when job exists' do
+ let(:dependencies) do
+ [{ pipeline: parent_pipeline.id.to_s, job: upstream_job.name, artifacts: true }]
+ end
+
+ let!(:upstream_job) { create(:ci_build, :success, pipeline: parent_pipeline) }
+
+ it { expect(cross_pipeline_deps).to contain_exactly(upstream_job) }
+ it { is_expected.to be_valid }
+
+ context 'when pipeline and job are specified via variables' do
+ let(:dependencies) do
+ [{ pipeline: '$parent_pipeline_ID', job: '$UPSTREAM_JOB', artifacts: true }]
+ end
+
+ before do
+ job.yaml_variables.push(key: 'parent_pipeline_ID', value: parent_pipeline.id.to_s, public: true)
+ job.yaml_variables.push(key: 'UPSTREAM_JOB', value: upstream_job.name, public: true)
+ job.save!
+ end
+
+ it { expect(cross_pipeline_deps).to contain_exactly(upstream_job) }
+ it { is_expected.to be_valid }
+ end
+
+ context 'when feature flag `ci_cross_pipeline_artifacts_download` is disabled' do
+ before do
+ stub_feature_flags(ci_cross_pipeline_artifacts_download: false)
+ end
+
+ it { expect(cross_pipeline_deps).to be_empty }
+ it { is_expected.to be_valid }
+ end
+ end
+
+ context 'when same job names exist in other pipelines in the hierarchy' do
+ let(:cross_pipeline_limit) do
+ ::Gitlab::Ci::Config::Entry::Needs::NEEDS_CROSS_PIPELINE_DEPENDENCIES_LIMIT
+ end
+
+ let(:sibling_pipeline) { create(:ci_pipeline, child_of: parent_pipeline) }
+
+ before do
+ cross_pipeline_limit.times do |index|
+ create(:ci_build, :success,
+ pipeline: parent_pipeline, name: "dependency-#{index}",
+ stage_idx: 1, stage: 'build', user: user
+ )
+
+ create(:ci_build, :success,
+ pipeline: sibling_pipeline, name: "dependency-#{index}",
+ stage_idx: 1, stage: 'build', user: user
+ )
+ end
+ end
+
+ let(:dependencies) do
+ [
+ { pipeline: parent_pipeline.id.to_s, job: 'dependency-0', artifacts: true },
+ { pipeline: parent_pipeline.id.to_s, job: 'dependency-1', artifacts: true },
+ { pipeline: parent_pipeline.id.to_s, job: 'dependency-2', artifacts: true },
+ { pipeline: sibling_pipeline.id.to_s, job: 'dependency-3', artifacts: true },
+ { pipeline: sibling_pipeline.id.to_s, job: 'dependency-4', artifacts: true },
+ { pipeline: sibling_pipeline.id.to_s, job: 'dependency-5', artifacts: true }
+ ]
+ end
+
+ it 'returns a limited number of dependencies with the right match' do
+ expect(job.options[:cross_dependencies].size).to eq(cross_pipeline_limit.next)
+ expect(cross_pipeline_deps.size).to eq(cross_pipeline_limit)
+ expect(cross_pipeline_deps.map { |dep| [dep.pipeline_id, dep.name] }).to contain_exactly(
+ [parent_pipeline.id, 'dependency-0'],
+ [parent_pipeline.id, 'dependency-1'],
+ [parent_pipeline.id, 'dependency-2'],
+ [sibling_pipeline.id, 'dependency-3'],
+ [sibling_pipeline.id, 'dependency-4'])
+ end
+ end
+
+ context 'when job does not exist' do
+ let(:dependencies) do
+ [{ pipeline: parent_pipeline.id.to_s, job: 'non-existent', artifacts: true }]
+ end
+
+ it { expect(cross_pipeline_deps).to be_empty }
+ it { is_expected.not_to be_valid }
+ end
+ end
+
+ context 'when pipeline does not exist' do
+ let(:dependencies) do
+ [{ pipeline: '123', job: 'non-existent', artifacts: true }]
+ end
+
+ it { expect(cross_pipeline_deps).to be_empty }
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'when jobs exist in different pipelines in the hierarchy' do
+ let!(:pipeline) { create(:ci_pipeline, child_of: parent_pipeline) }
+ let!(:parent_pipeline) { create(:ci_pipeline, project: project) }
+ let!(:parent_job) { create(:ci_build, :success, name: 'parent_job', pipeline: parent_pipeline) }
+
+ let!(:sibling_pipeline) { create(:ci_pipeline, child_of: parent_pipeline) }
+ let!(:sibling_job) { create(:ci_build, :success, name: 'sibling_job', pipeline: sibling_pipeline) }
+
+ context 'when pipeline and jobs dependencies are mismatched' do
+ let(:dependencies) do
+ [
+ { pipeline: parent_pipeline.id.to_s, job: sibling_job.name, artifacts: true },
+ { pipeline: sibling_pipeline.id.to_s, job: parent_job.name, artifacts: true }
+ ]
+ end
+
+ it { expect(cross_pipeline_deps).to be_empty }
+ it { is_expected.not_to be_valid }
+
+ context 'when dependencies contain a valid pair' do
+ let(:dependencies) do
+ [
+ { pipeline: parent_pipeline.id.to_s, job: sibling_job.name, artifacts: true },
+ { pipeline: sibling_pipeline.id.to_s, job: parent_job.name, artifacts: true },
+ { pipeline: sibling_pipeline.id.to_s, job: sibling_job.name, artifacts: true }
+ ]
+ end
+
+ it 'filters out the invalid ones' do
+ expect(cross_pipeline_deps).to contain_exactly(sibling_job)
+ end
+
+ it { is_expected.not_to be_valid }
+ end
+ end
+ end
+
+ context 'when job and pipeline exist outside the hierarchy' do
+ let!(:pipeline) { create(:ci_pipeline, project: project) }
+ let!(:another_pipeline) { create(:ci_pipeline, project: project) }
+ let!(:dependency) { create(:ci_build, :success, pipeline: another_pipeline) }
+
+ let(:dependencies) do
+ [{ pipeline: another_pipeline.id.to_s, job: dependency.name, artifacts: true }]
+ end
+
+ it 'ignores jobs outside the pipeline hierarchy' do
+ expect(cross_pipeline_deps).to be_empty
+ end
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'when current pipeline is specified' do
+ let!(:pipeline) { create(:ci_pipeline, project: project) }
+ let!(:dependency) { create(:ci_build, :success, pipeline: pipeline) }
+
+ let(:dependencies) do
+ [{ pipeline: pipeline.id.to_s, job: dependency.name, artifacts: true }]
+ end
+
+ it 'ignores jobs from the current pipeline as simple needs should be used instead' do
+ expect(cross_pipeline_deps).to be_empty
+ end
+
+ it { is_expected.not_to be_valid }
+ end
+ end
+
+ context 'when artifacts:false' do
+ let!(:pipeline) { create(:ci_pipeline, child_of: parent_pipeline) }
+ let!(:parent_pipeline) { create(:ci_pipeline, project: project) }
+ let!(:parent_job) { create(:ci_build, :success, name: 'parent_job', pipeline: parent_pipeline) }
+
+ let(:dependencies) do
+ [{ pipeline: parent_pipeline.id.to_s, job: parent_job.name, artifacts: false }]
+ end
+
+ it { expect(cross_pipeline_deps).to be_empty }
+ it { is_expected.to be_valid } # we simply ignore it
+ end
+ end
+
describe '#all' do
let!(:job) do
create(:ci_build, pipeline: pipeline, name: 'deploy', stage_idx: 3, stage: 'deploy')
@@ -155,9 +353,9 @@ RSpec.describe Ci::BuildDependencies do
subject { dependencies.all }
- it 'returns the union of all local dependencies and any cross pipeline dependencies' do
+ it 'returns the union of all local dependencies and any cross project dependencies' do
expect(dependencies).to receive(:local).and_return([1, 2, 3])
- expect(dependencies).to receive(:cross_pipeline).and_return([3, 4])
+ expect(dependencies).to receive(:cross_project).and_return([3, 4])
expect(subject).to contain_exactly(1, 2, 3, 4)
end