diff options
Diffstat (limited to 'spec/lib/gitlab/ci')
38 files changed, 1415 insertions, 203 deletions
diff --git a/spec/lib/gitlab/ci/badge/coverage/metadata_spec.rb b/spec/lib/gitlab/ci/badge/coverage/metadata_spec.rb new file mode 100644 index 00000000000..6d272f060ab --- /dev/null +++ b/spec/lib/gitlab/ci/badge/coverage/metadata_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'lib/gitlab/ci/badge/shared/metadata' + +RSpec.describe Gitlab::Ci::Badge::Coverage::Metadata do + let(:badge) do + double(project: create(:project), ref: 'feature', job: 'test') + end + + let(:metadata) { described_class.new(badge) } + + it_behaves_like 'badge metadata' + + describe '#title' do + it 'returns coverage report title' do + expect(metadata.title).to eq 'coverage report' + end + end + + describe '#image_url' do + it 'returns valid url' do + expect(metadata.image_url).to include 'badges/feature/coverage.svg' + end + end + + describe '#link_url' do + it 'returns valid link' do + expect(metadata.link_url).to include 'commits/feature' + end + end +end diff --git a/spec/lib/gitlab/ci/badge/coverage/report_spec.rb b/spec/lib/gitlab/ci/badge/coverage/report_spec.rb new file mode 100644 index 00000000000..13696d815aa --- /dev/null +++ b/spec/lib/gitlab/ci/badge/coverage/report_spec.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Badge::Coverage::Report do + let_it_be(:project) { create(:project) } + let_it_be(:success_pipeline) { create(:ci_pipeline, :success, project: project) } + let_it_be(:running_pipeline) { create(:ci_pipeline, :running, project: project) } + let_it_be(:failure_pipeline) { create(:ci_pipeline, :failed, project: project) } + + let_it_be(:builds) do + [ + create(:ci_build, :success, pipeline: success_pipeline, coverage: 40, created_at: 9.seconds.ago, name: 'coverage'), + create(:ci_build, :success, pipeline: success_pipeline, coverage: 60, created_at: 8.seconds.ago) + ] + end + + let(:badge) do + described_class.new(project, 'master', opts: { job: job_name }) + end + + let(:job_name) { nil } + + describe '#entity' do + it 'describes a coverage' do + expect(badge.entity).to eq 'coverage' + end + end + + describe '#metadata' do + it 'returns correct metadata' do + expect(badge.metadata.image_url).to include 'coverage.svg' + end + end + + describe '#template' do + it 'returns correct template' do + expect(badge.template.key_text).to eq 'coverage' + end + end + + describe '#status' do + context 'with no job specified' do + it 'returns the most recent successful pipeline coverage value' do + expect(badge.status).to eq(50.00) + end + + context 'and no successful pipelines' do + before do + allow(badge).to receive(:successful_pipeline).and_return(nil) + end + + it 'returns nil' do + expect(badge.status).to eq(nil) + end + end + end + + context 'with a blank job name' do + let(:job_name) { ' ' } + + it 'returns the latest successful pipeline coverage value' do + expect(badge.status).to eq(50.00) + end + end + + context 'with an unmatching job name specified' do + let(:job_name) { 'incorrect name' } + + it 'returns nil' do + expect(badge.status).to be_nil + end + end + + context 'with a matching job name specified' do + let(:job_name) { 'coverage' } + + it 'returns the pipeline coverage value' do + expect(badge.status).to eq(40.00) + end + + context 'with a more recent running pipeline' do + let!(:another_build) { create(:ci_build, :success, pipeline: running_pipeline, coverage: 20, created_at: 7.seconds.ago, name: 'coverage') } + + it 'returns the running pipeline coverage value' do + expect(badge.status).to eq(20.00) + end + end + + context 'with a more recent failed pipeline' do + let!(:another_build) { create(:ci_build, :success, pipeline: failure_pipeline, coverage: 10, created_at: 6.seconds.ago, name: 'coverage') } + + it 'returns the failed pipeline coverage value' do + expect(badge.status).to eq(10.00) + end + end + end + end +end diff --git a/spec/lib/gitlab/ci/badge/coverage/template_spec.rb b/spec/lib/gitlab/ci/badge/coverage/template_spec.rb new file mode 100644 index 00000000000..f010d1bce50 --- /dev/null +++ b/spec/lib/gitlab/ci/badge/coverage/template_spec.rb @@ -0,0 +1,182 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Badge::Coverage::Template do + let(:badge) { double(entity: 'coverage', status: 90.00, customization: {}) } + let(:template) { described_class.new(badge) } + + describe '#key_text' do + it 'says coverage by default' do + expect(template.key_text).to eq 'coverage' + end + + context 'when custom key_text is defined' do + before do + allow(badge).to receive(:customization).and_return({ key_text: "custom text" }) + end + + it 'returns custom value' do + expect(template.key_text).to eq "custom text" + end + + context 'when its size is larger than the max allowed value' do + before do + allow(badge).to receive(:customization).and_return({ key_text: 't' * 65 }) + end + + it 'returns default value' do + expect(template.key_text).to eq 'coverage' + end + end + end + end + + describe '#value_text' do + context 'when coverage is known' do + it 'returns coverage percentage' do + expect(template.value_text).to eq '90.00%' + end + end + + context 'when coverage is known to many digits' do + before do + allow(badge).to receive(:status).and_return(92.349) + end + + it 'returns rounded coverage percentage' do + expect(template.value_text).to eq '92.35%' + end + end + + context 'when coverage is unknown' do + before do + allow(badge).to receive(:status).and_return(nil) + end + + it 'returns string that says coverage is unknown' do + expect(template.value_text).to eq 'unknown' + end + end + end + + describe '#key_width' do + it 'is fixed by default' do + expect(template.key_width).to eq 62 + end + + context 'when custom key_width is defined' do + before do + allow(badge).to receive(:customization).and_return({ key_width: 101 }) + end + + it 'returns custom value' do + expect(template.key_width).to eq 101 + end + + context 'when it is larger than the max allowed value' do + before do + allow(badge).to receive(:customization).and_return({ key_width: 513 }) + end + + it 'returns default value' do + expect(template.key_width).to eq 62 + end + end + end + end + + describe '#value_width' do + context 'when coverage is known' do + it 'is narrower when coverage is known' do + expect(template.value_width).to eq 54 + end + end + + context 'when coverage is unknown' do + before do + allow(badge).to receive(:status).and_return(nil) + end + + it 'is wider when coverage is unknown to fit text' do + expect(template.value_width).to eq 58 + end + end + end + + describe '#key_color' do + it 'always has the same color' do + expect(template.key_color).to eq '#555' + end + end + + describe '#value_color' do + context 'when coverage is good' do + before do + allow(badge).to receive(:status).and_return(98) + end + + it 'is green' do + expect(template.value_color).to eq '#4c1' + end + end + + context 'when coverage is acceptable' do + before do + allow(badge).to receive(:status).and_return(90) + end + + it 'is green-orange' do + expect(template.value_color).to eq '#a3c51c' + end + end + + context 'when coverage is medium' do + before do + allow(badge).to receive(:status).and_return(75) + end + + it 'is orange-yellow' do + expect(template.value_color).to eq '#dfb317' + end + end + + context 'when coverage is low' do + before do + allow(badge).to receive(:status).and_return(50) + end + + it 'is red' do + expect(template.value_color).to eq '#e05d44' + end + end + + context 'when coverage is unknown' do + before do + allow(badge).to receive(:status).and_return(nil) + end + + it 'is grey' do + expect(template.value_color).to eq '#9f9f9f' + end + end + end + + describe '#width' do + context 'when coverage is known' do + it 'returns the key width plus value width' do + expect(template.width).to eq 116 + end + end + + context 'when coverage is unknown' do + before do + allow(badge).to receive(:status).and_return(nil) + end + + it 'returns key width plus wider value width' do + expect(template.width).to eq 120 + end + end + end +end diff --git a/spec/lib/gitlab/ci/badge/pipeline/metadata_spec.rb b/spec/lib/gitlab/ci/badge/pipeline/metadata_spec.rb new file mode 100644 index 00000000000..2f677237fad --- /dev/null +++ b/spec/lib/gitlab/ci/badge/pipeline/metadata_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'lib/gitlab/ci/badge/shared/metadata' + +RSpec.describe Gitlab::Ci::Badge::Pipeline::Metadata do + let(:badge) { double(project: create(:project), ref: 'feature') } + let(:metadata) { described_class.new(badge) } + + it_behaves_like 'badge metadata' + + describe '#title' do + it 'returns build status title' do + expect(metadata.title).to eq 'pipeline status' + end + end + + describe '#image_url' do + it 'returns valid url' do + expect(metadata.image_url).to include 'badges/feature/pipeline.svg' + end + end + + describe '#link_url' do + it 'returns valid link' do + expect(metadata.link_url).to include 'commits/feature' + end + end +end diff --git a/spec/lib/gitlab/ci/badge/pipeline/status_spec.rb b/spec/lib/gitlab/ci/badge/pipeline/status_spec.rb new file mode 100644 index 00000000000..45d0d781090 --- /dev/null +++ b/spec/lib/gitlab/ci/badge/pipeline/status_spec.rb @@ -0,0 +1,127 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Badge::Pipeline::Status do + let(:project) { create(:project, :repository) } + let(:sha) { project.commit.sha } + let(:branch) { 'master' } + let(:badge) { described_class.new(project, branch) } + + describe '#entity' do + it 'always says pipeline' do + expect(badge.entity).to eq 'pipeline' + end + end + + describe '#template' do + it 'returns badge template' do + expect(badge.template.key_text).to eq 'pipeline' + end + end + + describe '#metadata' do + it 'returns badge metadata' do + expect(badge.metadata.image_url).to include 'badges/master/pipeline.svg' + end + end + + context 'pipeline exists', :sidekiq_might_not_need_inline do + let!(:pipeline) { create_pipeline(project, sha, branch) } + + context 'pipeline success' do + before do + pipeline.success! + end + + describe '#status' do + it 'is successful' do + expect(badge.status).to eq 'success' + end + end + end + + context 'pipeline failed' do + before do + pipeline.drop! + end + + describe '#status' do + it 'failed' do + expect(badge.status).to eq 'failed' + end + end + end + + context 'when outdated pipeline for given ref exists' do + before do + pipeline.success! + + old_pipeline = create_pipeline(project, '11eeffdd', branch) + old_pipeline.drop! + end + + it 'does not take outdated pipeline into account' do + expect(badge.status).to eq 'success' + end + end + + context 'when multiple pipelines exist for given sha' do + before do + pipeline.drop! + + new_pipeline = create_pipeline(project, sha, branch) + new_pipeline.success! + end + + it 'does not take outdated pipeline into account' do + expect(badge.status).to eq 'success' + end + end + + context 'when ignored_skipped is set to true' do + let(:new_badge) { described_class.new(project, branch, opts: { ignore_skipped: true }) } + + before do + pipeline.skip! + end + + describe '#status' do + it 'uses latest non-skipped status' do + expect(new_badge.status).not_to eq 'skipped' + end + end + end + + context 'when ignored_skipped is set to false' do + let(:new_badge) { described_class.new(project, branch, opts: { ignore_skipped: false }) } + + before do + pipeline.skip! + end + + describe '#status' do + it 'uses latest status' do + expect(new_badge.status).to eq 'skipped' + end + end + end + end + + context 'build does not exist' do + describe '#status' do + it 'is unknown' do + expect(badge.status).to eq 'unknown' + end + end + end + + def create_pipeline(project, sha, branch) + pipeline = create(:ci_empty_pipeline, + project: project, + sha: sha, + ref: branch) + + create(:ci_build, pipeline: pipeline, stage: 'notify') + end +end diff --git a/spec/lib/gitlab/ci/badge/pipeline/template_spec.rb b/spec/lib/gitlab/ci/badge/pipeline/template_spec.rb new file mode 100644 index 00000000000..696bb62b4d6 --- /dev/null +++ b/spec/lib/gitlab/ci/badge/pipeline/template_spec.rb @@ -0,0 +1,140 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Badge::Pipeline::Template do + let(:badge) { double(entity: 'pipeline', status: 'success', customization: {}) } + let(:template) { described_class.new(badge) } + + describe '#key_text' do + it 'says pipeline by default' do + expect(template.key_text).to eq 'pipeline' + end + + context 'when custom key_text is defined' do + before do + allow(badge).to receive(:customization).and_return({ key_text: 'custom text' }) + end + + it 'returns custom value' do + expect(template.key_text).to eq 'custom text' + end + + context 'when its size is larger than the max allowed value' do + before do + allow(badge).to receive(:customization).and_return({ key_text: 't' * 65 }) + end + + it 'returns default value' do + expect(template.key_text).to eq 'pipeline' + end + end + end + end + + describe '#value_text' do + it 'is status value' do + expect(template.value_text).to eq 'passed' + end + end + + describe '#key_width' do + it 'is fixed by default' do + expect(template.key_width).to eq 62 + end + + context 'when custom key_width is defined' do + before do + allow(badge).to receive(:customization).and_return({ key_width: 101 }) + end + + it 'returns custom value' do + expect(template.key_width).to eq 101 + end + + context 'when it is larger than the max allowed value' do + before do + allow(badge).to receive(:customization).and_return({ key_width: 513 }) + end + + it 'returns default value' do + expect(template.key_width).to eq 62 + end + end + end + end + + describe 'widths and text anchors' do + it 'has fixed width and text anchors' do + expect(template.width).to eq 116 + expect(template.key_width).to eq 62 + expect(template.value_width).to eq 54 + expect(template.key_text_anchor).to eq 31 + expect(template.value_text_anchor).to eq 89 + end + end + + describe '#key_color' do + it 'is always the same' do + expect(template.key_color).to eq '#555' + end + end + + describe '#value_color' do + context 'when status is success' do + it 'has expected color' do + expect(template.value_color).to eq '#4c1' + end + end + + context 'when status is failed' do + before do + allow(badge).to receive(:status).and_return('failed') + end + + it 'has expected color' do + expect(template.value_color).to eq '#e05d44' + end + end + + context 'when status is running' do + before do + allow(badge).to receive(:status).and_return('running') + end + + it 'has expected color' do + expect(template.value_color).to eq '#dfb317' + end + end + + context 'when status is preparing' do + before do + allow(badge).to receive(:status).and_return('preparing') + end + + it 'has expected color' do + expect(template.value_color).to eq '#a7a7a7' + end + end + + context 'when status is unknown' do + before do + allow(badge).to receive(:status).and_return('unknown') + end + + it 'has expected color' do + expect(template.value_color).to eq '#9f9f9f' + end + end + + context 'when status does not match any known statuses' do + before do + allow(badge).to receive(:status).and_return('invalid') + end + + it 'has expected color' do + expect(template.value_color).to eq '#9f9f9f' + end + end + end +end diff --git a/spec/lib/gitlab/ci/badge/shared/metadata.rb b/spec/lib/gitlab/ci/badge/shared/metadata.rb new file mode 100644 index 00000000000..c99a65bb2f4 --- /dev/null +++ b/spec/lib/gitlab/ci/badge/shared/metadata.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'badge metadata' do + describe '#to_html' do + let(:html) { Nokogiri::HTML.parse(metadata.to_html) } + let(:a_href) { html.at('a') } + + it 'points to link' do + expect(a_href[:href]).to eq metadata.link_url + end + + it 'contains clickable image' do + expect(a_href.children.first.name).to eq 'img' + end + end + + describe '#to_markdown' do + subject { metadata.to_markdown } + + it { is_expected.to include metadata.image_url } + it { is_expected.to include metadata.link_url } + end + + describe '#to_asciidoc' do + subject { metadata.to_asciidoc } + + it { is_expected.to include metadata.image_url } + it { is_expected.to include metadata.link_url } + it { is_expected.to include 'image:' } + it { is_expected.to include 'link=' } + it { is_expected.to include 'title=' } + end +end diff --git a/spec/lib/gitlab/ci/build/credentials/registry/dependency_proxy_spec.rb b/spec/lib/gitlab/ci/build/credentials/registry/dependency_proxy_spec.rb new file mode 100644 index 00000000000..f50c6e99e99 --- /dev/null +++ b/spec/lib/gitlab/ci/build/credentials/registry/dependency_proxy_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Build::Credentials::Registry::DependencyProxy do + let(:build) { create(:ci_build, name: 'spinach', stage: 'test', stage_idx: 0) } + let(:gitlab_url) { 'gitlab.example.com:443' } + + subject { described_class.new(build) } + + before do + stub_config_setting(host: 'gitlab.example.com', port: 443) + end + + it 'contains valid dependency proxy credentials' do + expect(subject).to be_kind_of(described_class) + + expect(subject.username).to eq 'gitlab-ci-token' + expect(subject.password).to eq build.token + expect(subject.url).to eq gitlab_url + expect(subject.type).to eq 'registry' + end + + describe '.valid?' do + subject { described_class.new(build).valid? } + + context 'when dependency proxy is enabled' do + before do + stub_config(dependency_proxy: { enabled: true }) + end + + it { is_expected.to be_truthy } + end + + context 'when dependency proxy is disabled' do + before do + stub_config(dependency_proxy: { enabled: false }) + end + + it { is_expected.to be_falsey } + end + end +end diff --git a/spec/lib/gitlab/ci/build/credentials/registry_spec.rb b/spec/lib/gitlab/ci/build/credentials/registry/gitlab_registry_spec.rb index c0a76973f60..43913e91085 100644 --- a/spec/lib/gitlab/ci/build/credentials/registry_spec.rb +++ b/spec/lib/gitlab/ci/build/credentials/registry/gitlab_registry_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::Ci::Build::Credentials::Registry do +RSpec.describe Gitlab::Ci::Build::Credentials::Registry::GitlabRegistry do let(:build) { create(:ci_build, name: 'spinach', stage: 'test', stage_idx: 0) } let(:registry_url) { 'registry.example.com:5005' } diff --git a/spec/lib/gitlab/ci/build/rules_spec.rb b/spec/lib/gitlab/ci/build/rules_spec.rb index a1af5b75f87..0b50def05d4 100644 --- a/spec/lib/gitlab/ci/build/rules_spec.rb +++ b/spec/lib/gitlab/ci/build/rules_spec.rb @@ -201,40 +201,13 @@ RSpec.describe Gitlab::Ci::Build::Rules do end describe '#build_attributes' do - let(:seed_attributes) { {} } - subject(:build_attributes) do - result.build_attributes(seed_attributes) + result.build_attributes end it 'compacts nil values' do is_expected.to eq(options: {}, when: 'on_success') end - - context 'when there are variables in rules' do - let(:variables) { { VAR1: 'new var 1', VAR3: 'var 3' } } - - context 'when there are seed variables' do - let(:seed_attributes) do - { yaml_variables: [{ key: 'VAR1', value: 'var 1', public: true }, - { key: 'VAR2', value: 'var 2', public: true }] } - end - - it 'returns yaml_variables with override' do - is_expected.to include( - yaml_variables: [{ key: 'VAR1', value: 'new var 1', public: true }, - { key: 'VAR2', value: 'var 2', public: true }, - { key: 'VAR3', value: 'var 3', public: true }] - ) - end - end - - context 'when there is not seed variables' do - it 'does not return yaml_variables' do - is_expected.not_to have_key(:yaml_variables) - end - end - end end describe '#pass?' do diff --git a/spec/lib/gitlab/ci/charts_spec.rb b/spec/lib/gitlab/ci/charts_spec.rb index cfc2019a89b..46d7d4a58f0 100644 --- a/spec/lib/gitlab/ci/charts_spec.rb +++ b/spec/lib/gitlab/ci/charts_spec.rb @@ -9,6 +9,10 @@ RSpec.describe Gitlab::Ci::Charts do subject { chart.to } + before do + create(:ci_empty_pipeline, project: project, duration: 120) + end + it 'goes until the end of the current month (including the whole last day of the month)' do is_expected.to eq(Date.today.end_of_month.end_of_day) end @@ -20,6 +24,10 @@ RSpec.describe Gitlab::Ci::Charts do it 'uses %B %Y as labels format' do expect(chart.labels).to include(chart.from.strftime('%B %Y')) end + + it 'returns count of pipelines run each day in the current year' do + expect(chart.total.sum).to eq(1) + end end context 'monthchart' do @@ -28,6 +36,10 @@ RSpec.describe Gitlab::Ci::Charts do subject { chart.to } + before do + create(:ci_empty_pipeline, project: project, duration: 120) + end + it 'includes the whole current day' do is_expected.to eq(Date.today.end_of_day) end @@ -39,6 +51,10 @@ RSpec.describe Gitlab::Ci::Charts do it 'uses %d %B as labels format' do expect(chart.labels).to include(chart.from.strftime('%d %B')) end + + it 'returns count of pipelines run each day in the current month' do + expect(chart.total.sum).to eq(1) + end end context 'weekchart' do @@ -47,6 +63,10 @@ RSpec.describe Gitlab::Ci::Charts do subject { chart.to } + before do + create(:ci_empty_pipeline, project: project, duration: 120) + end + it 'includes the whole current day' do is_expected.to eq(Date.today.end_of_day) end @@ -58,6 +78,68 @@ RSpec.describe Gitlab::Ci::Charts do it 'uses %d %B as labels format' do expect(chart.labels).to include(chart.from.strftime('%d %B')) end + + it 'returns count of pipelines run each day in the current week' do + expect(chart.total.sum).to eq(1) + end + end + + context 'weekchart_utc' do + today = Date.today + end_of_today = Time.use_zone(Time.find_zone('UTC')) { today.end_of_day } + + let(:project) { create(:project) } + let(:chart) do + allow(Date).to receive(:today).and_return(today) + allow(today).to receive(:end_of_day).and_return(end_of_today) + Gitlab::Ci::Charts::WeekChart.new(project) + end + + subject { chart.total } + + before do + create(:ci_empty_pipeline, project: project, duration: 120) + end + + it 'uses a utc time zone for range times' do + expect(chart.to.zone).to eq(end_of_today.zone) + expect(chart.from.zone).to eq(end_of_today.zone) + end + + it 'returns count of pipelines run each day in the current week' do + expect(chart.total.sum).to eq(1) + end + end + + context 'weekchart_non_utc' do + today = Date.today + end_of_today = Time.use_zone(Time.find_zone('Asia/Dubai')) { today.end_of_day } + + let(:project) { create(:project) } + let(:chart) do + allow(Date).to receive(:today).and_return(today) + allow(today).to receive(:end_of_day).and_return(end_of_today) + Gitlab::Ci::Charts::WeekChart.new(project) + end + + subject { chart.total } + + before do + # The DB uses UTC always, so our use of a Time Zone in the application + # can cause the creation date of the pipeline to go unmatched depending + # on the offset. We can work around this by requesting the pipeline be + # created a with the `created_at` field set to a day ago in the same week. + create(:ci_empty_pipeline, project: project, duration: 120, created_at: today - 1.day) + end + + it 'uses a non-utc time zone for range times' do + expect(chart.to.zone).to eq(end_of_today.zone) + expect(chart.from.zone).to eq(end_of_today.zone) + end + + it 'returns count of pipelines run each day in the current week' do + expect(chart.total.sum).to eq(1) + end end context 'pipeline_times' do diff --git a/spec/lib/gitlab/ci/config/entry/cache_spec.rb b/spec/lib/gitlab/ci/config/entry/cache_spec.rb index 80427eaa6ee..247f4b63910 100644 --- a/spec/lib/gitlab/ci/config/entry/cache_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/cache_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Config::Entry::Cache do + using RSpec::Parameterized::TableSyntax + subject(:entry) { described_class.new(config) } describe 'validations' do @@ -56,8 +58,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Cache do end context 'with `policy`' do - using RSpec::Parameterized::TableSyntax - where(:policy, :result) do 'pull-push' | 'pull-push' 'push' | 'push' @@ -77,8 +77,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Cache do end context 'with `when`' do - using RSpec::Parameterized::TableSyntax - where(:when_config, :result) do 'on_success' | 'on_success' 'on_failure' | 'on_failure' @@ -109,8 +107,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Cache do end context 'with `policy`' do - using RSpec::Parameterized::TableSyntax - where(:policy, :valid) do 'pull-push' | true 'push' | true @@ -126,8 +122,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Cache do end context 'with `when`' do - using RSpec::Parameterized::TableSyntax - where(:when_config, :valid) do 'on_success' | true 'on_failure' | true diff --git a/spec/lib/gitlab/ci/config/entry/commands_spec.rb b/spec/lib/gitlab/ci/config/entry/commands_spec.rb index 439799fe973..1b8dfae692a 100644 --- a/spec/lib/gitlab/ci/config/entry/commands_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/commands_spec.rb @@ -87,18 +87,20 @@ RSpec.describe Gitlab::Ci::Config::Entry::Commands do describe '#errors' do it 'saves errors' do expect(entry.errors) - .to include 'commands config should be a string or an array containing strings and arrays of strings' + .to include 'commands config should be a string or a nested array of strings up to 10 levels deep' end end end context 'when entry value is multi-level nested array' do - let(:config) { [['ls', ['echo 1']], 'pwd'] } + let(:config) do + ['ls 0', ['ls 1', ['ls 2', ['ls 3', ['ls 4', ['ls 5', ['ls 6', ['ls 7', ['ls 8', ['ls 9', ['ls 10']]]]]]]]]]] + end describe '#errors' do it 'saves errors' do expect(entry.errors) - .to include 'commands config should be a string or an array containing strings and arrays of strings' + .to include 'commands config should be a string or a nested array of strings up to 10 levels deep' end end diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb index 7834a1a94f2..a3b5f32b9f9 100644 --- a/spec/lib/gitlab/ci/config/entry/job_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb @@ -763,16 +763,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do it 'returns allow_failure_criteria' do expect(entry.value[:allow_failure_criteria]).to match(exit_codes: [42]) end - - context 'with ci_allow_failure_with_exit_codes disabled' do - before do - stub_feature_flags(ci_allow_failure_with_exit_codes: false) - end - - it 'does not return allow_failure_criteria' do - expect(entry.value.key?(:allow_failure_criteria)).to be_falsey - end - end 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 aadf94365c6..04e80450263 100644 --- a/spec/lib/gitlab/ci/config/entry/processable_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/processable_spec.rb @@ -73,6 +73,15 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable do end end + context 'when resource_group key is not a string' do + let(:config) { { resource_group: 123 } } + + it 'returns error about wrong value type' do + expect(entry).not_to be_valid + expect(entry.errors).to include "job resource group should be a string" + end + end + context 'when it uses both "when:" and "rules:"' do let(:config) do { @@ -340,6 +349,26 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable do end end + context 'with resource group' do + using RSpec::Parameterized::TableSyntax + + where(:resource_group, :result) do + 'iOS' | 'iOS' + 'review/$CI_COMMIT_REF_NAME' | 'review/$CI_COMMIT_REF_NAME' + nil | nil + end + + with_them do + let(:config) { { script: 'ls', resource_group: resource_group }.compact } + + it do + entry.compose!(deps) + + expect(entry.resource_group).to eq(result) + end + end + end + context 'with inheritance' do context 'of variables' do let(:config) do diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb index 4fdaaca8316..99f546ceb37 100644 --- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb +++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb @@ -323,20 +323,6 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do expect { subject }.to raise_error(described_class::AmbigiousSpecificationError) end end - - context 'when feature flag is turned off' do - let(:values) do - { include: full_local_file_path } - end - - before do - stub_feature_flags(variables_in_include_section_ci: false) - end - - it 'does not expand the variables' do - expect(subject[0].location).to eq('$CI_PROJECT_PATH' + local_file) - end - end end end end diff --git a/spec/lib/gitlab/ci/config/yaml/tags/reference_spec.rb b/spec/lib/gitlab/ci/config/yaml/tags/reference_spec.rb new file mode 100644 index 00000000000..c68dccd3455 --- /dev/null +++ b/spec/lib/gitlab/ci/config/yaml/tags/reference_spec.rb @@ -0,0 +1,121 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Config::Yaml::Tags::Reference do + let(:config) do + Gitlab::Ci::Config::Yaml.load!(yaml) + end + + describe '.tag' do + it 'implements the tag method' do + expect(described_class.tag).to eq('!reference') + end + end + + describe '#resolve' do + subject { Gitlab::Ci::Config::Yaml::Tags::Resolver.new(config).to_hash } + + context 'with circular references' do + let(:yaml) do + <<~YML + a: !reference [b] + b: !reference [a] + YML + end + + it 'raises CircularReferenceError' do + expect { subject }.to raise_error Gitlab::Ci::Config::Yaml::Tags::TagError, '!reference ["b"] is part of a circular chain' + end + end + + context 'with nested circular references' do + let(:yaml) do + <<~YML + a: !reference [b, c] + b: { c: !reference [d, e, f] } + d: { e: { f: !reference [a] } } + YML + end + + it 'raises CircularReferenceError' do + expect { subject }.to raise_error Gitlab::Ci::Config::Yaml::Tags::TagError, '!reference ["b", "c"] is part of a circular chain' + end + end + + context 'with missing references' do + let(:yaml) { 'a: !reference [b]' } + + it 'raises MissingReferenceError' do + expect { subject }.to raise_error Gitlab::Ci::Config::Yaml::Tags::TagError, '!reference ["b"] could not be found' + end + end + + context 'with invalid references' do + using RSpec::Parameterized::TableSyntax + + where(:yaml, :error_message) do + 'a: !reference' | '!reference [] is not valid' + 'a: !reference str' | '!reference "str" is not valid' + 'a: !reference 1' | '!reference "1" is not valid' + 'a: !reference [1]' | '!reference [1] is not valid' + 'a: !reference { b: c }' | '!reference {"b"=>"c"} is not valid' + end + + with_them do + it 'raises an error' do + expect { subject }.to raise_error Gitlab::Ci::Config::Yaml::Tags::TagError, error_message + end + end + end + + context 'with arrays' do + let(:yaml) do + <<~YML + a: { b: [1, 2] } + c: { d: { e: [3, 4] } } + f: { g: [ !reference [a, b], 5, !reference [c, d, e]] } + YML + end + + it { is_expected.to match(a_hash_including({ f: { g: [[1, 2], 5, [3, 4]] } })) } + end + + context 'with hashes' do + context 'when referencing an entire hash' do + let(:yaml) do + <<~YML + a: { b: { c: 'c', d: 'd' } } + e: { f: !reference [a, b] } + YML + end + + it { is_expected.to match(a_hash_including({ e: { f: { c: 'c', d: 'd' } } })) } + end + + context 'when referencing only a hash value' do + let(:yaml) do + <<~YML + a: { b: { c: 'c', d: 'd' } } + e: { f: { g: !reference [a, b, c], h: 'h' } } + i: !reference [e, f] + YML + end + + it { is_expected.to match(a_hash_including({ i: { g: 'c', h: 'h' } })) } + end + + context 'when referencing a value before its definition' do + let(:yaml) do + <<~YML + a: { b: !reference [c, d] } + g: { h: { i: 'i', j: 1 } } + c: { d: { e: !reference [g, h, j], f: 'f' } } + YML + end + + it { is_expected.to match(a_hash_including({ a: { b: { e: 1, f: 'f' } } })) } + end + end + end +end diff --git a/spec/lib/gitlab/ci/config/yaml/tags/resolver_spec.rb b/spec/lib/gitlab/ci/config/yaml/tags/resolver_spec.rb new file mode 100644 index 00000000000..594242c33cc --- /dev/null +++ b/spec/lib/gitlab/ci/config/yaml/tags/resolver_spec.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Config::Yaml::Tags::Resolver do + let(:config) do + Gitlab::Ci::Config::Yaml.load!(yaml) + end + + describe '#to_hash' do + subject { described_class.new(config).to_hash } + + context 'when referencing deeply nested arrays' do + let(:yaml_templates) do + <<~YML + .job-1: + script: + - echo doing step 1 of job 1 + - echo doing step 2 of job 1 + + .job-2: + script: + - echo doing step 1 of job 2 + - !reference [.job-1, script] + - echo doing step 2 of job 2 + + .job-3: + script: + - echo doing step 1 of job 3 + - !reference [.job-2, script] + - echo doing step 2 of job 3 + YML + end + + let(:job_yaml) do + <<~YML + test: + script: + - echo preparing to test + - !reference [.job-3, script] + - echo test finished + YML + end + + shared_examples 'expands references' do + it 'expands the references' do + is_expected.to match({ + '.job-1': { + script: [ + 'echo doing step 1 of job 1', + 'echo doing step 2 of job 1' + ] + }, + '.job-2': { + script: [ + 'echo doing step 1 of job 2', + [ + 'echo doing step 1 of job 1', + 'echo doing step 2 of job 1' + ], + 'echo doing step 2 of job 2' + ] + }, + '.job-3': { + script: [ + 'echo doing step 1 of job 3', + [ + 'echo doing step 1 of job 2', + [ + 'echo doing step 1 of job 1', + 'echo doing step 2 of job 1' + ], + 'echo doing step 2 of job 2' + ], + 'echo doing step 2 of job 3' + ] + }, + test: { + script: [ + 'echo preparing to test', + [ + 'echo doing step 1 of job 3', + [ + 'echo doing step 1 of job 2', + [ + 'echo doing step 1 of job 1', + 'echo doing step 2 of job 1' + ], + 'echo doing step 2 of job 2' + ], + 'echo doing step 2 of job 3' + ], + 'echo test finished' + ] + } + }) + end + end + + context 'when templates are defined before the job' do + let(:yaml) do + <<~YML + #{yaml_templates} + #{job_yaml} + YML + end + + it_behaves_like 'expands references' + end + + context 'when templates are defined after the job' do + let(:yaml) do + <<~YML + #{job_yaml} + #{yaml_templates} + YML + end + + it_behaves_like 'expands references' + end + end + end +end diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb index dc03d2f80fe..45ce4cac6c4 100644 --- a/spec/lib/gitlab/ci/config_spec.rb +++ b/spec/lib/gitlab/ci/config_spec.rb @@ -263,6 +263,26 @@ RSpec.describe Gitlab::Ci::Config do end end end + + context 'when yaml uses circular !reference' do + let(:yml) do + <<~YAML + job-1: + script: + - !reference [job-2, before_script] + + job-2: + before_script: !reference [job-1, script] + YAML + end + + it 'raises error' do + expect { config }.to raise_error( + described_class::ConfigError, + /\!reference \["job-2", "before_script"\] is part of a circular chain/ + ) + end + end end context "when using 'include' directive" do diff --git a/spec/lib/gitlab/ci/cron_parser_spec.rb b/spec/lib/gitlab/ci/cron_parser_spec.rb index dd27b4045c9..15293429354 100644 --- a/spec/lib/gitlab/ci/cron_parser_spec.rb +++ b/spec/lib/gitlab/ci/cron_parser_spec.rb @@ -63,6 +63,17 @@ RSpec.describe Gitlab::Ci::CronParser do end end + context 'when range and slash used' do + let(:cron) { '3-59/10 * * * *' } + let(:cron_timezone) { 'UTC' } + + it_behaves_like returns_time_for_epoch + + it 'returns specific time' do + expect(subject.min).to be_in([3, 13, 23, 33, 43, 53]) + end + end + context 'when cron_timezone is TZInfo format' do before do allow(Time).to receive(:zone) diff --git a/spec/lib/gitlab/ci/jwt_spec.rb b/spec/lib/gitlab/ci/jwt_spec.rb index 3130c0c0c41..342ca6b8b75 100644 --- a/spec/lib/gitlab/ci/jwt_spec.rb +++ b/spec/lib/gitlab/ci/jwt_spec.rb @@ -44,6 +44,9 @@ RSpec.describe Gitlab::Ci::Jwt do expect(payload[:pipeline_id]).to eq(pipeline.id.to_s) expect(payload[:job_id]).to eq(build.id.to_s) expect(payload[:ref]).to eq(pipeline.source_ref) + expect(payload[:ref_protected]).to eq(build.protected.to_s) + expect(payload[:environment]).to be_nil + expect(payload[:environment_protected]).to be_nil end end @@ -90,6 +93,39 @@ RSpec.describe Gitlab::Ci::Jwt do expect(payload[:ref_protected]).to eq('true') end end + + describe 'environment' do + let(:environment) { build_stubbed(:environment, project: project, name: 'production') } + let(:build) do + build_stubbed( + :ci_build, + project: project, + user: user, + pipeline: pipeline, + environment: environment.name + ) + end + + before do + allow(build).to receive(:persisted_environment).and_return(environment) + end + + it 'has correct values for environment attributes' do + expect(payload[:environment]).to eq('production') + expect(payload[:environment_protected]).to eq('false') + end + + context ':ci_jwt_include_environment feature flag is disabled' do + before do + stub_feature_flags(ci_jwt_include_environment: false) + end + + it 'does not include environment attributes' do + expect(payload).not_to have_key(:environment) + expect(payload).not_to have_key(:environment_protected) + end + end + end end describe '.for_build' do diff --git a/spec/lib/gitlab/ci/parsers/instrumentation_spec.rb b/spec/lib/gitlab/ci/parsers/instrumentation_spec.rb new file mode 100644 index 00000000000..30bcce21be2 --- /dev/null +++ b/spec/lib/gitlab/ci/parsers/instrumentation_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Parsers::Instrumentation do + describe '#parse!' do + let(:parser_class) do + Class.new do + prepend Gitlab::Ci::Parsers::Instrumentation + + def parse!(arg1, arg2) + "parse #{arg1} #{arg2}" + end + end + end + + it 'sets metrics for duration of parsing' do + result = parser_class.new.parse!('hello', 'world') + + expect(result).to eq('parse hello world') + + metrics = Gitlab::Metrics.registry.get(:ci_report_parser_duration_seconds).get({ parser: parser_class.name }) + + expect(metrics.keys).to match_array(described_class::BUCKETS) + end + end +end diff --git a/spec/lib/gitlab/ci/parsers_spec.rb b/spec/lib/gitlab/ci/parsers_spec.rb index b932cd81272..c9891c06507 100644 --- a/spec/lib/gitlab/ci/parsers_spec.rb +++ b/spec/lib/gitlab/ci/parsers_spec.rb @@ -54,4 +54,12 @@ RSpec.describe Gitlab::Ci::Parsers do end end end + + describe '.instrument!' do + it 'prepends the Instrumentation module into each parser' do + expect(described_class.parsers.values).to all( receive(:prepend).with(Gitlab::Ci::Parsers::Instrumentation) ) + + described_class.instrument! + end + end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb index 20406acb658..53dea1d0d19 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb @@ -235,7 +235,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Build do with_them do before do - project.update!(ci_keep_latest_artifact: keep_latest_artifact) + project.update!(keep_latest_artifact: keep_latest_artifact) end it 'builds a pipeline with appropriate locked value' do 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 3eaecb11ae0..1d17244e519 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 @@ -58,20 +58,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::CancelPendingPipelines do expect(build_statuses(child_pipeline)).to contain_exactly('canceled') end - - context 'when FF ci_auto_cancel_all_pipelines is disabled' do - before do - stub_feature_flags(ci_auto_cancel_all_pipelines: false) - end - - it 'does not cancel interruptible builds of child pipeline' do - expect(build_statuses(child_pipeline)).to contain_exactly('running') - - perform - - expect(build_statuses(child_pipeline)).to contain_exactly('running') - end - end end context 'when the child pipeline has not an interruptible job' do 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 3616461d94f..cd868a57bbc 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb @@ -27,7 +27,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::TemplateUsage do %w(Template-1 Template-2).each do |expected_template| expect(Gitlab::UsageDataCounters::CiTemplateUniqueCounter).to( receive(:track_unique_project_event) - .with(project_id: project.id, template: expected_template) + .with(project_id: project.id, template: expected_template, config_source: pipeline.config_source) ) end diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb index cf020fc343c..0efc7484699 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb @@ -383,14 +383,25 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do end context 'when job is a bridge' do - let(:attributes) do + let(:base_attributes) do { name: 'rspec', ref: 'master', options: { trigger: 'my/project' }, scheduling_type: :stage } end + let(:attributes) { base_attributes } + it { is_expected.to be_a(::Ci::Bridge) } it { is_expected.to be_valid } + + context 'when job belongs to a resource group' do + let(:attributes) { base_attributes.merge(resource_group_key: 'iOS') } + + it 'returns a job with resource group' do + expect(subject.resource_group).not_to be_nil + expect(subject.resource_group.key).to eq('iOS') + end + end end it 'memoizes a resource object' do diff --git a/spec/lib/gitlab/ci/pipeline/seed/build/resource_group_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/processable/resource_group_spec.rb index 8fcc242ba5f..b7260599de2 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/build/resource_group_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/processable/resource_group_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::Ci::Pipeline::Seed::Build::ResourceGroup do +RSpec.describe Gitlab::Ci::Pipeline::Seed::Processable::ResourceGroup do let_it_be(:project) { create(:project) } let(:job) { build(:ci_build, project: project) } let(:seed) { described_class.new(job, resource_group_key) } diff --git a/spec/lib/gitlab/ci/reports/codequality_mr_diff_spec.rb b/spec/lib/gitlab/ci/reports/codequality_mr_diff_spec.rb new file mode 100644 index 00000000000..8b177fa7fc1 --- /dev/null +++ b/spec/lib/gitlab/ci/reports/codequality_mr_diff_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Reports::CodequalityMrDiff do + let(:codequality_report) { Gitlab::Ci::Reports::CodequalityReports.new } + let(:degradation_1) { build(:codequality_degradation_1) } + let(:degradation_2) { build(:codequality_degradation_2) } + let(:degradation_3) { build(:codequality_degradation_3) } + + describe '#initialize!' do + subject(:report) { described_class.new(codequality_report) } + + context 'when quality has degradations' do + context 'with several degradations on the same line' do + before do + codequality_report.add_degradation(degradation_1) + codequality_report.add_degradation(degradation_2) + end + + it 'generates quality report for mr diff' do + expect(report.files).to match( + "file_a.rb" => [ + { line: 10, description: "Avoid parameter lists longer than 5 parameters. [12/5]", severity: "major" }, + { line: 10, description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.", severity: "major" } + ] + ) + end + end + + context 'with several degradations on several files' do + before do + codequality_report.add_degradation(degradation_1) + codequality_report.add_degradation(degradation_2) + codequality_report.add_degradation(degradation_3) + end + + it 'returns quality report for mr diff' do + expect(report.files).to match( + "file_a.rb" => [ + { line: 10, description: "Avoid parameter lists longer than 5 parameters. [12/5]", severity: "major" }, + { line: 10, description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.", severity: "major" } + ], + "file_b.rb" => [ + { line: 10, description: "Avoid parameter lists longer than 5 parameters. [12/5]", severity: "minor" } + ] + ) + end + end + end + + context 'when quality has no degradation' do + it 'returns an empty hash' do + expect(report.files).to match({}) + end + end + end +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 7053d54381b..90188b56f5a 100644 --- a/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb +++ b/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb @@ -6,62 +6,8 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do let(:comparer) { described_class.new(base_report, head_report) } let(:base_report) { Gitlab::Ci::Reports::CodequalityReports.new } let(:head_report) { Gitlab::Ci::Reports::CodequalityReports.new } - let(:degradation_1) do - { - "categories": [ - "Complexity" - ], - "check_name": "argument_count", - "content": { - "body": "" - }, - "description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.", - "fingerprint": "15cdb5c53afd42bc22f8ca366a08d547", - "location": { - "path": "foo.rb", - "lines": { - "begin": 10, - "end": 10 - } - }, - "other_locations": [], - "remediation_points": 900000, - "severity": "major", - "type": "issue", - "engine_name": "structure" - }.with_indifferent_access - end - - let(:degradation_2) do - { - "type": "Issue", - "check_name": "Rubocop/Metrics/ParameterLists", - "description": "Avoid parameter lists longer than 5 parameters. [12/5]", - "categories": [ - "Complexity" - ], - "remediation_points": 550000, - "location": { - "path": "foo.rb", - "positions": { - "begin": { - "column": 14, - "line": 10 - }, - "end": { - "column": 39, - "line": 10 - } - } - }, - "content": { - "body": "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count." - }, - "engine_name": "rubocop", - "fingerprint": "ab5f8b935886b942d621399f5a2ca16e", - "severity": "minor" - }.with_indifferent_access - end + let(:degradation_1) { build(:codequality_degradation_1) } + let(:degradation_2) { build(:codequality_degradation_2) } describe '#status' do subject(:report_status) { comparer.status } diff --git a/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb b/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb index 44e67259369..ae9b2f2c62b 100644 --- a/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb +++ b/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb @@ -4,62 +4,8 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Reports::CodequalityReports do let(:codequality_report) { described_class.new } - let(:degradation_1) do - { - "categories": [ - "Complexity" - ], - "check_name": "argument_count", - "content": { - "body": "" - }, - "description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.", - "fingerprint": "15cdb5c53afd42bc22f8ca366a08d547", - "location": { - "path": "foo.rb", - "lines": { - "begin": 10, - "end": 10 - } - }, - "other_locations": [], - "remediation_points": 900000, - "severity": "major", - "type": "issue", - "engine_name": "structure" - }.with_indifferent_access - end - - let(:degradation_2) do - { - "type": "Issue", - "check_name": "Rubocop/Metrics/ParameterLists", - "description": "Avoid parameter lists longer than 5 parameters. [12/5]", - "categories": [ - "Complexity" - ], - "remediation_points": 550000, - "location": { - "path": "foo.rb", - "positions": { - "begin": { - "column": 14, - "line": 10 - }, - "end": { - "column": 39, - "line": 10 - } - } - }, - "content": { - "body": "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count." - }, - "engine_name": "rubocop", - "fingerprint": "ab5f8b935886b942d621399f5a2ca16e", - "severity": "minor" - }.with_indifferent_access - end + let(:degradation_1) { build(:codequality_degradation_1) } + let(:degradation_2) { build(:codequality_degradation_2) } it { expect(codequality_report.degradations).to eq({}) } diff --git a/spec/lib/gitlab/ci/status/bridge/factory_spec.rb b/spec/lib/gitlab/ci/status/bridge/factory_spec.rb index d27bb98ba9a..6081f104e42 100644 --- a/spec/lib/gitlab/ci/status/bridge/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/bridge/factory_spec.rb @@ -117,14 +117,31 @@ RSpec.describe Gitlab::Ci::Status::Bridge::Factory do end end + context 'when bridge is waiting for resource' do + let(:bridge) { create_bridge(:waiting_for_resource, :resource_group) } + + it 'matches correct core status' do + expect(factory.core_status).to be_a Gitlab::Ci::Status::WaitingForResource + end + + it 'fabricates status with correct details' do + expect(status.text).to eq 'waiting' + expect(status.group).to eq 'waiting-for-resource' + expect(status.icon).to eq 'status_pending' + expect(status.favicon).to eq 'favicon_pending' + expect(status.illustration).to include(:image, :size, :title) + expect(status).not_to have_details + end + end + private - def create_bridge(trait) + def create_bridge(*traits) upstream_project = create(:project, :repository) downstream_project = create(:project, :repository) upstream_pipeline = create(:ci_pipeline, :running, project: upstream_project) trigger = { trigger: { project: downstream_project.full_path, branch: 'feature' } } - create(:ci_bridge, trait, options: trigger, pipeline: upstream_pipeline) + create(:ci_bridge, *traits, options: trigger, pipeline: upstream_pipeline) end end diff --git a/spec/lib/gitlab/ci/status/bridge/waiting_for_resource_spec.rb b/spec/lib/gitlab/ci/status/bridge/waiting_for_resource_spec.rb new file mode 100644 index 00000000000..3e19df28d83 --- /dev/null +++ b/spec/lib/gitlab/ci/status/bridge/waiting_for_resource_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' + +RSpec.describe Gitlab::Ci::Status::Bridge::WaitingForResource do + it { expect(described_class).to be < Gitlab::Ci::Status::Processable::WaitingForResource } +end diff --git a/spec/lib/gitlab/ci/status/build/waiting_for_resource_spec.rb b/spec/lib/gitlab/ci/status/build/waiting_for_resource_spec.rb new file mode 100644 index 00000000000..44bd5a8611a --- /dev/null +++ b/spec/lib/gitlab/ci/status/build/waiting_for_resource_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' + +RSpec.describe Gitlab::Ci::Status::Build::WaitingForResource do + it { expect(described_class).to be < Gitlab::Ci::Status::Processable::WaitingForResource } +end diff --git a/spec/lib/gitlab/ci/status/processable/waiting_for_resource_spec.rb b/spec/lib/gitlab/ci/status/processable/waiting_for_resource_spec.rb new file mode 100644 index 00000000000..91a9724d043 --- /dev/null +++ b/spec/lib/gitlab/ci/status/processable/waiting_for_resource_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Status::Processable::WaitingForResource do + let(:user) { create(:user) } + + subject do + processable = create(:ci_build, :waiting_for_resource, :resource_group) + described_class.new(Gitlab::Ci::Status::Core.new(processable, user)) + end + + describe '#illustration' do + it { expect(subject.illustration).to include(:image, :size, :title) } + end + + describe '.matches?' do + subject {described_class.matches?(processable, user) } + + context 'when processable is waiting for resource' do + let(:processable) { create(:ci_build, :waiting_for_resource) } + + it 'is a correct match' do + expect(subject).to be true + end + end + + context 'when processable is not waiting for resource' do + let(:processable) { create(:ci_build) } + + it 'does not match' do + expect(subject).to be false + end + end + end +end diff --git a/spec/lib/gitlab/ci/trace/chunked_io_spec.rb b/spec/lib/gitlab/ci/trace/chunked_io_spec.rb index a2903391c6f..f09e03b4d55 100644 --- a/spec/lib/gitlab/ci/trace/chunked_io_spec.rb +++ b/spec/lib/gitlab/ci/trace/chunked_io_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do let(:chunked_io) { described_class.new(build) } before do - stub_feature_flags(ci_enable_live_trace: true) + stub_feature_flags(ci_enable_live_trace: true, gitlab_ci_trace_read_consistency: true) end describe "#initialize" do diff --git a/spec/lib/gitlab/ci/variables/collection/sorted_spec.rb b/spec/lib/gitlab/ci/variables/collection/sorted_spec.rb index d85bf29f77f..954273fd41e 100644 --- a/spec/lib/gitlab/ci/variables/collection/sorted_spec.rb +++ b/spec/lib/gitlab/ci/variables/collection/sorted_spec.rb @@ -5,8 +5,11 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Variables::Collection::Sorted do describe '#errors' do context 'when FF :variable_inside_variable is disabled' do + let_it_be(:project_with_flag_disabled) { create(:project) } + let_it_be(:project_with_flag_enabled) { create(:project) } + before do - stub_feature_flags(variable_inside_variable: false) + stub_feature_flags(variable_inside_variable: [project_with_flag_enabled]) end context 'table tests' do @@ -53,7 +56,7 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sorted do end with_them do - subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables) } + subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables, project_with_flag_disabled) } it 'does not report error' do expect(subject.errors).to eq(nil) @@ -67,8 +70,11 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sorted do end context 'when FF :variable_inside_variable is enabled' do + let_it_be(:project_with_flag_disabled) { create(:project) } + let_it_be(:project_with_flag_enabled) { create(:project) } + before do - stub_feature_flags(variable_inside_variable: true) + stub_feature_flags(variable_inside_variable: [project_with_flag_enabled]) end context 'table tests' do @@ -100,7 +106,7 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sorted do end with_them do - subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables) } + subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables, project_with_flag_enabled) } it 'errors matches expected validation result' do expect(subject.errors).to eq(validation_result) @@ -164,7 +170,8 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sorted do end with_them do - subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables) } + let_it_be(:project) { create(:project) } + subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables, project) } it 'does not expand variables' do expect(subject.sort).to eq(variables) @@ -239,7 +246,8 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sorted do end with_them do - subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables) } + let_it_be(:project) { create(:project) } + subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables, project) } it 'sort returns correctly sorted variables' do expect(subject.sort.map { |var| var[:key] }).to eq(result) diff --git a/spec/lib/gitlab/ci/variables/helpers_spec.rb b/spec/lib/gitlab/ci/variables/helpers_spec.rb new file mode 100644 index 00000000000..b45abf8c0e1 --- /dev/null +++ b/spec/lib/gitlab/ci/variables/helpers_spec.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' + +RSpec.describe Gitlab::Ci::Variables::Helpers do + describe '.merge_variables' do + let(:current_variables) do + [{ key: 'key1', value: 'value1' }, + { key: 'key2', value: 'value2' }] + end + + let(:new_variables) do + [{ key: 'key2', value: 'value22' }, + { key: 'key3', value: 'value3' }] + end + + let(:result) do + [{ key: 'key1', value: 'value1', public: true }, + { key: 'key2', value: 'value22', public: true }, + { key: 'key3', value: 'value3', public: true }] + end + + subject { described_class.merge_variables(current_variables, new_variables) } + + it { is_expected.to eq(result) } + + context 'when new variables is a hash' do + let(:new_variables) do + { 'key2' => 'value22', 'key3' => 'value3' } + end + + it { is_expected.to eq(result) } + end + + context 'when new variables is a hash with symbol keys' do + let(:new_variables) do + { key2: 'value22', key3: 'value3' } + end + + it { is_expected.to eq(result) } + end + + context 'when new variables is nil' do + let(:new_variables) {} + let(:result) do + [{ key: 'key1', value: 'value1', public: true }, + { key: 'key2', value: 'value2', public: true }] + end + + it { is_expected.to eq(result) } + end + end + + describe '.transform_to_yaml_variables' do + let(:variables) do + { 'key1' => 'value1', 'key2' => 'value2' } + end + + let(:result) do + [{ key: 'key1', value: 'value1', public: true }, + { key: 'key2', value: 'value2', public: true }] + end + + subject { described_class.transform_to_yaml_variables(variables) } + + it { is_expected.to eq(result) } + + context 'when variables is nil' do + let(:variables) {} + + it { is_expected.to eq([]) } + end + end + + describe '.transform_from_yaml_variables' do + let(:variables) do + [{ key: 'key1', value: 'value1', public: true }, + { key: 'key2', value: 'value2', public: true }] + end + + let(:result) do + { 'key1' => 'value1', 'key2' => 'value2' } + end + + subject { described_class.transform_from_yaml_variables(variables) } + + it { is_expected.to eq(result) } + + context 'when variables is nil' do + let(:variables) {} + + it { is_expected.to eq({}) } + end + + context 'when variables is a hash' do + let(:variables) do + { key1: 'value1', 'key2' => 'value2' } + end + + it { is_expected.to eq(result) } + end + end +end |