diff options
Diffstat (limited to 'spec/lib/gitlab/ci')
129 files changed, 2094 insertions, 279 deletions
diff --git a/spec/lib/gitlab/ci/ansi2html_spec.rb b/spec/lib/gitlab/ci/ansi2html_spec.rb index 3d57ce431ab..c8afcbd053d 100644 --- a/spec/lib/gitlab/ci/ansi2html_spec.rb +++ b/spec/lib/gitlab/ci/ansi2html_spec.rb @@ -1,14 +1,16 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Ansi2html do subject { described_class } it "prints non-ansi as-is" do - expect(convert_html("Hello")).to eq('<span class="">Hello</span>') + expect(convert_html("Hello")).to eq('<span>Hello</span>') end it "strips non-color-changing control sequences" do - expect(convert_html("Hello \e[2Kworld")).to eq('<span class="">Hello world</span>') + expect(convert_html("Hello \e[2Kworld")).to eq('<span>Hello world</span>') end it "prints simply red" do @@ -32,7 +34,7 @@ describe Gitlab::Ci::Ansi2html do end it "resets colors after red on blue" do - expect(convert_html("\e[31;44mHello\e[0m world")).to eq('<span class="term-fg-red term-bg-blue">Hello</span><span class=""> world</span>') + expect(convert_html("\e[31;44mHello\e[0m world")).to eq('<span class="term-fg-red term-bg-blue">Hello</span><span> world</span>') end it "performs color change from red/blue to yellow/blue" do @@ -44,11 +46,11 @@ describe Gitlab::Ci::Ansi2html do end it "performs color change from red/blue to reset to yellow/green" do - expect(convert_html("\e[31;44mHello\e[0m \e[33;42mworld")).to eq('<span class="term-fg-red term-bg-blue">Hello</span><span class=""> </span><span class="term-fg-yellow term-bg-green">world</span>') + expect(convert_html("\e[31;44mHello\e[0m \e[33;42mworld")).to eq('<span class="term-fg-red term-bg-blue">Hello</span><span> </span><span class="term-fg-yellow term-bg-green">world</span>') end it "ignores unsupported codes" do - expect(convert_html("\e[51mHello\e[0m")).to eq('<span class="">Hello</span>') + expect(convert_html("\e[51mHello\e[0m")).to eq('<span>Hello</span>') end it "prints light red" do @@ -72,8 +74,8 @@ describe Gitlab::Ci::Ansi2html do end it "resets bold text" do - expect(convert_html("\e[1mHello\e[21m world")).to eq('<span class="term-bold">Hello</span><span class=""> world</span>') - expect(convert_html("\e[1mHello\e[22m world")).to eq('<span class="term-bold">Hello</span><span class=""> world</span>') + expect(convert_html("\e[1mHello\e[21m world")).to eq('<span class="term-bold">Hello</span><span> world</span>') + expect(convert_html("\e[1mHello\e[22m world")).to eq('<span class="term-bold">Hello</span><span> world</span>') end it "prints italic text" do @@ -81,7 +83,7 @@ describe Gitlab::Ci::Ansi2html do end it "resets italic text" do - expect(convert_html("\e[3mHello\e[23m world")).to eq('<span class="term-italic">Hello</span><span class=""> world</span>') + expect(convert_html("\e[3mHello\e[23m world")).to eq('<span class="term-italic">Hello</span><span> world</span>') end it "prints underlined text" do @@ -89,7 +91,7 @@ describe Gitlab::Ci::Ansi2html do end it "resets underlined text" do - expect(convert_html("\e[4mHello\e[24m world")).to eq('<span class="term-underline">Hello</span><span class=""> world</span>') + expect(convert_html("\e[4mHello\e[24m world")).to eq('<span class="term-underline">Hello</span><span> world</span>') end it "prints concealed text" do @@ -97,7 +99,7 @@ describe Gitlab::Ci::Ansi2html do end it "resets concealed text" do - expect(convert_html("\e[8mHello\e[28m world")).to eq('<span class="term-conceal">Hello</span><span class=""> world</span>') + expect(convert_html("\e[8mHello\e[28m world")).to eq('<span class="term-conceal">Hello</span><span> world</span>') end it "prints crossed-out text" do @@ -105,7 +107,7 @@ describe Gitlab::Ci::Ansi2html do end it "resets crossed-out text" do - expect(convert_html("\e[9mHello\e[29m world")).to eq('<span class="term-cross">Hello</span><span class=""> world</span>') + expect(convert_html("\e[9mHello\e[29m world")).to eq('<span class="term-cross">Hello</span><span> world</span>') end it "can print 256 xterm fg colors" do @@ -137,15 +139,15 @@ describe Gitlab::Ci::Ansi2html do end it "prints <" do - expect(convert_html("<")).to eq('<span class=""><</span>') + expect(convert_html("<")).to eq('<span><</span>') end it "replaces newlines with line break tags" do - expect(convert_html("\n")).to eq('<span class=""><br/><span class=""></span></span>') + expect(convert_html("\n")).to eq('<span><br/></span>') end it "groups carriage returns with newlines" do - expect(convert_html("\r\n")).to eq('<span class=""><br/><span class=""></span></span>') + expect(convert_html("\r\n")).to eq('<span><br/></span>') end describe "incremental update" do @@ -182,7 +184,7 @@ describe Gitlab::Ci::Ansi2html do context "with partial sequence" do let(:pre_text) { "Hello\e" } - let(:pre_html) { "<span class=\"\">Hello</span>" } + let(:pre_html) { "<span>Hello</span>" } let(:text) { "[1m World" } let(:html) { "<span class=\"term-bold\"> World</span>" } @@ -191,9 +193,9 @@ describe Gitlab::Ci::Ansi2html do context 'with new line' do let(:pre_text) { "Hello\r" } - let(:pre_html) { "<span class=\"\">Hello\r</span>" } + let(:pre_html) { "<span>Hello\r</span>" } let(:text) { "\nWorld" } - let(:html) { "<span class=\"\"><br/><span class=\"\">World</span></span>" } + let(:html) { "<span><br/>World</span>" } it_behaves_like 'stateable converter' end @@ -207,7 +209,7 @@ describe Gitlab::Ci::Ansi2html do let(:section_start) { "section_start:#{section_start_time.to_i}:#{section_name}\r\033[0K"} let(:section_end) { "section_end:#{section_end_time.to_i}:#{section_name}\r\033[0K"} let(:section_start_html) do - '<div class="js-section-start fa fa-caret-down append-right-8 cursor-pointer"' \ + '<div class="js-section-start section-start fa fa-caret-down pr-2 cursor-pointer"' \ " data-timestamp=\"#{section_start_time.to_i}\" data-section=\"#{class_name(section_name)}\"" \ ' role="button"></div>' end @@ -220,7 +222,7 @@ 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="">#{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 @@ -230,12 +232,11 @@ describe Gitlab::Ci::Ansi2html do let(:text) { "#{section_start}Some text#{section_end}" } it 'prints light red' do - text = "#{section_start}\e[91mHello\e[0m\n#{section_end}" - header = %{<span class="term-fg-l-red section js-section-header section-header js-s-#{class_name(section_name)}">Hello</span>} - line_break = %{<span class="section js-section-header section-header js-s-#{class_name(section_name)}"><br/></span>} - line = %{<span class="section line s_#{class_name(section_name)}"></span>} - empty_line = %{<span class="section js-s-#{class_name(section_name)}"></span>} - html = "#{section_start_html}#{header}#{line_break}#{line}#{empty_line}#{section_end_html}" + text = "#{section_start}\e[91mHello\e[0m\nLine 1\nLine 2\nLine 3\n#{section_end}" + header = %{<span class="term-fg-l-red section js-section-header section-header cursor-pointer js-s-#{class_name(section_name)}">Hello</span>} + line_break = %{<span class="section js-section-header section-header cursor-pointer 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) end diff --git a/spec/lib/gitlab/ci/build/artifacts/adapters/gzip_stream_spec.rb b/spec/lib/gitlab/ci/build/artifacts/adapters/gzip_stream_spec.rb index 987c6b37aaa..cec3e70bb9f 100644 --- a/spec/lib/gitlab/ci/build/artifacts/adapters/gzip_stream_spec.rb +++ b/spec/lib/gitlab/ci/build/artifacts/adapters/gzip_stream_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Build::Artifacts::Adapters::GzipStream do diff --git a/spec/lib/gitlab/ci/build/artifacts/adapters/raw_stream_spec.rb b/spec/lib/gitlab/ci/build/artifacts/adapters/raw_stream_spec.rb index ec2dd724b45..66a234232e1 100644 --- a/spec/lib/gitlab/ci/build/artifacts/adapters/raw_stream_spec.rb +++ b/spec/lib/gitlab/ci/build/artifacts/adapters/raw_stream_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Build::Artifacts::Adapters::RawStream do 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 3b905611467..24d17eb0fb3 100644 --- a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb +++ b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb index a9a4af1f455..ff189c4701e 100644 --- a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb +++ b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Build::Artifacts::Metadata do diff --git a/spec/lib/gitlab/ci/build/artifacts/path_spec.rb b/spec/lib/gitlab/ci/build/artifacts/path_spec.rb index 7bd6a2ead25..7bbef0f5197 100644 --- a/spec/lib/gitlab/ci/build/artifacts/path_spec.rb +++ b/spec/lib/gitlab/ci/build/artifacts/path_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Build::Artifacts::Path do diff --git a/spec/lib/gitlab/ci/build/credentials/factory_spec.rb b/spec/lib/gitlab/ci/build/credentials/factory_spec.rb index d53db05e5e6..9148c0d579e 100644 --- a/spec/lib/gitlab/ci/build/credentials/factory_spec.rb +++ b/spec/lib/gitlab/ci/build/credentials/factory_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Build::Credentials::Factory do diff --git a/spec/lib/gitlab/ci/build/credentials/registry_spec.rb b/spec/lib/gitlab/ci/build/credentials/registry_spec.rb index c6054138cde..552580dcbbe 100644 --- a/spec/lib/gitlab/ci/build/credentials/registry_spec.rb +++ b/spec/lib/gitlab/ci/build/credentials/registry_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Build::Credentials::Registry do diff --git a/spec/lib/gitlab/ci/build/image_spec.rb b/spec/lib/gitlab/ci/build/image_spec.rb index 6e20e0ef5c3..04bab9c58b8 100644 --- a/spec/lib/gitlab/ci/build/image_spec.rb +++ b/spec/lib/gitlab/ci/build/image_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Build::Image do diff --git a/spec/lib/gitlab/ci/build/policy/changes_spec.rb b/spec/lib/gitlab/ci/build/policy/changes_spec.rb index 92cf0376c02..48ac2e4e657 100644 --- a/spec/lib/gitlab/ci/build/policy/changes_spec.rb +++ b/spec/lib/gitlab/ci/build/policy/changes_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Build::Policy::Changes do diff --git a/spec/lib/gitlab/ci/build/policy/kubernetes_spec.rb b/spec/lib/gitlab/ci/build/policy/kubernetes_spec.rb index 4510b82ca9d..bc2e6fe6b8d 100644 --- a/spec/lib/gitlab/ci/build/policy/kubernetes_spec.rb +++ b/spec/lib/gitlab/ci/build/policy/kubernetes_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Build::Policy::Kubernetes do diff --git a/spec/lib/gitlab/ci/build/policy/refs_spec.rb b/spec/lib/gitlab/ci/build/policy/refs_spec.rb index 22ca681cfd3..43c5d3ec980 100644 --- a/spec/lib/gitlab/ci/build/policy/refs_spec.rb +++ b/spec/lib/gitlab/ci/build/policy/refs_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Build::Policy::Refs do diff --git a/spec/lib/gitlab/ci/build/policy/variables_spec.rb b/spec/lib/gitlab/ci/build/policy/variables_spec.rb index 9b016901a20..7140c14facb 100644 --- a/spec/lib/gitlab/ci/build/policy/variables_spec.rb +++ b/spec/lib/gitlab/ci/build/policy/variables_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Build::Policy::Variables do @@ -11,7 +13,12 @@ describe Gitlab::Ci::Build::Policy::Variables do build(:ci_build, pipeline: pipeline, project: project, ref: 'master') end - let(:seed) { double('build seed', to_resource: ci_build) } + let(:seed) do + double('build seed', + to_resource: ci_build, + scoped_variables_hash: ci_build.scoped_variables_hash + ) + end before do pipeline.variables.build(key: 'CI_PROJECT_NAME', value: '') @@ -81,7 +88,12 @@ describe Gitlab::Ci::Build::Policy::Variables do build(:ci_bridge, pipeline: pipeline, project: project, ref: 'master') end - let(:seed) { double('bridge seed', to_resource: bridge) } + let(:seed) do + double('bridge seed', + to_resource: bridge, + scoped_variables_hash: ci_build.scoped_variables_hash + ) + end it 'is satisfied by a matching expression for a bridge job' do policy = described_class.new(['$MY_VARIABLE']) @@ -89,5 +101,38 @@ describe Gitlab::Ci::Build::Policy::Variables do expect(policy).to be_satisfied_by(pipeline, seed) end end + + 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') + 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/*') + end + + it 'is satisfied by scoped variable match' do + policy = described_class.new(['$SCOPED_VARIABLE == "my-value-2"']) + + expect(policy).to be_satisfied_by(pipeline, seed) + end + + it 'is not satisfied when matching against overridden variable' do + policy = described_class.new(['$SCOPED_VARIABLE == "my-value-1"']) + + expect(policy).not_to be_satisfied_by(pipeline, seed) + end + end end end diff --git a/spec/lib/gitlab/ci/build/policy_spec.rb b/spec/lib/gitlab/ci/build/policy_spec.rb index 20ee3dd3e89..80d7b2e9dc8 100644 --- a/spec/lib/gitlab/ci/build/policy_spec.rb +++ b/spec/lib/gitlab/ci/build/policy_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Build::Policy do diff --git a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb index d88a2097ba2..775550f2acc 100644 --- a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb +++ b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb @@ -3,9 +3,9 @@ require 'spec_helper' describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do - let(:build) { create(:ci_build) } - describe '#unmet?' do + let(:build) { create(:ci_build) } + subject { described_class.new(build).unmet? } context 'build has no deployment' do @@ -18,7 +18,6 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do context 'build has a deployment' do let!(:deployment) { create(:deployment, deployable: build, cluster: cluster) } - let(:cluster) { nil } context 'and a cluster to deploy to' do let(:cluster) { create(:cluster, :group) } @@ -32,12 +31,17 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do end context 'and a namespace is already created for this project' do - let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, cluster: cluster, project: build.project) } + let(:kubernetes_namespace) { instance_double(Clusters::KubernetesNamespace, service_account_token: 'token') } + + before do + allow(Clusters::KubernetesNamespaceFinder).to receive(:new) + .and_return(double(execute: kubernetes_namespace)) + end it { is_expected.to be_falsey } context 'and the service_account_token is blank' do - let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :without_token, cluster: cluster, project: build.project) } + let(:kubernetes_namespace) { instance_double(Clusters::KubernetesNamespace, service_account_token: nil) } it { is_expected.to be_truthy } end @@ -45,34 +49,79 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do end context 'and no cluster to deploy to' do + let(:cluster) { nil } + it { is_expected.to be_falsey } end end end describe '#complete!' do - let!(:deployment) { create(:deployment, deployable: build, cluster: cluster) } - let(:service) { double(execute: true) } - let(:cluster) { nil } + let(:build) { create(:ci_build) } + let(:prerequisite) { described_class.new(build) } - subject { described_class.new(build).complete! } + subject { prerequisite.complete! } context 'completion is required' do let(:cluster) { create(:cluster, :group) } + let(:deployment) { create(:deployment, cluster: cluster) } + let(:service) { double(execute: true) } + let(:kubernetes_namespace) { double } + + before do + allow(prerequisite).to receive(:unmet?).and_return(true) + allow(build).to receive(:deployment).and_return(deployment) + end + + context 'kubernetes namespace does not exist' do + let(:namespace_builder) { double(execute: kubernetes_namespace)} - it 'creates a kubernetes namespace' do - expect(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService) - .to receive(:new) - .with(cluster: cluster, kubernetes_namespace: instance_of(Clusters::KubernetesNamespace)) - .and_return(service) + before do + allow(Clusters::KubernetesNamespaceFinder).to receive(:new) + .and_return(double(execute: nil)) + end - expect(service).to receive(:execute).once + it 'creates a namespace using a new record' do + expect(Clusters::BuildKubernetesNamespaceService) + .to receive(:new) + .with(cluster, environment: deployment.environment) + .and_return(namespace_builder) - subject + expect(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService) + .to receive(:new) + .with(cluster: cluster, kubernetes_namespace: kubernetes_namespace) + .and_return(service) + + expect(service).to receive(:execute).once + + subject + end + end + + context 'kubernetes namespace exists (but has no service_account_token)' do + before do + allow(Clusters::KubernetesNamespaceFinder).to receive(:new) + .and_return(double(execute: kubernetes_namespace)) + end + + it 'creates a namespace using the tokenless record' do + expect(Clusters::BuildKubernetesNamespaceService).not_to receive(:new) + + expect(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService) + .to receive(:new) + .with(cluster: cluster, kubernetes_namespace: kubernetes_namespace) + .and_return(service) + + subject + end end end context 'completion is not required' do + before do + allow(prerequisite).to receive(:unmet?).and_return(false) + end + it 'does not create a namespace' do expect(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).not_to receive(:new) diff --git a/spec/lib/gitlab/ci/build/rules/rule_spec.rb b/spec/lib/gitlab/ci/build/rules/rule_spec.rb new file mode 100644 index 00000000000..99852bd4228 --- /dev/null +++ b/spec/lib/gitlab/ci/build/rules/rule_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe Gitlab::Ci::Build::Rules::Rule do + let(:seed) do + double('build seed', + to_resource: ci_build, + scoped_variables_hash: ci_build.scoped_variables_hash + ) + end + + let(:pipeline) { create(:ci_pipeline) } + let(:ci_build) { build(:ci_build, pipeline: pipeline) } + let(:rule) { described_class.new(rule_hash) } + + describe '#matches?' do + subject { rule.matches?(pipeline, seed) } + + context 'with one matching clause' do + let(:rule_hash) do + { if: '$VAR == null', when: 'always' } + end + + it { is_expected.to eq(true) } + end + + context 'with two matching clauses' do + let(:rule_hash) do + { if: '$VAR == null', changes: '**/*', when: 'always' } + end + + it { is_expected.to eq(true) } + end + + context 'with a matching and non-matching clause' do + let(:rule_hash) do + { if: '$VAR != null', changes: '$VAR == null', when: 'always' } + end + + it { is_expected.to eq(false) } + end + + context 'with two non-matching clauses' do + let(:rule_hash) do + { if: '$VAR != null', changes: 'README', when: 'always' } + end + + it { is_expected.to eq(false) } + end + end +end diff --git a/spec/lib/gitlab/ci/build/rules_spec.rb b/spec/lib/gitlab/ci/build/rules_spec.rb new file mode 100644 index 00000000000..d7793ebc806 --- /dev/null +++ b/spec/lib/gitlab/ci/build/rules_spec.rb @@ -0,0 +1,168 @@ +require 'spec_helper' + +describe Gitlab::Ci::Build::Rules do + let(:pipeline) { create(:ci_pipeline) } + let(:ci_build) { build(:ci_build, pipeline: pipeline) } + + let(:seed) do + double('build seed', + to_resource: ci_build, + scoped_variables_hash: ci_build.scoped_variables_hash + ) + end + + let(:rules) { described_class.new(rule_list) } + + describe '.new' do + let(:rules_ivar) { rules.instance_variable_get :@rule_list } + let(:default_when) { rules.instance_variable_get :@default_when } + + context 'with no rules' do + let(:rule_list) { [] } + + it 'sets @rule_list to an empty array' do + expect(rules_ivar).to eq([]) + end + + it 'sets @default_when to "on_success"' do + expect(default_when).to eq('on_success') + end + end + + context 'with one rule' do + let(:rule_list) { [{ if: '$VAR == null', when: 'always' }] } + + it 'sets @rule_list to an array of a single rule' do + expect(rules_ivar).to be_an(Array) + end + + it 'sets @default_when to "on_success"' do + expect(default_when).to eq('on_success') + end + end + + context 'with multiple rules' do + let(:rule_list) do + [ + { if: '$VAR == null', when: 'always' }, + { if: '$VAR == null', when: 'always' } + ] + end + + it 'sets @rule_list to an array of a single rule' do + expect(rules_ivar).to be_an(Array) + end + + it 'sets @default_when to "on_success"' do + expect(default_when).to eq('on_success') + end + end + + context 'with a specified default when:' do + let(:rule_list) { [{ if: '$VAR == null', when: 'always' }] } + let(:rules) { described_class.new(rule_list, 'manual') } + + it 'sets @rule_list to an array of a single rule' do + expect(rules_ivar).to be_an(Array) + end + + it 'sets @default_when to "manual"' do + expect(default_when).to eq('manual') + end + end + end + + describe '#evaluate' do + subject { rules.evaluate(pipeline, seed) } + + context 'with nil rules' do + let(:rule_list) { nil } + + it { is_expected.to eq(described_class::Result.new('on_success')) } + + context 'and when:manual set as the default' do + let(:rules) { described_class.new(rule_list, 'manual') } + + it { is_expected.to eq(described_class::Result.new('manual')) } + end + end + + context 'with no rules' do + let(:rule_list) { [] } + + it { is_expected.to eq(described_class::Result.new('never')) } + + context 'and when:manual set as the default' do + let(:rules) { described_class.new(rule_list, 'manual') } + + it { is_expected.to eq(described_class::Result.new('never')) } + end + end + + context 'with one rule without any clauses' do + let(:rule_list) { [{ when: 'manual' }] } + + it { is_expected.to eq(described_class::Result.new('manual')) } + end + + context 'with one matching rule' do + let(:rule_list) { [{ if: '$VAR == null', when: 'always' }] } + + it { is_expected.to eq(described_class::Result.new('always')) } + end + + context 'with two matching rules' do + let(:rule_list) do + [ + { if: '$VAR == null', when: 'delayed', start_in: '1 day' }, + { if: '$VAR == null', when: 'always' } + ] + end + + it 'returns the value of the first matched rule in the list' do + expect(subject).to eq(described_class::Result.new('delayed', '1 day')) + end + end + + context 'with a non-matching and matching rule' do + let(:rule_list) do + [ + { if: '$VAR =! null', when: 'delayed', start_in: '1 day' }, + { if: '$VAR == null', when: 'always' } + ] + end + + it { is_expected.to eq(described_class::Result.new('always')) } + end + + context 'with a matching and non-matching rule' do + let(:rule_list) do + [ + { if: '$VAR == null', when: 'delayed', start_in: '1 day' }, + { if: '$VAR != null', when: 'always' } + ] + end + + it { is_expected.to eq(described_class::Result.new('delayed', '1 day')) } + end + + context 'with non-matching rules' do + let(:rule_list) do + [ + { if: '$VAR != null', when: 'delayed', start_in: '1 day' }, + { if: '$VAR != null', when: 'always' } + ] + end + + it { is_expected.to eq(described_class::Result.new('never')) } + + context 'and when:manual set as the default' do + let(:rules) { described_class.new(rule_list, 'manual') } + + it 'does not return the default when:' do + expect(subject).to eq(described_class::Result.new('never')) + end + end + end + end +end diff --git a/spec/lib/gitlab/ci/build/step_spec.rb b/spec/lib/gitlab/ci/build/step_spec.rb index e3136fc925e..84e6e0e177f 100644 --- a/spec/lib/gitlab/ci/build/step_spec.rb +++ b/spec/lib/gitlab/ci/build/step_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Build::Step do diff --git a/spec/lib/gitlab/ci/charts_spec.rb b/spec/lib/gitlab/ci/charts_spec.rb index 1668d3bbaac..cfb7a3f72fa 100644 --- a/spec/lib/gitlab/ci/charts_spec.rb +++ b/spec/lib/gitlab/ci/charts_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Charts do diff --git a/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb b/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb index bd1f2c92844..a7f457e0f5e 100644 --- a/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Artifacts do diff --git a/spec/lib/gitlab/ci/config/entry/cache_spec.rb b/spec/lib/gitlab/ci/config/entry/cache_spec.rb index 8f711e02f9b..4cb63168ec7 100644 --- a/spec/lib/gitlab/ci/config/entry/cache_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/cache_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Cache do diff --git a/spec/lib/gitlab/ci/config/entry/commands_spec.rb b/spec/lib/gitlab/ci/config/entry/commands_spec.rb index 8934aeb83db..269a3406913 100644 --- a/spec/lib/gitlab/ci/config/entry/commands_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/commands_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Commands do diff --git a/spec/lib/gitlab/ci/config/entry/coverage_spec.rb b/spec/lib/gitlab/ci/config/entry/coverage_spec.rb index 4c6bd859552..48d0864cfca 100644 --- a/spec/lib/gitlab/ci/config/entry/coverage_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/coverage_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Coverage do diff --git a/spec/lib/gitlab/ci/config/entry/default_spec.rb b/spec/lib/gitlab/ci/config/entry/default_spec.rb index a901dd80c2c..27d63dbd407 100644 --- a/spec/lib/gitlab/ci/config/entry/default_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/default_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Default do diff --git a/spec/lib/gitlab/ci/config/entry/environment_spec.rb b/spec/lib/gitlab/ci/config/entry/environment_spec.rb index 0bc9e8bd3cd..7b72b45fd8d 100644 --- a/spec/lib/gitlab/ci/config/entry/environment_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/environment_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Environment do diff --git a/spec/lib/gitlab/ci/config/entry/hidden_spec.rb b/spec/lib/gitlab/ci/config/entry/hidden_spec.rb index c88ee10550c..40b73352676 100644 --- a/spec/lib/gitlab/ci/config/entry/hidden_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/hidden_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Hidden do diff --git a/spec/lib/gitlab/ci/config/entry/image_spec.rb b/spec/lib/gitlab/ci/config/entry/image_spec.rb index 1ebdda398b9..8de2e5de724 100644 --- a/spec/lib/gitlab/ci/config/entry/image_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/image_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Image do diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb index 25766d34c65..1853efde350 100644 --- a/spec/lib/gitlab/ci/config/entry/job_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Job do @@ -9,7 +11,7 @@ describe Gitlab::Ci::Config::Entry::Job do let(:result) do %i[before_script script stage type after_script cache - image services only except variables artifacts + image services only except rules variables artifacts environment coverage retry] end @@ -84,6 +86,31 @@ describe Gitlab::Ci::Config::Entry::Job do it { expect(entry).to be_valid } end end + + context 'when has needs' do + let(:config) do + { + stage: 'test', + script: 'echo', + needs: ['another-job'] + } + end + + it { expect(entry).to be_valid } + + context 'when has dependencies' do + let(:config) do + { + stage: 'test', + script: 'echo', + dependencies: ['another-job'], + needs: ['another-job'] + } + end + + it { expect(entry).to be_valid } + end + end end context 'when entry value is not correct' do @@ -174,6 +201,21 @@ describe Gitlab::Ci::Config::Entry::Job do expect(entry.errors).to include 'job parallel must be an integer' end end + + context 'when it uses both "when:" and "rules:"' do + let(:config) do + { + script: 'echo', + when: 'on_failure', + rules: [{ if: '$VARIABLE', when: 'on_success' }] + } + end + + it 'returns an error about when: being combined with rules' do + expect(entry).not_to be_valid + expect(entry.errors).to include 'job config key may not be used with `rules`: when' + end + end end context 'when delayed job' do @@ -213,6 +255,100 @@ describe Gitlab::Ci::Config::Entry::Job do end end + context 'when only: is used with rules:' do + let(:config) { { only: ['merge_requests'], rules: [{ if: '$THIS' }] } } + + it 'returns error about mixing only: with rules:' do + expect(entry).not_to be_valid + expect(entry.errors).to include /may not be used with `rules`/ + end + + context 'and only: is blank' do + let(:config) { { only: nil, rules: [{ if: '$THIS' }] } } + + it 'returns error about mixing only: with rules:' do + expect(entry).not_to be_valid + expect(entry.errors).to include /may not be used with `rules`/ + end + end + + context 'and rules: is blank' do + let(:config) { { only: ['merge_requests'], rules: nil } } + + it 'returns error about mixing only: with rules:' do + expect(entry).not_to be_valid + expect(entry.errors).to include /may not be used with `rules`/ + end + end + end + + context 'when except: is used with rules:' do + let(:config) { { except: { refs: %w[master] }, rules: [{ if: '$THIS' }] } } + + it 'returns error about mixing except: with rules:' do + expect(entry).not_to be_valid + expect(entry.errors).to include /may not be used with `rules`/ + end + + context 'and except: is blank' do + let(:config) { { except: nil, rules: [{ if: '$THIS' }] } } + + it 'returns error about mixing except: with rules:' do + expect(entry).not_to be_valid + expect(entry.errors).to include /may not be used with `rules`/ + end + end + + context 'and rules: is blank' do + let(:config) { { except: { refs: %w[master] }, rules: nil } } + + it 'returns error about mixing except: with rules:' do + expect(entry).not_to be_valid + expect(entry.errors).to include /may not be used with `rules`/ + end + end + end + + context 'when only: and except: are both used with rules:' do + let(:config) do + { + only: %w[merge_requests], + except: { refs: %w[master] }, + rules: [{ if: '$THIS' }] + } + end + + it 'returns errors about mixing both only: and except: with rules:' do + expect(entry).not_to be_valid + expect(entry.errors).to include /may not be used with `rules`/ + expect(entry.errors).to include /may not be used with `rules`/ + end + + context 'when only: and except: as both blank' do + let(:config) do + { only: nil, except: nil, rules: [{ if: '$THIS' }] } + end + + it 'returns errors about mixing both only: and except: with rules:' do + expect(entry).not_to be_valid + expect(entry.errors).to include /may not be used with `rules`/ + expect(entry.errors).to include /may not be used with `rules`/ + end + end + + context 'when rules: is blank' do + let(:config) do + { only: %w[merge_requests], except: { refs: %w[master] }, rules: nil } + end + + it 'returns errors about mixing both only: and except: with rules:' do + expect(entry).not_to be_valid + expect(entry.errors).to include /may not be used with `rules`/ + expect(entry.errors).to include /may not be used with `rules`/ + end + end + end + context 'when start_in specified without delayed specification' do let(:config) { { start_in: '1 day' } } @@ -221,6 +357,66 @@ describe Gitlab::Ci::Config::Entry::Job do expect(entry.errors).to include 'job start in must be blank' end end + + context 'when has dependencies' do + context 'that are not a array of strings' do + let(:config) do + { script: 'echo', dependencies: 'build-job' } + end + + it 'returns error about invalid type' do + expect(entry).not_to be_valid + expect(entry.errors).to include 'job dependencies should be an array of strings' + end + end + end + + context 'when has needs' do + context 'that are not a array of strings' do + let(:config) do + { + stage: 'test', + script: 'echo', + needs: 'build-job' + } + end + + it 'returns error about invalid type' do + expect(entry).not_to be_valid + expect(entry.errors).to include 'job needs should be an array of strings' + end + end + + context 'when have dependencies that are not subset of needs' do + let(:config) do + { + stage: 'test', + script: 'echo', + dependencies: ['another-job'], + needs: ['build-job'] + } + end + + it 'returns error about invalid data' do + expect(entry).not_to be_valid + expect(entry.errors).to include 'job dependencies the another-job should be part of needs' + end + end + + context 'when stage: is missing' do + let(:config) do + { + script: 'echo', + needs: ['build-job'] + } + end + + it 'returns error about invalid data' do + expect(entry).not_to be_valid + expect(entry.errors).to include 'job config missing required keys: stage' + end + end + end end end diff --git a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb index 3e4137930dc..61c8956d41f 100644 --- a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Jobs do diff --git a/spec/lib/gitlab/ci/config/entry/key_spec.rb b/spec/lib/gitlab/ci/config/entry/key_spec.rb index 3cbf19bea8b..a7874447725 100644 --- a/spec/lib/gitlab/ci/config/entry/key_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/key_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Key do diff --git a/spec/lib/gitlab/ci/config/entry/paths_spec.rb b/spec/lib/gitlab/ci/config/entry/paths_spec.rb index 1d9c5ddee9b..221d5ae5863 100644 --- a/spec/lib/gitlab/ci/config/entry/paths_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/paths_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Paths do diff --git a/spec/lib/gitlab/ci/config/entry/policy_spec.rb b/spec/lib/gitlab/ci/config/entry/policy_spec.rb index fba5671594d..a606eb303e7 100644 --- a/spec/lib/gitlab/ci/config/entry/policy_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/policy_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'fast_spec_helper' require 'support/helpers/stub_feature_flags' require_dependency 'active_model' @@ -49,8 +51,6 @@ describe Gitlab::Ci::Config::Entry::Policy do let(:config) { ['/^(?!master).+/'] } - subject { described_class.new([regexp]) } - context 'when allow_unsafe_ruby_regexp is disabled' do before do stub_feature_flags(allow_unsafe_ruby_regexp: false) @@ -111,8 +111,6 @@ describe Gitlab::Ci::Config::Entry::Policy do let(:config) { { refs: ['/^(?!master).+/'] } } - subject { described_class.new([regexp]) } - context 'when allow_unsafe_ruby_regexp is disabled' do before do stub_feature_flags(allow_unsafe_ruby_regexp: false) @@ -202,6 +200,14 @@ describe Gitlab::Ci::Config::Entry::Policy do end context 'when changes policy is invalid' do + let(:config) { { changes: 'some/*' } } + + it 'returns errors' do + expect(entry.errors).to include /changes should be an array of strings/ + end + end + + context 'when changes policy is invalid' do let(:config) { { changes: [1, 2] } } it 'returns errors' do diff --git a/spec/lib/gitlab/ci/config/entry/reports_spec.rb b/spec/lib/gitlab/ci/config/entry/reports_spec.rb index 38943138cbf..3c352c30e55 100644 --- a/spec/lib/gitlab/ci/config/entry/reports_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/reports_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Reports do diff --git a/spec/lib/gitlab/ci/config/entry/retry_spec.rb b/spec/lib/gitlab/ci/config/entry/retry_spec.rb index 164a9ed4c3d..f9efd2e014d 100644 --- a/spec/lib/gitlab/ci/config/entry/retry_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/retry_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Retry do diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb index 7a252ed675a..968dbb9c7f2 100644 --- a/spec/lib/gitlab/ci/config/entry/root_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Root do diff --git a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb new file mode 100644 index 00000000000..c25344ec1a4 --- /dev/null +++ b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb @@ -0,0 +1,208 @@ +require 'fast_spec_helper' +require 'chronic_duration' +require 'support/helpers/stub_feature_flags' +require_dependency 'active_model' + +describe Gitlab::Ci::Config::Entry::Rules::Rule do + let(:entry) { described_class.new(config) } + + describe '.new' do + subject { entry } + + context 'with a when: value but no clauses' do + let(:config) { { when: 'manual' } } + + it { is_expected.to be_valid } + end + + context 'when specifying an if: clause' do + let(:config) { { if: '$THIS || $THAT', when: 'manual' } } + + it { is_expected.to be_valid } + + describe '#when' do + subject { entry.when } + + it { is_expected.to eq('manual') } + end + end + + context 'using a list of multiple expressions' do + let(:config) { { if: ['$MY_VAR == "this"', '$YOUR_VAR == "that"'] } } + + it { is_expected.not_to be_valid } + + it 'reports an error about invalid format' do + expect(subject.errors).to include(/invalid expression syntax/) + end + end + + context 'when specifying an invalid if: clause expression' do + let(:config) { { if: ['$MY_VAR =='] } } + + it { is_expected.not_to be_valid } + + it 'reports an error about invalid statement' do + expect(subject.errors).to include(/invalid expression syntax/) + end + end + + context 'when specifying an if: clause expression with an invalid token' do + let(:config) { { if: ['$MY_VAR == 123'] } } + + it { is_expected.not_to be_valid } + + it 'reports an error about invalid statement' do + expect(subject.errors).to include(/invalid expression syntax/) + end + end + + context 'when using invalid regex in an if: clause' do + let(:config) { { if: ['$MY_VAR =~ /some ( thing/'] } } + + it 'reports an error about invalid expression' do + expect(subject.errors).to include(/invalid expression syntax/) + end + end + + context 'when using an if: clause with lookahead regex character "?"' do + let(:config) { { if: '$CI_COMMIT_REF =~ /^(?!master).+/' } } + + context 'when allow_unsafe_ruby_regexp is disabled' do + it { is_expected.not_to be_valid } + + it 'reports an error about invalid expression syntax' do + expect(subject.errors).to include(/invalid expression syntax/) + end + end + end + + context 'when using a changes: clause' do + let(:config) { { changes: %w[app/ lib/ spec/ other/* paths/**/*.rb] } } + + it { is_expected.to be_valid } + end + + context 'when using a string as an invalid changes: clause' do + let(:config) { { changes: 'a regular string' } } + + it { is_expected.not_to be_valid } + + it 'reports an error about invalid policy' do + expect(subject.errors).to include(/should be an array of strings/) + end + end + + context 'when using a list as an invalid changes: clause' do + let(:config) { { changes: [1, 2] } } + + it { is_expected.not_to be_valid } + + it 'returns errors' do + expect(subject.errors).to include(/changes should be an array of strings/) + end + end + + context 'specifying a delayed job' do + let(:config) { { if: '$THIS || $THAT', when: 'delayed', start_in: '15 minutes' } } + + it { is_expected.to be_valid } + + it 'sets attributes for the job delay' do + expect(entry.when).to eq('delayed') + expect(entry.start_in).to eq('15 minutes') + end + + context 'without a when: key' do + let(:config) { { if: '$THIS || $THAT', start_in: '15 minutes' } } + + it { is_expected.not_to be_valid } + + it 'returns an error about the disallowed key' do + expect(entry.errors).to include(/disallowed keys: start_in/) + end + end + + context 'without a start_in: key' do + let(:config) { { if: '$THIS || $THAT', when: 'delayed' } } + + it { is_expected.not_to be_valid } + + it 'returns an error about tstart_in being blank' do + expect(entry.errors).to include(/start in can't be blank/) + end + end + end + + context 'when specifying unknown policy' do + let(:config) { { invalid: :something } } + + it { is_expected.not_to be_valid } + + it 'returns error about invalid key' do + expect(entry.errors).to include(/unknown keys: invalid/) + end + end + + context 'when clause is empty' do + let(:config) { {} } + + it { is_expected.not_to be_valid } + + it 'is not a valid configuration' do + expect(entry.errors).to include(/can't be blank/) + end + end + + context 'when policy strategy does not match' do + let(:config) { 'string strategy' } + + it { is_expected.not_to be_valid } + + it 'returns information about errors' do + expect(entry.errors) + .to include(/should be a hash/) + end + end + end + + describe '#value' do + subject { entry.value } + + context 'when specifying an if: clause' do + let(:config) { { if: '$THIS || $THAT', when: 'manual' } } + + it 'stores the expression as "if"' do + expect(subject).to eq(if: '$THIS || $THAT', when: 'manual') + end + end + + context 'when using a changes: clause' do + let(:config) { { changes: %w[app/ lib/ spec/ other/* paths/**/*.rb] } } + + it { is_expected.to eq(config) } + end + + context 'when default value has been provided' do + let(:config) { { changes: %w[app/**/*.rb] } } + + before do + entry.default = { changes: %w[**/*] } + end + + it 'does not set a default value' do + expect(entry.default).to eq(nil) + end + + it 'does not add to provided configuration' do + expect(entry.value).to eq(config) + end + end + end + + describe '.default' do + it 'does not have default value' do + expect(described_class.default).to be_nil + end + end +end diff --git a/spec/lib/gitlab/ci/config/entry/rules_spec.rb b/spec/lib/gitlab/ci/config/entry/rules_spec.rb new file mode 100644 index 00000000000..291e7373daf --- /dev/null +++ b/spec/lib/gitlab/ci/config/entry/rules_spec.rb @@ -0,0 +1,135 @@ +require 'fast_spec_helper' +require 'support/helpers/stub_feature_flags' +require_dependency 'active_model' + +describe Gitlab::Ci::Config::Entry::Rules do + let(:entry) { described_class.new(config) } + + describe '.new' do + subject { entry } + + context 'with a list of rule rule' do + let(:config) do + [{ if: '$THIS == "that"', when: 'never' }] + end + + it { is_expected.to be_a(described_class) } + it { is_expected.to be_valid } + + context 'after #compose!' do + before do + subject.compose! + end + + it { is_expected.to be_valid } + end + end + + context 'with a list of two rules' do + let(:config) do + [ + { if: '$THIS == "that"', when: 'always' }, + { if: '$SKIP', when: 'never' } + ] + end + + it { is_expected.to be_a(described_class) } + it { is_expected.to be_valid } + + context 'after #compose!' do + before do + subject.compose! + end + + it { is_expected.to be_valid } + end + end + + context 'with a single rule object' do + let(:config) do + { if: '$SKIP', when: 'never' } + end + + it { is_expected.not_to be_valid } + end + + context 'with an invalid boolean when:' do + let(:config) do + [{ if: '$THIS == "that"', when: false }] + end + + it { is_expected.to be_a(described_class) } + it { is_expected.to be_valid } + + context 'after #compose!' do + before do + subject.compose! + end + + it { is_expected.not_to be_valid } + + it 'returns an error about invalid when:' do + expect(subject.errors).to include(/when unknown value: false/) + end + end + end + + context 'with an invalid string when:' do + let(:config) do + [{ if: '$THIS == "that"', when: 'explode' }] + end + + it { is_expected.to be_a(described_class) } + it { is_expected.to be_valid } + + context 'after #compose!' do + before do + subject.compose! + end + + it { is_expected.not_to be_valid } + + it 'returns an error about invalid when:' do + expect(subject.errors).to include(/when unknown value: explode/) + end + end + end + end + + describe '#value' do + subject { entry.value } + + context 'with a list of rule rule' do + let(:config) do + [{ if: '$THIS == "that"', when: 'never' }] + end + + it { is_expected.to eq(config) } + end + + context 'with a list of two rules' do + let(:config) do + [ + { if: '$THIS == "that"', when: 'always' }, + { if: '$SKIP', when: 'never' } + ] + end + + it { is_expected.to eq(config) } + end + + context 'with a single rule object' do + let(:config) do + { if: '$SKIP', when: 'never' } + end + + it { is_expected.to eq(config) } + end + end + + describe '.default' do + it 'does not have default policy' do + expect(described_class.default).to be_nil + end + end +end diff --git a/spec/lib/gitlab/ci/config/entry/script_spec.rb b/spec/lib/gitlab/ci/config/entry/script_spec.rb index 069eaa26422..d523243d3b6 100644 --- a/spec/lib/gitlab/ci/config/entry/script_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/script_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Script do diff --git a/spec/lib/gitlab/ci/config/entry/service_spec.rb b/spec/lib/gitlab/ci/config/entry/service_spec.rb index d31866a1987..66cca100688 100644 --- a/spec/lib/gitlab/ci/config/entry/service_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/service_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Service do diff --git a/spec/lib/gitlab/ci/config/entry/services_spec.rb b/spec/lib/gitlab/ci/config/entry/services_spec.rb index d5a1316f665..764f783b083 100644 --- a/spec/lib/gitlab/ci/config/entry/services_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/services_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Services do diff --git a/spec/lib/gitlab/ci/config/entry/stage_spec.rb b/spec/lib/gitlab/ci/config/entry/stage_spec.rb index 70c8a0a355a..574fa00575a 100644 --- a/spec/lib/gitlab/ci/config/entry/stage_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/stage_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Stage do diff --git a/spec/lib/gitlab/ci/config/entry/stages_spec.rb b/spec/lib/gitlab/ci/config/entry/stages_spec.rb index 182c8d867c7..97970522104 100644 --- a/spec/lib/gitlab/ci/config/entry/stages_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/stages_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Stages do diff --git a/spec/lib/gitlab/ci/config/entry/variables_spec.rb b/spec/lib/gitlab/ci/config/entry/variables_spec.rb index 84bfef9e8ad..1320b366367 100644 --- a/spec/lib/gitlab/ci/config/entry/variables_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/variables_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config::Entry::Variables do diff --git a/spec/lib/gitlab/ci/config/extendable/entry_spec.rb b/spec/lib/gitlab/ci/config/extendable/entry_spec.rb index d63612053b6..e00104e3c68 100644 --- a/spec/lib/gitlab/ci/config/extendable/entry_spec.rb +++ b/spec/lib/gitlab/ci/config/extendable/entry_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'fast_spec_helper' describe Gitlab::Ci::Config::Extendable::Entry do diff --git a/spec/lib/gitlab/ci/config/extendable_spec.rb b/spec/lib/gitlab/ci/config/extendable_spec.rb index 90213f6603d..874b224067b 100644 --- a/spec/lib/gitlab/ci/config/extendable_spec.rb +++ b/spec/lib/gitlab/ci/config/extendable_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'fast_spec_helper' describe Gitlab::Ci::Config::Extendable do diff --git a/spec/lib/gitlab/ci/config/normalizer_spec.rb b/spec/lib/gitlab/ci/config/normalizer_spec.rb index cd880177170..6b766cc37bf 100644 --- a/spec/lib/gitlab/ci/config/normalizer_spec.rb +++ b/spec/lib/gitlab/ci/config/normalizer_spec.rb @@ -49,37 +49,44 @@ describe Gitlab::Ci::Config::Normalizer do end end - context 'when jobs depend on parallelized jobs' do - let(:config) { { job_name => job_config, other_job: { script: 'echo 1', dependencies: [job_name.to_s] } } } - - it 'parallelizes dependencies' do - job_names = ["rspec 1/5", "rspec 2/5", "rspec 3/5", "rspec 4/5", "rspec 5/5"] - - expect(subject[:other_job][:dependencies]).to include(*job_names) + %i[dependencies needs].each do |context| + context "when job has #{context} on parallelized jobs" do + let(:config) do + { + job_name => job_config, + other_job: { script: 'echo 1', context => [job_name.to_s] } + } + end + + it "parallelizes #{context}" do + job_names = ["rspec 1/5", "rspec 2/5", "rspec 3/5", "rspec 4/5", "rspec 5/5"] + + expect(subject[:other_job][context]).to include(*job_names) + end + + it "does not include original job name in #{context}" do + expect(subject[:other_job][context]).not_to include(job_name) + end end - it 'does not include original job name in dependencies' do - expect(subject[:other_job][:dependencies]).not_to include(job_name) - end - end + context "when there are #{context} which are both parallelized and not" do + let(:config) do + { + job_name => job_config, + other_job: { script: 'echo 1' }, + final_job: { script: 'echo 1', context => [job_name.to_s, "other_job"] } + } + end - context 'when there are dependencies which are both parallelized and not' do - let(:config) do - { - job_name => job_config, - other_job: { script: 'echo 1' }, - final_job: { script: 'echo 1', dependencies: [job_name.to_s, "other_job"] } - } - end - - it 'parallelizes dependencies' do - job_names = ["rspec 1/5", "rspec 2/5", "rspec 3/5", "rspec 4/5", "rspec 5/5"] + it "parallelizes #{context}" do + job_names = ["rspec 1/5", "rspec 2/5", "rspec 3/5", "rspec 4/5", "rspec 5/5"] - expect(subject[:final_job][:dependencies]).to include(*job_names) - end + expect(subject[:final_job][context]).to include(*job_names) + end - it 'includes the regular job in dependencies' do - expect(subject[:final_job][:dependencies]).to include('other_job') + it "includes the regular job in #{context}" do + expect(subject[:final_job][context]).to include('other_job') + end end end end diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb index 4e8bff3d738..986cde3540a 100644 --- a/spec/lib/gitlab/ci/config_spec.rb +++ b/spec/lib/gitlab/ci/config_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Config do diff --git a/spec/lib/gitlab/ci/cron_parser_spec.rb b/spec/lib/gitlab/ci/cron_parser_spec.rb index 491e3fba9d9..af4e9d687c4 100644 --- a/spec/lib/gitlab/ci/cron_parser_spec.rb +++ b/spec/lib/gitlab/ci/cron_parser_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::CronParser do diff --git a/spec/lib/gitlab/ci/mask_secret_spec.rb b/spec/lib/gitlab/ci/mask_secret_spec.rb index 3789a142248..6607aaae399 100644 --- a/spec/lib/gitlab/ci/mask_secret_spec.rb +++ b/spec/lib/gitlab/ci/mask_secret_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::MaskSecret do diff --git a/spec/lib/gitlab/ci/parsers/test/junit_spec.rb b/spec/lib/gitlab/ci/parsers/test/junit_spec.rb index a49402c7398..8ff60710f67 100644 --- a/spec/lib/gitlab/ci/parsers/test/junit_spec.rb +++ b/spec/lib/gitlab/ci/parsers/test/junit_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'fast_spec_helper' describe Gitlab::Ci::Parsers::Test::Junit do diff --git a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb index 50cb45c39d1..bf9ff922c05 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Chain::Build do diff --git a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb index 5181e9c1583..5775e934cfd 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Chain::Command do diff --git a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb index 0edc3f315bb..650ab193997 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Chain::Create do diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb index 0302e4090cf..9bccd5be4fe 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Chain::Populate do @@ -36,8 +38,8 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do it 'populates pipeline with stages' do expect(pipeline.stages).to be_one expect(pipeline.stages.first).not_to be_persisted - expect(pipeline.stages.first.builds).to be_one - expect(pipeline.stages.first.builds.first).not_to be_persisted + expect(pipeline.stages.first.statuses).to be_one + expect(pipeline.stages.first.statuses.first).not_to be_persisted end it 'correctly assigns user' do @@ -189,8 +191,8 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do step.perform! expect(pipeline.stages.size).to eq 1 - expect(pipeline.stages.first.builds.size).to eq 1 - expect(pipeline.stages.first.builds.first.name).to eq 'rspec' + expect(pipeline.stages.first.statuses.size).to eq 1 + expect(pipeline.stages.first.statuses.first.name).to eq 'rspec' end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb index eca23694a2b..9cb59442dfd 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Chain::Sequence do diff --git a/spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb index c7f4fc98ca3..fe46633ed1b 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Chain::Skip do diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb index 7d750877d09..ac370433955 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do @@ -10,7 +12,11 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do let(:command) do Gitlab::Ci::Pipeline::Chain::Command.new( - project: project, current_user: user, origin_ref: origin_ref, merge_request: merge_request) + project: project, + current_user: user, + origin_ref: origin_ref, + merge_request: merge_request, + trigger_request: trigger_request) end let(:step) { described_class.new(pipeline, command) } @@ -18,6 +24,7 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do let(:ref) { 'master' } let(:origin_ref) { ref } let(:merge_request) { nil } + let(:trigger_request) { nil } shared_context 'detached merge request pipeline' do let(:merge_request) do @@ -69,6 +76,43 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do end end + context 'when pipeline triggered by legacy trigger' do + let(:user) { nil } + let(:trigger_request) do + build_stubbed(:ci_trigger_request, trigger: build_stubbed(:ci_trigger, owner: nil)) + end + + context 'when :use_legacy_pipeline_triggers feature flag is enabled' do + before do + stub_feature_flags(use_legacy_pipeline_triggers: true) + step.perform! + end + + it 'allows legacy triggers to create a pipeline' do + expect(pipeline).to be_valid + end + + it 'does not break the chain' do + expect(step.break?).to eq false + end + end + + context 'when :use_legacy_pipeline_triggers feature flag is disabled' do + before do + stub_feature_flags(use_legacy_pipeline_triggers: false) + step.perform! + end + + it 'prevents legacy triggers from creating a pipeline' do + expect(pipeline.errors.to_a).to include /Trigger token is invalid/ + end + + it 'breaks the pipeline builder chain' do + expect(step.break?).to eq true + end + end + end + describe '#allowed_to_create?' do subject { step.allowed_to_create? } diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb index 00bbc4c817a..79acd3e4f54 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Chain::Validate::Config do diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb index 2e8c9d70098..b866355906e 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Chain::Validate::Repository do diff --git a/spec/lib/gitlab/ci/pipeline/duration_spec.rb b/spec/lib/gitlab/ci/pipeline/duration_spec.rb index 7c9836e2da6..a4984092f35 100644 --- a/spec/lib/gitlab/ci/pipeline/duration_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/duration_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Duration do diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/and_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/and_spec.rb index 006ce4d8078..847d613dba3 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/and_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/and_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'fast_spec_helper' require 'rspec-parameterized' 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 fcbd2863289..0e13681a4cf 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/equals_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/equals_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Expression::Lexeme::Equals do diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb index a6fdec832a3..a527783ffac 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'fast_spec_helper' require_dependency 're2' @@ -67,6 +69,34 @@ describe Gitlab::Ci::Pipeline::Expression::Lexeme::Matches do it { is_expected.to eq(false) } end + context 'when right is nil' do + let(:left_value) { 'my-awesome-string' } + let(:right_value) { nil } + + it { is_expected.to eq(false) } + end + + context 'when left and right are nil' do + let(:left_value) { nil } + let(:right_value) { nil } + + it { is_expected.to eq(false) } + end + + context 'when left is an empty string' do + let(:left_value) { '' } + let(:right_value) { Gitlab::UntrustedRegexp.new('pattern') } + + it { is_expected.to eq(false) } + end + + context 'when left and right are empty strings' do + let(:left_value) { '' } + let(:right_value) { Gitlab::UntrustedRegexp.new('') } + + it { is_expected.to eq(true) } + end + context 'when left is a multiline string and matches right' do let(:left_value) do <<~TEXT diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb index 38d30c9035a..a3a48f83b27 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Expression::Lexeme::NotEquals do diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb index 99110ff8d88..fb4238ecaf3 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'fast_spec_helper' require_dependency 're2' @@ -67,6 +69,34 @@ describe Gitlab::Ci::Pipeline::Expression::Lexeme::NotMatches do it { is_expected.to eq(true) } end + context 'when right is nil' do + let(:left_value) { 'my-awesome-string' } + let(:right_value) { nil } + + it { is_expected.to eq(true) } + end + + context 'when left and right are nil' do + let(:left_value) { nil } + let(:right_value) { nil } + + it { is_expected.to eq(true) } + end + + context 'when left is an empty string' do + let(:left_value) { '' } + let(:right_value) { Gitlab::UntrustedRegexp.new('pattern') } + + it { is_expected.to eq(true) } + end + + context 'when left and right are empty strings' do + let(:left_value) { '' } + let(:right_value) { Gitlab::UntrustedRegexp.new('') } + + it { is_expected.to eq(false) } + end + context 'when left is a multiline string and matches right' do let(:left_value) do <<~TEXT diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/null_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/null_spec.rb index b5a59929e11..7013c6bacbb 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/null_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/null_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Expression::Lexeme::Null do diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/or_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/or_spec.rb index d542eebc613..15505ebc82b 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/or_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/or_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'fast_spec_helper' require 'rspec-parameterized' diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb index 30ea3f3e28e..2cc25a07417 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do @@ -107,42 +109,6 @@ describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do expect(token.build.evaluate) .to eq Gitlab::UntrustedRegexp.new('some numeric \$ pattern') end - - context 'with the ci_variables_complex_expressions feature flag disabled' do - before do - stub_feature_flags(ci_variables_complex_expressions: false) - end - - it 'is a greedy scanner for regexp boundaries' do - scanner = StringScanner.new('/some .* / pattern/') - - token = described_class.scan(scanner) - - expect(token).not_to be_nil - expect(token.build.evaluate) - .to eq Gitlab::UntrustedRegexp.new('some .* / pattern') - end - - it 'does not recognize the \ escape character for /' do - scanner = StringScanner.new('/some .* \/ pattern/') - - token = described_class.scan(scanner) - - expect(token).not_to be_nil - expect(token.build.evaluate) - .to eq Gitlab::UntrustedRegexp.new('some .* \/ pattern') - end - - it 'does not recognize the \ escape character for $' do - scanner = StringScanner.new('/some numeric \$ pattern/') - - token = described_class.scan(scanner) - - expect(token).not_to be_nil - expect(token.build.evaluate) - .to eq Gitlab::UntrustedRegexp.new('some numeric \$ pattern') - end - end end describe '#evaluate' do diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/string_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/string_spec.rb index f54ef492e6d..2a6b90d127f 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/string_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/string_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Expression::Lexeme::String do diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/variable_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/variable_spec.rb index 599a5411881..29e26930249 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/variable_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/variable_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Expression::Lexeme::Variable do diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexer_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexer_spec.rb index d8db9c262a1..2b0cee2d6f2 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexer_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexer_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Expression::Lexer do @@ -80,34 +82,6 @@ describe Gitlab::Ci::Pipeline::Expression::Lexer do it { is_expected.to eq(tokens) } end end - - context 'with the ci_variables_complex_expressions feature flag turned off' do - before do - stub_feature_flags(ci_variables_complex_expressions: false) - end - - it 'incorrectly tokenizes conjunctive match statements as one match statement' do - tokens = described_class.new('$PRESENT_VARIABLE =~ /my var/ && $EMPTY_VARIABLE =~ /nope/').tokens - - expect(tokens.map(&:value)).to eq(['$PRESENT_VARIABLE', '=~', '/my var/ && $EMPTY_VARIABLE =~ /nope/']) - end - - it 'incorrectly tokenizes disjunctive match statements as one statement' do - tokens = described_class.new('$PRESENT_VARIABLE =~ /my var/ || $EMPTY_VARIABLE =~ /nope/').tokens - - expect(tokens.map(&:value)).to eq(['$PRESENT_VARIABLE', '=~', '/my var/ || $EMPTY_VARIABLE =~ /nope/']) - end - - it 'raises an error about && operators' do - expect { described_class.new('$EMPTY_VARIABLE == "" && $PRESENT_VARIABLE').tokens } - .to raise_error(Gitlab::Ci::Pipeline::Expression::Lexer::SyntaxError).with_message('Unknown lexeme found!') - end - - it 'raises an error about || operators' do - expect { described_class.new('$EMPTY_VARIABLE == "" || $PRESENT_VARIABLE').tokens } - .to raise_error(Gitlab::Ci::Pipeline::Expression::Lexer::SyntaxError).with_message('Unknown lexeme found!') - end - end end describe '#lexemes' do diff --git a/spec/lib/gitlab/ci/pipeline/expression/parser_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/parser_spec.rb index e88ec5561b6..10adfa18af6 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/parser_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/parser_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'fast_spec_helper' describe Gitlab::Ci::Pipeline::Expression::Parser do diff --git a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb index a2c2e3653d5..79ee4d07e3a 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb @@ -1,6 +1,6 @@ -# TODO switch this back after the "ci_variables_complex_expressions" feature flag is removed -# require 'fast_spec_helper' -require 'spec_helper' +# frozen_string_literal: true + +require 'fast_spec_helper' require 'rspec-parameterized' describe Gitlab::Ci::Pipeline::Expression::Statement do @@ -118,54 +118,6 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do expect(subject.evaluate).to eq(value) end end - - context 'with the ci_variables_complex_expressions feature flag disabled' do - before do - stub_feature_flags(ci_variables_complex_expressions: false) - end - - where(:expression, :value) do - '$PRESENT_VARIABLE == "my variable"' | true - '"my variable" == $PRESENT_VARIABLE' | true - '$PRESENT_VARIABLE == null' | false - '$EMPTY_VARIABLE == null' | false - '"" == $EMPTY_VARIABLE' | true - '$EMPTY_VARIABLE' | '' - '$UNDEFINED_VARIABLE == null' | true - 'null == $UNDEFINED_VARIABLE' | true - '$PRESENT_VARIABLE' | 'my variable' - '$UNDEFINED_VARIABLE' | nil - "$PRESENT_VARIABLE =~ /var.*e$/" | true - "$PRESENT_VARIABLE =~ /^var.*/" | false - "$EMPTY_VARIABLE =~ /var.*/" | false - "$UNDEFINED_VARIABLE =~ /var.*/" | false - "$PRESENT_VARIABLE =~ /VAR.*/i" | true - '$PATH_VARIABLE =~ /path/variable/' | true - '$PATH_VARIABLE =~ /path\/variable/' | true - '$FULL_PATH_VARIABLE =~ /^/a/full/path/variable/value$/' | true - '$FULL_PATH_VARIABLE =~ /^\/a\/full\/path\/variable\/value$/' | true - '$PRESENT_VARIABLE != "my variable"' | false - '"my variable" != $PRESENT_VARIABLE' | false - '$PRESENT_VARIABLE != null' | true - '$EMPTY_VARIABLE != null' | true - '"" != $EMPTY_VARIABLE' | false - '$UNDEFINED_VARIABLE != null' | false - 'null != $UNDEFINED_VARIABLE' | false - "$PRESENT_VARIABLE !~ /var.*e$/" | false - "$PRESENT_VARIABLE !~ /^var.*/" | true - "$EMPTY_VARIABLE !~ /var.*/" | true - "$UNDEFINED_VARIABLE !~ /var.*/" | true - "$PRESENT_VARIABLE !~ /VAR.*/i" | false - end - - with_them do - let(:text) { expression } - - it "evaluates to `#{params[:value].inspect}`" do - expect(subject.evaluate).to eq value - end - end - end end describe '#truthful?' do diff --git a/spec/lib/gitlab/ci/pipeline/expression/token_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/token_spec.rb index cedfe270f9d..aa807cecb72 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/token_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/token_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'fast_spec_helper' describe Gitlab::Ci::Pipeline::Expression::Token do diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb index 7991e2f48b5..89431b80be3 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb @@ -1,53 +1,122 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Seed::Build do let(:project) { create(:project, :repository) } let(:pipeline) { create(:ci_empty_pipeline, project: project) } + let(:attributes) { { name: 'rspec', ref: 'master' } } + let(:previous_stages) { [] } - let(:attributes) do - { name: 'rspec', ref: 'master' } - end - - subject do - described_class.new(pipeline, attributes) - end + let(:seed_build) { described_class.new(pipeline, attributes, previous_stages) } describe '#attributes' do - it 'returns hash attributes of a build' do - expect(subject.attributes).to be_a Hash - expect(subject.attributes) - .to include(:name, :project, :ref) + subject { seed_build.attributes } + + it { is_expected.to be_a(Hash) } + it { is_expected.to include(:name, :project, :ref) } + + context 'with job:when' do + let(:attributes) { { name: 'rspec', ref: 'master', when: 'on_failure' } } + + it { is_expected.to include(when: 'on_failure') } + end + + context 'with job:when:delayed' do + let(:attributes) { { name: 'rspec', ref: 'master', when: 'delayed', start_in: '3 hours' } } + + it { is_expected.to include(when: 'delayed', start_in: '3 hours') } + end + + context 'with job:rules:[when:]' do + context 'is matched' do + let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR == null', when: 'always' }] } } + + it { is_expected.to include(when: 'always') } + end + + context 'is not matched' do + let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR != null', when: 'always' }] } } + + it { is_expected.to include(when: 'never') } + end + end + + context 'with job:rules:[when:delayed]' do + context 'is matched' do + let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR == null', when: 'delayed', start_in: '3 hours' }] } } + + it { is_expected.to include(when: 'delayed', start_in: '3 hours') } + end + + context 'is not matched' do + let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR != null', when: 'delayed', start_in: '3 hours' }] } } + + it { is_expected.to include(when: 'never') } + end + end + + context 'with job:rules but no explicit when:' do + context 'is matched' do + let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR == null' }] } } + + it { is_expected.to include(when: 'on_success') } + end + + context 'is not matched' do + let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR != null' }] } } + + it { is_expected.to include(when: 'never') } + end end end describe '#bridge?' do - context 'when job is a bridge' do + subject { seed_build.bridge? } + + context 'when job is a downstream bridge' do let(:attributes) do { name: 'rspec', ref: 'master', options: { trigger: 'my/project' } } end - it { is_expected.to be_bridge } + it { is_expected.to be_truthy } + + context 'when trigger definition is empty' do + let(:attributes) do + { name: 'rspec', ref: 'master', options: { trigger: '' } } + end + + it { is_expected.to be_falsey } + end end - context 'when trigger definition is empty' do + context 'when job is an upstream bridge' do let(:attributes) do - { name: 'rspec', ref: 'master', options: { trigger: '' } } + { name: 'rspec', ref: 'master', options: { bridge_needs: { pipeline: 'my/project' } } } end - it { is_expected.not_to be_bridge } + it { is_expected.to be_truthy } + + context 'when upstream definition is empty' do + let(:attributes) do + { name: 'rspec', ref: 'master', options: { bridge_needs: { pipeline: '' } } } + end + + it { is_expected.to be_falsey } + end end context 'when job is not a bridge' do - it { is_expected.not_to be_bridge } + it { is_expected.to be_falsey } end end describe '#to_resource' do + subject { seed_build.to_resource } + context 'when job is not a bridge' do - it 'returns a valid build resource' do - expect(subject.to_resource).to be_a(::Ci::Build) - expect(subject.to_resource).to be_valid - end + it { is_expected.to be_a(::Ci::Build) } + it { is_expected.to be_valid } end context 'when job is a bridge' do @@ -55,71 +124,117 @@ describe Gitlab::Ci::Pipeline::Seed::Build do { name: 'rspec', ref: 'master', options: { trigger: 'my/project' } } end - it 'returns a valid bridge resource' do - expect(subject.to_resource).to be_a(::Ci::Bridge) - expect(subject.to_resource).to be_valid - end + it { is_expected.to be_a(::Ci::Bridge) } + it { is_expected.to be_valid } end it 'memoizes a resource object' do - build = subject.to_resource - - expect(build.object_id).to eq subject.to_resource.object_id + expect(subject.object_id).to eq seed_build.to_resource.object_id end it 'can not be persisted without explicit assignment' do - build = subject.to_resource - pipeline.save! - expect(build).not_to be_persisted + expect(subject).not_to be_persisted end end - describe 'applying only/except policies' do + describe 'applying job inclusion policies' do + subject { seed_build } + context 'when no branch policy is specified' do - let(:attributes) { { name: 'rspec' } } + let(:attributes) do + { name: 'rspec' } + end it { is_expected.to be_included } end context 'when branch policy does not match' do context 'when using only' do - let(:attributes) { { name: 'rspec', only: { refs: ['deploy'] } } } + let(:attributes) do + { name: 'rspec', only: { refs: ['deploy'] } } + end it { is_expected.not_to be_included } end context 'when using except' do - let(:attributes) { { name: 'rspec', except: { refs: ['deploy'] } } } + let(:attributes) do + { name: 'rspec', except: { refs: ['deploy'] } } + end it { is_expected.to be_included } end + + context 'with both only and except policies' do + let(:attributes) do + { + name: 'rspec', + only: { refs: %w[deploy] }, + except: { refs: %w[deploy] } + } + end + + it { is_expected.not_to be_included } + end end context 'when branch regexp policy does not match' do context 'when using only' do - let(:attributes) { { name: 'rspec', only: { refs: ['/^deploy$/'] } } } + let(:attributes) do + { name: 'rspec', only: { refs: %w[/^deploy$/] } } + end it { is_expected.not_to be_included } end context 'when using except' do - let(:attributes) { { name: 'rspec', except: { refs: ['/^deploy$/'] } } } + let(:attributes) do + { name: 'rspec', except: { refs: %w[/^deploy$/] } } + end it { is_expected.to be_included } end + + context 'with both only and except policies' do + let(:attributes) do + { + name: 'rspec', + only: { refs: %w[/^deploy$/] }, + except: { refs: %w[/^deploy$/] } + } + end + + it { is_expected.not_to be_included } + end end context 'when branch policy matches' do context 'when using only' do - let(:attributes) { { name: 'rspec', only: { refs: %w[deploy master] } } } + let(:attributes) do + { name: 'rspec', only: { refs: %w[deploy master] } } + end it { is_expected.to be_included } end context 'when using except' do - let(:attributes) { { name: 'rspec', except: { refs: %w[deploy master] } } } + let(:attributes) do + { name: 'rspec', except: { refs: %w[deploy master] } } + end + + it { is_expected.not_to be_included } + end + + context 'when using both only and except policies' do + let(:attributes) do + { + name: 'rspec', + only: { refs: %w[deploy master] }, + except: { refs: %w[deploy master] } + } + end it { is_expected.not_to be_included } end @@ -127,13 +242,29 @@ describe Gitlab::Ci::Pipeline::Seed::Build do context 'when keyword policy matches' do context 'when using only' do - let(:attributes) { { name: 'rspec', only: { refs: ['branches'] } } } + let(:attributes) do + { name: 'rspec', only: { refs: %w[branches] } } + end it { is_expected.to be_included } end context 'when using except' do - let(:attributes) { { name: 'rspec', except: { refs: ['branches'] } } } + let(:attributes) do + { name: 'rspec', except: { refs: %w[branches] } } + end + + it { is_expected.not_to be_included } + end + + context 'when using both only and except policies' do + let(:attributes) do + { + name: 'rspec', + only: { refs: %w[branches] }, + except: { refs: %w[branches] } + } + end it { is_expected.not_to be_included } end @@ -141,50 +272,78 @@ describe Gitlab::Ci::Pipeline::Seed::Build do context 'when keyword policy does not match' do context 'when using only' do - let(:attributes) { { name: 'rspec', only: { refs: ['tags'] } } } + let(:attributes) do + { name: 'rspec', only: { refs: %w[tags] } } + end it { is_expected.not_to be_included } end context 'when using except' do - let(:attributes) { { name: 'rspec', except: { refs: ['tags'] } } } + let(:attributes) do + { name: 'rspec', except: { refs: %w[tags] } } + end it { is_expected.to be_included } end + + context 'when using both only and except policies' do + let(:attributes) do + { + name: 'rspec', + only: { refs: %w[tags] }, + except: { refs: %w[tags] } + } + end + + it { is_expected.not_to be_included } + end end context 'with source-keyword policy' do using RSpec::Parameterized - let(:pipeline) { build(:ci_empty_pipeline, ref: 'deploy', tag: false, source: source) } + let(:pipeline) do + build(:ci_empty_pipeline, ref: 'deploy', tag: false, source: source) + end context 'matches' do where(:keyword, :source) do [ - %w(pushes push), - %w(web web), - %w(triggers trigger), - %w(schedules schedule), - %w(api api), - %w(external external) + %w[pushes push], + %w[web web], + %w[triggers trigger], + %w[schedules schedule], + %w[api api], + %w[external external] ] end with_them do context 'using an only policy' do - let(:attributes) { { name: 'rspec', only: { refs: [keyword] } } } + let(:attributes) do + { name: 'rspec', only: { refs: [keyword] } } + end it { is_expected.to be_included } end context 'using an except policy' do - let(:attributes) { { name: 'rspec', except: { refs: [keyword] } } } + let(:attributes) do + { name: 'rspec', except: { refs: [keyword] } } + end it { is_expected.not_to be_included } end context 'using both only and except policies' do - let(:attributes) { { name: 'rspec', only: { refs: [keyword] }, except: { refs: [keyword] } } } + let(:attributes) do + { + name: 'rspec', + only: { refs: [keyword] }, + except: { refs: [keyword] } + } + end it { is_expected.not_to be_included } end @@ -193,29 +352,39 @@ describe Gitlab::Ci::Pipeline::Seed::Build do context 'non-matches' do where(:keyword, :source) do - %w(web trigger schedule api external).map { |source| ['pushes', source] } + - %w(push trigger schedule api external).map { |source| ['web', source] } + - %w(push web schedule api external).map { |source| ['triggers', source] } + - %w(push web trigger api external).map { |source| ['schedules', source] } + - %w(push web trigger schedule external).map { |source| ['api', source] } + - %w(push web trigger schedule api).map { |source| ['external', source] } + %w[web trigger schedule api external].map { |source| ['pushes', source] } + + %w[push trigger schedule api external].map { |source| ['web', source] } + + %w[push web schedule api external].map { |source| ['triggers', source] } + + %w[push web trigger api external].map { |source| ['schedules', source] } + + %w[push web trigger schedule external].map { |source| ['api', source] } + + %w[push web trigger schedule api].map { |source| ['external', source] } end with_them do context 'using an only policy' do - let(:attributes) { { name: 'rspec', only: { refs: [keyword] } } } + let(:attributes) do + { name: 'rspec', only: { refs: [keyword] } } + end it { is_expected.not_to be_included } end context 'using an except policy' do - let(:attributes) { { name: 'rspec', except: { refs: [keyword] } } } + let(:attributes) do + { name: 'rspec', except: { refs: [keyword] } } + end it { is_expected.to be_included } end context 'using both only and except policies' do - let(:attributes) { { name: 'rspec', only: { refs: [keyword] }, except: { refs: [keyword] } } } + let(:attributes) do + { + name: 'rspec', + only: { refs: [keyword] }, + except: { refs: [keyword] } + } + end it { is_expected.not_to be_included } end @@ -239,12 +408,40 @@ describe Gitlab::Ci::Pipeline::Seed::Build do it { is_expected.not_to be_included } end + + context 'when using both only and except policies' do + let(:attributes) do + { + name: 'rspec', + only: { refs: ["branches@#{pipeline.project_full_path}"] }, + except: { refs: ["branches@#{pipeline.project_full_path}"] } + } + end + + it { is_expected.not_to be_included } + end + + context 'when using both only and except policies' do + let(:attributes) do + { + name: 'rspec', + only: { + refs: ["branches@#{pipeline.project_full_path}"] + }, + except: { + refs: ["branches@#{pipeline.project_full_path}"] + } + } + end + + it { is_expected.not_to be_included } + end end - context 'when repository path does not matches' do + context 'when repository path does not match' do context 'when using only' do let(:attributes) do - { name: 'rspec', only: { refs: ['branches@fork'] } } + { name: 'rspec', only: { refs: %w[branches@fork] } } end it { is_expected.not_to be_included } @@ -252,11 +449,316 @@ describe Gitlab::Ci::Pipeline::Seed::Build do context 'when using except' do let(:attributes) do - { name: 'rspec', except: { refs: ['branches@fork'] } } + { name: 'rspec', except: { refs: %w[branches@fork] } } end it { is_expected.to be_included } end + + context 'when using both only and except policies' do + let(:attributes) do + { + name: 'rspec', + only: { refs: %w[branches@fork] }, + except: { refs: %w[branches@fork] } + } + end + + it { is_expected.not_to be_included } + end end + + context 'using rules:' do + using RSpec::Parameterized + + let(:attributes) { { name: 'rspec', rules: rule_set } } + + context 'with a matching if: rule' do + context 'with an explicit `when: never`' do + where(:rule_set) do + [ + [[{ if: '$VARIABLE == null', when: 'never' }]], + [[{ if: '$VARIABLE == null', when: 'never' }, { if: '$VARIABLE == null', when: 'always' }]], + [[{ if: '$VARIABLE != "the wrong value"', when: 'never' }, { if: '$VARIABLE == null', when: 'always' }]] + ] + end + + with_them do + it { is_expected.not_to be_included } + + it 'correctly populates when:' do + expect(seed_build.attributes).to include(when: 'never') + end + end + end + + context 'with an explicit `when: always`' do + where(:rule_set) do + [ + [[{ if: '$VARIABLE == null', when: 'always' }]], + [[{ if: '$VARIABLE == null', when: 'always' }, { if: '$VARIABLE == null', when: 'never' }]], + [[{ if: '$VARIABLE != "the wrong value"', when: 'always' }, { if: '$VARIABLE == null', when: 'never' }]] + ] + end + + with_them do + it { is_expected.to be_included } + + it 'correctly populates when:' do + expect(seed_build.attributes).to include(when: 'always') + end + end + end + + context 'with an explicit `when: on_failure`' do + where(:rule_set) do + [ + [[{ if: '$CI_JOB_NAME == "rspec" && $VAR == null', when: 'on_failure' }]], + [[{ if: '$VARIABLE != null', when: 'delayed', start_in: '1 day' }, { if: '$CI_JOB_NAME == "rspec"', when: 'on_failure' }]], + [[{ if: '$VARIABLE == "the wrong value"', when: 'delayed', start_in: '1 day' }, { if: '$CI_BUILD_NAME == "rspec"', when: 'on_failure' }]] + ] + end + + with_them do + it { is_expected.to be_included } + + it 'correctly populates when:' do + expect(seed_build.attributes).to include(when: 'on_failure') + end + end + end + + context 'with an explicit `when: delayed`' do + where(:rule_set) do + [ + [[{ if: '$VARIABLE == null', when: 'delayed', start_in: '1 day' }]], + [[{ if: '$VARIABLE == null', when: 'delayed', start_in: '1 day' }, { if: '$VARIABLE == null', when: 'never' }]], + [[{ if: '$VARIABLE != "the wrong value"', when: 'delayed', start_in: '1 day' }, { if: '$VARIABLE == null', when: 'never' }]] + ] + end + + with_them do + it { is_expected.to be_included } + + it 'correctly populates when:' do + expect(seed_build.attributes).to include(when: 'delayed', start_in: '1 day') + end + end + end + + context 'without an explicit when: value' do + where(:rule_set) do + [ + [[{ if: '$VARIABLE == null' }]], + [[{ if: '$VARIABLE == null' }, { if: '$VARIABLE == null' }]], + [[{ if: '$VARIABLE != "the wrong value"' }, { if: '$VARIABLE == null' }]] + ] + end + + with_them do + it { is_expected.to be_included } + + it 'correctly populates when:' do + expect(seed_build.attributes).to include(when: 'on_success') + end + end + end + end + + context 'with a matching changes: rule' do + let(:pipeline) do + create(:ci_pipeline, project: project).tap do |pipeline| + stub_pipeline_modified_paths(pipeline, %w[app/models/ci/pipeline.rb spec/models/ci/pipeline_spec.rb .gitlab-ci.yml]) + end + end + + context 'with an explicit `when: never`' do + where(:rule_set) do + [ + [[{ changes: %w[*/**/*.rb], when: 'never' }, { changes: %w[*/**/*.rb], when: 'always' }]], + [[{ changes: %w[app/models/ci/pipeline.rb], when: 'never' }, { changes: %w[app/models/ci/pipeline.rb], when: 'always' }]], + [[{ changes: %w[spec/**/*.rb], when: 'never' }, { changes: %w[spec/**/*.rb], when: 'always' }]], + [[{ changes: %w[*.yml], when: 'never' }, { changes: %w[*.yml], when: 'always' }]], + [[{ changes: %w[.*.yml], when: 'never' }, { changes: %w[.*.yml], when: 'always' }]], + [[{ changes: %w[**/*], when: 'never' }, { changes: %w[**/*], when: 'always' }]], + [[{ changes: %w[*/**/*.rb *.yml], when: 'never' }, { changes: %w[*/**/*.rb *.yml], when: 'always' }]], + [[{ changes: %w[.*.yml **/*], when: 'never' }, { changes: %w[.*.yml **/*], when: 'always' }]] + ] + end + + with_them do + it { is_expected.not_to be_included } + + it 'correctly populates when:' do + expect(seed_build.attributes).to include(when: 'never') + end + end + end + + context 'with an explicit `when: always`' do + where(:rule_set) do + [ + [[{ changes: %w[*/**/*.rb], when: 'always' }, { changes: %w[*/**/*.rb], when: 'never' }]], + [[{ changes: %w[app/models/ci/pipeline.rb], when: 'always' }, { changes: %w[app/models/ci/pipeline.rb], when: 'never' }]], + [[{ changes: %w[spec/**/*.rb], when: 'always' }, { changes: %w[spec/**/*.rb], when: 'never' }]], + [[{ changes: %w[*.yml], when: 'always' }, { changes: %w[*.yml], when: 'never' }]], + [[{ changes: %w[.*.yml], when: 'always' }, { changes: %w[.*.yml], when: 'never' }]], + [[{ changes: %w[**/*], when: 'always' }, { changes: %w[**/*], when: 'never' }]], + [[{ changes: %w[*/**/*.rb *.yml], when: 'always' }, { changes: %w[*/**/*.rb *.yml], when: 'never' }]], + [[{ changes: %w[.*.yml **/*], when: 'always' }, { changes: %w[.*.yml **/*], when: 'never' }]] + ] + end + + with_them do + it { is_expected.to be_included } + + it 'correctly populates when:' do + expect(seed_build.attributes).to include(when: 'always') + end + end + end + + context 'without an explicit when: value' do + where(:rule_set) do + [ + [[{ changes: %w[*/**/*.rb] }]], + [[{ changes: %w[app/models/ci/pipeline.rb] }]], + [[{ changes: %w[spec/**/*.rb] }]], + [[{ changes: %w[*.yml] }]], + [[{ changes: %w[.*.yml] }]], + [[{ changes: %w[**/*] }]], + [[{ changes: %w[*/**/*.rb *.yml] }]], + [[{ changes: %w[.*.yml **/*] }]] + ] + end + + with_them do + it { is_expected.to be_included } + + it 'correctly populates when:' do + expect(seed_build.attributes).to include(when: 'on_success') + end + end + end + end + + context 'with no matching rule' do + where(:rule_set) do + [ + [[{ if: '$VARIABLE != null', when: 'never' }]], + [[{ if: '$VARIABLE != null', when: 'never' }, { if: '$VARIABLE != null', when: 'always' }]], + [[{ if: '$VARIABLE == "the wrong value"', when: 'never' }, { if: '$VARIABLE != null', when: 'always' }]], + [[{ if: '$VARIABLE != null', when: 'always' }]], + [[{ if: '$VARIABLE != null', when: 'always' }, { if: '$VARIABLE != null', when: 'never' }]], + [[{ if: '$VARIABLE == "the wrong value"', when: 'always' }, { if: '$VARIABLE != null', when: 'never' }]], + [[{ if: '$VARIABLE != null' }]], + [[{ if: '$VARIABLE != null' }, { if: '$VARIABLE != null' }]], + [[{ if: '$VARIABLE == "the wrong value"' }, { if: '$VARIABLE != null' }]] + ] + end + + with_them do + it { is_expected.not_to be_included } + + it 'correctly populates when:' do + expect(seed_build.attributes).to include(when: 'never') + end + end + end + + context 'with no rules' do + let(:rule_set) { [] } + + it { is_expected.not_to be_included } + + it 'correctly populates when:' do + expect(seed_build.attributes).to include(when: 'never') + end + end + end + end + + describe 'applying needs: dependency' do + subject { seed_build } + + let(:needs_count) { 1 } + + let(:needs_attributes) do + Array.new(needs_count, name: 'build') + end + + let(:attributes) do + { + name: 'rspec', + needs_attributes: needs_attributes + } + end + + context 'when build job is not present in prior stages' do + it "is included" do + is_expected.to be_included + end + + it "returns an error" do + expect(subject.errors).to contain_exactly( + "rspec: needs 'build'") + end + end + + context 'when build job is part of prior stages' do + let(:stage_attributes) do + { + name: 'build', + index: 0, + builds: [{ name: 'build' }] + } + end + + let(:stage_seed) do + Gitlab::Ci::Pipeline::Seed::Stage.new(pipeline, stage_attributes, []) + end + + let(:previous_stages) { [stage_seed] } + + it "is included" do + is_expected.to be_included + end + + it "does not have errors" do + expect(subject.errors).to be_empty + end + end + + context 'when lower limit of needs is reached' do + before do + stub_feature_flags(ci_dag_limit_needs: true) + end + + let(:needs_count) { described_class::LOW_NEEDS_LIMIT + 1 } + + it "returns an error" do + expect(subject.errors).to contain_exactly( + "rspec: one job can only need 5 others, but you have listed 6. See needs keyword documentation for more details") + end + end + + context 'when upper limit of needs is reached' do + before do + stub_feature_flags(ci_dag_limit_needs: false) + end + + let(:needs_count) { described_class::HARD_NEEDS_LIMIT + 1 } + + it "returns an error" do + expect(subject.errors).to contain_exactly( + "rspec: one job can only need 50 others, but you have listed 51. See needs keyword documentation for more details") + end + end + end + + describe '#scoped_variables_hash' do + subject { seed_build.scoped_variables_hash } + + it { is_expected.to eq(seed_build.to_resource.scoped_variables_hash) } end end diff --git a/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb index 493ca3cd7b5..a13335f63d5 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb @@ -1,8 +1,11 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Seed::Stage do let(:project) { create(:project, :repository) } let(:pipeline) { create(:ci_empty_pipeline, project: project) } + let(:previous_stages) { [] } let(:attributes) do { name: 'test', @@ -13,7 +16,7 @@ describe Gitlab::Ci::Pipeline::Seed::Stage do end subject do - described_class.new(pipeline, attributes) + described_class.new(pipeline, attributes, previous_stages) end describe '#size' do @@ -107,6 +110,27 @@ describe Gitlab::Ci::Pipeline::Seed::Stage do end end + describe '#seeds_names' do + it 'returns all job names' do + expect(subject.seeds_names).to contain_exactly( + 'rspec', 'spinach') + end + + it 'returns a set' do + expect(subject.seeds_names).to be_a(Set) + end + end + + describe '#seeds_errors' do + it 'returns all errors from seeds' do + expect(subject.seeds.first) + .to receive(:errors) { ["build error"] } + + expect(subject.errors).to contain_exactly( + "build error") + end + end + describe '#to_resource' do it 'builds a valid stage object with all builds' do subject.to_resource.save! diff --git a/spec/lib/gitlab/ci/reports/test_case_spec.rb b/spec/lib/gitlab/ci/reports/test_case_spec.rb index 6932f79f0ce..20c489ee94c 100644 --- a/spec/lib/gitlab/ci/reports/test_case_spec.rb +++ b/spec/lib/gitlab/ci/reports/test_case_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Reports::TestCase do diff --git a/spec/lib/gitlab/ci/reports/test_reports_comparer_spec.rb b/spec/lib/gitlab/ci/reports/test_reports_comparer_spec.rb index 36582204cc1..48eef0643b2 100644 --- a/spec/lib/gitlab/ci/reports/test_reports_comparer_spec.rb +++ b/spec/lib/gitlab/ci/reports/test_reports_comparer_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Reports::TestReportsComparer do diff --git a/spec/lib/gitlab/ci/reports/test_reports_spec.rb b/spec/lib/gitlab/ci/reports/test_reports_spec.rb index 74ff134b239..0b5d05bada3 100644 --- a/spec/lib/gitlab/ci/reports/test_reports_spec.rb +++ b/spec/lib/gitlab/ci/reports/test_reports_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Reports::TestReports do diff --git a/spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb b/spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb index 579b3e6fd24..cf4690bb334 100644 --- a/spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb +++ b/spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Reports::TestSuiteComparer do diff --git a/spec/lib/gitlab/ci/reports/test_suite_spec.rb b/spec/lib/gitlab/ci/reports/test_suite_spec.rb index cd34dbaf62f..8646db43bc8 100644 --- a/spec/lib/gitlab/ci/reports/test_suite_spec.rb +++ b/spec/lib/gitlab/ci/reports/test_suite_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Reports::TestSuite do diff --git a/spec/lib/gitlab/ci/status/build/action_spec.rb b/spec/lib/gitlab/ci/status/build/action_spec.rb index bdec582b57b..3aae7e18d6d 100644 --- a/spec/lib/gitlab/ci/status/build/action_spec.rb +++ b/spec/lib/gitlab/ci/status/build/action_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Build::Action do diff --git a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb index 78d6fa65b5a..3841dae91c7 100644 --- a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb +++ b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Build::Cancelable do diff --git a/spec/lib/gitlab/ci/status/build/canceled_spec.rb b/spec/lib/gitlab/ci/status/build/canceled_spec.rb index c6b5cc68770..4b43c78f1a7 100644 --- a/spec/lib/gitlab/ci/status/build/canceled_spec.rb +++ b/spec/lib/gitlab/ci/status/build/canceled_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Build::Canceled do diff --git a/spec/lib/gitlab/ci/status/build/common_spec.rb b/spec/lib/gitlab/ci/status/build/common_spec.rb index ca3c66f0152..5114540708f 100644 --- a/spec/lib/gitlab/ci/status/build/common_spec.rb +++ b/spec/lib/gitlab/ci/status/build/common_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Build::Common do diff --git a/spec/lib/gitlab/ci/status/build/created_spec.rb b/spec/lib/gitlab/ci/status/build/created_spec.rb index 8bdfe6ef7a2..6e3aa442810 100644 --- a/spec/lib/gitlab/ci/status/build/created_spec.rb +++ b/spec/lib/gitlab/ci/status/build/created_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Build::Created do diff --git a/spec/lib/gitlab/ci/status/build/erased_spec.rb b/spec/lib/gitlab/ci/status/build/erased_spec.rb index 0acd271e375..af9c296da0c 100644 --- a/spec/lib/gitlab/ci/status/build/erased_spec.rb +++ b/spec/lib/gitlab/ci/status/build/erased_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Build::Erased do diff --git a/spec/lib/gitlab/ci/status/build/factory_spec.rb b/spec/lib/gitlab/ci/status/build/factory_spec.rb index b6231510b91..de489fa4664 100644 --- a/spec/lib/gitlab/ci/status/build/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/build/factory_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Build::Factory do diff --git a/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb b/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb index 2a5915d75d0..01500689619 100644 --- a/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb +++ b/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Build::FailedAllowed do diff --git a/spec/lib/gitlab/ci/status/build/failed_spec.rb b/spec/lib/gitlab/ci/status/build/failed_spec.rb index e424270f7c5..78f5214ca81 100644 --- a/spec/lib/gitlab/ci/status/build/failed_spec.rb +++ b/spec/lib/gitlab/ci/status/build/failed_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Build::Failed do diff --git a/spec/lib/gitlab/ci/status/build/failed_unmet_prerequisites_spec.rb b/spec/lib/gitlab/ci/status/build/failed_unmet_prerequisites_spec.rb index a4854bdc6b9..19a3c82eac7 100644 --- a/spec/lib/gitlab/ci/status/build/failed_unmet_prerequisites_spec.rb +++ b/spec/lib/gitlab/ci/status/build/failed_unmet_prerequisites_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' RSpec.describe Gitlab::Ci::Status::Build::FailedUnmetPrerequisites do diff --git a/spec/lib/gitlab/ci/status/build/manual_spec.rb b/spec/lib/gitlab/ci/status/build/manual_spec.rb index 6386296f992..bffe2c10d12 100644 --- a/spec/lib/gitlab/ci/status/build/manual_spec.rb +++ b/spec/lib/gitlab/ci/status/build/manual_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Build::Manual do diff --git a/spec/lib/gitlab/ci/status/build/pending_spec.rb b/spec/lib/gitlab/ci/status/build/pending_spec.rb index 4cf70828e53..64d57954c15 100644 --- a/spec/lib/gitlab/ci/status/build/pending_spec.rb +++ b/spec/lib/gitlab/ci/status/build/pending_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Build::Pending do diff --git a/spec/lib/gitlab/ci/status/build/play_spec.rb b/spec/lib/gitlab/ci/status/build/play_spec.rb index 02f8c4c114b..bb12a900b55 100644 --- a/spec/lib/gitlab/ci/status/build/play_spec.rb +++ b/spec/lib/gitlab/ci/status/build/play_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Build::Play do diff --git a/spec/lib/gitlab/ci/status/build/retried_spec.rb b/spec/lib/gitlab/ci/status/build/retried_spec.rb index 76c2fb01e3f..fce497d40a1 100644 --- a/spec/lib/gitlab/ci/status/build/retried_spec.rb +++ b/spec/lib/gitlab/ci/status/build/retried_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Build::Retried do diff --git a/spec/lib/gitlab/ci/status/build/retryable_spec.rb b/spec/lib/gitlab/ci/status/build/retryable_spec.rb index 84d98588f2d..5b0ae315927 100644 --- a/spec/lib/gitlab/ci/status/build/retryable_spec.rb +++ b/spec/lib/gitlab/ci/status/build/retryable_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Build::Retryable do diff --git a/spec/lib/gitlab/ci/status/build/scheduled_spec.rb b/spec/lib/gitlab/ci/status/build/scheduled_spec.rb index 68b87fea75d..8f87da10815 100644 --- a/spec/lib/gitlab/ci/status/build/scheduled_spec.rb +++ b/spec/lib/gitlab/ci/status/build/scheduled_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Build::Scheduled do diff --git a/spec/lib/gitlab/ci/status/build/skipped_spec.rb b/spec/lib/gitlab/ci/status/build/skipped_spec.rb index 46f6933025a..7ce5142da78 100644 --- a/spec/lib/gitlab/ci/status/build/skipped_spec.rb +++ b/spec/lib/gitlab/ci/status/build/skipped_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Build::Skipped do diff --git a/spec/lib/gitlab/ci/status/build/stop_spec.rb b/spec/lib/gitlab/ci/status/build/stop_spec.rb index 5b7534c96c1..d3e98400a53 100644 --- a/spec/lib/gitlab/ci/status/build/stop_spec.rb +++ b/spec/lib/gitlab/ci/status/build/stop_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Build::Stop do diff --git a/spec/lib/gitlab/ci/status/build/unschedule_spec.rb b/spec/lib/gitlab/ci/status/build/unschedule_spec.rb index ed046d66ca5..c18fc3252b4 100644 --- a/spec/lib/gitlab/ci/status/build/unschedule_spec.rb +++ b/spec/lib/gitlab/ci/status/build/unschedule_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Build::Unschedule do diff --git a/spec/lib/gitlab/ci/status/canceled_spec.rb b/spec/lib/gitlab/ci/status/canceled_spec.rb index dc74d7e28c5..6cfcea4fdde 100644 --- a/spec/lib/gitlab/ci/status/canceled_spec.rb +++ b/spec/lib/gitlab/ci/status/canceled_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Canceled do diff --git a/spec/lib/gitlab/ci/status/created_spec.rb b/spec/lib/gitlab/ci/status/created_spec.rb index ce4333f2aca..aeb41e9cfc3 100644 --- a/spec/lib/gitlab/ci/status/created_spec.rb +++ b/spec/lib/gitlab/ci/status/created_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Created do diff --git a/spec/lib/gitlab/ci/status/extended_spec.rb b/spec/lib/gitlab/ci/status/extended_spec.rb index 6eacb07078b..8accfc4a2f9 100644 --- a/spec/lib/gitlab/ci/status/extended_spec.rb +++ b/spec/lib/gitlab/ci/status/extended_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Extended do diff --git a/spec/lib/gitlab/ci/status/external/common_spec.rb b/spec/lib/gitlab/ci/status/external/common_spec.rb index 0d02c371a92..983522fa2d6 100644 --- a/spec/lib/gitlab/ci/status/external/common_spec.rb +++ b/spec/lib/gitlab/ci/status/external/common_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::External::Common do diff --git a/spec/lib/gitlab/ci/status/external/factory_spec.rb b/spec/lib/gitlab/ci/status/external/factory_spec.rb index 529d02a3e39..3b90fb60cca 100644 --- a/spec/lib/gitlab/ci/status/external/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/external/factory_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::External::Factory do diff --git a/spec/lib/gitlab/ci/status/factory_spec.rb b/spec/lib/gitlab/ci/status/factory_spec.rb index bbf9c7c83a3..b51c0bec47e 100644 --- a/spec/lib/gitlab/ci/status/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/factory_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Factory do diff --git a/spec/lib/gitlab/ci/status/failed_spec.rb b/spec/lib/gitlab/ci/status/failed_spec.rb index a4a92117c7f..5c7393fc8cf 100644 --- a/spec/lib/gitlab/ci/status/failed_spec.rb +++ b/spec/lib/gitlab/ci/status/failed_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Failed do diff --git a/spec/lib/gitlab/ci/status/group/common_spec.rb b/spec/lib/gitlab/ci/status/group/common_spec.rb index c0ca05881f5..35fff30ea9d 100644 --- a/spec/lib/gitlab/ci/status/group/common_spec.rb +++ b/spec/lib/gitlab/ci/status/group/common_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Group::Common do diff --git a/spec/lib/gitlab/ci/status/group/factory_spec.rb b/spec/lib/gitlab/ci/status/group/factory_spec.rb index 0cd83123938..be76a1d5a65 100644 --- a/spec/lib/gitlab/ci/status/group/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/group/factory_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Group::Factory do diff --git a/spec/lib/gitlab/ci/status/manual_spec.rb b/spec/lib/gitlab/ci/status/manual_spec.rb index 0463f2e1aff..0839452ec22 100644 --- a/spec/lib/gitlab/ci/status/manual_spec.rb +++ b/spec/lib/gitlab/ci/status/manual_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Manual do diff --git a/spec/lib/gitlab/ci/status/pending_spec.rb b/spec/lib/gitlab/ci/status/pending_spec.rb index 0e25358dd8a..5f830e5bb56 100644 --- a/spec/lib/gitlab/ci/status/pending_spec.rb +++ b/spec/lib/gitlab/ci/status/pending_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Pending do diff --git a/spec/lib/gitlab/ci/status/pipeline/blocked_spec.rb b/spec/lib/gitlab/ci/status/pipeline/blocked_spec.rb index 1a2b952d374..876ba712d05 100644 --- a/spec/lib/gitlab/ci/status/pipeline/blocked_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/blocked_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Pipeline::Blocked do diff --git a/spec/lib/gitlab/ci/status/pipeline/common_spec.rb b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb index 57df8325635..d3251d138b8 100644 --- a/spec/lib/gitlab/ci/status/pipeline/common_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Pipeline::Common do diff --git a/spec/lib/gitlab/ci/status/pipeline/delayed_spec.rb b/spec/lib/gitlab/ci/status/pipeline/delayed_spec.rb index f89712d2b03..90b797965b3 100644 --- a/spec/lib/gitlab/ci/status/pipeline/delayed_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/delayed_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Pipeline::Delayed do diff --git a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb index 466087a0e31..8a36cd1b658 100644 --- a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Pipeline::Factory do diff --git a/spec/lib/gitlab/ci/status/running_spec.rb b/spec/lib/gitlab/ci/status/running_spec.rb index 9c9d431bb5d..75ff58c5c98 100644 --- a/spec/lib/gitlab/ci/status/running_spec.rb +++ b/spec/lib/gitlab/ci/status/running_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Running do diff --git a/spec/lib/gitlab/ci/status/scheduled_spec.rb b/spec/lib/gitlab/ci/status/scheduled_spec.rb index b8ca3caa1f7..a0374e1a87b 100644 --- a/spec/lib/gitlab/ci/status/scheduled_spec.rb +++ b/spec/lib/gitlab/ci/status/scheduled_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Scheduled do diff --git a/spec/lib/gitlab/ci/status/skipped_spec.rb b/spec/lib/gitlab/ci/status/skipped_spec.rb index 63694ca0ea6..7f68d4a2fa9 100644 --- a/spec/lib/gitlab/ci/status/skipped_spec.rb +++ b/spec/lib/gitlab/ci/status/skipped_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Skipped do diff --git a/spec/lib/gitlab/ci/status/stage/common_spec.rb b/spec/lib/gitlab/ci/status/stage/common_spec.rb index bb2d0a2c75c..26ff0e901fd 100644 --- a/spec/lib/gitlab/ci/status/stage/common_spec.rb +++ b/spec/lib/gitlab/ci/status/stage/common_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Stage::Common do diff --git a/spec/lib/gitlab/ci/status/stage/factory_spec.rb b/spec/lib/gitlab/ci/status/stage/factory_spec.rb index 4b299170c87..8f5b1ff62a5 100644 --- a/spec/lib/gitlab/ci/status/stage/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/stage/factory_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Stage::Factory do diff --git a/spec/lib/gitlab/ci/status/success_spec.rb b/spec/lib/gitlab/ci/status/success_spec.rb index 2f67df71c4f..d4b3a9f12cc 100644 --- a/spec/lib/gitlab/ci/status/success_spec.rb +++ b/spec/lib/gitlab/ci/status/success_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::Success do diff --git a/spec/lib/gitlab/ci/status/success_warning_spec.rb b/spec/lib/gitlab/ci/status/success_warning_spec.rb index 9493b1d89f2..af952011e21 100644 --- a/spec/lib/gitlab/ci/status/success_warning_spec.rb +++ b/spec/lib/gitlab/ci/status/success_warning_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Status::SuccessWarning do diff --git a/spec/lib/gitlab/ci/trace/chunked_io_spec.rb b/spec/lib/gitlab/ci/trace/chunked_io_spec.rb index 546a9e7d0cc..e0077a5280a 100644 --- a/spec/lib/gitlab/ci/trace/chunked_io_spec.rb +++ b/spec/lib/gitlab/ci/trace/chunked_io_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do @@ -312,7 +314,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do end context 'when utf-8 is being used' do - let(:sample_trace_raw) { sample_trace_raw_utf8.force_encoding(Encoding::BINARY) } + let(:sample_trace_raw) { sample_trace_raw_utf8.dup.force_encoding(Encoding::BINARY) } let(:sample_trace_raw_utf8) { "😺\n😺\n😺\n😺" } before do @@ -442,5 +444,15 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do expect(Ci::BuildTraceChunk.where(build: build).count).to eq(0) end + + context 'when the job does not have archived trace' do + it 'leaves a message in sidekiq log' do + expect(Sidekiq.logger).to receive(:warn).with( + message: 'The job does not have archived trace but going to be destroyed.', + job_id: build.id).and_call_original + + subject + end + end end end diff --git a/spec/lib/gitlab/ci/trace/section_parser_spec.rb b/spec/lib/gitlab/ci/trace/section_parser_spec.rb index ca53ff87c6f..5e2efe083be 100644 --- a/spec/lib/gitlab/ci/trace/section_parser_spec.rb +++ b/spec/lib/gitlab/ci/trace/section_parser_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Trace::SectionParser do diff --git a/spec/lib/gitlab/ci/trace/stream_spec.rb b/spec/lib/gitlab/ci/trace/stream_spec.rb index 35250632e86..af519f4bae6 100644 --- a/spec/lib/gitlab/ci/trace/stream_spec.rb +++ b/spec/lib/gitlab/ci/trace/stream_spec.rb @@ -65,9 +65,9 @@ describe Gitlab::Ci::Trace::Stream, :clean_gitlab_redis_cache do result = stream.html expect(result).to eq( - "<span class=\"\">ヾ(´༎ຶД༎ຶ`)ノ<br/><span class=\"\"></span></span>"\ - "<span class=\"term-fg-green\">許功蓋</span><span class=\"\"><br/>"\ - "<span class=\"\"></span></span>") + "<span>ヾ(´༎ຶД༎ຶ`)ノ<br/></span>"\ + "<span class=\"term-fg-green\">許功蓋</span>"\ + "<span><br/></span>") expect(result.encoding).to eq(Encoding.default_external) end end @@ -253,7 +253,7 @@ describe Gitlab::Ci::Trace::Stream, :clean_gitlab_redis_cache do it 'returns html content with state' do result = stream.html_with_state - expect(result.html).to eq("<span class=\"\">1234</span>") + expect(result.html).to eq("<span>1234</span>") end context 'follow-up state' do @@ -269,7 +269,7 @@ describe Gitlab::Ci::Trace::Stream, :clean_gitlab_redis_cache do result = stream.html_with_state(last_result.state) expect(result.append).to be_truthy - expect(result.html).to eq("<span class=\"\">5678</span>") + expect(result.html).to eq("<span>5678</span>") end end end @@ -305,13 +305,11 @@ describe Gitlab::Ci::Trace::Stream, :clean_gitlab_redis_cache do describe '#html' do shared_examples_for 'htmls' do it "returns html" do - expect(stream.html).to eq( - "<span class=\"\">12<br/><span class=\"\">34<br/>"\ - "<span class=\"\">56</span></span></span>") + expect(stream.html).to eq("<span>12<br/>34<br/>56</span>") end it "returns html for last line only" do - expect(stream.html(last_lines: 1)).to eq("<span class=\"\">56</span>") + expect(stream.html(last_lines: 1)).to eq("<span>56</span>") end end diff --git a/spec/lib/gitlab/ci/trace_spec.rb b/spec/lib/gitlab/ci/trace_spec.rb index d6510649dba..f7bc5686b68 100644 --- a/spec/lib/gitlab/ci/trace_spec.rb +++ b/spec/lib/gitlab/ci/trace_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Trace, :clean_gitlab_redis_shared_state do diff --git a/spec/lib/gitlab/ci/variables/collection/item_spec.rb b/spec/lib/gitlab/ci/variables/collection/item_spec.rb index 613814df23f..1bdca753cd3 100644 --- a/spec/lib/gitlab/ci/variables/collection/item_spec.rb +++ b/spec/lib/gitlab/ci/variables/collection/item_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Variables::Collection::Item do diff --git a/spec/lib/gitlab/ci/variables/collection_spec.rb b/spec/lib/gitlab/ci/variables/collection_spec.rb index 8e732d44d5d..59b9f7d4fb9 100644 --- a/spec/lib/gitlab/ci/variables/collection_spec.rb +++ b/spec/lib/gitlab/ci/variables/collection_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Variables::Collection do diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb index 789d6813d5b..91c559dcd9b 100644 --- a/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' module Gitlab @@ -123,9 +125,11 @@ module Gitlab describe 'delayed job entry' do context 'when delayed is defined' do let(:config) do - YAML.dump(rspec: { script: 'rollout 10%', - when: 'delayed', - start_in: '1 day' }) + YAML.dump(rspec: { + script: 'rollout 10%', + when: 'delayed', + start_in: '1 day' + }) end it 'has the attributes' do @@ -724,12 +728,12 @@ module Gitlab end end - describe "When" do - %w(on_success on_failure always).each do |when_state| - it "returns #{when_state} when defined" do + describe 'when:' do + (Gitlab::Ci::Config::Entry::Job::ALLOWED_WHEN - %w[delayed]).each do |when_state| + it "#{when_state} creates one build and sets when:" do config = YAML.dump({ - rspec: { script: "rspec", when: when_state } - }) + rspec: { script: 'rspec', when: when_state } + }) config_processor = Gitlab::Ci::YamlProcessor.new(config) builds = config_processor.stage_builds_attributes("test") @@ -738,6 +742,35 @@ module Gitlab expect(builds.first[:when]).to eq(when_state) end end + + context 'delayed' do + context 'with start_in' do + it 'creates one build and sets when:' do + config = YAML.dump({ + rspec: { script: 'rspec', when: 'delayed', start_in: '1 hour' } + }) + + config_processor = Gitlab::Ci::YamlProcessor.new(config) + builds = config_processor.stage_builds_attributes("test") + + expect(builds.size).to eq(1) + expect(builds.first[:when]).to eq('delayed') + expect(builds.first[:options][:start_in]).to eq('1 hour') + end + end + + context 'without start_in' do + it 'raises an error' do + config = YAML.dump({ + rspec: { script: 'rspec', when: 'delayed' } + }) + + expect do + Gitlab::Ci::YamlProcessor.new(config) + end.to raise_error(YamlProcessor::ValidationError, /start in should be a duration/) + end + end + end end describe 'Parallel' do @@ -1083,6 +1116,175 @@ module Gitlab it { expect { subject }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'test1 job: dependency deploy is not defined in prior stages') } end + + context 'when a job depends on another job that references a not-yet defined stage' do + let(:config) do + { + "stages" => [ + "version" + ], + "version" => { + "stage" => "version", + "dependencies" => ["release:components:versioning"], + "script" => ["./versioning/versioning"] + }, + ".release_go" => { + "stage" => "build", + "script" => ["cd versioning"] + }, + "release:components:versioning" => { + "stage" => "build", + "script" => ["cd versioning"] + } + } + end + + it { expect { subject }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, /is not defined in prior stages/) } + end + end + + describe "Needs" do + let(:needs) { } + let(:dependencies) { } + + let(:config) do + { + build1: { stage: 'build', script: 'test' }, + build2: { stage: 'build', script: 'test' }, + test1: { stage: 'test', script: 'test', needs: needs, dependencies: dependencies }, + test2: { stage: 'test', script: 'test' }, + deploy: { stage: 'test', script: 'test' } + } + end + + subject { Gitlab::Ci::YamlProcessor.new(YAML.dump(config)) } + + context 'no needs' do + it { expect { subject }.not_to raise_error } + end + + context 'needs two builds' do + let(:needs) { %w(build1 build2) } + + it "does create jobs with valid specification" do + expect(subject.builds.size).to eq(5) + expect(subject.builds[0]).to eq( + stage: "build", + stage_idx: 0, + name: "build1", + options: { + script: ["test"] + }, + when: "on_success", + allow_failure: false, + yaml_variables: [] + ) + expect(subject.builds[2]).to eq( + stage: "test", + stage_idx: 1, + name: "test1", + options: { + script: ["test"], + # This does not make sense, there is a follow-up: + # https://gitlab.com/gitlab-org/gitlab-ce/issues/65569 + bridge_needs: %w[build1 build2] + }, + needs_attributes: [ + { name: "build1" }, + { name: "build2" } + ], + when: "on_success", + allow_failure: false, + yaml_variables: [] + ) + end + end + + context 'needs two builds defined as symbols' do + let(:needs) { [:build1, :build2] } + + it { expect { subject }.not_to raise_error } + end + + context 'undefined need' do + let(:needs) { ['undefined'] } + + it { expect { subject }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'test1 job: undefined need: undefined') } + end + + context 'needs to deploy' do + let(:needs) { ['deploy'] } + + it { expect { subject }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'test1 job: need deploy is not defined in prior stages') } + end + + context 'needs and dependencies that are mismatching' do + let(:needs) { %w(build1) } + let(:dependencies) { %w(build2) } + + it { expect { subject }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'jobs:test1 dependencies the build2 should be part of needs') } + end + end + + describe 'rules' do + subject { Gitlab::Ci::YamlProcessor.new(YAML.dump(config)) } + + let(:config) do + { + var_default: { stage: 'build', script: 'test', rules: [{ if: '$VAR == null' }] }, + var_when: { stage: 'build', script: 'test', rules: [{ if: '$VAR == null', when: 'always' }] }, + var_and_changes: { stage: 'build', script: 'test', rules: [{ if: '$VAR == null', changes: %w[README], when: 'always' }] }, + changes_not_var: { stage: 'test', script: 'test', rules: [{ if: '$VAR != null', changes: %w[README] }] }, + var_not_changes: { stage: 'test', script: 'test', rules: [{ if: '$VAR == null', changes: %w[other/file.rb], when: 'always' }] }, + nothing: { stage: 'test', script: 'test', rules: [{ when: 'manual' }] }, + var_never: { stage: 'deploy', script: 'test', rules: [{ if: '$VAR == null', when: 'never' }] }, + var_delayed: { stage: 'deploy', script: 'test', rules: [{ if: '$VAR == null', when: 'delayed', start_in: '3 hours' }] }, + two_rules: { stage: 'deploy', script: 'test', rules: [{ if: '$VAR == null', when: 'on_success' }, { changes: %w[README], when: 'manual' }] } + } + end + + it 'raises no exceptions' do + expect { subject }.not_to raise_error + end + + it 'returns all jobs regardless of their inclusion' do + expect(subject.builds.count).to eq(config.keys.count) + end + + context 'used with job-level when' do + let(:config) do + { + var_default: { + stage: 'build', + script: 'test', + when: 'always', + rules: [{ if: '$VAR == null' }] + } + } + end + + it 'raises a ValidationError' do + expect { subject }.to raise_error(YamlProcessor::ValidationError, /may not be used with `rules`: when/) + end + end + + context 'used with job-level when:delayed' do + let(:config) do + { + var_default: { + stage: 'build', + script: 'test', + when: 'delayed', + start_in: '10 minutes', + rules: [{ if: '$VAR == null' }] + } + } + end + + it 'raises a ValidationError' do + expect { subject }.to raise_error(YamlProcessor::ValidationError, /may not be used with `rules`: when, start_in/) + end + end end describe "Hidden jobs" do @@ -1403,7 +1605,7 @@ module Gitlab config = YAML.dump({ rspec: { script: "test", when: 1 } }) expect do Gitlab::Ci::YamlProcessor.new(config) - end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec when should be on_success, on_failure, always, manual or delayed") + end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec when should be one of: #{Gitlab::Ci::Config::Entry::Job::ALLOWED_WHEN.join(', ')}") end it "returns errors if job artifacts:name is not an a string" do |