# frozen_string_literal: true require 'spec_helper' RSpec.describe Ci::BuildDependencies do let_it_be(:user) { create(:user) } let_it_be(:project, reload: true) { create(:project, :repository) } let_it_be(:pipeline, reload: true) do create(:ci_pipeline, project: project, sha: project.commit.id, ref: project.default_branch, status: 'success') end let!(:build) { create(:ci_build, pipeline: pipeline, name: 'build', stage_idx: 0, stage: 'build') } let!(:rspec_test) { create(:ci_build, pipeline: pipeline, name: 'rspec', stage_idx: 1, stage: 'test') } 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') } describe '#local' do subject { described_class.new(job).local } describe 'jobs from previous stages' do context 'when job is in the first stage' do let(:job) { build } it { is_expected.to be_empty } end context 'when job is in the second stage' do let(:job) { rspec_test } it 'contains all jobs from the first stage' do is_expected.to contain_exactly(build) end end context 'when job is in the last stage' do let(:job) { staging } it 'contains all jobs from all previous stages' do is_expected.to contain_exactly(build, rspec_test, rubocop_test) end context 'when a job is retried' do before do project.add_developer(user) end 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) end end end end describe 'jobs from specified dependencies' do let(:dependencies) { } let(:needs) { } let!(:job) do scheduling_type = needs.present? ? :dag : :stage create(:ci_build, pipeline: pipeline, name: 'final', scheduling_type: scheduling_type, stage_idx: 3, stage: 'deploy', options: { dependencies: dependencies } ) end before do needs.to_a.each do |need| create(:ci_build_need, build: job, **need) end end context 'when dependencies are defined' do let(:dependencies) { %w(rspec staging) } it { is_expected.to contain_exactly(rspec_test, staging) } end context 'when needs are defined' do let(:needs) do [ { name: 'build', artifacts: true }, { name: 'rspec', artifacts: true }, { name: 'staging', artifacts: true } ] end it { is_expected.to contain_exactly(build, rspec_test, staging) } end context 'when need artifacts are defined' do let(:needs) do [ { name: 'build', artifacts: true }, { name: 'rspec', artifacts: false }, { name: 'staging', artifacts: true } ] end it { is_expected.to contain_exactly(build, staging) } end context 'when needs and dependencies are defined' do let(:dependencies) { %w(rspec staging) } let(:needs) do [ { name: 'build', artifacts: true }, { name: 'rspec', artifacts: true }, { name: 'staging', artifacts: true } ] end it { is_expected.to contain_exactly(rspec_test, staging) } end context 'when needs and dependencies contradict' do let(:dependencies) { %w(rspec staging) } let(:needs) do [ { name: 'build', artifacts: true }, { name: 'rspec', artifacts: false }, { name: 'staging', artifacts: true } ] end it 'returns only the intersection' do is_expected.to contain_exactly(staging) end end context 'when nor dependencies or needs are defined' do it 'returns the jobs from previous stages' do is_expected.to contain_exactly(build, rspec_test, rubocop_test, staging) end end end end describe '#all' do let!(:job) do create(:ci_build, pipeline: pipeline, name: 'deploy', stage_idx: 3, stage: 'deploy') end let(:dependencies) { described_class.new(job) } subject { dependencies.all } it 'returns the union of all local dependencies and any cross pipeline dependencies' do expect(dependencies).to receive(:local).and_return([1, 2, 3]) expect(dependencies).to receive(:cross_pipeline).and_return([3, 4]) expect(subject).to contain_exactly(1, 2, 3, 4) end end end