diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-11-14 11:41:52 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-11-14 11:41:52 +0300 |
commit | 585826cb22ecea5998a2c2a4675735c94bdeedac (patch) | |
tree | 5b05f0b30d33cef48963609e8a18a4dff260eab3 /spec/lib/gitlab/ci | |
parent | df221d036e5d0c6c0ee4d55b9c97f481ee05dee8 (diff) |
Add latest changes from gitlab-org/gitlab@16-6-stable-eev16.6.0-rc42
Diffstat (limited to 'spec/lib/gitlab/ci')
47 files changed, 791 insertions, 294 deletions
diff --git a/spec/lib/gitlab/ci/ansi2html_spec.rb b/spec/lib/gitlab/ci/ansi2html_spec.rb index 30359a7170f..2990599f840 100644 --- a/spec/lib/gitlab/ci/ansi2html_spec.rb +++ b/spec/lib/gitlab/ci/ansi2html_spec.rb @@ -227,7 +227,7 @@ RSpec.describe Gitlab::Ci::Ansi2html do text = "#{section_start}Some text#{section_end}" class_name_start = section_start.gsub("\033[0K", '').gsub('<', '<') class_name_end = section_end.gsub("\033[0K", '').gsub('<', '<') - html = %{<span>#{class_name_start}Some text#{class_name_end}</span>} + html = %(<span>#{class_name_start}Some text#{class_name_end}</span>) expect(convert_html(text)).to eq(html) end @@ -238,9 +238,9 @@ RSpec.describe Gitlab::Ci::Ansi2html do it 'prints light red' do text = "#{section_start}\e[91mHello\e[0m\nLine 1\nLine 2\nLine 3\n#{section_end}" - header = %{<span class="term-fg-l-red section section-header js-s-#{class_name(section_name)}">Hello</span>} - line_break = %{<span class="section section-header js-s-#{class_name(section_name)}"><br/></span>} - output_line = %{<span class="section line js-s-#{class_name(section_name)}">Line 1<br/>Line 2<br/>Line 3<br/></span>} + header = %(<span class="term-fg-l-red section section-header js-s-#{class_name(section_name)}">Hello</span>) + line_break = %(<span class="section section-header js-s-#{class_name(section_name)}"><br/></span>) + output_line = %(<span class="section line js-s-#{class_name(section_name)}">Line 1<br/>Line 2<br/>Line 3<br/></span>) html = "#{section_start_html}#{header}#{line_break}#{output_line}#{section_end_html}" expect(convert_html(text)).to eq(html) diff --git a/spec/lib/gitlab/ci/ansi2json/line_spec.rb b/spec/lib/gitlab/ci/ansi2json/line_spec.rb index b8563bb1d1c..475a54b275d 100644 --- a/spec/lib/gitlab/ci/ansi2json/line_spec.rb +++ b/spec/lib/gitlab/ci/ansi2json/line_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::Ci::Ansi2json::Line do +RSpec.describe Gitlab::Ci::Ansi2json::Line, feature_category: :continuous_integration do let(:offset) { 0 } let(:style) { Gitlab::Ci::Ansi2json::Style.new } @@ -75,6 +75,14 @@ RSpec.describe Gitlab::Ci::Ansi2json::Line do end end + describe '#set_as_section_footer' do + it 'change the section_footer to true' do + expect { subject.set_as_section_footer } + .to change { subject.section_footer } + .to be_truthy + end + end + describe '#set_section_duration' do using RSpec::Parameterized::TableSyntax @@ -178,6 +186,23 @@ RSpec.describe Gitlab::Ci::Ansi2json::Line do expect(subject.to_h).to eq(result) end end + + context 'when section footer is set' do + before do + subject.set_as_section_footer + end + + it 'serializes the attributes set' do + result = { + offset: 0, + content: [{ text: 'some data', style: 'term-bold' }], + section: 'section_2', + section_footer: true + } + + expect(subject.to_h).to eq(result) + end + end end context 'when there are no sections' do diff --git a/spec/lib/gitlab/ci/ansi2json/state_spec.rb b/spec/lib/gitlab/ci/ansi2json/state_spec.rb index 8dd4092f3d8..07e6579829a 100644 --- a/spec/lib/gitlab/ci/ansi2json/state_spec.rb +++ b/spec/lib/gitlab/ci/ansi2json/state_spec.rb @@ -8,7 +8,7 @@ RSpec.describe Gitlab::Ci::Ansi2json::State, feature_category: :continuous_integ state.offset = 1 state.new_line!(style: { fg: 'some-fg', bg: 'some-bg', mask: 1234 }) state.set_last_line_offset - state.open_section('hello', 111, {}) + state.open_section('hello', 100, {}) end end @@ -24,7 +24,7 @@ RSpec.describe Gitlab::Ci::Ansi2json::State, feature_category: :continuous_integ fg: 'some-fg', mask: 1234 }) - expect(new_state.open_sections).to eq({ 'hello' => 111 }) + expect(new_state.open_sections).to eq({ 'hello' => 100 }) end it 'ignores unsigned prior state', :aggregate_failures do @@ -44,6 +44,23 @@ RSpec.describe Gitlab::Ci::Ansi2json::State, feature_category: :continuous_integ expect(new_state.open_sections).to eq({}) end + it 'opens and closes a section', :aggregate_failures do + new_state = described_class.new('', 1000) + + new_state.new_line!(style: {}) + new_state.open_section('hello', 100, {}) + + expect(new_state.current_line.section_header).to eq(true) + expect(new_state.current_line.section_footer).to eq(false) + + new_state.new_line!(style: {}) + new_state.close_section('hello', 101) + + expect(new_state.current_line.section_header).to eq(false) + expect(new_state.current_line.section_duration).to eq('00:01') + expect(new_state.current_line.section_footer).to eq(true) + end + it 'ignores bad input', :aggregate_failures do expect(::Gitlab::AppLogger).to( receive(:warn).with( diff --git a/spec/lib/gitlab/ci/ansi2json_spec.rb b/spec/lib/gitlab/ci/ansi2json_spec.rb index 98fca40e8ea..23be3209171 100644 --- a/spec/lib/gitlab/ci/ansi2json_spec.rb +++ b/spec/lib/gitlab/ci/ansi2json_spec.rb @@ -145,6 +145,7 @@ RSpec.describe Gitlab::Ci::Ansi2json, feature_category: :continuous_integration offset: 63, content: [], section_duration: '01:03', + section_footer: true, section: 'prepare-script' } ]) @@ -163,7 +164,8 @@ RSpec.describe Gitlab::Ci::Ansi2json, feature_category: :continuous_integration offset: 56, content: [], section: 'prepare-script', - section_duration: '01:03' + section_duration: '01:03', + section_footer: true } ]) end @@ -181,7 +183,8 @@ RSpec.describe Gitlab::Ci::Ansi2json, feature_category: :continuous_integration offset: 49, content: [], section: 'prepare-script', - section_duration: '01:03' + section_duration: '01:03', + section_footer: true }, { offset: 91, @@ -262,7 +265,8 @@ RSpec.describe Gitlab::Ci::Ansi2json, feature_category: :continuous_integration offset: 75, content: [], section: 'prepare-script', - section_duration: '01:03' + section_duration: '01:03', + section_footer: true } ]) end @@ -300,7 +304,8 @@ RSpec.describe Gitlab::Ci::Ansi2json, feature_category: :continuous_integration offset: 106, content: [], section: 'prepare-script-nested', - section_duration: '00:02' + section_duration: '00:02', + section_footer: true }, { offset: 155, @@ -311,7 +316,8 @@ RSpec.describe Gitlab::Ci::Ansi2json, feature_category: :continuous_integration offset: 158, content: [], section: 'prepare-script', - section_duration: '01:03' + section_duration: '01:03', + section_footer: true }, { offset: 200, @@ -345,13 +351,15 @@ RSpec.describe Gitlab::Ci::Ansi2json, feature_category: :continuous_integration offset: 115, content: [], section: 'prepare-script-nested', - section_duration: '00:02' + section_duration: '00:02', + section_footer: true }, { offset: 164, content: [], section: 'prepare-script', - section_duration: '01:03' + section_duration: '01:03', + section_footer: true } ]) end @@ -378,7 +386,8 @@ RSpec.describe Gitlab::Ci::Ansi2json, feature_category: :continuous_integration offset: 83, content: [], section: 'prepare-script', - section_duration: '01:03' + section_duration: '01:03', + section_footer: true } ]) end @@ -554,7 +563,8 @@ RSpec.describe Gitlab::Ci::Ansi2json, feature_category: :continuous_integration offset: 77, content: [], section: 'prepare-script', - section_duration: '01:03' + section_duration: '01:03', + section_footer: true } ] end diff --git a/spec/lib/gitlab/ci/badge/pipeline/status_spec.rb b/spec/lib/gitlab/ci/badge/pipeline/status_spec.rb index 45d0d781090..d6b59d0da64 100644 --- a/spec/lib/gitlab/ci/badge/pipeline/status_spec.rb +++ b/spec/lib/gitlab/ci/badge/pipeline/status_spec.rb @@ -117,10 +117,7 @@ RSpec.describe Gitlab::Ci::Badge::Pipeline::Status do end def create_pipeline(project, sha, branch) - pipeline = create(:ci_empty_pipeline, - project: project, - sha: sha, - ref: branch) + pipeline = create(:ci_empty_pipeline, project: project, sha: sha, ref: branch) create(:ci_build, pipeline: pipeline, stage: 'notify') end diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb index 7b35c9ba483..6cd9432c6c5 100644 --- a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb +++ b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb @@ -69,9 +69,11 @@ RSpec.describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do it { is_expected.to all(be_an_instance_of(described_class)) } it do - is_expected.to contain_exactly entry('path/dir_1/file_1'), - entry('path/dir_1/file_b'), - entry('path/dir_1/subdir/') + is_expected.to contain_exactly( + entry('path/dir_1/file_1'), + entry('path/dir_1/file_b'), + entry('path/dir_1/subdir/') + ) end end @@ -82,8 +84,10 @@ RSpec.describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do it { is_expected.to all(be_an_instance_of(described_class)) } it do - is_expected.to contain_exactly entry('path/dir_1/file_1'), - entry('path/dir_1/file_b') + is_expected.to contain_exactly( + entry('path/dir_1/file_1'), + entry('path/dir_1/file_b') + ) end end @@ -103,8 +107,10 @@ RSpec.describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do it { is_expected.to all(be_an_instance_of(described_class)) } it do - is_expected.to contain_exactly entry('path/dir_1/subdir/'), - entry('path/') + is_expected.to contain_exactly( + entry('path/dir_1/subdir/'), + entry('path/') + ) end end diff --git a/spec/lib/gitlab/ci/build/context/build_spec.rb b/spec/lib/gitlab/ci/build/context/build_spec.rb index fae02e140f2..9fdb4ee9393 100644 --- a/spec/lib/gitlab/ci/build/context/build_spec.rb +++ b/spec/lib/gitlab/ci/build/context/build_spec.rb @@ -41,16 +41,6 @@ RSpec.describe Gitlab::Ci::Build::Context::Build, feature_category: :pipeline_co it { expect(context.variables).to be_instance_of(Gitlab::Ci::Variables::Collection) } it_behaves_like 'variables collection' - - context 'with FF disabled' do - before do - stub_feature_flags(reduced_build_attributes_list_for_rules: false) - end - - it { expect(context.variables).to be_instance_of(Gitlab::Ci::Variables::Collection) } - - it_behaves_like 'variables collection' - end end describe '#variables_hash' do @@ -59,15 +49,5 @@ RSpec.describe Gitlab::Ci::Build::Context::Build, feature_category: :pipeline_co it { expect(context.variables_hash).to be_instance_of(ActiveSupport::HashWithIndifferentAccess) } it_behaves_like 'variables collection' - - context 'with FF disabled' do - before do - stub_feature_flags(reduced_build_attributes_list_for_rules: false) - end - - it { expect(context.variables_hash).to be_instance_of(ActiveSupport::HashWithIndifferentAccess) } - - it_behaves_like 'variables collection' - end end end diff --git a/spec/lib/gitlab/ci/build/hook_spec.rb b/spec/lib/gitlab/ci/build/hook_spec.rb index 6c9175b4260..da9a680f110 100644 --- a/spec/lib/gitlab/ci/build/hook_spec.rb +++ b/spec/lib/gitlab/ci/build/hook_spec.rb @@ -4,8 +4,10 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Build::Hook, feature_category: :pipeline_composition do let_it_be(:build1) do - FactoryBot.build(:ci_build, - options: { hooks: { pre_get_sources_script: ["echo 'hello pre_get_sources_script'"] } }) + build( + :ci_build, + options: { hooks: { pre_get_sources_script: ["echo 'hello pre_get_sources_script'"] } } + ) end describe '.from_hooks' do diff --git a/spec/lib/gitlab/ci/build/policy/changes_spec.rb b/spec/lib/gitlab/ci/build/policy/changes_spec.rb index 5d5a212b9a5..00e44650d44 100644 --- a/spec/lib/gitlab/ci/build/policy/changes_spec.rb +++ b/spec/lib/gitlab/ci/build/policy/changes_spec.rb @@ -8,11 +8,14 @@ RSpec.describe Gitlab::Ci::Build::Policy::Changes do describe '#satisfied_by?' do describe 'paths matching' do let(:pipeline) do - build(:ci_empty_pipeline, project: project, - ref: 'master', - source: :push, - sha: '1234abcd', - before_sha: '0123aabb') + build( + :ci_empty_pipeline, + project: project, + ref: 'master', + source: :push, + sha: '1234abcd', + before_sha: '0123aabb' + ) end let(:ci_build) do @@ -92,11 +95,14 @@ RSpec.describe Gitlab::Ci::Build::Policy::Changes do let_it_be(:project) { create(:project, :repository) } let(:pipeline) do - create(:ci_empty_pipeline, project: project, - ref: 'master', - source: :push, - sha: '498214d', - before_sha: '281d3a7') + create( + :ci_empty_pipeline, + project: project, + ref: 'master', + source: :push, + sha: '498214d', + before_sha: '281d3a7' + ) end let(:build) do @@ -122,12 +128,15 @@ RSpec.describe Gitlab::Ci::Build::Policy::Changes do let_it_be(:project) { create(:project, :repository) } let(:pipeline) do - create(:ci_empty_pipeline, project: project, - ref: 'feature', - source: source, - sha: '0b4bc9a4', - before_sha: Gitlab::Git::BLANK_SHA, - merge_request: merge_request) + create( + :ci_empty_pipeline, + project: project, + ref: 'feature', + source: source, + sha: '0b4bc9a4', + before_sha: Gitlab::Git::BLANK_SHA, + merge_request: merge_request + ) end let(:ci_build) do @@ -140,11 +149,13 @@ RSpec.describe Gitlab::Ci::Build::Policy::Changes do let(:source) { :merge_request_event } let(:merge_request) do - create(:merge_request, - source_project: project, - source_branch: 'feature', - target_project: project, - target_branch: 'master') + create( + :merge_request, + source_project: project, + source_branch: 'feature', + target_project: project, + target_branch: 'master' + ) end it 'is satified by changes in the merge request' do diff --git a/spec/lib/gitlab/ci/build/policy/variables_spec.rb b/spec/lib/gitlab/ci/build/policy/variables_spec.rb index e560f1c2b5a..1073df60d4a 100644 --- a/spec/lib/gitlab/ci/build/policy/variables_spec.rb +++ b/spec/lib/gitlab/ci/build/policy/variables_spec.rb @@ -104,23 +104,32 @@ RSpec.describe Gitlab::Ci::Build::Policy::Variables do context 'when using project ci variables in environment scope' do let(:ci_build) do - build(:ci_build, pipeline: pipeline, - project: project, - ref: 'master', - stage: 'review', - environment: 'test/$CI_JOB_STAGE/1', - ci_stage: build(:ci_stage, name: 'review', project: project, pipeline: pipeline)) + build( + :ci_build, + pipeline: pipeline, + project: project, + ref: 'master', + stage: 'review', + environment: 'test/$CI_JOB_STAGE/1', + ci_stage: build(:ci_stage, name: 'review', project: project, pipeline: pipeline) + ) end before do - create(:ci_variable, project: project, - key: 'SCOPED_VARIABLE', - value: 'my-value-1') - - create(:ci_variable, project: project, - key: 'SCOPED_VARIABLE', - value: 'my-value-2', - environment_scope: 'test/review/*') + create( + :ci_variable, + project: project, + key: 'SCOPED_VARIABLE', + value: 'my-value-1' + ) + + create( + :ci_variable, + project: project, + key: 'SCOPED_VARIABLE', + value: 'my-value-2', + environment_scope: 'test/review/*' + ) end it 'is satisfied by scoped variable match' do 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 a22aa30304b..1fe54b9f2d5 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 @@ -95,8 +95,11 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do context 'when using compare_to' do let_it_be(:project) do - create(:project, :custom_repo, - files: { 'README.md' => 'readme' }) + create( + :project, + :custom_repo, + files: { 'README.md' => 'readme' } + ) end let_it_be(:user) { project.owner } diff --git a/spec/lib/gitlab/ci/config/entry/bridge_spec.rb b/spec/lib/gitlab/ci/config/entry/bridge_spec.rb index 567ffa68836..6e6b9d949c5 100644 --- a/spec/lib/gitlab/ci/config/entry/bridge_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/bridge_spec.rb @@ -101,14 +101,16 @@ RSpec.describe Gitlab::Ci::Config::Entry::Bridge do describe '#value' do it 'is returns a bridge job configuration' do - expect(subject.value).to eq(name: :my_bridge, - trigger: { project: 'some/project' }, - ignore: false, - stage: 'test', - only: { refs: %w[branches tags] }, - job_variables: {}, - root_variables_inheritance: true, - scheduling_type: :stage) + expect(subject.value).to eq( + name: :my_bridge, + trigger: { project: 'some/project' }, + ignore: false, + stage: 'test', + only: { refs: %w[branches tags] }, + job_variables: {}, + root_variables_inheritance: true, + scheduling_type: :stage + ) end end end @@ -124,15 +126,16 @@ RSpec.describe Gitlab::Ci::Config::Entry::Bridge do describe '#value' do it 'is returns a bridge job configuration hash' do - expect(subject.value).to eq(name: :my_bridge, - trigger: { project: 'some/project', - branch: 'feature' }, - ignore: false, - stage: 'test', - only: { refs: %w[branches tags] }, - job_variables: {}, - root_variables_inheritance: true, - scheduling_type: :stage) + expect(subject.value).to eq( + name: :my_bridge, + trigger: { project: 'some/project', branch: 'feature' }, + ignore: false, + stage: 'test', + only: { refs: %w[branches tags] }, + job_variables: {}, + root_variables_inheritance: true, + scheduling_type: :stage + ) end end end @@ -283,8 +286,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Bridge do ignore: false, stage: 'test', only: { refs: %w[branches tags] }, - parallel: { matrix: [{ 'PROVIDER' => ['aws'], 'STACK' => %w(monitoring app1) }, - { 'PROVIDER' => ['gcp'], 'STACK' => %w(data) }] }, + parallel: { matrix: [{ 'PROVIDER' => ['aws'], 'STACK' => %w[monitoring app1] }, + { 'PROVIDER' => ['gcp'], 'STACK' => %w[data] }] }, job_variables: {}, root_variables_inheritance: true, scheduling_type: :stage @@ -305,15 +308,16 @@ RSpec.describe Gitlab::Ci::Config::Entry::Bridge do describe '#value' do it 'returns a bridge job configuration hash' do - expect(subject.value).to eq(name: :my_bridge, - trigger: { project: 'some/project', - forward: { pipeline_variables: true } }, - ignore: false, - stage: 'test', - only: { refs: %w[branches tags] }, - job_variables: {}, - root_variables_inheritance: true, - scheduling_type: :stage) + expect(subject.value).to eq( + name: :my_bridge, + trigger: { project: 'some/project', forward: { pipeline_variables: true } }, + ignore: false, + stage: 'test', + only: { refs: %w[branches tags] }, + job_variables: {}, + root_variables_inheritance: true, + scheduling_type: :stage + ) end end end diff --git a/spec/lib/gitlab/ci/config/entry/commands_spec.rb b/spec/lib/gitlab/ci/config/entry/commands_spec.rb index 1b8dfae692a..f84a78b4804 100644 --- a/spec/lib/gitlab/ci/config/entry/commands_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/commands_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Commands do let(:entry) { described_class.new(config) } context 'when entry config value is an array of strings' do - let(:config) { %w(ls pwd) } + let(:config) { %w[ls pwd] } describe '#value' do it 'returns array of strings' do diff --git a/spec/lib/gitlab/ci/config/entry/environment_spec.rb b/spec/lib/gitlab/ci/config/entry/environment_spec.rb index 3562706ff33..cff94a96c99 100644 --- a/spec/lib/gitlab/ci/config/entry/environment_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/environment_spec.rb @@ -93,7 +93,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Environment do context 'when valid action is used' do where(:action) do - %w(start stop prepare verify access) + %w[start stop prepare verify access] end with_them do diff --git a/spec/lib/gitlab/ci/config/entry/image_spec.rb b/spec/lib/gitlab/ci/config/entry/image_spec.rb index b37498ba10a..17c45ec4c2c 100644 --- a/spec/lib/gitlab/ci/config/entry/image_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/image_spec.rb @@ -56,7 +56,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Image do end context 'when configuration is a hash' do - let(:config) { { name: 'image:1.0', entrypoint: %w(/bin/sh run) } } + let(:config) { { name: 'image:1.0', entrypoint: %w[/bin/sh run] } } describe '#value' do it 'returns image hash' do @@ -84,13 +84,13 @@ RSpec.describe Gitlab::Ci::Config::Entry::Image do describe '#entrypoint' do it "returns image's entrypoint" do - expect(entry.entrypoint).to eq %w(/bin/sh run) + expect(entry.entrypoint).to eq %w[/bin/sh run] end end context 'when configuration has ports' do let(:ports) { [{ number: 80, protocol: 'http', name: 'foobar' }] } - let(:config) { { name: 'image:1.0', entrypoint: %w(/bin/sh run), ports: ports } } + let(:config) { { name: 'image:1.0', entrypoint: %w[/bin/sh run], ports: ports } } let(:entry) { described_class.new(config, with_image_ports: image_ports) } let(:image_ports) { false } diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb index 1a78d929871..24d3cac6616 100644 --- a/spec/lib/gitlab/ci/config/entry/job_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb @@ -598,7 +598,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job, feature_category: :pipeline_compo end end - context 'when job is not a pages job' do + context 'when job is not a pages job', feature_category: :pages do let(:name) { :rspec } context 'if the config contains a publish entry' do @@ -609,9 +609,18 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job, feature_category: :pipeline_compo expect(entry.errors).to include /job publish can only be used within a `pages` job/ end end + + context 'if the config contains a pages entry' do + let(:entry) { described_class.new({ script: 'echo', pages: { path_prefix: 'foo' } }, name: name) } + + it 'is invalid' do + expect(entry).not_to be_valid + expect(entry.errors).to include /job pages can only be used within a `pages` job/ + end + end end - context 'when job is a pages job' do + context 'when job is a pages job', feature_category: :pages do let(:name) { :pages } context 'when it does not have a publish entry' do @@ -629,6 +638,28 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job, feature_category: :pipeline_compo expect(entry).to be_valid end end + + context 'when it has a pages entry' do + let(:entry) { described_class.new({ script: 'echo', pages: { path_prefix: 'foo' } }, name: name) } + + it 'is valid' do + expect(entry).to be_valid + end + end + end + end + + describe '#pages_job?', :aggregate_failures, feature_category: :pages do + where(:name, :result) do + :pages | true + :'pages:staging' | false + :'something:pages:else' | false + end + + with_them do + subject { described_class.new({}, name: name).pages_job? } + + it { is_expected.to eq(result) } end end @@ -739,20 +770,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job, feature_category: :pipeline_compo end end - describe '#pages_job?', :aggregate_failures, feature_category: :pages do - where(:name, :result) do - :pages | true - :'pages:staging' | false - :'something:pages:else' | false - end - - with_them do - subject { described_class.new({}, name: name).pages_job? } - - it { is_expected.to eq(result) } - end - end - context 'when composed' do before do entry.compose! @@ -773,19 +790,20 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job, feature_category: :pipeline_compo end it 'returns correct value' do - expect(entry.value) - .to eq(name: :rspec, - before_script: %w[ls pwd], - script: %w[rspec], - stage: 'test', - ignore: false, - after_script: %w[cleanup], - hooks: { pre_get_sources_script: ['echo hello'] }, - only: { refs: %w[branches tags] }, - job_variables: {}, - root_variables_inheritance: true, - scheduling_type: :stage, - id_tokens: { TEST_ID_TOKEN: { aud: 'https://gitlab.com' } }) + expect(entry.value).to eq( + name: :rspec, + before_script: %w[ls pwd], + script: %w[rspec], + stage: 'test', + ignore: false, + after_script: %w[cleanup], + hooks: { pre_get_sources_script: ['echo hello'] }, + only: { refs: %w[branches tags] }, + job_variables: {}, + root_variables_inheritance: true, + scheduling_type: :stage, + id_tokens: { TEST_ID_TOKEN: { aud: 'https://gitlab.com' } } + ) end end end diff --git a/spec/lib/gitlab/ci/config/entry/pages_spec.rb b/spec/lib/gitlab/ci/config/entry/pages_spec.rb new file mode 100644 index 00000000000..0ee692a443e --- /dev/null +++ b/spec/lib/gitlab/ci/config/entry/pages_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Config::Entry::Pages, feature_category: :pages do + subject(:entry) { described_class.new(config) } + + describe 'validation' do + context 'when value given is not a hash' do + let(:config) { 'value' } + + it 'is invalid' do + expect(entry).not_to be_valid + expect(entry.errors).to include('pages config should be a hash') + end + end + + context 'when value is a hash' do + context 'when the hash is valid' do + let(:config) { { path_prefix: 'prefix' } } + + it 'is valid' do + expect(entry).to be_valid + expect(entry.value).to eq({ + path_prefix: 'prefix' + }) + end + end + + context 'when path_prefix key is not a string' do + let(:config) { { path_prefix: 1 } } + + it 'is invalid' do + expect(entry).not_to be_valid + expect(entry.errors).to include('pages path prefix should be a string') + end + end + + context 'when hash contains not allowed keys' do + let(:config) { { unknown: 'echo' } } + + it 'is invalid' do + expect(entry).not_to be_valid + expect(entry.errors).to include('pages config contains unknown keys: unknown') + end + end + end + end +end diff --git a/spec/lib/gitlab/ci/config/entry/policy_spec.rb b/spec/lib/gitlab/ci/config/entry/policy_spec.rb index 7093a0a6edf..77a895b75c0 100644 --- a/spec/lib/gitlab/ci/config/entry/policy_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/policy_spec.rb @@ -221,8 +221,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Policy, feature_category: :continuous_ let(:config) { { variables: %w[$VARIABLE] } } it 'includes default values' do - expect(entry.value).to eq(refs: %w[branches tags], - variables: %w[$VARIABLE]) + expect(entry.value).to eq(refs: %w[branches tags], variables: %w[$VARIABLE]) 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 132e75a808b..44e2fdbac37 100644 --- a/spec/lib/gitlab/ci/config/entry/processable_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/processable_spec.rb @@ -119,6 +119,20 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable, feature_category: :pipeli end end + context 'when script: and trigger: are used together' do + let(:config) do + { + script: 'echo', + trigger: 'test-group/test-project' + } + end + + it 'returns is invalid' do + expect(entry).not_to be_valid + expect(entry.errors).to include(/these keys cannot be used together: script, trigger/) + end + end + context 'when only: is used with rules:' do let(:config) { { only: ['merge_requests'], rules: [{ if: '$THIS' }] } } diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb index 5fac5298e8e..0370bcbccf5 100644 --- a/spec/lib/gitlab/ci/config/entry/root_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb @@ -31,7 +31,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do context 'when top-level entries are defined' do let(:hash) do { - before_script: %w(ls pwd), + before_script: %w[ls pwd], image: 'image:1.0', default: {}, services: ['postgres:9.1', 'mysql:5.5'], @@ -41,7 +41,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do VAR3: { value: 'val3', options: %w[val3 val4 val5], description: 'this is var 3 and some options' } }, after_script: ['make clean'], - stages: %w(build pages release), + stages: %w[build pages release], cache: { key: 'k', untracked: true, paths: ['public/'] }, rspec: { script: %w[rspec ls] }, spinach: { before_script: [], variables: {}, script: 'spinach' }, @@ -123,7 +123,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do expect(root.jobs_value[:rspec]).to eq( { name: :rspec, script: %w[rspec ls], - before_script: %w(ls pwd), + before_script: %w[ls pwd], image: { name: 'image:1.0' }, services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }], stage: 'test', @@ -162,7 +162,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do services: [{ name: "postgres:9.1" }, { name: "mysql:5.5" }], cache: [{ key: "k", untracked: true, paths: ["public/"], policy: "pull-push", when: 'on_success', unprotect: false, fallback_keys: [] }], - only: { refs: %w(branches tags) }, + only: { refs: %w[branches tags] }, job_variables: { 'VAR' => { value: 'job' } }, root_variables_inheritance: true, after_script: [], @@ -176,14 +176,14 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do context 'when a mix of top-level and default entries is used' do let(:hash) do - { before_script: %w(ls pwd), + { before_script: %w[ls pwd], after_script: ['make clean'], default: { image: 'image:1.0', services: ['postgres:9.1', 'mysql:5.5'] }, variables: { VAR: 'root' }, - stages: %w(build pages), + stages: %w[build pages], cache: { key: 'k', untracked: true, paths: ['public/'] }, rspec: { script: %w[rspec ls] }, spinach: { before_script: [], variables: { VAR: 'job' }, script: 'spinach' } } @@ -205,7 +205,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do expect(root.jobs_value).to eq( rspec: { name: :rspec, script: %w[rspec ls], - before_script: %w(ls pwd), + before_script: %w[ls pwd], image: { name: 'image:1.0' }, services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }], stage: 'test', diff --git a/spec/lib/gitlab/ci/config/entry/service_spec.rb b/spec/lib/gitlab/ci/config/entry/service_spec.rb index e36484bb0ae..1f935bebed5 100644 --- a/spec/lib/gitlab/ci/config/entry/service_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/service_spec.rb @@ -51,7 +51,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Service do context 'when configuration is a hash' do let(:config) do - { name: 'postgresql:9.5', alias: 'db', command: %w(cmd run), entrypoint: %w(/bin/sh run) } + { name: 'postgresql:9.5', alias: 'db', command: %w[cmd run], entrypoint: %w[/bin/sh run] } end describe '#valid?' do @@ -80,13 +80,13 @@ RSpec.describe Gitlab::Ci::Config::Entry::Service do describe '#command' do it "returns service's command" do - expect(entry.command).to eq %w(cmd run) + expect(entry.command).to eq %w[cmd run] end end describe '#entrypoint' do it "returns service's entrypoint" do - expect(entry.entrypoint).to eq %w(/bin/sh run) + expect(entry.entrypoint).to eq %w[/bin/sh run] end end @@ -99,7 +99,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Service do context 'when configuration has ports' do let(:ports) { [{ number: 80, protocol: 'http', name: 'foobar' }] } let(:config) do - { name: 'postgresql:9.5', alias: 'db', command: %w(cmd run), entrypoint: %w(/bin/sh run), ports: ports } + { name: 'postgresql:9.5', alias: 'db', command: %w[cmd run], entrypoint: %w[/bin/sh run], ports: ports } end let(:entry) { described_class.new(config, with_image_ports: image_ports) } @@ -198,7 +198,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Service do context 'when service has ports' do let(:ports) { [{ number: 80, protocol: 'http', name: 'foobar' }] } let(:config) do - { name: 'postgresql:9.5', command: %w(cmd run), entrypoint: %w(/bin/sh run), ports: ports } + { name: 'postgresql:9.5', command: %w[cmd run], entrypoint: %w[/bin/sh run], ports: ports } end it 'alias field is mandatory' do @@ -209,7 +209,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Service do context 'when service does not have ports' do let(:config) do - { name: 'postgresql:9.5', alias: 'db', command: %w(cmd run), entrypoint: %w(/bin/sh run) } + { name: 'postgresql:9.5', alias: 'db', command: %w[cmd run], entrypoint: %w[/bin/sh run] } end it 'alias field is optional' do diff --git a/spec/lib/gitlab/ci/config/extendable/entry_spec.rb b/spec/lib/gitlab/ci/config/extendable/entry_spec.rb index 69aa3bab77a..b57a56b8389 100644 --- a/spec/lib/gitlab/ci/config/extendable/entry_spec.rb +++ b/spec/lib/gitlab/ci/config/extendable/entry_spec.rb @@ -128,8 +128,7 @@ RSpec.describe Gitlab::Ci::Config::Extendable::Entry do it 'raises an error' do expect { subject.extend! } - .to raise_error(described_class::InvalidExtensionError, - /invalid base hash/) + .to raise_error(described_class::InvalidExtensionError, /invalid base hash/) end end @@ -140,8 +139,7 @@ RSpec.describe Gitlab::Ci::Config::Extendable::Entry do it 'raises an error' do expect { subject.extend! } - .to raise_error(described_class::InvalidExtensionError, - /unknown key/) + .to raise_error(described_class::InvalidExtensionError, /unknown key/) end end @@ -178,7 +176,7 @@ RSpec.describe Gitlab::Ci::Config::Extendable::Entry do { first: { script: 'my value', image: 'ubuntu' }, second: { image: 'alpine' }, - test: { extends: %w(first second) } + test: { extends: %w[first second] } } end @@ -186,7 +184,7 @@ RSpec.describe Gitlab::Ci::Config::Extendable::Entry do { first: { script: 'my value', image: 'ubuntu' }, second: { image: 'alpine' }, - test: { extends: %w(first second), script: 'my value', image: 'alpine' } + test: { extends: %w[first second], script: 'my value', image: 'alpine' } } end @@ -230,8 +228,7 @@ RSpec.describe Gitlab::Ci::Config::Extendable::Entry do it 'raises an error' do expect { subject.extend! } - .to raise_error(described_class::CircularDependencyError, - /circular dependency detected/) + .to raise_error(described_class::CircularDependencyError, /circular dependency detected/) end end diff --git a/spec/lib/gitlab/ci/config/external/file/base_spec.rb b/spec/lib/gitlab/ci/config/external/file/base_spec.rb index 1415dbeb532..bcfab620bd9 100644 --- a/spec/lib/gitlab/ci/config/external/file/base_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/base_spec.rb @@ -60,7 +60,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Base, feature_category: :pipe end context 'when location is not a string' do - let(:location) { %w(some/file.txt other/file.txt) } + let(:location) { %w[some/file.txt other/file.txt] } it { is_expected.to be_falsy } end diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb index 56d1ddee4b8..5f28b45496f 100644 --- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb +++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb @@ -406,8 +406,10 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper, feature_category: :pipeline end it 'includes the matched local files' do - expect(subject).to contain_exactly(an_instance_of(Gitlab::Ci::Config::External::File::Local), - an_instance_of(Gitlab::Ci::Config::External::File::Local)) + expect(subject).to contain_exactly( + an_instance_of(Gitlab::Ci::Config::External::File::Local), + an_instance_of(Gitlab::Ci::Config::External::File::Local) + ) expect(subject.map(&:location)).to contain_exactly('myfolder/file1.yml', 'myfolder/file2.yml') end @@ -424,8 +426,10 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper, feature_category: :pipeline let(:project_id) { project.id } it 'includes the file' do - expect(subject).to contain_exactly(an_instance_of(Gitlab::Ci::Config::External::File::Remote), - an_instance_of(Gitlab::Ci::Config::External::File::Local)) + expect(subject).to contain_exactly( + an_instance_of(Gitlab::Ci::Config::External::File::Remote), + an_instance_of(Gitlab::Ci::Config::External::File::Local) + ) end end diff --git a/spec/lib/gitlab/ci/config/header/input_spec.rb b/spec/lib/gitlab/ci/config/header/input_spec.rb index 5d1fa4a8e6e..df70d1fd7c8 100644 --- a/spec/lib/gitlab/ci/config/header/input_spec.rb +++ b/spec/lib/gitlab/ci/config/header/input_spec.rb @@ -40,12 +40,24 @@ RSpec.describe Gitlab::Ci::Config::Header::Input, feature_category: :pipeline_co end end - context 'when has a default value' do + context 'when has a string default value' do let(:input_hash) { { default: 'bar' } } it_behaves_like 'a valid input' end + context 'when has a numeric default value' do + let(:input_hash) { { default: 6.66 } } + + it_behaves_like 'a valid input' + end + + context 'when has a boolean default value' do + let(:input_hash) { { default: true } } + + it_behaves_like 'a valid input' + end + context 'when has a description value' do let(:input_hash) { { description: 'bar' } } @@ -103,4 +115,21 @@ RSpec.describe Gitlab::Ci::Config::Header::Input, feature_category: :pipeline_co it_behaves_like 'an invalid input' end + + context 'when the limit for allowed number of options is reached' do + let(:limit) { described_class::ALLOWED_OPTIONS_LIMIT } + let(:input_hash) { { default: 'value1', options: options } } + let(:options) { Array.new(limit.next) { |i| "value#{i}" } } + + describe '#valid?' do + it { is_expected.not_to be_valid } + end + + describe '#errors' do + it 'returns error about incorrect type' do + expect(config.errors).to contain_exactly( + "foo config cannot define more than #{limit} options") + end + end + end end diff --git a/spec/lib/gitlab/ci/config/interpolation/inputs_spec.rb b/spec/lib/gitlab/ci/config/interpolation/inputs_spec.rb index b0618081207..57ced4eab98 100644 --- a/spec/lib/gitlab/ci/config/interpolation/inputs_spec.rb +++ b/spec/lib/gitlab/ci/config/interpolation/inputs_spec.rb @@ -7,6 +7,90 @@ RSpec.describe Gitlab::Ci::Config::Interpolation::Inputs, feature_category: :pip let(:specs) { { foo: { default: 'bar' } } } let(:args) { {} } + context 'when inputs are valid strings and have options' do + let(:specs) { { foo: { default: 'one', options: %w[one two three] } } } + + context 'and the value is selected' do + let(:args) { { foo: 'two' } } + + it 'assigns the selected value' do + expect(inputs).to be_valid + expect(inputs.to_hash).to eq({ foo: 'two' }) + end + end + + context 'and the value is not selected' do + it 'assigns the default value' do + expect(inputs).to be_valid + expect(inputs.to_hash).to eq({ foo: 'one' }) + end + end + end + + context 'when inputs options are valid integers' do + let(:specs) { { foo: { default: 1, options: [1, 2, 3, 4, 5], type: 'number' } } } + + context 'and a value of the wrong type is given' do + let(:args) { { foo: 'word' } } + + it 'returns an error' do + expect(inputs).not_to be_valid + expect(inputs.errors).to contain_exactly( + "`foo` input: `word` cannot be used because it is not in the list of the allowed options", + "`foo` input: provided value is not a number" + ) + end + end + + context 'and the value is selected' do + let(:args) { { foo: 2 } } + + it 'assigns the selected value' do + expect(inputs).to be_valid + expect(inputs.to_hash).to eq({ foo: 2 }) + end + end + + context 'and the value is not selected' do + it 'assigns the default value' do + expect(inputs).to be_valid + expect(inputs.to_hash).to eq({ foo: 1 }) + end + end + end + + context 'when inputs have invalid type options' do + let(:specs) { { foo: { default: true, options: [true, false], type: 'boolean' } } } + + it 'returns an error' do + expect(inputs).not_to be_valid + expect(inputs.errors).to contain_exactly("`foo` input: Options can only be used with string and number inputs") + end + end + + context 'when inputs are valid with options but the default value is not in the options' do + let(:specs) { { foo: { default: 'coop', options: %w[one two three] } } } + + it 'returns an error' do + expect(inputs).not_to be_valid + expect(inputs.errors).to contain_exactly( + '`foo` input: `coop` cannot be used because it is not in the list of allowed options' + ) + end + end + + context 'when inputs are valid with options but the value is not in the options' do + let(:specs) { { foo: { default: 'one', options: %w[one two three] } } } + let(:args) { { foo: 'niet' } } + + it 'returns an error' do + expect(inputs).not_to be_valid + expect(inputs.errors).to contain_exactly( + '`foo` input: `niet` cannot be used because it is not in the list of allowed options' + ) + end + end + context 'when given unrecognized inputs' do let(:specs) { { foo: nil } } let(:args) { { foo: 'bar', test: 'bar' } } @@ -164,7 +248,7 @@ RSpec.describe Gitlab::Ci::Config::Interpolation::Inputs, feature_category: :pip context 'when the value is not a number' do let(:specs) { { number_input: { type: 'number' } } } - let(:args) { { number_input: 'NaN' } } + let(:args) { { number_input: false } } it 'is invalid' do expect(inputs).not_to be_valid diff --git a/spec/lib/gitlab/ci/jwt_v2_spec.rb b/spec/lib/gitlab/ci/jwt_v2_spec.rb index d45d8cacb88..c2ced10620b 100644 --- a/spec/lib/gitlab/ci/jwt_v2_spec.rb +++ b/spec/lib/gitlab/ci/jwt_v2_spec.rb @@ -33,14 +33,6 @@ RSpec.describe Gitlab::Ci::JwtV2, feature_category: :continuous_integration do describe '#payload' do subject(:payload) { ci_job_jwt_v2.payload } - it 'has correct values for the standard JWT attributes' do - aggregate_failures do - expect(payload[:iss]).to eq(Settings.gitlab.base_url) - expect(payload[:aud]).to eq(Settings.gitlab.base_url) - expect(payload[:sub]).to eq("project_path:#{project.full_path}:ref_type:branch:ref:#{pipeline.source_ref}") - end - end - it 'includes user identities when enabled' do expect(user).to receive(:pass_user_identities_to_ci_jwt).and_return(true) identities = payload[:user_identities].map { |identity| identity.slice(:extern_uid, :provider) } @@ -53,6 +45,34 @@ RSpec.describe Gitlab::Ci::JwtV2, feature_category: :continuous_integration do expect(payload).not_to include(:user_identities) end + context 'when oidc_issuer_url is disabled' do + before do + stub_feature_flags(oidc_issuer_url: false) + end + + it 'has correct values for the standard JWT attributes' do + aggregate_failures do + expect(payload[:iss]).to eq(Settings.gitlab.base_url) + expect(payload[:aud]).to eq(Settings.gitlab.base_url) + expect(payload[:sub]).to eq("project_path:#{project.full_path}:ref_type:branch:ref:#{pipeline.source_ref}") + end + end + end + + context 'when oidc_issuer_url is enabled' do + before do + stub_feature_flags(oidc_issuer_url: true) + end + + it 'has correct values for the standard JWT attributes' do + aggregate_failures do + expect(payload[:iss]).to eq(Gitlab.config.gitlab.url) + expect(payload[:aud]).to eq(Settings.gitlab.base_url) + expect(payload[:sub]).to eq("project_path:#{project.full_path}:ref_type:branch:ref:#{pipeline.source_ref}") + end + end + end + context 'when given an aud' do let(:aud) { 'AWS' } diff --git a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb index dacbe07c8b3..2c57106b07c 100644 --- a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb +++ b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb @@ -42,15 +42,16 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::CyclonedxProperties, feature_category: it { is_expected.to be_nil } end - context 'when no dependency_scanning properties are present' do + context 'when no dependency_scanning or container_scanning properties are present' do let(:properties) do [ { 'name' => 'gitlab:meta:schema_version', 'value' => '1' } ] end - it 'does not call dependency_scanning parser' do + it 'does not call source parsers' do expect(Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning).not_to receive(:source) + expect(Gitlab::Ci::Parsers::Sbom::Source::ContainerScanning).not_to receive(:source) parse_source_from_properties end @@ -85,4 +86,35 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::CyclonedxProperties, feature_category: parse_source_from_properties end end + + context 'when container_scanning properties are present' do + let(:properties) do + [ + { 'name' => 'gitlab:meta:schema_version', 'value' => '1' }, + { 'name' => 'gitlab:container_scanning:image:name', 'value' => 'photon' }, + { 'name' => 'gitlab:container_scanning:image:tag', 'value' => '5.0-20231007' }, + { 'name' => 'gitlab:container_scanning:operating_system:name', 'value' => 'Photon OS' }, + { 'name' => 'gitlab:container_scanning:operating_system:version', 'value' => '5.0' } + ] + end + + let(:expected_input) do + { + 'image' => { + 'name' => 'photon', + 'tag' => '5.0-20231007' + }, + 'operating_system' => { + 'name' => 'Photon OS', + 'version' => '5.0' + } + } + end + + it 'passes only supported properties to the container scanning parser' do + expect(Gitlab::Ci::Parsers::Sbom::Source::ContainerScanning).to receive(:source).with(expected_input) + + parse_source_from_properties + end + end end diff --git a/spec/lib/gitlab/ci/parsers/sbom/source/container_scanning_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/source/container_scanning_spec.rb new file mode 100644 index 00000000000..410b2c0098a --- /dev/null +++ b/spec/lib/gitlab/ci/parsers/sbom/source/container_scanning_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' + +RSpec.describe Gitlab::Ci::Parsers::Sbom::Source::ContainerScanning, feature_category: :container_scanning do + subject { described_class.source(property_data) } + + context 'when required properties are present' do + let(:property_data) do + { + 'image' => { + 'name' => 'photon', + 'tag' => '5.0-20231007' + }, + 'operating_system' => { + 'name' => 'Photon OS', + 'version' => '5.0' + } + } + end + + it 'returns expected source data' do + is_expected.to have_attributes( + source_type: :container_scanning, + data: property_data + ) + end + end + + context 'when required properties are missing' do + let(:property_data) do + { + 'operating_system' => { + 'name' => 'Photon OS', + 'version' => '5.0' + } + } + end + + it { is_expected.to be_nil } + end + + context 'when some operating_system properties are missing' do + let(:property_data) do + { + 'image' => { + 'name' => 'photon', + 'tag' => '5.0-20231007' + }, + 'operating_system' => { + 'name' => 'Photon OS' + } + } + end + + it { is_expected.to be_nil } + 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 648b8ac2db9..431a6d94c48 100644 --- a/spec/lib/gitlab/ci/parsers/security/common_spec.rb +++ b/spec/lib/gitlab/ci/parsers/security/common_spec.rb @@ -335,7 +335,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common, feature_category: :vulnera expect(flags).to contain_exactly( have_attributes(type: 'flagged-as-likely-false-positive', origin: 'post analyzer X', description: 'static string to sink'), - have_attributes(type: 'flagged-as-likely-false-positive', origin: 'post analyzer Y', description: 'integer to sink') + have_attributes(type: 'flagged-as-likely-false-positive', origin: 'post analyzer Y', description: 'integer to sink') ) end end diff --git a/spec/lib/gitlab/ci/parsers/security/secret_detection_spec.rb b/spec/lib/gitlab/ci/parsers/security/secret_detection_spec.rb index 13999b2a9e5..640bed0d329 100644 --- a/spec/lib/gitlab/ci/parsers/security/secret_detection_spec.rb +++ b/spec/lib/gitlab/ci/parsers/security/secret_detection_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::SecretDetection do let(:created_at) { 2.weeks.ago } context "when parsing valid reports" do - where(report_format: %i(secret_detection)) + where(report_format: %i[secret_detection]) with_them do let(:report) { Gitlab::Ci::Reports::Security::Report.new(artifact.file_type, pipeline, created_at) } diff --git a/spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb index ddd0de69d79..70d73a8095c 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb @@ -21,11 +21,11 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::TemplateUsage do expect(command).to( receive(:yaml_processor_result) .and_return( - double(included_templates: %w(Template-1 Template-2)) + double(included_templates: %w[Template-1 Template-2]) ) ) - %w(Template-1 Template-2).each do |expected_template| + %w[Template-1 Template-2].each do |expected_template| expect(Gitlab::UsageDataCounters::CiTemplateUniqueCounter).to( receive(:track_unique_project_event) .with(project: project, template: expected_template, config_source: pipeline.config_source, user: user) diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/equals_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/equals_spec.rb index ab223ae41fa..eb71cc0f0bc 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/equals_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/equals_spec.rb @@ -49,7 +49,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Equals do context 'when left and right are equal' do where(:left_value, :right_value) do - [%w(string string)] + [%w[string string]] end with_them do diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb index 54e569f424b..ef9b8f2b82f 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb @@ -395,7 +395,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build, feature_category: :pipeline_co end context 'when root_variables_inheritance is an array' do - let(:root_variables_inheritance) { %w(VAR1 VAR2 VAR3) } + let(:root_variables_inheritance) { %w[VAR1 VAR2 VAR3] } it 'returns calculated yaml variables' do expect(subject[:yaml_variables]).to match_array( diff --git a/spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb b/spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb index ad8f1dc11f8..6d8b472a240 100644 --- a/spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb +++ b/spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb @@ -14,7 +14,7 @@ RSpec.describe Gitlab::Ci::Reports::AccessibilityReportsComparer do "type" => "error", "typeCode" => 1, "message" => "Anchor element found with a valid href attribute, but no link content has been supplied.", - "context" => %{<a href="/" class="navbar-brand animated"><svg height="36" viewBox="0 0 1...</a>}, + "context" => %(<a href="/" class="navbar-brand animated"><svg height="36" viewBox="0 0 1...</a>), "selector" => "#main-nav > div:nth-child(1) > a", "runner" => "htmlcs", "runnerExtras" => {} @@ -29,7 +29,7 @@ RSpec.describe Gitlab::Ci::Reports::AccessibilityReportsComparer do "type" => "error", "typeCode" => 1, "message" => "This element has insufficient contrast at this conformance level.", - "context" => %{<a href="/stages-devops-lifecycle/" class="main-nav-link">Product</a>}, + "context" => %(<a href="/stages-devops-lifecycle/" class="main-nav-link">Product</a>), "selector" => "#main-nav > div:nth-child(2) > ul > li:nth-child(1) > a", "runner" => "htmlcs", "runnerExtras" => {} diff --git a/spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb b/spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb index af6844491ca..dff59474746 100644 --- a/spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb +++ b/spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb @@ -12,7 +12,7 @@ RSpec.describe Gitlab::Ci::Reports::AccessibilityReports do "type": "error", "typeCode": 1, "message": "Anchor element found with a valid href attribute, but no link content has been supplied.", - "context": %{<a href="/customers/worldline"><svg viewBox="0 0 509 89" xmln...</a>}, + "context": %(<a href="/customers/worldline"><svg viewBox="0 0 509 89" xmln...</a>), "selector": "html > body > div:nth-child(9) > div:nth-child(2) > a:nth-child(17)", "runner": "htmlcs", "runnerExtras": {} @@ -22,7 +22,7 @@ RSpec.describe Gitlab::Ci::Reports::AccessibilityReports do "type": "error", "typeCode": 1, "message": "Anchor element found with a valid href attribute, but no link content has been supplied.", - "context": %{<a href="/customers/equinix"><svg xmlns="http://www.w3.org/...</a>}, + "context": %(<a href="/customers/equinix"><svg xmlns="http://www.w3.org/...</a>), "selector": "html > body > div:nth-child(9) > div:nth-child(2) > a:nth-child(18)", "runner": "htmlcs", "runnerExtras": {} diff --git a/spec/lib/gitlab/ci/reports/test_suite_spec.rb b/spec/lib/gitlab/ci/reports/test_suite_spec.rb index 05f6a8a8cb6..46ab0802200 100644 --- a/spec/lib/gitlab/ci/reports/test_suite_spec.rb +++ b/spec/lib/gitlab/ci/reports/test_suite_spec.rb @@ -192,7 +192,7 @@ RSpec.describe Gitlab::Ci::Reports::TestSuite do end context 'when there are multiple test cases' do - let(:status_ordered) { %w(error failed success skipped) } + let(:status_ordered) { %w[error failed success skipped] } before do test_suite.add_test_case(test_case_success) diff --git a/spec/lib/gitlab/ci/status/composite_spec.rb b/spec/lib/gitlab/ci/status/composite_spec.rb index cbf0976c976..e46ad573235 100644 --- a/spec/lib/gitlab/ci/status/composite_spec.rb +++ b/spec/lib/gitlab/ci/status/composite_spec.rb @@ -43,28 +43,29 @@ RSpec.describe Gitlab::Ci::Status::Composite, feature_category: :continuous_inte context 'allow_failure: false' do where(:build_statuses, :dag, :result, :has_warnings) do - %i(skipped) | false | 'skipped' | false - %i(skipped success) | false | 'success' | false - %i(skipped success) | true | 'skipped' | false - %i(created) | false | 'created' | false - %i(preparing) | false | 'preparing' | false - %i(canceled success skipped) | false | 'canceled' | false - %i(canceled success skipped) | true | 'skipped' | false - %i(pending created skipped) | false | 'pending' | false - %i(pending created skipped success) | false | 'running' | false - %i(running created skipped success) | false | 'running' | false - %i(pending created skipped) | true | 'skipped' | false - %i(pending created skipped success) | true | 'skipped' | false - %i(running created skipped success) | true | 'skipped' | false - %i(success waiting_for_resource) | false | 'waiting_for_resource' | false - %i(success manual) | false | 'manual' | false - %i(success scheduled) | false | 'scheduled' | false - %i(created preparing) | false | 'preparing' | false - %i(created success pending) | false | 'running' | false - %i(skipped success failed) | false | 'failed' | false - %i(skipped success failed) | true | 'skipped' | false - %i(success manual) | true | 'manual' | false - %i(success failed created) | true | 'running' | false + %i[skipped] | false | 'skipped' | false + %i[skipped success] | false | 'success' | false + %i[skipped success] | true | 'skipped' | false + %i[created] | false | 'created' | false + %i[preparing] | false | 'preparing' | false + %i[canceled success skipped] | false | 'canceled' | false + %i[canceled success skipped] | true | 'skipped' | false + %i[pending created skipped] | false | 'pending' | false + %i[pending created skipped success] | false | 'running' | false + %i[running created skipped success] | false | 'running' | false + %i[pending created skipped] | true | 'skipped' | false + %i[pending created skipped success] | true | 'skipped' | false + %i[running created skipped success] | true | 'skipped' | false + %i[success waiting_for_resource] | false | 'waiting_for_resource' | false + %i[success waiting_for_callback] | false | 'waiting_for_callback' | false + %i[success manual] | false | 'manual' | false + %i[success scheduled] | false | 'scheduled' | false + %i[created preparing] | false | 'preparing' | false + %i[created success pending] | false | 'running' | false + %i[skipped success failed] | false | 'failed' | false + %i[skipped success failed] | true | 'skipped' | false + %i[success manual] | true | 'manual' | false + %i[success failed created] | true | 'running' | false end with_them do @@ -78,13 +79,13 @@ RSpec.describe Gitlab::Ci::Status::Composite, feature_category: :continuous_inte context 'allow_failure: true' do where(:build_statuses, :dag, :result, :has_warnings) do - %i(manual) | false | 'skipped' | false - %i(skipped failed) | false | 'success' | true - %i(skipped failed) | true | 'skipped' | true - %i(success manual) | true | 'skipped' | false - %i(success manual) | false | 'success' | false - %i(created failed) | false | 'created' | true - %i(preparing manual) | false | 'preparing' | false + %i[manual] | false | 'skipped' | false + %i[skipped failed] | false | 'success' | true + %i[skipped failed] | true | 'skipped' | true + %i[success manual] | true | 'skipped' | false + %i[success manual] | false | 'success' | false + %i[created failed] | false | 'created' | true + %i[preparing manual] | false | 'preparing' | false end with_them do diff --git a/spec/lib/gitlab/ci/status/stage/factory_spec.rb b/spec/lib/gitlab/ci/status/stage/factory_spec.rb index 34e430202c9..98fefea7bdf 100644 --- a/spec/lib/gitlab/ci/status/stage/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/stage/factory_spec.rb @@ -22,7 +22,7 @@ RSpec.describe Gitlab::Ci::Status::Stage::Factory, feature_category: :continuous end context 'when stage has a core status' do - (Ci::HasStatus::AVAILABLE_STATUSES - %w(manual skipped scheduled)).each do |core_status| + (Ci::HasStatus::AVAILABLE_STATUSES - %w[manual skipped scheduled]).each do |core_status| context "when core status is #{core_status}" do let(:stage) { create(:ci_stage, pipeline: pipeline, status: core_status) } diff --git a/spec/lib/gitlab/ci/status/waiting_for_callback_spec.rb b/spec/lib/gitlab/ci/status/waiting_for_callback_spec.rb new file mode 100644 index 00000000000..6c833e96137 --- /dev/null +++ b/spec/lib/gitlab/ci/status/waiting_for_callback_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Status::WaitingForCallback, feature_category: :deployment_management do + subject do + described_class.new(double, double) + end + + describe '#text' do + it { expect(subject.text).to eq 'Waiting' } + end + + describe '#label' do + it { expect(subject.label).to eq 'waiting for callback' } + end + + describe '#icon' do + it { expect(subject.icon).to eq 'status_pending' } + end + + describe '#favicon' do + it { expect(subject.favicon).to eq 'favicon_status_pending' } + end + + describe '#group' do + it { expect(subject.group).to eq 'waiting-for-callback' } + end + + describe '#name' do + it { expect(subject.name).to eq 'WAITING_FOR_CALLBACK' } + end + + describe '#details_path' do + it { expect(subject.details_path).to be_nil } + end +end diff --git a/spec/lib/gitlab/ci/tags/bulk_insert_spec.rb b/spec/lib/gitlab/ci/tags/bulk_insert_spec.rb index 460ecbb05d0..c5125689207 100644 --- a/spec/lib/gitlab/ci/tags/bulk_insert_spec.rb +++ b/spec/lib/gitlab/ci/tags/bulk_insert_spec.rb @@ -13,7 +13,7 @@ RSpec.describe Gitlab::Ci::Tags::BulkInsert do subject(:service) { described_class.new(statuses) } describe 'gem version' do - let(:acceptable_version) { '9.0.1' } + let(:acceptable_version) { '10.0.0' } let(:error_message) do <<~MESSAGE diff --git a/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb index acb296082b8..dc9999ab9e4 100644 --- a/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb @@ -58,7 +58,7 @@ RSpec.describe 'Jobs/Deploy.gitlab-ci.yml' do context 'with no cluster or agent' do it 'does not create any kubernetes deployment jobs' do - expect(build_names).to eq %w(placeholder) + expect(build_names).to eq %w[placeholder] end end @@ -68,7 +68,7 @@ RSpec.describe 'Jobs/Deploy.gitlab-ci.yml' do end it 'does not create any kubernetes deployment jobs' do - expect(build_names).to eq %w(placeholder) + expect(build_names).to eq %w[placeholder] end end @@ -81,7 +81,7 @@ RSpec.describe 'Jobs/Deploy.gitlab-ci.yml' do it 'when CI_DEPLOY_FREEZE is present' do create(:ci_variable, project: project, key: 'CI_DEPLOY_FREEZE', value: 'true') - expect(build_names).to eq %w(placeholder) + expect(build_names).to eq %w[placeholder] end it 'when CANARY_ENABLED' do diff --git a/spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb index 2b9213ea921..86bc9259789 100644 --- a/spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb @@ -44,7 +44,7 @@ RSpec.describe 'Jobs/SAST-IaC.latest.gitlab-ci.yml', feature_category: :continuo it 'creates a pipeline with the expected jobs' do expect(pipeline).to be_merge_request_event expect(pipeline.errors.full_messages).to be_empty - expect(build_names).to match_array(%w(kics-iac-sast)) + expect(build_names).to match_array(%w[kics-iac-sast]) end end end diff --git a/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb index 09ca2678de5..7471dc58e44 100644 --- a/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb @@ -94,7 +94,7 @@ RSpec.describe 'Auto-DevOps.gitlab-ci.yml', feature_category: :auto_devops do project.repository.create_branch(pipeline_branch, default_branch) end - %w(review_ecs review_fargate).each do |job| + %w[review_ecs review_fargate].each do |job| it_behaves_like 'no ECS job when AUTO_DEVOPS_PLATFORM_TARGET is not present' do let(:job_name) { job } end @@ -142,7 +142,7 @@ RSpec.describe 'Auto-DevOps.gitlab-ci.yml', feature_category: :auto_devops do context 'when the project has no active cluster' do it 'only creates a build and a test stage' do - expect(pipeline.stages_names).to eq(%w(build test)) + expect(pipeline.stages_names).to eq(%w[build test]) end it_behaves_like 'no Kubernetes deployment job' @@ -273,25 +273,25 @@ RSpec.describe 'Auto-DevOps.gitlab-ci.yml', feature_category: :auto_devops do using RSpec::Parameterized::TableSyntax where(:case_name, :files, :variables, :include_build_names, :not_include_build_names) do - 'No match' | { 'README.md' => '' } | {} | %w() | %w(build test) - 'Buildpack' | { 'README.md' => '' } | { 'BUILDPACK_URL' => 'http://example.com' } | %w(build test) | %w() - 'Explicit set' | { 'README.md' => '' } | { 'AUTO_DEVOPS_EXPLICITLY_ENABLED' => '1' } | %w(build test) | %w() - 'Explicit unset' | { 'README.md' => '' } | { 'AUTO_DEVOPS_EXPLICITLY_ENABLED' => '0' } | %w() | %w(build test) - 'DOCKERFILE_PATH' | { 'README.md' => '' } | { 'DOCKERFILE_PATH' => 'Docker.file' } | %w(build test) | %w() - 'Dockerfile' | { 'Dockerfile' => '' } | {} | %w(build test) | %w() - 'Clojure' | { 'project.clj' => '' } | {} | %w(build test) | %w() - 'Go modules' | { 'go.mod' => '' } | {} | %w(build test) | %w() - 'Go gb' | { 'src/gitlab.com/gopackage.go' => '' } | {} | %w(build test) | %w() - 'Gradle' | { 'gradlew' => '' } | {} | %w(build test) | %w() - 'Java' | { 'pom.xml' => '' } | {} | %w(build test) | %w() - 'Multi-buildpack' | { '.buildpacks' => '' } | {} | %w(build test) | %w() - 'NodeJS' | { 'package.json' => '' } | {} | %w(build test) | %w() - 'PHP' | { 'composer.json' => '' } | {} | %w(build test) | %w() - 'Play' | { 'conf/application.conf' => '' } | {} | %w(build test) | %w() - 'Python' | { 'Pipfile' => '' } | {} | %w(build test) | %w() - 'Ruby' | { 'Gemfile' => '' } | {} | %w(build test) | %w() - 'Scala' | { 'build.sbt' => '' } | {} | %w(build test) | %w() - 'Static' | { '.static' => '' } | {} | %w(build test) | %w() + 'No match' | { 'README.md' => '' } | {} | %w[] | %w[build test] + 'Buildpack' | { 'README.md' => '' } | { 'BUILDPACK_URL' => 'http://example.com' } | %w[build test] | %w[] + 'Explicit set' | { 'README.md' => '' } | { 'AUTO_DEVOPS_EXPLICITLY_ENABLED' => '1' } | %w[build test] | %w[] + 'Explicit unset' | { 'README.md' => '' } | { 'AUTO_DEVOPS_EXPLICITLY_ENABLED' => '0' } | %w[] | %w[build test] + 'DOCKERFILE_PATH' | { 'README.md' => '' } | { 'DOCKERFILE_PATH' => 'Docker.file' } | %w[build test] | %w[] + 'Dockerfile' | { 'Dockerfile' => '' } | {} | %w[build test] | %w[] + 'Clojure' | { 'project.clj' => '' } | {} | %w[build test] | %w[] + 'Go modules' | { 'go.mod' => '' } | {} | %w[build test] | %w[] + 'Go gb' | { 'src/gitlab.com/gopackage.go' => '' } | {} | %w[build test] | %w[] + 'Gradle' | { 'gradlew' => '' } | {} | %w[build test] | %w[] + 'Java' | { 'pom.xml' => '' } | {} | %w[build test] | %w[] + 'Multi-buildpack' | { '.buildpacks' => '' } | {} | %w[build test] | %w[] + 'NodeJS' | { 'package.json' => '' } | {} | %w[build test] | %w[] + 'PHP' | { 'composer.json' => '' } | {} | %w[build test] | %w[] + 'Play' | { 'conf/application.conf' => '' } | {} | %w[build test] | %w[] + 'Python' | { 'Pipfile' => '' } | {} | %w[build test] | %w[] + 'Ruby' | { 'Gemfile' => '' } | {} | %w[build test] | %w[] + 'Scala' | { 'build.sbt' => '' } | {} | %w[build test] | %w[] + 'Static' | { '.static' => '' } | {} | %w[build test] | %w[] end with_them do diff --git a/spec/lib/gitlab/ci/variables/collection/item_spec.rb b/spec/lib/gitlab/ci/variables/collection/item_spec.rb index d96c8f1bd0c..aa612899f4b 100644 --- a/spec/lib/gitlab/ci/variables/collection/item_spec.rb +++ b/spec/lib/gitlab/ci/variables/collection/item_spec.rb @@ -123,11 +123,11 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item, feature_category: :secre }, "simple variable reference": { variable: { key: 'VAR', value: 'something_$VAR2' }, - expected_depends_on: %w(VAR2) + expected_depends_on: %w[VAR2] }, "complex expansion": { variable: { key: 'VAR', value: 'something_${VAR2}_$VAR3' }, - expected_depends_on: %w(VAR2 VAR3) + expected_depends_on: %w[VAR2 VAR3] }, "complex expansion in raw variable": { variable: { key: 'VAR', value: 'something_${VAR2}_$VAR3', raw: true }, @@ -135,7 +135,7 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item, feature_category: :secre }, "complex expansions for Windows": { variable: { key: 'variable3', value: 'key%variable%%variable2%' }, - expected_depends_on: %w(variable variable2) + expected_depends_on: %w[variable variable2] } } end @@ -282,7 +282,7 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item, feature_category: :secre it '#depends_on contains names of dependencies' do runner_variable = described_class.new(key: 'CI_VAR', value: '${CI_VAR_2}-123-$CI_VAR_3') - expect(runner_variable.depends_on).to eq(%w(CI_VAR_2 CI_VAR_3)) + expect(runner_variable.depends_on).to eq(%w[CI_VAR_2 CI_VAR_3]) end end diff --git a/spec/lib/gitlab/ci/yaml_processor/dag_spec.rb b/spec/lib/gitlab/ci/yaml_processor/dag_spec.rb index 082febacbd7..496d89403d5 100644 --- a/spec/lib/gitlab/ci/yaml_processor/dag_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor/dag_spec.rb @@ -3,38 +3,48 @@ require 'fast_spec_helper' require 'tsort' -RSpec.describe Gitlab::Ci::YamlProcessor::Dag do +RSpec.describe Gitlab::Ci::YamlProcessor::Dag, feature_category: :pipeline_composition do let(:nodes) { {} } subject(:result) { described_class.new(nodes).tsort } context 'when it is a regular pipeline' do let(:nodes) do - { 'job_c' => %w(job_b job_d), 'job_d' => %w(job_a), 'job_b' => %w(job_a), 'job_a' => %w() } + { 'job_c' => %w[job_b job_d], 'job_d' => %w[job_a], 'job_b' => %w[job_a], 'job_a' => %w[] } end it 'returns ordered jobs' do - expect(result).to eq(%w(job_a job_b job_d job_c)) + expect(result).to eq(%w[job_a job_b job_d job_c]) end end context 'when there is a circular dependency' do let(:nodes) do - { 'job_a' => %w(job_c), 'job_b' => %w(job_a), 'job_c' => %w(job_b) } + { 'job_a' => %w[job_c], 'job_b' => %w[job_a], 'job_c' => %w[job_b] } end - it 'raises TSort::Cyclic' do + it 'raises TSort::Cyclic error' do expect { result }.to raise_error(TSort::Cyclic, /topological sort failed/) end + + context 'when a job has a self-dependency' do + let(:nodes) do + { 'job_a' => %w[job_a] } + end + + it 'raises TSort::Cyclic error' do + expect { result }.to raise_error(TSort::Cyclic, "self-dependency: job_a") + end + end end context 'when there are some missing jobs' do let(:nodes) do - { 'job_a' => %w(job_d job_f), 'job_b' => %w(job_a job_c job_e) } + { 'job_a' => %w[job_d job_f], 'job_b' => %w[job_a job_c job_e] } end it 'ignores the missing ones and returns in a valid order' do - expect(result).to eq(%w(job_d job_f job_a job_c job_e job_b)) + expect(result).to eq(%w[job_d job_f job_a job_c job_e job_b]) end end end diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb index 81bc8c7ab9a..f01c1c7d053 100644 --- a/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -1510,7 +1510,7 @@ module Gitlab it 'correctly extends rspec job' do expect(config_processor.builds).to be_one - expect(subject.dig(:options, :script)).to eq %w(test) + expect(subject.dig(:options, :script)).to eq %w[test] expect(subject.dig(:options, :image, :name)).to eq 'ruby:alpine' end end @@ -1595,7 +1595,7 @@ module Gitlab it 'correctly extends rspec job' do expect(config_processor.builds).to be_one expect(subject.dig(:options, :before_script)).to eq ["bundle install"] - expect(subject.dig(:options, :script)).to eq %w(rspec) + expect(subject.dig(:options, :script)).to eq %w[rspec] expect(subject.dig(:options, :image, :name)).to eq 'image:test' expect(subject.dig(:when)).to eq 'always' end @@ -2386,7 +2386,7 @@ module Gitlab end context 'dependencies to builds' do - let(:dependencies) { %w(build1 build2) } + let(:dependencies) { %w[build1 build2] } it { is_expected.to be_valid } end @@ -2457,7 +2457,7 @@ module Gitlab end context 'needs a job from the same stage' do - let(:needs) { %w(test2) } + let(:needs) { %w[test2] } it 'creates jobs with valid specifications' do expect(subject.builds.size).to eq(7) @@ -2494,7 +2494,7 @@ module Gitlab end context 'needs two builds' do - let(:needs) { %w(build1 build2) } + let(:needs) { %w[build1 build2] } it "does create jobs with valid specification" do expect(subject.builds.size).to eq(7) @@ -2578,7 +2578,7 @@ module Gitlab end context 'needs parallel job' do - let(:needs) { %w(parallel) } + let(:needs) { %w[parallel] } it "does create jobs with valid specification" do expect(subject.builds.size).to eq(7) @@ -2707,7 +2707,7 @@ module Gitlab context 'duplicate needs' do context 'when needs are specified in an array' do - let(:needs) { %w(build1 build1) } + let(:needs) { %w[build1 build1] } it_behaves_like 'returns errors', 'test1 has the following needs duplicated: build1.' end @@ -2736,8 +2736,8 @@ module Gitlab end context 'needs and dependencies that are mismatching' do - let(:needs) { %w(build1) } - let(:dependencies) { %w(build2) } + let(:needs) { %w[build1] } + let(:dependencies) { %w[build2] } it_behaves_like 'returns errors', 'jobs:test1 dependencies the build2 should be part of needs' end @@ -2750,13 +2750,13 @@ module Gitlab ] end - let(:dependencies) { %w(build3) } + let(:dependencies) { %w[build3] } it_behaves_like 'returns errors', 'jobs:test1 dependencies the build3 should be part of needs' end context 'needs with an array type and dependency with a string type' do - let(:needs) { %w(build1) } + let(:needs) { %w[build1] } let(:dependencies) { 'deploy' } it_behaves_like 'returns errors', 'jobs:test1 dependencies should be an array of strings' @@ -2764,7 +2764,7 @@ module Gitlab context 'needs with a string type and dependency with an array type' do let(:needs) { 'build1' } - let(:dependencies) { %w(deploy) } + let(:dependencies) { %w[deploy] } it_behaves_like 'returns errors', 'jobs:test1:needs config can only be a hash or an array' end @@ -3252,7 +3252,7 @@ module Gitlab end context 'returns errors if job stage is not a defined stage' do - let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", stage: "acceptance" } }) } + let(:config) { YAML.dump({ stages: %w[build test], rspec: { script: "test", stage: "acceptance" } }) } it_behaves_like 'returns errors', 'rspec job: chosen stage does not exist; available stages are .pre, build, test, .post' end @@ -3288,37 +3288,37 @@ module Gitlab end context 'returns errors if job artifacts:name is not an a string' do - let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", artifacts: { name: 1 } } }) } + let(:config) { YAML.dump({ stages: %w[build test], rspec: { script: "test", artifacts: { name: 1 } } }) } it_behaves_like 'returns errors', 'jobs:rspec:artifacts name should be a string' end context 'returns errors if job artifacts:when is not an a predefined value' do - let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", artifacts: { when: 1 } } }) } + let(:config) { YAML.dump({ stages: %w[build test], rspec: { script: "test", artifacts: { when: 1 } } }) } it_behaves_like 'returns errors', 'jobs:rspec:artifacts when should be one of: on_success, on_failure, always' end context 'returns errors if job artifacts:expire_in is not an a string' do - let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", artifacts: { expire_in: 1 } } }) } + let(:config) { YAML.dump({ stages: %w[build test], rspec: { script: "test", artifacts: { expire_in: 1 } } }) } it_behaves_like 'returns errors', 'jobs:rspec:artifacts expire in should be a duration' end context 'returns errors if job artifacts:expire_in is not an a valid duration' do - let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", artifacts: { expire_in: "7 elephants" } } }) } + let(:config) { YAML.dump({ stages: %w[build test], rspec: { script: "test", artifacts: { expire_in: "7 elephants" } } }) } it_behaves_like 'returns errors', 'jobs:rspec:artifacts expire in should be a duration' end context 'returns errors if job artifacts:untracked is not an array of strings' do - let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", artifacts: { untracked: "string" } } }) } + let(:config) { YAML.dump({ stages: %w[build test], rspec: { script: "test", artifacts: { untracked: "string" } } }) } it_behaves_like 'returns errors', 'jobs:rspec:artifacts untracked should be a boolean value' end context 'returns errors if job artifacts:paths is not an array of strings' do - let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", artifacts: { paths: "string" } } }) } + let(:config) { YAML.dump({ stages: %w[build test], rspec: { script: "test", artifacts: { paths: "string" } } }) } it_behaves_like 'returns errors', 'jobs:rspec:artifacts paths should be an array of strings' end @@ -3342,49 +3342,49 @@ module Gitlab end context 'returns errors if job cache:key is not an a string' do - let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", cache: { key: 1 } } }) } + let(:config) { YAML.dump({ stages: %w[build test], rspec: { script: "test", cache: { key: 1 } } }) } it_behaves_like 'returns errors', "jobs:rspec:cache:key should be a hash, a string or a symbol" end context 'returns errors if job cache:key:files is not an array of strings' do - let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", cache: { key: { files: [1] } } } }) } + let(:config) { YAML.dump({ stages: %w[build test], rspec: { script: "test", cache: { key: { files: [1] } } } }) } it_behaves_like 'returns errors', 'jobs:rspec:cache:key:files config should be an array of strings' end context 'returns errors if job cache:key:files is an empty array' do - let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", cache: { key: { files: [] } } } }) } + let(:config) { YAML.dump({ stages: %w[build test], rspec: { script: "test", cache: { key: { files: [] } } } }) } it_behaves_like 'returns errors', 'jobs:rspec:cache:key:files config requires at least 1 item' end context 'returns errors if job defines only cache:key:prefix' do - let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", cache: { key: { prefix: 'prefix-key' } } } }) } + let(:config) { YAML.dump({ stages: %w[build test], rspec: { script: "test", cache: { key: { prefix: 'prefix-key' } } } }) } it_behaves_like 'returns errors', 'jobs:rspec:cache:key config missing required keys: files' end context 'returns errors if job cache:key:prefix is not an a string' do - let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", cache: { key: { prefix: 1, files: ['file'] } } } }) } + let(:config) { YAML.dump({ stages: %w[build test], rspec: { script: "test", cache: { key: { prefix: 1, files: ['file'] } } } }) } it_behaves_like 'returns errors', 'jobs:rspec:cache:key:prefix config should be a string or symbol' end context "returns errors if job cache:untracked is not an array of strings" do - let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", cache: { untracked: "string" } } }) } + let(:config) { YAML.dump({ stages: %w[build test], rspec: { script: "test", cache: { untracked: "string" } } }) } it_behaves_like 'returns errors', "jobs:rspec:cache:untracked config should be a boolean value" end context "returns errors if job cache:paths is not an array of strings" do - let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", cache: { paths: "string" } } }) } + let(:config) { YAML.dump({ stages: %w[build test], rspec: { script: "test", cache: { paths: "string" } } }) } it_behaves_like 'returns errors', "jobs:rspec:cache:paths config should be an array of strings" end context "returns errors if job dependencies is not an array of strings" do - let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", dependencies: "string" } }) } + let(:config) { YAML.dump({ stages: %w[build test], rspec: { script: "test", dependencies: "string" } }) } it_behaves_like 'returns errors', "jobs:rspec dependencies should be an array of strings" end @@ -3433,7 +3433,24 @@ module Gitlab YAML end - it_behaves_like 'returns errors', 'The pipeline has circular dependencies' + it_behaves_like 'returns errors', 'The pipeline has circular dependencies: topological sort failed: ["job_a", "job_c", "job_b"]' + + context 'when a job has a self-dependency' do + let(:config) do + <<~YAML + job_0: + stage: test + script: build + + job: + stage: test + script: build + needs: [job_0, job] + YAML + end + + it_behaves_like 'returns errors', 'The pipeline has circular dependencies: self-dependency: job' + end end end @@ -3668,6 +3685,70 @@ module Gitlab it { is_expected.to be_valid } end end + + context 'for pages jobs', feature_category: :pages do + context 'on publish option' do + context 'when not in a pages job' do + let(:config) do + <<-EOYML + not-pages: + script: echo + publish: 'foo' + EOYML + end + + it_behaves_like 'returns errors', 'jobs:not-pages publish can only be used within a `pages` job' + end + + context 'when in a pages job' do + let(:config) do + <<-EOYML + pages: + script: echo + publish: 'foo' + EOYML + end + + it { is_expected.to be_valid } + + it 'sets the publish configuration' do + expect(subject.builds.first[:options][:publish]).to eq('foo') + end + end + end + + context 'on pages option' do + context 'when not in a pages job' do + let(:config) do + <<-EOYML + not-pages: + script: echo + pages: + path_prefix: 'foo' + EOYML + end + + it_behaves_like 'returns errors', 'jobs:not-pages pages can only be used within a `pages` job' + end + + context 'when in a pages job' do + let(:config) do + <<-EOYML + pages: + script: echo + pages: + path_prefix: 'foo' + EOYML + end + + it { is_expected.to be_valid } + + it 'sets the pages configuration' do + expect(subject.builds.first[:options][:pages]).to eq(path_prefix: 'foo') + end + end + end + end end end end |