2020-01-01 13:55:28 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require 'spec_helper'
|
|
|
|
|
2023-05-27 22:25:52 +05:30
|
|
|
RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness,
|
|
|
|
feature_category: :pipeline_composition do
|
2020-01-01 13:55:28 +05:30
|
|
|
context 'needs' do
|
2021-01-29 00:20:46 +05:30
|
|
|
let_it_be(:project) { create(:project, :repository) }
|
2022-03-02 08:16:31 +05:30
|
|
|
let_it_be(:user) { project.first_owner }
|
2020-01-01 13:55:28 +05:30
|
|
|
|
|
|
|
let(:ref) { 'refs/heads/master' }
|
|
|
|
let(:source) { :push }
|
|
|
|
let(:service) { described_class.new(project, user, { ref: ref }) }
|
2021-10-27 15:23:28 +05:30
|
|
|
let(:pipeline) { service.execute(source).payload }
|
2020-01-01 13:55:28 +05:30
|
|
|
|
|
|
|
before do
|
|
|
|
stub_ci_pipeline_yaml_file(config)
|
2021-01-29 00:20:46 +05:30
|
|
|
project.add_developer(user)
|
2020-01-01 13:55:28 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'with a valid config' do
|
|
|
|
let(:config) do
|
|
|
|
<<~YAML
|
|
|
|
build_a:
|
|
|
|
stage: build
|
|
|
|
script:
|
|
|
|
- make
|
|
|
|
artifacts:
|
|
|
|
paths:
|
|
|
|
- binaries/
|
|
|
|
build_b:
|
|
|
|
stage: build
|
|
|
|
script:
|
|
|
|
- make
|
|
|
|
artifacts:
|
|
|
|
paths:
|
|
|
|
- other_binaries/
|
|
|
|
build_c:
|
|
|
|
stage: build
|
|
|
|
script:
|
|
|
|
- make
|
|
|
|
build_d:
|
|
|
|
stage: build
|
|
|
|
script:
|
|
|
|
- make
|
|
|
|
parallel: 3
|
|
|
|
|
|
|
|
test_a:
|
|
|
|
stage: test
|
|
|
|
script:
|
|
|
|
- ls
|
|
|
|
needs:
|
|
|
|
- build_a
|
|
|
|
- job: build_b
|
|
|
|
artifacts: true
|
|
|
|
- job: build_c
|
|
|
|
artifacts: false
|
|
|
|
dependencies:
|
|
|
|
- build_a
|
|
|
|
|
|
|
|
test_b:
|
|
|
|
stage: test
|
|
|
|
script:
|
|
|
|
- ls
|
|
|
|
parallel: 2
|
|
|
|
needs:
|
|
|
|
- build_a
|
|
|
|
- job: build_b
|
|
|
|
artifacts: true
|
|
|
|
- job: build_d
|
|
|
|
artifacts: false
|
|
|
|
|
|
|
|
test_c:
|
|
|
|
stage: test
|
|
|
|
script:
|
|
|
|
- ls
|
|
|
|
needs:
|
|
|
|
- build_a
|
|
|
|
- job: build_b
|
|
|
|
- job: build_c
|
|
|
|
artifacts: true
|
|
|
|
YAML
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:test_a_build) { pipeline.builds.find_by!(name: 'test_a') }
|
|
|
|
|
|
|
|
it 'creates a pipeline with builds' do
|
|
|
|
expected_builds = [
|
|
|
|
'build_a', 'build_b', 'build_c', 'build_d 1/3', 'build_d 2/3',
|
|
|
|
'build_d 3/3', 'test_a', 'test_b 1/2', 'test_b 2/2', 'test_c'
|
|
|
|
]
|
|
|
|
|
|
|
|
expect(pipeline).to be_persisted
|
|
|
|
expect(pipeline.builds.pluck(:name)).to contain_exactly(*expected_builds)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'saves needs' do
|
|
|
|
expect(test_a_build.needs.map(&:attributes))
|
|
|
|
.to contain_exactly(
|
|
|
|
a_hash_including('name' => 'build_a', 'artifacts' => true),
|
|
|
|
a_hash_including('name' => 'build_b', 'artifacts' => true),
|
|
|
|
a_hash_including('name' => 'build_c', 'artifacts' => false)
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'saves dependencies' do
|
|
|
|
expect(test_a_build.options)
|
2021-09-30 23:02:18 +05:30
|
|
|
.to match(a_hash_including(dependencies: ['build_a']))
|
2020-01-01 13:55:28 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it 'artifacts default to true' do
|
|
|
|
test_job = pipeline.builds.find_by!(name: 'test_c')
|
|
|
|
|
|
|
|
expect(test_job.needs.map(&:attributes))
|
|
|
|
.to contain_exactly(
|
|
|
|
a_hash_including('name' => 'build_a', 'artifacts' => true),
|
|
|
|
a_hash_including('name' => 'build_b', 'artifacts' => true),
|
|
|
|
a_hash_including('name' => 'build_c', 'artifacts' => true)
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'saves parallel jobs' do
|
|
|
|
['1/2', '2/2'].each do |part|
|
|
|
|
test_job = pipeline.builds.find_by(name: "test_b #{part}")
|
|
|
|
|
|
|
|
expect(test_job.needs.map(&:attributes))
|
|
|
|
.to contain_exactly(
|
|
|
|
a_hash_including('name' => 'build_a', 'artifacts' => true),
|
|
|
|
a_hash_including('name' => 'build_b', 'artifacts' => true),
|
|
|
|
a_hash_including('name' => 'build_d 1/3', 'artifacts' => false),
|
|
|
|
a_hash_including('name' => 'build_d 2/3', 'artifacts' => false),
|
|
|
|
a_hash_including('name' => 'build_d 3/3', 'artifacts' => false)
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
2020-03-13 15:44:24 +05:30
|
|
|
|
|
|
|
it "sets scheduling_type as 'dag'" do
|
|
|
|
expect(test_a_build.scheduling_type).to eq('dag')
|
|
|
|
end
|
2020-01-01 13:55:28 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'with an invalid config' do
|
|
|
|
let(:config) do
|
|
|
|
<<~YAML
|
|
|
|
build_a:
|
|
|
|
stage: build
|
|
|
|
script:
|
|
|
|
- make
|
|
|
|
artifacts:
|
|
|
|
paths:
|
|
|
|
- binaries/
|
|
|
|
|
|
|
|
build_b:
|
|
|
|
stage: build
|
|
|
|
script:
|
|
|
|
- make
|
|
|
|
artifacts:
|
|
|
|
paths:
|
|
|
|
- other_binaries/
|
|
|
|
|
|
|
|
test_a:
|
|
|
|
stage: test
|
|
|
|
script:
|
|
|
|
- ls
|
|
|
|
needs:
|
|
|
|
- build_a
|
|
|
|
- job: build_b
|
|
|
|
artifacts: string
|
|
|
|
YAML
|
|
|
|
end
|
|
|
|
|
|
|
|
it { expect(pipeline).to be_persisted }
|
|
|
|
it { expect(pipeline.builds.any?).to be_falsey }
|
|
|
|
|
|
|
|
it 'assigns an error to the pipeline' do
|
|
|
|
expect(pipeline.yaml_errors)
|
|
|
|
.to eq('jobs:test_a:needs:need artifacts should be a boolean value')
|
|
|
|
end
|
|
|
|
end
|
2020-03-13 15:44:24 +05:30
|
|
|
|
|
|
|
context 'when needs is empty array' do
|
|
|
|
let(:config) do
|
|
|
|
<<~YAML
|
|
|
|
build_a:
|
|
|
|
stage: build
|
|
|
|
script: ls
|
|
|
|
test_a:
|
|
|
|
stage: test
|
|
|
|
script: ls
|
|
|
|
test_b:
|
|
|
|
stage: test
|
|
|
|
script: ls
|
|
|
|
needs: []
|
|
|
|
deploy_a:
|
|
|
|
stage: deploy
|
|
|
|
script: ls
|
|
|
|
needs: [test_a]
|
|
|
|
deploy_b:
|
|
|
|
stage: deploy
|
|
|
|
script: ls
|
|
|
|
when: manual
|
|
|
|
needs: []
|
|
|
|
YAML
|
|
|
|
end
|
|
|
|
|
2021-04-29 21:17:54 +05:30
|
|
|
it 'creates a pipeline with build_a and test_b pending; deploy_b manual', :sidekiq_inline do
|
2020-03-13 15:44:24 +05:30
|
|
|
processables = pipeline.processables
|
|
|
|
|
|
|
|
build_a = processables.find { |processable| processable.name == 'build_a' }
|
|
|
|
test_a = processables.find { |processable| processable.name == 'test_a' }
|
|
|
|
test_b = processables.find { |processable| processable.name == 'test_b' }
|
|
|
|
deploy_a = processables.find { |processable| processable.name == 'deploy_a' }
|
|
|
|
deploy_b = processables.find { |processable| processable.name == 'deploy_b' }
|
|
|
|
|
2021-09-04 01:27:46 +05:30
|
|
|
expect(pipeline).to be_created_successfully
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(build_a.status).to eq('pending')
|
|
|
|
expect(test_a.status).to eq('created')
|
|
|
|
expect(test_b.status).to eq('pending')
|
|
|
|
expect(deploy_a.status).to eq('created')
|
|
|
|
expect(deploy_b.status).to eq('manual')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when needs is empty hash' do
|
|
|
|
let(:config) do
|
|
|
|
<<~YAML
|
|
|
|
regular_job:
|
|
|
|
stage: build
|
|
|
|
script: echo 'hello'
|
|
|
|
invalid_dag_job:
|
|
|
|
stage: test
|
|
|
|
script: ls
|
|
|
|
needs: {}
|
|
|
|
YAML
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'raises error' do
|
|
|
|
expect(pipeline.yaml_errors)
|
|
|
|
.to eq('jobs:invalid_dag_job:needs config can not be an empty hash')
|
|
|
|
end
|
|
|
|
end
|
2021-04-17 20:07:23 +05:30
|
|
|
|
|
|
|
context 'when the needed job has rules' do
|
|
|
|
let(:config) do
|
|
|
|
<<~YAML
|
|
|
|
build:
|
|
|
|
stage: build
|
|
|
|
script: exit 0
|
|
|
|
rules:
|
|
|
|
- if: $CI_COMMIT_REF_NAME == "invalid"
|
|
|
|
|
|
|
|
test:
|
|
|
|
stage: test
|
|
|
|
script: exit 0
|
|
|
|
needs: [build]
|
|
|
|
YAML
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns error' do
|
|
|
|
expect(pipeline.yaml_errors)
|
2021-09-30 23:02:18 +05:30
|
|
|
.to eq("'test' job needs 'build' job, but 'build' is not in any previous stage")
|
2021-04-17 20:07:23 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'when need is optional' do
|
|
|
|
let(:config) do
|
|
|
|
<<~YAML
|
|
|
|
build:
|
|
|
|
stage: build
|
|
|
|
script: exit 0
|
|
|
|
rules:
|
|
|
|
- if: $CI_COMMIT_REF_NAME == "invalid"
|
|
|
|
|
|
|
|
test:
|
|
|
|
stage: test
|
|
|
|
script: exit 0
|
|
|
|
needs:
|
|
|
|
- job: build
|
|
|
|
optional: true
|
|
|
|
YAML
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'creates the pipeline without an error' do
|
|
|
|
expect(pipeline).to be_persisted
|
|
|
|
expect(pipeline.builds.pluck(:name)).to contain_exactly('test')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2020-01-01 13:55:28 +05:30
|
|
|
end
|
|
|
|
end
|