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/ansi2json_spec.rb389
-rw-r--r--spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb36
-rw-r--r--spec/lib/gitlab/ci/config/entry/legacy_variables_spec.rb173
-rw-r--r--spec/lib/gitlab/ci/config/entry/processable_spec.rb14
-rw-r--r--spec/lib/gitlab/ci/config/entry/product/parallel_spec.rb9
-rw-r--r--spec/lib/gitlab/ci/config/entry/root_spec.rb22
-rw-r--r--spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb13
-rw-r--r--spec/lib/gitlab/ci/config/entry/variable_spec.rb52
-rw-r--r--spec/lib/gitlab/ci/config/entry/variables_spec.rb56
-rw-r--r--spec/lib/gitlab/ci/config/entry/workflow_spec.rb48
-rw-r--r--spec/lib/gitlab/ci/config/external/file/artifact_spec.rb7
-rw-r--r--spec/lib/gitlab/ci/config/external/file/project_spec.rb17
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper_spec.rb21
-rw-r--r--spec/lib/gitlab/ci/config_spec.rb27
-rw-r--r--spec/lib/gitlab/ci/jwt_v2_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/lint_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb22
-rw-r--r--spec/lib/gitlab/ci/parsers/security/common_spec.rb106
-rw-r--r--spec/lib/gitlab/ci/parsers/security/sast_spec.rb31
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb51
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb14
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/limit/active_jobs_spec.rb97
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb43
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb13
-rw-r--r--spec/lib/gitlab/ci/pipeline/duration_spec.rb224
-rw-r--r--spec/lib/gitlab/ci/pipeline/logger_spec.rb13
-rw-r--r--spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb33
-rw-r--r--spec/lib/gitlab/ci/reports/codequality_reports_spec.rb32
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/source_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/reports/security/report_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/secure_files/cer_spec.rb69
-rw-r--r--spec/lib/gitlab/ci/secure_files/mobile_provision_spec.rb149
-rw-r--r--spec/lib/gitlab/ci/secure_files/p12_spec.rb81
-rw-r--r--spec/lib/gitlab/ci/secure_files/x509_name_spec.rb30
-rw-r--r--spec/lib/gitlab/ci/trace_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/variables/builder/group_spec.rb11
-rw-r--r--spec/lib/gitlab/ci/variables/builder/project_spec.rb11
-rw-r--r--spec/lib/gitlab/ci/variables/builder/release_spec.rb69
-rw-r--r--spec/lib/gitlab/ci/variables/builder_spec.rb44
-rw-r--r--spec/lib/gitlab/ci/variables/collection/sort_spec.rb15
-rw-r--r--spec/lib/gitlab/ci/variables/collection_spec.rb37
-rw-r--r--spec/lib/gitlab/ci/yaml_processor/result_spec.rb9
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb330
45 files changed, 1590 insertions, 874 deletions
diff --git a/spec/lib/gitlab/ci/ansi2json_spec.rb b/spec/lib/gitlab/ci/ansi2json_spec.rb
index 4b3b049176f..0f8f3759834 100644
--- a/spec/lib/gitlab/ci/ansi2json_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2json_spec.rb
@@ -7,70 +7,74 @@ RSpec.describe Gitlab::Ci::Ansi2json do
describe 'lines' do
it 'prints non-ansi as-is' do
- expect(convert_json('Hello')).to eq([
- { offset: 0, content: [{ text: 'Hello' }] }
- ])
+ expect(convert_json('Hello')).to eq([{ offset: 0, content: [{ text: 'Hello' }] }])
end
context 'new lines' do
it 'adds new line when encountering \n' do
- expect(convert_json("Hello\nworld")).to eq([
- { offset: 0, content: [{ text: 'Hello' }] },
- { offset: 6, content: [{ text: 'world' }] }
- ])
+ expect(convert_json("Hello\nworld")).to eq(
+ [
+ { offset: 0, content: [{ text: 'Hello' }] },
+ { offset: 6, content: [{ text: 'world' }] }
+ ])
end
it 'adds new line when encountering \r\n' do
- expect(convert_json("Hello\r\nworld")).to eq([
- { offset: 0, content: [{ text: 'Hello' }] },
- { offset: 7, content: [{ text: 'world' }] }
- ])
+ expect(convert_json("Hello\r\nworld")).to eq(
+ [
+ { offset: 0, content: [{ text: 'Hello' }] },
+ { offset: 7, content: [{ text: 'world' }] }
+ ])
end
it 'ignores empty newlines' do
- expect(convert_json("Hello\n\nworld")).to eq([
- { offset: 0, content: [{ text: 'Hello' }] },
- { offset: 7, content: [{ text: 'world' }] }
- ])
- expect(convert_json("Hello\r\n\r\nworld")).to eq([
- { offset: 0, content: [{ text: 'Hello' }] },
- { offset: 9, content: [{ text: 'world' }] }
- ])
+ expect(convert_json("Hello\n\nworld")).to eq(
+ [
+ { offset: 0, content: [{ text: 'Hello' }] },
+ { offset: 7, content: [{ text: 'world' }] }
+ ])
+ expect(convert_json("Hello\r\n\r\nworld")).to eq(
+ [
+ { offset: 0, content: [{ text: 'Hello' }] },
+ { offset: 9, content: [{ text: 'world' }] }
+ ])
end
it 'replace the current line when encountering \r' do
- expect(convert_json("Hello\rworld")).to eq([
- { offset: 0, content: [{ text: 'world' }] }
- ])
+ expect(convert_json("Hello\rworld")).to eq([{ offset: 0, content: [{ text: 'world' }] }])
end
end
it 'recognizes color changing ANSI sequences' do
- expect(convert_json("\e[31mHello\e[0m")).to eq([
- { offset: 0, content: [{ text: 'Hello', style: 'term-fg-red' }] }
- ])
+ expect(convert_json("\e[31mHello\e[0m")).to eq(
+ [
+ { offset: 0, content: [{ text: 'Hello', style: 'term-fg-red' }] }
+ ])
end
it 'recognizes color changing ANSI sequences across multiple lines' do
- expect(convert_json("\e[31mHello\nWorld\e[0m")).to eq([
- { offset: 0, content: [{ text: 'Hello', style: 'term-fg-red' }] },
- { offset: 11, content: [{ text: 'World', style: 'term-fg-red' }] }
- ])
+ expect(convert_json("\e[31mHello\nWorld\e[0m")).to eq(
+ [
+ { offset: 0, content: [{ text: 'Hello', style: 'term-fg-red' }] },
+ { offset: 11, content: [{ text: 'World', style: 'term-fg-red' }] }
+ ])
end
it 'recognizes background and foreground colors' do
- expect(convert_json("\e[31;44mHello")).to eq([
- { offset: 0, content: [{ text: 'Hello', style: 'term-fg-red term-bg-blue' }] }
- ])
+ expect(convert_json("\e[31;44mHello")).to eq(
+ [
+ { offset: 0, content: [{ text: 'Hello', style: 'term-fg-red term-bg-blue' }] }
+ ])
end
it 'recognizes style changes within the same line' do
- expect(convert_json("\e[31;44mHello\e[0m world")).to eq([
- { offset: 0, content: [
- { text: 'Hello', style: 'term-fg-red term-bg-blue' },
- { text: ' world' }
- ] }
- ])
+ expect(convert_json("\e[31;44mHello\e[0m world")).to eq(
+ [
+ { offset: 0, content: [
+ { text: 'Hello', style: 'term-fg-red term-bg-blue' },
+ { text: ' world' }
+ ] }
+ ])
end
context 'with section markers' do
@@ -82,130 +86,137 @@ RSpec.describe Gitlab::Ci::Ansi2json do
let(:section_end) { "section_end:#{section_end_time.to_i}:#{section_name}\r\033[0K" }
it 'marks the first line of the section as header' do
- expect(convert_json("Hello#{section_start}world!")).to eq([
- {
- offset: 0,
- content: [{ text: 'Hello' }]
- },
- {
- offset: 5,
- content: [{ text: 'world!' }],
- section: 'prepare-script',
- section_header: true
- }
- ])
+ expect(convert_json("Hello#{section_start}world!")).to eq(
+ [
+ {
+ offset: 0,
+ content: [{ text: 'Hello' }]
+ },
+ {
+ offset: 5,
+ content: [{ text: 'world!' }],
+ section: 'prepare-script',
+ section_header: true
+ }
+ ])
end
it 'does not marks the other lines of the section as header' do
- expect(convert_json("outside section#{section_start}Hello\nworld!")).to eq([
- {
- offset: 0,
- content: [{ text: 'outside section' }]
- },
- {
- offset: 15,
- content: [{ text: 'Hello' }],
- section: 'prepare-script',
- section_header: true
- },
- {
- offset: 65,
- content: [{ text: 'world!' }],
- section: 'prepare-script'
- }
- ])
+ expect(convert_json("outside section#{section_start}Hello\nworld!")).to eq(
+ [
+ {
+ offset: 0,
+ content: [{ text: 'outside section' }]
+ },
+ {
+ offset: 15,
+ content: [{ text: 'Hello' }],
+ section: 'prepare-script',
+ section_header: true
+ },
+ {
+ offset: 65,
+ content: [{ text: 'world!' }],
+ section: 'prepare-script'
+ }
+ ])
end
it 'marks the last line of the section as footer' do
- expect(convert_json("#{section_start}Good\nmorning\nworld!#{section_end}")).to eq([
- {
- offset: 0,
- content: [{ text: 'Good' }],
- section: 'prepare-script',
- section_header: true
- },
- {
- offset: 49,
- content: [{ text: 'morning' }],
- section: 'prepare-script'
- },
- {
- offset: 57,
- content: [{ text: 'world!' }],
- section: 'prepare-script'
- },
- {
- offset: 63,
- content: [],
- section_duration: '01:03',
- section: 'prepare-script'
- }
- ])
+ expect(convert_json("#{section_start}Good\nmorning\nworld!#{section_end}")).to eq(
+ [
+ {
+ offset: 0,
+ content: [{ text: 'Good' }],
+ section: 'prepare-script',
+ section_header: true
+ },
+ {
+ offset: 49,
+ content: [{ text: 'morning' }],
+ section: 'prepare-script'
+ },
+ {
+ offset: 57,
+ content: [{ text: 'world!' }],
+ section: 'prepare-script'
+ },
+ {
+ offset: 63,
+ content: [],
+ section_duration: '01:03',
+ section: 'prepare-script'
+ }
+ ])
end
it 'marks the first line as header and footer if is the only line in the section' do
- expect(convert_json("#{section_start}Hello world!#{section_end}")).to eq([
- {
- offset: 0,
- content: [{ text: 'Hello world!' }],
- section: 'prepare-script',
- section_header: true
- },
- {
- offset: 56,
- content: [],
- section: 'prepare-script',
- section_duration: '01:03'
- }
- ])
+ expect(convert_json("#{section_start}Hello world!#{section_end}")).to eq(
+ [
+ {
+ offset: 0,
+ content: [{ text: 'Hello world!' }],
+ section: 'prepare-script',
+ section_header: true
+ },
+ {
+ offset: 56,
+ content: [],
+ section: 'prepare-script',
+ section_duration: '01:03'
+ }
+ ])
end
it 'does not add sections attribute to lines after the section is closed' do
- expect(convert_json("#{section_start}Hello#{section_end}world")).to eq([
- {
- offset: 0,
- content: [{ text: 'Hello' }],
- section: 'prepare-script',
- section_header: true
- },
- {
- offset: 49,
- content: [],
- section: 'prepare-script',
- section_duration: '01:03'
- },
- {
- offset: 91,
- content: [{ text: 'world' }]
- }
- ])
+ expect(convert_json("#{section_start}Hello#{section_end}world")).to eq(
+ [
+ {
+ offset: 0,
+ content: [{ text: 'Hello' }],
+ section: 'prepare-script',
+ section_header: true
+ },
+ {
+ offset: 49,
+ content: [],
+ section: 'prepare-script',
+ section_duration: '01:03'
+ },
+ {
+ offset: 91,
+ content: [{ text: 'world' }]
+ }
+ ])
end
it 'ignores section_end marker if no section_start exists' do
- expect(convert_json("Hello #{section_end}world")).to eq([
- {
- offset: 0,
- content: [{ text: 'Hello world' }]
- }
- ])
+ expect(convert_json("Hello #{section_end}world")).to eq(
+ [
+ {
+ offset: 0,
+ content: [{ text: 'Hello world' }]
+ }
+ ])
end
context 'when section name contains .-_ and capital letters' do
let(:section_name) { 'a.Legit-SeCtIoN_namE' }
it 'sanitizes the section name' do
- expect(convert_json("Hello#{section_start}world!")).to eq([
- {
- offset: 0,
- content: [{ text: 'Hello' }]
- },
- {
- offset: 5,
- content: [{ text: 'world!' }],
- section: 'a-legit-section-name',
- section_header: true
- }
- ])
+ expect(convert_json("Hello#{section_start}world!")).to eq(
+ [
+ {
+ offset: 0,
+ content: [{ text: 'Hello' }]
+ },
+ {
+ offset: 5,
+ content: [{ text: 'world!' }],
+ section: 'a-legit-section-name',
+ section_header: true
+ }
+ ])
end
end
@@ -213,12 +224,13 @@ RSpec.describe Gitlab::Ci::Ansi2json do
let(:section_name) { 'my_$ection' }
it 'ignores the section' do
- expect(convert_json("#{section_start}hello")).to eq([
- {
- offset: 0,
- content: [{ text: 'hello' }]
- }
- ])
+ expect(convert_json("#{section_start}hello")).to eq(
+ [
+ {
+ offset: 0,
+ content: [{ text: 'hello' }]
+ }
+ ])
end
end
@@ -226,31 +238,33 @@ RSpec.describe Gitlab::Ci::Ansi2json do
let(:section_name) { '<a_tag>' }
it 'ignores the section' do
- expect(convert_json("#{section_start}hello")).to eq([
- {
- offset: 0,
- content: [{ text: 'hello' }]
- }
- ])
+ expect(convert_json("#{section_start}hello")).to eq(
+ [
+ {
+ offset: 0,
+ content: [{ text: 'hello' }]
+ }
+ ])
end
end
it 'prints HTML tags as is' do
trace = "#{section_start}section_end:1:2<div>hello</div>#{section_end}"
- expect(convert_json(trace)).to eq([
- {
- offset: 0,
- content: [{ text: 'section_end:1:2<div>hello</div>' }],
- section: 'prepare-script',
- section_header: true
- },
- {
- offset: 75,
- content: [],
- section: 'prepare-script',
- section_duration: '01:03'
- }
- ])
+ expect(convert_json(trace)).to eq(
+ [
+ {
+ offset: 0,
+ content: [{ text: 'section_end:1:2<div>hello</div>' }],
+ section: 'prepare-script',
+ section_header: true
+ },
+ {
+ offset: 75,
+ content: [],
+ section: 'prepare-script',
+ section_duration: '01:03'
+ }
+ ])
end
context 'with nested section' do
@@ -264,7 +278,8 @@ RSpec.describe Gitlab::Ci::Ansi2json do
it 'adds multiple sections to the lines inside the nested section' do
trace = "Hello#{section_start}foo#{nested_section_start}bar#{nested_section_end}baz#{section_end}world"
- expect(convert_json(trace)).to eq([
+ expect(convert_json(trace)).to eq(
+ [
{
offset: 0,
content: [{ text: 'Hello' }]
@@ -308,7 +323,8 @@ RSpec.describe Gitlab::Ci::Ansi2json do
it 'adds multiple sections to the lines inside the nested section and closes all sections together' do
trace = "Hello#{section_start}\e[91mfoo\e[0m#{nested_section_start}bar#{nested_section_end}#{section_end}"
- expect(convert_json(trace)).to eq([
+ expect(convert_json(trace)).to eq(
+ [
{
offset: 0,
content: [{ text: 'Hello' }]
@@ -346,24 +362,25 @@ RSpec.describe Gitlab::Ci::Ansi2json do
it 'provides section options when set' do
trace = "#{option_section_start}hello#{section_end}"
- expect(convert_json(trace)).to eq([
- {
- offset: 0,
- content: [{ text: 'hello' }],
- section: 'prepare-script',
- section_header: true,
- section_options: {
- 'collapsed' => 'true',
- 'unused_option' => '123'
+ expect(convert_json(trace)).to eq(
+ [
+ {
+ offset: 0,
+ content: [{ text: 'hello' }],
+ section: 'prepare-script',
+ section_header: true,
+ section_options: {
+ 'collapsed' => 'true',
+ 'unused_option' => '123'
+ }
+ },
+ {
+ offset: 83,
+ content: [],
+ section: 'prepare-script',
+ section_duration: '01:03'
}
- },
- {
- offset: 83,
- content: [],
- section: 'prepare-script',
- section_duration: '01:03'
- }
- ])
+ ])
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 234ba68d627..a22aa30304b 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
@@ -122,19 +122,17 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do
context 'when compare_to is branch or tag' do
using RSpec::Parameterized::TableSyntax
- where(:pipeline_ref, :compare_to, :paths, :ff, :result) do
- 'feature_1' | 'master' | ['file1.txt'] | true | true
- 'feature_1' | 'master' | ['README.md'] | true | false
- 'feature_1' | 'master' | ['xyz.md'] | true | false
- 'feature_2' | 'master' | ['file1.txt'] | true | true
- 'feature_2' | 'master' | ['file2.txt'] | true | true
- 'feature_2' | 'feature_1' | ['file1.txt'] | true | false
- 'feature_2' | 'feature_1' | ['file1.txt'] | false | true
- 'feature_2' | 'feature_1' | ['file2.txt'] | true | true
- 'feature_1' | 'tag_1' | ['file1.txt'] | true | false
- 'feature_1' | 'tag_1' | ['file1.txt'] | false | true
- 'feature_1' | 'tag_1' | ['file2.txt'] | true | true
- 'feature_2' | 'tag_1' | ['file2.txt'] | true | true
+ where(:pipeline_ref, :compare_to, :paths, :result) do
+ 'feature_1' | 'master' | ['file1.txt'] | true
+ 'feature_1' | 'master' | ['README.md'] | false
+ 'feature_1' | 'master' | ['xyz.md'] | false
+ 'feature_2' | 'master' | ['file1.txt'] | true
+ 'feature_2' | 'master' | ['file2.txt'] | true
+ 'feature_2' | 'feature_1' | ['file1.txt'] | false
+ 'feature_2' | 'feature_1' | ['file2.txt'] | true
+ 'feature_1' | 'tag_1' | ['file1.txt'] | false
+ 'feature_1' | 'tag_1' | ['file2.txt'] | true
+ 'feature_2' | 'tag_1' | ['file2.txt'] | true
end
with_them do
@@ -144,10 +142,6 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do
build(:ci_pipeline, project: project, ref: pipeline_ref, sha: project.commit(pipeline_ref).sha)
end
- before do
- stub_feature_flags(ci_rules_changes_compare: ff)
- end
-
it { is_expected.to eq(result) }
end
end
@@ -174,14 +168,6 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do
::Gitlab::Ci::Build::Rules::Rule::Clause::ParseError, 'rules:changes:compare_to is not a valid ref'
)
end
-
- context 'when the FF ci_rules_changes_compare is disabled' do
- before do
- stub_feature_flags(ci_rules_changes_compare: false)
- end
-
- it { is_expected.to be_truthy }
- end
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/legacy_variables_spec.rb b/spec/lib/gitlab/ci/config/entry/legacy_variables_spec.rb
deleted file mode 100644
index e9edec9a0a4..00000000000
--- a/spec/lib/gitlab/ci/config/entry/legacy_variables_spec.rb
+++ /dev/null
@@ -1,173 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Ci::Config::Entry::LegacyVariables do
- let(:config) { {} }
- let(:metadata) { {} }
-
- subject(:entry) { described_class.new(config, **metadata) }
-
- before do
- entry.compose!
- end
-
- shared_examples 'valid config' do
- describe '#value' do
- it 'returns hash with key value strings' do
- expect(entry.value).to eq result
- 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
-
- shared_examples 'invalid config' do |error_message|
- describe '#valid?' do
- it 'is not valid' do
- expect(entry).not_to be_valid
- end
- end
-
- describe '#errors' do
- it 'saves errors' do
- expect(entry.errors)
- .to include(error_message)
- end
- end
- end
-
- context 'when entry config value has key-value pairs' do
- let(:config) do
- { 'VARIABLE_1' => 'value 1', 'VARIABLE_2' => 'value 2' }
- end
-
- let(:result) do
- { 'VARIABLE_1' => 'value 1', 'VARIABLE_2' => 'value 2' }
- end
-
- it_behaves_like 'valid config'
-
- describe '#value_with_data' do
- it 'returns variable with data' do
- expect(entry.value_with_data).to eq(
- 'VARIABLE_1' => { value: 'value 1' },
- 'VARIABLE_2' => { value: 'value 2' }
- )
- end
- end
- end
-
- context 'with numeric keys and values in the config' do
- let(:config) { { 10 => 20 } }
- let(:result) do
- { '10' => '20' }
- end
-
- it_behaves_like 'valid config'
- end
-
- context 'when key is an array' do
- let(:config) { { ['VAR1'] => 'val1' } }
- let(:result) do
- { 'VAR1' => 'val1' }
- end
-
- it_behaves_like 'invalid config', /should be a hash of key value pairs/
- end
-
- context 'when value is a symbol' do
- let(:config) { { 'VAR1' => :val1 } }
- let(:result) do
- { 'VAR1' => 'val1' }
- end
-
- it_behaves_like 'valid config'
- end
-
- context 'when value is a boolean' do
- let(:config) { { 'VAR1' => true } }
- let(:result) do
- { 'VAR1' => 'val1' }
- end
-
- it_behaves_like 'invalid config', /should be a hash of key value pairs/
- end
-
- context 'when entry config value has key-value pair and hash' do
- let(:config) do
- { 'VARIABLE_1' => { value: 'value 1', description: 'variable 1' },
- 'VARIABLE_2' => 'value 2' }
- end
-
- it_behaves_like 'invalid config', /should be a hash of key value pairs/
-
- context 'when metadata has use_value_data: true' do
- let(:metadata) { { use_value_data: true } }
-
- let(:result) do
- { 'VARIABLE_1' => 'value 1', 'VARIABLE_2' => 'value 2' }
- end
-
- it_behaves_like 'valid config'
-
- describe '#value_with_data' do
- it 'returns variable with data' do
- expect(entry.value_with_data).to eq(
- 'VARIABLE_1' => { value: 'value 1', description: 'variable 1' },
- 'VARIABLE_2' => { value: 'value 2' }
- )
- end
- end
- end
- end
-
- context 'when entry value is an array' do
- let(:config) { [:VAR, 'test'] }
-
- it_behaves_like 'invalid config', /should be a hash of key value pairs/
- end
-
- context 'when metadata has use_value_data: true' do
- let(:metadata) { { use_value_data: true } }
-
- context 'when entry value has hash with other key-pairs' do
- let(:config) do
- { 'VARIABLE_1' => { value: 'value 1', hello: 'variable 1' },
- 'VARIABLE_2' => 'value 2' }
- end
-
- it_behaves_like 'invalid config', /should be a hash of key value pairs, value can be a hash/
- end
-
- context 'when entry config value has hash with nil description' do
- let(:config) do
- { 'VARIABLE_1' => { value: 'value 1', description: nil } }
- end
-
- it_behaves_like 'invalid config', /should be a hash of key value pairs, value can be a hash/
- end
-
- context 'when entry config value has hash without description' do
- let(:config) do
- { 'VARIABLE_1' => { value: 'value 1' } }
- end
-
- let(:result) do
- { 'VARIABLE_1' => 'value 1' }
- end
-
- it_behaves_like 'valid config'
- end
- end
-end
diff --git a/spec/lib/gitlab/ci/config/entry/processable_spec.rb b/spec/lib/gitlab/ci/config/entry/processable_spec.rb
index 5f42a8c49a7..ad90dd59585 100644
--- a/spec/lib/gitlab/ci/config/entry/processable_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/processable_spec.rb
@@ -210,20 +210,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable do
expect(entry.errors)
.to include 'variables:var2 config must be a string'
end
-
- context 'when the FF ci_variables_refactoring_to_variable is disabled' do
- let(:entry_without_ff) { node_class.new(config, name: :rspec) }
-
- before do
- stub_feature_flags(ci_variables_refactoring_to_variable: false)
- entry_without_ff.compose!
- end
-
- it 'reports error about variable' do
- expect(entry_without_ff.errors)
- .to include /config should be a hash of key value pairs/
- end
- end
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/product/parallel_spec.rb b/spec/lib/gitlab/ci/config/entry/product/parallel_spec.rb
index 937642f07e7..a16f1cf9e43 100644
--- a/spec/lib/gitlab/ci/config/entry/product/parallel_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/product/parallel_spec.rb
@@ -91,10 +91,11 @@ RSpec.describe ::Gitlab::Ci::Config::Entry::Product::Parallel do
describe '#value' do
it 'returns job needs configuration' do
- expect(parallel.value).to match(matrix: [
- { PROVIDER: 'aws', STACK: %w[monitoring app1 app2] },
- { PROVIDER: 'gcp', STACK: %w[data processing] }
- ])
+ expect(parallel.value).to match(matrix:
+ [
+ { PROVIDER: 'aws', STACK: %w[monitoring app1 app2] },
+ { PROVIDER: 'gcp', STACK: %w[data processing] }
+ ])
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb
index 3d19987a0be..a55e13e7c2d 100644
--- a/spec/lib/gitlab/ci/config/entry/root_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb
@@ -34,7 +34,11 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
image: 'image:1.0',
default: {},
services: ['postgres:9.1', 'mysql:5.5'],
- variables: { VAR: 'root', VAR2: { value: 'val 2', description: 'this is var 2' } },
+ variables: {
+ VAR: 'root',
+ VAR2: { value: 'val 2', description: 'this is var 2' },
+ VAR3: { value: %w[val3 val3b], description: 'this is var 3' }
+ },
after_script: ['make clean'],
stages: %w(build pages release),
cache: { key: 'k', untracked: true, paths: ['public/'] },
@@ -83,7 +87,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
end
it 'sets correct variables value' do
- expect(root.variables_value).to eq('VAR' => 'root', 'VAR2' => 'val 2')
+ expect(root.variables_value).to eq('VAR' => 'root', 'VAR2' => 'val 2', 'VAR3' => 'val3')
end
describe '#leaf?' do
@@ -361,20 +365,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
expect(root.errors)
.to include /var1 config uses invalid data keys: invalid/
end
-
- context 'when the FF ci_variables_refactoring_to_variable is disabled' do
- let(:root_without_ff) { described_class.new(hash, user: user, project: project) }
-
- before do
- stub_feature_flags(ci_variables_refactoring_to_variable: false)
- root_without_ff.compose!
- end
-
- it 'reports errors about the invalid variable' do
- expect(root_without_ff.errors)
- .to include /variables config should be a hash of key value pairs, value can be a hash/
- end
- end
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
index 303d825c591..3531d6e9f1a 100644
--- a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
@@ -364,19 +364,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule do
it 'returns an error about invalid variables:' do
expect(subject.errors).to include(/variables config should be a hash/)
end
-
- context 'when the FF ci_variables_refactoring_to_variable is disabled' do
- let(:entry_without_ff) { factory.create! }
-
- before do
- stub_feature_flags(ci_variables_refactoring_to_variable: false)
- entry_without_ff.compose!
- end
-
- it 'returns an error about invalid variables:' do
- expect(subject.errors).to include(/variables config should be a hash/)
- end
- end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/variable_spec.rb b/spec/lib/gitlab/ci/config/entry/variable_spec.rb
index 744a89d4509..076a5b32e92 100644
--- a/spec/lib/gitlab/ci/config/entry/variable_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/variable_spec.rb
@@ -127,20 +127,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variable do
end
end
- context 'when config value is an array' do
- let(:config) { { value: ['value'], description: 'description' } }
-
- describe '#valid?' do
- it { is_expected.not_to be_valid }
- end
-
- describe '#errors' do
- subject(:errors) { entry.errors }
-
- it { is_expected.to include 'var1 config value must be an alphanumeric string' }
- end
- end
-
context 'when config description is a symbol' do
let(:config) { { value: 'value', description: :description } }
@@ -209,4 +195,42 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variable do
end
end
end
+
+ describe 'ComplexArrayVariable' do
+ context 'when allow_array_value metadata is false' do
+ let(:config) { { value: %w[value value2], description: 'description' } }
+ let(:metadata) { { allow_array_value: false } }
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ subject(:errors) { entry.errors }
+
+ it { is_expected.to include 'var1 config value must be an alphanumeric string' }
+ end
+ end
+
+ context 'when allow_array_value metadata is true' do
+ let(:config) { { value: %w[value value2], description: 'description' } }
+ let(:metadata) { { allowed_value_data: %i[value description], allow_array_value: true } }
+
+ describe '#valid?' do
+ it { is_expected.to be_valid }
+ end
+
+ describe '#value' do
+ subject(:value) { entry.value }
+
+ it { is_expected.to eq('value') }
+ end
+
+ describe '#value_with_data' do
+ subject(:value_with_data) { entry.value_with_data }
+
+ it { is_expected.to eq(value: 'value', description: 'description', value_options: %w[value value2]) }
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/config/entry/variables_spec.rb b/spec/lib/gitlab/ci/config/entry/variables_spec.rb
index ad7290d0589..085f304094e 100644
--- a/spec/lib/gitlab/ci/config/entry/variables_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/variables_spec.rb
@@ -98,6 +98,62 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do
it_behaves_like 'invalid config', /must be either a string or a hash/
end
+ context 'when entry config value has unallowed value key-value pair and value is a string' do
+ let(:config) do
+ { 'VARIABLE_1' => { value: 'value', description: 'variable 1' } }
+ end
+
+ context 'when there is no allowed_value_data metadata' do
+ it_behaves_like 'invalid config', /variable_1 config must be a string/
+ end
+
+ context 'when metadata has allow_array_value and allowed_value_data' do
+ let(:metadata) { { allowed_value_data: %i[value description], allow_array_value: true } }
+
+ let(:result) do
+ { 'VARIABLE_1' => 'value' }
+ end
+
+ it_behaves_like 'valid config'
+
+ describe '#value_with_data' do
+ it 'returns variable with data' do
+ expect(entry.value_with_data).to eq(
+ 'VARIABLE_1' => { value: 'value', description: 'variable 1' }
+ )
+ end
+ end
+ end
+ end
+
+ context 'when entry config value has key-value pair and value is an array' do
+ let(:config) do
+ { 'VARIABLE_1' => { value: %w[value1 value2], description: 'variable 1' } }
+ end
+
+ context 'when there is no allowed_value_data metadata' do
+ it_behaves_like 'invalid config', /variable_1 config value must be an alphanumeric string/
+ end
+
+ context 'when metadata has allow_array_value and allowed_value_data' do
+ let(:metadata) { { allowed_value_data: %i[value description], allow_array_value: true } }
+
+ let(:result) do
+ { 'VARIABLE_1' => 'value1' }
+ end
+
+ it_behaves_like 'valid config'
+
+ describe '#value_with_data' do
+ it 'returns variable with data' do
+ expect(entry.value_with_data).to eq(
+ 'VARIABLE_1' => { value: 'value1', value_options: %w[value1 value2], description: 'variable 1' }
+ )
+ end
+ end
+ end
+ end
+
context 'when entry config value has key-value pair and hash' do
let(:config) do
{ 'VARIABLE_1' => { value: 'value 1', description: 'variable 1' },
diff --git a/spec/lib/gitlab/ci/config/entry/workflow_spec.rb b/spec/lib/gitlab/ci/config/entry/workflow_spec.rb
index 3d19832e13d..97ac199f47d 100644
--- a/spec/lib/gitlab/ci/config/entry/workflow_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/workflow_spec.rb
@@ -65,6 +65,54 @@ RSpec.describe Gitlab::Ci::Config::Entry::Workflow do
end
end
end
+
+ context 'with workflow name' do
+ let(:factory) { Gitlab::Config::Entry::Factory.new(described_class).value(workflow_hash) }
+
+ context 'with a blank name' do
+ let(:workflow_hash) do
+ { name: '' }
+ end
+
+ it 'is invalid' do
+ expect(config).not_to be_valid
+ end
+
+ it 'returns error about invalid name' do
+ expect(config.errors).to include('workflow name is too short (minimum is 1 character)')
+ end
+ end
+
+ context 'with too long name' do
+ let(:workflow_hash) do
+ { name: 'a' * 256 }
+ end
+
+ it 'is invalid' do
+ expect(config).not_to be_valid
+ end
+
+ it 'returns error about invalid name' do
+ expect(config.errors).to include('workflow name is too long (maximum is 255 characters)')
+ end
+ end
+
+ context 'when name is nil' do
+ let(:workflow_hash) { { name: nil } }
+
+ it 'is valid' do
+ expect(config).to be_valid
+ end
+ end
+
+ context 'when name is not provided' do
+ let(:workflow_hash) { { rules: [{ if: '$VAR' }] } }
+
+ it 'is valid' do
+ expect(config).to be_valid
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/config/external/file/artifact_spec.rb b/spec/lib/gitlab/ci/config/external/file/artifact_spec.rb
index 9da8d106862..a8dc7897082 100644
--- a/spec/lib/gitlab/ci/config/external/file/artifact_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/artifact_spec.rb
@@ -174,9 +174,10 @@ RSpec.describe Gitlab::Ci::Config::External::File::Artifact do
context 'when job is provided as a variable' do
let(:variables) do
- Gitlab::Ci::Variables::Collection.new([
- { key: 'VAR1', value: 'a_secret_variable_value', masked: true }
- ])
+ Gitlab::Ci::Variables::Collection.new(
+ [
+ { key: 'VAR1', value: 'a_secret_variable_value', masked: true }
+ ])
end
let(:params) { { artifact: 'generated.yml', job: 'a_secret_variable_value' } }
diff --git a/spec/lib/gitlab/ci/config/external/file/project_spec.rb b/spec/lib/gitlab/ci/config/external/file/project_spec.rb
index 72a85c9b03d..0ba92d1e92d 100644
--- a/spec/lib/gitlab/ci/config/external/file/project_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/project_spec.rb
@@ -163,9 +163,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project do
context 'when non-existing project is used with a masked variable' do
let(:variables) do
- Gitlab::Ci::Variables::Collection.new([
- { key: 'VAR1', value: 'a_secret_variable_value', masked: true }
- ])
+ Gitlab::Ci::Variables::Collection.new([{ key: 'VAR1', value: 'a_secret_variable_value', masked: true }])
end
let(:params) do
@@ -180,9 +178,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project do
context 'when a project contained in an array is used with a masked variable' do
let(:variables) do
- Gitlab::Ci::Variables::Collection.new([
- { key: 'VAR1', value: 'a_secret_variable_value', masked: true }
- ])
+ Gitlab::Ci::Variables::Collection.new([{ key: 'VAR1', value: 'a_secret_variable_value', masked: true }])
end
let(:params) do
@@ -231,10 +227,11 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project do
context 'when project name and ref include masked variables' do
let(:variables) do
- Gitlab::Ci::Variables::Collection.new([
- { key: 'VAR1', value: 'a_secret_variable_value1', masked: true },
- { key: 'VAR2', value: 'a_secret_variable_value2', masked: true }
- ])
+ Gitlab::Ci::Variables::Collection.new(
+ [
+ { key: 'VAR1', value: 'a_secret_variable_value1', masked: true },
+ { key: 'VAR2', value: 'a_secret_variable_value2', masked: true }
+ ])
end
let(:params) { { project: 'a_secret_variable_value1', ref: 'a_secret_variable_value2', file: '/file.yml' } }
diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
index 9eaba12f388..e12f5dcee0a 100644
--- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
@@ -207,9 +207,9 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
context "when duplicate 'include's are defined" do
let(:values) do
{ include: [
- { 'local' => local_file },
- { 'local' => local_file }
- ],
+ { 'local' => local_file },
+ { 'local' => local_file }
+ ],
image: 'image:1.0' }
end
@@ -416,17 +416,18 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
context "when locations are same after masking variables" do
let(:variables) do
- Gitlab::Ci::Variables::Collection.new([
- { 'key' => 'GITLAB_TOKEN', 'value' => 'secret-file1', 'masked' => true },
- { 'key' => 'GITLAB_TOKEN', 'value' => 'secret-file2', 'masked' => true }
- ])
+ Gitlab::Ci::Variables::Collection.new(
+ [
+ { 'key' => 'GITLAB_TOKEN', 'value' => 'secret-file1', 'masked' => true },
+ { 'key' => 'GITLAB_TOKEN', 'value' => 'secret-file2', 'masked' => true }
+ ])
end
let(:values) do
{ include: [
- { 'local' => 'hello/secret-file1.yml' },
- { 'local' => 'hello/secret-file2.yml' }
- ],
+ { 'local' => 'hello/secret-file1.yml' },
+ { 'local' => 'hello/secret-file2.yml' }
+ ],
image: 'ruby:2.7' }
end
diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb
index 055114769ea..475503de7da 100644
--- a/spec/lib/gitlab/ci/config_spec.rb
+++ b/spec/lib/gitlab/ci/config_spec.rb
@@ -889,4 +889,31 @@ RSpec.describe Gitlab::Ci::Config do
it { is_expected.to eq([{ if: '$CI_COMMIT_REF_NAME == "master"' }]) }
end
+
+ describe '#workflow_name' do
+ subject(:workflow_name) { config.workflow_name }
+
+ let(:yml) do
+ <<-EOS
+ workflow:
+ name: 'Pipeline name'
+
+ rspec:
+ script: exit 0
+ EOS
+ end
+
+ it { is_expected.to eq('Pipeline name') }
+
+ context 'with no name' do
+ let(:yml) do
+ <<-EOS
+ rspec:
+ script: exit 0
+ EOS
+ end
+
+ it { is_expected.to be_nil }
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/jwt_v2_spec.rb b/spec/lib/gitlab/ci/jwt_v2_spec.rb
index 33aaa145a39..5eeab658a8e 100644
--- a/spec/lib/gitlab/ci/jwt_v2_spec.rb
+++ b/spec/lib/gitlab/ci/jwt_v2_spec.rb
@@ -7,6 +7,8 @@ RSpec.describe Gitlab::Ci::JwtV2 do
let(:project) { build_stubbed(:project, namespace: namespace) }
let(:user) { build_stubbed(:user) }
let(:pipeline) { build_stubbed(:ci_pipeline, ref: 'auto-deploy-2020-03-19') }
+ let(:aud) { described_class::DEFAULT_AUD }
+
let(:build) do
build_stubbed(
:ci_build,
@@ -16,7 +18,7 @@ RSpec.describe Gitlab::Ci::JwtV2 do
)
end
- subject(:ci_job_jwt_v2) { described_class.new(build, ttl: 30) }
+ subject(:ci_job_jwt_v2) { described_class.new(build, ttl: 30, aud: aud) }
it { is_expected.to be_a Gitlab::Ci::Jwt }
@@ -30,5 +32,13 @@ RSpec.describe Gitlab::Ci::JwtV2 do
expect(payload[:sub]).to eq("project_path:#{project.full_path}:ref_type:branch:ref:#{pipeline.source_ref}")
end
end
+
+ context 'when given an aud' do
+ let(:aud) { 'AWS' }
+
+ it 'uses that aud in the payload' do
+ expect(payload[:aud]).to eq('AWS')
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/lint_spec.rb b/spec/lib/gitlab/ci/lint_spec.rb
index 3d46d266c13..cf07e952f26 100644
--- a/spec/lib/gitlab/ci/lint_spec.rb
+++ b/spec/lib/gitlab/ci/lint_spec.rb
@@ -342,6 +342,7 @@ RSpec.describe Gitlab::Ci::Lint do
{
'count' => a_kind_of(Numeric),
'avg' => a_kind_of(Numeric),
+ 'sum' => a_kind_of(Numeric),
'max' => a_kind_of(Numeric),
'min' => a_kind_of(Numeric)
}
diff --git a/spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb
index 7222ebc3cb8..e12fa380209 100644
--- a/spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb
@@ -19,8 +19,7 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning do
it 'returns expected source data' do
is_expected.to have_attributes(
source_type: :dependency_scanning,
- data: property_data,
- fingerprint: '4dbcb747e6f0fb3ed4f48d96b777f1d64acdf43e459fdfefad404e55c004a188'
+ data: property_data
)
end
end
diff --git a/spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb
index c54a3268bbe..f58a463f047 100644
--- a/spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb
@@ -72,12 +72,13 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::Validators::CyclonedxSchemaValidator d
it { is_expected.not_to be_valid }
it "outputs errors for each validation failure" do
- expect(validator.errors).to match_array([
- "property '/components/0' is missing required keys: name",
- "property '/components/0/type' is not one of: [\"application\", \"framework\"," \
- " \"library\", \"container\", \"operating-system\", \"device\", \"firmware\", \"file\"]",
- "property '/components/1' is missing required keys: type"
- ])
+ expect(validator.errors).to match_array(
+ [
+ "property '/components/0' is missing required keys: name",
+ "property '/components/0/type' is not one of: [\"application\", \"framework\"," \
+ " \"library\", \"container\", \"operating-system\", \"device\", \"firmware\", \"file\"]",
+ "property '/components/1' is missing required keys: type"
+ ])
end
end
end
@@ -121,10 +122,11 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::Validators::CyclonedxSchemaValidator d
it { is_expected.not_to be_valid }
it "outputs errors for each validation failure" do
- expect(validator.errors).to match_array([
- "property '/metadata/properties/0/name' is not of type: string",
- "property '/metadata/properties/0/value' is not of type: string"
- ])
+ expect(validator.errors).to match_array(
+ [
+ "property '/metadata/properties/0/name' is not of type: string",
+ "property '/metadata/properties/0/value' is not of type: string"
+ ])
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 297ef1f5bb9..7dbad354e4c 100644
--- a/spec/lib/gitlab/ci/parsers/security/common_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/security/common_spec.rb
@@ -54,24 +54,15 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
context 'when the validate flag is set to `false`' do
let(:validate) { false }
- let(:valid?) { false }
- let(:errors) { ['foo'] }
- let(:warnings) { ['bar'] }
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(errors)
- allow(instance).to receive(:warnings).and_return(warnings)
- end
-
allow(parser).to receive_messages(create_scanner: true, create_scan: true)
end
- it 'instantiates the validator with correct params' do
+ it 'does not instantiate the validator' do
parse_report
- expect(validator_class).to have_received(:new).with(
+ expect(validator_class).not_to have_received(:new).with(
report.type,
data.deep_stringify_keys,
report.version,
@@ -80,43 +71,17 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
)
end
- context 'when the report data is not valid according to the schema' do
- it 'adds warnings to the report' do
- expect { parse_report }.to change { report.warnings }.from([]).to(
- [
- { message: 'foo', type: 'Schema' },
- { message: 'bar', type: 'Schema' }
- ]
- )
- end
-
- it 'keeps the execution flow as normal' do
- parse_report
+ it 'marks the report as valid' do
+ parse_report
- expect(parser).to have_received(:create_scanner)
- expect(parser).to have_received(:create_scan)
- end
+ expect(report).not_to be_errored
end
- context 'when the report data is valid according to the schema' do
- let(:valid?) { true }
- let(:errors) { [] }
- let(:warnings) { [] }
-
- it 'does not add errors to the report' do
- expect { parse_report }.not_to change { report.errors }
- end
-
- it 'does not add warnings to the report' do
- expect { parse_report }.not_to change { report.warnings }
- end
-
- it 'keeps the execution flow as normal' do
- parse_report
+ 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
+ expect(parser).to have_received(:create_scanner)
+ expect(parser).to have_received(:create_scan)
end
end
@@ -152,12 +117,17 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
it 'adds errors to the report' do
expect { parse_report }.to change { report.errors }.from([]).to(
[
- { message: 'foo', type: 'Schema' },
- { message: 'bar', type: 'Schema' }
+ { message: 'foo', type: 'Schema' }
]
)
end
+ it 'marks the report as invalid' do
+ parse_report
+
+ expect(report).to be_errored
+ end
+
it 'does not try to create report entities' do
parse_report
@@ -175,8 +145,24 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
expect { parse_report }.not_to change { report.errors }.from([])
end
- it 'does not add warnings to the report' do
- expect { parse_report }.not_to change { report.warnings }.from([])
+ context 'and no warnings are present' do
+ let(:warnings) { [] }
+
+ it 'does not add warnings to the report' do
+ expect { parse_report }.not_to change { report.warnings }.from([])
+ end
+ end
+
+ context 'and some warnings are present' do
+ let(:warnings) { ['bar'] }
+
+ it 'does add warnings to the report' do
+ expect { parse_report }.to change { report.warnings }.from([]).to(
+ [
+ { message: 'bar', type: 'Schema' }
+ ]
+ )
+ end
end
it 'keeps the execution flow as normal' do
@@ -298,8 +284,8 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
scans = report.findings.map(&:scan)
expect(scans.map(&:status).all?('success')).to be(true)
- expect(scans.map(&:start_time).all?('placeholder-value')).to be(true)
- expect(scans.map(&:end_time).all?('placeholder-value')).to be(true)
+ expect(scans.map(&:start_time).all?('2022-08-10T21:37:00')).to be(true)
+ expect(scans.map(&:end_time).all?('2022-08-10T21:38:00')).to be(true)
expect(scans.size).to eq(7)
expect(scans.first).to be_a(::Gitlab::Ci::Reports::Security::Scan)
end
@@ -418,11 +404,11 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
{
'type' => 'source',
'items' => [
- 'signatures' => [
- { 'algorithm' => 'hash', 'value' => 'hash_value' },
- { 'algorithm' => 'location', 'value' => 'location_value' },
- { 'algorithm' => 'scope_offset', 'value' => 'scope_offset_value' }
- ]
+ 'signatures' => [
+ { 'algorithm' => 'hash', 'value' => 'hash_value' },
+ { 'algorithm' => 'location', 'value' => 'location_value' },
+ { 'algorithm' => 'scope_offset', 'value' => 'scope_offset_value' }
+ ]
]
}
end
@@ -440,11 +426,11 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
{
'type' => 'source',
'items' => [
- 'signatures' => [
- { 'algorithm' => 'hash', 'value' => 'hash_value' },
- { 'algorithm' => 'location', 'value' => 'location_value' },
- { 'algorithm' => 'INVALID', 'value' => 'scope_offset_value' }
- ]
+ 'signatures' => [
+ { 'algorithm' => 'hash', 'value' => 'hash_value' },
+ { 'algorithm' => 'location', 'value' => 'location_value' },
+ { 'algorithm' => 'INVALID', 'value' => 'scope_offset_value' }
+ ]
]
}
end
diff --git a/spec/lib/gitlab/ci/parsers/security/sast_spec.rb b/spec/lib/gitlab/ci/parsers/security/sast_spec.rb
index 4bc48f6611a..f6113308201 100644
--- a/spec/lib/gitlab/ci/parsers/security/sast_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/security/sast_spec.rb
@@ -10,24 +10,39 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Sast do
let(:created_at) { 2.weeks.ago }
- context "when parsing valid reports" do
- where(:report_format, :report_version, :scanner_length, :finding_length, :identifier_length, :file_path, :line) do
- :sast | '14.0.0' | 1 | 5 | 6 | 'groovy/src/main/java/com/gitlab/security_products/tests/App.groovy' | 47
- :sast_deprecated | '1.2' | 3 | 33 | 17 | 'python/hardcoded/hardcoded-tmp.py' | 1
+ context "when passing valid report" do
+ # rubocop: disable Layout/LineLength
+ where(:report_format, :report_version, :scanner_length, :finding_length, :identifier_length, :file_path, :start_line, :end_line, :primary_identifiers_length) do
+ :sast | '14.0.0' | 1 | 5 | 6 | 'groovy/src/main/java/com/gitlab/security_products/tests/App.groovy' | 47 | 47 | nil
+ :sast_semgrep_for_multiple_findings | '14.0.4' | 1 | 2 | 6 | 'app/app.py' | 39 | nil | 2
end
+ # rubocop: enable Layout/LineLength
with_them do
- let(:report) { Gitlab::Ci::Reports::Security::Report.new(artifact.file_type, pipeline, created_at) }
+ let(:report) do
+ Gitlab::Ci::Reports::Security::Report.new(
+ artifact.file_type,
+ pipeline,
+ created_at
+ )
+ end
+
let(:artifact) { create(:ci_job_artifact, report_format) }
before do
- artifact.each_blob { |blob| described_class.parse!(blob, report) }
+ artifact.each_blob { |blob| described_class.parse!(blob, report, validate: true) }
end
it "parses all identifiers and findings" do
expect(report.findings.length).to eq(finding_length)
expect(report.identifiers.length).to eq(identifier_length)
expect(report.scanners.length).to eq(scanner_length)
+
+ if primary_identifiers_length
+ expect(
+ report.scanners.each_value.first.primary_identifiers.length
+ ).to eq(primary_identifiers_length)
+ end
end
it 'generates expected location' do
@@ -36,8 +51,8 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Sast do
expect(location).to be_a(::Gitlab::Ci::Reports::Security::Locations::Sast)
expect(location).to have_attributes(
file_path: file_path,
- end_line: line,
- start_line: line
+ end_line: end_line,
+ start_line: start_line
)
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb
index b570f2a7f75..fc3de2a14cd 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb
@@ -44,6 +44,20 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::CancelPendingPipelines do
expect(build_statuses(pipeline)).to contain_exactly('pending')
end
+ it 'logs canceled pipelines' do
+ allow(Gitlab::AppLogger).to receive(:info)
+
+ perform
+
+ expect(Gitlab::AppLogger).to have_received(:info).with(
+ class: described_class.name,
+ message: "Pipeline #{pipeline.id} auto-canceling pipeline #{prev_pipeline.id}",
+ canceled_pipeline_id: prev_pipeline.id,
+ canceled_by_pipeline_id: pipeline.id,
+ canceled_by_pipeline_source: pipeline.source
+ )
+ end
+
it 'cancels the builds with 2 queries to avoid query timeout' do
second_query_regex = /WHERE "ci_pipelines"\."id" = \d+ AND \(NOT EXISTS/
recorder = ActiveRecord::QueryRecorder.new { perform }
@@ -141,7 +155,42 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::CancelPendingPipelines do
end
end
- context 'when the prev pipeline source is webide' do
+ context 'when the pipeline is a child pipeline' do
+ let!(:parent_pipeline) { create(:ci_pipeline, project: project, sha: new_commit.sha) }
+ let(:pipeline) { create(:ci_pipeline, child_of: parent_pipeline) }
+
+ before do
+ create(:ci_build, :interruptible, :running, pipeline: parent_pipeline)
+ create(:ci_build, :interruptible, :running, pipeline: parent_pipeline)
+ end
+
+ it 'does not cancel any builds' do
+ expect(build_statuses(prev_pipeline)).to contain_exactly('running', 'success', 'created')
+ expect(build_statuses(parent_pipeline)).to contain_exactly('running', 'running')
+
+ perform
+
+ expect(build_statuses(prev_pipeline)).to contain_exactly('running', 'success', 'created')
+ expect(build_statuses(parent_pipeline)).to contain_exactly('running', 'running')
+ end
+
+ context 'when feature flag ci_skip_auto_cancelation_on_child_pipelines is disabled' do
+ before do
+ stub_feature_flags(ci_skip_auto_cancelation_on_child_pipelines: false)
+ end
+
+ it 'does not cancel the parent pipeline' do
+ expect(build_statuses(parent_pipeline)).to contain_exactly('running', 'running')
+
+ perform
+
+ expect(build_statuses(prev_pipeline)).to contain_exactly('success', 'canceled', 'canceled')
+ expect(build_statuses(parent_pipeline)).to contain_exactly('running', 'running')
+ end
+ end
+ end
+
+ context 'when the previous pipeline source is webide' do
let(:prev_pipeline) { create(:ci_pipeline, :webide, project: project) }
it 'does not cancel builds of the previous pipeline' do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb
index f451bd6bfef..e0d656f456e 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb
@@ -11,9 +11,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Config::Content do
subject { described_class.new(pipeline, command) }
- # TODO: change this to `describe` and remove rubocop-disable
- # when removing the FF ci_project_pipeline_config_refactoring
- shared_context '#perform!' do # rubocop:disable RSpec/ContextWording
+ describe '#perform!' do
context 'when bridge job is passed in as parameter' do
let(:ci_config_path) { nil }
let(:bridge) { create(:ci_bridge) }
@@ -203,14 +201,4 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Config::Content do
end
end
end
-
- it_behaves_like '#perform!'
-
- context 'when the FF ci_project_pipeline_config_refactoring is disabled' do
- before do
- stub_feature_flags(ci_project_pipeline_config_refactoring: false)
- end
-
- it_behaves_like '#perform!'
- end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/limit/active_jobs_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/limit/active_jobs_spec.rb
new file mode 100644
index 00000000000..bc453f1502b
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/chain/limit/active_jobs_spec.rb
@@ -0,0 +1,97 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Gitlab::Ci::Pipeline::Chain::Limit::ActiveJobs do
+ let_it_be(:namespace) { create(:namespace) }
+ let_it_be(:project) { create(:project, namespace: namespace) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:default_plan) { create(:default_plan) }
+
+ let(:command) do
+ instance_double(
+ ::Gitlab::Ci::Pipeline::Chain::Command,
+ project: project,
+ current_user: user,
+ save_incompleted: true,
+ pipeline_seed: pipeline_seed_double
+ )
+ end
+
+ let(:pipeline_seed_double) do
+ instance_double(::Gitlab::Ci::Pipeline::Seed::Pipeline, size: 5)
+ end
+
+ let(:pipeline) do
+ create(:ci_pipeline, project: project)
+ end
+
+ let(:existing_pipeline) { create(:ci_pipeline, project: project) }
+ let(:step) { described_class.new(pipeline, command) }
+ let(:limit) { 10 }
+
+ subject { step.perform! }
+
+ before do
+ create(:plan_limits, plan: default_plan, ci_active_jobs: limit)
+ namespace.clear_memoization(:actual_plan)
+ end
+
+ shared_examples 'successful step' do
+ it 'doest not fail the pipeline and does not interrupt the chain' do
+ subject
+
+ expect(pipeline).not_to be_failed
+ expect(step).not_to be_break
+ end
+ end
+
+ context 'when active jobs limit is exceeded' do
+ before do
+ create_list(:ci_build, 3, pipeline: existing_pipeline)
+ create_list(:ci_bridge, 3, pipeline: existing_pipeline)
+ end
+
+ it 'fails the pipeline with an error', :aggregate_failures do
+ subject
+
+ expect(pipeline).to be_failed
+ expect(pipeline).to be_job_activity_limit_exceeded
+ expect(pipeline.errors.full_messages).to include(described_class::MESSAGE)
+ end
+
+ it 'logs the failure' do
+ allow(Gitlab::AppLogger).to receive(:info)
+
+ subject
+
+ expect(Gitlab::AppLogger).to have_received(:info).with(
+ class: described_class.name,
+ message: described_class::MESSAGE,
+ project_id: project.id,
+ plan: default_plan.name
+ )
+ end
+
+ it 'breaks the chain' do
+ subject
+
+ expect(step).to be_break
+ end
+
+ context 'when active jobs limit not enabled' do
+ let(:limit) { 0 }
+
+ it_behaves_like 'successful step'
+ end
+ end
+
+ context 'when active jobs limit is not exceeded' do
+ before do
+ create_list(:ci_build, 3, pipeline: existing_pipeline)
+ create_list(:ci_bridge, 1, pipeline: existing_pipeline)
+ end
+
+ it_behaves_like 'successful step'
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb
index 62de4d2e96d..51d1661b586 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb
@@ -236,4 +236,47 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Populate do
end
end
end
+
+ context 'with pipeline name' do
+ let(:config) do
+ { workflow: { name: ' Pipeline name ' }, rspec: { script: 'rspec' } }
+ end
+
+ context 'with feature flag disabled' do
+ before do
+ stub_feature_flags(pipeline_name: false)
+ end
+
+ it 'does not build pipeline_metadata' do
+ run_chain
+
+ expect(pipeline.pipeline_metadata).to be_nil
+ end
+ end
+
+ context 'with feature flag enabled' do
+ before do
+ stub_feature_flags(pipeline_name: true)
+ end
+
+ it 'builds pipeline_metadata' do
+ run_chain
+
+ expect(pipeline.pipeline_metadata.title).to eq('Pipeline name')
+ expect(pipeline.pipeline_metadata.project).to eq(pipeline.project)
+ end
+
+ context 'with empty name' do
+ let(:config) do
+ { workflow: { name: ' ' }, rspec: { script: 'rspec' } }
+ end
+
+ it 'strips whitespace from name' do
+ run_chain
+
+ expect(pipeline.pipeline_metadata).to be_nil
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
index 8c4f7af0ef4..323bab89e6a 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
@@ -68,8 +68,10 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
end
context 'when refs policy is specified' do
+ let(:tag_name) { project.repository.tags.first.name }
+
let(:pipeline) do
- build(:ci_pipeline, project: project, ref: 'feature', tag: true)
+ build(:ci_pipeline, project: project, ref: tag_name, tag: true)
end
let(:config) do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
index ee32661f267..c69aa661b05 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
@@ -100,19 +100,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Sequence do
expect(histogram).to have_received(:observe)
.with(hash_including(plan: project.actual_plan_name), 4)
end
-
- context 'when feature flag ci_limit_active_jobs_early is disabled' do
- before do
- stub_feature_flags(ci_limit_active_jobs_early: false)
- end
-
- it 'counts all the active builds' do
- subject.build!
-
- expect(histogram).to have_received(:observe)
- .with(hash_including(plan: project.actual_plan_name), 3)
- end
- end
end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/duration_spec.rb b/spec/lib/gitlab/ci/pipeline/duration_spec.rb
index 46c7072ad8e..36714413da6 100644
--- a/spec/lib/gitlab/ci/pipeline/duration_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/duration_spec.rb
@@ -1,117 +1,187 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Duration do
- let(:calculated_duration) { calculate(data) }
+ describe '.from_periods' do
+ let(:calculated_duration) { calculate(data) }
- shared_examples 'calculating duration' do
- it do
- expect(calculated_duration).to eq(duration)
+ shared_examples 'calculating duration' do
+ it do
+ expect(calculated_duration).to eq(duration)
+ end
end
- end
- context 'test sample A' do
- let(:data) do
- [[0, 1],
- [1, 2],
- [3, 4],
- [5, 6]]
+ context 'test sample A' do
+ let(:data) do
+ [[0, 1],
+ [1, 2],
+ [3, 4],
+ [5, 6]]
+ end
+
+ let(:duration) { 4 }
+
+ it_behaves_like 'calculating duration'
end
- let(:duration) { 4 }
+ context 'test sample B' do
+ let(:data) do
+ [[0, 1],
+ [1, 2],
+ [2, 3],
+ [3, 4],
+ [0, 4]]
+ end
- it_behaves_like 'calculating duration'
- end
+ let(:duration) { 4 }
- context 'test sample B' do
- let(:data) do
- [[0, 1],
- [1, 2],
- [2, 3],
- [3, 4],
- [0, 4]]
+ it_behaves_like 'calculating duration'
end
- let(:duration) { 4 }
+ context 'test sample C' do
+ let(:data) do
+ [[0, 4],
+ [2, 6],
+ [5, 7],
+ [8, 9]]
+ end
- it_behaves_like 'calculating duration'
- end
+ let(:duration) { 8 }
- context 'test sample C' do
- let(:data) do
- [[0, 4],
- [2, 6],
- [5, 7],
- [8, 9]]
+ it_behaves_like 'calculating duration'
end
- let(:duration) { 8 }
+ context 'test sample D' do
+ let(:data) do
+ [[0, 1],
+ [2, 3],
+ [4, 5],
+ [6, 7]]
+ end
- it_behaves_like 'calculating duration'
- end
+ let(:duration) { 4 }
+
+ it_behaves_like 'calculating duration'
+ end
- context 'test sample D' do
- let(:data) do
- [[0, 1],
- [2, 3],
- [4, 5],
- [6, 7]]
+ context 'test sample E' do
+ let(:data) do
+ [[0, 1],
+ [3, 9],
+ [3, 4],
+ [3, 5],
+ [3, 8],
+ [4, 5],
+ [4, 7],
+ [5, 8]]
+ end
+
+ let(:duration) { 7 }
+
+ it_behaves_like 'calculating duration'
end
- let(:duration) { 4 }
+ context 'test sample F' do
+ let(:data) do
+ [[1, 3],
+ [2, 4],
+ [2, 4],
+ [2, 4],
+ [5, 8]]
+ end
- it_behaves_like 'calculating duration'
- end
+ let(:duration) { 6 }
- context 'test sample E' do
- let(:data) do
- [[0, 1],
- [3, 9],
- [3, 4],
- [3, 5],
- [3, 8],
- [4, 5],
- [4, 7],
- [5, 8]]
+ it_behaves_like 'calculating duration'
end
- let(:duration) { 7 }
+ context 'test sample G' do
+ let(:data) do
+ [[1, 3],
+ [2, 4],
+ [6, 7]]
+ end
- it_behaves_like 'calculating duration'
- end
+ let(:duration) { 4 }
- context 'test sample F' do
- let(:data) do
- [[1, 3],
- [2, 4],
- [2, 4],
- [2, 4],
- [5, 8]]
+ it_behaves_like 'calculating duration'
end
- let(:duration) { 6 }
+ def calculate(data)
+ periods = data.shuffle.map do |(first, last)|
+ described_class::Period.new(first, last)
+ end
- it_behaves_like 'calculating duration'
+ described_class.from_periods(periods.sort_by(&:first))
+ end
end
- context 'test sample G' do
- let(:data) do
- [[1, 3],
- [2, 4],
- [6, 7]]
+ describe '.from_pipeline' do
+ let_it_be(:start_time) { Time.current.change(usec: 0) }
+ let_it_be(:current) { start_time + 1000 }
+ let_it_be(:pipeline) { create(:ci_pipeline) }
+ let_it_be(:success_build) { create_build(:success, started_at: start_time, finished_at: start_time + 60) }
+ let_it_be(:failed_build) { create_build(:failed, started_at: start_time + 60, finished_at: start_time + 120) }
+ let_it_be(:canceled_build) { create_build(:canceled, started_at: start_time + 120, finished_at: start_time + 180) }
+ let_it_be(:skipped_build) { create_build(:skipped, started_at: start_time) }
+ let_it_be(:pending_build) { create_build(:pending) }
+ let_it_be(:created_build) { create_build(:created) }
+ let_it_be(:preparing_build) { create_build(:preparing) }
+ let_it_be(:scheduled_build) { create_build(:scheduled) }
+ let_it_be(:expired_scheduled_build) { create_build(:expired_scheduled) }
+ let_it_be(:manual_build) { create_build(:manual) }
+
+ let!(:running_build) { create_build(:running, started_at: start_time) }
+
+ it 'returns the duration of the running build' do
+ travel_to(current) do
+ expect(described_class.from_pipeline(pipeline)).to eq 1000.seconds
+ end
end
- let(:duration) { 4 }
+ context 'when there is no running build' do
+ let(:running_build) { nil }
- it_behaves_like 'calculating duration'
- end
+ it 'returns the duration for all the builds' do
+ travel_to(current) do
+ expect(described_class.from_pipeline(pipeline)).to eq 180.seconds
+ end
+ end
+ end
- def calculate(data)
- periods = data.shuffle.map do |(first, last)|
- described_class::Period.new(first, last)
+ context 'when there are bridge jobs' do
+ let!(:success_bridge) { create_bridge(:success, started_at: start_time + 220, finished_at: start_time + 280) }
+ let!(:failed_bridge) { create_bridge(:failed, started_at: start_time + 180, finished_at: start_time + 240) }
+ let!(:skipped_bridge) { create_bridge(:skipped, started_at: start_time) }
+ let!(:created_bridge) { create_bridge(:created) }
+ let!(:manual_bridge) { create_bridge(:manual) }
+
+ it 'returns the duration of the running build' do
+ travel_to(current) do
+ expect(described_class.from_pipeline(pipeline)).to eq 1000.seconds
+ end
+ end
+
+ context 'when there is no running build' do
+ let!(:running_build) { nil }
+
+ it 'returns the duration for all the builds and bridge jobs' do
+ travel_to(current) do
+ expect(described_class.from_pipeline(pipeline)).to eq 280.seconds
+ end
+ end
+ end
end
- described_class.from_periods(periods.sort_by(&:first))
+ private
+
+ def create_build(trait, **opts)
+ create(:ci_build, trait, pipeline: pipeline, **opts)
+ end
+
+ def create_bridge(trait, **opts)
+ create(:ci_bridge, trait, pipeline: pipeline, **opts)
+ end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/logger_spec.rb b/spec/lib/gitlab/ci/pipeline/logger_spec.rb
index f31361431f2..3af0ebe7484 100644
--- a/spec/lib/gitlab/ci/pipeline/logger_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/logger_spec.rb
@@ -25,6 +25,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
loggable_data = {
'expensive_operation_duration_s' => {
'count' => 1,
+ 'sum' => a_kind_of(Numeric),
'avg' => a_kind_of(Numeric),
'max' => a_kind_of(Numeric),
'min' => a_kind_of(Numeric)
@@ -62,6 +63,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
accumulator[key] = {
'count' => count,
'avg' => a_kind_of(Numeric),
+ 'sum' => a_kind_of(Numeric),
'max' => a_kind_of(Numeric),
'min' => a_kind_of(Numeric)
}
@@ -71,6 +73,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
data['expensive_operation_db_count']['max'] = db_count
data['expensive_operation_db_count']['min'] = db_count
data['expensive_operation_db_count']['avg'] = db_count
+ data['expensive_operation_db_count']['sum'] = count * db_count
end
data
@@ -131,7 +134,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
it 'records durations of observed operations' do
loggable_data = {
'pipeline_creation_duration_s' => {
- 'avg' => 30, 'count' => 1, 'max' => 30, 'min' => 30
+ 'avg' => 30, 'sum' => 30, 'count' => 1, 'max' => 30, 'min' => 30
}
}
@@ -165,10 +168,10 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
'pipeline_creation_caller' => 'source',
'pipeline_source' => pipeline.source,
'pipeline_save_duration_s' => {
- 'avg' => 60, 'count' => 1, 'max' => 60, 'min' => 60
+ 'avg' => 60, 'sum' => 60, 'count' => 1, 'max' => 60, 'min' => 60
},
'pipeline_creation_duration_s' => {
- 'avg' => 20, 'count' => 2, 'max' => 30, 'min' => 10
+ 'avg' => 20, 'sum' => 40, 'count' => 2, 'max' => 30, 'min' => 10
}
}
end
@@ -215,10 +218,10 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
'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
+ 'avg' => 60, 'sum' => 60, 'count' => 1, 'max' => 60, 'min' => 60
},
'pipeline_creation_duration_s' => {
- 'avg' => 20, 'count' => 2, 'max' => 30, 'min' => 10
+ 'avg' => 20, 'sum' => 40, 'count' => 2, 'max' => 30, 'min' => 10
}
}
end
diff --git a/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb b/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb
index e289e59b281..effa2c43418 100644
--- a/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb
+++ b/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb
@@ -191,11 +191,12 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
end
it 'includes the base report errors sorted by severity' do
- expect(existing_errors).to eq([
- blocker_degradation,
- critical_degradation,
- major_degradation
- ])
+ expect(existing_errors).to eq(
+ [
+ blocker_degradation,
+ critical_degradation,
+ major_degradation
+ ])
end
end
@@ -242,11 +243,12 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
end
it 'includes errors not found in the base report sorted by severity' do
- expect(new_errors).to eq([
- blocker_degradation,
- critical_degradation,
- minor_degradation
- ])
+ expect(new_errors).to eq(
+ [
+ blocker_degradation,
+ critical_degradation,
+ minor_degradation
+ ])
end
end
@@ -304,11 +306,12 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
end
it 'returns the base report errors not found in the head report, sorted by severity' do
- expect(resolved_errors).to eq([
- blocker_degradation,
- critical_degradation,
- minor_degradation
- ])
+ expect(resolved_errors).to eq(
+ [
+ blocker_degradation,
+ critical_degradation,
+ minor_degradation
+ ])
end
end
diff --git a/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb b/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb
index f4b47893805..68e70525c55 100644
--- a/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb
+++ b/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb
@@ -103,15 +103,16 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReports do
end
it 'sorts degradations based on severity' do
- expect(codequality_report.degradations.values).to eq([
- blocker,
- critical,
- major,
- major_2,
- minor,
- info,
- unknown
- ])
+ expect(codequality_report.degradations.values).to eq(
+ [
+ blocker,
+ critical,
+ major,
+ major_2,
+ minor,
+ info,
+ unknown
+ ])
end
context 'with non-existence and uppercase severities' do
@@ -126,12 +127,13 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReports do
end
it 'sorts unknown last' do
- expect(other_report.degradations.values).to eq([
- blocker,
- uppercase_major,
- minor,
- non_existent
- ])
+ expect(other_report.degradations.values).to eq(
+ [
+ blocker,
+ uppercase_major,
+ minor,
+ non_existent
+ ])
end
end
end
diff --git a/spec/lib/gitlab/ci/reports/sbom/source_spec.rb b/spec/lib/gitlab/ci/reports/sbom/source_spec.rb
index cb30bd721dd..343c0d8c15c 100644
--- a/spec/lib/gitlab/ci/reports/sbom/source_spec.rb
+++ b/spec/lib/gitlab/ci/reports/sbom/source_spec.rb
@@ -12,8 +12,7 @@ RSpec.describe Gitlab::Ci::Reports::Sbom::Source do
'source_file' => { 'path' => 'package.json' },
'package_manager' => { 'name' => 'npm' },
'language' => { 'name' => 'JavaScript' }
- },
- fingerprint: '4dbcb747e6f0fb3ed4f48d96b777f1d64acdf43e459fdfefad404e55c004a188'
+ }
}
end
@@ -22,8 +21,7 @@ RSpec.describe Gitlab::Ci::Reports::Sbom::Source do
it 'has correct attributes' do
expect(subject).to have_attributes(
source_type: attributes[:type],
- data: attributes[:data],
- fingerprint: attributes[:fingerprint]
+ data: attributes[:data]
)
end
end
diff --git a/spec/lib/gitlab/ci/reports/security/report_spec.rb b/spec/lib/gitlab/ci/reports/security/report_spec.rb
index ab0efb90901..d7f967f1c55 100644
--- a/spec/lib/gitlab/ci/reports/security/report_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/report_spec.rb
@@ -140,6 +140,24 @@ RSpec.describe Gitlab::Ci::Reports::Security::Report do
it { is_expected.to eq(scanner_1) }
end
+ describe '#primary_identifiers' do
+ it 'returns matching identifiers' do
+ scanner_with_identifiers = create(
+ :ci_reports_security_scanner,
+ external_id: 'external_id_1',
+ primary_identifiers: [create(:ci_reports_security_identifier, external_id: 'other_id', name: 'other_scanner')]
+ )
+ scanner_without_identifiers = create(
+ :ci_reports_security_scanner,
+ external_id: 'external_id_2')
+
+ report.add_scanner(scanner_with_identifiers)
+ report.add_scanner(scanner_without_identifiers)
+
+ expect(report.primary_identifiers).to eq(scanner_with_identifiers.primary_identifiers)
+ end
+ end
+
describe '#add_error' do
context 'when the message is not given' do
it 'adds a new error to report with the generic error message' do
diff --git a/spec/lib/gitlab/ci/secure_files/cer_spec.rb b/spec/lib/gitlab/ci/secure_files/cer_spec.rb
new file mode 100644
index 00000000000..6b9cd0e3bfc
--- /dev/null
+++ b/spec/lib/gitlab/ci/secure_files/cer_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::SecureFiles::Cer do
+ context 'when the supplied certificate cannot be parsed' do
+ let(:invalid_certificate) { described_class.new('xyzabc') }
+
+ describe '#certificate_data' do
+ it 'assigns the error message and returns nil' do
+ expect(invalid_certificate.certificate_data).to be nil
+ expect(invalid_certificate.error).to eq('not enough data')
+ end
+ end
+
+ describe '#metadata' do
+ it 'returns an empty hash' do
+ expect(invalid_certificate.metadata).to eq({})
+ end
+ end
+
+ describe '#expires_at' do
+ it 'returns nil' do
+ expect(invalid_certificate.metadata[:expires_at]).to be_nil
+ end
+ end
+ end
+
+ context 'when the supplied certificate can be parsed' do
+ let(:sample_file) { fixture_file('ci_secure_files/sample.cer') }
+ let(:subject) { described_class.new(sample_file) }
+
+ describe '#certificate_data' do
+ it 'returns an OpenSSL::X509::Certificate object' do
+ expect(subject.certificate_data.class).to be(OpenSSL::X509::Certificate)
+ end
+ end
+
+ describe '#metadata' do
+ it 'returns a hash with the expected keys' do
+ expect(subject.metadata.keys).to match_array([:issuer, :subject, :id, :expires_at])
+ end
+ end
+
+ describe '#id' do
+ it 'returns the certificate serial number' do
+ expect(subject.metadata[:id]).to eq('33669367788748363528491290218354043267')
+ end
+ end
+
+ describe '#expires_at' do
+ it 'returns the certificate expiration timestamp' do
+ expect(subject.metadata[:expires_at]).to eq('2022-04-26 19:20:40 UTC')
+ end
+ end
+
+ describe '#issuer' do
+ it 'calls parse on X509Name' do
+ expect(subject.metadata[:issuer]["O"]).to eq('Apple Inc.')
+ end
+ end
+
+ describe '#subject' do
+ it 'calls parse on X509Name' do
+ expect(subject.metadata[:subject]["OU"]).to eq('N7SYAN8PX8')
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/secure_files/mobile_provision_spec.rb b/spec/lib/gitlab/ci/secure_files/mobile_provision_spec.rb
new file mode 100644
index 00000000000..fb382174c64
--- /dev/null
+++ b/spec/lib/gitlab/ci/secure_files/mobile_provision_spec.rb
@@ -0,0 +1,149 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::SecureFiles::MobileProvision do
+ context 'when the supplied profile cannot be parsed' do
+ context 'when the supplied certificate cannot be parsed' do
+ let(:invalid_profile) { described_class.new('xyzabc') }
+
+ describe '#decoded_plist' do
+ it 'assigns the error message and returns nil' do
+ expect(invalid_profile.decoded_plist).to be nil
+ expect(invalid_profile.error).to eq('Could not parse the PKCS7: not enough data')
+ end
+ end
+
+ describe '#properties' do
+ it 'returns nil' do
+ expect(invalid_profile.properties).to be_nil
+ end
+ end
+
+ describe '#metadata' do
+ it 'returns an empty hash' do
+ expect(invalid_profile.metadata).to eq({})
+ end
+ end
+
+ describe '#expires_at' do
+ it 'returns nil' do
+ expect(invalid_profile.metadata[:expires_at]).to be_nil
+ end
+ end
+ end
+ end
+
+ context 'when the supplied profile can be parsed' do
+ let(:sample_file) { fixture_file('ci_secure_files/sample.mobileprovision') }
+ let(:subject) { described_class.new(sample_file) }
+
+ describe '#decoded_plist' do
+ it 'returns an XML string' do
+ expect(subject.decoded_plist.class).to be(String)
+ expect(subject.decoded_plist.starts_with?('<?xml version="1.0"')).to be true
+ end
+ end
+
+ describe '#properties' do
+ it 'returns the property list of the decoded plist provided' do
+ expect(subject.properties.class).to be(Hash)
+ expect(subject.properties.keys).to match_array(%w[AppIDName ApplicationIdentifierPrefix CreationDate
+ Platform IsXcodeManaged DeveloperCertificates
+ DER-Encoded-Profile PPQCheck Entitlements ExpirationDate
+ Name ProvisionedDevices TeamIdentifier TeamName
+ TimeToLive UUID Version])
+ end
+
+ it 'returns nil if the property list fails to be parsed from the decoded plist' do
+ allow(subject).to receive(:decoded_plist).and_return('foo/bar')
+ expect(subject.properties).to be nil
+ expect(subject.error).to start_with('invalid XML')
+ end
+ end
+
+ describe '#metadata' do
+ it 'returns a hash with the expected keys' do
+ expect(subject.metadata.keys).to match_array([:id, :expires_at, :app_id, :app_id_prefix, :app_name,
+ :certificate_ids, :devices, :entitlements, :platforms,
+ :team_id, :team_name, :xcode_managed])
+ end
+ end
+
+ describe '#id' do
+ it 'returns the profile UUID' do
+ expect(subject.metadata[:id]).to eq('6b9fcce1-b9a9-4b37-b2ce-ec4da2044abf')
+ end
+ end
+
+ describe '#expires_at' do
+ it 'returns the expiration timestamp of the profile' do
+ expect(subject.metadata[:expires_at].utc).to eq('2023-08-01 23:15:13 UTC')
+ end
+ end
+
+ describe '#platforms' do
+ it 'returns the platforms assigned to the profile' do
+ expect(subject.metadata[:platforms]).to match_array(['iOS'])
+ end
+ end
+
+ describe '#team_name' do
+ it 'returns the team name in the profile' do
+ expect(subject.metadata[:team_name]).to eq('Darby Frey')
+ end
+ end
+
+ describe '#team_id' do
+ it 'returns the team ids in the profile' do
+ expect(subject.metadata[:team_id]).to match_array(['N7SYAN8PX8'])
+ end
+ end
+
+ describe '#app_name' do
+ it 'returns the app name in the profile' do
+ expect(subject.metadata[:app_name]).to eq('iOS Demo')
+ end
+ end
+
+ describe '#app_id' do
+ it 'returns the app id in the profile' do
+ expect(subject.metadata[:app_id]).to eq('match Development com.gitlab.ios-demo')
+ end
+ end
+
+ describe '#app_id_prefix' do
+ it 'returns the app id prefixes in the profile' do
+ expect(subject.metadata[:app_id_prefix]).to match_array(['N7SYAN8PX8'])
+ end
+ end
+
+ describe '#xcode_managed' do
+ it 'returns the xcode_managed property in the profile' do
+ expect(subject.metadata[:xcode_managed]).to be false
+ end
+ end
+
+ describe '#entitlements' do
+ it 'returns the entitlements in the profile' do
+ expect(subject.metadata[:entitlements].keys).to match_array(['application-identifier',
+ 'com.apple.developer.game-center',
+ 'com.apple.developer.team-identifier',
+ 'get-task-allow',
+ 'keychain-access-groups'])
+ end
+ end
+
+ describe '#devices' do
+ it 'returns the devices attached to the profile' do
+ expect(subject.metadata[:devices]).to match_array(["00008101-001454860C10001E"])
+ end
+ end
+
+ describe '#certificate_ids' do
+ it 'returns the certificate ids attached to the profile' do
+ expect(subject.metadata[:certificate_ids]).to match_array(["23380136242930206312716563638445789376"])
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/secure_files/p12_spec.rb b/spec/lib/gitlab/ci/secure_files/p12_spec.rb
new file mode 100644
index 00000000000..beabf4b4856
--- /dev/null
+++ b/spec/lib/gitlab/ci/secure_files/p12_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::SecureFiles::P12 do
+ context 'when the supplied certificate cannot be parsed' do
+ let(:invalid_certificate) { described_class.new('xyzabc') }
+
+ describe '#certificate_data' do
+ it 'assigns the error message and returns nil' do
+ expect(invalid_certificate.certificate_data).to be nil
+ expect(invalid_certificate.error).to eq('PKCS12_parse: mac verify failure')
+ end
+ end
+
+ describe '#metadata' do
+ it 'returns an empty hash' do
+ expect(invalid_certificate.metadata).to eq({})
+ end
+ end
+
+ describe '#expires_at' do
+ it 'returns nil' do
+ expect(invalid_certificate.metadata[:expires_at]).to be_nil
+ end
+ end
+ end
+
+ context 'when the supplied certificate can be parsed, but the password is invalid' do
+ let(:sample_file) { fixture_file('ci_secure_files/sample.p12') }
+ let(:subject) { described_class.new(sample_file, 'foo') }
+
+ describe '#certificate_data' do
+ it 'assigns the error message and returns nil' do
+ expect(subject.certificate_data).to be nil
+ expect(subject.error).to eq('PKCS12_parse: mac verify failure')
+ end
+ end
+ end
+
+ context 'when the supplied certificate can be parsed' do
+ let(:sample_file) { fixture_file('ci_secure_files/sample.p12') }
+ let(:subject) { described_class.new(sample_file) }
+
+ describe '#certificate_data' do
+ it 'returns an OpenSSL::X509::Certificate object' do
+ expect(subject.certificate_data.class).to be(OpenSSL::X509::Certificate)
+ end
+ end
+
+ describe '#metadata' do
+ it 'returns a hash with the expected keys' do
+ expect(subject.metadata.keys).to match_array([:issuer, :subject, :id, :expires_at])
+ end
+ end
+
+ describe '#id' do
+ it 'returns the certificate serial number' do
+ expect(subject.metadata[:id]).to eq('75949910542696343243264405377658443914')
+ end
+ end
+
+ describe '#expires_at' do
+ it 'returns the certificate expiration timestamp' do
+ expect(subject.metadata[:expires_at]).to eq('2022-09-21 14:56:00 UTC')
+ end
+ end
+
+ describe '#issuer' do
+ it 'calls parse on X509Name' do
+ expect(subject.metadata[:issuer]["O"]).to eq('Apple Inc.')
+ end
+ end
+
+ describe '#subject' do
+ it 'calls parse on X509Name' do
+ expect(subject.metadata[:subject]["OU"]).to eq('N7SYAN8PX8')
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/secure_files/x509_name_spec.rb b/spec/lib/gitlab/ci/secure_files/x509_name_spec.rb
new file mode 100644
index 00000000000..3a523924c5b
--- /dev/null
+++ b/spec/lib/gitlab/ci/secure_files/x509_name_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::SecureFiles::X509Name do
+ describe '.parse' do
+ it 'parses an X509Name object into a hash format' do
+ sample = OpenSSL::X509::Name.new([
+ ['C', 'Test Country'],
+ ['O', 'Test Org Name'],
+ ['OU', 'Test Org Unit'],
+ ['CN', 'Test Common Name'],
+ ['UID', 'Test UID']
+ ])
+
+ parsed_sample = described_class.parse(sample)
+
+ expect(parsed_sample["C"]).to eq('Test Country')
+ expect(parsed_sample["O"]).to eq('Test Org Name')
+ expect(parsed_sample["OU"]).to eq('Test Org Unit')
+ expect(parsed_sample["CN"]).to eq('Test Common Name')
+ expect(parsed_sample["UID"]).to eq('Test UID')
+ end
+
+ it 'returns an empty hash when an error occurs' do
+ parsed_sample = described_class.parse('unexpectedinput')
+ expect(parsed_sample).to eq({})
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/trace_spec.rb b/spec/lib/gitlab/ci/trace_spec.rb
index 3043c8c5467..321a47c0634 100644
--- a/spec/lib/gitlab/ci/trace_spec.rb
+++ b/spec/lib/gitlab/ci/trace_spec.rb
@@ -74,7 +74,7 @@ RSpec.describe Gitlab::Ci::Trace, :clean_gitlab_redis_shared_state, factory_defa
trace.being_watched!
result = Gitlab::Redis::SharedState.with do |redis|
- redis.exists(cache_key)
+ redis.exists?(cache_key)
end
expect(result).to eq(true)
diff --git a/spec/lib/gitlab/ci/variables/builder/group_spec.rb b/spec/lib/gitlab/ci/variables/builder/group_spec.rb
index 72487588cde..c3743ebd2d7 100644
--- a/spec/lib/gitlab/ci/variables/builder/group_spec.rb
+++ b/spec/lib/gitlab/ci/variables/builder/group_spec.rb
@@ -132,11 +132,12 @@ RSpec.describe Gitlab::Ci::Variables::Builder::Group do
end
it 'orders the variables from least to most matched' do
- variables_collection = Gitlab::Ci::Variables::Collection.new([
- variable,
- partially_matched_variable,
- perfectly_matched_variable
- ]).to_runner_variables
+ 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
diff --git a/spec/lib/gitlab/ci/variables/builder/project_spec.rb b/spec/lib/gitlab/ci/variables/builder/project_spec.rb
index b64b6ea98e2..c1cefc425f5 100644
--- a/spec/lib/gitlab/ci/variables/builder/project_spec.rb
+++ b/spec/lib/gitlab/ci/variables/builder/project_spec.rb
@@ -132,11 +132,12 @@ RSpec.describe Gitlab::Ci::Variables::Builder::Project do
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
+ 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
diff --git a/spec/lib/gitlab/ci/variables/builder/release_spec.rb b/spec/lib/gitlab/ci/variables/builder/release_spec.rb
new file mode 100644
index 00000000000..85b1659d07b
--- /dev/null
+++ b/spec/lib/gitlab/ci/variables/builder/release_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Variables::Builder::Release do
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:release) { create(:release, project: project) }
+
+ let(:builder) { described_class.new(release) }
+
+ describe '#variables' do
+ let(:description_variable) do
+ {
+ key: 'CI_RELEASE_DESCRIPTION',
+ value: release.description,
+ public: true,
+ masked: false,
+ raw: true
+ }
+ end
+
+ subject do
+ builder.variables
+ end
+
+ context 'when the release is present' do
+ let(:description_item) { item(description_variable) }
+
+ it 'contains all the variables' do
+ is_expected.to contain_exactly(description_item)
+ end
+
+ context 'for large description' do
+ before do
+ release.update_attribute(:description, "Test Description ..." * 5000)
+ end
+
+ it 'truncates' do
+ expect(subject['CI_RELEASE_DESCRIPTION'].value.length).to eq(1024)
+ end
+ end
+
+ context 'when description is nil' do
+ before do
+ release.update_attribute(:description, nil)
+ end
+
+ it 'returns without error' do
+ builder = subject
+
+ expect(builder).to match_array([])
+ expect(builder.errors).to be_nil
+ end
+ end
+ end
+
+ context 'when the release is not present' do
+ let(:release) { nil }
+
+ it 'contains no variables' do
+ is_expected.to match_array([])
+ 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 4833ccf9093..52ba85d2df1 100644
--- a/spec/lib/gitlab/ci/variables/builder_spec.rb
+++ b/spec/lib/gitlab/ci/variables/builder_spec.rb
@@ -10,6 +10,7 @@ RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache do
let_it_be(:user) { create(:user) }
let_it_be_with_reload(:job) do
create(:ci_build,
+ name: 'rspec:test 1',
pipeline: pipeline,
user: user,
yaml_variables: [{ key: 'YAML_VARIABLE', value: 'value' }]
@@ -24,13 +25,15 @@ RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache do
let(:predefined_variables) do
[
{ key: 'CI_JOB_NAME',
- value: job.name },
+ value: 'rspec:test 1' },
+ { key: 'CI_JOB_NAME_SLUG',
+ value: 'rspec-test-1' },
{ key: 'CI_JOB_STAGE',
value: job.stage_name },
{ key: 'CI_NODE_TOTAL',
value: '1' },
{ key: 'CI_BUILD_NAME',
- value: job.name },
+ value: 'rspec:test 1' },
{ key: 'CI_BUILD_STAGE',
value: job.stage_name },
{ key: 'CI',
@@ -171,6 +174,7 @@ RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache do
allow(builder).to receive(:secret_project_variables) { [var('L', 12), var('M', 12)] }
allow(pipeline).to receive(:variables) { [var('M', 13), var('N', 13)] }
allow(pipeline).to receive(:pipeline_schedule) { double(job_variables: [var('N', 14), var('O', 14)]) }
+ allow(builder).to receive(:release_variables) { [var('P', 15), var('Q', 15)] }
end
it 'returns variables in order depending on resource hierarchy' do
@@ -187,7 +191,8 @@ RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache do
var('K', 11), var('L', 11),
var('L', 12), var('M', 12),
var('M', 13), var('N', 13),
- var('N', 14), var('O', 14)])
+ var('N', 14), var('O', 14),
+ var('P', 15), var('Q', 15)])
end
it 'overrides duplicate keys depending on resource hierarchy' do
@@ -199,7 +204,8 @@ RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache do
'I' => '9', 'J' => '10',
'K' => '11', 'L' => '12',
'M' => '13', 'N' => '14',
- 'O' => '14')
+ 'O' => '14', 'P' => '15',
+ 'Q' => '15')
end
end
@@ -216,6 +222,27 @@ RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache do
.to include(a_hash_including(key: schedule_variable.key, value: schedule_variable.value))
end
end
+
+ context 'with release variables' do
+ let(:release_description_key) { 'CI_RELEASE_DESCRIPTION' }
+
+ let_it_be(:tag) { project.repository.tags.first }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project, tag: true, ref: tag.name) }
+ let_it_be(:release) { create(:release, tag: tag.name, project: project) }
+
+ it 'includes release variables' do
+ expect(subject.to_hash).to include(release_description_key => release.description)
+ end
+
+ context 'when there is no release' do
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project, tag: false, ref: 'master') }
+ let(:release) { nil }
+
+ it 'does not include release variables' do
+ expect(subject.to_hash).not_to have_key(release_description_key)
+ end
+ end
+ end
end
describe '#user_variables' do
@@ -261,10 +288,11 @@ RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache do
end
it 'includes #deployment_variables and merges the KUBECONFIG values', :aggregate_failures do
- expect(builder).to receive(:deployment_variables).and_return([
- { key: 'KUBECONFIG', value: 'deployment-kubeconfig' },
- { key: 'OTHER', value: 'some value' }
- ])
+ expect(builder).to receive(:deployment_variables).and_return(
+ [
+ { key: 'KUBECONFIG', value: 'deployment-kubeconfig' },
+ { key: 'OTHER', value: 'some value' }
+ ])
expect(template).to receive(:merge_yaml).with('deployment-kubeconfig')
expect(subject['KUBECONFIG'].value).to eq('example-kubeconfig')
expect(subject['OTHER'].value).to eq('some value')
diff --git a/spec/lib/gitlab/ci/variables/collection/sort_spec.rb b/spec/lib/gitlab/ci/variables/collection/sort_spec.rb
index 57171e5be69..432225c53f0 100644
--- a/spec/lib/gitlab/ci/variables/collection/sort_spec.rb
+++ b/spec/lib/gitlab/ci/variables/collection/sort_spec.rb
@@ -192,13 +192,14 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sort do
end
it 'preserves relative order of overridden variables' do
- is_expected.to eq([
- { 'TOP_LEVEL_GROUP_NAME' => 'top-level-group' },
- { 'SUBGROUP_VAR' => '$TOP_LEVEL_GROUP_NAME' },
- { 'SUB_GROUP_NAME' => 'vars-in-vars-subgroup' },
- { 'SUBGROUP_VAR' => '$SUB_GROUP_NAME' },
- { 'PROJECT_VAR' => '$SUBGROUP_VAR' }
- ])
+ is_expected.to eq(
+ [
+ { 'TOP_LEVEL_GROUP_NAME' => 'top-level-group' },
+ { 'SUBGROUP_VAR' => '$TOP_LEVEL_GROUP_NAME' },
+ { 'SUB_GROUP_NAME' => 'vars-in-vars-subgroup' },
+ { 'SUBGROUP_VAR' => '$SUB_GROUP_NAME' },
+ { 'PROJECT_VAR' => '$SUBGROUP_VAR' }
+ ])
end
end
end
diff --git a/spec/lib/gitlab/ci/variables/collection_spec.rb b/spec/lib/gitlab/ci/variables/collection_spec.rb
index 8ac03301322..7d4a1eef70b 100644
--- a/spec/lib/gitlab/ci/variables/collection_spec.rb
+++ b/spec/lib/gitlab/ci/variables/collection_spec.rb
@@ -571,5 +571,42 @@ RSpec.describe Gitlab::Ci::Variables::Collection do
end
end
end
+
+ context 'with the file_variable_is_referenced_in_another_variable logging' do
+ let(:collection) do
+ Gitlab::Ci::Variables::Collection.new
+ .append(key: 'VAR1', value: 'test-1')
+ .append(key: 'VAR2', value: '$VAR1')
+ .append(key: 'VAR3', value: '$VAR1', raw: true)
+ .append(key: 'FILEVAR4', value: 'file-test-4', file: true)
+ .append(key: 'VAR5', value: '$FILEVAR4')
+ .append(key: 'VAR6', value: '$FILEVAR4', raw: true)
+ end
+
+ subject(:sort_and_expand_all) { collection.sort_and_expand_all(project: project) }
+
+ context 'when a project is not passed' do
+ let(:project) {}
+
+ it 'does not log anything' do
+ expect(Gitlab::AppJsonLogger).not_to receive(:info)
+
+ sort_and_expand_all
+ end
+ end
+
+ context 'when a project is passed' do
+ let(:project) { create(:project) }
+
+ it 'logs file_variable_is_referenced_in_another_variable once for VAR5' do
+ expect(Gitlab::AppJsonLogger).to receive(:info).with(
+ event: 'file_variable_is_referenced_in_another_variable',
+ project_id: project.id
+ ).once
+
+ sort_and_expand_all
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
index f7a0905d9da..7f203168706 100644
--- a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
@@ -71,10 +71,11 @@ module Gitlab
subject(:yaml_variables_for) { result.yaml_variables_for(job_name) }
it 'returns calculated variables with root and job variables' do
- is_expected.to match_array([
- { key: 'VAR1', value: 'value 11' },
- { key: 'VAR2', value: 'value 2' }
- ])
+ is_expected.to match_array(
+ [
+ { key: 'VAR1', value: 'value 11' },
+ { key: 'VAR2', value: 'value 2' }
+ ])
end
context 'when an absent job is sent' do
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index cc327f5b5f1..ebf8422489e 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -15,8 +15,10 @@ module Gitlab
end
end
- describe '#build_attributes' do
- subject { described_class.new(config, user: nil).execute.build_attributes(:rspec) }
+ describe '#builds' do
+ subject(:builds) { described_class.new(config, user: nil).execute.builds }
+
+ let(:rspec_build) { builds.find { |build| build[:name] == 'rspec' } }
describe 'attributes list' do
let(:config) do
@@ -30,7 +32,7 @@ module Gitlab
end
it 'returns valid build attributes' do
- expect(subject).to eq({
+ expect(builds).to eq([{
stage: "test",
stage_idx: 2,
name: "rspec",
@@ -45,7 +47,7 @@ module Gitlab
job_variables: [],
root_variables_inheritance: true,
scheduling_type: :stage
- })
+ }])
end
end
@@ -63,7 +65,7 @@ module Gitlab
end
it 'returns valid build attributes' do
- expect(subject).to eq({
+ expect(builds).to eq([{
stage: 'test',
stage_idx: 2,
name: 'rspec',
@@ -77,7 +79,7 @@ module Gitlab
job_variables: [],
root_variables_inheritance: true,
scheduling_type: :stage
- })
+ }])
end
end
@@ -89,21 +91,22 @@ module Gitlab
end
it 'includes coverage regexp in build attributes' do
- expect(subject)
+ expect(rspec_build)
.to include(coverage_regex: 'Code coverage: \d+\.\d+')
end
end
end
describe 'tags entry with default values' do
- it 'applies default values' do
- config = YAML.dump({ default: { tags: %w[A B] },
- rspec: { script: "rspec" } })
-
- config_processor = Gitlab::Ci::YamlProcessor.new(config).execute
+ let(:config) do
+ YAML.dump(
+ default: { tags: %w[A B] },
+ rspec: { script: "rspec" }
+ )
+ end
- expect(config_processor.stage_builds_attributes("test").size).to eq(1)
- expect(config_processor.stage_builds_attributes("test").first).to eq({
+ it 'applies default values' do
+ expect(rspec_build).to eq({
stage: "test",
stage_idx: 2,
name: "rspec",
@@ -125,7 +128,7 @@ module Gitlab
YAML.dump(rspec: { script: 'rspec', interruptible: true })
end
- it { expect(subject[:interruptible]).to be_truthy }
+ it { expect(rspec_build[:interruptible]).to be_truthy }
end
describe 'interruptible job with default value' do
@@ -133,7 +136,7 @@ module Gitlab
YAML.dump(rspec: { script: 'rspec' })
end
- it { expect(subject).not_to have_key(:interruptible) }
+ it { expect(rspec_build).not_to have_key(:interruptible) }
end
describe 'uninterruptible job' do
@@ -141,7 +144,7 @@ module Gitlab
YAML.dump(rspec: { script: 'rspec', interruptible: false })
end
- it { expect(subject[:interruptible]).to be_falsy }
+ it { expect(rspec_build[:interruptible]).to be_falsy }
end
it "returns interruptible when overridden for job" do
@@ -149,9 +152,10 @@ module Gitlab
rspec: { script: "rspec" } })
config_processor = Gitlab::Ci::YamlProcessor.new(config).execute
+ builds = config_processor.builds.select { |b| b[:stage] == "test" }
- expect(config_processor.stage_builds_attributes("test").size).to eq(1)
- expect(config_processor.stage_builds_attributes("test").first).to eq({
+ expect(builds.size).to eq(1)
+ expect(builds.first).to eq({
stage: "test",
stage_idx: 2,
name: "rspec",
@@ -174,7 +178,7 @@ module Gitlab
end
it 'includes retry count in build options attribute' do
- expect(subject[:options]).to include(retry: { max: 1 })
+ expect(rspec_build[:options]).to include(retry: { max: 1 })
end
end
@@ -184,7 +188,7 @@ module Gitlab
end
it 'does not persist retry count in the database' do
- expect(subject[:options]).not_to have_key(:retry)
+ expect(rspec_build[:options]).not_to have_key(:retry)
end
end
@@ -195,7 +199,7 @@ module Gitlab
end
it 'does use the default value' do
- expect(subject[:options]).to include(retry: { max: 1 })
+ expect(rspec_build[:options]).to include(retry: { max: 1 })
end
end
@@ -206,7 +210,7 @@ module Gitlab
end
it 'does use the job value' do
- expect(subject[:options]).to include(retry: { max: 2 })
+ expect(rspec_build[:options]).to include(retry: { max: 2 })
end
end
end
@@ -221,7 +225,7 @@ module Gitlab
end
it 'is not allowed to fail' do
- expect(subject[:allow_failure]).to be false
+ expect(rspec_build[:allow_failure]).to be false
end
end
@@ -232,7 +236,7 @@ module Gitlab
end
it 'is allowed to fail' do
- expect(subject[:allow_failure]).to be true
+ expect(rspec_build[:allow_failure]).to be true
end
end
@@ -244,11 +248,11 @@ module Gitlab
end
it 'is not allowed to fail' do
- expect(subject[:allow_failure]).to be false
+ expect(rspec_build[:allow_failure]).to be false
end
it 'saves allow_failure_criteria into options' do
- expect(subject[:options]).to match(
+ expect(rspec_build[:options]).to match(
a_hash_including(allow_failure_criteria: { exit_codes: [1] }))
end
end
@@ -262,7 +266,7 @@ module Gitlab
end
it 'is not allowed to fail' do
- expect(subject[:allow_failure]).to be false
+ expect(rspec_build[:allow_failure]).to be false
end
end
@@ -272,7 +276,7 @@ module Gitlab
end
it 'is not allowed to fail' do
- expect(subject[:allow_failure]).to be false
+ expect(rspec_build[:allow_failure]).to be false
end
end
@@ -283,11 +287,11 @@ module Gitlab
end
it 'is not allowed to fail' do
- expect(subject[:allow_failure]).to be false
+ expect(rspec_build[:allow_failure]).to be false
end
it 'saves allow_failure_criteria into options' do
- expect(subject[:options]).to match(
+ expect(rspec_build[:options]).to match(
a_hash_including(allow_failure_criteria: { exit_codes: [1] }))
end
end
@@ -305,8 +309,8 @@ module Gitlab
end
it 'has the attributes' do
- expect(subject[:when]).to eq 'delayed'
- expect(subject[:options][:start_in]).to eq '1 day'
+ expect(rspec_build[:when]).to eq 'delayed'
+ expect(rspec_build[:options][:start_in]).to eq '1 day'
end
end
end
@@ -321,7 +325,7 @@ module Gitlab
end
it 'has the attributes' do
- expect(subject[:resource_group_key]).to eq 'iOS'
+ expect(rspec_build[:resource_group_key]).to eq 'iOS'
end
end
end
@@ -337,7 +341,7 @@ module Gitlab
end
it 'has the attributes' do
- expect(subject[:options]).to eq(
+ expect(rspec_build[:options]).to eq(
trigger: { project: 'namespace/project', branch: 'main' }
)
end
@@ -353,7 +357,7 @@ module Gitlab
end
it 'has the attributes' do
- expect(subject[:options]).to eq(
+ expect(rspec_build[:options]).to eq(
trigger: { project: 'namespace/project', forward: { pipeline_variables: true } }
)
end
@@ -510,6 +514,35 @@ module Gitlab
expect(subject.root_variables).to eq([])
end
end
+
+ context 'with name' do
+ let(:config) do
+ <<-EOYML
+ workflow:
+ name: 'Pipeline name'
+
+ hello:
+ script: echo world
+ EOYML
+ end
+
+ it 'parses the workflow:name as workflow_name' do
+ expect(subject.workflow_name).to eq('Pipeline name')
+ end
+ end
+
+ context 'with no name' do
+ let(:config) do
+ <<-EOYML
+ hello:
+ script: echo world
+ EOYML
+ end
+
+ it 'parses the workflow:name' do
+ expect(subject.workflow_name).to be_nil
+ end
+ end
end
describe '#warnings' do
@@ -682,7 +715,7 @@ module Gitlab
let(:config_data) { YAML.dump(config) }
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(config_data).execute }
- subject { config_processor.stage_builds_attributes('test').first }
+ subject(:test_build) { config_processor.builds.find { |build| build[:name] == 'test' } }
describe "before_script" do
context "in global context" do
@@ -850,9 +883,9 @@ module Gitlab
rspec: { script: "rspec" } })
config_processor = Gitlab::Ci::YamlProcessor.new(config).execute
+ rspec_build = config_processor.builds.find { |build| build[:name] == 'rspec' }
- expect(config_processor.stage_builds_attributes("test").size).to eq(1)
- expect(config_processor.stage_builds_attributes("test").first).to eq({
+ expect(rspec_build).to eq({
stage: "test",
stage_idx: 2,
name: "rspec",
@@ -884,9 +917,9 @@ module Gitlab
script: "rspec" } })
config_processor = Gitlab::Ci::YamlProcessor.new(config).execute
+ rspec_build = config_processor.builds.find { |build| build[:name] == 'rspec' }
- expect(config_processor.stage_builds_attributes("test").size).to eq(1)
- expect(config_processor.stage_builds_attributes("test").first).to eq({
+ expect(rspec_build).to eq({
stage: "test",
stage_idx: 2,
name: "rspec",
@@ -916,9 +949,9 @@ module Gitlab
rspec: { script: "rspec" } })
config_processor = Gitlab::Ci::YamlProcessor.new(config).execute
+ rspec_build = config_processor.builds.find { |build| build[:name] == 'rspec' }
- expect(config_processor.stage_builds_attributes("test").size).to eq(1)
- expect(config_processor.stage_builds_attributes("test").first).to eq({
+ expect(rspec_build).to eq({
stage: "test",
stage_idx: 2,
name: "rspec",
@@ -944,9 +977,9 @@ module Gitlab
rspec: { image: "image:1.0", services: ["postgresql", "docker:dind"], script: "rspec" } })
config_processor = Gitlab::Ci::YamlProcessor.new(config).execute
+ rspec_build = config_processor.builds.find { |build| build[:name] == 'rspec' }
- expect(config_processor.stage_builds_attributes("test").size).to eq(1)
- expect(config_processor.stage_builds_attributes("test").first).to eq({
+ expect(rspec_build).to eq({
stage: "test",
stage_idx: 2,
name: "rspec",
@@ -981,7 +1014,7 @@ module Gitlab
it { is_expected.to be_valid }
it "returns with image" do
- expect(processor.stage_builds_attributes("test")).to contain_exactly({
+ expect(processor.builds).to contain_exactly({
stage: "test",
stage_idx: 2,
name: "test",
@@ -1014,7 +1047,7 @@ module Gitlab
it { is_expected.to be_valid }
it "returns with service" do
- expect(processor.stage_builds_attributes("test")).to contain_exactly({
+ expect(processor.builds).to contain_exactly({
stage: "test",
stage_idx: 2,
name: "test",
@@ -1033,8 +1066,7 @@ module Gitlab
end
end
- # Change this to a `describe` block when removing the FF ci_variables_refactoring_to_variable
- shared_examples 'Variables' do
+ describe 'Variables' do
subject(:execute) { described_class.new(config).execute }
let(:build) { execute.builds.first }
@@ -1163,18 +1195,6 @@ module Gitlab
end
end
- context 'when ci_variables_refactoring_to_variable is enabled' do
- it_behaves_like 'Variables'
- end
-
- context 'when ci_variables_refactoring_to_variable is disabled' do
- before do
- stub_feature_flags(ci_variables_refactoring_to_variable: false)
- end
-
- it_behaves_like 'Variables'
- end
-
context 'when using `extends`' do
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(config).execute }
@@ -1375,7 +1395,7 @@ module Gitlab
})
config_processor = Gitlab::Ci::YamlProcessor.new(config).execute
- builds = config_processor.stage_builds_attributes("test")
+ builds = config_processor.builds
expect(builds.size).to eq(1)
expect(builds.first[:when]).to eq(when_state)
@@ -1391,7 +1411,7 @@ module Gitlab
end
it 'creates one build and sets when:' do
- builds = subject.stage_builds_attributes("test")
+ builds = processor.builds
expect(builds.size).to eq(1)
expect(builds.first[:when]).to eq('delayed')
@@ -1419,7 +1439,7 @@ module Gitlab
end
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(config).execute }
- let(:builds) { config_processor.stage_builds_attributes('test') }
+ let(:builds) { config_processor.builds }
context 'when job is parallelized' do
let(:parallel) { 5 }
@@ -1535,15 +1555,16 @@ module Gitlab
})
config_processor = Gitlab::Ci::YamlProcessor.new(config).execute
+ rspec_build = config_processor.builds.find { |build| build[:name] == 'rspec' }
- expect(config_processor.stage_builds_attributes("test").size).to eq(1)
- expect(config_processor.stage_builds_attributes("test").first[:cache]).to eq([
- paths: ["logs/", "binaries/"],
- untracked: true,
- key: 'key',
- policy: 'pull-push',
- when: 'on_success'
- ])
+ expect(rspec_build[:cache]).to eq(
+ [
+ paths: ["logs/", "binaries/"],
+ untracked: true,
+ key: 'key',
+ policy: 'pull-push',
+ when: 'on_success'
+ ])
end
it "returns cache when defined in default context" do
@@ -1558,32 +1579,34 @@ module Gitlab
})
config_processor = Gitlab::Ci::YamlProcessor.new(config).execute
+ rspec_build = config_processor.builds.find { |build| build[:name] == 'rspec' }
- expect(config_processor.stage_builds_attributes("test").size).to eq(1)
- expect(config_processor.stage_builds_attributes("test").first[:cache]).to eq([
- paths: ["logs/", "binaries/"],
- untracked: true,
- key: { files: ['file'] },
- policy: 'pull-push',
- when: 'on_success'
- ])
+ expect(rspec_build[:cache]).to eq(
+ [
+ paths: ["logs/", "binaries/"],
+ untracked: true,
+ key: { files: ['file'] },
+ policy: 'pull-push',
+ when: 'on_success'
+ ])
end
it 'returns cache key/s when defined in a job' do
- config = YAML.dump({
- rspec: {
- cache: [
- { paths: ['binaries/'], untracked: true, key: 'keya' },
- { paths: ['logs/', 'binaries/'], untracked: true, key: 'key' }
- ],
- script: 'rspec'
- }
- })
+ config = YAML.dump(
+ {
+ rspec: {
+ cache: [
+ { paths: ['binaries/'], untracked: true, key: 'keya' },
+ { paths: ['logs/', 'binaries/'], untracked: true, key: 'key' }
+ ],
+ script: 'rspec'
+ }
+ })
config_processor = Gitlab::Ci::YamlProcessor.new(config).execute
+ rspec_build = config_processor.builds.find { |build| build[:name] == 'rspec' }
- expect(config_processor.stage_builds_attributes('test').size).to eq(1)
- expect(config_processor.stage_builds_attributes('test').first[:cache]).to eq(
+ expect(rspec_build[:cache]).to eq(
[
{
paths: ['binaries/'],
@@ -1616,15 +1639,16 @@ module Gitlab
)
config_processor = Gitlab::Ci::YamlProcessor.new(config).execute
+ rspec_build = config_processor.builds.find { |build| build[:name] == 'rspec' }
- expect(config_processor.stage_builds_attributes('test').size).to eq(1)
- expect(config_processor.stage_builds_attributes('test').first[:cache]).to eq([
- paths: ['binaries/'],
- untracked: true,
- key: { files: ['file'] },
- policy: 'pull-push',
- when: 'on_success'
- ])
+ expect(rspec_build[:cache]).to eq(
+ [
+ paths: ['binaries/'],
+ untracked: true,
+ key: { files: ['file'] },
+ policy: 'pull-push',
+ when: 'on_success'
+ ])
end
it 'returns cache files with prefix' do
@@ -1640,61 +1664,65 @@ module Gitlab
)
config_processor = Gitlab::Ci::YamlProcessor.new(config).execute
+ rspec_build = config_processor.builds.find { |build| build[:name] == 'rspec' }
- expect(config_processor.stage_builds_attributes('test').size).to eq(1)
- expect(config_processor.stage_builds_attributes('test').first[:cache]).to eq([
- paths: ['logs/', 'binaries/'],
- untracked: true,
- key: { files: ['file'], prefix: 'prefix' },
- policy: 'pull-push',
- when: 'on_success'
- ])
+ expect(rspec_build[:cache]).to eq(
+ [
+ paths: ['logs/', 'binaries/'],
+ untracked: true,
+ key: { files: ['file'], prefix: 'prefix' },
+ policy: 'pull-push',
+ when: 'on_success'
+ ])
end
it "overwrite cache when defined for a job and globally" do
- config = YAML.dump({
- cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'global' },
- rspec: {
- script: "rspec",
- cache: { paths: ["test/"], untracked: false, key: 'local' }
- }
- })
+ config = YAML.dump(
+ {
+ cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'global' },
+ rspec: {
+ script: "rspec",
+ cache: { paths: ["test/"], untracked: false, key: 'local' }
+ }
+ })
config_processor = Gitlab::Ci::YamlProcessor.new(config).execute
+ rspec_build = config_processor.builds.find { |build| build[:name] == 'rspec' }
- expect(config_processor.stage_builds_attributes("test").size).to eq(1)
- expect(config_processor.stage_builds_attributes("test").first[:cache]).to eq([
- paths: ["test/"],
- untracked: false,
- key: 'local',
- policy: 'pull-push',
- when: 'on_success'
- ])
+ expect(rspec_build[:cache]).to eq(
+ [
+ paths: ["test/"],
+ untracked: false,
+ key: 'local',
+ policy: 'pull-push',
+ when: 'on_success'
+ ])
end
end
describe "Artifacts" do
it "returns artifacts when defined" do
- config = YAML.dump({
- image: "image:1.0",
- services: ["mysql"],
- before_script: ["pwd"],
- rspec: {
- artifacts: {
- paths: ["logs/", "binaries/"],
- expose_as: "Exposed artifacts",
- untracked: true,
- name: "custom_name",
- expire_in: "7d"
- },
- script: "rspec"
- }
- })
+ config = YAML.dump(
+ {
+ image: "image:1.0",
+ services: ["mysql"],
+ before_script: ["pwd"],
+ rspec: {
+ artifacts: {
+ paths: ["logs/", "binaries/"],
+ expose_as: "Exposed artifacts",
+ untracked: true,
+ name: "custom_name",
+ expire_in: "7d"
+ },
+ script: "rspec"
+ }
+ })
config_processor = Gitlab::Ci::YamlProcessor.new(config).execute
+ rspec_build = config_processor.builds.find { |build| build[:name] == 'rspec' }
- expect(config_processor.stage_builds_attributes("test").size).to eq(1)
- expect(config_processor.stage_builds_attributes("test").first).to eq({
+ expect(rspec_build).to eq({
stage: "test",
stage_idx: 2,
name: "rspec",
@@ -1729,7 +1757,7 @@ module Gitlab
})
config_processor = Gitlab::Ci::YamlProcessor.new(config).execute
- builds = config_processor.stage_builds_attributes("test")
+ builds = config_processor.builds
expect(builds.size).to eq(1)
expect(builds.first[:options][:artifacts][:expire_in]).to eq('never')
@@ -1745,7 +1773,7 @@ module Gitlab
})
config_processor = Gitlab::Ci::YamlProcessor.new(config).execute
- builds = config_processor.stage_builds_attributes("test")
+ builds = config_processor.builds
expect(builds.size).to eq(1)
expect(builds.first[:options][:artifacts][:when]).to eq(when_state)
@@ -1778,7 +1806,7 @@ module Gitlab
- my/test/something
YAML
- attributes = Gitlab::Ci::YamlProcessor.new(config).execute.build_attributes('test')
+ attributes = Gitlab::Ci::YamlProcessor.new(config).execute.builds.find { |build| build[:name] == 'test' }
expect(attributes.dig(*%i[options artifacts exclude])).to eq(%w[my/test/something])
end
@@ -1819,7 +1847,7 @@ module Gitlab
end
it "returns release info" do
- expect(processor.stage_builds_attributes('release').first[:options])
+ expect(processor.builds.first[:options])
.to eq(config[:release].except(:stage, :only))
end
end
@@ -1833,7 +1861,7 @@ module Gitlab
subject { Gitlab::Ci::YamlProcessor.new(YAML.dump(config)).execute }
- let(:builds) { subject.stage_builds_attributes('deploy') }
+ let(:builds) { subject.builds }
context 'when a production environment is specified' do
let(:environment) { 'production' }
@@ -1943,7 +1971,7 @@ module Gitlab
subject { Gitlab::Ci::YamlProcessor.new(YAML.dump(config)).execute }
- let(:builds) { subject.stage_builds_attributes('deploy') }
+ let(:builds) { subject.builds }
context 'when no timeout was provided' do
it 'does not include job_timeout' do
@@ -2370,8 +2398,8 @@ module Gitlab
it 'returns a valid configuration and sets artifacts: true by default' do
expect(subject).to be_valid
- rspec = subject.build_attributes(:rspec)
- expect(rspec.dig(:options, :cross_dependencies)).to eq(
+ rspec_build = subject.builds.find { |build| build[:name] == 'rspec' }
+ expect(rspec_build.dig(:options, :cross_dependencies)).to eq(
[{ pipeline: '$THE_PIPELINE_ID', job: 'dependency-job', artifacts: true }]
)
end
@@ -2391,8 +2419,8 @@ module Gitlab
it 'returns a valid configuration and sets artifacts: true by default' do
expect(subject).to be_valid
- rspec = subject.build_attributes(:rspec)
- expect(rspec.dig(:options, :cross_dependencies)).to eq(
+ rspec_build = subject.builds.find { |build| build[:name] == 'rspec' }
+ expect(rspec_build.dig(:options, :cross_dependencies)).to eq(
[{ pipeline: '123', job: 'dependency-job', artifacts: true }]
)
end
@@ -2422,7 +2450,7 @@ module Gitlab
describe "Hidden jobs" do
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(config).execute }
- subject { config_processor.stage_builds_attributes("test") }
+ subject { config_processor.builds }
shared_examples 'hidden_job_handling' do
it "doesn't create jobs that start with dot" do
@@ -2470,7 +2498,7 @@ module Gitlab
describe "YAML Alias/Anchor" do
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(config).execute }
- subject { config_processor.stage_builds_attributes("build") }
+ subject { config_processor.builds }
shared_examples 'job_templates_handling' do
it "is correctly supported for jobs" do