Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib/gitlab/ci/config/interpolation/inputs_spec.rb')
-rw-r--r--spec/lib/gitlab/ci/config/interpolation/inputs_spec.rb409
1 files changed, 291 insertions, 118 deletions
diff --git a/spec/lib/gitlab/ci/config/interpolation/inputs_spec.rb b/spec/lib/gitlab/ci/config/interpolation/inputs_spec.rb
index ea06f181fa4..b0618081207 100644
--- a/spec/lib/gitlab/ci/config/interpolation/inputs_spec.rb
+++ b/spec/lib/gitlab/ci/config/interpolation/inputs_spec.rb
@@ -7,130 +7,303 @@ RSpec.describe Gitlab::Ci::Config::Interpolation::Inputs, feature_category: :pip
let(:specs) { { foo: { default: 'bar' } } }
let(:args) { {} }
- context 'when inputs are valid' do
- where(:specs, :args, :merged) do
- [
- [
- { foo: { default: 'bar' } }, {},
- { foo: 'bar' }
- ],
- [
- { foo: { default: 'bar' } }, { foo: 'test' },
- { foo: 'test' }
- ],
- [
- { foo: nil }, { foo: 'bar' },
- { foo: 'bar' }
- ],
- [
- { foo: { type: 'string' } }, { foo: 'bar' },
- { foo: 'bar' }
- ],
- [
- { foo: { type: 'string', default: 'bar' } }, { foo: 'test' },
- { foo: 'test' }
- ],
- [
- { foo: { type: 'string', default: 'bar' } }, {},
- { foo: 'bar' }
- ],
- [
- { foo: { default: 'bar' }, baz: nil }, { baz: 'test' },
- { foo: 'bar', baz: 'test' }
- ],
- [
- { number_input: { type: 'number' } },
- { number_input: 8 },
- { number_input: 8 }
- ],
- [
- { default_number_input: { default: 9, type: 'number' } },
- {},
- { default_number_input: 9 }
- ],
- [
- { true_input: { type: 'boolean' }, false_input: { type: 'boolean' } },
- { true_input: true, false_input: false },
- { true_input: true, false_input: false }
- ],
- [
- { default_boolean_input: { default: true, type: 'boolean' } },
- {},
- { default_boolean_input: true }
- ]
- ]
- end
-
- with_them do
- it 'contains the merged inputs' do
+ context 'when given unrecognized inputs' do
+ let(:specs) { { foo: nil } }
+ let(:args) { { foo: 'bar', test: 'bar' } }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly('unknown input arguments: test')
+ end
+ end
+
+ context 'when given unrecognized configuration keywords' do
+ let(:specs) { { foo: 123 } }
+ let(:args) { {} }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly(
+ 'unknown input specification for `foo` (valid types: boolean, number, string)'
+ )
+ end
+ end
+
+ context 'when the inputs have multiple errors' do
+ let(:specs) { { foo: nil } }
+ let(:args) { { test: 'bar', gitlab: '1' } }
+
+ it 'reports all of them' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly(
+ 'unknown input arguments: test, gitlab',
+ '`foo` input: required value has not been provided'
+ )
+ end
+ end
+
+ describe 'required inputs' do
+ let(:specs) { { foo: nil } }
+
+ context 'when a value is given' do
+ let(:args) { { foo: 'bar' } }
+
+ it 'is valid' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(foo: 'bar')
+ end
+ end
+
+ context 'when no value is given' do
+ let(:args) { {} }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly('`foo` input: required value has not been provided')
+ end
+ end
+ end
+
+ describe 'inputs with a default value' do
+ let(:specs) { { foo: { default: 'bar' } } }
+
+ context 'when a value is given' do
+ let(:args) { { foo: 'test' } }
+
+ it 'uses the given value' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(foo: 'test')
+ end
+ end
+
+ context 'when no value is given' do
+ let(:args) { {} }
+
+ it 'uses the default value' do
expect(inputs).to be_valid
- expect(inputs.to_hash).to eq(merged)
+ expect(inputs.to_hash).to eq(foo: 'bar')
end
end
end
- context 'when inputs are invalid' do
- where(:specs, :args, :errors) do
- [
- [
- { foo: nil }, { foo: 'bar', test: 'bar' },
- ['unknown input arguments: test']
- ],
- [
- { foo: nil }, { test: 'bar', gitlab: '1' },
- ['unknown input arguments: test, gitlab', '`foo` input: required value has not been provided']
- ],
- [
- { foo: 123 }, {},
- ['unknown input specification for `foo` (valid types: boolean, number, string)']
- ],
- [
- { a: nil, foo: 123 }, { a: '123' },
- ['unknown input specification for `foo` (valid types: boolean, number, string)']
- ],
- [
- { foo: nil }, {},
- ['`foo` input: required value has not been provided']
- ],
- [
- { foo: { default: 123 } }, { foo: 'test' },
- ['`foo` input: default value is not a string']
- ],
- [
- { foo: { default: 'test' } }, { foo: 123 },
- ['`foo` input: provided value is not a string']
- ],
- [
- { foo: nil }, { foo: 123 },
- ['`foo` input: provided value is not a string']
- ],
- [
- { number_input: { type: 'number' } },
- { number_input: 'NaN' },
- ['`number_input` input: provided value is not a number']
- ],
- [
- { default_number_input: { default: 'NaN', type: 'number' } },
- {},
- ['`default_number_input` input: default value is not a number']
- ],
- [
- { boolean_input: { type: 'boolean' } },
- { boolean_input: 'string' },
- ['`boolean_input` input: provided value is not a boolean']
- ],
- [
- { default_boolean_input: { default: 'string', type: 'boolean' } },
- {},
- ['`default_boolean_input` input: default value is not a boolean']
- ]
- ]
- end
-
- with_them do
- it 'contains the merged inputs', :aggregate_failures do
+ describe 'inputs with type validation' do
+ describe 'string validation' do
+ let(:specs) { { a_input: nil, b_input: { default: 'test' }, c_input: { default: 123 } } }
+ let(:args) { { a_input: 123, b_input: 123, c_input: 'test' } }
+
+ it 'is the default type' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly(
+ '`a_input` input: provided value is not a string',
+ '`b_input` input: provided value is not a string',
+ '`c_input` input: default value is not a string'
+ )
+ end
+
+ context 'when the value is a string' do
+ let(:specs) { { foo: { type: 'string' } } }
+ let(:args) { { foo: 'bar' } }
+
+ it 'is valid' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(foo: 'bar')
+ end
+ end
+
+ context 'when the default is a string' do
+ let(:specs) { { foo: { type: 'string', default: 'bar' } } }
+ let(:args) { {} }
+
+ it 'is valid' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(foo: 'bar')
+ end
+ end
+
+ context 'when the value is not a string' do
+ let(:specs) { { foo: { type: 'string' } } }
+ let(:args) { { foo: 123 } }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly('`foo` input: provided value is not a string')
+ end
+ end
+
+ context 'when the default is not a string' do
+ let(:specs) { { foo: { default: 123, type: 'string' } } }
+ let(:args) { {} }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly('`foo` input: default value is not a string')
+ end
+ end
+ end
+
+ describe 'number validation' do
+ let(:specs) { { integer: { type: 'number' }, float: { type: 'number' } } }
+
+ context 'when the value is a float or integer' do
+ let(:args) { { integer: 6, float: 6.6 } }
+
+ it 'is valid' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(integer: 6, float: 6.6)
+ end
+ end
+
+ context 'when the default is a float or integer' do
+ let(:specs) { { integer: { default: 6, type: 'number' }, float: { default: 6.6, type: 'number' } } }
+
+ it 'is valid' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(integer: 6, float: 6.6)
+ end
+ end
+
+ context 'when the value is not a number' do
+ let(:specs) { { number_input: { type: 'number' } } }
+ let(:args) { { number_input: 'NaN' } }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly('`number_input` input: provided value is not a number')
+ end
+ end
+
+ context 'when the default is not a number' do
+ let(:specs) { { number_input: { default: 'NaN', type: 'number' } } }
+ let(:args) { {} }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly('`number_input` input: default value is not a number')
+ end
+ end
+ end
+
+ describe 'boolean validation' do
+ context 'when the value is true or false' do
+ let(:specs) { { truthy: { type: 'boolean' }, falsey: { type: 'boolean' } } }
+ let(:args) { { truthy: true, falsey: false } }
+
+ it 'is valid' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(truthy: true, falsey: false)
+ end
+ end
+
+ context 'when the default is true or false' do
+ let(:specs) { { truthy: { default: true, type: 'boolean' }, falsey: { default: false, type: 'boolean' } } }
+ let(:args) { {} }
+
+ it 'is valid' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(truthy: true, falsey: false)
+ end
+ end
+
+ context 'when the value is not a boolean' do
+ let(:specs) { { boolean_input: { type: 'boolean' } } }
+ let(:args) { { boolean_input: 'string' } }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly('`boolean_input` input: provided value is not a boolean')
+ end
+ end
+
+ context 'when the default is not a boolean' do
+ let(:specs) { { boolean_input: { default: 'string', type: 'boolean' } } }
+ let(:args) { {} }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly('`boolean_input` input: default value is not a boolean')
+ end
+ end
+ end
+
+ context 'when given an unknown type' do
+ let(:specs) { { unknown: { type: 'datetime' } } }
+ let(:args) { { unknown: '2023-10-31' } }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly(
+ 'unknown input specification for `unknown` (valid types: boolean, number, string)'
+ )
+ end
+ end
+ end
+
+ describe 'inputs with RegEx validation' do
+ context 'when given a value that matches the pattern' do
+ let(:specs) { { test_input: { regex: '^input_value$' } } }
+ let(:args) { { test_input: 'input_value' } }
+
+ it 'is valid' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(test_input: 'input_value')
+ end
+ end
+
+ context 'when given a default that matches the pattern' do
+ let(:specs) { { test_input: { default: 'input_value', regex: '^input_value$' } } }
+ let(:args) { {} }
+
+ it 'is valid' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(test_input: 'input_value')
+ end
+ end
+
+ context 'when given a value that does not match the pattern' do
+ let(:specs) { { test_input: { regex: '^input_value$' } } }
+ let(:args) { { test_input: 'input' } }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly(
+ '`test_input` input: provided value does not match required RegEx pattern'
+ )
+ end
+ end
+
+ context 'when given a default that does not match the pattern' do
+ let(:specs) { { test_input: { default: 'input', regex: '^input_value$' } } }
+ let(:args) { {} }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly(
+ '`test_input` input: default value does not match required RegEx pattern'
+ )
+ end
+ end
+
+ context 'when used with any type other than `string`' do
+ let(:specs) { { test_input: { regex: '^input_value$', type: 'number' } } }
+ let(:args) { { test_input: 999 } }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly(
+ '`test_input` input: RegEx validation can only be used with string inputs'
+ )
+ end
+ end
+
+ context 'when the pattern is unsafe' do
+ let(:specs) { { test_input: { regex: 'a++' } } }
+ let(:args) { { test_input: 'aaaaaaaaaaaaaaaaaaaaa' } }
+
+ it 'is invalid' do
expect(inputs).not_to be_valid
- expect(inputs.errors).to contain_exactly(*errors)
+ expect(inputs.errors).to contain_exactly(
+ '`test_input` input: invalid regular expression'
+ )
end
end
end