debian-mirror-gitlab/spec/lib/gitlab/ci/config/entry/processable_spec.rb
2021-02-22 17:27:13 +05:30

473 lines
14 KiB
Ruby

# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::Entry::Processable do
let(:node_class) do
Class.new(::Gitlab::Config::Entry::Node) do
include Gitlab::Ci::Config::Entry::Processable
entry :tags, ::Gitlab::Config::Entry::ArrayOfStrings,
description: 'Set the default tags.',
inherit: true
def self.name
'job'
end
end
end
let(:entry) { node_class.new(config, name: :rspec) }
describe 'validations' do
before do
entry.compose!
end
context 'when entry config value is correct' do
let(:config) { { stage: 'test' } }
describe '#valid?' do
it 'is valid' do
expect(entry).to be_valid
end
end
context 'when job name is empty' do
let(:entry) { node_class.new(config, name: ''.to_sym) }
it 'reports error' do
expect(entry.errors).to include "job name can't be blank"
end
end
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 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
expect(entry.errors).to include "job extends should be an array of strings or a string"
end
end
context 'when it uses both "when:" and "rules:"' do
let(:config) do
{
script: 'echo',
when: 'on_failure',
rules: [{ if: '$VARIABLE', when: 'on_success' }]
}
end
it 'returns an error about when: being combined with rules' do
expect(entry).not_to be_valid
expect(entry.errors).to include 'job config key may not be used with `rules`: when'
end
end
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
end
end
describe '#relevant?' do
it 'is a relevant entry' do
entry = node_class.new({ stage: 'test' }, name: :rspec)
expect(entry).to be_relevant
end
end
describe '#compose!' do
let(:unspecified) { double('unspecified', 'specified?' => false) }
let(:default) { double('default', '[]' => unspecified) }
let(:workflow) { double('workflow', 'has_rules?' => false) }
let(:variables) { }
let(:deps) do
double('deps',
default_entry: default,
workflow_entry: workflow,
variables_value: variables)
end
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 }
it "#{name}" do
expect(workflow).to receive(:has_rules?) { has_workflow_rules? }
entry.compose!(deps)
expect(entry.only_value).to eq(result)
end
end
end
shared_examples 'has no warnings' do
it 'does not raise the warning' do
expect(entry.warnings).to be_empty
end
end
context 'when workflow rules is used' do
let(:workflow) { double('workflow', 'has_rules?' => true) }
before do
entry.compose!(deps)
end
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 and only is defined' do
let(:config) { { script: 'ls', cache: { key: 'test' }, only: [] } }
it 'keeps only entry' do
expect(entry).to be_only_defined
end
end
end
context 'when workflow rules is not used' do
let(:workflow) { double('workflow', 'has_rules?' => false) }
let(:feature_flag_value) { true }
before do
stub_feature_flags(ci_raise_job_rules_without_workflow_rules_warning: feature_flag_value)
entry.compose!(deps)
end
context 'when rules are valid' do
let(:config) do
{
script: 'ls',
rules: [
{ if: '$CI_COMMIT_BRANCH', when: 'on_success' },
last_rule
]
}
end
context 'when last rule contains only `when`' do
let(:last_rule) { { when: when_value } }
context 'and its value is not `never`' do
let(:when_value) { 'on_success' }
it 'raises a warning' do
expect(entry.warnings).to contain_exactly(/may allow multiple pipelines/)
end
context 'when feature flag is disabled' do
let(:feature_flag_value) { false }
it_behaves_like 'has no warnings'
end
end
context 'and its value is `never`' do
let(:when_value) { 'never' }
it_behaves_like 'has no warnings'
end
end
context 'when last rule does not contain only `when`' do
let(:last_rule) { { if: '$CI_MERGE_REQUEST_ID', when: 'always' } }
it_behaves_like 'has no warnings'
end
end
context 'when rules are invalid' do
let(:config) { { script: 'ls', rules: { when: 'always' } } }
it_behaves_like 'has no warnings'
end
end
context 'when workflow rules is used' do
let(:workflow) { double('workflow', 'has_rules?' => true) }
before do
entry.compose!(deps)
end
context 'when last rule contains only `when' do
let(:config) do
{
script: 'ls',
rules: [
{ if: '$CI_COMMIT_BRANCH', when: 'on_success' },
{ when: 'always' }
]
}
end
it_behaves_like 'has no warnings'
end
end
context 'with inheritance' do
context 'of variables' do
let(:config) do
{ variables: { A: 'job', B: 'job' } }
end
before do
entry.compose!(deps)
end
context 'with only job variables' do
it 'does return defined variables' do
expect(entry.value).to include(
variables: { 'A' => 'job', 'B' => 'job' }
)
end
end
context 'when root yaml variables are used' do
let(:variables) do
Gitlab::Ci::Config::Entry::Variables.new(
{ A: 'root', C: 'root', D: 'root' }
).value
end
it 'does return all variables and overwrite them' do
expect(entry.value).to include(
variables: { 'A' => 'job', 'B' => 'job', 'C' => 'root', 'D' => 'root' }
)
end
context 'when inherit of defaults is disabled' do
let(:config) do
{
variables: { A: 'job', B: 'job' },
inherit: { variables: false }
}
end
it 'does return only job variables' do
expect(entry.value).to include(
variables: { 'A' => 'job', 'B' => 'job' }
)
end
end
context 'when inherit of only specific variable is enabled' do
let(:config) do
{
variables: { A: 'job', B: 'job' },
inherit: { variables: ['D'] }
}
end
it 'does return only job variables' do
expect(entry.value).to include(
variables: { 'A' => 'job', 'B' => 'job', 'D' => 'root' }
)
end
end
end
end
context 'of default:tags' do
using RSpec::Parameterized::TableSyntax
where(:name, :default_tags, :tags, :inherit_default, :result) do
"only local tags" | nil | %w[a b] | nil | %w[a b]
"only local tags" | nil | %w[a b] | true | %w[a b]
"only local tags" | nil | %w[a b] | false | %w[a b]
"global and local tags" | %w[b c] | %w[a b] | nil | %w[a b]
"global and local tags" | %w[b c] | %w[a b] | true | %w[a b]
"global and local tags" | %w[b c] | %w[a b] | false | %w[a b]
"only global tags" | %w[b c] | nil | nil | %w[b c]
"only global tags" | %w[b c] | nil | true | %w[b c]
"only global tags" | %w[b c] | nil | false | nil
"only global tags" | %w[b c] | nil | %w[image] | nil
"only global tags" | %w[b c] | nil | %w[tags] | %w[b c]
end
with_them do
let(:config) do
{ tags: tags,
inherit: { default: inherit_default } }
end
let(:default_specified_tags) do
double('tags',
'specified?' => true,
'valid?' => true,
'value' => default_tags,
'errors' => [])
end
before do
allow(default).to receive('[]').with(:tags).and_return(default_specified_tags)
entry.compose!(deps)
expect(entry).to be_valid
end
it { expect(entry.tags_value).to eq(result) }
end
end
end
end
context 'when composed' do
before do
entry.compose!
end
describe '#value' do
context 'when entry is correct' do
let(:config) do
{ stage: 'test' }
end
it 'returns correct value' do
expect(entry.value).to eq(
name: :rspec,
stage: 'test',
only: { refs: %w[branches tags] },
variables: {}
)
end
end
end
end
end