2019-09-30 21:07:59 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2018-05-09 12:01:36 +05:30
|
|
|
require 'spec_helper'
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
|
2019-01-03 12:48:30 +05:30
|
|
|
let(:project) { create(:project, :repository) }
|
2019-12-26 22:10:19 +05:30
|
|
|
let(:head_sha) { project.repository.head_commit.id }
|
|
|
|
let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: head_sha) }
|
2020-03-13 15:44:24 +05:30
|
|
|
let(:attributes) { { name: 'rspec', ref: 'master', scheduling_type: :stage } }
|
2019-10-12 21:52:04 +05:30
|
|
|
let(:previous_stages) { [] }
|
2018-05-09 12:01:36 +05:30
|
|
|
|
2019-10-12 21:52:04 +05:30
|
|
|
let(:seed_build) { described_class.new(pipeline, attributes, previous_stages) }
|
2018-05-09 12:01:36 +05:30
|
|
|
|
|
|
|
describe '#attributes' do
|
2019-09-30 21:07:59 +05:30
|
|
|
subject { seed_build.attributes }
|
|
|
|
|
|
|
|
it { is_expected.to be_a(Hash) }
|
|
|
|
it { is_expected.to include(:name, :project, :ref) }
|
2019-12-04 20:38:33 +05:30
|
|
|
|
|
|
|
context 'with job:when' do
|
|
|
|
let(:attributes) { { name: 'rspec', ref: 'master', when: 'on_failure' } }
|
|
|
|
|
|
|
|
it { is_expected.to include(when: 'on_failure') }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with job:when:delayed' do
|
|
|
|
let(:attributes) { { name: 'rspec', ref: 'master', when: 'delayed', start_in: '3 hours' } }
|
|
|
|
|
|
|
|
it { is_expected.to include(when: 'delayed', start_in: '3 hours') }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with job:rules:[when:]' do
|
|
|
|
context 'is matched' do
|
|
|
|
let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR == null', when: 'always' }] } }
|
|
|
|
|
|
|
|
it { is_expected.to include(when: 'always') }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'is not matched' do
|
|
|
|
let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR != null', when: 'always' }] } }
|
|
|
|
|
|
|
|
it { is_expected.to include(when: 'never') }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with job:rules:[when:delayed]' do
|
|
|
|
context 'is matched' do
|
|
|
|
let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR == null', when: 'delayed', start_in: '3 hours' }] } }
|
|
|
|
|
|
|
|
it { is_expected.to include(when: 'delayed', options: { start_in: '3 hours' }) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'is not matched' do
|
|
|
|
let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR != null', when: 'delayed', start_in: '3 hours' }] } }
|
|
|
|
|
|
|
|
it { is_expected.to include(when: 'never') }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with job:rules but no explicit when:' do
|
|
|
|
context 'is matched' do
|
|
|
|
let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR == null' }] } }
|
|
|
|
|
|
|
|
it { is_expected.to include(when: 'on_success') }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'is not matched' do
|
|
|
|
let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR != null' }] } }
|
|
|
|
|
|
|
|
it { is_expected.to include(when: 'never') }
|
|
|
|
end
|
|
|
|
end
|
2019-12-26 22:10:19 +05:30
|
|
|
|
|
|
|
context 'with cache:key' do
|
|
|
|
let(:attributes) do
|
|
|
|
{
|
|
|
|
name: 'rspec',
|
|
|
|
ref: 'master',
|
|
|
|
cache: {
|
|
|
|
key: 'a-value'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to include(options: { cache: { key: 'a-value' } }) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with cache:key:files' do
|
|
|
|
let(:attributes) do
|
|
|
|
{
|
|
|
|
name: 'rspec',
|
|
|
|
ref: 'master',
|
|
|
|
cache: {
|
|
|
|
key: {
|
|
|
|
files: ['VERSION']
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'includes cache options' do
|
|
|
|
cache_options = {
|
|
|
|
options: {
|
|
|
|
cache: {
|
|
|
|
key: 'f155568ad0933d8358f66b846133614f76dd0ca4'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
is_expected.to include(cache_options)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with cache:key:prefix' do
|
|
|
|
let(:attributes) do
|
|
|
|
{
|
|
|
|
name: 'rspec',
|
|
|
|
ref: 'master',
|
|
|
|
cache: {
|
|
|
|
key: {
|
|
|
|
prefix: 'something'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to include(options: { cache: { key: 'something-default' } }) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with cache:key:files and prefix' do
|
|
|
|
let(:attributes) do
|
|
|
|
{
|
|
|
|
name: 'rspec',
|
|
|
|
ref: 'master',
|
|
|
|
cache: {
|
|
|
|
key: {
|
|
|
|
files: ['VERSION'],
|
|
|
|
prefix: 'something'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'includes cache options' do
|
|
|
|
cache_options = {
|
|
|
|
options: {
|
|
|
|
cache: {
|
|
|
|
key: 'something-f155568ad0933d8358f66b846133614f76dd0ca4'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
is_expected.to include(cache_options)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with empty cache' do
|
|
|
|
let(:attributes) do
|
|
|
|
{
|
|
|
|
name: 'rspec',
|
|
|
|
ref: 'master',
|
|
|
|
cache: {}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to include(options: {}) }
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
end
|
|
|
|
|
2019-03-02 22:35:43 +05:30
|
|
|
describe '#bridge?' do
|
2019-09-30 21:07:59 +05:30
|
|
|
subject { seed_build.bridge? }
|
|
|
|
|
2019-10-12 21:52:04 +05:30
|
|
|
context 'when job is a downstream bridge' do
|
2019-03-02 22:35:43 +05:30
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', ref: 'master', options: { trigger: 'my/project' } }
|
|
|
|
end
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
it { is_expected.to be_truthy }
|
2019-10-12 21:52:04 +05:30
|
|
|
|
|
|
|
context 'when trigger definition is empty' do
|
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', ref: 'master', options: { trigger: '' } }
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to be_falsey }
|
|
|
|
end
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
|
2019-10-12 21:52:04 +05:30
|
|
|
context 'when job is an upstream bridge' do
|
2019-03-02 22:35:43 +05:30
|
|
|
let(:attributes) do
|
2019-10-12 21:52:04 +05:30
|
|
|
{ name: 'rspec', ref: 'master', options: { bridge_needs: { pipeline: 'my/project' } } }
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
|
2019-10-12 21:52:04 +05:30
|
|
|
it { is_expected.to be_truthy }
|
|
|
|
|
|
|
|
context 'when upstream definition is empty' do
|
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', ref: 'master', options: { bridge_needs: { pipeline: '' } } }
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to be_falsey }
|
|
|
|
end
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'when job is not a bridge' do
|
2019-09-30 21:07:59 +05:30
|
|
|
it { is_expected.to be_falsey }
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-05-09 12:01:36 +05:30
|
|
|
describe '#to_resource' do
|
2019-09-30 21:07:59 +05:30
|
|
|
subject { seed_build.to_resource }
|
|
|
|
|
2019-03-02 22:35:43 +05:30
|
|
|
context 'when job is not a bridge' do
|
2019-09-30 21:07:59 +05:30
|
|
|
it { is_expected.to be_a(::Ci::Build) }
|
|
|
|
it { is_expected.to be_valid }
|
2019-12-21 20:55:43 +05:30
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
shared_examples_for 'deployment job' do
|
2019-12-21 20:55:43 +05:30
|
|
|
it 'returns a job with deployment' do
|
|
|
|
expect(subject.deployment).not_to be_nil
|
|
|
|
expect(subject.deployment.deployable).to eq(subject)
|
2020-04-08 14:13:33 +05:30
|
|
|
expect(subject.deployment.environment.name).to eq(expected_environment_name)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
shared_examples_for 'non-deployment job' do
|
|
|
|
it 'returns a job without deployment' do
|
|
|
|
expect(subject.deployment).to be_nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
shared_examples_for 'ensures environment existence' do
|
|
|
|
it 'has environment' do
|
|
|
|
expect(subject).to be_has_environment
|
|
|
|
expect(subject.environment).to eq(environment_name)
|
|
|
|
expect(subject.metadata.expanded_environment_name).to eq(expected_environment_name)
|
|
|
|
expect(Environment.exists?(name: expected_environment_name)).to eq(true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
shared_examples_for 'ensures environment inexistence' do
|
|
|
|
it 'does not have environment' do
|
|
|
|
expect(subject).not_to be_has_environment
|
|
|
|
expect(subject.environment).to be_nil
|
|
|
|
expect(subject.metadata.expanded_environment_name).to be_nil
|
|
|
|
expect(Environment.exists?(name: expected_environment_name)).to eq(false)
|
2019-12-21 20:55:43 +05:30
|
|
|
end
|
2020-04-08 14:13:33 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'when job deploys to production' do
|
|
|
|
let(:environment_name) { 'production' }
|
|
|
|
let(:expected_environment_name) { 'production' }
|
|
|
|
let(:attributes) { { name: 'deploy', ref: 'master', environment: 'production' } }
|
|
|
|
|
|
|
|
it_behaves_like 'deployment job'
|
|
|
|
it_behaves_like 'ensures environment existence'
|
2019-12-21 20:55:43 +05:30
|
|
|
|
|
|
|
context 'when the environment name is invalid' do
|
2020-04-08 14:13:33 +05:30
|
|
|
let(:attributes) { { name: 'deploy', ref: 'master', environment: '!!!' } }
|
|
|
|
|
|
|
|
it_behaves_like 'non-deployment job'
|
|
|
|
it_behaves_like 'ensures environment inexistence'
|
2019-12-21 20:55:43 +05:30
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
it 'tracks an exception' do
|
|
|
|
expect(Gitlab::ErrorTracking).to receive(:track_exception)
|
|
|
|
.with(an_instance_of(described_class::EnvironmentCreationFailure),
|
|
|
|
project_id: project.id,
|
|
|
|
reason: %q{Name can contain only letters, digits, '-', '_', '/', '$', '{', '}', '.', and spaces, but it cannot start or end with '/'})
|
|
|
|
.once
|
|
|
|
|
|
|
|
subject
|
2019-12-21 20:55:43 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2020-03-13 15:44:24 +05:30
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
context 'when job starts a review app' do
|
|
|
|
let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' }
|
|
|
|
let(:expected_environment_name) { "review/#{pipeline.ref}" }
|
|
|
|
|
|
|
|
let(:attributes) do
|
|
|
|
{
|
|
|
|
name: 'deploy', ref: 'master', environment: environment_name,
|
|
|
|
options: { environment: { name: environment_name } }
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'deployment job'
|
|
|
|
it_behaves_like 'ensures environment existence'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when job stops a review app' do
|
|
|
|
let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' }
|
|
|
|
let(:expected_environment_name) { "review/#{pipeline.ref}" }
|
|
|
|
|
|
|
|
let(:attributes) do
|
|
|
|
{
|
|
|
|
name: 'deploy', ref: 'master', environment: environment_name,
|
|
|
|
options: { environment: { name: environment_name, action: 'stop' } }
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns a job without deployment' do
|
|
|
|
expect(subject.deployment).to be_nil
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'non-deployment job'
|
|
|
|
it_behaves_like 'ensures environment existence'
|
|
|
|
end
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
context 'when job belongs to a resource group' do
|
|
|
|
let(:attributes) { { name: 'rspec', ref: 'master', resource_group_key: 'iOS' } }
|
|
|
|
|
|
|
|
it 'returns a job with resource group' do
|
|
|
|
expect(subject.resource_group).not_to be_nil
|
|
|
|
expect(subject.resource_group.key).to eq('iOS')
|
|
|
|
end
|
|
|
|
end
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'when job is a bridge' do
|
|
|
|
let(:attributes) do
|
2020-03-13 15:44:24 +05:30
|
|
|
{
|
|
|
|
name: 'rspec', ref: 'master', options: { trigger: 'my/project' }, scheduling_type: :stage
|
|
|
|
}
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
it { is_expected.to be_a(::Ci::Bridge) }
|
|
|
|
it { is_expected.to be_valid }
|
2018-05-09 12:01:36 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it 'memoizes a resource object' do
|
2019-09-30 21:07:59 +05:30
|
|
|
expect(subject.object_id).to eq seed_build.to_resource.object_id
|
2018-05-09 12:01:36 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it 'can not be persisted without explicit assignment' do
|
|
|
|
pipeline.save!
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
expect(subject).not_to be_persisted
|
2018-05-09 12:01:36 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
describe 'applying job inclusion policies' do
|
|
|
|
subject { seed_build }
|
|
|
|
|
2018-05-09 12:01:36 +05:30
|
|
|
context 'when no branch policy is specified' do
|
2019-09-30 21:07:59 +05:30
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec' }
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
|
|
|
|
it { is_expected.to be_included }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when branch policy does not match' do
|
|
|
|
context 'when using only' do
|
2019-09-30 21:07:59 +05:30
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', only: { refs: ['deploy'] } }
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when using except' do
|
2019-09-30 21:07:59 +05:30
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', except: { refs: ['deploy'] } }
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
|
|
|
|
it { is_expected.to be_included }
|
|
|
|
end
|
2019-09-30 21:07:59 +05:30
|
|
|
|
|
|
|
context 'with both only and except policies' do
|
|
|
|
let(:attributes) do
|
|
|
|
{
|
|
|
|
name: 'rspec',
|
|
|
|
only: { refs: %w[deploy] },
|
|
|
|
except: { refs: %w[deploy] }
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'when branch regexp policy does not match' do
|
|
|
|
context 'when using only' do
|
2019-09-30 21:07:59 +05:30
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', only: { refs: %w[/^deploy$/] } }
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when using except' do
|
2019-09-30 21:07:59 +05:30
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', except: { refs: %w[/^deploy$/] } }
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
|
|
|
|
it { is_expected.to be_included }
|
|
|
|
end
|
2019-09-30 21:07:59 +05:30
|
|
|
|
|
|
|
context 'with both only and except policies' do
|
|
|
|
let(:attributes) do
|
|
|
|
{
|
|
|
|
name: 'rspec',
|
|
|
|
only: { refs: %w[/^deploy$/] },
|
|
|
|
except: { refs: %w[/^deploy$/] }
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'when branch policy matches' do
|
|
|
|
context 'when using only' do
|
2019-09-30 21:07:59 +05:30
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', only: { refs: %w[deploy master] } }
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
|
|
|
|
it { is_expected.to be_included }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when using except' do
|
2019-09-30 21:07:59 +05:30
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', except: { refs: %w[deploy master] } }
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when using both only and except policies' do
|
|
|
|
let(:attributes) do
|
|
|
|
{
|
|
|
|
name: 'rspec',
|
|
|
|
only: { refs: %w[deploy master] },
|
|
|
|
except: { refs: %w[deploy master] }
|
|
|
|
}
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when keyword policy matches' do
|
|
|
|
context 'when using only' do
|
2019-09-30 21:07:59 +05:30
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', only: { refs: %w[branches] } }
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
|
|
|
|
it { is_expected.to be_included }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when using except' do
|
2019-09-30 21:07:59 +05:30
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', except: { refs: %w[branches] } }
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when using both only and except policies' do
|
|
|
|
let(:attributes) do
|
|
|
|
{
|
|
|
|
name: 'rspec',
|
|
|
|
only: { refs: %w[branches] },
|
|
|
|
except: { refs: %w[branches] }
|
|
|
|
}
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when keyword policy does not match' do
|
|
|
|
context 'when using only' do
|
2019-09-30 21:07:59 +05:30
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', only: { refs: %w[tags] } }
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when using except' do
|
2019-09-30 21:07:59 +05:30
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', except: { refs: %w[tags] } }
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
|
|
|
|
it { is_expected.to be_included }
|
|
|
|
end
|
2019-09-30 21:07:59 +05:30
|
|
|
|
|
|
|
context 'when using both only and except policies' do
|
|
|
|
let(:attributes) do
|
|
|
|
{
|
|
|
|
name: 'rspec',
|
|
|
|
only: { refs: %w[tags] },
|
|
|
|
except: { refs: %w[tags] }
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
end
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
context 'with source-keyword policy' do
|
|
|
|
using RSpec::Parameterized
|
2018-05-09 12:01:36 +05:30
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
let(:pipeline) do
|
|
|
|
build(:ci_empty_pipeline, ref: 'deploy', tag: false, source: source)
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
context 'matches' do
|
|
|
|
where(:keyword, :source) do
|
|
|
|
[
|
|
|
|
%w[pushes push],
|
|
|
|
%w[web web],
|
|
|
|
%w[triggers trigger],
|
|
|
|
%w[schedules schedule],
|
|
|
|
%w[api api],
|
|
|
|
%w[external external]
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
context 'using an only policy' do
|
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', only: { refs: [keyword] } }
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
|
|
|
|
it { is_expected.to be_included }
|
|
|
|
end
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
context 'using an except policy' do
|
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', except: { refs: [keyword] } }
|
2018-05-09 12:01:36 +05:30
|
|
|
end
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'using both only and except policies' do
|
|
|
|
let(:attributes) do
|
|
|
|
{
|
|
|
|
name: 'rspec',
|
|
|
|
only: { refs: [keyword] },
|
|
|
|
except: { refs: [keyword] }
|
|
|
|
}
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
context 'non-matches' do
|
|
|
|
where(:keyword, :source) do
|
|
|
|
%w[web trigger schedule api external].map { |source| ['pushes', source] } +
|
|
|
|
%w[push trigger schedule api external].map { |source| ['web', source] } +
|
|
|
|
%w[push web schedule api external].map { |source| ['triggers', source] } +
|
|
|
|
%w[push web trigger api external].map { |source| ['schedules', source] } +
|
|
|
|
%w[push web trigger schedule external].map { |source| ['api', source] } +
|
|
|
|
%w[push web trigger schedule api].map { |source| ['external', source] }
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
with_them do
|
|
|
|
context 'using an only policy' do
|
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', only: { refs: [keyword] } }
|
2018-05-09 12:01:36 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
context 'using an except policy' do
|
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', except: { refs: [keyword] } }
|
2018-05-09 12:01:36 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to be_included }
|
|
|
|
end
|
2019-09-30 21:07:59 +05:30
|
|
|
|
|
|
|
context 'using both only and except policies' do
|
|
|
|
let(:attributes) do
|
|
|
|
{
|
|
|
|
name: 'rspec',
|
|
|
|
only: { refs: [keyword] },
|
|
|
|
except: { refs: [keyword] }
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when repository path matches' do
|
|
|
|
context 'when using only' do
|
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', only: { refs: ["branches@#{pipeline.project_full_path}"] } }
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to be_included }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when using except' do
|
|
|
|
let(:attributes) do
|
|
|
|
{ name: 'rspec', except: { refs: ["branches@#{pipeline.project_full_path}"] } }
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
2019-09-30 21:07:59 +05:30
|
|
|
|
|
|
|
context 'when using both only and except policies' do
|
|
|
|
let(:attributes) do
|
|
|
|
{
|
|
|
|
name: 'rspec',
|
|
|
|
only: { refs: ["branches@#{pipeline.project_full_path}"] },
|
|
|
|
except: { refs: ["branches@#{pipeline.project_full_path}"] }
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
2019-12-04 20:38:33 +05:30
|
|
|
|
|
|
|
context 'when using both only and except policies' do
|
|
|
|
let(:attributes) do
|
|
|
|
{
|
|
|
|
name: 'rspec',
|
|
|
|
only: {
|
|
|
|
refs: ["branches@#{pipeline.project_full_path}"]
|
|
|
|
},
|
|
|
|
except: {
|
|
|
|
refs: ["branches@#{pipeline.project_full_path}"]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
end
|
|
|
|
|
2019-12-04 20:38:33 +05:30
|
|
|
context 'when repository path does not match' do
|
2018-05-09 12:01:36 +05:30
|
|
|
context 'when using only' do
|
|
|
|
let(:attributes) do
|
2019-09-30 21:07:59 +05:30
|
|
|
{ name: 'rspec', only: { refs: %w[branches@fork] } }
|
2018-05-09 12:01:36 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when using except' do
|
|
|
|
let(:attributes) do
|
2019-09-30 21:07:59 +05:30
|
|
|
{ name: 'rspec', except: { refs: %w[branches@fork] } }
|
2018-05-09 12:01:36 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to be_included }
|
|
|
|
end
|
2019-09-30 21:07:59 +05:30
|
|
|
|
|
|
|
context 'when using both only and except policies' do
|
|
|
|
let(:attributes) do
|
|
|
|
{
|
|
|
|
name: 'rspec',
|
|
|
|
only: { refs: %w[branches@fork] },
|
|
|
|
except: { refs: %w[branches@fork] }
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
end
|
2019-12-04 20:38:33 +05:30
|
|
|
|
|
|
|
context 'using rules:' do
|
|
|
|
using RSpec::Parameterized
|
|
|
|
|
|
|
|
let(:attributes) { { name: 'rspec', rules: rule_set } }
|
|
|
|
|
|
|
|
context 'with a matching if: rule' do
|
|
|
|
context 'with an explicit `when: never`' do
|
|
|
|
where(:rule_set) do
|
|
|
|
[
|
|
|
|
[[{ if: '$VARIABLE == null', when: 'never' }]],
|
|
|
|
[[{ if: '$VARIABLE == null', when: 'never' }, { if: '$VARIABLE == null', when: 'always' }]],
|
|
|
|
[[{ if: '$VARIABLE != "the wrong value"', when: 'never' }, { if: '$VARIABLE == null', when: 'always' }]]
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
|
|
|
|
it 'correctly populates when:' do
|
|
|
|
expect(seed_build.attributes).to include(when: 'never')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with an explicit `when: always`' do
|
|
|
|
where(:rule_set) do
|
|
|
|
[
|
|
|
|
[[{ if: '$VARIABLE == null', when: 'always' }]],
|
|
|
|
[[{ if: '$VARIABLE == null', when: 'always' }, { if: '$VARIABLE == null', when: 'never' }]],
|
|
|
|
[[{ if: '$VARIABLE != "the wrong value"', when: 'always' }, { if: '$VARIABLE == null', when: 'never' }]]
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
it { is_expected.to be_included }
|
|
|
|
|
|
|
|
it 'correctly populates when:' do
|
|
|
|
expect(seed_build.attributes).to include(when: 'always')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with an explicit `when: on_failure`' do
|
|
|
|
where(:rule_set) do
|
|
|
|
[
|
|
|
|
[[{ if: '$CI_JOB_NAME == "rspec" && $VAR == null', when: 'on_failure' }]],
|
|
|
|
[[{ if: '$VARIABLE != null', when: 'delayed', start_in: '1 day' }, { if: '$CI_JOB_NAME == "rspec"', when: 'on_failure' }]],
|
|
|
|
[[{ if: '$VARIABLE == "the wrong value"', when: 'delayed', start_in: '1 day' }, { if: '$CI_BUILD_NAME == "rspec"', when: 'on_failure' }]]
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
it { is_expected.to be_included }
|
|
|
|
|
|
|
|
it 'correctly populates when:' do
|
|
|
|
expect(seed_build.attributes).to include(when: 'on_failure')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with an explicit `when: delayed`' do
|
|
|
|
where(:rule_set) do
|
|
|
|
[
|
|
|
|
[[{ if: '$VARIABLE == null', when: 'delayed', start_in: '1 day' }]],
|
|
|
|
[[{ if: '$VARIABLE == null', when: 'delayed', start_in: '1 day' }, { if: '$VARIABLE == null', when: 'never' }]],
|
|
|
|
[[{ if: '$VARIABLE != "the wrong value"', when: 'delayed', start_in: '1 day' }, { if: '$VARIABLE == null', when: 'never' }]]
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
it { is_expected.to be_included }
|
|
|
|
|
|
|
|
it 'correctly populates when:' do
|
|
|
|
expect(seed_build.attributes).to include(when: 'delayed', options: { start_in: '1 day' })
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'without an explicit when: value' do
|
|
|
|
where(:rule_set) do
|
|
|
|
[
|
|
|
|
[[{ if: '$VARIABLE == null' }]],
|
|
|
|
[[{ if: '$VARIABLE == null' }, { if: '$VARIABLE == null' }]],
|
|
|
|
[[{ if: '$VARIABLE != "the wrong value"' }, { if: '$VARIABLE == null' }]]
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
it { is_expected.to be_included }
|
|
|
|
|
|
|
|
it 'correctly populates when:' do
|
|
|
|
expect(seed_build.attributes).to include(when: 'on_success')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with a matching changes: rule' do
|
|
|
|
let(:pipeline) do
|
|
|
|
create(:ci_pipeline, project: project).tap do |pipeline|
|
|
|
|
stub_pipeline_modified_paths(pipeline, %w[app/models/ci/pipeline.rb spec/models/ci/pipeline_spec.rb .gitlab-ci.yml])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with an explicit `when: never`' do
|
|
|
|
where(:rule_set) do
|
|
|
|
[
|
|
|
|
[[{ changes: %w[*/**/*.rb], when: 'never' }, { changes: %w[*/**/*.rb], when: 'always' }]],
|
|
|
|
[[{ changes: %w[app/models/ci/pipeline.rb], when: 'never' }, { changes: %w[app/models/ci/pipeline.rb], when: 'always' }]],
|
|
|
|
[[{ changes: %w[spec/**/*.rb], when: 'never' }, { changes: %w[spec/**/*.rb], when: 'always' }]],
|
|
|
|
[[{ changes: %w[*.yml], when: 'never' }, { changes: %w[*.yml], when: 'always' }]],
|
|
|
|
[[{ changes: %w[.*.yml], when: 'never' }, { changes: %w[.*.yml], when: 'always' }]],
|
|
|
|
[[{ changes: %w[**/*], when: 'never' }, { changes: %w[**/*], when: 'always' }]],
|
|
|
|
[[{ changes: %w[*/**/*.rb *.yml], when: 'never' }, { changes: %w[*/**/*.rb *.yml], when: 'always' }]],
|
|
|
|
[[{ changes: %w[.*.yml **/*], when: 'never' }, { changes: %w[.*.yml **/*], when: 'always' }]]
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
|
|
|
|
it 'correctly populates when:' do
|
|
|
|
expect(seed_build.attributes).to include(when: 'never')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with an explicit `when: always`' do
|
|
|
|
where(:rule_set) do
|
|
|
|
[
|
|
|
|
[[{ changes: %w[*/**/*.rb], when: 'always' }, { changes: %w[*/**/*.rb], when: 'never' }]],
|
|
|
|
[[{ changes: %w[app/models/ci/pipeline.rb], when: 'always' }, { changes: %w[app/models/ci/pipeline.rb], when: 'never' }]],
|
|
|
|
[[{ changes: %w[spec/**/*.rb], when: 'always' }, { changes: %w[spec/**/*.rb], when: 'never' }]],
|
|
|
|
[[{ changes: %w[*.yml], when: 'always' }, { changes: %w[*.yml], when: 'never' }]],
|
|
|
|
[[{ changes: %w[.*.yml], when: 'always' }, { changes: %w[.*.yml], when: 'never' }]],
|
|
|
|
[[{ changes: %w[**/*], when: 'always' }, { changes: %w[**/*], when: 'never' }]],
|
|
|
|
[[{ changes: %w[*/**/*.rb *.yml], when: 'always' }, { changes: %w[*/**/*.rb *.yml], when: 'never' }]],
|
|
|
|
[[{ changes: %w[.*.yml **/*], when: 'always' }, { changes: %w[.*.yml **/*], when: 'never' }]]
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
it { is_expected.to be_included }
|
|
|
|
|
|
|
|
it 'correctly populates when:' do
|
|
|
|
expect(seed_build.attributes).to include(when: 'always')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'without an explicit when: value' do
|
|
|
|
where(:rule_set) do
|
|
|
|
[
|
|
|
|
[[{ changes: %w[*/**/*.rb] }]],
|
|
|
|
[[{ changes: %w[app/models/ci/pipeline.rb] }]],
|
|
|
|
[[{ changes: %w[spec/**/*.rb] }]],
|
|
|
|
[[{ changes: %w[*.yml] }]],
|
|
|
|
[[{ changes: %w[.*.yml] }]],
|
|
|
|
[[{ changes: %w[**/*] }]],
|
|
|
|
[[{ changes: %w[*/**/*.rb *.yml] }]],
|
|
|
|
[[{ changes: %w[.*.yml **/*] }]]
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
it { is_expected.to be_included }
|
|
|
|
|
|
|
|
it 'correctly populates when:' do
|
|
|
|
expect(seed_build.attributes).to include(when: 'on_success')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with no matching rule' do
|
|
|
|
where(:rule_set) do
|
|
|
|
[
|
|
|
|
[[{ if: '$VARIABLE != null', when: 'never' }]],
|
|
|
|
[[{ if: '$VARIABLE != null', when: 'never' }, { if: '$VARIABLE != null', when: 'always' }]],
|
|
|
|
[[{ if: '$VARIABLE == "the wrong value"', when: 'never' }, { if: '$VARIABLE != null', when: 'always' }]],
|
|
|
|
[[{ if: '$VARIABLE != null', when: 'always' }]],
|
|
|
|
[[{ if: '$VARIABLE != null', when: 'always' }, { if: '$VARIABLE != null', when: 'never' }]],
|
|
|
|
[[{ if: '$VARIABLE == "the wrong value"', when: 'always' }, { if: '$VARIABLE != null', when: 'never' }]],
|
|
|
|
[[{ if: '$VARIABLE != null' }]],
|
|
|
|
[[{ if: '$VARIABLE != null' }, { if: '$VARIABLE != null' }]],
|
|
|
|
[[{ if: '$VARIABLE == "the wrong value"' }, { if: '$VARIABLE != null' }]]
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
|
|
|
|
it 'correctly populates when:' do
|
|
|
|
expect(seed_build.attributes).to include(when: 'never')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with no rules' do
|
|
|
|
let(:rule_set) { [] }
|
|
|
|
|
|
|
|
it { is_expected.not_to be_included }
|
|
|
|
|
|
|
|
it 'correctly populates when:' do
|
|
|
|
expect(seed_build.attributes).to include(when: 'never')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
end
|
2019-10-12 21:52:04 +05:30
|
|
|
|
|
|
|
describe 'applying needs: dependency' do
|
|
|
|
subject { seed_build }
|
|
|
|
|
|
|
|
let(:needs_count) { 1 }
|
|
|
|
|
|
|
|
let(:needs_attributes) do
|
|
|
|
Array.new(needs_count, name: 'build')
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:attributes) do
|
|
|
|
{
|
|
|
|
name: 'rspec',
|
|
|
|
needs_attributes: needs_attributes
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when build job is not present in prior stages' do
|
|
|
|
it "is included" do
|
|
|
|
is_expected.to be_included
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns an error" do
|
|
|
|
expect(subject.errors).to contain_exactly(
|
|
|
|
"rspec: needs 'build'")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when build job is part of prior stages' do
|
|
|
|
let(:stage_attributes) do
|
|
|
|
{
|
|
|
|
name: 'build',
|
|
|
|
index: 0,
|
|
|
|
builds: [{ name: 'build' }]
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:stage_seed) do
|
|
|
|
Gitlab::Ci::Pipeline::Seed::Stage.new(pipeline, stage_attributes, [])
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:previous_stages) { [stage_seed] }
|
|
|
|
|
|
|
|
it "is included" do
|
|
|
|
is_expected.to be_included
|
|
|
|
end
|
|
|
|
|
|
|
|
it "does not have errors" do
|
|
|
|
expect(subject.errors).to be_empty
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
context 'when using 101 needs' do
|
|
|
|
let(:needs_count) { 101 }
|
2019-10-12 21:52:04 +05:30
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
context 'when ci_plan_needs_size_limit is disabled' do
|
|
|
|
before do
|
|
|
|
stub_feature_flags(ci_plan_needs_size_limit: false)
|
|
|
|
end
|
2019-10-12 21:52:04 +05:30
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
it "returns an error" do
|
|
|
|
expect(subject.errors).to contain_exactly(
|
|
|
|
"rspec: one job can only need 10 others, but you have listed 101. See needs keyword documentation for more details")
|
|
|
|
end
|
2019-10-12 21:52:04 +05:30
|
|
|
end
|
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
context 'when ci_plan_needs_size_limit is enabled' do
|
|
|
|
before do
|
|
|
|
stub_feature_flags(ci_plan_needs_size_limit: true)
|
|
|
|
end
|
2019-10-12 21:52:04 +05:30
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
it "returns an error" do
|
|
|
|
expect(subject.errors).to contain_exactly(
|
|
|
|
"rspec: one job can only need 50 others, but you have listed 101. See needs keyword documentation for more details")
|
|
|
|
end
|
2019-10-12 21:52:04 +05:30
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
context 'when ci_needs_size_limit is set to 100' do
|
|
|
|
before do
|
|
|
|
project.actual_limits.update!(ci_needs_size_limit: 100)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns an error" do
|
|
|
|
expect(subject.errors).to contain_exactly(
|
|
|
|
"rspec: one job can only need 100 others, but you have listed 101. See needs keyword documentation for more details")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when ci_needs_size_limit is set to 0' do
|
|
|
|
before do
|
|
|
|
project.actual_limits.update!(ci_needs_size_limit: 0)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns an error" do
|
|
|
|
expect(subject.errors).to contain_exactly(
|
|
|
|
"rspec: one job can only need 0 others, but you have listed 101. See needs keyword documentation for more details")
|
|
|
|
end
|
|
|
|
end
|
2019-10-12 21:52:04 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2018-05-09 12:01:36 +05:30
|
|
|
end
|