diff options
Diffstat (limited to 'spec/lib/gitlab/ci')
36 files changed, 674 insertions, 271 deletions
diff --git a/spec/lib/gitlab/ci/ansi2json_spec.rb b/spec/lib/gitlab/ci/ansi2json_spec.rb index c9c0d1a744e..f9d23ff97bc 100644 --- a/spec/lib/gitlab/ci/ansi2json_spec.rb +++ b/spec/lib/gitlab/ci/ansi2json_spec.rb @@ -27,6 +27,17 @@ RSpec.describe Gitlab::Ci::Ansi2json do ]) end + it 'ignores empty newlines' do + expect(convert_json("Hello\n\nworld")).to eq([ + { offset: 0, content: [{ text: 'Hello' }] }, + { offset: 7, content: [{ text: 'world' }] } + ]) + expect(convert_json("Hello\r\n\r\nworld")).to eq([ + { offset: 0, content: [{ text: 'Hello' }] }, + { offset: 9, content: [{ text: 'world' }] } + ]) + end + it 'replace the current line when encountering \r' do expect(convert_json("Hello\rworld")).to eq([ { offset: 0, content: [{ text: 'world' }] } diff --git a/spec/lib/gitlab/ci/build/rules/rule/clause/if_spec.rb b/spec/lib/gitlab/ci/build/rules/rule/clause/if_spec.rb new file mode 100644 index 00000000000..81bce989833 --- /dev/null +++ b/spec/lib/gitlab/ci/build/rules/rule/clause/if_spec.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' +require 'support/helpers/stubbed_feature' +require 'support/helpers/stub_feature_flags' + +RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::If do + include StubFeatureFlags + + subject(:if_clause) { described_class.new(expression) } + + describe '#satisfied_by?' do + let(:context_class) { Gitlab::Ci::Build::Context::Base } + let(:rules_context) { instance_double(context_class, variables_hash: {}) } + + subject(:satisfied_by?) { if_clause.satisfied_by?(nil, rules_context) } + + context 'when expression is a basic string comparison' do + context 'when comparison is true' do + let(:expression) { '"value" == "value"' } + + it { is_expected.to eq(true) } + end + + context 'when comparison is false' do + let(:expression) { '"value" == "other"' } + + it { is_expected.to eq(false) } + end + end + + context 'when expression is a regexp' do + context 'when comparison is true' do + let(:expression) { '"abcde" =~ /^ab.*/' } + + it { is_expected.to eq(true) } + end + + context 'when comparison is false' do + let(:expression) { '"abcde" =~ /^af.*/' } + + it { is_expected.to eq(false) } + end + + context 'when both side of the expression are variables' do + let(:expression) { '$teststring =~ $pattern' } + + context 'when comparison is true' do + let(:rules_context) do + instance_double(context_class, variables_hash: { 'teststring' => 'abcde', 'pattern' => '/^ab.*/' }) + end + + it { is_expected.to eq(true) } + + context 'when the FF ci_fix_rules_if_comparison_with_regexp_variable is disabled' do + before do + stub_feature_flags(ci_fix_rules_if_comparison_with_regexp_variable: false) + end + + it { is_expected.to eq(false) } + end + end + + context 'when comparison is false' do + let(:rules_context) do + instance_double(context_class, variables_hash: { 'teststring' => 'abcde', 'pattern' => '/^af.*/' }) + end + + it { is_expected.to eq(false) } + end + end + end + end +end diff --git a/spec/lib/gitlab/ci/build/rules_spec.rb b/spec/lib/gitlab/ci/build/rules_spec.rb index 37bfdca4d1d..e82dcd0254d 100644 --- a/spec/lib/gitlab/ci/build/rules_spec.rb +++ b/spec/lib/gitlab/ci/build/rules_spec.rb @@ -188,6 +188,19 @@ RSpec.describe Gitlab::Ci::Build::Rules do it { is_expected.to eq(described_class::Result.new('on_success', nil, nil, { MY_VAR: 'my var' })) } end end + + context 'with a regexp variable matching rule' do + let(:rule_list) { [{ if: '"abcde" =~ $pattern' }] } + + before do + allow(ci_build).to receive(:scoped_variables).and_return( + Gitlab::Ci::Variables::Collection.new + .append(key: 'pattern', value: '/^ab.*/', public: true) + ) + end + + it { is_expected.to eq(described_class::Result.new('on_success')) } + end end describe 'Gitlab::Ci::Build::Rules::Result' do diff --git a/spec/lib/gitlab/ci/config/entry/environment_spec.rb b/spec/lib/gitlab/ci/config/entry/environment_spec.rb index dd8a79f0d84..36c26c8ee4f 100644 --- a/spec/lib/gitlab/ci/config/entry/environment_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/environment_spec.rb @@ -92,24 +92,18 @@ RSpec.describe Gitlab::Ci::Config::Entry::Environment do end context 'when valid action is used' do - let(:config) do - { name: 'production', - action: 'start' } - end - - it 'is valid' do - expect(entry).to be_valid + where(:action) do + %w(start stop prepare verify access) end - end - context 'when prepare action is used' do - let(:config) do - { name: 'production', - action: 'prepare' } - end + with_them do + let(:config) do + { name: 'production', action: action } + end - it 'is valid' do - expect(entry).to be_valid + it 'is valid' do + expect(entry).to be_valid + end end end @@ -148,7 +142,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Environment do describe '#errors' do it 'contains error about invalid action' do expect(entry.errors) - .to include 'environment action should be start, stop or prepare' + .to include 'environment action should be start, stop, prepare, verify, or access' 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 97691504abd..ca336c3ecaa 100644 --- a/spec/lib/gitlab/ci/config/entry/job_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb @@ -27,7 +27,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do subject { described_class.nodes.keys } let(:result) do - %i[before_script script stage type after_script cache + %i[before_script script stage after_script cache image services only except rules needs variables artifacts environment coverage retry interruptible timeout release tags inherit parallel] diff --git a/spec/lib/gitlab/ci/config/entry/reports_spec.rb b/spec/lib/gitlab/ci/config/entry/reports_spec.rb index 061d8f34c8d..051cccb4833 100644 --- a/spec/lib/gitlab/ci/config/entry/reports_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/reports_spec.rb @@ -45,10 +45,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Reports do :load_performance | 'load-performance.json' :lsif | 'lsif.json' :dotenv | 'build.dotenv' - :cobertura | 'cobertura-coverage.xml' :terraform | 'tfplan.json' :accessibility | 'gl-accessibility.json' - :cluster_applications | 'gl-cluster-applications.json' end with_them do @@ -90,18 +88,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Reports do expect(entry.value).to eq({ coverage_report: coverage_report, dast: ['gl-dast-report.json'] }) end end - - context 'and a direct coverage report format is specified' do - let(:config) { { coverage_report: coverage_report, cobertura: 'cobertura-coverage.xml' } } - - it 'is not valid' do - expect(entry).not_to be_valid - end - - it 'reports error' do - expect(entry.errors).to include /please use only one the following keys: coverage_report, cobertura/ - 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 b9c32bc51be..55ad119ea21 100644 --- a/spec/lib/gitlab/ci/config/entry/root_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb @@ -21,7 +21,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do # The purpose of `Root` is have only globally defined configuration. expect(described_class.nodes.keys) .to match_array(%i[before_script image services after_script - variables cache stages types include default workflow]) + variables cache stages include default workflow]) end end end @@ -55,41 +55,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do } end - context 'when deprecated types/type keywords are defined' do - let(:project) { create(:project, :repository) } - let(:user) { create(:user) } - - let(:hash) do - { types: %w(test deploy), - rspec: { script: 'rspec', type: 'test' } } - end - - before do - root.compose! - end - - it 'returns array of types as stages with a warning' do - expect(root.jobs_value[:rspec][:stage]).to eq 'test' - expect(root.stages_value).to eq %w[test deploy] - expect(root.warnings).to match_array([ - "root `types` is deprecated in 9.0 and will be removed in 15.0.", - "jobs:rspec `type` is deprecated in 9.0 and will be removed in 15.0." - ]) - end - - it 'logs usage of keywords' do - expect(Gitlab::AppJsonLogger).to( - receive(:info) - .with(event: 'ci_used_deprecated_keyword', - entry: root[:stages].key.to_s, - user_id: user.id, - project_id: project.id) - ) - - root.compose! - end - end - describe '#compose!' do before do root.compose! diff --git a/spec/lib/gitlab/ci/config/external/file/local_spec.rb b/spec/lib/gitlab/ci/config/external/file/local_spec.rb index c0a0b0009ce..0e78498c98e 100644 --- a/spec/lib/gitlab/ci/config/external/file/local_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/local_spec.rb @@ -199,6 +199,8 @@ RSpec.describe Gitlab::Ci::Config::External::File::Local do context_sha: '12345', type: :local, location: location, + blob: "http://localhost/#{project.full_path}/-/blob/12345/lib/gitlab/ci/templates/existent-file.yml", + raw: "http://localhost/#{project.full_path}/-/raw/12345/lib/gitlab/ci/templates/existent-file.yml", extra: {} ) } diff --git a/spec/lib/gitlab/ci/config/external/file/project_spec.rb b/spec/lib/gitlab/ci/config/external/file/project_spec.rb index 5d3412a148b..77e542cf933 100644 --- a/spec/lib/gitlab/ci/config/external/file/project_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/project_spec.rb @@ -207,6 +207,8 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project do context_sha: '12345', type: :file, location: '/file.yml', + blob: "http://localhost/#{project.full_path}/-/blob/#{project.commit('master').id}/file.yml", + raw: "http://localhost/#{project.full_path}/-/raw/#{project.commit('master').id}/file.yml", extra: { project: project.full_path, ref: 'HEAD' } ) } @@ -227,6 +229,8 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project do context_sha: '12345', type: :file, location: '/file.yml', + blob: nil, + raw: nil, extra: { project: 'xxxxxxxxxxxxxxxxxxxxxxxx', ref: 'xxxxxxxxxxxxxxxxxxxxxxxx' } ) } diff --git a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb index 5c07c87fd5a..3e1c4df4e32 100644 --- a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb @@ -213,6 +213,8 @@ RSpec.describe Gitlab::Ci::Config::External::File::Remote do context_sha: '12345', type: :remote, location: 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.xxxxxxxxxxx.yml', + raw: 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.xxxxxxxxxxx.yml', + blob: nil, extra: {} ) } diff --git a/spec/lib/gitlab/ci/config/external/file/template_spec.rb b/spec/lib/gitlab/ci/config/external/file/template_spec.rb index 4da9a933a9f..074e7a1d32d 100644 --- a/spec/lib/gitlab/ci/config/external/file/template_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/template_spec.rb @@ -124,6 +124,8 @@ RSpec.describe Gitlab::Ci::Config::External::File::Template do context_sha: '12345', type: :template, location: template, + raw: "https://gitlab.com/gitlab-org/gitlab/-/raw/master/lib/gitlab/ci/templates/#{template}", + blob: nil, extra: {} ) } diff --git a/spec/lib/gitlab/ci/config/external/processor_spec.rb b/spec/lib/gitlab/ci/config/external/processor_spec.rb index 56cd006717e..15a0ff40aa4 100644 --- a/spec/lib/gitlab/ci/config/external/processor_spec.rb +++ b/spec/lib/gitlab/ci/config/external/processor_spec.rb @@ -267,11 +267,41 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do perform expect(context.includes).to contain_exactly( - { type: :local, location: '/local/file.yml', extra: {}, context_project: project.full_path, context_sha: '12345' }, - { type: :template, location: 'Ruby.gitlab-ci.yml', extra: {}, context_project: project.full_path, context_sha: '12345' }, - { type: :remote, location: 'http://my.domain.com/config.yml', extra: {}, context_project: project.full_path, context_sha: '12345' }, - { type: :file, location: '/templates/my-workflow.yml', extra: { project: another_project.full_path, ref: 'HEAD' }, context_project: project.full_path, context_sha: '12345' }, - { type: :local, location: '/templates/my-build.yml', extra: {}, context_project: another_project.full_path, context_sha: another_project.commit.sha } + { type: :local, + location: '/local/file.yml', + blob: "http://localhost/#{project.full_path}/-/blob/12345/local/file.yml", + raw: "http://localhost/#{project.full_path}/-/raw/12345/local/file.yml", + extra: {}, + context_project: project.full_path, + context_sha: '12345' }, + { type: :template, + location: 'Ruby.gitlab-ci.yml', + blob: nil, + raw: 'https://gitlab.com/gitlab-org/gitlab/-/raw/master/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml', + extra: {}, + context_project: project.full_path, + context_sha: '12345' }, + { type: :remote, + location: 'http://my.domain.com/config.yml', + blob: nil, + raw: "http://my.domain.com/config.yml", + extra: {}, + context_project: project.full_path, + context_sha: '12345' }, + { type: :file, + location: '/templates/my-workflow.yml', + blob: "http://localhost/#{another_project.full_path}/-/blob/#{another_project.commit.sha}/templates/my-workflow.yml", + raw: "http://localhost/#{another_project.full_path}/-/raw/#{another_project.commit.sha}/templates/my-workflow.yml", + extra: { project: another_project.full_path, ref: 'HEAD' }, + context_project: project.full_path, + context_sha: '12345' }, + { type: :local, + location: '/templates/my-build.yml', + blob: "http://localhost/#{another_project.full_path}/-/blob/#{another_project.commit.sha}/templates/my-build.yml", + raw: "http://localhost/#{another_project.full_path}/-/raw/#{another_project.commit.sha}/templates/my-build.yml", + extra: {}, + context_project: another_project.full_path, + context_sha: another_project.commit.sha } ) end end @@ -394,8 +424,20 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do perform expect(context.includes).to contain_exactly( - { type: :file, location: '/templates/my-build.yml', extra: { project: another_project.full_path, ref: 'HEAD' }, context_project: project.full_path, context_sha: '12345' }, - { type: :file, location: '/templates/my-test.yml', extra: { project: another_project.full_path, ref: 'HEAD' }, context_project: project.full_path, context_sha: '12345' } + { type: :file, + location: '/templates/my-build.yml', + blob: "http://localhost/#{another_project.full_path}/-/blob/#{another_project.commit.sha}/templates/my-build.yml", + raw: "http://localhost/#{another_project.full_path}/-/raw/#{another_project.commit.sha}/templates/my-build.yml", + extra: { project: another_project.full_path, ref: 'HEAD' }, + context_project: project.full_path, + context_sha: '12345' }, + { type: :file, + blob: "http://localhost/#{another_project.full_path}/-/blob/#{another_project.commit.sha}/templates/my-test.yml", + raw: "http://localhost/#{another_project.full_path}/-/raw/#{another_project.commit.sha}/templates/my-test.yml", + location: '/templates/my-test.yml', + extra: { project: another_project.full_path, ref: 'HEAD' }, + context_project: project.full_path, + context_sha: '12345' } ) end end @@ -438,8 +480,20 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do perform expect(context.includes).to contain_exactly( - { type: :local, location: 'myfolder/file1.yml', extra: {}, context_project: project.full_path, context_sha: '12345' }, - { type: :local, location: 'myfolder/file2.yml', extra: {}, context_project: project.full_path, context_sha: '12345' } + { type: :local, + location: 'myfolder/file1.yml', + blob: "http://localhost/#{project.full_path}/-/blob/12345/myfolder/file1.yml", + raw: "http://localhost/#{project.full_path}/-/raw/12345/myfolder/file1.yml", + extra: {}, + context_project: project.full_path, + context_sha: '12345' }, + { type: :local, + blob: "http://localhost/#{project.full_path}/-/blob/12345/myfolder/file2.yml", + raw: "http://localhost/#{project.full_path}/-/raw/12345/myfolder/file2.yml", + location: 'myfolder/file2.yml', + extra: {}, + context_project: project.full_path, + context_sha: '12345' } ) end end diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb index 3ba6a9059c6..5eb04d969eb 100644 --- a/spec/lib/gitlab/ci/config_spec.rb +++ b/spec/lib/gitlab/ci/config_spec.rb @@ -109,16 +109,22 @@ RSpec.describe Gitlab::Ci::Config do expect(config.metadata[:includes]).to contain_exactly( { type: :template, location: 'Jobs/Deploy.gitlab-ci.yml', + blob: nil, + raw: 'https://gitlab.com/gitlab-org/gitlab/-/raw/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml', extra: {}, context_project: nil, context_sha: nil }, { type: :template, location: 'Jobs/Build.gitlab-ci.yml', + blob: nil, + raw: 'https://gitlab.com/gitlab-org/gitlab/-/raw/master/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml', extra: {}, context_project: nil, context_sha: nil }, { type: :remote, location: 'https://example.com/gitlab-ci.yml', + blob: nil, + raw: 'https://example.com/gitlab-ci.yml', extra: {}, context_project: nil, context_sha: nil } @@ -428,16 +434,22 @@ RSpec.describe Gitlab::Ci::Config do expect(config.metadata[:includes]).to contain_exactly( { type: :local, location: local_location, + blob: "http://localhost/#{project.full_path}/-/blob/12345/#{local_location}", + raw: "http://localhost/#{project.full_path}/-/raw/12345/#{local_location}", extra: {}, context_project: project.full_path, context_sha: '12345' }, { type: :remote, location: remote_location, + blob: nil, + raw: remote_location, extra: {}, context_project: project.full_path, context_sha: '12345' }, { type: :file, location: '.gitlab-ci.yml', + blob: "http://localhost/#{main_project.full_path}/-/blob/#{main_project.commit.sha}/.gitlab-ci.yml", + raw: "http://localhost/#{main_project.full_path}/-/raw/#{main_project.commit.sha}/.gitlab-ci.yml", extra: { project: main_project.full_path, ref: 'HEAD' }, context_project: project.full_path, context_sha: '12345' } diff --git a/spec/lib/gitlab/ci/lint_spec.rb b/spec/lib/gitlab/ci/lint_spec.rb index 747ff13c840..7e0b2b5aa8e 100644 --- a/spec/lib/gitlab/ci/lint_spec.rb +++ b/spec/lib/gitlab/ci/lint_spec.rb @@ -62,7 +62,7 @@ RSpec.describe Gitlab::Ci::Lint do end end - shared_examples 'sets merged yaml' do + shared_examples 'sets config metadata' do let(:content) do <<~YAML :include: @@ -106,6 +106,20 @@ RSpec.describe Gitlab::Ci::Lint do expect(subject.merged_yaml).to eq(expected_config.to_yaml) end + + it 'sets includes' do + expect(subject.includes).to contain_exactly( + { + type: :local, + location: 'another-gitlab-ci.yml', + blob: "http://localhost/#{project.full_path}/-/blob/#{project.commit.sha}/another-gitlab-ci.yml", + raw: "http://localhost/#{project.full_path}/-/raw/#{project.commit.sha}/another-gitlab-ci.yml", + extra: {}, + context_project: project.full_path, + context_sha: project.commit.sha + } + ) + end end shared_examples 'content with errors and warnings' do @@ -220,7 +234,7 @@ RSpec.describe Gitlab::Ci::Lint do end end - it_behaves_like 'sets merged yaml' + it_behaves_like 'sets config metadata' include_context 'advanced validations' do it 'does not catch advanced logical errors' do @@ -275,7 +289,7 @@ RSpec.describe Gitlab::Ci::Lint do end end - it_behaves_like 'sets merged yaml' + it_behaves_like 'sets config metadata' include_context 'advanced validations' do it 'runs advanced logical validations' do diff --git a/spec/lib/gitlab/ci/parsers/security/common_spec.rb b/spec/lib/gitlab/ci/parsers/security/common_spec.rb index dfc5dec1481..6495d1f654b 100644 --- a/spec/lib/gitlab/ci/parsers/security/common_spec.rb +++ b/spec/lib/gitlab/ci/parsers/security/common_spec.rb @@ -292,7 +292,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do expect(scans.map(&:status).all?('success')).to be(true) expect(scans.map(&:start_time).all?('placeholder-value')).to be(true) expect(scans.map(&:end_time).all?('placeholder-value')).to be(true) - expect(scans.size).to eq(3) + expect(scans.size).to eq(7) expect(scans.first).to be_a(::Gitlab::Ci::Reports::Security::Scan) end @@ -348,22 +348,29 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do it 'returns links object for each finding', :aggregate_failures do links = report.findings.flat_map(&:links) - expect(links.map(&:url)).to match_array(['https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1020', 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1030']) - expect(links.map(&:name)).to match_array([nil, 'CVE-1030']) - expect(links.size).to eq(2) + expect(links.map(&:url)).to match_array(['https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1020', 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1030', + "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2137", "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2138", + "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2139", "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2140"]) + expect(links.map(&:name)).to match_array([nil, nil, nil, nil, nil, 'CVE-1030']) + expect(links.size).to eq(6) expect(links.first).to be_a(::Gitlab::Ci::Reports::Security::Link) end end describe 'parsing evidence' do - it 'returns evidence object for each finding', :aggregate_failures do - evidences = report.findings.map(&:evidence) + RSpec::Matchers.define_negated_matcher :have_values, :be_empty - expect(evidences.first.data).not_to be_empty - expect(evidences.first.data["summary"]).to match(/The Origin header was changed/) - expect(evidences.size).to eq(3) - expect(evidences.compact.size).to eq(2) - expect(evidences.first).to be_a(::Gitlab::Ci::Reports::Security::Evidence) + it 'returns evidence object for each finding', :aggregate_failures do + all_evidences = report.findings.map(&:evidence) + evidences = all_evidences.compact + data = evidences.map(&:data) + summaries = evidences.map { |e| e.data["summary"] } + + expect(all_evidences.size).to eq(7) + expect(evidences.size).to eq(2) + expect(evidences).to all( be_a(::Gitlab::Ci::Reports::Security::Evidence) ) + expect(data).to all( have_values ) + expect(summaries).to all( match(/The Origin header was changed/) ) end end diff --git a/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb b/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb index f6409c8b01f..d06077d69b6 100644 --- a/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb +++ b/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb @@ -5,6 +5,8 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do let_it_be(:project) { create(:project) } + let(:supported_dast_versions) { described_class::SUPPORTED_VERSIONS[:dast].join(', ') } + let(:scanner) do { 'id' => 'gemnasium', @@ -22,7 +24,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do expect(described_class::SUPPORTED_VERSIONS.keys).to eq(described_class::DEPRECATED_VERSIONS.keys) end - context 'files under schema path are explicitly listed' do + context 'when a schema JSON file exists for a particular report type version' do # We only care about the part that comes before report-format.json # https://rubular.com/r/N8Juz7r8hYDYgD filename_regex = /(?<report_type>[-\w]*)\-report-format.json/ @@ -36,14 +38,14 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do matches = filename_regex.match(file) report_type = matches[:report_type].tr("-", "_").to_sym - it "#{report_type} #{version}" do + it "#{report_type} #{version} is in the constant" do expect(described_class::SUPPORTED_VERSIONS[report_type]).to include(version) end end end end - context 'every SUPPORTED_VERSION has a corresponding JSON file' do + context 'when every SUPPORTED_VERSION has a corresponding JSON file' do described_class::SUPPORTED_VERSIONS.each_key do |report_type| # api_fuzzing is covered by DAST schema next if report_type == :api_fuzzing @@ -66,7 +68,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do let(:report_type) { :dast } let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last } - context 'and the report is valid' do + context 'when the report is valid' do let(:report_data) do { 'version' => report_version, @@ -77,7 +79,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do it { is_expected.to be_truthy } end - context 'and the report is invalid' do + context 'when the report is invalid' do let(:report_data) do { 'version' => report_version @@ -104,9 +106,19 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do context 'when given a deprecated schema version' do let(:report_type) { :dast } + let(:deprecations_hash) do + { + dast: %w[10.0.0] + } + end + let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last } - context 'and the report passes schema validation' do + before do + stub_const("#{described_class}::DEPRECATED_VERSIONS", deprecations_hash) + end + + context 'when the report passes schema validation' do let(:report_data) do { 'version' => '10.0.0', @@ -131,8 +143,8 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do end end - context 'and the report does not pass schema validation' do - context 'and enforce_security_report_validation is enabled' do + context 'when the report does not pass schema validation' do + context 'when enforce_security_report_validation is enabled' do before do stub_feature_flags(enforce_security_report_validation: true) end @@ -146,7 +158,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do it { is_expected.to be_falsey } end - context 'and enforce_security_report_validation is disabled' do + context 'when enforce_security_report_validation is disabled' do before do stub_feature_flags(enforce_security_report_validation: false) end @@ -166,12 +178,12 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do let(:report_type) { :dast } let(:report_version) { "12.37.0" } - context 'if enforce_security_report_validation is enabled' do + context 'when enforce_security_report_validation is enabled' do before do stub_feature_flags(enforce_security_report_validation: true) end - context 'and the report is valid' do + context 'when the report is valid' do let(:report_data) do { 'version' => report_version, @@ -196,14 +208,14 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do end end - context 'and the report is invalid' do + context 'when the report is invalid' do let(:report_data) do { 'version' => report_version } end - context 'and scanner information is empty' do + context 'when scanner information is empty' do let(:scanner) { {} } it 'logs related information' do @@ -235,12 +247,12 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do end end - context 'if enforce_security_report_validation is disabled' do + context 'when enforce_security_report_validation is disabled' do before do stub_feature_flags(enforce_security_report_validation: false) end - context 'and the report is valid' do + context 'when the report is valid' do let(:report_data) do { 'version' => report_version, @@ -251,7 +263,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do it { is_expected.to be_truthy } end - context 'and the report is invalid' do + context 'when the report is invalid' do let(:report_data) do { 'version' => report_version @@ -262,6 +274,30 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do end end end + + context 'when not given a schema version' do + let(:report_type) { :dast } + let(:report_version) { nil } + let(:report_data) do + { + 'vulnerabilities' => [] + } + end + + before do + stub_feature_flags(enforce_security_report_validation: true) + end + + it { is_expected.to be_falsey } + + context 'when enforce_security_report_validation is disabled' do + before do + stub_feature_flags(enforce_security_report_validation: false) + end + + it { is_expected.to be_truthy } + end + end end describe '#errors' do @@ -271,7 +307,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do let(:report_type) { :dast } let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last } - context 'and the report is valid' do + context 'when the report is valid' do let(:report_data) do { 'version' => report_version, @@ -279,19 +315,17 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do } end - let(:expected_errors) { [] } - - it { is_expected.to match_array(expected_errors) } + it { is_expected.to be_empty } end - context 'and the report is invalid' do + context 'when the report is invalid' do let(:report_data) do { 'version' => report_version } end - context 'if enforce_security_report_validation is enabled' do + context 'when enforce_security_report_validation is enabled' do before do stub_feature_flags(enforce_security_report_validation: project) end @@ -305,23 +339,31 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do it { is_expected.to match_array(expected_errors) } end - context 'if enforce_security_report_validation is disabled' do + context 'when enforce_security_report_validation is disabled' do before do stub_feature_flags(enforce_security_report_validation: false) end - let(:expected_errors) { [] } - - it { is_expected.to match_array(expected_errors) } + it { is_expected.to be_empty } end end end context 'when given a deprecated schema version' do let(:report_type) { :dast } + let(:deprecations_hash) do + { + dast: %w[10.0.0] + } + end + let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last } - context 'and the report passes schema validation' do + before do + stub_const("#{described_class}::DEPRECATED_VERSIONS", deprecations_hash) + end + + context 'when the report passes schema validation' do let(:report_data) do { 'version' => '10.0.0', @@ -329,13 +371,11 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do } end - let(:expected_errors) { [] } - - it { is_expected.to match_array(expected_errors) } + it { is_expected.to be_empty } end - context 'and the report does not pass schema validation' do - context 'and enforce_security_report_validation is enabled' do + context 'when the report does not pass schema validation' do + context 'when enforce_security_report_validation is enabled' do before do stub_feature_flags(enforce_security_report_validation: true) end @@ -356,7 +396,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do it { is_expected.to match_array(expected_errors) } end - context 'and enforce_security_report_validation is disabled' do + context 'when enforce_security_report_validation is disabled' do before do stub_feature_flags(enforce_security_report_validation: false) end @@ -367,9 +407,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do } end - let(:expected_errors) { [] } - - it { is_expected.to match_array(expected_errors) } + it { is_expected.to be_empty } end end end @@ -378,12 +416,12 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do let(:report_type) { :dast } let(:report_version) { "12.37.0" } - context 'if enforce_security_report_validation is enabled' do + context 'when enforce_security_report_validation is enabled' do before do stub_feature_flags(enforce_security_report_validation: true) end - context 'and the report is valid' do + context 'when the report is valid' do let(:report_data) do { 'version' => report_version, @@ -393,14 +431,14 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do let(:expected_errors) do [ - "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0, 14.1.1" + "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: #{supported_dast_versions}" ] end it { is_expected.to match_array(expected_errors) } end - context 'and the report is invalid' do + context 'when the report is invalid' do let(:report_data) do { 'version' => report_version @@ -409,7 +447,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do let(:expected_errors) do [ - "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0, 14.1.1", + "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: #{supported_dast_versions}", "root is missing required keys: vulnerabilities" ] end @@ -418,12 +456,12 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do end end - context 'if enforce_security_report_validation is disabled' do + context 'when enforce_security_report_validation is disabled' do before do stub_feature_flags(enforce_security_report_validation: false) end - context 'and the report is valid' do + context 'when the report is valid' do let(:report_data) do { 'version' => report_version, @@ -431,22 +469,45 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do } end - let(:expected_errors) { [] } - - it { is_expected.to match_array(expected_errors) } + it { is_expected.to be_empty } end - context 'and the report is invalid' do + context 'when the report is invalid' do let(:report_data) do { 'version' => report_version } end - let(:expected_errors) { [] } + it { is_expected.to be_empty } + end + end + end - it { is_expected.to match_array(expected_errors) } + context 'when not given a schema version' do + let(:report_type) { :dast } + let(:report_version) { nil } + let(:report_data) do + { + 'vulnerabilities' => [] + } + end + + let(:expected_errors) do + [ + "root is missing required keys: version", + "Report version not provided, dast report type supports versions: #{supported_dast_versions}" + ] + end + + it { is_expected.to match_array(expected_errors) } + + context 'when enforce_security_report_validation is disabled' do + before do + stub_feature_flags(enforce_security_report_validation: false) end + + it { is_expected.to be_empty } end end end @@ -458,9 +519,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do let(:report_type) { :dast } let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last } - let(:expected_deprecation_warnings) { [] } - - context 'and the report is valid' do + context 'when the report is valid' do let(:report_data) do { 'version' => report_version, @@ -468,30 +527,40 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do } end - it { is_expected.to match_array(expected_deprecation_warnings) } + it { is_expected.to be_empty } end - context 'and the report is invalid' do + context 'when the report is invalid' do let(:report_data) do { 'version' => report_version } end - it { is_expected.to match_array(expected_deprecation_warnings) } + it { is_expected.to be_empty } end end context 'when given a deprecated schema version' do let(:report_type) { :dast } + let(:deprecations_hash) do + { + dast: %w[V2.7.0] + } + end + let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last } let(:expected_deprecation_warnings) do [ - "Version V2.7.0 for report type dast has been deprecated, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0, 14.1.1" + "Version V2.7.0 for report type dast has been deprecated, supported versions for this report type are: #{supported_dast_versions}" ] end - context 'and the report passes schema validation' do + before do + stub_const("#{described_class}::DEPRECATED_VERSIONS", deprecations_hash) + end + + context 'when the report passes schema validation' do let(:report_data) do { 'version' => report_version, @@ -502,7 +571,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do it { is_expected.to match_array(expected_deprecation_warnings) } end - context 'and the report does not pass schema validation' do + context 'when the report does not pass schema validation' do let(:report_data) do { 'version' => 'V2.7.0' @@ -535,7 +604,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do let(:report_type) { :dast } let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last } - context 'and the report is valid' do + context 'when the report is valid' do let(:report_data) do { 'version' => report_version, @@ -543,29 +612,25 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do } end - let(:expected_warnings) { [] } - - it { is_expected.to match_array(expected_warnings) } + it { is_expected.to be_empty } end - context 'and the report is invalid' do + context 'when the report is invalid' do let(:report_data) do { 'version' => report_version } end - context 'if enforce_security_report_validation is enabled' do + context 'when enforce_security_report_validation is enabled' do before do stub_feature_flags(enforce_security_report_validation: project) end - let(:expected_warnings) { [] } - - it { is_expected.to match_array(expected_warnings) } + it { is_expected.to be_empty } end - context 'if enforce_security_report_validation is disabled' do + context 'when enforce_security_report_validation is disabled' do before do stub_feature_flags(enforce_security_report_validation: false) end @@ -583,38 +648,44 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do context 'when given a deprecated schema version' do let(:report_type) { :dast } + let(:deprecations_hash) do + { + dast: %w[V2.7.0] + } + end + let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last } - context 'and the report passes schema validation' do + before do + stub_const("#{described_class}::DEPRECATED_VERSIONS", deprecations_hash) + end + + context 'when the report passes schema validation' do let(:report_data) do { 'vulnerabilities' => [] } end - let(:expected_warnings) { [] } - - it { is_expected.to match_array(expected_warnings) } + it { is_expected.to be_empty } end - context 'and the report does not pass schema validation' do + context 'when the report does not pass schema validation' do let(:report_data) do { 'version' => 'V2.7.0' } end - context 'and enforce_security_report_validation is enabled' do + context 'when enforce_security_report_validation is enabled' do before do stub_feature_flags(enforce_security_report_validation: true) end - let(:expected_warnings) { [] } - - it { is_expected.to match_array(expected_warnings) } + it { is_expected.to be_empty } end - context 'and enforce_security_report_validation is disabled' do + context 'when enforce_security_report_validation is disabled' do before do stub_feature_flags(enforce_security_report_validation: false) end @@ -635,12 +706,12 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do let(:report_type) { :dast } let(:report_version) { "12.37.0" } - context 'if enforce_security_report_validation is enabled' do + context 'when enforce_security_report_validation is enabled' do before do stub_feature_flags(enforce_security_report_validation: true) end - context 'and the report is valid' do + context 'when the report is valid' do let(:report_data) do { 'version' => report_version, @@ -648,30 +719,26 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do } end - let(:expected_warnings) { [] } - - it { is_expected.to match_array(expected_warnings) } + it { is_expected.to be_empty } end - context 'and the report is invalid' do + context 'when the report is invalid' do let(:report_data) do { 'version' => report_version } end - let(:expected_warnings) { [] } - - it { is_expected.to match_array(expected_warnings) } + it { is_expected.to be_empty } end end - context 'if enforce_security_report_validation is disabled' do + context 'when enforce_security_report_validation is disabled' do before do stub_feature_flags(enforce_security_report_validation: false) end - context 'and the report is valid' do + context 'when the report is valid' do let(:report_data) do { 'version' => report_version, @@ -681,14 +748,14 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do let(:expected_warnings) do [ - "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0, 14.1.1" + "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: #{supported_dast_versions}" ] end it { is_expected.to match_array(expected_warnings) } end - context 'and the report is invalid' do + context 'when the report is invalid' do let(:report_data) do { 'version' => report_version @@ -697,7 +764,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do let(:expected_warnings) do [ - "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0, 14.1.1", + "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: #{supported_dast_versions}", "root is missing required keys: vulnerabilities" ] end @@ -706,5 +773,32 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do end end end + + context 'when not given a schema version' do + let(:report_type) { :dast } + let(:report_version) { nil } + let(:report_data) do + { + 'vulnerabilities' => [] + } + end + + it { is_expected.to be_empty } + + context 'when enforce_security_report_validation is disabled' do + before do + stub_feature_flags(enforce_security_report_validation: false) + end + + let(:expected_warnings) do + [ + "root is missing required keys: version", + "Report version not provided, dast report type supports versions: #{supported_dast_versions}" + ] + end + + it { is_expected.to match_array(expected_warnings) } + end + end end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb index 25e81f6d538..b570f2a7f75 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb @@ -106,7 +106,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::CancelPendingPipelines do create(:ci_build, :interruptible, :running, pipeline: child_pipeline) end - not_started_statuses = Ci::HasStatus::AVAILABLE_STATUSES - Ci::HasStatus::BUILD_STARTED_RUNNING_STATUSES + not_started_statuses = Ci::HasStatus::AVAILABLE_STATUSES - Ci::HasStatus::STARTED_STATUSES context 'when the jobs are cancelable' do cancelable_not_started_statuses = Set.new(not_started_statuses).intersection(Ci::HasStatus::CANCELABLE_STATUSES) cancelable_not_started_statuses.each do |status| diff --git a/spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb index 1aa104310af..431073b5a09 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb @@ -87,7 +87,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Chain::Limit::Deployments do it 'logs the error' do expect(Gitlab::ErrorTracking).to receive(:log_exception).with( instance_of(Gitlab::Ci::Limit::LimitExceededError), - project_id: project.id, plan: namespace.actual_plan_name + { project_id: project.id, plan: namespace.actual_plan_name } ) perform diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb index 0da04d8dcf7..83742699d3d 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb @@ -1,9 +1,13 @@ # frozen_string_literal: true require 'fast_spec_helper' +require 'support/helpers/stubbed_feature' +require 'support/helpers/stub_feature_flags' require_dependency 're2' RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Matches do + include StubFeatureFlags + let(:left) { double('left') } let(:right) { double('right') } @@ -148,5 +152,29 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Matches do it { is_expected.to eq(false) } end + + context 'when right value is a regexp string' do + let(:right_value) { '/^ab.*/' } + + context 'when matching' do + let(:left_value) { 'abcde' } + + it { is_expected.to eq(true) } + + context 'when the FF ci_fix_rules_if_comparison_with_regexp_variable is disabled' do + before do + stub_feature_flags(ci_fix_rules_if_comparison_with_regexp_variable: false) + end + + it { is_expected.to eq(false) } + end + end + + context 'when not matching' do + let(:left_value) { 'dfg' } + + it { is_expected.to eq(false) } + end + end end end diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb index 9bff2355d58..aad33106647 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb @@ -1,9 +1,13 @@ # frozen_string_literal: true require 'fast_spec_helper' +require 'support/helpers/stubbed_feature' +require 'support/helpers/stub_feature_flags' require_dependency 're2' RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::NotMatches do + include StubFeatureFlags + let(:left) { double('left') } let(:right) { double('right') } @@ -148,5 +152,29 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::NotMatches do it { is_expected.to eq(true) } end + + context 'when right value is a regexp string' do + let(:right_value) { '/^ab.*/' } + + context 'when matching' do + let(:left_value) { 'abcde' } + + it { is_expected.to eq(false) } + + context 'when the FF ci_fix_rules_if_comparison_with_regexp_variable is disabled' do + before do + stub_feature_flags(ci_fix_rules_if_comparison_with_regexp_variable: false) + end + + it { is_expected.to eq(true) } + end + end + + context 'when not matching' do + let(:left_value) { 'dfg' } + + it { is_expected.to eq(true) } + end + end end end diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb index fa4f8a20984..be205395b69 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb @@ -1,8 +1,32 @@ # frozen_string_literal: true -require 'spec_helper' +require 'fast_spec_helper' RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do + describe '#initialize' do + context 'when the value is a valid regular expression' do + it 'initializes the pattern' do + pattern = described_class.new('/foo/') + + expect(pattern.value).to eq('/foo/') + end + end + + context 'when the value is a valid regular expression with escaped slashes' do + it 'initializes the pattern' do + pattern = described_class.new('/foo\\/bar/') + + expect(pattern.value).to eq('/foo/bar/') + end + end + + context 'when the value is not a valid regular expression' do + it 'raises an error' do + expect { described_class.new('foo') }.to raise_error(Gitlab::Ci::Pipeline::Expression::Lexer::SyntaxError) + end + end + end + describe '.build' do it 'creates a new instance of the token' do expect(described_class.build('/.*/')) @@ -15,6 +39,29 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do end end + describe '.build_and_evaluate' do + context 'when the value is a valid regular expression' do + it 'returns the value as a Gitlab::UntrustedRegexp' do + expect(described_class.build_and_evaluate('/foo/')) + .to eq(Gitlab::UntrustedRegexp.new('foo')) + end + end + + context 'when the value is a Gitlab::UntrustedRegexp' do + it 'returns the value itself' do + expect(described_class.build_and_evaluate(Gitlab::UntrustedRegexp.new('foo'))) + .to eq(Gitlab::UntrustedRegexp.new('foo')) + end + end + + context 'when the value is not a valid regular expression' do + it 'returns the value itself' do + expect(described_class.build_and_evaluate('foo')) + .to eq('foo') + end + end + end + describe '.type' do it 'is a value lexeme' do expect(described_class.type).to eq :value diff --git a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb index 84713e2a798..bbd11a00149 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb @@ -12,7 +12,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Statement do .to_hash end - subject do + subject(:statement) do described_class.new(text, variables) end @@ -29,6 +29,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Statement do describe '#evaluate' do using RSpec::Parameterized::TableSyntax + subject(:evaluate) { statement.evaluate } + where(:expression, :value) do '$PRESENT_VARIABLE == "my variable"' | true '"my variable" == $PRESENT_VARIABLE' | true @@ -125,7 +127,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Statement do let(:text) { expression } it "evaluates to `#{params[:value].inspect}`" do - expect(subject.evaluate).to eq(value) + expect(evaluate).to eq(value) end end end @@ -133,6 +135,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Statement do describe '#truthful?' do using RSpec::Parameterized::TableSyntax + subject(:truthful?) { statement.truthful? } + where(:expression, :value) do '$PRESENT_VARIABLE == "my variable"' | true "$PRESENT_VARIABLE == 'no match'" | false @@ -151,7 +155,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Statement do let(:text) { expression } it "returns `#{params[:value].inspect}`" do - expect(subject.truthful?).to eq value + expect(truthful?).to eq value end end @@ -159,10 +163,41 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Statement do let(:text) { '$PRESENT_VARIABLE' } it 'returns false' do - allow(subject).to receive(:evaluate) + allow(statement).to receive(:evaluate) .and_raise(described_class::StatementError) - expect(subject.truthful?).to be_falsey + expect(truthful?).to be_falsey + end + end + + context 'when variables have patterns' do + let(:variables) do + Gitlab::Ci::Variables::Collection.new + .append(key: 'teststring', value: 'abcde') + .append(key: 'pattern1', value: '/^ab.*/') + .append(key: 'pattern2', value: '/^at.*/') + .to_hash + end + + where(:expression, :ff, :result) do + '$teststring =~ "abcde"' | true | true + '$teststring =~ "abcde"' | false | true + '$teststring =~ $teststring' | true | true + '$teststring =~ $teststring' | false | true + '$teststring =~ $pattern1' | true | true + '$teststring =~ $pattern1' | false | false + '$teststring =~ $pattern2' | true | false + '$teststring =~ $pattern2' | false | false + end + + with_them do + let(:text) { expression } + + before do + stub_feature_flags(ci_fix_rules_if_comparison_with_regexp_variable: ff) + end + + it { is_expected.to eq(result) } end end end diff --git a/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb index 9f7281fb714..51185be3e74 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb @@ -90,29 +90,22 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Deployment do end end - context 'when job has environment attribute with stop action' do - let(:attributes) do - { - environment: 'production', - options: { environment: { name: 'production', action: 'stop' } } - } - end - - it 'returns nothing' do - is_expected.to be_nil + context 'when job does not start environment' do + where(:action) do + %w(stop prepare verify access) end - end - context 'when job has environment attribute with prepare action' do - let(:attributes) do - { - environment: 'production', - options: { environment: { name: 'production', action: 'prepare' } } - } - 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 + it 'returns nothing' do + is_expected.to be_nil + end end end diff --git a/spec/lib/gitlab/ci/reports/security/scanner_spec.rb b/spec/lib/gitlab/ci/reports/security/scanner_spec.rb index eb406e01b24..d7ac82e3b53 100644 --- a/spec/lib/gitlab/ci/reports/security/scanner_spec.rb +++ b/spec/lib/gitlab/ci/reports/security/scanner_spec.rb @@ -103,8 +103,6 @@ RSpec.describe Gitlab::Ci::Reports::Security::Scanner do context 'when the `external_id` of the scanners are different' do where(:scanner_1_attributes, :scanner_2_attributes, :expected_comparison_result) do - { external_id: 'bundler_audit', name: 'foo', vendor: 'bar' } | { external_id: 'retire.js', name: 'foo', vendor: 'bar' } | -1 - { external_id: 'retire.js', name: 'foo', vendor: 'bar' } | { external_id: 'gemnasium', name: 'foo', vendor: 'bar' } | -1 { external_id: 'gemnasium', name: 'foo', vendor: 'bar' } | { external_id: 'gemnasium-maven', name: 'foo', vendor: 'bar' } | -1 { external_id: 'gemnasium-maven', name: 'foo', vendor: 'bar' } | { external_id: 'gemnasium-python', name: 'foo', vendor: 'bar' } | -1 { external_id: 'gemnasium-python', name: 'foo', vendor: 'bar' } | { external_id: 'bandit', name: 'foo', vendor: 'bar' } | 1 diff --git a/spec/lib/gitlab/ci/runner_upgrade_check_spec.rb b/spec/lib/gitlab/ci/runner_upgrade_check_spec.rb index b430da376dd..f2b4e7573c0 100644 --- a/spec/lib/gitlab/ci/runner_upgrade_check_spec.rb +++ b/spec/lib/gitlab/ci/runner_upgrade_check_spec.rb @@ -22,8 +22,8 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do context 'with nil runner_version' do let(:runner_version) { nil } - it 'raises :unknown' do - is_expected.to eq(:unknown) + it 'returns :invalid' do + is_expected.to eq(:invalid) end end 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 0f97bc06a4e..85516d0bbb0 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 @@ -2,8 +2,8 @@ require 'spec_helper' -RSpec.describe 'Jobs/SAST-IaC.latest.gitlab-ci.yml' do - subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('Jobs/SAST-IaC.latest') } +RSpec.describe 'Jobs/SAST-IaC.gitlab-ci.yml' do + subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('Jobs/SAST-IaC') } describe 'the created pipeline' do let_it_be(:project) { create(:project, :repository) } 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 new file mode 100644 index 00000000000..0f97bc06a4e --- /dev/null +++ b/spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Jobs/SAST-IaC.latest.gitlab-ci.yml' do + subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('Jobs/SAST-IaC.latest') } + + describe 'the created pipeline' do + let_it_be(:project) { create(:project, :repository) } + let_it_be(:user) { project.first_owner } + + let(:default_branch) { 'main' } + let(:pipeline_ref) { default_branch } + let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) } + let(:pipeline) { service.execute!(:push).payload } + let(:build_names) { pipeline.builds.pluck(:name) } + + before do + stub_ci_pipeline_yaml_file(template.content) + allow_next_instance_of(Ci::BuildScheduleWorker) do |instance| + allow(instance).to receive(:perform).and_return(true) + end + allow(project).to receive(:default_branch).and_return(default_branch) + end + + context 'on feature branch' do + let(:pipeline_ref) { 'feature' } + + it 'creates the kics-iac-sast job' do + expect(build_names).to contain_exactly('kics-iac-sast') + 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 'has no jobs' do + expect(pipeline).to be_merge_request_event + expect(build_names).to be_empty + end + end + + context 'SAST_DISABLED is set' do + before do + create(:ci_variable, key: 'SAST_DISABLED', value: 'true', project: project) + end + + context 'on default branch' do + it 'has no jobs' do + expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError) + end + end + + context 'on feature branch' do + let(:pipeline_ref) { 'feature' } + + it 'has no jobs' do + expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError) + end + end + end + end +end diff --git a/spec/lib/gitlab/ci/templates/MATLAB_spec.rb b/spec/lib/gitlab/ci/templates/MATLAB_spec.rb index a12d69b67a6..432040c4a14 100644 --- a/spec/lib/gitlab/ci/templates/MATLAB_spec.rb +++ b/spec/lib/gitlab/ci/templates/MATLAB_spec.rb @@ -20,7 +20,7 @@ RSpec.describe 'MATLAB.gitlab-ci.yml' do end it 'creates all jobs' do - expect(build_names).to include('command', 'test', 'test_artifacts_job') + expect(build_names).to include('command', 'test', 'test_artifacts') end end end 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 5e9224cebd9..eca79f37779 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 @@ -16,7 +16,6 @@ RSpec.describe 'Terraform/Base.gitlab-ci.yml' do before do stub_ci_pipeline_yaml_file(template.content) - allow_any_instance_of(Ci::BuildScheduleWorker).to receive(:perform).and_return(true) allow(project).to receive(:default_branch).and_return(default_branch) end diff --git a/spec/lib/gitlab/ci/templates/managed_cluster_applications_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/managed_cluster_applications_gitlab_ci_yaml_spec.rb deleted file mode 100644 index 14aaf717453..00000000000 --- a/spec/lib/gitlab/ci/templates/managed_cluster_applications_gitlab_ci_yaml_spec.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Managed-Cluster-Applications.gitlab-ci.yml' do - subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('Managed-Cluster-Applications') } - - describe 'the created pipeline' do - let_it_be(:user) { create(:user) } - - let(:project) { create(:project, :custom_repo, namespace: user.namespace, files: { 'README.md' => '' }) } - let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) } - let(:pipeline) { service.execute!(:push).payload } - let(:build_names) { pipeline.builds.pluck(:name) } - let(:default_branch) { project.default_branch_or_main } - let(:pipeline_branch) { default_branch } - - before do - stub_ci_pipeline_yaml_file(template.content) - end - - context 'for a default branch' do - it 'creates a apply job' do - expect(build_names).to match_array('apply') - end - end - - context 'outside of default branch' do - let(:pipeline_branch) { 'a_branch' } - - before do - project.repository.create_branch(pipeline_branch, default_branch) - end - - it 'has no jobs' do - expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError, 'No stages / jobs for this pipeline.') - end - end - end -end diff --git a/spec/lib/gitlab/ci/templates/templates_spec.rb b/spec/lib/gitlab/ci/templates/templates_spec.rb index ca096fcecc4..36c6e805bdf 100644 --- a/spec/lib/gitlab/ci/templates/templates_spec.rb +++ b/spec/lib/gitlab/ci/templates/templates_spec.rb @@ -7,10 +7,9 @@ RSpec.describe 'CI YML Templates' do let(:all_templates) { Gitlab::Template::GitlabCiYmlTemplate.all.map(&:full_name) } let(:excluded_templates) do - excluded = all_templates.select do |name| + all_templates.select do |name| Gitlab::Template::GitlabCiYmlTemplate.excluded_patterns.any? { |pattern| pattern.match?(name) } end - excluded + ["Terraform.gitlab-ci.yml"] end shared_examples 'require default stages to be included' do 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 346ab9f7af7..2fc4b509aab 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 @@ -20,13 +20,16 @@ RSpec.describe 'Terraform.gitlab-ci.yml' do before do stub_ci_pipeline_yaml_file(template.content) + allow_next_instance_of(Ci::BuildScheduleWorker) do |instance| + allow(instance).to receive(:perform).and_return(true) + end allow(project).to receive(:default_branch).and_return(default_branch) end context 'on master branch' do it 'creates init, validate and build jobs', :aggregate_failures do expect(pipeline.errors).to be_empty - expect(build_names).to include('init', 'validate', 'build', 'deploy') + expect(build_names).to include('validate', 'build', 'deploy') 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 6c06403adff..42e56c4ab3c 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 @@ -20,7 +20,9 @@ RSpec.describe 'Terraform.latest.gitlab-ci.yml' do before do stub_ci_pipeline_yaml_file(template.content) - allow_any_instance_of(Ci::BuildScheduleWorker).to receive(:perform).and_return(true) + allow_next_instance_of(Ci::BuildScheduleWorker) do |instance| + allow(instance).to receive(:perform).and_return(true) + end allow(project).to receive(:default_branch).and_return(default_branch) end diff --git a/spec/lib/gitlab/ci/variables/builder_spec.rb b/spec/lib/gitlab/ci/variables/builder_spec.rb index b9aa5f7c431..e13a0993fa8 100644 --- a/spec/lib/gitlab/ci/variables/builder_spec.rb +++ b/spec/lib/gitlab/ci/variables/builder_spec.rb @@ -246,7 +246,7 @@ RSpec.describe Gitlab::Ci::Variables::Builder do subject { builder.kubernetes_variables(environment: nil, job: job) } before do - allow(Ci::GenerateKubeconfigService).to receive(:new).with(job).and_return(service) + allow(Ci::GenerateKubeconfigService).to receive(:new).with(job.pipeline, token: job.token).and_return(service) end it { is_expected.to include(key: 'KUBECONFIG', value: 'example-kubeconfig', public: false, file: true) } diff --git a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb index 25705fd4260..8416501e949 100644 --- a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb @@ -12,8 +12,8 @@ module Gitlab let(:ci_config) { Gitlab::Ci::Config.new(config_content, user: user) } let(:result) { described_class.new(ci_config: ci_config, warnings: ci_config&.warnings) } - describe '#merged_yaml' do - subject(:merged_yaml) { result.merged_yaml } + describe '#config_metadata' do + subject(:config_metadata) { result.config_metadata } let(:config_content) do YAML.dump( @@ -33,11 +33,23 @@ module Gitlab end it 'returns expanded yaml config' do - expanded_config = YAML.safe_load(merged_yaml, [Symbol]) + expanded_config = YAML.safe_load(config_metadata[:merged_yaml], [Symbol]) included_config = YAML.safe_load(included_yml, [Symbol]) expect(expanded_config).to include(*included_config.keys) end + + it 'returns includes' do + expect(config_metadata[:includes]).to contain_exactly( + { type: :remote, + location: 'https://example.com/sample.yml', + blob: nil, + raw: 'https://example.com/sample.yml', + extra: {}, + context_project: nil, + context_sha: nil } + ) + end end describe '#yaml_variables_for' do diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb index 9b68ee2d6a2..1910057622b 100644 --- a/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -630,7 +630,7 @@ module Gitlab describe 'only / except policies validations' do context 'when `only` has an invalid value' do - let(:config) { { rspec: { script: "rspec", type: "test", only: only } } } + let(:config) { { rspec: { script: "rspec", stage: "test", only: only } } } subject { Gitlab::Ci::YamlProcessor.new(YAML.dump(config)).execute } @@ -2606,19 +2606,19 @@ module Gitlab end context 'returns errors if job stage is not a string' do - let(:config) { YAML.dump({ rspec: { script: "test", type: 1 } }) } + let(:config) { YAML.dump({ rspec: { script: "test", stage: 1 } }) } - it_behaves_like 'returns errors', 'jobs:rspec:type config should be a string' + it_behaves_like 'returns errors', 'jobs:rspec:stage config should be a string' end context 'returns errors if job stage is not a pre-defined stage' do - let(:config) { YAML.dump({ rspec: { script: "test", type: "acceptance" } }) } + let(:config) { YAML.dump({ rspec: { script: "test", stage: "acceptance" } }) } it_behaves_like 'returns errors', 'rspec job: chosen stage does not exist; available stages are .pre, build, test, deploy, .post' end context 'returns errors if job stage is not a defined stage' do - let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", type: "acceptance" } }) } + let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", stage: "acceptance" } }) } it_behaves_like 'returns errors', 'rspec job: chosen stage does not exist; available stages are .pre, build, test, .post' end |