summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/ci
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib/gitlab/ci')
-rw-r--r--spec/lib/gitlab/ci/badge/release/latest_release_spec.rb42
-rw-r--r--spec/lib/gitlab/ci/badge/release/metadata_spec.rb40
-rw-r--r--spec/lib/gitlab/ci/badge/release/template_spec.rb90
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/expire_in_parser_spec.rb21
-rw-r--r--spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb19
-rw-r--r--spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/config/entry/include/rules_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/config/entry/include_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/config/entry/jobs_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/config/entry/policy_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/config/entry/root_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/config/entry/script_spec.rb109
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper_spec.rb32
-rw-r--r--spec/lib/gitlab/ci/config/external/rules_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/config_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/lint_spec.rb124
-rw-r--r--spec/lib/gitlab/ci/parsers/security/common_spec.rb148
-rw-r--r--spec/lib/gitlab/ci/parsers/test/junit_spec.rb26
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/ensure_resource_groups_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/pipeline/logger_spec.rb39
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build_spec.rb165
-rw-r--r--spec/lib/gitlab/ci/reports/codequality_reports_spec.rb28
-rw-r--r--spec/lib/gitlab/ci/reports/security/finding_key_spec.rb61
-rw-r--r--spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb7
-rw-r--r--spec/lib/gitlab/ci/templates/Jobs/sast_iac_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/Terraform/base_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/Terraform/base_latest_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/templates/flutter_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/kaniko_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/npm_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/terraform_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/variables/builder/instance_spec.rb39
-rw-r--r--spec/lib/gitlab/ci/variables/builder/project_spec.rb149
-rw-r--r--spec/lib/gitlab/ci/variables/builder_spec.rb247
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb58
46 files changed, 1090 insertions, 469 deletions
diff --git a/spec/lib/gitlab/ci/badge/release/latest_release_spec.rb b/spec/lib/gitlab/ci/badge/release/latest_release_spec.rb
new file mode 100644
index 00000000000..36f9f4fb321
--- /dev/null
+++ b/spec/lib/gitlab/ci/badge/release/latest_release_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Badge::Release::LatestRelease do
+ let(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
+
+ before do
+ project.add_guest(user)
+ create(:release, project: project, released_at: 1.day.ago)
+ end
+
+ subject { described_class.new(project, user) }
+
+ describe '#entity' do
+ it 'describes latest release' do
+ expect(subject.entity).to eq 'Latest Release'
+ end
+ end
+
+ describe '#tag' do
+ it 'returns latest release tag for the project ordered using release_at' do
+ create(:release, tag: "v1.0.0", project: project, released_at: 1.hour.ago)
+ latest_release = create(:release, tag: "v2.0.0", project: project, released_at: Time.current)
+
+ expect(subject.tag).to eq latest_release.tag
+ end
+ end
+
+ describe '#metadata' do
+ it 'returns correct metadata' do
+ expect(subject.metadata.image_url).to include 'release.svg'
+ end
+ end
+
+ describe '#template' do
+ it 'returns correct template' do
+ expect(subject.template.key_text).to eq 'Latest Release'
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/badge/release/metadata_spec.rb b/spec/lib/gitlab/ci/badge/release/metadata_spec.rb
new file mode 100644
index 00000000000..d68358f1458
--- /dev/null
+++ b/spec/lib/gitlab/ci/badge/release/metadata_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'lib/gitlab/ci/badge/shared/metadata'
+
+RSpec.describe Gitlab::Ci::Badge::Release::Metadata do
+ let(:project) { create(:project) }
+ let(:ref) { 'feature' }
+ let!(:release) { create(:release, tag: ref, project: project) }
+ let(:user) { create(:user) }
+ let(:badge) do
+ Gitlab::Ci::Badge::Release::LatestRelease.new(project, user)
+ end
+
+ let(:metadata) { described_class.new(badge) }
+
+ before do
+ project.add_guest(user)
+ end
+
+ it_behaves_like 'badge metadata'
+
+ describe '#title' do
+ it 'returns latest release title' do
+ expect(metadata.title).to eq 'Latest Release'
+ end
+ end
+
+ describe '#image_url' do
+ it 'returns valid url' do
+ expect(metadata.image_url).to include "/-/badges/release.svg"
+ end
+ end
+
+ describe '#link_url' do
+ it 'returns valid link' do
+ expect(metadata.link_url).to include "/-/releases"
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/badge/release/template_spec.rb b/spec/lib/gitlab/ci/badge/release/template_spec.rb
new file mode 100644
index 00000000000..2b66c296a94
--- /dev/null
+++ b/spec/lib/gitlab/ci/badge/release/template_spec.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Badge::Release::Template do
+ let(:project) { create(:project) }
+ let(:ref) { 'v1.2.3' }
+ let(:user) { create(:user) }
+ let!(:release) { create(:release, tag: ref, project: project) }
+ let(:badge) { Gitlab::Ci::Badge::Release::LatestRelease.new(project, user) }
+ let(:template) { described_class.new(badge) }
+
+ before do
+ project.add_guest(user)
+ end
+
+ describe '#key_text' do
+ it 'defaults to latest release' do
+ expect(template.key_text).to eq 'Latest Release'
+ end
+
+ it 'returns custom key text' do
+ key_text = 'Test Release'
+ badge = Gitlab::Ci::Badge::Release::LatestRelease.new(project, user, opts: { key_text: key_text })
+
+ expect(described_class.new(badge).key_text).to eq key_text
+ end
+ end
+
+ describe '#value_text' do
+ context 'when a release exists' do
+ it 'returns the tag of the release' do
+ expect(template.value_text).to eq ref
+ end
+ end
+
+ context 'no releases exist' do
+ before do
+ allow(badge).to receive(:tag).and_return(nil)
+ end
+
+ it 'returns string that latest release is none' do
+ expect(template.value_text).to eq 'none'
+ end
+ end
+ end
+
+ describe '#key_width' do
+ it 'returns the default key width' do
+ expect(template.key_width).to eq 90
+ end
+
+ it 'returns custom key width' do
+ key_width = 100
+ badge = Gitlab::Ci::Badge::Release::LatestRelease.new(project, user, opts: { key_width: key_width })
+
+ expect(described_class.new(badge).key_width).to eq key_width
+ end
+ end
+
+ describe '#value_width' do
+ it 'has a fixed value width' do
+ expect(template.value_width).to eq 54
+ end
+ end
+
+ describe '#key_color' do
+ it 'always has the same color' do
+ expect(template.key_color).to eq '#555'
+ end
+ end
+
+ describe '#value_color' do
+ context 'when release exists' do
+ it 'is blue' do
+ expect(template.value_color).to eq '#3076af'
+ end
+ end
+
+ context 'when release does not exist' do
+ before do
+ allow(badge).to receive(:tag).and_return(nil)
+ end
+
+ it 'is red' do
+ expect(template.value_color).to eq '#e05d44'
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/artifacts/expire_in_parser_spec.rb b/spec/lib/gitlab/ci/build/artifacts/expire_in_parser_spec.rb
index 0e26a9fa571..889878cf3ef 100644
--- a/spec/lib/gitlab/ci/build/artifacts/expire_in_parser_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/expire_in_parser_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Build::Artifacts::ExpireInParser do
- describe '.validate_duration' do
+ describe '.validate_duration', :request_store do
subject { described_class.validate_duration(value) }
context 'with never' do
@@ -20,14 +20,33 @@ RSpec.describe Gitlab::Ci::Build::Artifacts::ExpireInParser do
context 'with a duration' do
let(:value) { '1 Day' }
+ let(:other_value) { '30 seconds' }
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
+
+ 2.times do
+ expect(described_class.validate_duration(value)).to eq(86400)
+ expect(described_class.validate_duration(other_value)).to eq(30)
+ end
+ end
end
context 'without a duration' do
let(:value) { 'something' }
it { is_expected.to be_falsy }
+
+ it 'caches data' do
+ expect(ChronicDuration).to receive(:parse).with(value).once.and_call_original
+
+ 2.times do
+ expect(described_class.validate_duration(value)).to be_falsey
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb b/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb
index 532c83f6768..4ac8bf61738 100644
--- a/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb
+++ b/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb
@@ -4,14 +4,23 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do
describe '#satisfied_by?' do
+ subject { described_class.new(globs).satisfied_by?(pipeline, context) }
+
it_behaves_like 'a glob matching rule' do
let(:pipeline) { build(:ci_pipeline) }
+ let(:context) {}
before do
allow(pipeline).to receive(:modified_paths).and_return(files.keys)
end
+ end
- subject { described_class.new(globs).satisfied_by?(pipeline, nil) }
+ context 'when pipeline is nil' do
+ let(:pipeline) {}
+ let(:context) {}
+ let(:globs) { [] }
+
+ it { is_expected.to be_truthy }
end
context 'when using variable expansion' do
@@ -20,8 +29,6 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do
let(:globs) { ['$HELM_DIR/**/*'] }
let(:context) { double('context') }
- subject { described_class.new(globs).satisfied_by?(pipeline, context) }
-
before do
allow(pipeline).to receive(:modified_paths).and_return(modified_paths)
end
@@ -32,6 +39,12 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do
it { is_expected.to be_falsey }
end
+ context 'when modified paths are nil' do
+ let(:modified_paths) {}
+
+ it { is_expected.to be_truthy }
+ end
+
context 'when context has the specified variables' do
let(:variables_hash) do
{ 'HELM_DIR' => 'helm' }
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 0505b17ea91..e83d4974bb7 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,7 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require_dependency 'active_model'
RSpec.describe Gitlab::Ci::Config::Entry::Include::Rules::Rule do
let(:factory) do
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 c255d6e9dd6..d5988dbbb58 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,7 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require_dependency 'active_model'
RSpec.describe Gitlab::Ci::Config::Entry::Include::Rules do
let(:factory) do
diff --git a/spec/lib/gitlab/ci/config/entry/include_spec.rb b/spec/lib/gitlab/ci/config/entry/include_spec.rb
index 275cdcddeb0..fd7f85c9298 100644
--- a/spec/lib/gitlab/ci/config/entry/include_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/include_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require_dependency 'active_model'
RSpec.describe ::Gitlab::Ci::Config::Entry::Include do
subject(:include_entry) { described_class.new(config) }
diff --git a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
index 9a2a67389fc..b03175cd80f 100644
--- a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
@@ -70,6 +70,14 @@ RSpec.describe Gitlab::Ci::Config::Entry::Jobs do
it 'reports error' do
expect(entry.errors).to include 'jobs rspec config should implement a script: or a trigger: keyword'
end
+
+ context 'when the job name cannot be cast directly to a symbol' do
+ let(:config) { { true => nil } }
+
+ it 'properly parses the job name without raising a NoMethodError' do
+ expect(entry.errors).to include 'jobs true config should implement a script: or a trigger: keyword'
+ end
+ end
end
context 'when no visible jobs present' do
diff --git a/spec/lib/gitlab/ci/config/entry/policy_spec.rb b/spec/lib/gitlab/ci/config/entry/policy_spec.rb
index 46800055dd9..e5de0fb38e3 100644
--- a/spec/lib/gitlab/ci/config/entry/policy_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/policy_spec.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
-require 'support/helpers/stub_feature_flags'
-require_dependency 'active_model'
+require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::Entry::Policy do
let(:entry) { described_class.new(config) }
@@ -47,6 +45,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Policy do
end
context 'when using unsafe regexp' do
+ # When removed we could use `require 'fast_spec_helper'` again.
include StubFeatureFlags
let(:config) { ['/^(?!master).+/'] }
@@ -89,7 +88,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Policy do
describe '#errors' do
it 'saves errors' do
expect(entry.errors)
- .to include /policy config should be an array of strings or regexps/
+ .to include /policy config should be an array of strings or regular expressions/
end
end
end
@@ -107,6 +106,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Policy do
end
context 'when using unsafe regexp' do
+ # When removed we could use `require 'fast_spec_helper'` again.
include StubFeatureFlags
let(:config) { { refs: ['/^(?!master).+/'] } }
diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb
index 749d1386ed9..daf58aff116 100644
--- a/spec/lib/gitlab/ci/config/entry/root_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb
@@ -55,13 +55,13 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
}
end
- context 'when deprecated types keyword is defined' do
+ 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' } }
+ rspec: { script: 'rspec', type: 'test' } }
end
before do
@@ -69,11 +69,15 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
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."])
+ 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 types keyword' do
+ it 'logs usage of keywords' do
expect(Gitlab::AppJsonLogger).to(
receive(:info)
.with(event: 'ci_used_deprecated_keyword',
@@ -350,9 +354,9 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
root.compose!
end
- context 'when before script is not an array' do
+ context 'when before script is a number' do
let(:hash) do
- { before_script: 'ls' }
+ { before_script: 123 }
end
describe '#valid?' do
@@ -364,7 +368,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
describe '#errors' do
it 'reports errors from child nodes' do
expect(root.errors)
- .to include 'before_script config should be an array containing strings and arrays of strings'
+ .to include 'before_script config should be a string or a nested array of strings up to 10 levels deep'
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/script_spec.rb b/spec/lib/gitlab/ci/config/entry/script_spec.rb
deleted file mode 100644
index 1ddf7881e81..00000000000
--- a/spec/lib/gitlab/ci/config/entry/script_spec.rb
+++ /dev/null
@@ -1,109 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Ci::Config::Entry::Script do
- let(:entry) { described_class.new(config) }
-
- describe 'validations' do
- context 'when entry config value is array of strings' do
- let(:config) { %w(ls pwd) }
-
- describe '#value' do
- it 'returns array of strings' do
- expect(entry.value).to eq config
- end
- end
-
- describe '#errors' do
- it 'does not append errors' do
- expect(entry.errors).to be_empty
- end
- end
-
- describe '#valid?' do
- it 'is valid' do
- expect(entry).to be_valid
- end
- end
- end
-
- context 'when entry config value is array of arrays of strings' do
- let(:config) { [['ls'], ['pwd', 'echo 1']] }
-
- describe '#value' do
- it 'returns array of strings' do
- expect(entry.value).to eq ['ls', 'pwd', 'echo 1']
- end
- end
-
- describe '#errors' do
- it 'does not append errors' do
- expect(entry.errors).to be_empty
- end
- end
-
- describe '#valid?' do
- it 'is valid' do
- expect(entry).to be_valid
- end
- end
- end
-
- context 'when entry config value is array containing strings and arrays of strings' do
- let(:config) { ['ls', ['pwd', 'echo 1']] }
-
- describe '#value' do
- it 'returns array of strings' do
- expect(entry.value).to eq ['ls', 'pwd', 'echo 1']
- end
- end
-
- describe '#errors' do
- it 'does not append errors' do
- expect(entry.errors).to be_empty
- end
- end
-
- describe '#valid?' do
- it 'is valid' do
- expect(entry).to be_valid
- end
- end
- end
-
- context 'when entry value is string' do
- let(:config) { 'ls' }
-
- describe '#errors' do
- it 'saves errors' do
- expect(entry.errors)
- .to include 'script config should be an array containing strings and arrays of strings'
- end
- end
-
- describe '#valid?' do
- it 'is not valid' do
- expect(entry).not_to be_valid
- end
- end
- end
-
- context 'when entry value is multi-level nested array' do
- let(:config) { [['ls', ['echo 1']], 'pwd'] }
-
- describe '#errors' do
- it 'saves errors' do
- expect(entry.errors)
- .to include 'script config should be an array containing strings and arrays of strings'
- end
- end
-
- describe '#valid?' do
- it 'is not valid' do
- expect(entry).not_to be_valid
- end
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
index cebe8984741..f8754d7e124 100644
--- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
@@ -175,27 +175,35 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
end
end
- context "when duplicate 'include' is defined" do
+ context "when duplicate 'include's are defined" do
+ let(:values) do
+ { include: [
+ { 'local' => local_file },
+ { 'local' => local_file }
+ ],
+ image: 'ruby:2.7' }
+ end
+
+ it 'does not raise an exception' do
+ expect { subject }.not_to raise_error
+ end
+ end
+
+ context 'when passing max number of files' do
let(:values) do
{ include: [
{ 'local' => local_file },
- { 'local' => local_file }
+ { 'remote' => remote_url }
],
image: 'ruby:2.7' }
end
- it 'raises an exception' do
- expect { subject }.to raise_error(described_class::DuplicateIncludesError)
+ before do
+ stub_const("#{described_class}::MAX_INCLUDES", 2)
end
- context 'when including multiple files from a project' do
- let(:values) do
- { include: { project: project.full_path, file: [local_file, local_file] } }
- end
-
- it 'raises an exception' do
- expect { subject }.to raise_error(described_class::DuplicateIncludesError)
- end
+ it 'does not raise an exception' do
+ expect { subject }.not_to raise_error
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 091bd3b07e6..e2bb55f3854 100644
--- a/spec/lib/gitlab/ci/config/external/rules_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/rules_spec.rb
@@ -45,7 +45,7 @@ RSpec.describe Gitlab::Ci::Config::External::Rules do
let(:context) { double(project: project, sha: project.repository.tree.sha, top_level_worktree_paths: ['Dockerfile']) }
before do
- project.repository.create_file(project.owner, 'Dockerfile', "commit", message: 'test', branch_name: "master")
+ project.repository.create_file(project.first_owner, 'Dockerfile', "commit", message: 'test', branch_name: "master")
end
it { is_expected.to eq(true) }
diff --git a/spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb b/spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb
index a29471706cc..1cc8b462224 100644
--- a/spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb
+++ b/spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb
@@ -1,12 +1,8 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'support/helpers/stubbed_feature'
-require 'support/helpers/stub_feature_flags'
RSpec.describe Gitlab::Ci::Config::Normalizer::MatrixStrategy do
- include StubFeatureFlags
-
describe '.applies_to?' do
subject { described_class.applies_to?(config) }
diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb
index 1b3e8a2ce4a..05ff1f3618b 100644
--- a/spec/lib/gitlab/ci/config_spec.rb
+++ b/spec/lib/gitlab/ci/config_spec.rb
@@ -462,7 +462,7 @@ RSpec.describe Gitlab::Ci::Config do
expect(project.repository).to receive(:blob_data_at)
.with('eeff1122', local_location)
- described_class.new(gitlab_ci_yml, project: project, sha: 'eeff1122', user: user)
+ described_class.new(gitlab_ci_yml, project: project, sha: 'eeff1122', user: user, pipeline: pipeline)
end
end
@@ -470,7 +470,7 @@ RSpec.describe Gitlab::Ci::Config do
it 'is using latest SHA on the default branch' do
expect(project.repository).to receive(:root_ref_sha)
- described_class.new(gitlab_ci_yml, project: project, sha: nil, user: user)
+ described_class.new(gitlab_ci_yml, project: project, sha: nil, user: user, pipeline: pipeline)
end
end
end
diff --git a/spec/lib/gitlab/ci/lint_spec.rb b/spec/lib/gitlab/ci/lint_spec.rb
index 1e433d7854a..747ff13c840 100644
--- a/spec/lib/gitlab/ci/lint_spec.rb
+++ b/spec/lib/gitlab/ci/lint_spec.rb
@@ -7,9 +7,10 @@ RSpec.describe Gitlab::Ci::Lint do
let_it_be(:user) { create(:user) }
let(:lint) { described_class.new(project: project, current_user: user) }
+ let(:ref) { project.default_branch }
describe '#validate' do
- subject { lint.validate(content, dry_run: dry_run) }
+ subject { lint.validate(content, dry_run: dry_run, ref: ref) }
shared_examples 'content is valid' do
let(:content) do
@@ -251,6 +252,29 @@ RSpec.describe Gitlab::Ci::Lint do
end
end
+ context 'when using a ref other than the default branch' do
+ let(:ref) { 'feature' }
+ let(:content) do
+ <<~YAML
+ build:
+ stage: build
+ script: echo 1
+ rules:
+ - if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"
+ test:
+ stage: test
+ script: echo 2
+ rules:
+ - if: "$CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH"
+ YAML
+ end
+
+ it 'includes only jobs that are excluded on the default branch' do
+ expect(subject.jobs.size).to eq(1)
+ expect(subject.jobs[0][:name]).to eq('test')
+ end
+ end
+
it_behaves_like 'sets merged yaml'
include_context 'advanced validations' do
@@ -298,4 +322,102 @@ RSpec.describe Gitlab::Ci::Lint do
end
end
end
+
+ context 'pipeline logger' do
+ let(:counters) do
+ {
+ 'count' => a_kind_of(Numeric),
+ 'avg' => a_kind_of(Numeric),
+ 'max' => a_kind_of(Numeric),
+ 'min' => a_kind_of(Numeric)
+ }
+ end
+
+ let(:loggable_data) do
+ {
+ 'class' => 'Gitlab::Ci::Pipeline::Logger',
+ 'config_build_context_duration_s' => counters,
+ 'config_build_variables_duration_s' => counters,
+ 'config_compose_duration_s' => counters,
+ 'config_expand_duration_s' => counters,
+ 'config_external_process_duration_s' => counters,
+ 'config_stages_inject_duration_s' => counters,
+ 'config_tags_resolve_duration_s' => counters,
+ 'config_yaml_extend_duration_s' => counters,
+ 'config_yaml_load_duration_s' => counters,
+ 'pipeline_creation_caller' => 'Gitlab::Ci::Lint',
+ 'pipeline_creation_service_duration_s' => a_kind_of(Numeric),
+ 'pipeline_persisted' => false,
+ 'pipeline_source' => 'unknown',
+ 'project_id' => project&.id,
+ 'yaml_process_duration_s' => counters
+ }
+ end
+
+ let(:content) do
+ <<~YAML
+ build:
+ script: echo
+ YAML
+ end
+
+ subject(:validate) { lint.validate(content, dry_run: false) }
+
+ before do
+ project&.add_developer(user)
+ end
+
+ context 'when the duration is under the threshold' do
+ it 'does not create a log entry' do
+ expect(Gitlab::AppJsonLogger).not_to receive(:info)
+
+ validate
+ end
+ end
+
+ context 'when the durations exceeds the threshold' do
+ let(:timer) do
+ proc do
+ @timer = @timer.to_i + 30
+ end
+ end
+
+ before do
+ allow(Gitlab::Ci::Pipeline::Logger)
+ .to receive(:current_monotonic_time) { timer.call }
+ end
+
+ it 'creates a log entry' do
+ expect(Gitlab::AppJsonLogger).to receive(:info).with(loggable_data)
+
+ validate
+ end
+
+ context 'when the feature flag is disabled' do
+ before do
+ stub_feature_flags(ci_pipeline_creation_logger: false)
+ end
+
+ it 'does not create a log entry' do
+ expect(Gitlab::AppJsonLogger).not_to receive(:info)
+
+ validate
+ end
+ end
+
+ context 'when project is not provided' do
+ let(:project) { nil }
+
+ let(:project_nil_loggable_data) do
+ loggable_data.except('project_id')
+ end
+
+ it 'creates a log entry without project_id' do
+ expect(Gitlab::AppJsonLogger).to receive(:info).with(project_nil_loggable_data)
+
+ validate
+ end
+ 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 c49673f5a4a..7eec78ff186 100644
--- a/spec/lib/gitlab/ci/parsers/security/common_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/security/common_spec.rb
@@ -40,60 +40,142 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
allow(validator_class).to receive(:new).and_call_original
end
- context 'when the validate flag is set as `false`' do
- let(:validate) { false }
+ context 'when enforce_security_report_validation is enabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: true)
+ end
- it 'does not run the validation logic' do
- parse_report
+ context 'when the validate flag is set as `true`' do
+ let(:validate) { true }
- expect(validator_class).not_to have_received(:new)
- end
- end
+ it 'instantiates the validator with correct params' do
+ parse_report
- context 'when the validate flag is set as `true`' do
- let(:validate) { true }
- let(:valid?) { false }
+ expect(validator_class).to have_received(:new).with(report.type, {})
+ end
- before do
- allow_next_instance_of(validator_class) do |instance|
- allow(instance).to receive(:valid?).and_return(valid?)
- allow(instance).to receive(:errors).and_return(['foo'])
+ context 'when the report data is valid according to the schema' do
+ let(:valid?) { true }
+
+ before do
+ allow_next_instance_of(validator_class) do |instance|
+ allow(instance).to receive(:valid?).and_return(valid?)
+ allow(instance).to receive(:errors).and_return([])
+ end
+
+ allow(parser).to receive_messages(create_scanner: true, create_scan: true)
+ end
+
+ it 'does not add errors to the report' do
+ expect { parse_report }.not_to change { report.errors }.from([])
+ end
+
+ it 'adds the schema validation status to the report' do
+ parse_report
+
+ expect(report.schema_validation_status).to eq(:valid_schema)
+ end
+
+ it 'keeps the execution flow as normal' do
+ parse_report
+
+ expect(parser).to have_received(:create_scanner)
+ expect(parser).to have_received(:create_scan)
+ end
end
- allow(parser).to receive_messages(create_scanner: true, create_scan: true)
- end
+ context 'when the report data is not valid according to the schema' do
+ let(:valid?) { false }
- it 'instantiates the validator with correct params' do
- parse_report
+ before do
+ allow_next_instance_of(validator_class) do |instance|
+ allow(instance).to receive(:valid?).and_return(valid?)
+ allow(instance).to receive(:errors).and_return(['foo'])
+ end
- expect(validator_class).to have_received(:new).with(report.type, {})
- end
+ allow(parser).to receive_messages(create_scanner: true, create_scan: true)
+ end
+
+ it 'adds errors to the report' do
+ expect { parse_report }.to change { report.errors }.from([]).to([{ message: 'foo', type: 'Schema' }])
+ end
+
+ it 'adds the schema validation status to the report' do
+ parse_report
- context 'when the report data is not valid according to the schema' do
- it 'adds errors to the report' do
- expect { parse_report }.to change { report.errors }.from([]).to([{ message: 'foo', type: 'Schema' }])
+ expect(report.schema_validation_status).to eq(:invalid_schema)
+ end
+
+ it 'does not try to create report entities' do
+ parse_report
+
+ expect(parser).not_to have_received(:create_scanner)
+ expect(parser).not_to have_received(:create_scan)
+ end
end
+ end
+ end
+
+ context 'when enforce_security_report_validation is disabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: false)
+ end
+
+ context 'when the validate flag is set as `false`' do
+ let(:validate) { false }
- it 'does not try to create report entities' do
+ it 'does not run the validation logic' do
parse_report
- expect(parser).not_to have_received(:create_scanner)
- expect(parser).not_to have_received(:create_scan)
+ expect(validator_class).not_to have_received(:new)
end
end
- context 'when the report data is valid according to the schema' do
- let(:valid?) { true }
+ context 'when the validate flag is set as `true`' do
+ let(:validate) { true }
+ let(:valid?) { false }
- it 'does not add errors to the report' do
- expect { parse_report }.not_to change { report.errors }.from([])
+ before do
+ allow_next_instance_of(validator_class) do |instance|
+ allow(instance).to receive(:valid?).and_return(valid?)
+ allow(instance).to receive(:errors).and_return(['foo'])
+ end
+
+ allow(parser).to receive_messages(create_scanner: true, create_scan: true)
end
- it 'keeps the execution flow as normal' do
+ it 'instantiates the validator with correct params' do
parse_report
- expect(parser).to have_received(:create_scanner)
- expect(parser).to have_received(:create_scan)
+ expect(validator_class).to have_received(:new).with(report.type, {})
+ end
+
+ context 'when the report data is not valid according to the schema' do
+ it 'adds errors to the report' do
+ expect { parse_report }.to change { report.errors }.from([]).to([{ message: 'foo', type: 'Schema' }])
+ end
+
+ it 'does not try to create report entities' do
+ parse_report
+
+ expect(parser).not_to have_received(:create_scanner)
+ expect(parser).not_to have_received(:create_scan)
+ end
+ end
+
+ context 'when the report data is valid according to the schema' do
+ let(:valid?) { true }
+
+ it 'does not add errors to the report' do
+ expect { parse_report }.not_to change { report.errors }.from([])
+ end
+
+ it 'keeps the execution flow as normal' do
+ parse_report
+
+ expect(parser).to have_received(:create_scanner)
+ expect(parser).to have_received(:create_scan)
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/parsers/test/junit_spec.rb b/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
index 4ca8f74e57f..82fa11d5f98 100644
--- a/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
@@ -99,6 +99,19 @@ RSpec.describe Gitlab::Ci::Parsers::Test::Junit do
'Some failure'
end
+ context 'and has failure with no message but has system-err' do
+ let(:testcase_content) do
+ <<-EOF.strip_heredoc
+ <failure></failure>
+ <system-err>Some failure</system-err>
+ EOF
+ end
+
+ it_behaves_like '<testcase> XML parser',
+ ::Gitlab::Ci::Reports::TestCase::STATUS_FAILED,
+ 'Some failure'
+ end
+
context 'and has error' do
let(:testcase_content) { '<error>Some error</error>' }
@@ -107,6 +120,19 @@ RSpec.describe Gitlab::Ci::Parsers::Test::Junit do
'Some error'
end
+ context 'and has error with no message but has system-err' do
+ let(:testcase_content) do
+ <<-EOF.strip_heredoc
+ <error></error>
+ <system-err>Some error</system-err>
+ EOF
+ end
+
+ it_behaves_like '<testcase> XML parser',
+ ::Gitlab::Ci::Reports::TestCase::STATUS_ERROR,
+ 'Some error'
+ end
+
context 'and has skipped' do
let(:testcase_content) { '<skipped/>' }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb
index 0a592395c3a..375841ce236 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb
@@ -47,18 +47,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::CreateDeployments do
expect(job.deployment).to be_nil
end
end
-
- context 'when create_deployment_in_separate_transaction feature flag is disabled' do
- before do
- stub_feature_flags(create_deployment_in_separate_transaction: false)
- end
-
- it 'does not create a deployment record' do
- expect { subject }.not_to change { Deployment.count }
-
- expect(job.deployment).to be_nil
- end
- end
end
context 'when a pipeline contains a teardown job' do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb
index 253928e1a19..6a7d9b58a05 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb
@@ -57,18 +57,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
expect(job.persisted_environment).to be_nil
end
end
-
- context 'when create_deployment_in_separate_transaction feature flag is disabled' do
- before do
- stub_feature_flags(create_deployment_in_separate_transaction: false)
- end
-
- it 'does not create any environments' do
- expect { subject }.not_to change { Environment.count }
-
- expect(job.persisted_environment).to be_nil
- end
- end
end
context 'when a pipeline contains a teardown job' do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/ensure_resource_groups_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/ensure_resource_groups_spec.rb
index 87df5a3e21b..571455d6279 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/ensure_resource_groups_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/ensure_resource_groups_spec.rb
@@ -60,18 +60,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureResourceGroups do
expect(job.resource_group).to be_nil
end
end
-
- context 'when create_deployment_in_separate_transaction feature flag is disabled' do
- before do
- stub_feature_flags(create_deployment_in_separate_transaction: false)
- end
-
- it 'does not create any resource groups' do
- expect { subject }.not_to change { Ci::ResourceGroup.count }
-
- expect(job.resource_group).to be_nil
- end
- end
end
context 'when a pipeline does not contain a job that requires a resource group' do
diff --git a/spec/lib/gitlab/ci/pipeline/logger_spec.rb b/spec/lib/gitlab/ci/pipeline/logger_spec.rb
index a488bc184f8..f31361431f2 100644
--- a/spec/lib/gitlab/ci/pipeline/logger_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/logger_spec.rb
@@ -47,13 +47,15 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
end
def loggable_data(count:, db_count: nil)
- keys = %w[
+ database_name = Ci::ApplicationRecord.connection.pool.db_config.name
+
+ keys = %W[
expensive_operation_duration_s
expensive_operation_db_count
expensive_operation_db_primary_count
expensive_operation_db_primary_duration_s
- expensive_operation_db_main_count
- expensive_operation_db_main_duration_s
+ expensive_operation_db_#{database_name}_count
+ expensive_operation_db_#{database_name}_duration_s
]
data = keys.each.with_object({}) do |key, accumulator|
@@ -75,7 +77,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
end
context 'with a single query' do
- let(:operation) { -> { Project.count } }
+ let(:operation) { -> { Ci::Pipeline.count } }
it { is_expected.to eq(operation.call) }
@@ -201,6 +203,35 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
expect(commit).to be_truthy
end
end
+
+ context 'when project is not passed and pipeline is not persisted' do
+ let(:project) {}
+ let(:pipeline) { build(:ci_pipeline) }
+
+ let(:loggable_data) do
+ {
+ 'class' => described_class.name.to_s,
+ 'pipeline_persisted' => false,
+ 'pipeline_creation_service_duration_s' => a_kind_of(Numeric),
+ 'pipeline_creation_caller' => 'source',
+ 'pipeline_save_duration_s' => {
+ 'avg' => 60, 'count' => 1, 'max' => 60, 'min' => 60
+ },
+ 'pipeline_creation_duration_s' => {
+ 'avg' => 20, 'count' => 2, 'max' => 30, 'min' => 10
+ }
+ }
+ end
+
+ it 'logs to application.json' do
+ expect(Gitlab::AppJsonLogger)
+ .to receive(:info)
+ .with(a_hash_including(loggable_data))
+ .and_call_original
+
+ expect(commit).to be_truthy
+ end
+ end
end
context 'when the feature flag is disabled' do
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
index 2f9fcd7caac..49505d397c2 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
@@ -411,171 +411,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
describe '#to_resource' do
subject { seed_build.to_resource }
- before do
- stub_feature_flags(create_deployment_in_separate_transaction: false)
- end
-
- context 'when job is Ci::Build' do
- it { is_expected.to be_a(::Ci::Build) }
- it { is_expected.to be_valid }
-
- shared_examples_for 'deployment job' do
- it 'returns a job with deployment' do
- expect { subject }.to change { Environment.count }.by(1)
-
- expect(subject.deployment).not_to be_nil
- expect(subject.deployment.deployable).to eq(subject)
- expect(subject.deployment.environment.name).to eq(expected_environment_name)
- end
- end
-
- shared_examples_for 'non-deployment job' do
- it 'returns a job without deployment' do
- expect(subject.deployment).to be_nil
- end
- end
-
- shared_examples_for 'ensures environment existence' do
- it 'has environment' do
- expect { subject }.to change { Environment.count }.by(1)
-
- expect(subject).to be_has_environment
- expect(subject.environment).to eq(environment_name)
- expect(subject.metadata.expanded_environment_name).to eq(expected_environment_name)
- expect(Environment.exists?(name: expected_environment_name)).to eq(true)
- end
- end
-
- shared_examples_for 'ensures environment inexistence' do
- it 'does not have environment' do
- expect { subject }.not_to change { Environment.count }
-
- expect(subject).not_to be_has_environment
- expect(subject.environment).to be_nil
- expect(subject.metadata&.expanded_environment_name).to be_nil
- expect(Environment.exists?(name: expected_environment_name)).to eq(false)
- end
- end
-
- context 'when job deploys to production' do
- let(:environment_name) { 'production' }
- let(:expected_environment_name) { 'production' }
- let(:attributes) { { name: 'deploy', ref: 'master', environment: 'production' } }
-
- it_behaves_like 'deployment job'
- it_behaves_like 'ensures environment existence'
-
- context 'when create_deployment_in_separate_transaction feature flag is enabled' do
- before do
- stub_feature_flags(create_deployment_in_separate_transaction: true)
- end
-
- it 'does not create any deployments nor environments' do
- expect(subject.deployment).to be_nil
- expect(Environment.count).to eq(0)
- expect(Deployment.count).to eq(0)
- end
- end
-
- context 'when the environment name is invalid' do
- let(:attributes) { { name: 'deploy', ref: 'master', environment: '!!!' } }
-
- it 'fails the job with a failure reason and does not create an environment' do
- expect(subject).to be_failed
- expect(subject).to be_environment_creation_failure
- expect(subject.metadata.expanded_environment_name).to be_nil
- expect(Environment.exists?(name: expected_environment_name)).to eq(false)
- end
- end
- end
-
- context 'when job starts a review app' do
- let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' }
- let(:expected_environment_name) { "review/#{pipeline.ref}" }
-
- let(:attributes) do
- {
- name: 'deploy', ref: 'master', environment: environment_name,
- options: { environment: { name: environment_name } }
- }
- end
-
- it_behaves_like 'deployment job'
- it_behaves_like 'ensures environment existence'
- end
-
- context 'when job stops a review app' do
- let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' }
- let(:expected_environment_name) { "review/#{pipeline.ref}" }
-
- let(:attributes) do
- {
- name: 'deploy', ref: 'master', environment: environment_name,
- options: { environment: { name: environment_name, action: 'stop' } }
- }
- end
-
- it 'returns a job without deployment' do
- expect(subject.deployment).to be_nil
- end
-
- it_behaves_like 'non-deployment job'
- it_behaves_like 'ensures environment existence'
- end
-
- context 'when job belongs to a resource group' do
- let(:resource_group) { 'iOS' }
- let(:attributes) { { name: 'rspec', ref: 'master', resource_group_key: resource_group, environment: 'production' }}
-
- it 'returns a job with resource group' do
- expect(subject.resource_group).not_to be_nil
- expect(subject.resource_group.key).to eq('iOS')
- expect(Ci::ResourceGroup.count).to eq(1)
- end
-
- context 'when create_deployment_in_separate_transaction feature flag is enabled' do
- before do
- stub_feature_flags(create_deployment_in_separate_transaction: true)
- end
-
- it 'does not create any resource groups' do
- expect(subject.resource_group).to be_nil
- expect(Ci::ResourceGroup.count).to eq(0)
- end
- end
-
- context 'when resource group has $CI_ENVIRONMENT_NAME in it' do
- let(:resource_group) { 'test/$CI_ENVIRONMENT_NAME' }
-
- it 'expands environment name' do
- expect(subject.resource_group.key).to eq('test/production')
- end
- end
- end
- end
-
- context 'when job is a bridge' do
- let(:base_attributes) do
- {
- name: 'rspec', ref: 'master', options: { trigger: 'my/project' }, scheduling_type: :stage
- }
- end
-
- let(:attributes) { base_attributes }
-
- it { is_expected.to be_a(::Ci::Bridge) }
- it { is_expected.to be_valid }
-
- context 'when job belongs to a resource group' do
- let(:attributes) { base_attributes.merge(resource_group_key: 'iOS') }
-
- it 'returns a job with resource group' do
- expect(subject.resource_group).not_to be_nil
- expect(subject.resource_group.key).to eq('iOS')
- end
- end
- end
-
it 'memoizes a resource object' do
expect(subject.object_id).to eq seed_build.to_resource.object_id
end
diff --git a/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb b/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb
index 3b0eaffc54e..f4b47893805 100644
--- a/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb
+++ b/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb
@@ -85,6 +85,9 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReports do
let(:info) { build(:codequality_degradation, :info) }
let(:major_2) { build(:codequality_degradation, :major) }
let(:critical) { build(:codequality_degradation, :critical) }
+ let(:uppercase_major) { build(:codequality_degradation, severity: 'MAJOR') }
+ let(:unknown) { build(:codequality_degradation, severity: 'unknown') }
+
let(:codequality_report) { described_class.new }
before do
@@ -94,6 +97,7 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReports do
codequality_report.add_degradation(major_2)
codequality_report.add_degradation(info)
codequality_report.add_degradation(critical)
+ codequality_report.add_degradation(unknown)
codequality_report.sort_degradations!
end
@@ -105,8 +109,30 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReports do
major,
major_2,
minor,
- info
+ info,
+ unknown
])
end
+
+ context 'with non-existence and uppercase severities' do
+ let(:other_report) { described_class.new }
+ let(:non_existent) { build(:codequality_degradation, severity: 'non-existent') }
+
+ before do
+ other_report.add_degradation(blocker)
+ other_report.add_degradation(uppercase_major)
+ other_report.add_degradation(minor)
+ other_report.add_degradation(non_existent)
+ end
+
+ it 'sorts unknown last' do
+ expect(other_report.degradations.values).to eq([
+ blocker,
+ uppercase_major,
+ minor,
+ non_existent
+ ])
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/reports/security/finding_key_spec.rb b/spec/lib/gitlab/ci/reports/security/finding_key_spec.rb
index 784c1183320..2320f011cf5 100644
--- a/spec/lib/gitlab/ci/reports/security/finding_key_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/finding_key_spec.rb
@@ -6,36 +6,47 @@ RSpec.describe Gitlab::Ci::Reports::Security::FindingKey do
using RSpec::Parameterized::TableSyntax
describe '#==' do
- where(:location_fp_1, :location_fp_2, :identifier_fp_1, :identifier_fp_2, :equals?) do
- nil | 'different location fp' | 'identifier fp' | 'different identifier fp' | false
- 'location fp' | nil | 'identifier fp' | 'different identifier fp' | false
- 'location fp' | 'different location fp' | nil | 'different identifier fp' | false
- 'location fp' | 'different location fp' | 'identifier fp' | nil | false
- nil | nil | 'identifier fp' | 'identifier fp' | false
- 'location fp' | 'location fp' | nil | nil | false
- nil | nil | nil | nil | false
- 'location fp' | 'different location fp' | 'identifier fp' | 'different identifier fp' | false
- 'location fp' | 'different location fp' | 'identifier fp' | 'identifier fp' | false
- 'location fp' | 'location fp' | 'identifier fp' | 'different identifier fp' | false
- 'location fp' | 'location fp' | 'identifier fp' | 'identifier fp' | true
- end
-
- with_them do
- let(:finding_key_1) do
- build(:ci_reports_security_finding_key,
- location_fingerprint: location_fp_1,
- identifier_fingerprint: identifier_fp_1)
+ context 'when the comparison is done between FindingKey instances' do
+ where(:location_fp_1, :location_fp_2, :identifier_fp_1, :identifier_fp_2, :equals?) do
+ nil | 'different location fp' | 'identifier fp' | 'different identifier fp' | false
+ 'location fp' | nil | 'identifier fp' | 'different identifier fp' | false
+ 'location fp' | 'different location fp' | nil | 'different identifier fp' | false
+ 'location fp' | 'different location fp' | 'identifier fp' | nil | false
+ nil | nil | 'identifier fp' | 'identifier fp' | false
+ 'location fp' | 'location fp' | nil | nil | false
+ nil | nil | nil | nil | false
+ 'location fp' | 'different location fp' | 'identifier fp' | 'different identifier fp' | false
+ 'location fp' | 'different location fp' | 'identifier fp' | 'identifier fp' | false
+ 'location fp' | 'location fp' | 'identifier fp' | 'different identifier fp' | false
+ 'location fp' | 'location fp' | 'identifier fp' | 'identifier fp' | true
end
- let(:finding_key_2) do
- build(:ci_reports_security_finding_key,
- location_fingerprint: location_fp_2,
- identifier_fingerprint: identifier_fp_2)
+ with_them do
+ let(:finding_key_1) do
+ build(:ci_reports_security_finding_key,
+ location_fingerprint: location_fp_1,
+ identifier_fingerprint: identifier_fp_1)
+ end
+
+ let(:finding_key_2) do
+ build(:ci_reports_security_finding_key,
+ location_fingerprint: location_fp_2,
+ identifier_fingerprint: identifier_fp_2)
+ end
+
+ subject { finding_key_1 == finding_key_2 }
+
+ it { is_expected.to be(equals?) }
end
+ end
+
+ context 'when the comparison is not done between FindingKey instances' do
+ let(:finding_key) { build(:ci_reports_security_finding_key) }
+ let(:uuid) { SecureRandom.uuid }
- subject { finding_key_1 == finding_key_2 }
+ subject { finding_key == uuid }
- it { is_expected.to be(equals?) }
+ it { is_expected.to be_falsey }
end
end
end
diff --git a/spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb
index f8df2266689..8204b104832 100644
--- a/spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe '5-Minute-Production-App.gitlab-ci.yml' do
describe 'the created pipeline' do
let_it_be(:project) { create(:project, :auto_devops, :custom_repo, files: { 'README.md' => '' }) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:default_branch) { 'master' }
let(:pipeline_branch) { default_branch }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
diff --git a/spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb
index ca6f6872f89..27de8324206 100644
--- a/spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'Deploy-ECS.gitlab-ci.yml' do
let(:default_branch) { project.default_branch_or_main }
let(:pipeline_branch) { default_branch }
let(:project) { create(:project, :auto_devops, :custom_repo, files: { 'README.md' => '' }) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
let(:pipeline) { service.execute!(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
diff --git a/spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb
index bd701aec8fc..21052f03cb8 100644
--- a/spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe 'Jobs/Build.gitlab-ci.yml' do
describe 'the created pipeline' do
let_it_be(:project) { create(:project, :repository) }
- let_it_be(:user) { project.owner }
+ let_it_be(:user) { project.first_owner }
let(:default_branch) { 'master' }
let(:pipeline_ref) { default_branch }
diff --git a/spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb
index 64243f2d205..d88d9782021 100644
--- a/spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe 'Jobs/Code-Quality.gitlab-ci.yml' do
describe 'the created pipeline' do
let_it_be(:project) { create(:project, :repository) }
- let_it_be(:user) { project.owner }
+ let_it_be(:user) { project.first_owner }
let(:default_branch) { 'master' }
let(:pipeline_ref) { default_branch }
diff --git a/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb
index 789f694b4b4..b657f73fa77 100644
--- a/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe 'Jobs/Deploy.gitlab-ci.yml' do
describe 'the created pipeline' do
let_it_be(:project, refind: true) { create(:project, :repository) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:default_branch) { 'master' }
let(:pipeline_ref) { default_branch }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
@@ -66,6 +66,11 @@ RSpec.describe 'Jobs/Deploy.gitlab-ci.yml' do
expect(build_names).not_to include('review')
end
+ it 'when CI_DEPLOY_FREEZE is present' do
+ create(:ci_variable, project: project, key: 'CI_DEPLOY_FREEZE', value: 'true')
+ expect(build_names).to eq %w(placeholder)
+ end
+
it 'when CANARY_ENABLED' do
create(:ci_variable, project: project, key: 'CANARY_ENABLED', value: 'true')
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 b9256ece78b..0f97bc06a4e 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
@@ -7,7 +7,7 @@ RSpec.describe 'Jobs/SAST-IaC.latest.gitlab-ci.yml' do
describe 'the created pipeline' do
let_it_be(:project) { create(:project, :repository) }
- let_it_be(:user) { project.owner }
+ let_it_be(:user) { project.first_owner }
let(:default_branch) { 'main' }
let(:pipeline_ref) { default_branch }
diff --git a/spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb
index db9d7496251..a92a8397e96 100644
--- a/spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe 'Jobs/Test.gitlab-ci.yml' do
describe 'the created pipeline' do
let_it_be(:project) { create(:project, :repository) }
- let_it_be(:user) { project.owner }
+ let_it_be(:user) { project.first_owner }
let(:default_branch) { 'master' }
let(:pipeline_ref) { default_branch }
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 4685d843ce0..5e9224cebd9 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
@@ -9,7 +9,7 @@ RSpec.describe 'Terraform/Base.gitlab-ci.yml' do
let(:default_branch) { 'master' }
let(:pipeline_branch) { default_branch }
let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
let(:pipeline) { service.execute!(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
diff --git a/spec/lib/gitlab/ci/templates/Terraform/base_latest_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Terraform/base_latest_gitlab_ci_yaml_spec.rb
index e35f2eabe8e..0ab81f97f20 100644
--- a/spec/lib/gitlab/ci/templates/Terraform/base_latest_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/Terraform/base_latest_gitlab_ci_yaml_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'Terraform/Base.latest.gitlab-ci.yml' do
let(:default_branch) { 'master' }
let(:pipeline_branch) { default_branch }
let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
let(:pipeline) { service.execute!(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
diff --git a/spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb
index 004261bc617..d6c7cd32f79 100644
--- a/spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe 'Verify/Load-Performance-Testing.gitlab-ci.yml' do
describe 'the created pipeline' do
let(:project) { create(:project, :repository) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:default_branch) { 'master' }
let(:pipeline_ref) { default_branch }
diff --git a/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb
index 64ef6ecd7f8..6a4be1fa072 100644
--- a/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe 'Auto-DevOps.gitlab-ci.yml' do
describe 'the created pipeline' do
let(:pipeline_branch) { default_branch }
let(:project) { create(:project, :auto_devops, :custom_repo, files: { 'README.md' => '' }) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
let(:pipeline) { service.execute!(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
@@ -276,7 +276,7 @@ RSpec.describe 'Auto-DevOps.gitlab-ci.yml' do
with_them do
let(:project) { create(:project, :custom_repo, files: files) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: default_branch ) }
let(:pipeline) { service.execute(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
diff --git a/spec/lib/gitlab/ci/templates/flutter_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/flutter_gitlab_ci_yaml_spec.rb
index 3d97b47473d..de94eec09fe 100644
--- a/spec/lib/gitlab/ci/templates/flutter_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/flutter_gitlab_ci_yaml_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'Flutter.gitlab-ci.yml' do
describe 'the created pipeline' do
let(:pipeline_branch) { 'master' }
let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
let(:pipeline) { service.execute!(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
diff --git a/spec/lib/gitlab/ci/templates/kaniko_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/kaniko_gitlab_ci_yaml_spec.rb
index c7dbbea4622..ebf52e6d65a 100644
--- a/spec/lib/gitlab/ci/templates/kaniko_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/kaniko_gitlab_ci_yaml_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'Kaniko.gitlab-ci.yml' do
describe 'the created pipeline' do
let(:pipeline_branch) { 'master' }
let(:project) { create(:project, :custom_repo, files: { 'Dockerfile' => 'FROM alpine:latest' }) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
let(:pipeline) { service.execute!(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
diff --git a/spec/lib/gitlab/ci/templates/npm_spec.rb b/spec/lib/gitlab/ci/templates/npm_spec.rb
index ea954690133..d86a3a67823 100644
--- a/spec/lib/gitlab/ci/templates/npm_spec.rb
+++ b/spec/lib/gitlab/ci/templates/npm_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'npm.gitlab-ci.yml' do
let(:repo_files) { { 'package.json' => '{}', 'README.md' => '' } }
let(:modified_files) { %w[package.json] }
let(:project) { create(:project, :custom_repo, files: repo_files) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:pipeline_branch) { project.default_branch }
let(:pipeline_tag) { 'v1.2.1' }
let(:pipeline_ref) { pipeline_branch }
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 936cd6ac8aa..346ab9f7af7 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
@@ -13,7 +13,7 @@ RSpec.describe 'Terraform.gitlab-ci.yml' do
let(:default_branch) { project.default_branch_or_main }
let(:pipeline_branch) { default_branch }
let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
let(:pipeline) { service.execute!(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
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 fd5d5d6af7f..6c06403adff 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
@@ -13,7 +13,7 @@ RSpec.describe 'Terraform.latest.gitlab-ci.yml' do
let(:default_branch) { project.default_branch_or_main }
let(:pipeline_branch) { default_branch }
let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
let(:pipeline) { service.execute!(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
diff --git a/spec/lib/gitlab/ci/variables/builder/instance_spec.rb b/spec/lib/gitlab/ci/variables/builder/instance_spec.rb
new file mode 100644
index 00000000000..7abda2bd615
--- /dev/null
+++ b/spec/lib/gitlab/ci/variables/builder/instance_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Variables::Builder::Instance do
+ let_it_be(:variable) { create(:ci_instance_variable, protected: false) }
+ let_it_be(:protected_variable) { create(:ci_instance_variable, protected: true) }
+
+ let(:builder) { described_class.new }
+
+ describe '#secret_variables' do
+ let(:variable_item) { item(variable) }
+ let(:protected_variable_item) { item(protected_variable) }
+
+ subject do
+ builder.secret_variables(protected_ref: protected_ref)
+ end
+
+ context 'when the ref is protected' do
+ let(:protected_ref) { true }
+
+ it 'contains all the variables' do
+ is_expected.to contain_exactly(variable_item, protected_variable_item)
+ end
+ end
+
+ context 'when the ref is not protected' do
+ let(:protected_ref) { false }
+
+ it 'contains only unprotected variables' do
+ is_expected.to contain_exactly(variable_item)
+ end
+ end
+ end
+
+ def item(variable)
+ Gitlab::Ci::Variables::Collection::Item.fabricate(variable)
+ end
+end
diff --git a/spec/lib/gitlab/ci/variables/builder/project_spec.rb b/spec/lib/gitlab/ci/variables/builder/project_spec.rb
new file mode 100644
index 00000000000..b64b6ea98e2
--- /dev/null
+++ b/spec/lib/gitlab/ci/variables/builder/project_spec.rb
@@ -0,0 +1,149 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Variables::Builder::Project do
+ let_it_be(:project) { create(:project, :repository) }
+
+ let(:builder) { described_class.new(project) }
+
+ describe '#secret_variables' do
+ let(:environment) { '*' }
+ let(:protected_ref) { false }
+
+ let_it_be(:variable) do
+ create(:ci_variable,
+ value: 'secret',
+ project: project)
+ end
+
+ let_it_be(:protected_variable) do
+ create(:ci_variable, :protected,
+ value: 'protected',
+ project: project)
+ end
+
+ let(:variable_item) { item(variable) }
+ let(:protected_variable_item) { item(protected_variable) }
+
+ subject do
+ builder.secret_variables(
+ environment: environment,
+ protected_ref: protected_ref)
+ end
+
+ context 'when the ref is protected' do
+ let(:protected_ref) { true }
+
+ it 'contains all the variables' do
+ is_expected.to contain_exactly(variable_item, protected_variable_item)
+ end
+ end
+
+ context 'when the ref is not protected' do
+ let(:protected_ref) { false }
+
+ it 'contains only the unprotected variables' do
+ is_expected.to contain_exactly(variable_item)
+ end
+ end
+
+ context 'when environment name is specified' do
+ let(:environment) { 'review/name' }
+
+ before do
+ Ci::Variable.update_all(environment_scope: environment_scope)
+ end
+
+ context 'when environment scope is exactly matched' do
+ let(:environment_scope) { 'review/name' }
+
+ it { is_expected.to contain_exactly(variable_item) }
+ end
+
+ context 'when environment scope is matched by wildcard' do
+ let(:environment_scope) { 'review/*' }
+
+ it { is_expected.to contain_exactly(variable_item) }
+ end
+
+ context 'when environment scope does not match' do
+ let(:environment_scope) { 'review/*/special' }
+
+ it { is_expected.not_to contain_exactly(variable_item) }
+ end
+
+ context 'when environment scope has _' do
+ let(:environment_scope) { '*_*' }
+
+ it 'does not treat it as wildcard' do
+ is_expected.not_to contain_exactly(variable_item)
+ end
+ end
+
+ context 'when environment name contains underscore' do
+ let(:environment) { 'foo_bar/test' }
+ let(:environment_scope) { 'foo_bar/*' }
+
+ it 'matches literally for _' do
+ is_expected.to contain_exactly(variable_item)
+ end
+ end
+
+ # The environment name and scope cannot have % at the moment,
+ # but we're considering relaxing it and we should also make sure
+ # it doesn't break in case some data sneaked in somehow as we're
+ # not checking this integrity in database level.
+ context 'when environment scope has %' do
+ let(:environment_scope) { '*%*' }
+
+ it 'does not treat it as wildcard' do
+ is_expected.not_to contain_exactly(variable_item)
+ end
+ end
+
+ context 'when environment name contains a percent' do
+ let(:environment) { 'foo%bar/test' }
+ let(:environment_scope) { 'foo%bar/*' }
+
+ it 'matches literally for _' do
+ is_expected.to contain_exactly(variable_item)
+ end
+ end
+ end
+
+ context 'when variables with the same name have different environment scopes' do
+ let(:environment) { 'review/name' }
+
+ let_it_be(:partially_matched_variable) do
+ create(:ci_variable,
+ key: variable.key,
+ value: 'partial',
+ environment_scope: 'review/*',
+ project: project)
+ end
+
+ let_it_be(:perfectly_matched_variable) do
+ create(:ci_variable,
+ key: variable.key,
+ value: 'prefect',
+ environment_scope: 'review/name',
+ project: project)
+ end
+
+ it 'puts variables matching environment scope more in the end' do
+ variables_collection = Gitlab::Ci::Variables::Collection.new([
+ variable,
+ partially_matched_variable,
+ perfectly_matched_variable
+ ]).to_runner_variables
+
+ expect(subject.to_runner_variables).to eq(variables_collection)
+ end
+ end
+ end
+
+ def item(variable)
+ Gitlab::Ci::Variables::Collection::Item.fabricate(variable)
+ end
+end
diff --git a/spec/lib/gitlab/ci/variables/builder_spec.rb b/spec/lib/gitlab/ci/variables/builder_spec.rb
index 8a87cbe45c1..6e144d62ac0 100644
--- a/spec/lib/gitlab/ci/variables/builder_spec.rb
+++ b/spec/lib/gitlab/ci/variables/builder_spec.rb
@@ -3,10 +3,11 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Variables::Builder do
- let_it_be(:project) { create(:project, :repository) }
- let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
- let_it_be(:user) { project.owner }
- let_it_be(:job) do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :repository, namespace: group) }
+ let_it_be_with_reload(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be_with_reload(:job) do
create(:ci_build,
pipeline: pipeline,
user: user,
@@ -153,7 +154,7 @@ RSpec.describe Gitlab::Ci::Variables::Builder do
before do
allow(builder).to receive(:predefined_variables) { [var('A', 1), var('B', 1)] }
- allow(project).to receive(:predefined_variables) { [var('B', 2), var('C', 2)] }
+ allow(pipeline.project).to receive(:predefined_variables) { [var('B', 2), var('C', 2)] }
allow(pipeline).to receive(:predefined_variables) { [var('C', 3), var('D', 3)] }
allow(job).to receive(:runner) { double(predefined_variables: [var('D', 4), var('E', 4)]) }
allow(builder).to receive(:kubernetes_variables) { [var('E', 5), var('F', 5)] }
@@ -201,4 +202,240 @@ RSpec.describe Gitlab::Ci::Variables::Builder do
end
end
end
+
+ describe '#user_variables' do
+ context 'with user' do
+ subject { builder.user_variables(user).to_hash }
+
+ let(:expected_variables) do
+ {
+ 'GITLAB_USER_EMAIL' => user.email,
+ 'GITLAB_USER_ID' => user.id.to_s,
+ 'GITLAB_USER_LOGIN' => user.username,
+ 'GITLAB_USER_NAME' => user.name
+ }
+ end
+
+ it { is_expected.to eq(expected_variables) }
+ end
+
+ context 'without user' do
+ subject { builder.user_variables(nil).to_hash }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
+ describe '#kubernetes_variables' do
+ let(:service) { double(execute: template) }
+ let(:template) { double(to_yaml: 'example-kubeconfig', valid?: template_valid) }
+ let(:template_valid) { true }
+
+ subject { builder.kubernetes_variables(job) }
+
+ before do
+ allow(Ci::GenerateKubeconfigService).to receive(:new).with(job).and_return(service)
+ end
+
+ it { is_expected.to include(key: 'KUBECONFIG', value: 'example-kubeconfig', public: false, file: true) }
+
+ context 'generated config is invalid' do
+ let(:template_valid) { false }
+
+ it { is_expected.not_to include(key: 'KUBECONFIG', value: 'example-kubeconfig', public: false, file: true) }
+ end
+ end
+
+ describe '#deployment_variables' do
+ let(:environment) { 'production' }
+ let(:kubernetes_namespace) { 'namespace' }
+ let(:project_variables) { double }
+
+ subject { builder.deployment_variables(environment: environment, job: job) }
+
+ before do
+ allow(job).to receive(:expanded_kubernetes_namespace)
+ .and_return(kubernetes_namespace)
+
+ allow(project).to receive(:deployment_variables)
+ .with(environment: environment, kubernetes_namespace: kubernetes_namespace)
+ .and_return(project_variables)
+ end
+
+ context 'environment is nil' do
+ let(:environment) { nil }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
+ shared_examples "secret CI variables" do
+ context 'when ref is branch' do
+ context 'when ref is protected' do
+ before do
+ create(:protected_branch, :developers_can_merge, name: job.ref, project: project)
+ end
+
+ it { is_expected.to contain_exactly(protected_variable_item, unprotected_variable_item) }
+ end
+
+ context 'when ref is not protected' do
+ it { is_expected.to contain_exactly(unprotected_variable_item) }
+ end
+ end
+
+ context 'when ref is tag' do
+ before do
+ job.update!(ref: 'v1.1.0', tag: true)
+ pipeline.update!(ref: 'v1.1.0', tag: true)
+ end
+
+ context 'when ref is protected' do
+ before do
+ create(:protected_tag, project: project, name: 'v*')
+ end
+
+ it { is_expected.to contain_exactly(protected_variable_item, unprotected_variable_item) }
+ end
+
+ context 'when ref is not protected' do
+ it { is_expected.to contain_exactly(unprotected_variable_item) }
+ end
+ end
+
+ context 'when ref is merge request' do
+ let_it_be(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline, source_project: project) }
+ let_it_be(:pipeline) { merge_request.pipelines_for_merge_request.first }
+ let_it_be(:job) { create(:ci_build, ref: merge_request.source_branch, tag: false, pipeline: pipeline) }
+
+ context 'when ref is protected' do
+ before do
+ create(:protected_branch, :developers_can_merge, name: merge_request.source_branch, project: project)
+ end
+
+ it 'does not return protected variables as it is not supported for merge request pipelines' do
+ is_expected.to contain_exactly(unprotected_variable_item)
+ end
+ end
+
+ context 'when ref is not protected' do
+ it { is_expected.to contain_exactly(unprotected_variable_item) }
+ end
+ end
+ end
+
+ describe '#secret_instance_variables' do
+ subject { builder.secret_instance_variables }
+
+ let_it_be(:protected_variable) { create(:ci_instance_variable, protected: true) }
+ let_it_be(:unprotected_variable) { create(:ci_instance_variable, protected: false) }
+
+ let(:protected_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(protected_variable) }
+ let(:unprotected_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(unprotected_variable) }
+
+ include_examples "secret CI variables"
+ end
+
+ describe '#secret_group_variables' do
+ subject { builder.secret_group_variables(ref: job.git_ref, environment: job.expanded_environment_name) }
+
+ let_it_be(:protected_variable) { create(:ci_group_variable, protected: true, group: group) }
+ let_it_be(:unprotected_variable) { create(:ci_group_variable, protected: false, group: group) }
+
+ let(:protected_variable_item) { protected_variable }
+ let(:unprotected_variable_item) { unprotected_variable }
+
+ include_examples "secret CI variables"
+ end
+
+ describe '#secret_project_variables' do
+ let_it_be(:protected_variable) { create(:ci_variable, protected: true, project: project) }
+ let_it_be(:unprotected_variable) { create(:ci_variable, protected: false, project: project) }
+
+ let(:ref) { job.git_ref }
+ let(:environment) { job.expanded_environment_name }
+
+ subject { builder.secret_project_variables(ref: ref, environment: environment) }
+
+ context 'with ci_variables_builder_memoize_secret_variables disabled' do
+ before do
+ stub_feature_flags(ci_variables_builder_memoize_secret_variables: false)
+ end
+
+ let(:protected_variable_item) { protected_variable }
+ let(:unprotected_variable_item) { unprotected_variable }
+
+ include_examples "secret CI variables"
+ end
+
+ context 'with ci_variables_builder_memoize_secret_variables enabled' do
+ before do
+ stub_feature_flags(ci_variables_builder_memoize_secret_variables: true)
+ end
+
+ let(:protected_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(protected_variable) }
+ let(:unprotected_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(unprotected_variable) }
+
+ include_examples "secret CI variables"
+
+ context 'variables memoization' do
+ let_it_be(:scoped_variable) { create(:ci_variable, project: project, environment_scope: 'scoped') }
+
+ let(:scoped_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(scoped_variable) }
+
+ context 'with protected environments' do
+ it 'memoizes the result by environment' do
+ expect(pipeline.project)
+ .to receive(:protected_for?)
+ .with(pipeline.jobs_git_ref)
+ .once.and_return(true)
+
+ expect_next_instance_of(described_class::Project) do |project_variables_builder|
+ expect(project_variables_builder)
+ .to receive(:secret_variables)
+ .with(environment: 'production', protected_ref: true)
+ .once
+ .and_call_original
+ end
+
+ 2.times do
+ expect(builder.secret_project_variables(ref: ref, environment: 'production'))
+ .to contain_exactly(unprotected_variable_item, protected_variable_item)
+ end
+ end
+ end
+
+ context 'with unprotected environments' do
+ it 'memoizes the result by environment' do
+ expect(pipeline.project)
+ .to receive(:protected_for?)
+ .with(pipeline.jobs_git_ref)
+ .once.and_return(false)
+
+ expect_next_instance_of(described_class::Project) do |project_variables_builder|
+ expect(project_variables_builder)
+ .to receive(:secret_variables)
+ .with(environment: nil, protected_ref: false)
+ .once
+ .and_call_original
+
+ expect(project_variables_builder)
+ .to receive(:secret_variables)
+ .with(environment: 'scoped', protected_ref: false)
+ .once
+ .and_call_original
+ end
+
+ 2.times do
+ expect(builder.secret_project_variables(ref: 'other', environment: nil))
+ .to contain_exactly(unprotected_variable_item)
+
+ expect(builder.secret_project_variables(ref: 'other', environment: 'scoped'))
+ .to contain_exactly(unprotected_variable_item, scoped_variable_item)
+ end
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 20af84ce648..5f46607b042 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -9,6 +9,10 @@ module Gitlab
subject { described_class.new(config, user: nil).execute }
+ before do
+ stub_feature_flags(allow_unsafe_ruby_regexp: false)
+ end
+
shared_examples 'returns errors' do |error_message|
it 'adds a message when an error is encountered' do
expect(subject.errors).to include(error_message)
@@ -609,13 +613,13 @@ module Gitlab
context 'when it is an array of integers' do
let(:only) { [1, 1] }
- it_behaves_like 'returns errors', 'jobs:rspec:only config should be an array of strings or regexps'
+ it_behaves_like 'returns errors', 'jobs:rspec:only config should be an array of strings or regular expressions using re2 syntax'
end
context 'when it is invalid regex' do
let(:only) { ["/*invalid/"] }
- it_behaves_like 'returns errors', 'jobs:rspec:only config should be an array of strings or regexps'
+ it_behaves_like 'returns errors', 'jobs:rspec:only config should be an array of strings or regular expressions using re2 syntax'
end
end
@@ -633,13 +637,13 @@ module Gitlab
context 'when it is an array of integers' do
let(:except) { [1, 1] }
- it_behaves_like 'returns errors', 'jobs:rspec:except config should be an array of strings or regexps'
+ it_behaves_like 'returns errors', 'jobs:rspec:except config should be an array of strings or regular expressions using re2 syntax'
end
context 'when it is invalid regex' do
let(:except) { ["/*invalid/"] }
- it_behaves_like 'returns errors', 'jobs:rspec:except config should be an array of strings or regexps'
+ it_behaves_like 'returns errors', 'jobs:rspec:except config should be an array of strings or regular expressions using re2 syntax'
end
end
end
@@ -710,16 +714,16 @@ module Gitlab
end
end
- context 'when script is array of arrays of strings' do
+ context 'when script is nested arrays of strings' do
let(:config) do
{
- before_script: [["global script", "echo 1"], ["ls"], "pwd"],
+ before_script: [[["global script"], "echo 1"], "echo 2", ["ls"], "pwd"],
test: { script: ["script"] }
}
end
it "return commands with scripts concatenated" do
- expect(subject[:options][:before_script]).to eq(["global script", "echo 1", "ls", "pwd"])
+ expect(subject[:options][:before_script]).to eq(["global script", "echo 1", "echo 2", "ls", "pwd"])
end
end
end
@@ -737,15 +741,15 @@ module Gitlab
end
end
- context 'when script is array of arrays of strings' do
+ context 'when script is nested arrays of strings' do
let(:config) do
{
- test: { script: [["script"], ["echo 1"], "ls"] }
+ test: { script: [[["script"], "echo 1", "echo 2"], "ls"] }
}
end
it "return commands with scripts concatenated" do
- expect(subject[:options][:script]).to eq(["script", "echo 1", "ls"])
+ expect(subject[:options][:script]).to eq(["script", "echo 1", "echo 2", "ls"])
end
end
end
@@ -790,16 +794,16 @@ module Gitlab
end
end
- context 'when script is array of arrays of strings' do
+ context 'when script is nested arrays of strings' do
let(:config) do
{
- after_script: [["global script", "echo 1"], ["ls"], "pwd"],
+ after_script: [[["global script"], "echo 1"], "echo 2", ["ls"], "pwd"],
test: { script: ["script"] }
}
end
it "return after_script in options" do
- expect(subject[:options][:after_script]).to eq(["global script", "echo 1", "ls", "pwd"])
+ expect(subject[:options][:after_script]).to eq(["global script", "echo 1", "echo 2", "ls", "pwd"])
end
end
end
@@ -2469,40 +2473,16 @@ module Gitlab
it_behaves_like 'returns errors', 'jobs:rspec:tags config should be an array of strings'
end
- context 'returns errors if before_script parameter is invalid' do
- let(:config) { YAML.dump({ before_script: "bundle update", rspec: { script: "test" } }) }
-
- it_behaves_like 'returns errors', 'before_script config should be an array containing strings and arrays of strings'
- end
-
context 'returns errors if job before_script parameter is not an array of strings' do
let(:config) { YAML.dump({ rspec: { script: "test", before_script: [10, "test"] } }) }
- it_behaves_like 'returns errors', 'jobs:rspec:before_script config should be an array containing strings and arrays of strings'
- end
-
- context 'returns errors if job before_script parameter is multi-level nested array of strings' do
- let(:config) { YAML.dump({ rspec: { script: "test", before_script: [["ls", ["pwd"]], "test"] } }) }
-
- it_behaves_like 'returns errors', 'jobs:rspec:before_script config should be an array containing strings and arrays of strings'
- end
-
- context 'returns errors if after_script parameter is invalid' do
- let(:config) { YAML.dump({ after_script: "bundle update", rspec: { script: "test" } }) }
-
- it_behaves_like 'returns errors', 'after_script config should be an array containing strings and arrays of strings'
+ it_behaves_like 'returns errors', 'jobs:rspec:before_script config should be a string or a nested array of strings up to 10 levels deep'
end
context 'returns errors if job after_script parameter is not an array of strings' do
let(:config) { YAML.dump({ rspec: { script: "test", after_script: [10, "test"] } }) }
- it_behaves_like 'returns errors', 'jobs:rspec:after_script config should be an array containing strings and arrays of strings'
- end
-
- context 'returns errors if job after_script parameter is multi-level nested array of strings' do
- let(:config) { YAML.dump({ rspec: { script: "test", after_script: [["ls", ["pwd"]], "test"] } }) }
-
- it_behaves_like 'returns errors', 'jobs:rspec:after_script config should be an array containing strings and arrays of strings'
+ it_behaves_like 'returns errors', 'jobs:rspec:after_script config should be a string or a nested array of strings up to 10 levels deep'
end
context 'returns errors if image parameter is invalid' do