2019-10-12 21:52:04 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2018-12-13 13:39:08 +05:30
|
|
|
require 'spec_helper'
|
2016-09-13 17:45:13 +05:30
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
RSpec.describe Gitlab::Ci::Config::Entry::Job do
|
2016-09-13 17:45:13 +05:30
|
|
|
let(:entry) { described_class.new(config, name: :rspec) }
|
|
|
|
|
2019-12-26 22:10:19 +05:30
|
|
|
it_behaves_like 'with inheritable CI config' do
|
2020-04-08 14:13:33 +05:30
|
|
|
let(:config) { { script: 'echo' } }
|
2019-12-26 22:10:19 +05:30
|
|
|
let(:inheritable_key) { 'default' }
|
|
|
|
let(:inheritable_class) { Gitlab::Ci::Config::Entry::Default }
|
|
|
|
|
|
|
|
# These are entries defined in Default
|
|
|
|
# that we know that we don't want to inherit
|
|
|
|
# as they do not have sense in context of Job
|
|
|
|
let(:ignored_inheritable_columns) do
|
|
|
|
%i[]
|
|
|
|
end
|
2020-04-08 14:13:33 +05:30
|
|
|
|
|
|
|
before do
|
|
|
|
allow(entry).to receive_message_chain(:inherit_entry, :default_entry, :inherit?).and_return(true)
|
|
|
|
end
|
2019-12-26 22:10:19 +05:30
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
describe '.nodes' do
|
|
|
|
context 'when filtering all the entry/node names' do
|
|
|
|
subject { described_class.nodes.keys }
|
|
|
|
|
|
|
|
let(:result) do
|
2023-03-04 22:38:38 +05:30
|
|
|
%i[before_script script after_script hooks stage cache
|
2019-12-26 22:10:19 +05:30
|
|
|
image services only except rules needs variables artifacts
|
2020-04-08 14:13:33 +05:30
|
|
|
environment coverage retry interruptible timeout release tags
|
2020-10-24 23:57:45 +05:30
|
|
|
inherit parallel]
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
it { is_expected.to include(*result) }
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
describe '.matching?' do
|
|
|
|
subject { described_class.matching?(name, config) }
|
|
|
|
|
|
|
|
context 'when config is not a hash' do
|
|
|
|
let(:name) { :rspec }
|
|
|
|
let(:config) { 'string' }
|
|
|
|
|
|
|
|
it { is_expected.to be_falsey }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when config is a regular job' do
|
|
|
|
let(:name) { :rspec }
|
|
|
|
let(:config) do
|
|
|
|
{ script: 'ls -al' }
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to be_truthy }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when config is a bridge job' do
|
|
|
|
let(:name) { :rspec }
|
|
|
|
let(:config) do
|
|
|
|
{ trigger: 'other-project' }
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to be_falsey }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when config is a hidden job' do
|
|
|
|
let(:name) { '.rspec' }
|
|
|
|
let(:config) do
|
|
|
|
{ script: 'ls -al' }
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to be_falsey }
|
|
|
|
end
|
2020-10-24 23:57:45 +05:30
|
|
|
|
|
|
|
context 'when using the default job without script' do
|
|
|
|
let(:name) { :default }
|
|
|
|
let(:config) do
|
|
|
|
{ before_script: "cd ${PROJ_DIR} " }
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to be_falsey }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when using the default job with script' do
|
|
|
|
let(:name) { :default }
|
|
|
|
let(:config) do
|
|
|
|
{
|
|
|
|
before_script: "cd ${PROJ_DIR} ",
|
|
|
|
script: "ls"
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to be_truthy }
|
|
|
|
end
|
2019-09-30 21:07:59 +05:30
|
|
|
end
|
|
|
|
|
2016-09-13 17:45:13 +05:30
|
|
|
describe 'validations' do
|
2017-09-10 17:25:29 +05:30
|
|
|
before do
|
|
|
|
entry.compose!
|
|
|
|
end
|
2016-09-29 09:46:39 +05:30
|
|
|
|
2016-09-13 17:45:13 +05:30
|
|
|
context 'when entry config value is correct' do
|
|
|
|
let(:config) { { script: 'rspec' } }
|
|
|
|
|
|
|
|
describe '#valid?' do
|
|
|
|
it 'is valid' do
|
|
|
|
expect(entry).to be_valid
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when job name is empty' do
|
|
|
|
let(:entry) { described_class.new(config, name: ''.to_sym) }
|
|
|
|
|
|
|
|
it 'reports error' do
|
2017-08-17 22:00:37 +05:30
|
|
|
expect(entry.errors).to include "job name can't be blank"
|
2016-09-13 17:45:13 +05:30
|
|
|
end
|
|
|
|
end
|
2018-12-05 23:21:45 +05:30
|
|
|
|
2022-01-26 12:08:38 +05:30
|
|
|
context 'when config uses both "when:" and "rules:"' do
|
|
|
|
let(:config) do
|
|
|
|
{
|
|
|
|
script: 'echo',
|
|
|
|
when: 'on_failure',
|
|
|
|
rules: [{ if: '$VARIABLE', when: 'on_success' }]
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'is valid' do
|
|
|
|
expect(entry).to be_valid
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-12-05 23:21:45 +05:30
|
|
|
context 'when delayed job' do
|
|
|
|
context 'when start_in is specified' do
|
2020-01-01 13:55:28 +05:30
|
|
|
let(:config) { { script: 'echo', when: 'delayed', start_in: '1 week' } }
|
2018-12-05 23:21:45 +05:30
|
|
|
|
|
|
|
it { expect(entry).to be_valid }
|
|
|
|
end
|
|
|
|
end
|
2019-10-12 21:52:04 +05:30
|
|
|
|
|
|
|
context 'when has needs' do
|
|
|
|
let(:config) do
|
|
|
|
{
|
|
|
|
stage: 'test',
|
|
|
|
script: 'echo',
|
|
|
|
needs: ['another-job']
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it { expect(entry).to be_valid }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
it "returns scheduling_type as :dag" do
|
|
|
|
expect(entry.value[:scheduling_type]).to eq(:dag)
|
|
|
|
end
|
|
|
|
|
2019-10-12 21:52:04 +05:30
|
|
|
context 'when has dependencies' do
|
|
|
|
let(:config) do
|
|
|
|
{
|
|
|
|
stage: 'test',
|
|
|
|
script: 'echo',
|
|
|
|
dependencies: ['another-job'],
|
|
|
|
needs: ['another-job']
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it { expect(entry).to be_valid }
|
|
|
|
end
|
2020-03-13 15:44:24 +05:30
|
|
|
|
|
|
|
context 'when it is a release' do
|
|
|
|
let(:config) do
|
|
|
|
{
|
|
|
|
script: ["make changelog | tee release_changelog.txt"],
|
|
|
|
release: {
|
|
|
|
tag_name: "v0.06",
|
|
|
|
name: "Release $CI_TAG_NAME",
|
|
|
|
description: "./release_changelog.txt"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it { expect(entry).to be_valid }
|
|
|
|
end
|
2019-10-12 21:52:04 +05:30
|
|
|
end
|
2021-11-11 11:23:49 +05:30
|
|
|
|
|
|
|
context 'when rules are used' do
|
|
|
|
let(:config) { { script: 'ls', cache: { key: 'test' }, rules: rules } }
|
|
|
|
|
|
|
|
let(:rules) do
|
|
|
|
[
|
|
|
|
{ if: '$CI_PIPELINE_SOURCE == "schedule"', when: 'never' },
|
|
|
|
[
|
|
|
|
{ if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' },
|
|
|
|
{ if: '$CI_PIPELINE_SOURCE == "merge_request_event"' }
|
|
|
|
]
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
it { expect(entry).to be_valid }
|
|
|
|
end
|
2016-09-13 17:45:13 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'when entry value is not correct' do
|
|
|
|
context 'incorrect config value type' do
|
|
|
|
let(:config) { ['incorrect'] }
|
|
|
|
|
|
|
|
describe '#errors' do
|
|
|
|
it 'reports error about a config type' do
|
|
|
|
expect(entry.errors)
|
|
|
|
.to include 'job config should be a hash'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when config is empty' do
|
|
|
|
let(:config) { {} }
|
|
|
|
|
|
|
|
describe '#valid' do
|
|
|
|
it 'is invalid' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when unknown keys detected' do
|
|
|
|
let(:config) { { unknown: true } }
|
|
|
|
|
|
|
|
describe '#valid' do
|
|
|
|
it 'is not valid' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
context 'when script is not provided' do
|
|
|
|
let(:config) { { stage: 'test' } }
|
|
|
|
|
|
|
|
it 'returns error about missing script entry' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include "job script can't be blank"
|
|
|
|
end
|
|
|
|
end
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2018-11-20 20:47:30 +05:30
|
|
|
context 'when extends key is not a string' do
|
|
|
|
let(:config) { { extends: 123 } }
|
|
|
|
|
|
|
|
it 'returns error about wrong value type' do
|
|
|
|
expect(entry).not_to be_valid
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(entry.errors).to include "job extends should be an array of strings or a string"
|
2018-11-20 20:47:30 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-12-13 13:39:08 +05:30
|
|
|
context 'when parallel value is not correct' do
|
2017-09-10 17:25:29 +05:30
|
|
|
context 'when it is not a numeric value' do
|
2020-10-24 23:57:45 +05:30
|
|
|
let(:config) { { script: 'echo', parallel: true } }
|
2017-09-10 17:25:29 +05:30
|
|
|
|
|
|
|
it 'returns error about invalid type' do
|
|
|
|
expect(entry).not_to be_valid
|
2020-10-24 23:57:45 +05:30
|
|
|
expect(entry.errors).to include 'parallel should be an integer or a hash'
|
2017-09-10 17:25:29 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-12-13 13:39:08 +05:30
|
|
|
context 'when it is lower than two' do
|
2020-10-24 23:57:45 +05:30
|
|
|
let(:config) { { script: 'echo', parallel: 1 } }
|
2017-09-10 17:25:29 +05:30
|
|
|
|
|
|
|
it 'returns error about value too low' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors)
|
2020-10-24 23:57:45 +05:30
|
|
|
.to include 'parallel config must be greater than or equal to 2'
|
2017-09-10 17:25:29 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
context 'when it is an empty hash' do
|
|
|
|
let(:config) { { script: 'echo', parallel: {} } }
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
it 'returns error about missing matrix' do
|
2017-09-10 17:25:29 +05:30
|
|
|
expect(entry).not_to be_valid
|
2018-12-13 13:39:08 +05:30
|
|
|
expect(entry.errors)
|
2020-10-24 23:57:45 +05:30
|
|
|
.to include 'parallel config missing required keys: matrix'
|
2017-09-10 17:25:29 +05:30
|
|
|
end
|
|
|
|
end
|
2020-10-24 23:57:45 +05:30
|
|
|
end
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2018-12-05 23:21:45 +05:30
|
|
|
context 'when delayed job' do
|
|
|
|
context 'when start_in is specified' do
|
2020-01-01 13:55:28 +05:30
|
|
|
let(:config) { { script: 'echo', when: 'delayed', start_in: '1 week' } }
|
2018-12-05 23:21:45 +05:30
|
|
|
|
2020-01-01 13:55:28 +05:30
|
|
|
it { expect(entry).to be_valid }
|
2018-12-05 23:21:45 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'when start_in is empty' do
|
|
|
|
let(:config) { { when: 'delayed', start_in: nil } }
|
|
|
|
|
|
|
|
it 'returns error about invalid type' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include 'job start in should be a duration'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when start_in is not formatted as a duration' do
|
|
|
|
let(:config) { { when: 'delayed', start_in: 'test' } }
|
|
|
|
|
|
|
|
it 'returns error about invalid type' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include 'job start in should be a duration'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-01-01 13:55:28 +05:30
|
|
|
context 'when start_in is longer than one week' do
|
|
|
|
let(:config) { { when: 'delayed', start_in: '8 days' } }
|
2018-12-05 23:21:45 +05:30
|
|
|
|
|
|
|
it 'returns error about exceeding the limit' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include 'job start in should not exceed the limit'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-01-13 00:05:48 +05:30
|
|
|
context 'when the `when` keyword is not a string' do
|
|
|
|
context 'when it is an array' do
|
|
|
|
let(:config) { { script: 'exit 0', when: ['always'] } }
|
|
|
|
|
|
|
|
it 'returns error' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include 'job when should be a string'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when it is a boolean' do
|
|
|
|
let(:config) { { script: 'exit 0', when: true } }
|
|
|
|
|
|
|
|
it 'returns error' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include 'job when should be a string'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-12-04 20:38:33 +05:30
|
|
|
context 'when only: is used with rules:' do
|
|
|
|
let(:config) { { only: ['merge_requests'], rules: [{ if: '$THIS' }] } }
|
|
|
|
|
|
|
|
it 'returns error about mixing only: with rules:' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include /may not be used with `rules`/
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'and only: is blank' do
|
|
|
|
let(:config) { { only: nil, rules: [{ if: '$THIS' }] } }
|
|
|
|
|
|
|
|
it 'returns error about mixing only: with rules:' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include /may not be used with `rules`/
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'and rules: is blank' do
|
|
|
|
let(:config) { { only: ['merge_requests'], rules: nil } }
|
|
|
|
|
|
|
|
it 'returns error about mixing only: with rules:' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include /may not be used with `rules`/
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when except: is used with rules:' do
|
|
|
|
let(:config) { { except: { refs: %w[master] }, rules: [{ if: '$THIS' }] } }
|
|
|
|
|
|
|
|
it 'returns error about mixing except: with rules:' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include /may not be used with `rules`/
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'and except: is blank' do
|
|
|
|
let(:config) { { except: nil, rules: [{ if: '$THIS' }] } }
|
|
|
|
|
|
|
|
it 'returns error about mixing except: with rules:' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include /may not be used with `rules`/
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'and rules: is blank' do
|
|
|
|
let(:config) { { except: { refs: %w[master] }, rules: nil } }
|
|
|
|
|
|
|
|
it 'returns error about mixing except: with rules:' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include /may not be used with `rules`/
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when only: and except: are both used with rules:' do
|
|
|
|
let(:config) do
|
|
|
|
{
|
|
|
|
only: %w[merge_requests],
|
|
|
|
except: { refs: %w[master] },
|
|
|
|
rules: [{ if: '$THIS' }]
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns errors about mixing both only: and except: with rules:' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include /may not be used with `rules`/
|
|
|
|
expect(entry.errors).to include /may not be used with `rules`/
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when only: and except: as both blank' do
|
|
|
|
let(:config) do
|
|
|
|
{ only: nil, except: nil, rules: [{ if: '$THIS' }] }
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns errors about mixing both only: and except: with rules:' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include /may not be used with `rules`/
|
|
|
|
expect(entry.errors).to include /may not be used with `rules`/
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when rules: is blank' do
|
|
|
|
let(:config) do
|
|
|
|
{ only: %w[merge_requests], except: { refs: %w[master] }, rules: nil }
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns errors about mixing both only: and except: with rules:' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include /may not be used with `rules`/
|
|
|
|
expect(entry.errors).to include /may not be used with `rules`/
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-12-05 23:21:45 +05:30
|
|
|
context 'when start_in specified without delayed specification' do
|
|
|
|
let(:config) { { start_in: '1 day' } }
|
|
|
|
|
|
|
|
it 'returns error about invalid type' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include 'job start in must be blank'
|
|
|
|
end
|
|
|
|
end
|
2019-10-12 21:52:04 +05:30
|
|
|
|
2022-05-07 20:08:51 +05:30
|
|
|
context 'when it has dependencies' do
|
2019-10-12 21:52:04 +05:30
|
|
|
context 'that are not a array of strings' do
|
|
|
|
let(:config) do
|
|
|
|
{ script: 'echo', dependencies: 'build-job' }
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns error about invalid type' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include 'job dependencies should be an array of strings'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-05-07 20:08:51 +05:30
|
|
|
context 'when the job has needs' do
|
|
|
|
context 'and there are dependencies that are not included in needs' do
|
2019-10-12 21:52:04 +05:30
|
|
|
let(:config) do
|
|
|
|
{
|
|
|
|
stage: 'test',
|
|
|
|
script: 'echo',
|
|
|
|
dependencies: ['another-job'],
|
|
|
|
needs: ['build-job']
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns error about invalid data' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include 'job dependencies the another-job should be part of needs'
|
|
|
|
end
|
2022-05-07 20:08:51 +05:30
|
|
|
|
|
|
|
context 'and they are only cross pipeline needs' do
|
|
|
|
let(:config) do
|
|
|
|
{
|
|
|
|
script: 'echo',
|
|
|
|
dependencies: ['rspec'],
|
|
|
|
needs: [{
|
|
|
|
job: 'rspec',
|
|
|
|
pipeline: 'other'
|
|
|
|
}]
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'adds an error for dependency keyword usage' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include 'job needs corresponding to dependencies must be from the same pipeline'
|
|
|
|
end
|
|
|
|
end
|
2019-10-12 21:52:04 +05:30
|
|
|
end
|
|
|
|
end
|
2019-12-04 20:38:33 +05:30
|
|
|
|
|
|
|
context 'when timeout value is not correct' do
|
|
|
|
context 'when it is higher than instance wide timeout' do
|
2020-01-01 13:55:28 +05:30
|
|
|
let(:config) { { timeout: '3 months', script: 'test' } }
|
2019-12-04 20:38:33 +05:30
|
|
|
|
|
|
|
it 'returns error about value too high' do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors)
|
2020-01-01 13:55:28 +05:30
|
|
|
.to include "timeout config should not exceed the limit"
|
2019-12-04 20:38:33 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when it is not a duration' do
|
2020-01-01 13:55:28 +05:30
|
|
|
let(:config) { { timeout: 100, script: 'test' } }
|
2019-12-04 20:38:33 +05:30
|
|
|
|
|
|
|
it 'returns error about wrong value' do
|
|
|
|
expect(entry).not_to be_valid
|
2020-01-01 13:55:28 +05:30
|
|
|
expect(entry.errors).to include 'timeout config should be a duration'
|
2019-12-04 20:38:33 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when timeout value is correct' do
|
|
|
|
let(:config) { { script: 'echo', timeout: '1m 1s' } }
|
|
|
|
|
|
|
|
it 'returns correct timeout' do
|
|
|
|
expect(entry).to be_valid
|
|
|
|
expect(entry.errors).to be_empty
|
|
|
|
expect(entry.timeout).to eq('1m 1s')
|
|
|
|
end
|
|
|
|
end
|
2020-03-13 15:44:24 +05:30
|
|
|
|
|
|
|
context 'when it is a release' do
|
|
|
|
context 'when `release:description` is missing' do
|
|
|
|
let(:config) do
|
|
|
|
{
|
|
|
|
script: ["make changelog | tee release_changelog.txt"],
|
|
|
|
release: {
|
|
|
|
tag_name: "v0.06",
|
|
|
|
name: "Release $CI_TAG_NAME"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns error" do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors).to include "release description can't be blank"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2021-11-11 11:23:49 +05:30
|
|
|
|
|
|
|
context 'when invalid rules are used' do
|
|
|
|
let(:config) { { script: 'ls', cache: { key: 'test' }, rules: rules } }
|
|
|
|
|
|
|
|
context 'with rules nested more than max allowed levels' do
|
|
|
|
let(:sample_rule) { { if: '$THIS == "other"', when: 'always' } }
|
|
|
|
|
|
|
|
let(:rules) do
|
|
|
|
[
|
|
|
|
{ if: '$THIS == "that"', when: 'always' },
|
|
|
|
[
|
|
|
|
{ if: '$SKIP', when: 'never' },
|
|
|
|
[
|
|
|
|
sample_rule,
|
|
|
|
[
|
|
|
|
sample_rule,
|
|
|
|
[
|
|
|
|
sample_rule,
|
|
|
|
[
|
|
|
|
sample_rule,
|
|
|
|
[
|
|
|
|
sample_rule,
|
|
|
|
[
|
|
|
|
sample_rule,
|
|
|
|
[
|
|
|
|
sample_rule,
|
|
|
|
[
|
|
|
|
sample_rule,
|
|
|
|
[
|
|
|
|
sample_rule,
|
|
|
|
[
|
|
|
|
sample_rule,
|
|
|
|
[sample_rule]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
it { expect(entry).not_to be_valid }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with rules with invalid keys' do
|
|
|
|
let(:rules) do
|
|
|
|
[
|
|
|
|
{ invalid_key: 'invalid' },
|
|
|
|
[
|
|
|
|
{ if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' },
|
|
|
|
{ if: '$CI_PIPELINE_SOURCE == "merge_request_event"' }
|
|
|
|
]
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
it { expect(entry).not_to be_valid }
|
|
|
|
end
|
|
|
|
end
|
2016-09-13 17:45:13 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-09-29 09:46:39 +05:30
|
|
|
describe '#relevant?' do
|
|
|
|
it 'is a relevant entry' do
|
2018-11-20 20:47:30 +05:30
|
|
|
entry = described_class.new({ script: 'rspec' }, name: :rspec)
|
|
|
|
|
2016-09-29 09:46:39 +05:30
|
|
|
expect(entry).to be_relevant
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#compose!' do
|
|
|
|
let(:specified) do
|
|
|
|
double('specified', 'specified?' => true, value: 'specified')
|
|
|
|
end
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
let(:unspecified) { double('unspecified', 'specified?' => false) }
|
|
|
|
let(:default) { double('default', '[]' => unspecified) }
|
2020-01-01 13:55:28 +05:30
|
|
|
let(:workflow) { double('workflow', 'has_rules?' => false) }
|
2020-04-08 14:13:33 +05:30
|
|
|
|
|
|
|
let(:deps) do
|
|
|
|
double('deps',
|
|
|
|
'default_entry' => default,
|
2022-10-11 01:57:18 +05:30
|
|
|
'workflow_entry' => workflow)
|
2020-04-08 14:13:33 +05:30
|
|
|
end
|
2016-09-29 09:46:39 +05:30
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
context 'when job config overrides default config' do
|
2017-09-10 17:25:29 +05:30
|
|
|
before do
|
|
|
|
entry.compose!(deps)
|
|
|
|
end
|
2016-09-29 09:46:39 +05:30
|
|
|
|
2016-09-13 17:45:13 +05:30
|
|
|
let(:config) do
|
2017-08-17 22:00:37 +05:30
|
|
|
{ script: 'rspec', image: 'some_image', cache: { key: 'test' } }
|
2016-09-29 09:46:39 +05:30
|
|
|
end
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
it 'overrides default config' do
|
2017-09-10 17:25:29 +05:30
|
|
|
expect(entry[:image].value).to eq(name: 'some_image')
|
2021-04-17 20:07:23 +05:30
|
|
|
expect(entry[:cache].value).to eq([key: 'test', policy: 'pull-push', when: 'on_success'])
|
2016-09-29 09:46:39 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
context 'when job config does not override default config' do
|
2016-09-29 09:46:39 +05:30
|
|
|
before do
|
2019-09-30 21:07:59 +05:30
|
|
|
allow(default).to receive('[]').with(:image).and_return(specified)
|
|
|
|
|
2016-09-29 09:46:39 +05:30
|
|
|
entry.compose!(deps)
|
2016-09-13 17:45:13 +05:30
|
|
|
end
|
|
|
|
|
2016-09-29 09:46:39 +05:30
|
|
|
let(:config) { { script: 'ls', cache: { key: 'test' } } }
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
it 'uses config from default entry' do
|
2016-09-29 09:46:39 +05:30
|
|
|
expect(entry[:image].value).to eq 'specified'
|
2021-04-17 20:07:23 +05:30
|
|
|
expect(entry[:cache].value).to eq([key: 'test', policy: 'pull-push', when: 'on_success'])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-01-01 13:55:28 +05:30
|
|
|
context 'with workflow rules' do
|
|
|
|
using RSpec::Parameterized::TableSyntax
|
|
|
|
|
|
|
|
where(:name, :has_workflow_rules?, :only, :rules, :result) do
|
|
|
|
"uses default only" | false | nil | nil | { refs: %w[branches tags] }
|
|
|
|
"uses user only" | false | %w[branches] | nil | { refs: %w[branches] }
|
|
|
|
"does not define only" | false | nil | [] | nil
|
|
|
|
"does not define only" | true | nil | nil | nil
|
|
|
|
"uses user only" | true | %w[branches] | nil | { refs: %w[branches] }
|
|
|
|
"does not define only" | true | nil | [] | nil
|
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
let(:config) { { script: 'ls', rules: rules, only: only }.compact }
|
|
|
|
|
2023-01-13 00:05:48 +05:30
|
|
|
it name.to_s do
|
2020-01-01 13:55:28 +05:30
|
|
|
expect(workflow).to receive(:has_rules?) { has_workflow_rules? }
|
|
|
|
|
|
|
|
entry.compose!(deps)
|
|
|
|
|
|
|
|
expect(entry.only_value).to eq(result)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when workflow rules is used' do
|
|
|
|
context 'when rules are used' do
|
|
|
|
let(:config) { { script: 'ls', cache: { key: 'test' }, rules: [] } }
|
|
|
|
|
|
|
|
it 'does not define only' do
|
|
|
|
expect(entry).not_to be_only_defined
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when rules are not used' do
|
|
|
|
let(:config) { { script: 'ls', cache: { key: 'test' }, only: [] } }
|
|
|
|
|
|
|
|
it 'does not define only' do
|
|
|
|
expect(entry).not_to be_only_defined
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-09-13 17:45:13 +05:30
|
|
|
end
|
|
|
|
|
2016-09-29 09:46:39 +05:30
|
|
|
context 'when composed' do
|
2017-09-10 17:25:29 +05:30
|
|
|
before do
|
|
|
|
entry.compose!
|
|
|
|
end
|
2016-09-29 09:46:39 +05:30
|
|
|
|
|
|
|
describe '#value' do
|
2017-09-10 17:25:29 +05:30
|
|
|
before do
|
|
|
|
entry.compose!
|
|
|
|
end
|
2016-09-29 09:46:39 +05:30
|
|
|
|
|
|
|
context 'when entry is correct' do
|
|
|
|
let(:config) do
|
|
|
|
{ before_script: %w[ls pwd],
|
|
|
|
script: 'rspec',
|
2023-03-04 22:38:38 +05:30
|
|
|
after_script: %w[cleanup],
|
|
|
|
id_tokens: { TEST_ID_TOKEN: { aud: 'https://gitlab.com' } },
|
|
|
|
hooks: { pre_get_sources_script: 'echo hello' } }
|
2016-09-29 09:46:39 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns correct value' do
|
|
|
|
expect(entry.value)
|
|
|
|
.to eq(name: :rspec,
|
|
|
|
before_script: %w[ls pwd],
|
|
|
|
script: %w[rspec],
|
|
|
|
stage: 'test',
|
2017-08-17 22:00:37 +05:30
|
|
|
ignore: false,
|
2019-02-15 15:39:39 +05:30
|
|
|
after_script: %w[cleanup],
|
2023-03-04 22:38:38 +05:30
|
|
|
hooks: { pre_get_sources_script: ['echo hello'] },
|
2019-09-30 21:07:59 +05:30
|
|
|
only: { refs: %w[branches tags] },
|
2021-04-29 21:17:54 +05:30
|
|
|
job_variables: {},
|
|
|
|
root_variables_inheritance: true,
|
2023-03-04 22:38:38 +05:30
|
|
|
scheduling_type: :stage,
|
|
|
|
id_tokens: { TEST_ID_TOKEN: { aud: 'https://gitlab.com' } })
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when the FF ci_hooks_pre_get_sources_script is disabled' do
|
|
|
|
before do
|
|
|
|
stub_feature_flags(ci_hooks_pre_get_sources_script: false)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns correct value' do
|
|
|
|
expect(entry.value)
|
|
|
|
.to eq(name: :rspec,
|
|
|
|
before_script: %w[ls pwd],
|
|
|
|
script: %w[rspec],
|
|
|
|
stage: 'test',
|
|
|
|
ignore: false,
|
|
|
|
after_script: %w[cleanup],
|
|
|
|
only: { refs: %w[branches tags] },
|
|
|
|
job_variables: {},
|
|
|
|
root_variables_inheritance: true,
|
|
|
|
scheduling_type: :stage,
|
|
|
|
id_tokens: { TEST_ID_TOKEN: { aud: 'https://gitlab.com' } })
|
|
|
|
end
|
2016-09-29 09:46:39 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2021-11-11 11:23:49 +05:30
|
|
|
|
|
|
|
context 'when job is using tags' do
|
|
|
|
context 'when limit is reached' do
|
|
|
|
let(:tags) { Array.new(100) { |i| "tag-#{i}" } }
|
|
|
|
let(:config) { { tags: tags, script: 'test' } }
|
|
|
|
|
|
|
|
it 'returns error', :aggregate_failures do
|
|
|
|
expect(entry).not_to be_valid
|
|
|
|
expect(entry.errors)
|
|
|
|
.to include "tags config must be less than the limit of #{Gitlab::Ci::Config::Entry::Tags::TAGS_LIMIT} tags"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when limit is not reached' do
|
|
|
|
let(:config) { { tags: %w[tag1 tag2], script: 'test' } }
|
|
|
|
|
|
|
|
it 'returns a valid entry', :aggregate_failures do
|
|
|
|
expect(entry).to be_valid
|
|
|
|
expect(entry.errors).to be_empty
|
|
|
|
expect(entry.tags).to eq(%w[tag1 tag2])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-09-13 17:45:13 +05:30
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
describe '#manual_action?' do
|
|
|
|
context 'when job is a manual action' do
|
|
|
|
let(:config) { { script: 'deploy', when: 'manual' } }
|
|
|
|
|
|
|
|
it 'is a manual action' do
|
|
|
|
expect(entry).to be_manual_action
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when job is not a manual action' do
|
|
|
|
let(:config) { { script: 'deploy' } }
|
|
|
|
|
|
|
|
it 'is not a manual action' do
|
|
|
|
expect(entry).not_to be_manual_action
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-12-05 23:21:45 +05:30
|
|
|
describe '#delayed?' do
|
|
|
|
context 'when job is a delayed' do
|
|
|
|
let(:config) { { script: 'deploy', when: 'delayed' } }
|
|
|
|
|
|
|
|
it 'is a delayed' do
|
|
|
|
expect(entry).to be_delayed
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when job is not a delayed' do
|
|
|
|
let(:config) { { script: 'deploy' } }
|
|
|
|
|
|
|
|
it 'is not a delayed' do
|
|
|
|
expect(entry).not_to be_delayed
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
describe '#ignored?' do
|
2021-02-22 17:27:13 +05:30
|
|
|
before do
|
|
|
|
entry.compose!
|
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
context 'when job is a manual action' do
|
|
|
|
context 'when it is not specified if job is allowed to fail' do
|
|
|
|
let(:config) do
|
|
|
|
{ script: 'deploy', when: 'manual' }
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'is an ignored job' do
|
|
|
|
expect(entry).to be_ignored
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when job is allowed to fail' do
|
|
|
|
let(:config) do
|
|
|
|
{ script: 'deploy', when: 'manual', allow_failure: true }
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'is an ignored job' do
|
|
|
|
expect(entry).to be_ignored
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when job is not allowed to fail' do
|
|
|
|
let(:config) do
|
|
|
|
{ script: 'deploy', when: 'manual', allow_failure: false }
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'is not an ignored job' do
|
|
|
|
expect(entry).not_to be_ignored
|
|
|
|
end
|
|
|
|
end
|
2021-02-22 17:27:13 +05:30
|
|
|
|
|
|
|
context 'when job is dynamically allowed to fail' do
|
|
|
|
let(:config) do
|
|
|
|
{ script: 'deploy', when: 'manual', allow_failure: { exit_codes: 42 } }
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'is not an ignored job' do
|
|
|
|
expect(entry).not_to be_ignored
|
|
|
|
end
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'when job is not a manual action' do
|
|
|
|
context 'when it is not specified if job is allowed to fail' do
|
|
|
|
let(:config) { { script: 'deploy' } }
|
|
|
|
|
|
|
|
it 'is not an ignored job' do
|
|
|
|
expect(entry).not_to be_ignored
|
|
|
|
end
|
2021-02-22 17:27:13 +05:30
|
|
|
|
|
|
|
it 'does not return allow_failure' do
|
|
|
|
expect(entry.value.key?(:allow_failure_criteria)).to be_falsey
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'when job is allowed to fail' do
|
|
|
|
let(:config) { { script: 'deploy', allow_failure: true } }
|
|
|
|
|
|
|
|
it 'is an ignored job' do
|
|
|
|
expect(entry).to be_ignored
|
|
|
|
end
|
2021-02-22 17:27:13 +05:30
|
|
|
|
|
|
|
it 'does not return allow_failure_criteria' do
|
|
|
|
expect(entry.value.key?(:allow_failure_criteria)).to be_falsey
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'when job is not allowed to fail' do
|
|
|
|
let(:config) { { script: 'deploy', allow_failure: false } }
|
|
|
|
|
|
|
|
it 'is not an ignored job' do
|
|
|
|
expect(entry).not_to be_ignored
|
|
|
|
end
|
2021-02-22 17:27:13 +05:30
|
|
|
|
|
|
|
it 'does not return allow_failure_criteria' do
|
|
|
|
expect(entry.value.key?(:allow_failure_criteria)).to be_falsey
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when job is dynamically allowed to fail' do
|
|
|
|
let(:config) { { script: 'deploy', allow_failure: { exit_codes: 42 } } }
|
|
|
|
|
|
|
|
it 'is not an ignored job' do
|
|
|
|
expect(entry).not_to be_ignored
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns allow_failure_criteria' do
|
|
|
|
expect(entry.value[:allow_failure_criteria]).to match(exit_codes: [42])
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-09-13 17:45:13 +05:30
|
|
|
end
|