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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib/gitlab/ci')
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb206
-rw-r--r--spec/lib/gitlab/ci/build/duration_parser_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/components/instance_path_spec.rb251
-rw-r--r--spec/lib/gitlab/ci/config/entry/bridge_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/default_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb38
-rw-r--r--spec/lib/gitlab/ci/config/entry/include/rules_spec.rb35
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb20
-rw-r--r--spec/lib/gitlab/ci/config/entry/processable_spec.rb33
-rw-r--r--spec/lib/gitlab/ci/config/external/context_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/config/external/file/component_spec.rb35
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper/verifier_spec.rb26
-rw-r--r--spec/lib/gitlab/ci/config/external/processor_spec.rb14
-rw-r--r--spec/lib/gitlab/ci/config/external/rules_spec.rb218
-rw-r--r--spec/lib/gitlab/ci/config/interpolation/interpolator_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/config/yaml/tags/reference_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/config/yaml/tags/resolver_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb65
-rw-r--r--spec/lib/gitlab/ci/parsers/security/common_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build_spec.rb120
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/component_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/metadata_spec.rb54
-rw-r--r--spec/lib/gitlab/ci/templates/MATLAB_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/trace/stream_spec.rb50
-rw-r--r--spec/lib/gitlab/ci/variables/builder/pipeline_spec.rb7
-rw-r--r--spec/lib/gitlab/ci/variables/builder_spec.rb23
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb122
27 files changed, 954 insertions, 410 deletions
diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
index efe99cd276c..1f3ba0ef76e 100644
--- a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Build::Artifacts::Metadata do
+RSpec.describe Gitlab::Ci::Build::Artifacts::Metadata, feature_category: :build_artifacts do
def metadata(path = '', **opts)
described_class.new(metadata_file_stream, path, **opts)
end
@@ -19,132 +19,158 @@ RSpec.describe Gitlab::Ci::Build::Artifacts::Metadata do
metadata_file_stream&.close
end
- context 'metadata file exists' do
- describe '#find_entries! empty string' do
- subject { metadata('').find_entries! }
+ describe '#to_entry' do
+ subject(:entry) { metadata.to_entry }
- it 'matches correct paths' do
- expect(subject.keys).to contain_exactly 'ci_artifacts.txt',
- 'other_artifacts_0.1.2/',
- 'rails_sample.jpg',
- 'tests_encoding/'
- end
-
- it 'matches metadata for every path' do
- expect(subject.keys.count).to eq 4
- end
+ it { is_expected.to be_an_instance_of(Gitlab::Ci::Build::Artifacts::Metadata::Entry) }
- it 'return Hashes for each metadata' do
- expect(subject.values).to all(be_kind_of(Hash))
+ context 'when given path starts with a ./ prefix' do
+ it 'instantiates the entry without the ./ prefix from the path' do
+ meta = metadata("./some/path")
+ expect(Gitlab::Ci::Build::Artifacts::Metadata::Entry).to receive(:new).with("some/path", {})
+ meta.to_entry
end
end
+ end
- describe '#find_entries! other_artifacts_0.1.2/' do
- subject { metadata('other_artifacts_0.1.2/').find_entries! }
+ describe '#full_version' do
+ subject { metadata.full_version }
- it 'matches correct paths' do
- expect(subject.keys)
- .to contain_exactly 'other_artifacts_0.1.2/',
- 'other_artifacts_0.1.2/doc_sample.txt',
- 'other_artifacts_0.1.2/another-subdirectory/'
- end
- end
+ it { is_expected.to eq 'GitLab Build Artifacts Metadata 0.0.1' }
+ end
- describe '#find_entries! other_artifacts_0.1.2/another-subdirectory/' do
- subject { metadata('other_artifacts_0.1.2/another-subdirectory/').find_entries! }
+ describe '#version' do
+ subject { metadata.version }
- it 'matches correct paths' do
- expect(subject.keys)
- .to contain_exactly 'other_artifacts_0.1.2/another-subdirectory/',
- 'other_artifacts_0.1.2/another-subdirectory/empty_directory/',
- 'other_artifacts_0.1.2/another-subdirectory/banana_sample.gif'
- end
- end
+ it { is_expected.to eq '0.0.1' }
+ end
- describe '#find_entries! recursively for other_artifacts_0.1.2/' do
- subject { metadata('other_artifacts_0.1.2/', recursive: true).find_entries! }
+ describe '#errors' do
+ subject { metadata.errors }
- it 'matches correct paths' do
- expect(subject.keys)
- .to contain_exactly 'other_artifacts_0.1.2/',
- 'other_artifacts_0.1.2/doc_sample.txt',
- 'other_artifacts_0.1.2/another-subdirectory/',
- 'other_artifacts_0.1.2/another-subdirectory/empty_directory/',
- 'other_artifacts_0.1.2/another-subdirectory/banana_sample.gif'
- end
- end
+ it { is_expected.to eq({}) }
+ end
- describe '#to_entry' do
- subject { metadata('').to_entry }
+ describe '#find_entries!' do
+ let(:recursive) { false }
- it { is_expected.to be_an_instance_of(Gitlab::Ci::Build::Artifacts::Metadata::Entry) }
- end
+ subject(:find_entries) { metadata(path, recursive: recursive).find_entries! }
- describe '#full_version' do
- subject { metadata('').full_version }
+ context 'when metadata file exists' do
+ context 'and given path is an empty string' do
+ let(:path) { '' }
- it { is_expected.to eq 'GitLab Build Artifacts Metadata 0.0.1' }
- end
+ it 'returns paths to all files and directories at the root level' do
+ expect(find_entries.keys).to contain_exactly(
+ 'ci_artifacts.txt',
+ 'other_artifacts_0.1.2/',
+ 'rails_sample.jpg',
+ 'tests_encoding/'
+ )
+ end
- describe '#version' do
- subject { metadata('').version }
+ it 'return Hashes for each metadata' do
+ expect(find_entries.values).to all(be_kind_of(Hash))
+ end
+ end
- it { is_expected.to eq '0.0.1' }
- end
+ shared_examples 'finding entries for a given path' do |options|
+ let(:path) { "#{options[:path_prefix]}#{target_path}" }
+
+ context 'when given path targets a directory at the root level' do
+ let(:target_path) { 'other_artifacts_0.1.2/' }
+
+ it 'returns paths to all files and directories at the first level of the directory' do
+ expect(find_entries.keys).to contain_exactly(
+ 'other_artifacts_0.1.2/',
+ 'other_artifacts_0.1.2/doc_sample.txt',
+ 'other_artifacts_0.1.2/another-subdirectory/'
+ )
+ end
+ end
+
+ context 'when given path targets a sub-directory' do
+ let(:target_path) { 'other_artifacts_0.1.2/another-subdirectory/' }
+
+ it 'returns paths to all files and directories at the first level of the sub-directory' do
+ expect(find_entries.keys).to contain_exactly(
+ 'other_artifacts_0.1.2/another-subdirectory/',
+ 'other_artifacts_0.1.2/another-subdirectory/empty_directory/',
+ 'other_artifacts_0.1.2/another-subdirectory/banana_sample.gif'
+ )
+ end
+ end
+
+ context 'when given path targets a directory recursively' do
+ let(:target_path) { 'other_artifacts_0.1.2/' }
+ let(:recursive) { true }
+
+ it 'returns all paths recursively within the target directory' do
+ expect(subject.keys).to contain_exactly(
+ 'other_artifacts_0.1.2/',
+ 'other_artifacts_0.1.2/doc_sample.txt',
+ 'other_artifacts_0.1.2/another-subdirectory/',
+ 'other_artifacts_0.1.2/another-subdirectory/empty_directory/',
+ 'other_artifacts_0.1.2/another-subdirectory/banana_sample.gif'
+ )
+ end
+ end
+ end
- describe '#errors' do
- subject { metadata('').errors }
+ context 'and given path does not start with a ./ prefix' do
+ it_behaves_like 'finding entries for a given path', path_prefix: ''
+ end
- it { is_expected.to eq({}) }
+ context 'and given path starts with a ./ prefix' do
+ it_behaves_like 'finding entries for a given path', path_prefix: './'
+ end
end
- end
- context 'metadata file does not exist' do
- let(:metadata_file_path) { nil }
+ context 'when metadata file stream is nil' do
+ let(:path) { '' }
+ let(:metadata_file_stream) { nil }
- describe '#find_entries!' do
it 'raises error' do
- expect { metadata.find_entries! }.to raise_error(described_class::InvalidStreamError, /Invalid stream/)
+ expect { find_entries }.to raise_error(described_class::InvalidStreamError, /Invalid stream/)
end
end
- end
- context 'metadata file is invalid' do
- let(:metadata_file_path) { Rails.root + 'spec/fixtures/ci_build_artifacts.zip' }
+ context 'when metadata file is invalid' do
+ let(:path) { '' }
+ let(:metadata_file_path) { Rails.root + 'spec/fixtures/ci_build_artifacts.zip' }
- describe '#find_entries!' do
it 'raises error' do
- expect { metadata.find_entries! }.to raise_error(described_class::InvalidStreamError, /not in gzip format/)
+ expect { find_entries }.to raise_error(described_class::InvalidStreamError, /not in gzip format/)
end
end
- end
- context 'generated metadata' do
- let(:tmpfile) { Tempfile.new('test-metadata') }
- let(:generator) { CiArtifactMetadataGenerator.new(tmpfile) }
- let(:entry_count) { 5 }
+ context 'with generated metadata' do
+ let(:tmpfile) { Tempfile.new('test-metadata') }
+ let(:generator) { CiArtifactMetadataGenerator.new(tmpfile) }
+ let(:entry_count) { 5 }
- before do
- tmpfile.binmode
+ before do
+ tmpfile.binmode
- (1..entry_count).each do |index|
- generator.add_entry("public/test-#{index}.txt")
- end
+ (1..entry_count).each do |index|
+ generator.add_entry("public/test-#{index}.txt")
+ end
- generator.write
- end
+ generator.write
+ end
- after do
- File.unlink(tmpfile.path)
- end
+ after do
+ File.unlink(tmpfile.path)
+ end
- describe '#find_entries!' do
- it 'reads expected number of entries' do
- stream = File.open(tmpfile.path)
+ describe '#find_entries!' do
+ it 'reads expected number of entries' do
+ stream = File.open(tmpfile.path)
- metadata = described_class.new(stream, 'public', recursive: true)
+ metadata = described_class.new(stream, 'public', recursive: true)
- expect(metadata.find_entries!.count).to eq entry_count
+ expect(metadata.find_entries!.count).to eq entry_count
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/build/duration_parser_spec.rb b/spec/lib/gitlab/ci/build/duration_parser_spec.rb
index 7f5ff1eb0ee..bc905aa0a35 100644
--- a/spec/lib/gitlab/ci/build/duration_parser_spec.rb
+++ b/spec/lib/gitlab/ci/build/duration_parser_spec.rb
@@ -25,8 +25,8 @@ RSpec.describe Gitlab::Ci::Build::DurationParser do
it { is_expected.to be_truthy }
it 'caches data' do
- expect(ChronicDuration).to receive(:parse).with(value).once.and_call_original
- expect(ChronicDuration).to receive(:parse).with(other_value).once.and_call_original
+ expect(ChronicDuration).to receive(:parse).with(value, use_complete_matcher: true).once.and_call_original
+ expect(ChronicDuration).to receive(:parse).with(other_value, use_complete_matcher: true).once.and_call_original
2.times do
expect(described_class.validate_duration(value)).to eq(86400)
@@ -41,7 +41,7 @@ RSpec.describe Gitlab::Ci::Build::DurationParser do
it { is_expected.to be_falsy }
it 'caches data' do
- expect(ChronicDuration).to receive(:parse).with(value).once.and_call_original
+ expect(ChronicDuration).to receive(:parse).with(value, use_complete_matcher: true).once.and_call_original
2.times do
expect(described_class.validate_duration(value)).to be_falsey
diff --git a/spec/lib/gitlab/ci/components/instance_path_spec.rb b/spec/lib/gitlab/ci/components/instance_path_spec.rb
index f4bc706f9b4..97843781891 100644
--- a/spec/lib/gitlab/ci/components/instance_path_spec.rb
+++ b/spec/lib/gitlab/ci/components/instance_path_spec.rb
@@ -14,125 +14,214 @@ RSpec.describe Gitlab::Ci::Components::InstancePath, feature_category: :pipeline
end
describe 'FQDN path' do
- let_it_be(:existing_project) { create(:project, :repository) }
-
- let(:project_path) { existing_project.full_path }
- let(:address) { "acme.com/#{project_path}/component@#{version}" }
let(:version) { 'master' }
+ let(:project_path) { project.full_path }
+ let(:address) { "acme.com/#{project_path}/secret-detection@#{version}" }
+
+ context 'when the project repository contains a templates directory' do
+ let_it_be(:project) do
+ create(
+ :project, :custom_repo,
+ files: {
+ 'templates/secret-detection.yml' => 'image: alpine_1',
+ 'templates/dast/template.yml' => 'image: alpine_2',
+ 'templates/dast/another-template.yml' => 'image: alpine_3',
+ 'templates/dast/another-folder/template.yml' => 'image: alpine_4'
+ }
+ )
+ end
- context 'when project exists' do
- it 'provides the expected attributes', :aggregate_failures do
- expect(path.project).to eq(existing_project)
- expect(path.host).to eq(current_host)
- expect(path.sha).to eq(existing_project.commit('master').id)
- expect(path.project_file_path).to eq('component/template.yml')
+ before do
+ project.add_developer(user)
end
- context 'when content exists' do
- let(:content) { 'image: alpine' }
+ context 'when user does not have permissions' do
+ it 'raises an error when fetching the content' do
+ expect { path.fetch_content!(current_user: build(:user)) }
+ .to raise_error(Gitlab::Access::AccessDeniedError)
+ end
+ end
- before do
- allow_next_instance_of(Repository) do |instance|
- allow(instance)
- .to receive(:blob_data_at)
- .with(existing_project.commit('master').id, 'component/template.yml')
- .and_return(content)
- end
+ context 'when the component is simple (single file template)' do
+ it 'fetches the component content', :aggregate_failures do
+ expect(path.fetch_content!(current_user: user)).to eq('image: alpine_1')
+ expect(path.host).to eq(current_host)
+ expect(path.project_file_path).to eq('templates/secret-detection.yml')
+ expect(path.project).to eq(project)
+ expect(path.sha).to eq(project.commit('master').id)
end
+ end
- context 'when user has permissions to read code' do
- before do
- existing_project.add_developer(user)
- end
+ context 'when the component is complex (directory-based template)' do
+ let(:address) { "acme.com/#{project_path}/dast@#{version}" }
+
+ it 'fetches the component content', :aggregate_failures do
+ expect(path.fetch_content!(current_user: user)).to eq('image: alpine_2')
+ expect(path.host).to eq(current_host)
+ expect(path.project_file_path).to eq('templates/dast/template.yml')
+ expect(path.project).to eq(project)
+ expect(path.sha).to eq(project.commit('master').id)
+ end
- it 'fetches the content' do
- expect(path.fetch_content!(current_user: user)).to eq(content)
+ context 'when there is an invalid nested component folder' do
+ let(:address) { "acme.com/#{project_path}/dast/another-folder@#{version}" }
+
+ it 'returns nil' do
+ expect(path.fetch_content!(current_user: user)).to be_nil
end
end
- context 'when user does not have permissions to download code' do
- it 'raises an error when fetching the content' do
- expect { path.fetch_content!(current_user: user) }
- .to raise_error(Gitlab::Access::AccessDeniedError)
+ context 'when there is an invalid nested component path' do
+ let(:address) { "acme.com/#{project_path}/dast/another-template@#{version}" }
+
+ it 'returns nil' do
+ expect(path.fetch_content!(current_user: user)).to be_nil
end
end
end
- end
- context 'when project path is nested under a subgroup' do
- let(:existing_group) { create(:group, :nested) }
- let(:existing_project) { create(:project, :repository, group: existing_group) }
+ context 'when fetching the latest version of a component' do
+ let_it_be(:project) do
+ create(
+ :project, :custom_repo,
+ files: {
+ 'templates/secret-detection.yml' => 'image: alpine_1'
+ }
+ )
+ end
- it 'provides the expected attributes', :aggregate_failures do
- expect(path.project).to eq(existing_project)
- expect(path.host).to eq(current_host)
- expect(path.sha).to eq(existing_project.commit('master').id)
- expect(path.project_file_path).to eq('component/template.yml')
- end
- end
+ let(:version) { '~latest' }
- context 'when current GitLab instance is installed on a relative URL' do
- let(:address) { "acme.com/gitlab/#{project_path}/component@#{version}" }
- let(:current_host) { 'acme.com/gitlab/' }
+ let(:latest_sha) do
+ project.repository.commit('master').id
+ end
- it 'provides the expected attributes', :aggregate_failures do
- expect(path.project).to eq(existing_project)
- expect(path.host).to eq(current_host)
- expect(path.sha).to eq(existing_project.commit('master').id)
- expect(path.project_file_path).to eq('component/template.yml')
+ before do
+ create(:release, project: project, sha: project.repository.root_ref_sha,
+ released_at: Time.zone.now - 1.day)
+
+ project.repository.update_file(
+ user, 'templates/secret-detection.yml', 'image: alpine_2',
+ message: 'Updates image', branch_name: project.default_branch
+ )
+
+ create(:release, project: project, sha: latest_sha,
+ released_at: Time.zone.now)
+ end
+
+ it 'fetches the component content', :aggregate_failures do
+ expect(path.fetch_content!(current_user: user)).to eq('image: alpine_2')
+ expect(path.host).to eq(current_host)
+ expect(path.project_file_path).to eq('templates/secret-detection.yml')
+ expect(path.project).to eq(project)
+ expect(path.sha).to eq(latest_sha)
+ end
end
- end
- context 'when version does not exist' do
- let(:version) { 'non-existent' }
+ context 'when version does not exist' do
+ let(:version) { 'non-existent' }
- it 'provides the expected attributes', :aggregate_failures do
- expect(path.project).to eq(existing_project)
- expect(path.host).to eq(current_host)
- expect(path.sha).to be_nil
- expect(path.project_file_path).to eq('component/template.yml')
+ it 'returns nil', :aggregate_failures do
+ expect(path.fetch_content!(current_user: user)).to be_nil
+ expect(path.host).to eq(current_host)
+ expect(path.project_file_path).to be_nil
+ expect(path.project).to eq(project)
+ expect(path.sha).to be_nil
+ end
end
- it 'returns nil when fetching the content' do
- expect(path.fetch_content!(current_user: user)).to be_nil
+ context 'when current GitLab instance is installed on a relative URL' do
+ let(:address) { "acme.com/gitlab/#{project_path}/secret-detection@#{version}" }
+ let(:current_host) { 'acme.com/gitlab/' }
+
+ it 'fetches the component content', :aggregate_failures do
+ expect(path.fetch_content!(current_user: user)).to eq('image: alpine_1')
+ expect(path.host).to eq(current_host)
+ expect(path.project_file_path).to eq('templates/secret-detection.yml')
+ expect(path.project).to eq(project)
+ expect(path.sha).to eq(project.commit('master').id)
+ end
end
end
- context 'when version is `~latest`' do
- let(:version) { '~latest' }
+ # All the following tests are for deprecated code and will be removed
+ # in https://gitlab.com/gitlab-org/gitlab/-/issues/415855
+ context 'when the project does not contain a templates directory' do
+ let(:project_path) { project.full_path }
+ let(:address) { "acme.com/#{project_path}/component@#{version}" }
+
+ let_it_be(:project) do
+ create(
+ :project, :custom_repo,
+ files: {
+ 'component/template.yml' => 'image: alpine'
+ }
+ )
+ end
+
+ before do
+ project.add_developer(user)
+ end
- context 'when project has releases' do
- let_it_be(:latest_release) do
- create(:release, project: existing_project, sha: 'sha-1', released_at: Time.zone.now)
- end
+ it 'fetches the component content', :aggregate_failures do
+ expect(path.fetch_content!(current_user: user)).to eq('image: alpine')
+ expect(path.host).to eq(current_host)
+ expect(path.project_file_path).to eq('component/template.yml')
+ expect(path.project).to eq(project)
+ expect(path.sha).to eq(project.commit('master').id)
+ end
- before_all do
- # Previous release
- create(:release, project: existing_project, sha: 'sha-2', released_at: Time.zone.now - 1.day)
+ context 'when project path is nested under a subgroup' do
+ let_it_be(:group) { create(:group, :nested) }
+ let_it_be(:project) do
+ create(
+ :project, :custom_repo,
+ files: {
+ 'component/template.yml' => 'image: alpine'
+ },
+ group: group
+ )
end
- it 'returns the sha of the latest release' do
- expect(path.sha).to eq(latest_release.sha)
+ it 'fetches the component content', :aggregate_failures do
+ expect(path.fetch_content!(current_user: user)).to eq('image: alpine')
+ expect(path.host).to eq(current_host)
+ expect(path.project_file_path).to eq('component/template.yml')
+ expect(path.project).to eq(project)
+ expect(path.sha).to eq(project.commit('master').id)
end
end
- context 'when project does not have releases' do
- it { expect(path.sha).to be_nil }
+ context 'when current GitLab instance is installed on a relative URL' do
+ let(:address) { "acme.com/gitlab/#{project_path}/component@#{version}" }
+ let(:current_host) { 'acme.com/gitlab/' }
+
+ it 'fetches the component content', :aggregate_failures do
+ expect(path.fetch_content!(current_user: user)).to eq('image: alpine')
+ expect(path.host).to eq(current_host)
+ expect(path.project_file_path).to eq('component/template.yml')
+ expect(path.project).to eq(project)
+ expect(path.sha).to eq(project.commit('master').id)
+ end
end
- end
- context 'when project does not exist' do
- let(:project_path) { 'non-existent/project' }
+ context 'when version does not exist' do
+ let(:version) { 'non-existent' }
- it 'provides the expected attributes', :aggregate_failures do
- expect(path.project).to be_nil
- expect(path.host).to eq(current_host)
- expect(path.sha).to be_nil
- expect(path.project_file_path).to be_nil
+ it 'returns nil', :aggregate_failures do
+ expect(path.fetch_content!(current_user: user)).to be_nil
+ expect(path.host).to eq(current_host)
+ expect(path.project_file_path).to be_nil
+ expect(path.project).to eq(project)
+ expect(path.sha).to be_nil
+ end
end
- it 'returns nil when fetching the content' do
- expect(path.fetch_content!(current_user: user)).to be_nil
+ context 'when user does not have permissions' do
+ it 'raises an error when fetching the content' do
+ expect { path.fetch_content!(current_user: build(:user)) }
+ .to raise_error(Gitlab::Access::AccessDeniedError)
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/bridge_spec.rb b/spec/lib/gitlab/ci/config/entry/bridge_spec.rb
index 736c184a289..567ffa68836 100644
--- a/spec/lib/gitlab/ci/config/entry/bridge_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/bridge_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Bridge do
# as they do not have sense in context of Bridge
let(:ignored_inheritable_columns) do
%i[before_script after_script hooks image services cache interruptible timeout
- retry tags artifacts]
+ retry tags artifacts id_tokens]
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/default_spec.rb b/spec/lib/gitlab/ci/config/entry/default_spec.rb
index 46e96843ee3..17e716629cd 100644
--- a/spec/lib/gitlab/ci/config/entry/default_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/default_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Default do
it 'contains the expected node names' do
expect(described_class.nodes.keys)
.to match_array(%i[before_script after_script hooks cache image services
- interruptible timeout retry tags artifacts])
+ interruptible timeout retry tags artifacts id_tokens])
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb b/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb
index dd15b049b9b..cd8e35ede61 100644
--- a/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper' # Change this to fast spec helper when FF `ci_refactor_external_rules` is removed
+require 'fast_spec_helper'
require_dependency 'active_model'
RSpec.describe Gitlab::Ci::Config::Entry::Include::Rules::Rule, feature_category: :pipeline_composition do
@@ -14,21 +14,11 @@ RSpec.describe Gitlab::Ci::Config::Entry::Include::Rules::Rule, feature_category
entry.compose!
end
- shared_examples 'a valid config' do
+ shared_examples 'a valid config' do |expected_value = nil|
it { is_expected.to be_valid }
it 'returns the expected value' do
- expect(entry.value).to eq(config.compact)
- end
-
- context 'when FF `ci_refactor_external_rules` is disabled' do
- before do
- stub_feature_flags(ci_refactor_external_rules: false)
- end
-
- it 'returns the expected value' do
- expect(entry.value).to eq(config)
- end
+ expect(entry.value).to eq(expected_value || config.compact)
end
end
@@ -99,19 +89,37 @@ RSpec.describe Gitlab::Ci::Config::Entry::Include::Rules::Rule, feature_category
it_behaves_like 'a valid config'
- context 'when array' do
+ context 'when exists: clause is an array' do
let(:config) { { exists: ['./this.md', './that.md'] } }
it_behaves_like 'a valid config'
end
- context 'when null' do
+ context 'when exists: clause is null' do
let(:config) { { exists: nil } }
it_behaves_like 'a valid config'
end
end
+ context 'when specifying a changes: clause' do
+ let(:config) { { changes: %w[Dockerfile lib/* paths/**/*.rb] } }
+
+ it_behaves_like 'a valid config', { changes: { paths: %w[Dockerfile lib/* paths/**/*.rb] } }
+
+ context 'with paths:' do
+ let(:config) { { changes: { paths: %w[Dockerfile lib/* paths/**/*.rb] } } }
+
+ it_behaves_like 'a valid config'
+ end
+
+ context 'with paths: and compare_to:' do
+ let(:config) { { changes: { paths: ['Dockerfile'], compare_to: 'branch1' } } }
+
+ it_behaves_like 'a valid config'
+ end
+ end
+
context 'when specifying an unknown keyword' do
let(:config) { { invalid: :something } }
diff --git a/spec/lib/gitlab/ci/config/entry/include/rules_spec.rb b/spec/lib/gitlab/ci/config/entry/include/rules_spec.rb
index 05db81abfc1..503020e2202 100644
--- a/spec/lib/gitlab/ci/config/entry/include/rules_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/include/rules_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper' # Change this to fast spec helper when FF `ci_refactor_external_rules` is removed
+require 'fast_spec_helper'
require_dependency 'active_model'
RSpec.describe Gitlab::Ci::Config::Entry::Include::Rules, feature_category: :pipeline_composition do
@@ -50,7 +50,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Include::Rules, feature_category: :pip
entry.compose!
end
- it_behaves_like 'an invalid config', /contains unknown keys: changes/
+ it_behaves_like 'a valid config'
end
end
@@ -80,7 +80,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Include::Rules, feature_category: :pip
let(:config) do
[
{ if: '$THIS == "that"' },
- { if: '$SKIP', when: 'never' }
+ { if: '$SKIP', when: 'never' },
+ { changes: ['Dockerfile'] }
]
end
@@ -96,7 +97,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Include::Rules, feature_category: :pip
is_expected.to eq(
[
{ if: '$THIS == "that"' },
- { if: '$SKIP', when: 'never' }
+ { if: '$SKIP', when: 'never' },
+ { changes: { paths: ['Dockerfile'] } }
]
)
end
@@ -115,30 +117,5 @@ RSpec.describe Gitlab::Ci::Config::Entry::Include::Rules, feature_category: :pip
end
end
end
-
- context 'when FF `ci_refactor_external_rules` is disabled' do
- before do
- stub_feature_flags(ci_refactor_external_rules: false)
- end
-
- context 'with an "if"' do
- let(:config) do
- [{ if: '$THIS == "that"' }]
- end
-
- it { is_expected.to eq(config) }
- end
-
- context 'with a list of two rules' do
- let(:config) do
- [
- { if: '$THIS == "that"' },
- { if: '$SKIP' }
- ]
- end
-
- it { is_expected.to eq(config) }
- 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 4be7c11fab0..1a78d929871 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::Entry::Job, feature_category: :pipeline_composition do
+ using RSpec::Parameterized::TableSyntax
+
let(:entry) { described_class.new(config, name: :rspec) }
it_behaves_like 'with inheritable CI config' do
@@ -29,7 +31,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job, feature_category: :pipeline_compo
let(:result) do
%i[before_script script after_script hooks stage cache
image services only except rules needs variables artifacts
- environment coverage retry interruptible timeout release tags
+ coverage retry interruptible timeout release tags
inherit parallel]
end
@@ -696,8 +698,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job, feature_category: :pipeline_compo
end
context 'with workflow rules' do
- using RSpec::Parameterized::TableSyntax
-
where(:name, :has_workflow_rules?, :only, :rules, :result) do
"uses default only" | false | nil | nil | { refs: %w[branches tags] }
"uses user only" | false | %w[branches] | nil | { refs: %w[branches] }
@@ -739,6 +739,20 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job, feature_category: :pipeline_compo
end
end
+ describe '#pages_job?', :aggregate_failures, feature_category: :pages do
+ where(:name, :result) do
+ :pages | true
+ :'pages:staging' | false
+ :'something:pages:else' | false
+ end
+
+ with_them do
+ subject { described_class.new({}, name: name).pages_job? }
+
+ it { is_expected.to eq(result) }
+ end
+ end
+
context 'when composed' do
before do
entry.compose!
diff --git a/spec/lib/gitlab/ci/config/entry/processable_spec.rb b/spec/lib/gitlab/ci/config/entry/processable_spec.rb
index 4f13940d7e2..132e75a808b 100644
--- a/spec/lib/gitlab/ci/config/entry/processable_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/processable_spec.rb
@@ -371,6 +371,39 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable, feature_category: :pipeli
end
end
+ context 'with environment' do
+ context 'when environment name is specified' do
+ let(:config) { { script: 'ls', environment: 'prod' }.compact }
+
+ it 'sets environment name and action to the entry value' do
+ entry.compose!(deps)
+
+ expect(entry.value[:environment]).to eq({ action: 'start', name: 'prod' })
+ expect(entry.value[:environment_name]).to eq('prod')
+ end
+ end
+
+ context 'when environment name, url and action are specified' do
+ let(:config) do
+ {
+ script: 'ls',
+ environment: {
+ name: 'staging',
+ url: 'https://gitlab.com',
+ action: 'prepare'
+ }
+ }.compact
+ end
+
+ it 'sets environment name, action and url to the entry value' do
+ entry.compose!(deps)
+
+ expect(entry.value[:environment]).to eq({ action: 'prepare', name: 'staging', url: 'https://gitlab.com' })
+ expect(entry.value[:environment_name]).to eq('staging')
+ end
+ end
+ end
+
context 'with inheritance' do
context 'of default:tags' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/lib/gitlab/ci/config/external/context_spec.rb b/spec/lib/gitlab/ci/config/external/context_spec.rb
index d8bd578be94..9ac72ebbac8 100644
--- a/spec/lib/gitlab/ci/config/external/context_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/context_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::External::Context, feature_category: :pipeline_composition do
let(:project) { build(:project) }
+ let(:pipeline) { double('Pipeline') }
let(:user) { double('User') }
let(:sha) { '12345' }
let(:variables) { Gitlab::Ci::Variables::Collection.new([{ 'key' => 'a', 'value' => 'b' }]) }
@@ -11,6 +12,7 @@ RSpec.describe Gitlab::Ci::Config::External::Context, feature_category: :pipelin
let(:attributes) do
{
project: project,
+ pipeline: pipeline,
user: user,
sha: sha,
variables: variables,
@@ -32,7 +34,7 @@ RSpec.describe Gitlab::Ci::Config::External::Context, feature_category: :pipelin
end
context 'without values' do
- let(:attributes) { { project: nil, user: nil, sha: nil } }
+ let(:attributes) { { project: nil, pipeline: nil, user: nil, sha: nil } }
it { is_expected.to have_attributes(**attributes) }
it { expect(subject.expandset).to eq([]) }
@@ -148,6 +150,7 @@ RSpec.describe Gitlab::Ci::Config::External::Context, feature_category: :pipelin
let(:attributes) do
{
project: project,
+ pipeline: pipeline,
user: user,
sha: sha,
logger: double('logger')
@@ -165,6 +168,7 @@ RSpec.describe Gitlab::Ci::Config::External::Context, feature_category: :pipelin
it { expect(mutated).not_to eq(subject) }
it { expect(mutated).to be_a(described_class) }
it { expect(mutated).to have_attributes(new_attributes) }
+ it { expect(mutated.pipeline).to eq(subject.pipeline) }
it { expect(mutated.expandset).to eq(subject.expandset) }
it { expect(mutated.execution_deadline).to eq(mutated.execution_deadline) }
it { expect(mutated.logger).to eq(mutated.logger) }
diff --git a/spec/lib/gitlab/ci/config/external/file/component_spec.rb b/spec/lib/gitlab/ci/config/external/file/component_spec.rb
index 487690296b5..0f7b811b5df 100644
--- a/spec/lib/gitlab/ci/config/external/file/component_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/component_spec.rb
@@ -120,6 +120,41 @@ RSpec.describe Gitlab::Ci::Config::External::File::Component, feature_category:
end
end
+ describe '#content' do
+ context 'when component is valid' do
+ let(:content) do
+ <<~COMPONENT
+ job:
+ script: echo
+ COMPONENT
+ end
+
+ let(:response) do
+ ServiceResponse.success(payload: {
+ content: content,
+ path: instance_double(::Gitlab::Ci::Components::InstancePath, project: project, sha: '12345')
+ })
+ end
+
+ it 'tracks the event' do
+ expect(::Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event).with('cicd_component_usage',
+ values: external_resource.context.user.id)
+
+ external_resource.content
+ end
+ end
+
+ context 'when component is invalid' do
+ let(:content) { 'the-content' }
+
+ it 'does not track the event' do
+ expect(::Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event)
+
+ external_resource.content
+ end
+ end
+ end
+
describe '#metadata' do
subject(:metadata) { external_resource.metadata }
diff --git a/spec/lib/gitlab/ci/config/external/mapper/verifier_spec.rb b/spec/lib/gitlab/ci/config/external/mapper/verifier_spec.rb
index 69b0524be9e..f542c0485e0 100644
--- a/spec/lib/gitlab/ci/config/external/mapper/verifier_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/mapper/verifier_spec.rb
@@ -409,32 +409,6 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper::Verifier, feature_category:
expect { process }.to raise_error(expected_error_class)
end
end
-
- context 'when introduce_ci_max_total_yaml_size_bytes is disabled' do
- before do
- stub_feature_flags(introduce_ci_max_total_yaml_size_bytes: false)
- end
-
- context 'when pipeline tree size is within the limit' do
- before do
- stub_application_setting(ci_max_total_yaml_size_bytes: 10000)
- end
-
- it 'passes the verification' do
- expect(process.all?(&:valid?)).to be_truthy
- end
- end
-
- context 'when pipeline tree size is larger then the limit' do
- before do
- stub_application_setting(ci_max_total_yaml_size_bytes: 100)
- end
-
- it 'passes the verification' do
- expect(process.all?(&:valid?)).to be_truthy
- end
- end
- end
end
end
end
diff --git a/spec/lib/gitlab/ci/config/external/processor_spec.rb b/spec/lib/gitlab/ci/config/external/processor_spec.rb
index 19113ce6a4e..68cdf56f198 100644
--- a/spec/lib/gitlab/ci/config/external/processor_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/processor_spec.rb
@@ -557,21 +557,11 @@ RSpec.describe Gitlab::Ci::Config::External::Processor, feature_category: :pipel
context 'when rules defined' do
context 'when a rule is invalid' do
let(:values) do
- { include: [{ local: 'builds.yml', rules: [{ changes: ['$MY_VAR'] }] }] }
+ { include: [{ local: 'builds.yml', rules: [{ allow_failure: ['$MY_VAR'] }] }] }
end
it 'raises IncludeError' do
- expect { subject }.to raise_error(described_class::IncludeError, /contains unknown keys: changes/)
- end
-
- context 'when FF `ci_refactor_external_rules` is disabled' do
- before do
- stub_feature_flags(ci_refactor_external_rules: false)
- end
-
- it 'raises IncludeError' do
- expect { subject }.to raise_error(described_class::IncludeError, /invalid include rule/)
- end
+ expect { subject }.to raise_error(described_class::IncludeError, /contains unknown keys: allow_failure/)
end
end
end
diff --git a/spec/lib/gitlab/ci/config/external/rules_spec.rb b/spec/lib/gitlab/ci/config/external/rules_spec.rb
index 8674af7ab65..15d7801ff2a 100644
--- a/spec/lib/gitlab/ci/config/external/rules_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/rules_spec.rb
@@ -4,76 +4,45 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::External::Rules, feature_category: :pipeline_composition do
let(:context) { double(variables_hash: {}) }
- let(:rule_hashes) { [{ if: '$MY_VAR == "hello"' }] }
+ let(:rule_hashes) {}
+ let(:pipeline) { instance_double(Ci::Pipeline) }
+ let_it_be(:project) { create(:project, :custom_repo, files: { 'file.txt' => 'file' }) }
subject(:rules) { described_class.new(rule_hashes) }
+ before do
+ allow(context).to receive(:project).and_return(project)
+ allow(context).to receive(:pipeline).and_return(pipeline)
+ end
+
describe '#evaluate' do
subject(:result) { rules.evaluate(context).pass? }
context 'when there is no rule' do
- let(:rule_hashes) {}
-
it { is_expected.to eq(true) }
end
- shared_examples 'when there is a rule with if' do |rule_matched_result = true, rule_not_matched_result = false|
- context 'when the rule matches' do
- let(:context) { double(variables_hash: { 'MY_VAR' => 'hello' }) }
-
- it { is_expected.to eq(rule_matched_result) }
- end
-
- context 'when the rule does not match' do
- let(:context) { double(variables_hash: { 'MY_VAR' => 'invalid' }) }
-
- it { is_expected.to eq(rule_not_matched_result) }
- end
- end
-
- shared_examples 'when there is a rule with exists' do |file_exists_result = true, file_not_exists_result = false|
- let(:project) { create(:project, :repository) }
-
- context 'when the file exists' do
- let(:context) { double(project: project, sha: project.repository.tree.sha, top_level_worktree_paths: ['Dockerfile']) }
-
+ shared_examples 'with when: specified' do
+ context 'with when: never' do
before do
- project.repository.create_file(project.first_owner, 'Dockerfile', "commit", message: 'test', branch_name: "master")
+ rule_hashes.first[:when] = 'never'
end
- it { is_expected.to eq(file_exists_result) }
- end
-
- context 'when the file does not exist' do
- let(:context) { double(project: project, sha: project.repository.tree.sha, top_level_worktree_paths: ['test.md']) }
-
- it { is_expected.to eq(file_not_exists_result) }
- end
- end
-
- it_behaves_like 'when there is a rule with if'
-
- context 'when there is a rule with exists' do
- let(:rule_hashes) { [{ exists: 'Dockerfile' }] }
-
- it_behaves_like 'when there is a rule with exists'
- end
-
- context 'when there is a rule with if and when' do
- context 'with when: never' do
- let(:rule_hashes) { [{ if: '$MY_VAR == "hello"', when: 'never' }] }
-
- it_behaves_like 'when there is a rule with if', false, false
+ it { is_expected.to eq(false) }
end
context 'with when: always' do
- let(:rule_hashes) { [{ if: '$MY_VAR == "hello"', when: 'always' }] }
+ before do
+ rule_hashes.first[:when] = 'always'
+ end
- it_behaves_like 'when there is a rule with if'
+ it { is_expected.to eq(true) }
end
context 'with when: <invalid string>' do
- let(:rule_hashes) { [{ if: '$MY_VAR == "hello"', when: 'on_success' }] }
+ before do
+ rule_hashes.first[:when] = 'on_success'
+ end
it 'raises an error' do
expect { result }.to raise_error(described_class::InvalidIncludeRulesError, /when unknown value: on_success/)
@@ -81,132 +50,125 @@ RSpec.describe Gitlab::Ci::Config::External::Rules, feature_category: :pipeline_
end
context 'with when: null' do
- let(:rule_hashes) { [{ if: '$MY_VAR == "hello"', when: nil }] }
+ before do
+ rule_hashes.first[:when] = nil
+ end
- it_behaves_like 'when there is a rule with if'
+ it { is_expected.to eq(true) }
end
end
- context 'when there is a rule with exists and when' do
- context 'with when: never' do
- let(:rule_hashes) { [{ exists: 'Dockerfile', when: 'never' }] }
+ context 'when there is a rule with if:' do
+ let(:rule_hashes) { [{ if: '$MY_VAR == "hello"' }] }
- it_behaves_like 'when there is a rule with exists', false, false
- end
+ context 'when the rule matches' do
+ let(:context) { double(variables_hash: { 'MY_VAR' => 'hello' }) }
- context 'with when: always' do
- let(:rule_hashes) { [{ exists: 'Dockerfile', when: 'always' }] }
+ it { is_expected.to eq(true) }
- it_behaves_like 'when there is a rule with exists'
+ it_behaves_like 'with when: specified'
end
- context 'with when: <invalid string>' do
- let(:rule_hashes) { [{ exists: 'Dockerfile', when: 'on_success' }] }
+ context 'when the rule does not match' do
+ let(:context) { double(variables_hash: { 'MY_VAR' => 'invalid' }) }
- it 'raises an error' do
- expect { result }.to raise_error(described_class::InvalidIncludeRulesError, /when unknown value: on_success/)
- end
+ it { is_expected.to eq(false) }
end
+ end
- context 'with when: null' do
- let(:rule_hashes) { [{ exists: 'Dockerfile', when: nil }] }
+ context 'when there is a rule with exists:' do
+ let(:rule_hashes) { [{ exists: 'file.txt' }] }
- it_behaves_like 'when there is a rule with exists'
+ context 'when the file exists' do
+ let(:context) { double(top_level_worktree_paths: ['file.txt']) }
+
+ it { is_expected.to eq(true) }
+
+ it_behaves_like 'with when: specified'
end
- end
- context 'when there is a rule with changes' do
- let(:rule_hashes) { [{ changes: ['$MY_VAR'] }] }
+ context 'when the file does not exist' do
+ let(:context) { double(top_level_worktree_paths: ['README.md']) }
- it 'raises an error' do
- expect { result }.to raise_error(described_class::InvalidIncludeRulesError, /contains unknown keys: changes/)
+ it { is_expected.to eq(false) }
end
end
- context 'when FF `ci_refactor_external_rules` is disabled' do
- before do
- stub_feature_flags(ci_refactor_external_rules: false)
- end
+ context 'when there is a rule with changes:' do
+ let(:rule_hashes) { [{ changes: ['file.txt'] }] }
- context 'when there is no rule' do
- let(:rule_hashes) {}
+ shared_examples 'when the pipeline has modified paths' do
+ let(:modified_paths) { ['file.txt'] }
- it { is_expected.to eq(true) }
- end
+ before do
+ allow(pipeline).to receive(:modified_paths).and_return(modified_paths)
+ end
- it_behaves_like 'when there is a rule with if'
+ context 'when the file has changed' do
+ it { is_expected.to eq(true) }
- context 'when there is a rule with exists' do
- let(:rule_hashes) { [{ exists: 'Dockerfile' }] }
+ it_behaves_like 'with when: specified'
+ end
- it_behaves_like 'when there is a rule with exists'
+ context 'when the file has not changed' do
+ let(:modified_paths) { ['README.md'] }
+
+ it { is_expected.to eq(false) }
+ end
end
- context 'when there is a rule with if and when' do
- context 'with when: never' do
- let(:rule_hashes) { [{ if: '$MY_VAR == "hello"', when: 'never' }] }
+ it_behaves_like 'when the pipeline has modified paths'
- it_behaves_like 'when there is a rule with if', false, false
- end
+ context 'with paths: specified' do
+ let(:rule_hashes) { [{ changes: { paths: ['file.txt'] } }] }
- context 'with when: always' do
- let(:rule_hashes) { [{ if: '$MY_VAR == "hello"', when: 'always' }] }
+ it_behaves_like 'when the pipeline has modified paths'
+ end
- it_behaves_like 'when there is a rule with if'
- end
+ context 'with paths: and compare_to: specified' do
+ before_all do
+ project.repository.add_branch(project.owner, 'branch1', 'master')
- context 'with when: <invalid string>' do
- let(:rule_hashes) { [{ if: '$MY_VAR == "hello"', when: 'on_success' }] }
+ project.repository.update_file(
+ project.owner, 'file.txt', 'file updated', message: 'Update file.txt', branch_name: 'branch1'
+ )
- it 'raises an error' do
- expect { result }.to raise_error(described_class::InvalidIncludeRulesError,
- 'invalid include rule: {:if=>"$MY_VAR == \"hello\"", :when=>"on_success"}')
- end
+ project.repository.add_branch(project.owner, 'branch2', 'branch1')
end
- context 'with when: null' do
- let(:rule_hashes) { [{ if: '$MY_VAR == "hello"', when: nil }] }
-
- it_behaves_like 'when there is a rule with if'
+ let_it_be(:pipeline) do
+ build(:ci_pipeline, project: project, ref: 'branch2', sha: project.commit('branch2').sha)
end
- end
- context 'when there is a rule with exists and when' do
- context 'with when: never' do
- let(:rule_hashes) { [{ exists: 'Dockerfile', when: 'never' }] }
+ context 'when the file has changed compared to the given ref' do
+ let(:rule_hashes) { [{ changes: { paths: ['file.txt'], compare_to: 'master' } }] }
+
+ it { is_expected.to eq(true) }
- it_behaves_like 'when there is a rule with exists', false, false
+ it_behaves_like 'with when: specified'
end
- context 'with when: always' do
- let(:rule_hashes) { [{ exists: 'Dockerfile', when: 'always' }] }
+ context 'when the file has not changed compared to the given ref' do
+ let(:rule_hashes) { [{ changes: { paths: ['file.txt'], compare_to: 'branch1' } }] }
- it_behaves_like 'when there is a rule with exists'
+ it { is_expected.to eq(false) }
end
- context 'with when: <invalid string>' do
- let(:rule_hashes) { [{ exists: 'Dockerfile', when: 'on_success' }] }
+ context 'when compare_to: is invalid' do
+ let(:rule_hashes) { [{ changes: { paths: ['file.txt'], compare_to: 'invalid' } }] }
it 'raises an error' do
- expect { result }.to raise_error(described_class::InvalidIncludeRulesError,
- 'invalid include rule: {:exists=>"Dockerfile", :when=>"on_success"}')
+ expect { result }.to raise_error(described_class::InvalidIncludeRulesError, /compare_to is not a valid ref/)
end
end
-
- context 'with when: null' do
- let(:rule_hashes) { [{ exists: 'Dockerfile', when: nil }] }
-
- it_behaves_like 'when there is a rule with exists'
- end
end
+ end
- context 'when there is a rule with changes' do
- let(:rule_hashes) { [{ changes: ['$MY_VAR'] }] }
+ context 'when there is a rule with an invalid key' do
+ let(:rule_hashes) { [{ invalid: ['$MY_VAR'] }] }
- it 'raises an error' do
- expect { result }.to raise_error(described_class::InvalidIncludeRulesError,
- 'invalid include rule: {:changes=>["$MY_VAR"]}')
- end
+ it 'raises an error' do
+ expect { result }.to raise_error(described_class::InvalidIncludeRulesError, /contains unknown keys: invalid/)
end
end
end
diff --git a/spec/lib/gitlab/ci/config/interpolation/interpolator_spec.rb b/spec/lib/gitlab/ci/config/interpolation/interpolator_spec.rb
index 7bb09d35064..804164c933a 100644
--- a/spec/lib/gitlab/ci/config/interpolation/interpolator_spec.rb
+++ b/spec/lib/gitlab/ci/config/interpolation/interpolator_spec.rb
@@ -57,7 +57,8 @@ RSpec.describe Gitlab::Ci::Config::Interpolation::Interpolator, feature_category
expect(subject).not_to be_valid
expect(subject.error_message).to eq subject.errors.first
- expect(subject.errors).to include('unknown input arguments')
+ expect(subject.errors).to include('Given inputs not defined in the `spec` section of the included ' \
+ 'configuration file')
end
end
diff --git a/spec/lib/gitlab/ci/config/yaml/tags/reference_spec.rb b/spec/lib/gitlab/ci/config/yaml/tags/reference_spec.rb
index bf89942bf14..0af1b721eb6 100644
--- a/spec/lib/gitlab/ci/config/yaml/tags/reference_spec.rb
+++ b/spec/lib/gitlab/ci/config/yaml/tags/reference_spec.rb
@@ -2,9 +2,9 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Config::Yaml::Tags::Reference do
+RSpec.describe Gitlab::Ci::Config::Yaml::Tags::Reference, feature_category: :pipeline_composition do
let(:config) do
- Gitlab::Ci::Config::Yaml.load!(yaml)
+ Gitlab::Ci::Config::Yaml::Loader.new(yaml).load.content
end
describe '.tag' do
diff --git a/spec/lib/gitlab/ci/config/yaml/tags/resolver_spec.rb b/spec/lib/gitlab/ci/config/yaml/tags/resolver_spec.rb
index 594242c33cc..74d7513ebdf 100644
--- a/spec/lib/gitlab/ci/config/yaml/tags/resolver_spec.rb
+++ b/spec/lib/gitlab/ci/config/yaml/tags/resolver_spec.rb
@@ -2,9 +2,9 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Config::Yaml::Tags::Resolver do
+RSpec.describe Gitlab::Ci::Config::Yaml::Tags::Resolver, feature_category: :pipeline_composition do
let(:config) do
- Gitlab::Ci::Config::Yaml.load!(yaml)
+ Gitlab::Ci::Config::Yaml::Loader.new(yaml).load.content
end
describe '#to_hash' do
diff --git a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb
index d06537ac330..a331af9a9ac 100644
--- a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb
@@ -3,18 +3,20 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Parsers::Sbom::Cyclonedx, feature_category: :dependency_management do
- let(:report) { instance_double('Gitlab::Ci::Reports::Sbom::Report') }
+ let(:report) { Gitlab::Ci::Reports::Sbom::Report.new }
let(:report_data) { base_report_data }
let(:raw_report_data) { report_data.to_json }
let(:report_valid?) { true }
let(:validator_errors) { [] }
let(:properties_parser) { class_double('Gitlab::Ci::Parsers::Sbom::CyclonedxProperties') }
+ let(:uuid) { 'c9d550a3-feb8-483b-a901-5aa892d039f9' }
let(:base_report_data) do
{
'bomFormat' => 'CycloneDX',
'specVersion' => '1.4',
- 'version' => 1
+ 'version' => 1,
+ 'serialNumber' => "urn:uuid:#{uuid}"
}
end
@@ -28,6 +30,7 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::Cyclonedx, feature_category: :dependen
allow(properties_parser).to receive(:parse_source)
stub_const('Gitlab::Ci::Parsers::Sbom::CyclonedxProperties', properties_parser)
+ allow(SecureRandom).to receive(:uuid).and_return(uuid)
end
context 'when report JSON is invalid' do
@@ -149,8 +152,22 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::Cyclonedx, feature_category: :dependen
end
end
- context 'when report has metadata properties' do
- let(:report_data) { base_report_data.merge({ 'metadata' => { 'properties' => properties } }) }
+ context 'when report has metadata tools, author and properties' do
+ let(:report_data) { base_report_data.merge(metadata) }
+
+ let(:tools) do
+ [
+ { name: 'Gemnasium', vendor: 'vendor-1', version: '2.34.0' },
+ { name: 'Gemnasium', vendor: 'vendor-2', version: '2.34.0' }
+ ]
+ end
+
+ let(:authors) do
+ [
+ { name: 'author-1', email: 'support@gitlab.com' },
+ { name: 'author-2', email: 'support@gitlab.com' }
+ ]
+ end
let(:properties) do
[
@@ -163,10 +180,44 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::Cyclonedx, feature_category: :dependen
]
end
- it 'passes them to the properties parser' do
- expect(properties_parser).to receive(:parse_source).with(properties)
+ context 'when metadata attributes are present' do
+ let(:metadata) do
+ {
+ 'metadata' => {
+ 'tools' => tools,
+ 'authors' => authors,
+ 'properties' => properties
+ }
+ }
+ end
- parse!
+ it 'passes them to the report' do
+ expect(properties_parser).to receive(:parse_source).with(properties)
+
+ parse!
+
+ expect(report.metadata).to have_attributes(
+ tools: tools.map(&:with_indifferent_access),
+ authors: authors.map(&:with_indifferent_access),
+ properties: properties.map(&:with_indifferent_access)
+ )
+ end
+ end
+
+ context 'when metadata attributes are not present' do
+ let(:metadata) { { 'metadata' => {} } }
+
+ it 'passes them to the report' do
+ expect(properties_parser).to receive(:parse_source).with(nil)
+
+ parse!
+
+ expect(report.metadata).to have_attributes(
+ tools: [],
+ authors: [],
+ properties: []
+ )
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/parsers/security/common_spec.rb b/spec/lib/gitlab/ci/parsers/security/common_spec.rb
index dc16ddf4e0e..9470d59f502 100644
--- a/spec/lib/gitlab/ci/parsers/security/common_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/security/common_spec.rb
@@ -229,8 +229,9 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common, feature_category: :vulnera
describe 'parsing finding.details' do
context 'when details are provided' do
+ let(:finding) { report.findings[4] }
+
it 'sets details from the report' do
- finding = report.findings.find { |x| x.compare_key == 'CVE-1020' }
expected_details = Gitlab::Json.parse(finding.raw_metadata)['details']
expect(finding.details).to eq(expected_details)
@@ -238,8 +239,9 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common, feature_category: :vulnera
end
context 'when details are not provided' do
+ let(:finding) { report.findings[5] }
+
it 'sets empty hash' do
- finding = report.findings.find { |x| x.compare_key == 'CVE-1030' }
expect(finding.details).to eq({})
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
index 5f87e0ccc33..54e569f424b 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
@@ -1081,6 +1081,126 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build, feature_category: :pipeline_co
end
end
+ context 'with a rule using CI_ENVIRONMENT_ACTION variable' do
+ let(:rule_set) do
+ [{ if: '$CI_ENVIRONMENT_ACTION == "start"' }]
+ end
+
+ context 'when environment:action satisfies the rule' do
+ let(:attributes) do
+ { name: 'rspec', rules: rule_set, environment: 'test', when: 'on_success',
+ options: { environment: { action: 'start' } } }
+ end
+
+ it { is_expected.to be_included }
+
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'on_success')
+ end
+ end
+
+ context 'when environment:action does not satisfy rule' do
+ let(:attributes) do
+ { name: 'rspec', rules: rule_set, environment: 'test', when: 'on_success',
+ options: { environment: { action: 'stop' } } }
+ end
+
+ it { is_expected.not_to be_included }
+
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'never')
+ end
+ end
+
+ context 'when environment:action is not set' do
+ it { is_expected.not_to be_included }
+
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'never')
+ end
+ end
+ end
+
+ context 'with a rule using CI_ENVIRONMENT_TIER variable' do
+ let(:rule_set) do
+ [{ if: '$CI_ENVIRONMENT_TIER == "production"' }]
+ end
+
+ context 'when environment:deployment_tier satisfies the rule' do
+ let(:attributes) do
+ { name: 'rspec', rules: rule_set, environment: 'test', when: 'on_success',
+ options: { environment: { deployment_tier: 'production' } } }
+ end
+
+ it { is_expected.to be_included }
+
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'on_success')
+ end
+ end
+
+ context 'when environment:deployment_tier does not satisfy rule' do
+ let(:attributes) do
+ { name: 'rspec', rules: rule_set, environment: 'test', when: 'on_success',
+ options: { environment: { deployment_tier: 'development' } } }
+ end
+
+ it { is_expected.not_to be_included }
+
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'never')
+ end
+ end
+
+ context 'when environment:action is not set' do
+ it { is_expected.not_to be_included }
+
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'never')
+ end
+ end
+ end
+
+ context 'with a rule using CI_ENVIRONMENT_URL variable' do
+ let(:rule_set) do
+ [{ if: '$CI_ENVIRONMENT_URL == "http://gitlab.com"' }]
+ end
+
+ context 'when environment:url satisfies the rule' do
+ let(:attributes) do
+ { name: 'rspec', rules: rule_set, environment: 'test', when: 'on_success',
+ options: { environment: { url: 'http://gitlab.com' } } }
+ end
+
+ it { is_expected.to be_included }
+
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'on_success')
+ end
+ end
+
+ context 'when environment:url does not satisfy rule' do
+ let(:attributes) do
+ { name: 'rspec', rules: rule_set, environment: 'test', when: 'on_success',
+ options: { environment: { url: 'http://staging.gitlab.com' } } }
+ end
+
+ it { is_expected.not_to be_included }
+
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'never')
+ end
+ end
+
+ context 'when environment:action is not set' do
+ it { is_expected.not_to be_included }
+
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'never')
+ end
+ end
+ end
+
context 'with no rules' do
let(:rule_set) { [] }
diff --git a/spec/lib/gitlab/ci/reports/sbom/component_spec.rb b/spec/lib/gitlab/ci/reports/sbom/component_spec.rb
index d62d25aeefe..4c9fd00f96a 100644
--- a/spec/lib/gitlab/ci/reports/sbom/component_spec.rb
+++ b/spec/lib/gitlab/ci/reports/sbom/component_spec.rb
@@ -49,6 +49,18 @@ RSpec.describe Gitlab::Ci::Reports::Sbom::Component, feature_category: :dependen
end
end
+ describe '#purl_type' do
+ subject { component.purl_type }
+
+ it { is_expected.to eq(purl_type) }
+ end
+
+ describe '#type' do
+ subject { component.type }
+
+ it { is_expected.to eq(component_type) }
+ end
+
describe '#<=>' do
where do
{
diff --git a/spec/lib/gitlab/ci/reports/sbom/metadata_spec.rb b/spec/lib/gitlab/ci/reports/sbom/metadata_spec.rb
new file mode 100644
index 00000000000..fe0b9481039
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/sbom/metadata_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Reports::Sbom::Metadata, feature_category: :dependency_management do
+ let(:tools) do
+ [
+ {
+ vendor: "vendor",
+ name: "Gemnasium",
+ version: "2.34.0"
+ }
+ ]
+ end
+
+ let(:authors) do
+ [
+ {
+ name: "author_name",
+ email: "support@gitlab.com"
+ }
+ ]
+ end
+
+ let(:properties) do
+ [
+ {
+ name: "property_name",
+ value: "package-lock.json"
+ }
+ ]
+ end
+
+ let(:timestamp) { "2020-04-13T20:20:39+00:00" }
+
+ subject(:metadata) do
+ metadata = described_class.new(
+ tools: tools,
+ authors: authors,
+ properties: properties
+ )
+ metadata.timestamp = timestamp
+ metadata
+ end
+
+ it 'has correct attributes' do
+ expect(metadata).to have_attributes(
+ tools: tools,
+ authors: authors,
+ properties: properties,
+ timestamp: timestamp
+ )
+ end
+end
diff --git a/spec/lib/gitlab/ci/templates/MATLAB_spec.rb b/spec/lib/gitlab/ci/templates/MATLAB_spec.rb
index 3889d1fc8c9..8b6ff7f27a2 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')
+ expect(build_names).to include('command', 'test', 'test_artifacts', 'build')
end
end
end
diff --git a/spec/lib/gitlab/ci/trace/stream_spec.rb b/spec/lib/gitlab/ci/trace/stream_spec.rb
index d65b6fb41f6..9439d29aa11 100644
--- a/spec/lib/gitlab/ci/trace/stream_spec.rb
+++ b/spec/lib/gitlab/ci/trace/stream_spec.rb
@@ -243,6 +243,56 @@ RSpec.describe Gitlab::Ci::Trace::Stream, :clean_gitlab_redis_cache do
expect(result.encoding).to eq(Encoding.default_external)
end
end
+
+ context 'limit max size' do
+ before do
+ # specifying BUFFER_SIZE forces to seek backwards
+ allow(described_class).to receive(:BUFFER_SIZE)
+ .and_return(2)
+ end
+
+ it 'returns every lines with respect of the size' do
+ all_lines = lines.join
+ max_size = all_lines.bytesize.div(2)
+ result = stream.raw(max_size: max_size)
+
+ expect(result.bytes).to eq(all_lines.bytes[-max_size..])
+ expect(result.lines.count).to be > 1
+ expect(result.encoding).to eq(Encoding.default_external)
+ end
+
+ it 'returns everything if trying to get too many bytes' do
+ all_lines = lines.join
+ result = stream.raw(max_size: all_lines.bytesize * 2)
+
+ expect(result).to eq(all_lines)
+ expect(result.encoding).to eq(Encoding.default_external)
+ end
+ end
+
+ context 'limit max lines and max size' do
+ before do
+ # specifying BUFFER_SIZE forces to seek backwards
+ allow(described_class).to receive(:BUFFER_SIZE)
+ .and_return(2)
+ end
+
+ it 'returns max lines if max size is greater' do
+ result = stream.raw(last_lines: 2, max_size: lines.join.bytesize * 2)
+
+ expect(result).to eq(lines.last(2).join)
+ expect(result.encoding).to eq(Encoding.default_external)
+ end
+
+ it 'returns max size if max lines is greater' do
+ all_lines = lines.join
+ max_size = all_lines.bytesize.div(2)
+ result = stream.raw(last_lines: lines.size * 2, max_size: max_size)
+
+ expect(result.bytes).to eq(all_lines.bytes[-max_size..])
+ expect(result.encoding).to eq(Encoding.default_external)
+ end
+ end
end
let(:path) { __FILE__ }
diff --git a/spec/lib/gitlab/ci/variables/builder/pipeline_spec.rb b/spec/lib/gitlab/ci/variables/builder/pipeline_spec.rb
index 0880c556523..860a1fd30bd 100644
--- a/spec/lib/gitlab/ci/variables/builder/pipeline_spec.rb
+++ b/spec/lib/gitlab/ci/variables/builder/pipeline_spec.rb
@@ -108,12 +108,17 @@ RSpec.describe Gitlab::Ci::Variables::Builder::Pipeline, feature_category: :secr
'CI_MERGE_REQUEST_SOURCE_PROJECT_URL' => merge_request.source_project.web_url,
'CI_MERGE_REQUEST_SOURCE_BRANCH_NAME' => merge_request.source_branch.to_s,
'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA' => '',
+ 'CI_MERGE_REQUEST_SOURCE_BRANCH_PROTECTED' => ProtectedBranch.protected?(
+ merge_request.source_project,
+ merge_request.source_branch
+ ).to_s,
'CI_MERGE_REQUEST_TITLE' => merge_request.title,
'CI_MERGE_REQUEST_ASSIGNEES' => merge_request.assignee_username_list,
'CI_MERGE_REQUEST_MILESTONE' => milestone.title,
'CI_MERGE_REQUEST_LABELS' => labels.map(&:title).sort.join(','),
'CI_MERGE_REQUEST_EVENT_TYPE' => 'detached',
- 'CI_OPEN_MERGE_REQUESTS' => merge_request.to_reference(full: true))
+ 'CI_OPEN_MERGE_REQUESTS' => merge_request.to_reference(full: true)),
+ 'CI_MERGE_REQUEST_SQUASH_ON_MERGE' => merge_request.squash_on_merge?.to_s
end
it 'exposes diff variables' do
diff --git a/spec/lib/gitlab/ci/variables/builder_spec.rb b/spec/lib/gitlab/ci/variables/builder_spec.rb
index 3411426fcdb..af745c75f42 100644
--- a/spec/lib/gitlab/ci/variables/builder_spec.rb
+++ b/spec/lib/gitlab/ci/variables/builder_spec.rb
@@ -10,18 +10,27 @@ RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache, featur
let_it_be(:user) { create(:user) }
let_it_be_with_reload(:job) do
create(:ci_build,
+ :with_deployment,
name: 'rspec:test 1',
pipeline: pipeline,
user: user,
yaml_variables: [{ key: 'YAML_VARIABLE', value: 'value' }],
- environment: 'test'
+ environment: 'review/$CI_COMMIT_REF_NAME',
+ options: {
+ environment: {
+ name: 'review/$CI_COMMIT_REF_NAME',
+ action: 'prepare',
+ deployment_tier: 'testing',
+ url: 'https://gitlab.com'
+ }
+ }
)
end
let(:builder) { described_class.new(pipeline) }
describe '#scoped_variables' do
- let(:environment) { job.expanded_environment_name }
+ let(:environment_name) { job.expanded_environment_name }
let(:dependencies) { true }
let(:predefined_variables) do
[
@@ -34,7 +43,13 @@ RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache, featur
{ key: 'CI_NODE_TOTAL',
value: '1' },
{ key: 'CI_ENVIRONMENT_NAME',
- value: 'test' },
+ value: 'review/master' },
+ { key: 'CI_ENVIRONMENT_ACTION',
+ value: 'prepare' },
+ { key: 'CI_ENVIRONMENT_TIER',
+ value: 'testing' },
+ { key: 'CI_ENVIRONMENT_URL',
+ value: 'https://gitlab.com' },
{ key: 'CI',
value: 'true' },
{ key: 'GITLAB_CI',
@@ -150,7 +165,7 @@ RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache, featur
].map { |var| var.merge(public: true, masked: false) }
end
- subject { builder.scoped_variables(job, environment: environment, dependencies: dependencies) }
+ subject { builder.scoped_variables(job, environment: environment_name, dependencies: dependencies) }
it { is_expected.to be_instance_of(Gitlab::Ci::Variables::Collection) }
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index f8f1d71e773..c09c0b31e97 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -794,6 +794,28 @@ module Gitlab
it_behaves_like 'returns errors', 'test_job_1 has the following needs duplicated: test_job_2.'
end
+
+ context 'when needed job name is too long' do
+ let(:job_name) { 'a' * (::Ci::BuildNeed::MAX_JOB_NAME_LENGTH + 1) }
+
+ let(:config) do
+ <<-EOYML
+ lint_job:
+ script: 'echo lint_job'
+ rules:
+ - if: $var == null
+ needs: [#{job_name}]
+ #{job_name}:
+ script: 'echo job'
+ EOYML
+ end
+
+ it 'returns an error' do
+ expect(subject.errors).to include(
+ "lint_job job: need `#{job_name}` name is too long (maximum is #{::Ci::BuildNeed::MAX_JOB_NAME_LENGTH} characters)"
+ )
+ end
+ end
end
context 'rule needs as hash' do
@@ -2020,6 +2042,52 @@ module Gitlab
end
end
+ describe 'id_tokens' do
+ subject(:execute) { described_class.new(config).execute }
+
+ let(:build) { execute.builds.first }
+ let(:id_tokens_vars) { { ID_TOKEN_1: { aud: 'http://gcp.com' } } }
+ let(:job_id_tokens_vars) { { ID_TOKEN_2: { aud: 'http://job.com' } } }
+
+ context 'when defined on job level' do
+ let(:config) do
+ YAML.dump({
+ rspec: { script: 'rspec', id_tokens: id_tokens_vars }
+ })
+ end
+
+ it 'returns defined id_tokens' do
+ expect(build[:id_tokens]).to eq(id_tokens_vars)
+ end
+ end
+
+ context 'when defined as default' do
+ let(:config) do
+ YAML.dump({
+ default: { id_tokens: id_tokens_vars },
+ rspec: { script: 'rspec' }
+ })
+ end
+
+ it 'returns inherited by default id_tokens' do
+ expect(build[:id_tokens]).to eq(id_tokens_vars)
+ end
+ end
+
+ context 'when defined as default and on job level' do
+ let(:config) do
+ YAML.dump({
+ default: { id_tokens: id_tokens_vars },
+ rspec: { script: 'rspec', id_tokens: job_id_tokens_vars }
+ })
+ end
+
+ it 'overrides default and returns defined on job level' do
+ expect(build[:id_tokens]).to eq(job_id_tokens_vars)
+ end
+ end
+ end
+
describe "Artifacts" do
it "returns artifacts when defined" do
config = YAML.dump(
@@ -2553,6 +2621,60 @@ module Gitlab
scheduling_type: :dag
)
end
+
+ context 'when expanded job name is too long' do
+ let(:parallel_job_name) { 'a' * ::Ci::BuildNeed::MAX_JOB_NAME_LENGTH }
+ let(:needs) { [parallel_job_name] }
+
+ before do
+ config[parallel_job_name] = { stage: 'build', script: 'test', parallel: 1 }
+ end
+
+ it 'returns an error' do
+ expect(subject.errors).to include(
+ "test1 job: need `#{parallel_job_name} 1/1` name is too long (maximum is #{::Ci::BuildNeed::MAX_JOB_NAME_LENGTH} characters)"
+ )
+ end
+ end
+
+ context 'when parallel job has matrix specified' do
+ let(:var1) { '1' }
+ let(:var2) { '2' }
+
+ before do
+ config[:parallel] = { stage: 'build', script: 'test', parallel: { matrix: [{ VAR1: var1, VAR2: var2 }] } }
+ end
+
+ it 'does create jobs with valid specification' do
+ expect(subject.builds.size).to eq(6)
+ expect(subject.builds[3]).to eq(
+ stage: 'test',
+ stage_idx: 2,
+ name: 'test1',
+ only: { refs: %w[branches tags] },
+ options: { script: ['test'] },
+ needs_attributes: [
+ { name: 'parallel: [1, 2]', artifacts: true, optional: false }
+ ],
+ when: "on_success",
+ allow_failure: false,
+ job_variables: [],
+ root_variables_inheritance: true,
+ scheduling_type: :dag
+ )
+ end
+
+ context 'when expanded job name is too long' do
+ let(:var1) { '1' * (::Ci::BuildNeed::MAX_JOB_NAME_LENGTH / 2) }
+ let(:var2) { '2' * (::Ci::BuildNeed::MAX_JOB_NAME_LENGTH / 2) }
+
+ it 'returns an error' do
+ expect(subject.errors).to include(
+ "test1 job: need `parallel: [#{var1}, #{var2}]` name is too long (maximum is #{::Ci::BuildNeed::MAX_JOB_NAME_LENGTH} characters)"
+ )
+ end
+ end
+ end
end
context 'needs dependencies artifacts' do