diff --git a/lib/ci/inputs/base_input.rb b/lib/ci/inputs/base_input.rb index 65bf073e321517eb9397eeb6a843dcdb945045ca..bb4cdc2fb990d1ba7a689a6ed0a929f094e19d07 100644 --- a/lib/ci/inputs/base_input.rb +++ b/lib/ci/inputs/base_input.rb @@ -122,7 +122,7 @@ def has_default_through_rules? spec[:rules].any? do |rule| next false unless rule.is_a?(Hash) - rule.key?(:default) || !rule.key?(:if) + rule.key?(:default) end end diff --git a/spec/lib/ci/inputs/base_input_spec.rb b/spec/lib/ci/inputs/base_input_spec.rb index b0f1872fa373bdc8eb9124d7e877befa683ca61e..f1ba9df8c86b6e8d9f16088ad07a900ce68d91b1 100644 --- a/spec/lib/ci/inputs/base_input_spec.rb +++ b/spec/lib/ci/inputs/base_input_spec.rb @@ -107,9 +107,9 @@ def self.type_name end end - context 'when there is a fallback rule' do + context 'when there is a fallback rule with a default' do it 'is not required' do - spec = { rules: [{ options: %w[a b] }] } + spec = { rules: [{ options: %w[a b], default: 'a' }] } input = described_class.new(name: :test, spec: spec) expect(input).not_to be_required end @@ -156,4 +156,23 @@ def self.type_name end end end + + describe 'rules-based inputs without explicit defaults' do + let(:input) { described_class.new(name: :test_input, spec: spec) } + + context 'when fallback rule has options but no default' do + let(:spec) do + { + type: 'string', + rules: [ + { options: %w[x y] } + ] + } + end + + it 'is required' do + expect(input).to be_required + end + end + end end diff --git a/spec/requests/api/graphql/project/ci/pipeline_creation/input_spec.rb b/spec/requests/api/graphql/project/ci/pipeline_creation/input_spec.rb index f3d8523b76e1c0e305a9911dc0cd547141472f2e..460dbb6e2bc62a3fb05e0804f301b3a9fa8f9017 100644 --- a/spec/requests/api/graphql/project/ci/pipeline_creation/input_spec.rb +++ b/spec/requests/api/graphql/project/ci/pipeline_creation/input_spec.rb @@ -338,6 +338,173 @@ end end end + + context 'when inputs have mixed types and rules' do + let_it_be(:config_yaml_comprehensive) do + <<~YAML + spec: + inputs: + string_with_description: + type: string + description: 'A string input with description' + default: 'test-value' + string_with_options: + type: string + options: ['option1', 'option2', 'option3'] + default: 'option1' + string_with_regex: + type: string + regex: '^[a-z]+$' + default: 'abc' + number_input: + type: number + default: 42 + boolean_input: + type: boolean + default: true + required_input: + type: string + rules_based_input: + type: string + rules: + - if: '$[[ inputs.string_with_options ]] == "option1"' + options: ['a', 'b'] + default: 'a' + - if: '$[[ inputs.string_with_options ]] == "option2"' + options: ['c', 'd'] + default: 'c' + --- + job: + script: echo "All input types" + YAML + end + + let(:comprehensive_query) do + <<~GQL + query { + project(fullPath: "#{project.full_path}") { + ciPipelineCreationInputs(ref: "#{ref}") { + name + type + description + required + default + options + regex + rules { + if + options + default + } + } + } + } + GQL + end + + let(:ref) { 'feature-comprehensive' } + + before_all do + project.repository.create_file( + project.creator, + '.gitlab-ci.yml', + config_yaml_comprehensive, + message: 'Add comprehensive inputs CI', + branch_name: 'feature-comprehensive') + end + + context 'when feature flag is enabled' do + before do + stub_feature_flags(ci_dynamic_pipeline_inputs: project) + end + + it 'returns all input types correctly formatted as hashes' do + post_graphql(comprehensive_query, current_user: user) + + inputs = graphql_data['project']['ciPipelineCreationInputs'] + + expect(inputs).to contain_exactly( + a_hash_including( + 'name' => 'string_with_description', + 'type' => 'STRING', + 'description' => 'A string input with description', + 'default' => 'test-value', + 'required' => false, + 'options' => nil, + 'regex' => nil, + 'rules' => nil + ), + a_hash_including( + 'name' => 'string_with_options', + 'type' => 'STRING', + 'default' => 'option1', + 'options' => %w[option1 option2 option3], + 'required' => false, + 'regex' => nil, + 'rules' => nil + ), + a_hash_including( + 'name' => 'string_with_regex', + 'type' => 'STRING', + 'default' => 'abc', + 'regex' => '^[a-z]+$', + 'required' => false, + 'options' => nil, + 'rules' => nil + ), + a_hash_including( + 'name' => 'number_input', + 'type' => 'NUMBER', + 'default' => 42, + 'required' => false, + 'options' => nil, + 'regex' => nil, + 'rules' => nil + ), + a_hash_including( + 'name' => 'boolean_input', + 'type' => 'BOOLEAN', + 'default' => true, + 'required' => false, + 'options' => nil, + 'regex' => nil, + 'rules' => nil + ), + a_hash_including( + 'name' => 'required_input', + 'type' => 'STRING', + 'required' => true, + 'default' => nil, + 'options' => nil, + 'regex' => nil, + 'rules' => nil + ), + a_hash_including( + 'name' => 'rules_based_input', + 'type' => 'STRING', + 'required' => false, + 'rules' => contain_exactly( + a_hash_including( + 'if' => '$[[ inputs.string_with_options ]] == "option1"', + 'options' => %w[a b], + 'default' => 'a' + ), + a_hash_including( + 'if' => '$[[ inputs.string_with_options ]] == "option2"', + 'options' => %w[c d], + 'default' => 'c' + ) + ) + ) + ) + end + + it 'successfully queries all input fields' do + expect { post_graphql(comprehensive_query, current_user: user) }.not_to raise_error + expect(graphql_errors).to be_nil + end + end + end end context 'when current user cannot access the project' do