diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-11-17 14:33:21 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-11-17 14:33:21 +0300 |
commit | 7021455bd1ed7b125c55eb1b33c5a01f2bc55ee0 (patch) | |
tree | 5bdc2229f5198d516781f8d24eace62fc7e589e9 /spec/lib/gitlab/ci | |
parent | 185b095e93520f96e9cfc31d9c3e69b498cdab7c (diff) |
Add latest changes from gitlab-org/gitlab@15-6-stable-eev15.6.0-rc42
Diffstat (limited to 'spec/lib/gitlab/ci')
51 files changed, 982 insertions, 523 deletions
diff --git a/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb b/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb index f9ebab149a5..647653f8e9e 100644 --- a/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb +++ b/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb @@ -4,11 +4,37 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Exists do describe '#satisfied_by?' do - shared_examples 'an exists rule with a context' do + subject(:satisfied_by?) { described_class.new(globs).satisfied_by?(nil, context) } + + shared_examples 'a rules:exists with a context' do it_behaves_like 'a glob matching rule' do let(:project) { create(:project, :custom_repo, files: files) } end + context 'when the rules:exists has a variable' do + let_it_be(:project) { create(:project, :custom_repo, files: { 'helm/helm_file.txt' => '' }) } + + let(:globs) { ['$HELM_DIR/**/*'] } + + let(:variables_hash) do + { 'HELM_DIR' => 'helm' } + end + + before do + allow(context).to receive(:variables_hash).and_return(variables_hash) + end + + context 'when the context has the specified variables' do + it { is_expected.to be_truthy } + end + + context 'when variable expansion does not match' do + let(:variables_hash) { {} } + + it { is_expected.to be_falsey } + end + end + context 'after pattern comparision limit is reached' do let(:globs) { ['*definitely_not_a_matching_glob*'] } let(:project) { create(:project, :repository) } @@ -22,26 +48,24 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Exists do end end - subject(:satisfied_by?) { described_class.new(globs).satisfied_by?(nil, context) } - - context 'when context is Build::Context::Build' do - it_behaves_like 'an exists rule with a context' do + context 'when the rules are being evaluated at job level' do + it_behaves_like 'a rules:exists with a context' do let(:pipeline) { build(:ci_pipeline, project: project, sha: project.repository.commit.sha) } let(:context) { Gitlab::Ci::Build::Context::Build.new(pipeline, sha: project.repository.commit.sha) } end end - context 'when context is Build::Context::Global' do - it_behaves_like 'an exists rule with a context' do + context 'when the rules are being evaluated for an entire pipeline' do + it_behaves_like 'a rules:exists with a context' do let(:pipeline) { build(:ci_pipeline, project: project, sha: project.repository.commit.sha) } let(:context) { Gitlab::Ci::Build::Context::Global.new(pipeline, yaml_variables: {}) } end end - context 'when context is Config::External::Context' do + context 'when rules are being evaluated with `include`' do let(:context) { Gitlab::Ci::Config::External::Context.new(project: project, sha: sha) } - it_behaves_like 'an exists rule with a context' do + it_behaves_like 'a rules:exists with a context' do let(:sha) { project.repository.commit.sha } end diff --git a/spec/lib/gitlab/ci/config/entry/bridge_spec.rb b/spec/lib/gitlab/ci/config/entry/bridge_spec.rb index c56f2d25074..8da46561b73 100644 --- a/spec/lib/gitlab/ci/config/entry/bridge_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/bridge_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Config::Entry::Bridge do - subject { described_class.new(config, name: :my_bridge) } + subject(:entry) { described_class.new(config, name: :my_bridge) } it_behaves_like 'with inheritable CI config' do let(:inheritable_key) { 'default' } @@ -380,4 +380,38 @@ RSpec.describe Gitlab::Ci::Config::Entry::Bridge do end end end + + describe '#when' do + context 'when bridge is a manual action' do + let(:config) { { script: 'deploy', when: 'manual' } } + + it { expect(entry.when).to eq('manual') } + end + + context 'when bridge has no `when` attribute' do + let(:config) { { script: 'deploy' } } + + it { expect(entry.when).to be_nil } + end + + 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 'bridge 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 'bridge when should be a string' + end + end + end + end end diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb index 75ac2ca87ab..acf60a6cdda 100644 --- a/spec/lib/gitlab/ci/config/entry/job_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb @@ -317,6 +317,26 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do end end + 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 + context 'when only: is used with rules:' do let(:config) { { only: ['merge_requests'], rules: [{ if: '$THIS' }] } } @@ -653,7 +673,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do with_them do let(:config) { { script: 'ls', rules: rules, only: only }.compact } - it "#{name}" do + it name.to_s do expect(workflow).to receive(:has_rules?) { has_workflow_rules? } entry.compose!(deps) diff --git a/spec/lib/gitlab/ci/config/entry/processable_spec.rb b/spec/lib/gitlab/ci/config/entry/processable_spec.rb index ad90dd59585..f1578a068b9 100644 --- a/spec/lib/gitlab/ci/config/entry/processable_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/processable_spec.rb @@ -208,7 +208,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable do it 'reports error about variable' do expect(entry.errors) - .to include 'variables:var2 config must be a string' + .to include 'variables:var2 config uses invalid data keys: description' end end end @@ -248,7 +248,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable do with_them do let(:config) { { script: 'ls', rules: rules, only: only }.compact } - it "#{name}" do + it name.to_s do expect(workflow).to receive(:has_rules?) { has_workflow_rules? } entry.compose!(deps) @@ -447,6 +447,29 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable do ) end end + + context 'when variables have "expand" data' do + let(:config) do + { + script: 'echo', + variables: { 'VAR1' => 'val 1', + 'VAR2' => { value: 'val 2', expand: false }, + 'VAR3' => { value: 'val 3', expand: true } } + } + end + + it 'returns correct value' do + expect(entry.value).to eq( + name: :rspec, + stage: 'test', + only: { refs: %w[branches tags] }, + job_variables: { 'VAR1' => { value: 'val 1' }, + 'VAR2' => { value: 'val 2', raw: true }, + 'VAR3' => { value: 'val 3', raw: false } }, + root_variables_inheritance: true + ) + end + end end end end diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb index a55e13e7c2d..085293d7368 100644 --- a/spec/lib/gitlab/ci/config/entry/root_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb @@ -316,6 +316,35 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do end end end + + context 'when variables have "expand" data' do + let(:hash) do + { + variables: { 'VAR1' => 'val 1', + 'VAR2' => { value: 'val 2', expand: false }, + 'VAR3' => { value: 'val 3', expand: true } }, + rspec: { script: 'rspec' } + } + end + + before do + root.compose! + end + + it 'returns correct value' do + expect(root.variables_entry.value_with_data).to eq( + 'VAR1' => { value: 'val 1' }, + 'VAR2' => { value: 'val 2', raw: true }, + 'VAR3' => { value: 'val 3', raw: false } + ) + + expect(root.variables_value).to eq( + 'VAR1' => 'val 1', + 'VAR2' => 'val 2', + 'VAR3' => 'val 3' + ) + end + end end context 'when configuration is not valid' do diff --git a/spec/lib/gitlab/ci/config/entry/variable_spec.rb b/spec/lib/gitlab/ci/config/entry/variable_spec.rb index 076a5b32e92..d7023072312 100644 --- a/spec/lib/gitlab/ci/config/entry/variable_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/variable_spec.rb @@ -92,6 +92,12 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variable do describe '#value_with_data' do subject(:value_with_data) { entry.value_with_data } + it { is_expected.to eq(value: 'value') } + end + + describe '#value_with_prefill_data' do + subject(:value_with_prefill_data) { entry.value_with_prefill_data } + it { is_expected.to eq(value: 'value', description: 'description') } end @@ -107,6 +113,12 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variable do describe '#value_with_data' do subject(:value_with_data) { entry.value_with_data } + it { is_expected.to eq(value: 'value') } + end + + describe '#value_with_prefill_data' do + subject(:value_with_prefill_data) { entry.value_with_prefill_data } + it { is_expected.to eq(value: 'value', description: 'description') } end end @@ -123,6 +135,12 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variable do describe '#value_with_data' do subject(:value_with_data) { entry.value_with_data } + it { is_expected.to eq(value: '123') } + end + + describe '#value_with_prefill_data' do + subject(:value_with_prefill_data) { entry.value_with_prefill_data } + it { is_expected.to eq(value: '123', description: 'description') } end end @@ -139,6 +157,12 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variable do describe '#value_with_data' do subject(:value_with_data) { entry.value_with_data } + it { is_expected.to eq(value: 'value') } + end + + describe '#value_with_prefill_data' do + subject(:value_with_prefill_data) { entry.value_with_prefill_data } + it { is_expected.to eq(value: 'value', description: :description) } end end @@ -192,6 +216,94 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variable do it { is_expected.to eq(value: 'value') } end + + describe '#value_with_prefill_data' do + subject(:value_with_prefill_data) { entry.value_with_prefill_data } + + it { is_expected.to eq(value: 'value') } + end + end + end + + context 'when config is a hash with expand' do + let(:config) { { value: 'value', expand: false } } + + context 'when metadata allowed_value_data is not provided' do + describe '#valid?' do + it { is_expected.not_to be_valid } + end + + describe '#errors' do + subject(:errors) { entry.errors } + + it { is_expected.to include 'var1 config must be a string' } + end + end + + context 'when metadata allowed_value_data is (value, expand)' do + let(:metadata) { { allowed_value_data: %i[value expand] } } + + describe '#valid?' do + it { is_expected.to be_valid } + end + + describe '#value' do + subject(:value) { entry.value } + + it { is_expected.to eq('value') } + end + + describe '#value_with_data' do + subject(:value_with_data) { entry.value_with_data } + + it { is_expected.to eq(value: 'value', raw: true) } + + context 'when the FF ci_raw_variables_in_yaml_config is disabled' do + before do + stub_feature_flags(ci_raw_variables_in_yaml_config: false) + end + + it { is_expected.to eq(value: 'value') } + end + end + + context 'when config expand is true' do + let(:config) { { value: 'value', expand: true } } + + describe '#value_with_data' do + subject(:value_with_data) { entry.value_with_data } + + it { is_expected.to eq(value: 'value', raw: false) } + end + end + + context 'when config expand is a string' do + let(:config) { { value: 'value', expand: "true" } } + + describe '#valid?' do + it { is_expected.not_to be_valid } + end + + describe '#errors' do + subject(:errors) { entry.errors } + + it { is_expected.to include 'var1 config expand should be a boolean value' } + end + end + end + + context 'when metadata allowed_value_data is (value, xyz)' do + let(:metadata) { { allowed_value_data: %i[value xyz] } } + + describe '#valid?' do + it { is_expected.not_to be_valid } + end + + describe '#errors' do + subject(:errors) { entry.errors } + + it { is_expected.to include 'var1 config uses invalid data keys: expand' } + end end end end @@ -229,6 +341,12 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variable do describe '#value_with_data' do subject(:value_with_data) { entry.value_with_data } + it { is_expected.to eq(value: 'value') } + end + + describe '#value_with_prefill_data' do + subject(:value_with_prefill_data) { entry.value_with_prefill_data } + it { is_expected.to eq(value: 'value', description: 'description', value_options: %w[value value2]) } end end diff --git a/spec/lib/gitlab/ci/config/entry/variables_spec.rb b/spec/lib/gitlab/ci/config/entry/variables_spec.rb index 085f304094e..609e4422d5c 100644 --- a/spec/lib/gitlab/ci/config/entry/variables_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/variables_spec.rb @@ -66,6 +66,15 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do ) end end + + describe '#value_with_prefill_data' do + it 'returns variable with prefill data' do + expect(entry.value_with_prefill_data).to eq( + 'VARIABLE_1' => { value: 'value 1' }, + 'VARIABLE_2' => { value: 'value 2' } + ) + end + end end context 'with numeric keys and values in the config' do @@ -119,6 +128,14 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do describe '#value_with_data' do it 'returns variable with data' do expect(entry.value_with_data).to eq( + 'VARIABLE_1' => { value: 'value' } + ) + end + end + + describe '#value_with_prefill_data' do + it 'returns variable with prefill data' do + expect(entry.value_with_prefill_data).to eq( 'VARIABLE_1' => { value: 'value', description: 'variable 1' } ) end @@ -147,6 +164,14 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do describe '#value_with_data' do it 'returns variable with data' do expect(entry.value_with_data).to eq( + 'VARIABLE_1' => { value: 'value1' } + ) + end + end + + describe '#value_with_prefill_data' do + it 'returns variable with prefill data' do + expect(entry.value_with_prefill_data).to eq( 'VARIABLE_1' => { value: 'value1', value_options: %w[value1 value2], description: 'variable 1' } ) end @@ -174,6 +199,15 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do describe '#value_with_data' do it 'returns variable with data' do expect(entry.value_with_data).to eq( + 'VARIABLE_1' => { value: 'value 1' }, + 'VARIABLE_2' => { value: 'value 2' } + ) + end + end + + describe '#value_with_prefill_data' do + it 'returns variable with prefill data' do + expect(entry.value_with_prefill_data).to eq( 'VARIABLE_1' => { value: 'value 1', description: 'variable 1' }, 'VARIABLE_2' => { value: 'value 2' } ) diff --git a/spec/lib/gitlab/ci/config/external/file/base_spec.rb b/spec/lib/gitlab/ci/config/external/file/base_spec.rb index 1306d61d99c..8475c3a8b19 100644 --- a/spec/lib/gitlab/ci/config/external/file/base_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/base_spec.rb @@ -14,6 +14,10 @@ RSpec.describe Gitlab::Ci::Config::External::File::Base do super end + + def validate_context! + # no-op + end end end @@ -95,6 +99,24 @@ RSpec.describe Gitlab::Ci::Config::External::File::Base do expect(file.error_message).to eq('Included file `some/file/xxxxxxxxxxxxxxxx.yml` does not have valid YAML syntax!') end end + + context 'when the class has no validate_context!' do + let(:test_class) do + Class.new(described_class) do + def initialize(params, context) + @location = params + + super + end + end + end + + let(:location) { 'some/file/config.yaml' } + + it 'raises an error' do + expect { valid? }.to raise_error(NotImplementedError) + end + end end describe '#to_hash' do diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb index e12f5dcee0a..d905568f01e 100644 --- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb +++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb @@ -113,7 +113,19 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do it_behaves_like 'logging config file fetch', 'config_file_fetch_template_content_duration_s', 1 end - context 'when the key is a hash of file and remote' do + context 'when the key is not valid' do + let(:local_file) { 'secret-file.yml' } + let(:values) do + { include: { invalid: local_file }, + image: 'image:1.0' } + end + + it 'returns ambigious specification error' do + expect { subject }.to raise_error(described_class::AmbigiousSpecificationError, '`{"invalid":"secret-file.yml"}` does not have a valid subkey for include. Valid subkeys are: `local`, `project`, `remote`, `template`, `artifact`') + end + end + + context 'when the key is a hash of local and remote' do let(:variables) { Gitlab::Ci::Variables::Collection.new([{ 'key' => 'GITLAB_TOKEN', 'value' => 'secret-file', 'masked' => true }]) } let(:local_file) { 'secret-file.yml' } let(:remote_url) { 'https://gitlab.com/secret-file.yml' } @@ -123,7 +135,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do end it 'returns ambigious specification error' do - expect { subject }.to raise_error(described_class::AmbigiousSpecificationError, 'Include `{"local":"xxxxxxxxxxx.yml","remote":"https://gitlab.com/xxxxxxxxxxx.yml"}` needs to match exactly one accessor!') + expect { subject }.to raise_error(described_class::AmbigiousSpecificationError, 'Each include must use only one of: `local`, `project`, `remote`, `template`, `artifact`') end end diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb index 475503de7da..c4a6641ff6b 100644 --- a/spec/lib/gitlab/ci/config_spec.rb +++ b/spec/lib/gitlab/ci/config_spec.rb @@ -484,7 +484,7 @@ RSpec.describe Gitlab::Ci::Config do it 'raises ConfigError' do expect { config }.to raise_error( described_class::ConfigError, - 'Include `{"remote":"http://url","local":"/local/file.yml"}` needs to match exactly one accessor!' + /Each include must use only one of/ ) end end @@ -714,7 +714,7 @@ RSpec.describe Gitlab::Ci::Config do it 'raises an error' do expect { config }.to raise_error( described_class::ConfigError, - /needs to match exactly one accessor!/ + /does not have a valid subkey for include/ ) end end diff --git a/spec/lib/gitlab/ci/parsers/codequality/code_climate_spec.rb b/spec/lib/gitlab/ci/parsers/codequality/code_climate_spec.rb index 6a08e8f0b7f..1ef341ff863 100644 --- a/spec/lib/gitlab/ci/parsers/codequality/code_climate_spec.rb +++ b/spec/lib/gitlab/ci/parsers/codequality/code_climate_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Parsers::Codequality::CodeClimate do describe '#parse!' do - subject(:parse) { described_class.new.parse!(code_climate, codequality_report) } + subject(:parse) { described_class.new.parse!(code_climate, codequality_report, metadata) } let(:codequality_report) { Gitlab::Ci::Reports::CodequalityReports.new } let(:code_climate) do @@ -35,6 +35,15 @@ RSpec.describe Gitlab::Ci::Parsers::Codequality::CodeClimate do ].to_json end + let_it_be(:group) { create(:group, name: 'test-group') } + let_it_be(:project) { create(:project, path: 'test-project', group: group) } + let(:metadata) do + { + project: project, + commit_sha: 'f0cc5229e2aa5e9429f1b17a3b3b102f21d7fe31' + } + end + context "when data is code_climate style JSON" do context "when there are no degradations" do let(:code_climate) { [].to_json } @@ -133,5 +142,56 @@ RSpec.describe Gitlab::Ci::Parsers::Codequality::CodeClimate do expect(codequality_report.degradations_count).to eq(0) end end + + context 'for web_url' do + let(:code_climate) do + [ + { + "categories": [ + "Complexity" + ], + "check_name": "argument_count", + "content": { + "body": "" + }, + "description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.", + "fingerprint": "15cdb5c53afd42bc22f8ca366a08d547", + "location": { + "path": "foo.rb", + "lines": { + "begin": 10, + "end": 10 + } + }, + "other_locations": [], + "remediation_points": 900000, + "severity": "major", + "type": "issue", + "engine_name": "structure" + } + ].to_json + end + + context 'when metadata has project and commit_sha' do + it 'adds a non nil url' do + want = 'http://localhost/test-group/test-project/-/blob/f0cc5229e2aa5e9429f1b17a3b3b102f21d7fe31/foo.rb#L10' + expect { parse }.not_to raise_error + + expect(codequality_report.degradations_count).to eq(1) + expect(codequality_report.all_degradations[0]['web_url']).to eq(want) + end + end + + context 'when metadata does not have project and commit_sha' do + let(:metadata) { {} } + + it 'adds a nil url' do + expect { parse }.not_to raise_error + + expect(codequality_report.degradations_count).to eq(1) + expect(codequality_report.all_degradations[0]['web_url']).to be_nil + end + end + end end end diff --git a/spec/lib/gitlab/ci/parsers/coverage/sax_document_spec.rb b/spec/lib/gitlab/ci/parsers/coverage/sax_document_spec.rb index a9851d78f48..e4ae6b25362 100644 --- a/spec/lib/gitlab/ci/parsers/coverage/sax_document_spec.rb +++ b/spec/lib/gitlab/ci/parsers/coverage/sax_document_spec.rb @@ -8,6 +8,7 @@ RSpec.describe Gitlab::Ci::Parsers::Coverage::SaxDocument do describe '#parse!' do let(:coverage_report) { Gitlab::Ci::Reports::CoverageReport.new } let(:project_path) { 'foo/bar' } + let(:windows_path) { 'foo\bar' } let(:paths) { ['app/user.rb'] } let(:cobertura) do @@ -269,6 +270,36 @@ RSpec.describe Gitlab::Ci::Parsers::Coverage::SaxDocument do it_behaves_like 'ignoring sources, project_path, and worktree_paths' end + context 'and has Windows-style paths' do + let(:sources_xml) do + <<~EOF_WIN + <sources> + <source>D:\\builds\\#{windows_path}\\app</source> + </sources> + EOF_WIN + end + + context 'when there is a single <class>' do + context 'with a single line' do + let(:classes_xml) do + <<~EOF + <packages><package name="app"><classes> + <class filename="user.rb"><lines> + <line number="1" hits="2"/> + </lines></class> + </classes></package></packages> + EOF + end + + it 'parses XML and returns a single file with the filename relative to project root' do + expect { parse_report }.not_to raise_error + + expect(coverage_report.files).to eq({ 'app/user.rb' => { 1 => 2 } }) + end + end + end + end + context 'and has multiple sources with a pattern for Go projects' do let(:project_path) { 'local/go' } # Make sure we're not making false positives let(:sources_xml) do diff --git a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb index 38b229e0dd8..f09b85aa2c7 100644 --- a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb +++ b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb @@ -3,7 +3,7 @@ require 'fast_spec_helper' RSpec.describe Gitlab::Ci::Parsers::Sbom::CyclonedxProperties do - subject(:parse_source) { described_class.parse_source(properties) } + subject(:parse_source_from_properties) { described_class.parse_source(properties) } context 'when properties are nil' do let(:properties) { nil } @@ -50,9 +50,9 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::CyclonedxProperties do end it 'does not call dependency_scanning parser' do - expect(Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning).not_to receive(:parse_source) + expect(Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning).not_to receive(:source) - parse_source + parse_source_from_properties end end @@ -82,7 +82,7 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::CyclonedxProperties do it 'passes only supported properties to the dependency scanning parser' do expect(Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning).to receive(:source).with(expected_input) - parse_source + parse_source_from_properties end end end diff --git a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb index f3636106b98..0b094880f69 100644 --- a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb +++ b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb @@ -100,16 +100,53 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::Cyclonedx do ] end + before do + allow(report).to receive(:add_component) + end + it 'adds each component, ignoring unused attributes' do expect(report).to receive(:add_component) - .with(an_object_having_attributes(name: "activesupport", version: "5.1.4", component_type: "library")) + .with( + an_object_having_attributes( + name: "activesupport", + version: "5.1.4", + component_type: "library", + purl: an_object_having_attributes(type: "gem") + ) + ) expect(report).to receive(:add_component) - .with(an_object_having_attributes(name: "byebug", version: "10.0.0", component_type: "library")) + .with( + an_object_having_attributes( + name: "byebug", + version: "10.0.0", + component_type: "library", + purl: an_object_having_attributes(type: "gem") + ) + ) expect(report).to receive(:add_component) .with(an_object_having_attributes(name: "minimal-component", version: nil, component_type: "library")) parse! end + + context 'when a component has an invalid purl' do + before do + components.push( + { + "name" => "invalid-component", + "version" => "v0.0.1", + "purl" => "pkg:nil", + "type" => "library" + } + ) + end + + it 'adds an error to the report' do + expect(report).to receive(:add_error).with("/components/#{components.size - 1}/purl is invalid") + + parse! + end + end end context 'when report has metadata properties' do diff --git a/spec/lib/gitlab/ci/parsers/security/common_spec.rb b/spec/lib/gitlab/ci/parsers/security/common_spec.rb index 7dbad354e4c..03cab021c17 100644 --- a/spec/lib/gitlab/ci/parsers/security/common_spec.rb +++ b/spec/lib/gitlab/ci/parsers/security/common_spec.rb @@ -400,26 +400,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do end describe 'parsing tracking' do - let(:tracking_data) do - { - 'type' => 'source', - 'items' => [ - 'signatures' => [ - { 'algorithm' => 'hash', 'value' => 'hash_value' }, - { 'algorithm' => 'location', 'value' => 'location_value' }, - { 'algorithm' => 'scope_offset', 'value' => 'scope_offset_value' } - ] - ] - } - end - - context 'with valid tracking information' do - it 'creates signatures for each algorithm' do - finding = report.findings.first - expect(finding.signatures.size).to eq(3) - expect(finding.signatures.map(&:algorithm_type).to_set).to eq(Set['hash', 'location', 'scope_offset']) - end - end + let(:finding) { report.findings.first } context 'with invalid tracking information' do let(:tracking_data) do @@ -436,15 +417,26 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do end it 'ignores invalid algorithm types' do - finding = report.findings.first expect(finding.signatures.size).to eq(2) expect(finding.signatures.map(&:algorithm_type).to_set).to eq(Set['hash', 'location']) end end context 'with valid tracking information' do + let(:tracking_data) do + { + 'type' => 'source', + 'items' => [ + 'signatures' => [ + { 'algorithm' => 'hash', 'value' => 'hash_value' }, + { 'algorithm' => 'location', 'value' => 'location_value' }, + { 'algorithm' => 'scope_offset', 'value' => 'scope_offset_value' } + ] + ] + } + end + it 'creates signatures for each signature algorithm' do - finding = report.findings.first expect(finding.signatures.size).to eq(3) expect(finding.signatures.map(&:algorithm_type)).to eq(%w[hash location scope_offset]) @@ -456,7 +448,6 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do end it 'sets the uuid according to the higest priority signature' do - finding = report.findings.first highest_signature = finding.signatures.max_by(&:priority) identifiers = if signatures_enabled diff --git a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb index 6e8b6e40928..9126c6dab21 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb @@ -409,4 +409,21 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do end end end + + describe '#observe_pipeline_size' do + let(:command) { described_class.new(project: project) } + + let(:pipeline) { instance_double(Ci::Pipeline, total_size: 5, project: project, source: "schedule") } + + it 'logs the pipeline total size to histogram' do + histogram = instance_double(Prometheus::Client::Histogram) + + expect(::Gitlab::Ci::Pipeline::Metrics).to receive(:pipeline_size_histogram) + .and_return(histogram) + expect(histogram).to receive(:observe) + .with({ source: pipeline.source, plan: project.actual_plan_name }, pipeline.total_size) + + command.observe_pipeline_size(pipeline) + end + end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/limit/active_jobs_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/limit/active_jobs_spec.rb index bc453f1502b..c5a5e905d17 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/limit/active_jobs_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/limit/active_jobs_spec.rb @@ -69,7 +69,9 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Chain::Limit::ActiveJobs do class: described_class.name, message: described_class::MESSAGE, project_id: project.id, - plan: default_plan.name + plan: default_plan.name, + project_path: project.path, + jobs_in_alive_pipelines_count: step.send(:count_jobs_in_alive_pipelines) ) end diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb new file mode 100644 index 00000000000..ce1ee2fcda0 --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb @@ -0,0 +1,136 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Pipeline::Chain::PopulateMetadata do + let_it_be(:project) { create(:project, :repository) } + let_it_be(:user) { create(:user) } + + let(:pipeline) do + build(:ci_pipeline, project: project, ref: 'master', user: user) + end + + let(:command) do + Gitlab::Ci::Pipeline::Chain::Command.new( + project: project, + current_user: user, + origin_ref: 'master') + end + + let(:dependencies) do + [ + Gitlab::Ci::Pipeline::Chain::Config::Content.new(pipeline, command), + Gitlab::Ci::Pipeline::Chain::Config::Process.new(pipeline, command), + Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules.new(pipeline, command), + Gitlab::Ci::Pipeline::Chain::SeedBlock.new(pipeline, command), + Gitlab::Ci::Pipeline::Chain::Seed.new(pipeline, command), + Gitlab::Ci::Pipeline::Chain::Populate.new(pipeline, command) + ] + end + + let(:step) { described_class.new(pipeline, command) } + + let(:config) do + { rspec: { script: 'rspec' } } + end + + def run_chain + dependencies.map(&:perform!) + step.perform! + end + + before do + stub_ci_pipeline_yaml_file(YAML.dump(config)) + end + + context 'with pipeline name' do + let(:config) do + { workflow: { name: ' Pipeline name ' }, rspec: { script: 'rspec' } } + end + + it 'does not break the chain' do + run_chain + + expect(step.break?).to be false + end + + context 'with feature flag disabled' do + before do + stub_feature_flags(pipeline_name: false) + end + + it 'does not build pipeline_metadata' do + run_chain + + expect(pipeline.pipeline_metadata).to be_nil + end + end + + context 'with feature flag enabled' do + before do + stub_feature_flags(pipeline_name: true) + end + + it 'builds pipeline_metadata' do + run_chain + + expect(pipeline.pipeline_metadata.name).to eq('Pipeline name') + expect(pipeline.pipeline_metadata.project).to eq(pipeline.project) + expect(pipeline.pipeline_metadata).not_to be_persisted + end + + context 'with empty name' do + let(:config) do + { workflow: { name: ' ' }, rspec: { script: 'rspec' } } + end + + it 'strips whitespace from name' do + run_chain + + expect(pipeline.pipeline_metadata).to be_nil + end + end + + context 'with variables' do + let(:config) do + { + variables: { ROOT_VAR: 'value $WORKFLOW_VAR1' }, + workflow: { + name: 'Pipeline $ROOT_VAR $WORKFLOW_VAR2 $UNKNOWN_VAR', + rules: [{ variables: { WORKFLOW_VAR1: 'value1', WORKFLOW_VAR2: 'value2' } }] + }, + rspec: { script: 'rspec' } + } + end + + it 'substitutes variables' do + run_chain + + expect(pipeline.pipeline_metadata.name).to eq('Pipeline value value1 value2 ') + end + end + + context 'with invalid name' do + let(:config) do + { + variables: { ROOT_VAR: 'a' * 256 }, + workflow: { + name: 'Pipeline $ROOT_VAR' + }, + rspec: { script: 'rspec' } + } + end + + it 'returns error and breaks chain' do + ret = run_chain + + expect(ret) + .to match_array(["Failed to build pipeline metadata! Name is too long (maximum is 255 characters)"]) + expect(pipeline.pipeline_metadata.errors.full_messages) + .to match_array(['Name is too long (maximum is 255 characters)']) + expect(step.break?).to be true + end + end + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb index 51d1661b586..62de4d2e96d 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb @@ -236,47 +236,4 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Populate do end end end - - context 'with pipeline name' do - let(:config) do - { workflow: { name: ' Pipeline name ' }, rspec: { script: 'rspec' } } - end - - context 'with feature flag disabled' do - before do - stub_feature_flags(pipeline_name: false) - end - - it 'does not build pipeline_metadata' do - run_chain - - expect(pipeline.pipeline_metadata).to be_nil - end - end - - context 'with feature flag enabled' do - before do - stub_feature_flags(pipeline_name: true) - end - - it 'builds pipeline_metadata' do - run_chain - - expect(pipeline.pipeline_metadata.title).to eq('Pipeline name') - expect(pipeline.pipeline_metadata.project).to eq(pipeline.project) - end - - context 'with empty name' do - let(:config) do - { workflow: { name: ' ' }, rspec: { script: 'rspec' } } - end - - it 'strips whitespace from name' do - run_chain - - expect(pipeline.pipeline_metadata).to be_nil - end - end - end - end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb index c69aa661b05..31086f6ae4a 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb @@ -80,7 +80,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Sequence do subject.build! expect(histogram).to have_received(:observe) - .with({ source: 'push' }, 0) + .with({ source: 'push', plan: project.actual_plan_name }, 0) end describe 'active jobs by pipeline plan histogram' do diff --git a/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb deleted file mode 100644 index 6569ce937ac..00000000000 --- a/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb +++ /dev/null @@ -1,119 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Gitlab::Ci::Pipeline::Seed::Deployment do - let_it_be(:project, refind: true) { create(:project, :repository) } - - let(:pipeline) do - create(:ci_pipeline, project: project, sha: 'b83d6e391c22777fca1ed3012fce84f633d7fed0') - end - - let(:job) { build(:ci_build, project: project, pipeline: pipeline) } - let(:environment) { Gitlab::Ci::Pipeline::Seed::Environment.new(job).to_resource } - let(:seed) { described_class.new(job, environment) } - let(:attributes) { {} } - - before do - job.assign_attributes(**attributes) - end - - describe '#to_resource' do - subject { seed.to_resource } - - context 'when job has environment attribute' do - let(:attributes) do - { - environment: 'production', - options: { environment: { name: 'production', **kubernetes_options } } - } - end - - let(:kubernetes_options) { {} } - - it 'returns a deployment object with environment' do - expect(subject).to be_a(Deployment) - expect(subject.iid).to be_present - expect(subject.environment.name).to eq('production') - expect(subject.cluster).to be_nil - expect(subject.deployment_cluster).to be_nil - end - - context 'when environment has deployment platform' do - let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project], managed: managed_cluster) } - let(:managed_cluster) { true } - - it 'sets the cluster and deployment_cluster' do - expect(subject.cluster).to eq(cluster) # until we stop double writing in 12.9: https://gitlab.com/gitlab-org/gitlab/issues/202628 - expect(subject.deployment_cluster.cluster).to eq(cluster) - end - - context 'when a custom namespace is given' do - let(:kubernetes_options) { { kubernetes: { namespace: 'the-custom-namespace' } } } - - context 'when cluster is managed' do - it 'does not set the custom namespace' do - expect(subject.deployment_cluster.kubernetes_namespace).not_to eq('the-custom-namespace') - end - end - - context 'when cluster is not managed' do - let(:managed_cluster) { false } - - it 'sets the custom namespace' do - expect(subject.deployment_cluster.kubernetes_namespace).to eq('the-custom-namespace') - end - end - end - end - - context 'when environment has an invalid URL' do - let(:attributes) do - { - environment: '!!!', - options: { environment: { name: '!!!' } } - } - end - - it 'returns nothing' do - is_expected.to be_nil - end - end - - context 'when job has already deployment' do - let(:job) { build(:ci_build, :with_deployment, project: project, environment: 'production') } - - it 'returns the persisted deployment' do - is_expected.to eq(job.deployment) - end - end - end - - context 'when job does not start environment' do - where(:action) do - %w(stop prepare verify access) - end - - with_them do - let(:attributes) do - { - environment: 'production', - options: { environment: { name: 'production', action: action } } - } - end - - it 'returns nothing' do - is_expected.to be_nil - end - end - end - - context 'when job does not have environment attribute' do - let(:attributes) { { name: 'test' } } - - it 'returns nothing' do - is_expected.to be_nil - end - end - end -end diff --git a/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb deleted file mode 100644 index 2b9d8127886..00000000000 --- a/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb +++ /dev/null @@ -1,224 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do - let_it_be(:project) { create(:project) } - - let!(:pipeline) { create(:ci_pipeline, project: project) } - - let(:job) { build(:ci_build, project: project, pipeline: pipeline) } - let(:seed) { described_class.new(job) } - let(:attributes) { {} } - - before do - job.assign_attributes(**attributes) - end - - describe '#to_resource' do - subject { seed.to_resource } - - shared_examples_for 'returning a correct environment' do - let(:expected_auto_stop_in_seconds) do - if expected_auto_stop_in - ChronicDuration.parse(expected_auto_stop_in).seconds - end - end - - it 'returns a persisted environment object' do - freeze_time do - expect { subject }.to change { Environment.count }.by(1) - - expect(subject).to be_a(Environment) - expect(subject).to be_persisted - expect(subject.project).to eq(project) - expect(subject.name).to eq(expected_environment_name) - expect(subject.auto_stop_in).to eq(expected_auto_stop_in_seconds) - end - end - - context 'when environment has already existed' do - let!(:environment) do - create(:environment, - project: project, - name: expected_environment_name - ).tap do |env| - env.auto_stop_in = expected_auto_stop_in - end - end - - it 'returns the existing environment object' do - expect { subject }.not_to change { Environment.count } - expect { subject }.not_to change { environment.auto_stop_at } - - expect(subject).to be_persisted - expect(subject).to eq(environment) - end - end - end - - context 'when job has environment name attribute' do - let(:environment_name) { 'production' } - let(:expected_environment_name) { 'production' } - let(:expected_auto_stop_in) { nil } - - let(:attributes) do - { - environment: environment_name, - options: { environment: { name: environment_name } } - } - end - - it_behaves_like 'returning a correct environment' - - context 'and job environment also has an auto_stop_in attribute' do - let(:environment_auto_stop_in) { '5 minutes' } - let(:expected_auto_stop_in) { '5 minutes' } - - let(:attributes) do - { - environment: environment_name, - options: { - environment: { - name: environment_name, - auto_stop_in: environment_auto_stop_in - } - } - } - end - - it_behaves_like 'returning a correct environment' - end - - context 'and job environment has an auto_stop_in variable attribute' do - let(:environment_auto_stop_in) { '10 minutes' } - let(:expected_auto_stop_in) { '10 minutes' } - - let(:attributes) do - { - environment: environment_name, - options: { - environment: { - name: environment_name, - auto_stop_in: '$TTL' - } - }, - yaml_variables: [ - { key: "TTL", value: environment_auto_stop_in, public: true } - ] - } - end - - it_behaves_like 'returning a correct environment' - end - end - - context 'when job has deployment tier attribute' do - let(:attributes) do - { - environment: 'customer-portal', - options: { - environment: { - name: 'customer-portal', - deployment_tier: deployment_tier - } - } - } - end - - let(:deployment_tier) { 'production' } - - context 'when environment has not been created yet' do - it 'sets the specified deployment tier' do - is_expected.to be_production - end - - context 'when deployment tier is staging' do - let(:deployment_tier) { 'staging' } - - it 'sets the specified deployment tier' do - is_expected.to be_staging - end - end - - context 'when deployment tier is unknown' do - let(:deployment_tier) { 'unknown' } - - it 'raises an error' do - expect { subject }.to raise_error(ArgumentError, "'unknown' is not a valid tier") - end - end - end - - context 'when environment has already been created' do - before do - create(:environment, project: project, name: 'customer-portal', tier: :staging) - end - - it 'does not overwrite the specified deployment tier' do - # This is to be updated when a deployment succeeded i.e. Deployments::UpdateEnvironmentService. - is_expected.to be_staging - end - end - end - - context 'when job starts a review app' do - let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' } - let(:expected_environment_name) { "review/#{job.ref}" } - let(:expected_auto_stop_in) { nil } - - let(:attributes) do - { - environment: environment_name, - options: { environment: { name: environment_name } } - } - end - - it_behaves_like 'returning a correct environment' - end - - context 'when job stops a review app' do - let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' } - let(:expected_environment_name) { "review/#{job.ref}" } - let(:expected_auto_stop_in) { nil } - - let(:attributes) do - { - environment: environment_name, - options: { environment: { name: environment_name, action: 'stop' } } - } - end - - it_behaves_like 'returning a correct environment' - end - - context 'when merge_request is provided' do - let(:environment_name) { 'development' } - let(:attributes) { { environment: environment_name, options: { environment: { name: environment_name } } } } - let(:merge_request) { create(:merge_request, source_project: project) } - let(:seed) { described_class.new(job, merge_request: merge_request) } - - context 'and environment does not exist' do - let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' } - - it 'creates an environment associated with the merge request' do - expect { subject }.to change { Environment.count }.by(1) - - expect(subject.merge_request).to eq(merge_request) - end - end - - context 'and environment already exists' do - before do - create(:environment, project: project, name: environment_name) - end - - it 'does not change the merge request associated with the environment' do - expect { subject }.not_to change { Environment.count } - - expect(subject.merge_request).to be_nil - end - end - end - end -end diff --git a/spec/lib/gitlab/ci/pipeline/seed/pipeline_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/pipeline_spec.rb index a76b4874eca..55980ae72a0 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/pipeline_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/pipeline_spec.rb @@ -6,7 +6,9 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Pipeline do let_it_be(:project) { create(:project, :repository) } let_it_be(:pipeline) { create(:ci_pipeline, project: project) } - let(:seed_context) { Gitlab::Ci::Pipeline::Seed::Context.new(pipeline, root_variables: []) } + let(:root_variables) { [] } + + let(:seed_context) { Gitlab::Ci::Pipeline::Seed::Context.new(pipeline, root_variables: root_variables) } let(:stages_attributes) do [ @@ -75,4 +77,12 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Pipeline do expect(seed.deployments_count).to eq(2) end end + + describe '#root_variables' do + let(:root_variables) { %w[var1 value1] } + + it 'returns root_variables' do + expect(seed.root_variables).to eq(root_variables) + end + end end diff --git a/spec/lib/gitlab/ci/reports/sbom/component_spec.rb b/spec/lib/gitlab/ci/reports/sbom/component_spec.rb index 06ea3433ef0..cdaf9354104 100644 --- a/spec/lib/gitlab/ci/reports/sbom/component_spec.rb +++ b/spec/lib/gitlab/ci/reports/sbom/component_spec.rb @@ -1,23 +1,67 @@ # frozen_string_literal: true -require 'fast_spec_helper' +require 'spec_helper' RSpec.describe Gitlab::Ci::Reports::Sbom::Component do - let(:attributes) do - { - type: 'library', - name: 'component-name', - version: 'v0.0.1' - } - end + let(:component_type) { 'library' } + let(:name) { 'component-name' } + let(:purl_type) { 'npm' } + let(:purl) { Sbom::PackageUrl.new(type: purl_type, name: name, version: version).to_s } + let(:version) { 'v0.0.1' } - subject { described_class.new(**attributes) } + subject(:component) do + described_class.new( + type: component_type, + name: name, + purl: purl, + version: version + ) + end it 'has correct attributes' do - expect(subject).to have_attributes( - component_type: attributes[:type], - name: attributes[:name], - version: attributes[:version] + expect(component).to have_attributes( + component_type: component_type, + name: name, + purl: an_object_having_attributes(type: purl_type), + version: version ) end + + describe '#ingestible?' do + subject { component.ingestible? } + + context 'when component_type is invalid' do + let(:component_type) { 'invalid' } + + it { is_expected.to be(false) } + end + + context 'when purl_type is invalid' do + let(:purl_type) { 'invalid' } + + it { is_expected.to be(false) } + end + + context 'when component_type is valid' do + where(:component_type) { ::Enums::Sbom.component_types.keys.map(&:to_s) } + + with_them do + it { is_expected.to be(true) } + end + end + + context 'when purl_type is valid' do + where(:purl_type) { ::Enums::Sbom.purl_types.keys.map(&:to_s) } + + with_them do + it { is_expected.to be(true) } + end + end + + context 'when there is no purl' do + let(:purl) { nil } + + it { is_expected.to be(true) } + end + end end diff --git a/spec/lib/gitlab/ci/reports/sbom/report_spec.rb b/spec/lib/gitlab/ci/reports/sbom/report_spec.rb index 6ffa93e5fc8..f9a83378f46 100644 --- a/spec/lib/gitlab/ci/reports/sbom/report_spec.rb +++ b/spec/lib/gitlab/ci/reports/sbom/report_spec.rb @@ -5,6 +5,21 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Reports::Sbom::Report do subject(:report) { described_class.new } + describe '#valid?' do + context 'when there are no errors' do + it { is_expected.to be_valid } + end + + context 'when report contains errors' do + before do + report.add_error('error1') + report.add_error('error2') + end + + it { is_expected.not_to be_valid } + end + end + describe '#add_error' do it 'appends errors to a list' do report.add_error('error1') diff --git a/spec/lib/gitlab/ci/reports/security/flag_spec.rb b/spec/lib/gitlab/ci/reports/security/flag_spec.rb index 6ee074f7aeb..0ef8f6c75a0 100644 --- a/spec/lib/gitlab/ci/reports/security/flag_spec.rb +++ b/spec/lib/gitlab/ci/reports/security/flag_spec.rb @@ -29,5 +29,11 @@ RSpec.describe Gitlab::Ci::Reports::Security::Flag do ) end end + + describe '#false_positive?' do + subject { security_flag.false_positive? } + + it { is_expected.to be_truthy } + end end end diff --git a/spec/lib/gitlab/ci/reports/security/reports_spec.rb b/spec/lib/gitlab/ci/reports/security/reports_spec.rb index e240edc4a12..33f3317c655 100644 --- a/spec/lib/gitlab/ci/reports/security/reports_spec.rb +++ b/spec/lib/gitlab/ci/reports/security/reports_spec.rb @@ -125,6 +125,32 @@ RSpec.describe Gitlab::Ci::Reports::Security::Reports do it { is_expected.to be(false) } end + + context 'when target_reports is not nil and reports is empty' do + let(:without_reports) { described_class.new(pipeline) } + + subject { without_reports.violates_default_policy_against?(target_reports, vulnerabilities_allowed, severity_levels, vulnerability_states) } + + before do + target_reports.get_report('sast', artifact).add_finding(high_severity_dast) + end + + context 'when require_approval_on_scan_removal feature is enabled' do + before do + stub_feature_flags(require_approval_on_scan_removal: true) + end + + it { is_expected.to be(true) } + end + + context 'when require_approval_on_scan_removal feature is disabled' do + before do + stub_feature_flags(require_approval_on_scan_removal: false) + end + + it { is_expected.to be(false) } + end + end end end end diff --git a/spec/lib/gitlab/ci/reports/test_suite_spec.rb b/spec/lib/gitlab/ci/reports/test_suite_spec.rb index 4a1f77bed65..05f6a8a8cb6 100644 --- a/spec/lib/gitlab/ci/reports/test_suite_spec.rb +++ b/spec/lib/gitlab/ci/reports/test_suite_spec.rb @@ -209,7 +209,7 @@ RSpec.describe Gitlab::Ci::Reports::TestSuite do Gitlab::Ci::Reports::TestCase::STATUS_TYPES.each do |status_type| describe "##{status_type}" do - subject { test_suite.public_send("#{status_type}") } + subject { test_suite.public_send(status_type.to_s) } context "when #{status_type} test case exists" do before do diff --git a/spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb index 8204b104832..43deb465025 100644 --- a/spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb @@ -12,7 +12,7 @@ RSpec.describe '5-Minute-Production-App.gitlab-ci.yml' do let(:default_branch) { 'master' } let(:pipeline_branch) { default_branch } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } before do diff --git a/spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb index 65fd2b016ac..f2bff5ff3e0 100644 --- a/spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb @@ -11,7 +11,7 @@ RSpec.describe 'Deploy-ECS.gitlab-ci.yml' do let(:project) { create(:project, :auto_devops, :custom_repo, files: { 'README.md' => '' }) } let(:user) { project.first_owner } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } let(:platform_target) { 'ECS' } diff --git a/spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb index 21052f03cb8..07cfa939623 100644 --- a/spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb @@ -3,8 +3,20 @@ require 'spec_helper' RSpec.describe 'Jobs/Build.gitlab-ci.yml' do + include Ci::TemplateHelpers + subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('Jobs/Build') } + describe 'AUTO_BUILD_IMAGE_VERSION' do + it 'corresponds to a published image in the registry' do + registry = "https://#{template_registry_host}" + repository = "gitlab-org/cluster-integration/auto-build-image" + reference = YAML.safe_load(template.content).dig('variables', 'AUTO_BUILD_IMAGE_VERSION') + + expect(public_image_exist?(registry, repository, reference)).to be true + end + end + describe 'the created pipeline' do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { project.first_owner } @@ -12,7 +24,7 @@ RSpec.describe 'Jobs/Build.gitlab-ci.yml' do let(:default_branch) { 'master' } let(:pipeline_ref) { default_branch } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } before do diff --git a/spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb index d88d9782021..16c5d7a4b6d 100644 --- a/spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb @@ -12,7 +12,7 @@ RSpec.describe 'Jobs/Code-Quality.gitlab-ci.yml' do let(:default_branch) { 'master' } let(:pipeline_ref) { default_branch } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } before do @@ -62,7 +62,8 @@ RSpec.describe 'Jobs/Code-Quality.gitlab-ci.yml' do context 'on master' do it 'has no jobs' do - expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError) + expect(build_names).to be_empty + expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."]) end end @@ -70,7 +71,8 @@ RSpec.describe 'Jobs/Code-Quality.gitlab-ci.yml' do let(:pipeline_ref) { 'feature' } it 'has no jobs' do - expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError) + expect(build_names).to be_empty + expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."]) end end @@ -78,7 +80,8 @@ RSpec.describe 'Jobs/Code-Quality.gitlab-ci.yml' do let(:pipeline_ref) { 'v1.0.0' } it 'has no jobs' do - expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError) + expect(build_names).to be_empty + expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."]) end end end diff --git a/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb index b657f73fa77..acb296082b8 100644 --- a/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe 'Jobs/Deploy.gitlab-ci.yml' do + include Ci::TemplateHelpers + subject(:template) do <<~YAML stages: @@ -26,6 +28,17 @@ RSpec.describe 'Jobs/Deploy.gitlab-ci.yml' do YAML end + describe 'AUTO_DEPLOY_IMAGE_VERSION' do + it 'corresponds to a published image in the registry' do + template = Gitlab::Template::GitlabCiYmlTemplate.find('Jobs/Deploy') + registry = "https://#{template_registry_host}" + repository = "gitlab-org/cluster-integration/auto-deploy-image" + reference = YAML.safe_load(template.content, aliases: true).dig('variables', 'AUTO_DEPLOY_IMAGE_VERSION') + + expect(public_image_exist?(registry, repository, reference)).to be true + end + end + describe 'the created pipeline' do let_it_be(:project, refind: true) { create(:project, :repository) } @@ -33,7 +46,7 @@ RSpec.describe 'Jobs/Deploy.gitlab-ci.yml' do let(:default_branch) { 'master' } let(:pipeline_ref) { default_branch } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } before do diff --git a/spec/lib/gitlab/ci/templates/Jobs/sast_iac_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/sast_iac_gitlab_ci_yaml_spec.rb index 85516d0bbb0..8a5aea7c0f0 100644 --- a/spec/lib/gitlab/ci/templates/Jobs/sast_iac_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/Jobs/sast_iac_gitlab_ci_yaml_spec.rb @@ -9,10 +9,10 @@ RSpec.describe 'Jobs/SAST-IaC.gitlab-ci.yml' do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { project.first_owner } - let(:default_branch) { 'main' } + let(:default_branch) { "master" } let(:pipeline_ref) { default_branch } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } before do @@ -49,7 +49,8 @@ RSpec.describe 'Jobs/SAST-IaC.gitlab-ci.yml' do context 'on default branch' do it 'has no jobs' do - expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError) + expect(build_names).to be_empty + expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."]) end end @@ -57,7 +58,8 @@ RSpec.describe 'Jobs/SAST-IaC.gitlab-ci.yml' do let(:pipeline_ref) { 'feature' } it 'has no jobs' do - expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError) + expect(build_names).to be_empty + expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."]) end end end diff --git a/spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb index 5ff179b6fee..d540b035f81 100644 --- a/spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb @@ -9,10 +9,10 @@ RSpec.describe 'Jobs/SAST-IaC.latest.gitlab-ci.yml' do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { project.first_owner } - let(:default_branch) { 'main' } + let(:default_branch) { "master" } let(:pipeline_ref) { default_branch } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } before do @@ -50,7 +50,8 @@ RSpec.describe 'Jobs/SAST-IaC.latest.gitlab-ci.yml' do context 'on default branch' do it 'has no jobs' do - expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError) + expect(build_names).to be_empty + expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."]) end end @@ -58,7 +59,8 @@ RSpec.describe 'Jobs/SAST-IaC.latest.gitlab-ci.yml' do let(:pipeline_ref) { 'feature' } it 'has no jobs' do - expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError) + expect(build_names).to be_empty + expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."]) end end end diff --git a/spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb index a92a8397e96..7cf0cf3ed33 100644 --- a/spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb @@ -12,7 +12,7 @@ RSpec.describe 'Jobs/Test.gitlab-ci.yml' do let(:default_branch) { 'master' } let(:pipeline_ref) { default_branch } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } before do @@ -62,7 +62,8 @@ RSpec.describe 'Jobs/Test.gitlab-ci.yml' do context 'on master' do it 'has no jobs' do - expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError) + expect(build_names).to be_empty + expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."]) end end @@ -70,7 +71,8 @@ RSpec.describe 'Jobs/Test.gitlab-ci.yml' do let(:pipeline_ref) { 'feature' } it 'has no jobs' do - expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError) + expect(build_names).to be_empty + expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."]) end end @@ -78,7 +80,8 @@ RSpec.describe 'Jobs/Test.gitlab-ci.yml' do let(:pipeline_ref) { 'v1.0.0' } it 'has no jobs' do - expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError) + expect(build_names).to be_empty + expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."]) end end end diff --git a/spec/lib/gitlab/ci/templates/MATLAB_spec.rb b/spec/lib/gitlab/ci/templates/MATLAB_spec.rb index 432040c4a14..3889d1fc8c9 100644 --- a/spec/lib/gitlab/ci/templates/MATLAB_spec.rb +++ b/spec/lib/gitlab/ci/templates/MATLAB_spec.rb @@ -12,7 +12,7 @@ RSpec.describe 'MATLAB.gitlab-ci.yml' do let(:default_branch) { 'master' } let(:pipeline_branch) { default_branch } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } before do diff --git a/spec/lib/gitlab/ci/templates/Terraform/base_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Terraform/base_gitlab_ci_yaml_spec.rb index eca79f37779..42df924f8fd 100644 --- a/spec/lib/gitlab/ci/templates/Terraform/base_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/Terraform/base_gitlab_ci_yaml_spec.rb @@ -11,7 +11,7 @@ RSpec.describe 'Terraform/Base.gitlab-ci.yml' do let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) } let(:user) { project.first_owner } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } before do diff --git a/spec/lib/gitlab/ci/templates/Terraform/base_latest_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Terraform/base_latest_gitlab_ci_yaml_spec.rb index 0ab81f97f20..332708ffa13 100644 --- a/spec/lib/gitlab/ci/templates/Terraform/base_latest_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/Terraform/base_latest_gitlab_ci_yaml_spec.rb @@ -11,7 +11,7 @@ RSpec.describe 'Terraform/Base.latest.gitlab-ci.yml' do let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) } let(:user) { project.first_owner } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } before do diff --git a/spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb index d6c7cd32f79..0f0192ad38f 100644 --- a/spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb @@ -25,7 +25,7 @@ RSpec.describe 'Verify/Load-Performance-Testing.gitlab-ci.yml' do let(:default_branch) { 'master' } let(:pipeline_ref) { default_branch } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } before do diff --git a/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb index 1a909f52ec3..b2ca906e172 100644 --- a/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb @@ -17,7 +17,7 @@ RSpec.describe 'Auto-DevOps.gitlab-ci.yml' do let(:project) { create(:project, :auto_devops, :custom_repo, files: { 'README.md' => '' }) } let(:user) { project.first_owner } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } before do diff --git a/spec/lib/gitlab/ci/templates/flutter_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/flutter_gitlab_ci_yaml_spec.rb index de94eec09fe..afb7773ad7a 100644 --- a/spec/lib/gitlab/ci/templates/flutter_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/flutter_gitlab_ci_yaml_spec.rb @@ -10,7 +10,7 @@ RSpec.describe 'Flutter.gitlab-ci.yml' do let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) } let(:user) { project.first_owner } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } before do diff --git a/spec/lib/gitlab/ci/templates/kaniko_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/kaniko_gitlab_ci_yaml_spec.rb index ebf52e6d65a..62e4188f59b 100644 --- a/spec/lib/gitlab/ci/templates/kaniko_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/kaniko_gitlab_ci_yaml_spec.rb @@ -10,7 +10,7 @@ RSpec.describe 'Kaniko.gitlab-ci.yml' do let(:project) { create(:project, :custom_repo, files: { 'Dockerfile' => 'FROM alpine:latest' }) } let(:user) { project.first_owner } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } before do diff --git a/spec/lib/gitlab/ci/templates/katalon_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/katalon_gitlab_ci_yaml_spec.rb index 5a62324da74..a44833b0c01 100644 --- a/spec/lib/gitlab/ci/templates/katalon_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/katalon_gitlab_ci_yaml_spec.rb @@ -27,7 +27,7 @@ RSpec.describe 'Katalon.gitlab-ci.yml' do let(:user) { project.first_owner } let(:service) { Ci::CreatePipelineService.new(project, user, ref: 'master' ) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } before do diff --git a/spec/lib/gitlab/ci/templates/npm_spec.rb b/spec/lib/gitlab/ci/templates/npm_spec.rb index d86a3a67823..55fd4675f11 100644 --- a/spec/lib/gitlab/ci/templates/npm_spec.rb +++ b/spec/lib/gitlab/ci/templates/npm_spec.rb @@ -14,7 +14,7 @@ RSpec.describe 'npm.gitlab-ci.yml' do let(:pipeline_tag) { 'v1.2.1' } let(:pipeline_ref) { pipeline_branch } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref ) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } def create_branch(name:) @@ -42,7 +42,8 @@ RSpec.describe 'npm.gitlab-ci.yml' do shared_examples 'no pipeline created' do it 'does not create a pipeline because the only job (publish) is not created' do - expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError, 'No stages / jobs for this pipeline.') + expect(build_names).to be_empty + expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."]) end end diff --git a/spec/lib/gitlab/ci/templates/terraform_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/terraform_gitlab_ci_yaml_spec.rb index 2fc4b509aab..aa7d0249066 100644 --- a/spec/lib/gitlab/ci/templates/terraform_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/terraform_gitlab_ci_yaml_spec.rb @@ -12,10 +12,10 @@ RSpec.describe 'Terraform.gitlab-ci.yml' do describe 'the created pipeline' do let(:default_branch) { project.default_branch_or_main } let(:pipeline_branch) { default_branch } - let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) } + let_it_be(:project) { create(:project, :repository, create_branch: 'patch-1') } let(:user) { project.first_owner } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } before do @@ -27,23 +27,30 @@ RSpec.describe 'Terraform.gitlab-ci.yml' do end context 'on master branch' do - it 'creates init, validate and build jobs', :aggregate_failures do + it 'creates init, validate,build terraform jobs as well as kics-iac-sast job', :aggregate_failures do expect(pipeline.errors).to be_empty - expect(build_names).to include('validate', 'build', 'deploy') + expect(build_names).to include('kics-iac-sast', 'validate', 'build', 'deploy') end end context 'outside the master branch' do let(:pipeline_branch) { 'patch-1' } - before do - project.repository.create_branch(pipeline_branch, default_branch) - end - it 'does not creates a deploy and a test job', :aggregate_failures do expect(pipeline.errors).to be_empty expect(build_names).not_to include('deploy') end end + + context 'on merge request' do + let(:service) { MergeRequests::CreatePipelineService.new(project: project, current_user: user) } + let(:merge_request) { create(:merge_request, :simple, source_project: project) } + let(:pipeline) { service.execute(merge_request).payload } + + it 'creates a pipeline with no jobs' do + expect(pipeline).to be_merge_request_event + expect(pipeline.builds.count).to be_zero + end + end end end diff --git a/spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb index 42e56c4ab3c..6ae51f9783b 100644 --- a/spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb @@ -12,10 +12,10 @@ RSpec.describe 'Terraform.latest.gitlab-ci.yml' do describe 'the created pipeline' do let(:default_branch) { project.default_branch_or_main } let(:pipeline_branch) { default_branch } - let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) } - let(:user) { project.first_owner } + let_it_be(:project) { create(:project, :repository, create_branch: 'patch-1') } + let_it_be(:user) { project.first_owner } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } before do @@ -36,14 +36,38 @@ RSpec.describe 'Terraform.latest.gitlab-ci.yml' do context 'outside the master branch' do let(:pipeline_branch) { 'patch-1' } - before do - project.repository.create_branch(pipeline_branch, default_branch) - end - it 'does not creates a deploy and a test job', :aggregate_failures do expect(pipeline.errors).to be_empty expect(build_names).not_to include('deploy') end end + + context 'on merge request' do + let(:pipeline_branch) { 'patch-1' } + let(:mr_service) { MergeRequests::CreatePipelineService.new(project: project, current_user: user) } + let(:merge_request) { create(:merge_request, :simple, source_project: project, source_branch: pipeline_branch ) } + let(:mr_pipeline) { mr_service.execute(merge_request).payload } + let(:mr_build_names) { mr_pipeline.builds.pluck(:name) } + let(:branch_service) { Ci::CreatePipelineService.new(project, user, ref: merge_request.source_branch ) } + let(:branch_pipeline) { branch_service.execute(:push).payload } + let(:branch_build_names) { branch_pipeline.builds.pluck(:name) } + + # This is needed so that the terraform artifacts and sast_iac artifacts + # are both available in the MR + it 'creates a pipeline with the terraform and sast_iac jobs' do + expect(mr_pipeline).to be_merge_request_event + expect(mr_pipeline.errors.full_messages).to be_empty + expect(mr_build_names).to include('kics-iac-sast', 'validate', 'build') + end + + it 'does not creates a deploy', :aggregate_failures do + expect(mr_build_names).not_to include('deploy') + end + + it 'does not create a branch pipeline', :aggregate_failures do + expect(branch_build_names).to be_empty + expect(branch_pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."]) + end + end end end diff --git a/spec/lib/gitlab/ci/templates/themekit_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/themekit_gitlab_ci_yaml_spec.rb index 4708108f404..157fd39f1cc 100644 --- a/spec/lib/gitlab/ci/templates/themekit_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/themekit_gitlab_ci_yaml_spec.rb @@ -14,7 +14,7 @@ RSpec.describe 'ThemeKit.gitlab-ci.yml' do let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) } let(:user) { project.first_owner } let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) } - let(:pipeline) { service.execute!(:push).payload } + let(:pipeline) { service.execute(:push).payload } let(:build_names) { pipeline.builds.pluck(:name) } before do @@ -51,9 +51,8 @@ RSpec.describe 'ThemeKit.gitlab-ci.yml' do end it 'has no jobs' do - expect { pipeline }.to raise_error( - Ci::CreatePipelineService::CreateError, 'No stages / jobs for this pipeline.' - ) + expect(build_names).to be_empty + expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."]) end end end diff --git a/spec/lib/gitlab/ci/variables/collection/item_spec.rb b/spec/lib/gitlab/ci/variables/collection/item_spec.rb index 9443bf6d6d5..f7c6f7f51df 100644 --- a/spec/lib/gitlab/ci/variables/collection/item_spec.rb +++ b/spec/lib/gitlab/ci/variables/collection/item_spec.rb @@ -197,11 +197,11 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item do end end - describe '#raw' do + describe '#raw?' do it 'returns false when :raw is not specified' do item = described_class.new(**variable) - expect(item.raw).to eq false + expect(item.raw?).to eq false end context 'when :raw is specified as true' do @@ -212,7 +212,7 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item do it 'returns true' do item = described_class.new(**variable) - expect(item.raw).to eq true + expect(item.raw?).to eq true end end end diff --git a/spec/lib/gitlab/ci/variables/collection_spec.rb b/spec/lib/gitlab/ci/variables/collection_spec.rb index 7d4a1eef70b..10b8f0065d9 100644 --- a/spec/lib/gitlab/ci/variables/collection_spec.rb +++ b/spec/lib/gitlab/ci/variables/collection_spec.rb @@ -300,7 +300,6 @@ RSpec.describe Gitlab::Ci::Variables::Collection do Gitlab::Ci::Variables::Collection.new .append(key: 'CI_JOB_NAME', value: 'test-1') .append(key: 'CI_BUILD_ID', value: '1') - .append(key: 'RAW_VAR', value: '$TEST1', raw: true) .append(key: 'TEST1', value: 'test-3') .append(key: 'FILEVAR1', value: 'file value 1', file: true) end @@ -322,10 +321,6 @@ RSpec.describe Gitlab::Ci::Variables::Collection do value: 'key${TEST1}-${CI_JOB_NAME}', result: 'keytest-3-test-1' }, - "complex expansions with raw variable": { - value: 'key${RAW_VAR}-${CI_JOB_NAME}', - result: 'key$TEST1-test-1' - }, "missing variable not keeping original": { value: 'key${MISSING_VAR}-${CI_JOB_NAME}', result: 'key-test-1' @@ -339,22 +334,22 @@ RSpec.describe Gitlab::Ci::Variables::Collection do value: 'key-$TEST1-%%HOME%%-$${HOME}', result: 'key-test-3-%%HOME%%-$${HOME}' }, - "file variable with expand_file_vars: true": { + "file variable with expand_file_refs: true": { value: 'key-$FILEVAR1-$TEST1', result: 'key-file value 1-test-3' }, - "file variable with expand_file_vars: false": { + "file variable with expand_file_refs: false": { value: 'key-$FILEVAR1-$TEST1', result: 'key-$FILEVAR1-test-3', - expand_file_vars: false + expand_file_refs: false } } end with_them do - let(:options) { { keep_undefined: keep_undefined, expand_file_vars: expand_file_vars }.compact } + let(:options) { { keep_undefined: keep_undefined, expand_file_refs: expand_file_refs }.compact } - subject(:result) { collection.expand_value(value, **options) } + subject(:expanded_result) { collection.expand_value(value, **options) } it 'matches expected expansion' do is_expected.to eq(result) @@ -509,17 +504,35 @@ RSpec.describe Gitlab::Ci::Variables::Collection do { key: 'variable4', value: 'keyvalue${variable2}value3' } ] }, - "complex expansions with raw variable": { + "complex expansions with raw variable with expand_raw_refs: true (default)": { + variables: [ + { key: 'variable1', value: 'value1' }, + { key: 'raw_var', value: 'raw-$variable1', raw: true }, + { key: 'nonraw_var', value: 'nonraw-$variable1' }, + { key: 'variable2', value: '$raw_var and $nonraw_var' } + ], + keep_undefined: false, + result: [ + { key: 'variable1', value: 'value1' }, + { key: 'raw_var', value: 'raw-$variable1', raw: true }, + { key: 'nonraw_var', value: 'nonraw-value1' }, + { key: 'variable2', value: 'raw-$variable1 and nonraw-value1' } + ] + }, + "complex expansions with raw variable with expand_raw_refs: false": { variables: [ - { key: 'variable3', value: 'key_${variable}_${variable2}' }, - { key: 'variable', value: '$variable2', raw: true }, - { key: 'variable2', value: 'value2' } + { key: 'variable1', value: 'value1' }, + { key: 'raw_var', value: 'raw-$variable1', raw: true }, + { key: 'nonraw_var', value: 'nonraw-$variable1' }, + { key: 'variable2', value: '$raw_var and $nonraw_var' } ], keep_undefined: false, + expand_raw_refs: false, result: [ - { key: 'variable', value: '$variable2', raw: true }, - { key: 'variable2', value: 'value2' }, - { key: 'variable3', value: 'key_$variable2_value2' } + { key: 'variable1', value: 'value1' }, + { key: 'raw_var', value: 'raw-$variable1', raw: true }, + { key: 'nonraw_var', value: 'nonraw-value1' }, + { key: 'variable2', value: '$raw_var and nonraw-value1' } ] }, "variable value referencing password with special characters": { @@ -553,8 +566,9 @@ RSpec.describe Gitlab::Ci::Variables::Collection do with_them do let(:collection) { Gitlab::Ci::Variables::Collection.new(variables) } + let(:options) { { keep_undefined: keep_undefined, expand_raw_refs: expand_raw_refs }.compact } - subject { collection.sort_and_expand_all(keep_undefined: keep_undefined) } + subject(:expanded_result) { collection.sort_and_expand_all(**options) } it 'returns Collection' do is_expected.to be_an_instance_of(Gitlab::Ci::Variables::Collection) @@ -601,7 +615,8 @@ RSpec.describe Gitlab::Ci::Variables::Collection do it 'logs file_variable_is_referenced_in_another_variable once for VAR5' do expect(Gitlab::AppJsonLogger).to receive(:info).with( event: 'file_variable_is_referenced_in_another_variable', - project_id: project.id + project_id: project.id, + variable: 'FILEVAR4' ).once sort_and_expand_all diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb index ebf8422489e..5de813f7739 100644 --- a/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -1071,6 +1071,7 @@ module Gitlab let(:build) { execute.builds.first } let(:job_variables) { build[:job_variables] } + let(:root_variables) { execute.root_variables } let(:root_variables_inheritance) { build[:root_variables_inheritance] } context 'when global variables are defined' do @@ -1193,6 +1194,78 @@ module Gitlab expect(root_variables_inheritance).to eq(true) end end + + context 'when variables have data other than value' do + let(:config) do + <<~YAML + variables: + VAR1: value1 + VAR2: + value: value2 + description: description2 + VAR3: + value: value3 + expand: false + + rspec: + script: rspec + variables: + VAR4: value4 + VAR5: + value: value5 + expand: false + VAR6: + value: value6 + expand: true + YAML + end + + it 'returns variables' do + expect(job_variables).to contain_exactly( + { key: 'VAR4', value: 'value4' }, + { key: 'VAR5', value: 'value5', raw: true }, + { key: 'VAR6', value: 'value6', raw: false } + ) + + expect(execute.root_variables).to contain_exactly( + { key: 'VAR1', value: 'value1' }, + { key: 'VAR2', value: 'value2' }, + { key: 'VAR3', value: 'value3', raw: true } + ) + + expect(execute.root_variables_with_prefill_data).to eq( + 'VAR1' => { value: 'value1' }, + 'VAR2' => { value: 'value2', description: 'description2' }, + 'VAR3' => { value: 'value3', raw: true } + ) + end + + context 'when the FF ci_raw_variables_in_yaml_config is disabled' do + before do + stub_feature_flags(ci_raw_variables_in_yaml_config: false) + end + + it 'returns variables without description and raw' do + expect(job_variables).to contain_exactly( + { key: 'VAR4', value: 'value4' }, + { key: 'VAR5', value: 'value5' }, + { key: 'VAR6', value: 'value6' } + ) + + expect(execute.root_variables).to contain_exactly( + { key: 'VAR1', value: 'value1' }, + { key: 'VAR2', value: 'value2' }, + { key: 'VAR3', value: 'value3' } + ) + + expect(execute.root_variables_with_prefill_data).to eq( + 'VAR1' => { value: 'value1' }, + 'VAR2' => { value: 'value2', description: 'description2' }, + 'VAR3' => { value: 'value3' } + ) + end + end + end end context 'when using `extends`' do @@ -1334,7 +1407,7 @@ module Gitlab context "when an array of wrong keyed object is provided" do let(:include_content) { [{ yolo: "/local.gitlab-ci.yml" }] } - it_behaves_like 'returns errors', /needs to match exactly one accessor/ + it_behaves_like 'returns errors', /does not have a valid subkey for include/ end context "when an array of mixed typed objects is provided" do @@ -1359,7 +1432,7 @@ module Gitlab context "when the include type is incorrect" do let(:include_content) { { name: "/local.gitlab-ci.yml" } } - it_behaves_like 'returns errors', /needs to match exactly one accessor/ + it_behaves_like 'returns errors', /does not have a valid subkey for include/ end end |