From 7e9c479f7de77702622631cff2628a9c8dcbc627 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 19 Nov 2020 08:27:35 +0000 Subject: Add latest changes from gitlab-org/gitlab@13-6-stable-ee --- .../ci/build/rules/rule/clause/changes_spec.rb | 42 ++++++ spec/lib/gitlab/ci/charts_spec.rb | 20 ++- .../gitlab/ci/config/entry/product/matrix_spec.rb | 128 ++++-------------- .../ci/config/entry/product/variables_spec.rb | 71 ++-------- spec/lib/gitlab/ci/config/external/mapper_spec.rb | 56 ++++++++ .../gitlab/ci/config/external/processor_spec.rb | 77 +++++++++++ spec/lib/gitlab/ci/config_spec.rb | 78 ++++++++--- spec/lib/gitlab/ci/jwt_spec.rb | 63 ++++++--- .../chain/cancel_pending_pipelines_spec.rb | 123 +++++++++++++++++ spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb | 19 ++- .../gitlab/ci/pipeline/chain/seed_block_spec.rb | 78 +++++++++++ spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb | 66 ++++++++-- .../gitlab/ci/pipeline/seed/environment_spec.rb | 63 ++++++++- spec/lib/gitlab/ci/reports/test_case_spec.rb | 23 +++- .../gitlab/ci/reports/test_failure_history_spec.rb | 45 +++++++ spec/lib/gitlab/ci/reports/test_reports_spec.rb | 6 +- .../gitlab/ci/reports/test_suite_comparer_spec.rb | 146 +++++++++++++++++++-- spec/lib/gitlab/ci/reports/test_suite_spec.rb | 4 +- spec/lib/gitlab/ci/runner_instructions_spec.rb | 7 + .../AWS/deploy_ecs_gitlab_ci_yaml_spec.rb | 59 +++++++++ .../templates/auto_devops_gitlab_ci_yaml_spec.rb | 13 +- .../gitlab/ci/variables/collection/item_spec.rb | 4 +- 22 files changed, 940 insertions(+), 251 deletions(-) create mode 100644 spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb create mode 100644 spec/lib/gitlab/ci/pipeline/chain/seed_block_spec.rb create mode 100644 spec/lib/gitlab/ci/reports/test_failure_history_spec.rb create mode 100644 spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb (limited to 'spec/lib/gitlab/ci') diff --git a/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb b/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb index cf52f601006..d20ea6c9202 100644 --- a/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb +++ b/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb @@ -13,5 +13,47 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do subject { described_class.new(globs).satisfied_by?(pipeline, nil) } end + + context 'when using variable expansion' do + let(:pipeline) { build(:ci_pipeline) } + let(:modified_paths) { ['helm/test.txt'] } + let(:globs) { ['$HELM_DIR/**/*'] } + let(:context) { double('context') } + + subject { described_class.new(globs).satisfied_by?(pipeline, context) } + + before do + allow(pipeline).to receive(:modified_paths).and_return(modified_paths) + end + + context 'when context is nil' do + let(:context) {} + + it { is_expected.to be_falsey } + end + + context 'when context has the specified variables' do + let(:variables) do + [{ key: "HELM_DIR", value: "helm", public: true }] + end + + before do + allow(context).to receive(:variables).and_return(variables) + end + + it { is_expected.to be_truthy } + end + + context 'when variable expansion does not match' do + let(:globs) { ['path/with/$in/it/*'] } + let(:modified_paths) { ['path/with/$in/it/file.txt'] } + + before do + allow(context).to receive(:variables).and_return([]) + end + + it { is_expected.to be_truthy } + end + end end end diff --git a/spec/lib/gitlab/ci/charts_spec.rb b/spec/lib/gitlab/ci/charts_spec.rb index e00e5ed3920..cfc2019a89b 100644 --- a/spec/lib/gitlab/ci/charts_spec.rb +++ b/spec/lib/gitlab/ci/charts_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Charts do - context "yearchart" do + context 'yearchart' do let(:project) { create(:project) } let(:chart) { Gitlab::Ci::Charts::YearChart.new(project) } @@ -16,9 +16,13 @@ RSpec.describe Gitlab::Ci::Charts do it 'starts at the beginning of the current year' do expect(chart.from).to eq(chart.to.years_ago(1).beginning_of_month.beginning_of_day) end + + it 'uses %B %Y as labels format' do + expect(chart.labels).to include(chart.from.strftime('%B %Y')) + end end - context "monthchart" do + context 'monthchart' do let(:project) { create(:project) } let(:chart) { Gitlab::Ci::Charts::MonthChart.new(project) } @@ -31,9 +35,13 @@ RSpec.describe Gitlab::Ci::Charts do it 'starts one month ago' do expect(chart.from).to eq(1.month.ago.beginning_of_day) end + + it 'uses %d %B as labels format' do + expect(chart.labels).to include(chart.from.strftime('%d %B')) + end end - context "weekchart" do + context 'weekchart' do let(:project) { create(:project) } let(:chart) { Gitlab::Ci::Charts::WeekChart.new(project) } @@ -46,9 +54,13 @@ RSpec.describe Gitlab::Ci::Charts do it 'starts one week ago' do expect(chart.from).to eq(1.week.ago.beginning_of_day) end + + it 'uses %d %B as labels format' do + expect(chart.labels).to include(chart.from.strftime('%d %B')) + end end - context "pipeline_times" do + context 'pipeline_times' do let(:project) { create(:project) } let(:chart) { Gitlab::Ci::Charts::PipelineTime.new(project) } diff --git a/spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb b/spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb index 3388ae0af2f..ff44a235ea5 100644 --- a/spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb @@ -46,98 +46,53 @@ RSpec.describe ::Gitlab::Ci::Config::Entry::Product::Matrix do end end - context 'with one_dimensional_matrix feature flag enabled' do - before do - stub_feature_flags(one_dimensional_matrix: true) - matrix.compose! + context 'when entry config has only one variable with multiple values' do + let(:config) do + [ + { + 'VAR_1' => %w[build test] + } + ] end - context 'when entry config has only one variable with multiple values' do - let(:config) do - [ - { - 'VAR_1' => %w[build test] - } - ] - end - - describe '#valid?' do - it { is_expected.to be_valid } - end + describe '#valid?' do + it { is_expected.to be_valid } + end - describe '#errors' do - it 'returns no errors' do - expect(matrix.errors) - .to be_empty - end + describe '#errors' do + it 'returns no errors' do + expect(matrix.errors) + .to be_empty end + end - describe '#value' do - before do - matrix.compose! - end - - it 'returns the value without raising an error' do - expect(matrix.value).to eq([{ 'VAR_1' => %w[build test] }]) - end + describe '#value' do + before do + matrix.compose! end - context 'when entry config has only one variable with one value' do - let(:config) do - [ - { - 'VAR_1' => %w[test] - } - ] - end - - describe '#valid?' do - it { is_expected.to be_valid } - end - - describe '#errors' do - it 'returns no errors' do - expect(matrix.errors) - .to be_empty - end - end - - describe '#value' do - before do - matrix.compose! - end - - it 'returns the value without raising an error' do - expect(matrix.value).to eq([{ 'VAR_1' => %w[test] }]) - end - end + it 'returns the value without raising an error' do + expect(matrix.value).to eq([{ 'VAR_1' => %w[build test] }]) end end - end - context 'with one_dimensional_matrix feature flag disabled' do - before do - stub_feature_flags(one_dimensional_matrix: false) - matrix.compose! - end - - context 'when entry config has only one variable with multiple values' do + context 'when entry config has only one variable with one value' do let(:config) do [ { - 'VAR_1' => %w[build test] + 'VAR_1' => %w[test] } ] end describe '#valid?' do - it { is_expected.not_to be_valid } + it { is_expected.to be_valid } end describe '#errors' do - it 'returns error about too many jobs' do + it 'returns no errors' do expect(matrix.errors) - .to include('variables config requires at least 2 items') + .to be_empty end end @@ -147,38 +102,7 @@ RSpec.describe ::Gitlab::Ci::Config::Entry::Product::Matrix do end it 'returns the value without raising an error' do - expect(matrix.value).to eq([{ 'VAR_1' => %w[build test] }]) - end - end - - context 'when entry config has only one variable with one value' do - let(:config) do - [ - { - 'VAR_1' => %w[test] - } - ] - end - - describe '#valid?' do - it { is_expected.not_to be_valid } - end - - describe '#errors' do - it 'returns no errors' do - expect(matrix.errors) - .to include('variables config requires at least 2 items') - end - end - - describe '#value' do - before do - matrix.compose! - end - - it 'returns the value without raising an error' do - expect(matrix.value).to eq([{ 'VAR_1' => %w[test] }]) - end + expect(matrix.value).to eq([{ 'VAR_1' => %w[test] }]) end end end diff --git a/spec/lib/gitlab/ci/config/entry/product/variables_spec.rb b/spec/lib/gitlab/ci/config/entry/product/variables_spec.rb index 407efb438b5..5e920ce34e0 100644 --- a/spec/lib/gitlab/ci/config/entry/product/variables_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/product/variables_spec.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true -# After Feature one_dimensional_matrix is removed, this can be changed back to fast_spec_helper -require 'spec_helper' +require 'fast_spec_helper' require_dependency 'active_model' RSpec.describe Gitlab::Ci::Config::Entry::Product::Variables do @@ -46,70 +45,18 @@ RSpec.describe Gitlab::Ci::Config::Entry::Product::Variables do end end - context 'with one_dimensional_matrix feature flag enabled' do - context 'with only one variable' do - before do - stub_feature_flags(one_dimensional_matrix: true) - end - let(:config) { { VAR: 'test' } } - - describe '#valid?' do - it 'is valid' do - expect(entry).to be_valid - end - end + context 'with only one variable' do + let(:config) { { VAR: 'test' } } - describe '#errors' do - it 'does not append errors' do - expect(entry.errors).to be_empty - end + describe '#valid?' do + it 'is valid' do + expect(entry).to be_valid end end - end - - context 'with one_dimensional_matrix feature flag disabled' do - context 'when entry value is not correct' do - before do - stub_feature_flags(one_dimensional_matrix: false) - end - shared_examples 'invalid variables' do |message| - describe '#errors' do - it 'saves errors' do - expect(entry.errors).to include(message) - end - end - - describe '#valid?' do - it 'is not valid' do - expect(entry).not_to be_valid - end - end - end - - context 'with array' do - let(:config) { [:VAR, 'test'] } - it_behaves_like 'invalid variables', /should be a hash of key value pairs/ - end - - context 'with empty array' do - let(:config) { { VAR: 'test', VAR2: [] } } - - it_behaves_like 'invalid variables', /should be a hash of key value pairs/ - end - - context 'with nested array' do - let(:config) { { VAR: 'test', VAR2: [1, [2]] } } - - it_behaves_like 'invalid variables', /should be a hash of key value pairs/ - end - - context 'with one_dimensional_matrix feature flag disabled' do - context 'with only one variable' do - let(:config) { { VAR: 'test' } } - - it_behaves_like 'invalid variables', /variables config requires at least 2 items/ - end + describe '#errors' do + it 'does not append errors' do + expect(entry.errors).to be_empty end end end diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb index bf14d8d6b34..7ad57827e30 100644 --- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb +++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb @@ -100,6 +100,42 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do expect { subject }.to raise_error(described_class::AmbigiousSpecificationError) end end + + context "when the key is a project's file" do + let(:values) do + { include: { project: project.full_path, file: local_file }, + image: 'ruby:2.7' } + end + + it 'returns File instances' do + expect(subject).to contain_exactly( + an_instance_of(Gitlab::Ci::Config::External::File::Project)) + end + end + + context "when the key is project's files" do + let(:values) do + { include: { project: project.full_path, file: [local_file, 'another_file_path.yml'] }, + image: 'ruby:2.7' } + end + + it 'returns two File instances' do + expect(subject).to contain_exactly( + an_instance_of(Gitlab::Ci::Config::External::File::Project), + an_instance_of(Gitlab::Ci::Config::External::File::Project)) + end + + context 'when FF ci_include_multiple_files_from_project is disabled' do + before do + stub_feature_flags(ci_include_multiple_files_from_project: false) + end + + it 'returns a File instance' do + expect(subject).to contain_exactly( + an_instance_of(Gitlab::Ci::Config::External::File::Project)) + end + end + end end context "when 'include' is defined as an array" do @@ -161,6 +197,16 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do it 'raises an exception' do expect { subject }.to raise_error(described_class::DuplicateIncludesError) end + + context 'when including multiple files from a project' do + let(:values) do + { include: { project: project.full_path, file: [local_file, local_file] } } + end + + it 'raises an exception' do + expect { subject }.to raise_error(described_class::DuplicateIncludesError) + end + end end context "when too many 'includes' are defined" do @@ -179,6 +225,16 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do it 'raises an exception' do expect { subject }.to raise_error(described_class::TooManyIncludesError) end + + context 'when including multiple files from a project' do + let(:values) do + { include: { project: project.full_path, file: [local_file, 'another_file_path.yml'] } } + end + + it 'raises an exception' do + expect { subject }.to raise_error(described_class::TooManyIncludesError) + end + end end end end diff --git a/spec/lib/gitlab/ci/config/external/processor_spec.rb b/spec/lib/gitlab/ci/config/external/processor_spec.rb index 9786e050399..150a2ec2929 100644 --- a/spec/lib/gitlab/ci/config/external/processor_spec.rb +++ b/spec/lib/gitlab/ci/config/external/processor_spec.rb @@ -302,5 +302,82 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do end end end + + context 'when a valid project file is defined' do + let(:values) do + { + include: { project: another_project.full_path, file: '/templates/my-build.yml' }, + image: 'ruby:2.7' + } + end + + before do + another_project.add_developer(user) + + allow_next_instance_of(Repository) do |repository| + allow(repository).to receive(:blob_data_at).with(another_project.commit.id, '/templates/my-build.yml') do + <<~HEREDOC + my_build: + script: echo Hello World + HEREDOC + end + end + end + + it 'appends the file to the values' do + output = processor.perform + expect(output.keys).to match_array([:image, :my_build]) + end + end + + context 'when valid project files are defined in a single include' do + let(:values) do + { + include: { + project: another_project.full_path, + file: ['/templates/my-build.yml', '/templates/my-test.yml'] + }, + image: 'ruby:2.7' + } + end + + before do + another_project.add_developer(user) + + allow_next_instance_of(Repository) do |repository| + allow(repository).to receive(:blob_data_at).with(another_project.commit.id, '/templates/my-build.yml') do + <<~HEREDOC + my_build: + script: echo Hello World + HEREDOC + end + + allow(repository).to receive(:blob_data_at).with(another_project.commit.id, '/templates/my-test.yml') do + <<~HEREDOC + my_test: + script: echo Hello World + HEREDOC + end + end + end + + it 'appends the file to the values' do + output = processor.perform + expect(output.keys).to match_array([:image, :my_build, :my_test]) + end + + context 'when FF ci_include_multiple_files_from_project is disabled' do + before do + stub_feature_flags(ci_include_multiple_files_from_project: false) + end + + it 'raises an error' do + expect { processor.perform }.to raise_error( + described_class::IncludeError, + 'Included file `["/templates/my-build.yml", "/templates/my-test.yml"]` needs to be a string' + ) + end + end + end end end diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb index 41a45fe4ab7..b5a0f0e3fd7 100644 --- a/spec/lib/gitlab/ci/config_spec.rb +++ b/spec/lib/gitlab/ci/config_spec.rb @@ -246,6 +246,14 @@ RSpec.describe Gitlab::Ci::Config do let(:remote_location) { 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.gitlab-ci-1.yml' } let(:local_location) { 'spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml' } + let(:local_file_content) do + File.read(Rails.root.join(local_location)) + end + + let(:local_location_hash) do + YAML.safe_load(local_file_content).deep_symbolize_keys + end + let(:remote_file_content) do <<~HEREDOC variables: @@ -256,8 +264,8 @@ RSpec.describe Gitlab::Ci::Config do HEREDOC end - let(:local_file_content) do - File.read(Rails.root.join(local_location)) + let(:remote_file_hash) do + YAML.safe_load(remote_file_content).deep_symbolize_keys end let(:gitlab_ci_yml) do @@ -283,22 +291,11 @@ RSpec.describe Gitlab::Ci::Config do context "when gitlab_ci_yml has valid 'include' defined" do it 'returns a composed hash' do - before_script_values = [ - "apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs", "ruby -v", - "which ruby", - "bundle install --jobs $(nproc) \"${FLAGS[@]}\"" - ] - variables = { - POSTGRES_USER: "user", - POSTGRES_PASSWORD: "testing-password", - POSTGRES_ENABLED: "true", - POSTGRES_DB: "$CI_ENVIRONMENT_SLUG" - } composed_hash = { - before_script: before_script_values, + before_script: local_location_hash[:before_script], image: "ruby:2.7", rspec: { script: ["bundle exec rspec"] }, - variables: variables + variables: remote_file_hash[:variables] } expect(config.to_hash).to eq(composed_hash) @@ -575,5 +572,56 @@ RSpec.describe Gitlab::Ci::Config do ) end end + + context "when including multiple files from a project" do + let(:other_file_location) { 'my_builds.yml' } + + let(:other_file_content) do + <<~HEREDOC + build: + stage: build + script: echo hello + + rspec: + stage: test + script: bundle exec rspec + HEREDOC + end + + let(:gitlab_ci_yml) do + <<~HEREDOC + include: + - project: #{project.full_path} + file: + - #{local_location} + - #{other_file_location} + + image: ruby:2.7 + HEREDOC + end + + before do + project.add_developer(user) + + allow_next_instance_of(Repository) do |repository| + allow(repository).to receive(:blob_data_at).with(an_instance_of(String), local_location) + .and_return(local_file_content) + + allow(repository).to receive(:blob_data_at).with(an_instance_of(String), other_file_location) + .and_return(other_file_content) + end + end + + it 'returns a composed hash' do + composed_hash = { + before_script: local_location_hash[:before_script], + image: "ruby:2.7", + build: { stage: "build", script: "echo hello" }, + rspec: { stage: "test", script: "bundle exec rspec" } + } + + expect(config.to_hash).to eq(composed_hash) + end + end end end diff --git a/spec/lib/gitlab/ci/jwt_spec.rb b/spec/lib/gitlab/ci/jwt_spec.rb index 9b133efad9c..3130c0c0c41 100644 --- a/spec/lib/gitlab/ci/jwt_spec.rb +++ b/spec/lib/gitlab/ci/jwt_spec.rb @@ -93,32 +93,65 @@ RSpec.describe Gitlab::Ci::Jwt do end describe '.for_build' do - let(:rsa_key) { OpenSSL::PKey::RSA.new(Rails.application.secrets.openid_connect_signing_key) } + shared_examples 'generating JWT for build' do + context 'when signing key is present' do + let(:rsa_key) { OpenSSL::PKey::RSA.generate(1024) } + let(:rsa_key_data) { rsa_key.to_s } - subject(:jwt) { described_class.for_build(build) } + it 'generates JWT with key id' do + _payload, headers = JWT.decode(jwt, rsa_key.public_key, true, { algorithm: 'RS256' }) + + expect(headers['kid']).to eq(rsa_key.public_key.to_jwk['kid']) + end + + it 'generates JWT for the given job with ttl equal to build timeout' do + expect(build).to receive(:metadata_timeout).and_return(3_600) + + payload, _headers = JWT.decode(jwt, rsa_key.public_key, true, { algorithm: 'RS256' }) + ttl = payload["exp"] - payload["iat"] + + expect(ttl).to eq(3_600) + end + + it 'generates JWT for the given job with default ttl if build timeout is not set' do + expect(build).to receive(:metadata_timeout).and_return(nil) + + payload, _headers = JWT.decode(jwt, rsa_key.public_key, true, { algorithm: 'RS256' }) + ttl = payload["exp"] - payload["iat"] - it 'generates JWT with key id' do - _payload, headers = JWT.decode(jwt, rsa_key.public_key, true, { algorithm: 'RS256' }) + expect(ttl).to eq(5.minutes.to_i) + end + end + + context 'when signing key is missing' do + let(:rsa_key_data) { nil } - expect(headers['kid']).to eq(rsa_key.public_key.to_jwk['kid']) + it 'raises NoSigningKeyError' do + expect { jwt }.to raise_error described_class::NoSigningKeyError + end + end end - it 'generates JWT for the given job with ttl equal to build timeout' do - expect(build).to receive(:metadata_timeout).and_return(3_600) + subject(:jwt) { described_class.for_build(build) } + + context 'when ci_jwt_signing_key feature flag is disabled' do + before do + stub_feature_flags(ci_jwt_signing_key: false) - payload, _headers = JWT.decode(jwt, rsa_key.public_key, true, { algorithm: 'RS256' }) - ttl = payload["exp"] - payload["iat"] + allow(Rails.application.secrets).to receive(:openid_connect_signing_key).and_return(rsa_key_data) + end - expect(ttl).to eq(3_600) + it_behaves_like 'generating JWT for build' end - it 'generates JWT for the given job with default ttl if build timeout is not set' do - expect(build).to receive(:metadata_timeout).and_return(nil) + context 'when ci_jwt_signing_key feature flag is enabled' do + before do + stub_feature_flags(ci_jwt_signing_key: true) - payload, _headers = JWT.decode(jwt, rsa_key.public_key, true, { algorithm: 'RS256' }) - ttl = payload["exp"] - payload["iat"] + stub_application_setting(ci_jwt_signing_key: rsa_key_data) + end - expect(ttl).to eq(5.minutes.to_i) + it_behaves_like 'generating JWT for build' end end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb new file mode 100644 index 00000000000..3eaecb11ae0 --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Pipeline::Chain::CancelPendingPipelines do + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user) } + let(:prev_pipeline) { create(:ci_pipeline, project: project) } + let(:new_commit) { create(:commit, project: project) } + let(:pipeline) { create(:ci_pipeline, project: project, sha: new_commit.sha) } + + let(:command) do + Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user) + end + + let(:step) { described_class.new(pipeline, command) } + + before do + create(:ci_build, :interruptible, :running, pipeline: prev_pipeline) + create(:ci_build, :interruptible, :success, pipeline: prev_pipeline) + create(:ci_build, :created, pipeline: prev_pipeline) + + create(:ci_build, :interruptible, pipeline: pipeline) + end + + describe '#perform!' do + subject(:perform) { step.perform! } + + before do + expect(build_statuses(prev_pipeline)).to contain_exactly('running', 'success', 'created') + expect(build_statuses(pipeline)).to contain_exactly('pending') + end + + context 'when auto-cancel is enabled' do + before do + project.update!(auto_cancel_pending_pipelines: 'enabled') + end + + it 'cancels only previous interruptible builds' do + perform + + expect(build_statuses(prev_pipeline)).to contain_exactly('canceled', 'success', 'canceled') + expect(build_statuses(pipeline)).to contain_exactly('pending') + end + + context 'when the previous pipeline has a child pipeline' do + let(:child_pipeline) { create(:ci_pipeline, child_of: prev_pipeline) } + + context 'when the child pipeline has an interruptible job' do + before do + create(:ci_build, :interruptible, :running, pipeline: child_pipeline) + end + + it 'cancels interruptible builds of child pipeline' do + expect(build_statuses(child_pipeline)).to contain_exactly('running') + + perform + + expect(build_statuses(child_pipeline)).to contain_exactly('canceled') + end + + context 'when FF ci_auto_cancel_all_pipelines is disabled' do + before do + stub_feature_flags(ci_auto_cancel_all_pipelines: false) + end + + it 'does not cancel interruptible builds of child pipeline' do + expect(build_statuses(child_pipeline)).to contain_exactly('running') + + perform + + expect(build_statuses(child_pipeline)).to contain_exactly('running') + end + end + end + + context 'when the child pipeline has not an interruptible job' do + before do + create(:ci_build, :running, pipeline: child_pipeline) + end + + it 'does not cancel the build of child pipeline' do + expect(build_statuses(child_pipeline)).to contain_exactly('running') + + perform + + expect(build_statuses(child_pipeline)).to contain_exactly('running') + end + end + end + + context 'when the prev pipeline source is webide' do + let(:prev_pipeline) { create(:ci_pipeline, :webide, project: project) } + + it 'does not cancel builds of the previous pipeline' do + perform + + expect(build_statuses(prev_pipeline)).to contain_exactly('created', 'running', 'success') + expect(build_statuses(pipeline)).to contain_exactly('pending') + end + end + end + + context 'when auto-cancel is disabled' do + before do + project.update!(auto_cancel_pending_pipelines: 'disabled') + end + + it 'does not cancel any build' do + subject + + expect(build_statuses(prev_pipeline)).to contain_exactly('running', 'success', 'created') + expect(build_statuses(pipeline)).to contain_exactly('pending') + end + end + end + + private + + def build_statuses(pipeline) + pipeline.builds.pluck(:status) + end +end diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb index 8c02121857a..5506b079d0f 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb @@ -22,6 +22,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Populate do [ Gitlab::Ci::Pipeline::Chain::Config::Content.new(pipeline, command), Gitlab::Ci::Pipeline::Chain::Config::Process.new(pipeline, command), + Gitlab::Ci::Pipeline::Chain::SeedBlock.new(pipeline, command), Gitlab::Ci::Pipeline::Chain::Seed.new(pipeline, command) ] end @@ -180,23 +181,21 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Populate do ->(pipeline) { pipeline.variables.create!(key: 'VAR', value: '123') } end - it 'wastes pipeline iid' do - expect { run_chain }.to raise_error(ActiveRecord::RecordNotSaved) - - last_iid = InternalId.ci_pipelines - .where(project_id: project.id) - .last.last_value - - expect(last_iid).to be > 0 + it 'raises error' do + expect { run_chain }.to raise_error(ActiveRecord::RecordNotSaved, + 'You cannot call create unless the parent is saved') end end end context 'when pipeline gets persisted during the process' do - let(:pipeline) { create(:ci_pipeline, project: project) } + before do + dependencies.each(&:perform!) + pipeline.save! + end it 'raises error' do - expect { run_chain }.to raise_error(described_class::PopulateError) + expect { step.perform! }.to raise_error(described_class::PopulateError) end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/seed_block_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/seed_block_spec.rb new file mode 100644 index 00000000000..85c8e20767f --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/seed_block_spec.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Pipeline::Chain::SeedBlock do + let(:project) { create(:project, :repository) } + let(:user) { create(:user, developer_projects: [project]) } + let(:seeds_block) { } + + let(:command) do + Gitlab::Ci::Pipeline::Chain::Command.new( + project: project, + current_user: user, + origin_ref: 'master', + seeds_block: seeds_block) + end + + let(:pipeline) { build(:ci_pipeline, project: project) } + + describe '#perform!' do + before do + stub_ci_pipeline_yaml_file(YAML.dump(config)) + end + + subject(:run_chain) do + [ + Gitlab::Ci::Pipeline::Chain::Config::Content.new(pipeline, command), + Gitlab::Ci::Pipeline::Chain::Config::Process.new(pipeline, command) + ].map(&:perform!) + + described_class.new(pipeline, command).perform! + end + + let(:config) do + { rspec: { script: 'rake' } } + end + + context 'when there is not seeds_block' do + it 'does nothing' do + expect { run_chain }.not_to raise_error + end + end + + context 'when there is seeds_block' do + let(:seeds_block) do + ->(pipeline) { pipeline.variables.build(key: 'VAR', value: '123') } + end + + it 'executes the block' do + run_chain + + expect(pipeline.variables.size).to eq(1) + end + + context 'when FF ci_seed_block_run_before_workflow_rules is disabled' do + before do + stub_feature_flags(ci_seed_block_run_before_workflow_rules: false) + end + + it 'does not execute the block' do + run_chain + + expect(pipeline.variables.size).to eq(0) + end + end + end + + context 'when the seeds_block tries to save the pipelie' do + let(:seeds_block) do + ->(pipeline) { pipeline.save! } + end + + it 'raises error' do + expect { run_chain }.to raise_error('Pipeline cannot be persisted by `seeds_block`') + end + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb index f83cd49d780..d849c768a3c 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb @@ -5,22 +5,14 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do let(:project) { create(:project, :repository) } let(:user) { create(:user, developer_projects: [project]) } + let(:seeds_block) { } let(:command) do Gitlab::Ci::Pipeline::Chain::Command.new( project: project, current_user: user, origin_ref: 'master', - seeds_block: nil) - end - - def run_chain(pipeline, command) - [ - Gitlab::Ci::Pipeline::Chain::Config::Content.new(pipeline, command), - Gitlab::Ci::Pipeline::Chain::Config::Process.new(pipeline, command) - ].map(&:perform!) - - described_class.new(pipeline, command).perform! + seeds_block: seeds_block) end let(:pipeline) { build(:ci_pipeline, project: project) } @@ -28,22 +20,36 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do describe '#perform!' do before do stub_ci_pipeline_yaml_file(YAML.dump(config)) - run_chain(pipeline, command) end let(:config) do { rspec: { script: 'rake' } } end + subject(:run_chain) do + [ + Gitlab::Ci::Pipeline::Chain::Config::Content.new(pipeline, command), + Gitlab::Ci::Pipeline::Chain::Config::Process.new(pipeline, command) + ].map(&:perform!) + + described_class.new(pipeline, command).perform! + end + it 'allocates next IID' do + run_chain + expect(pipeline.iid).to be_present end it 'ensures ci_ref' do + run_chain + expect(pipeline.ci_ref).to be_present end it 'sets the seeds in the command object' do + run_chain + expect(command.stage_seeds).to all(be_a Gitlab::Ci::Pipeline::Seed::Base) expect(command.stage_seeds.count).to eq 1 end @@ -58,6 +64,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do end it 'correctly fabricates a stage seeds object' do + run_chain + seeds = command.stage_seeds expect(seeds.size).to eq 2 expect(seeds.first.attributes[:name]).to eq 'test' @@ -81,6 +89,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do end it 'returns stage seeds only assigned to master' do + run_chain + seeds = command.stage_seeds expect(seeds.size).to eq 1 @@ -100,6 +110,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do end it 'returns stage seeds only assigned to schedules' do + run_chain + seeds = command.stage_seeds expect(seeds.size).to eq 1 @@ -127,6 +139,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do let(:pipeline) { build(:ci_pipeline, project: project) } it 'returns seeds for kubernetes dependent job' do + run_chain + seeds = command.stage_seeds expect(seeds.size).to eq 2 @@ -138,6 +152,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do context 'when kubernetes is not active' do it 'does not return seeds for kubernetes dependent job' do + run_chain + seeds = command.stage_seeds expect(seeds.size).to eq 1 @@ -155,11 +171,39 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do end it 'returns stage seeds only when variables expression is truthy' do + run_chain + seeds = command.stage_seeds expect(seeds.size).to eq 1 expect(seeds.dig(0, 0, :name)).to eq 'unit' end end + + context 'when there is seeds_block' do + let(:seeds_block) do + ->(pipeline) { pipeline.variables.build(key: 'VAR', value: '123') } + end + + context 'when FF ci_seed_block_run_before_workflow_rules is enabled' do + it 'does not execute the block' do + run_chain + + expect(pipeline.variables.size).to eq(0) + end + end + + context 'when FF ci_seed_block_run_before_workflow_rules is disabled' do + before do + stub_feature_flags(ci_seed_block_run_before_workflow_rules: false) + end + + it 'executes the block' do + run_chain + + expect(pipeline.variables.size).to eq(1) + end + end + end end end diff --git a/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb index 0c8a0de2f34..e62bf042fba 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb @@ -16,20 +16,37 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do subject { seed.to_resource } shared_examples_for 'returning a correct environment' do + let(:expected_auto_stop_in_seconds) do + if expected_auto_stop_in + ChronicDuration.parse(expected_auto_stop_in).seconds + end + end + it 'returns a persisted environment object' do - expect { subject }.to change { Environment.count }.by(1) + freeze_time do + expect { subject }.to change { Environment.count }.by(1) - expect(subject).to be_a(Environment) - expect(subject).to be_persisted - expect(subject.project).to eq(project) - expect(subject.name).to eq(expected_environment_name) + expect(subject).to be_a(Environment) + expect(subject).to be_persisted + expect(subject.project).to eq(project) + expect(subject.name).to eq(expected_environment_name) + expect(subject.auto_stop_in).to eq(expected_auto_stop_in_seconds) + end end context 'when environment has already existed' do - let!(:environment) { create(:environment, project: project, name: expected_environment_name) } + let!(:environment) do + create(:environment, + project: project, + name: expected_environment_name + ).tap do |env| + env.auto_stop_in = expected_auto_stop_in + end + end it 'returns the existing environment object' do expect { subject }.not_to change { Environment.count } + expect { subject }.not_to change { environment.auto_stop_at } expect(subject).to be_persisted expect(subject).to eq(environment) @@ -37,9 +54,10 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do end end - context 'when job has environment attribute' do + context 'when job has environment name attribute' do let(:environment_name) { 'production' } let(:expected_environment_name) { 'production' } + let(:expected_auto_stop_in) { nil } let(:attributes) do { @@ -49,11 +67,41 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do end it_behaves_like 'returning a correct environment' + + context 'and job environment also has an auto_stop_in attribute' do + let(:environment_auto_stop_in) { '5 minutes' } + let(:expected_auto_stop_in) { '5 minutes' } + + let(:attributes) do + { + environment: environment_name, + options: { + environment: { + name: environment_name, + auto_stop_in: environment_auto_stop_in + } + } + } + end + + it_behaves_like 'returning a correct environment' + + context 'but the environment auto_stop_in on create flag is disabled' do + let(:expected_auto_stop_in) { nil } + + before do + stub_feature_flags(environment_auto_stop_start_on_create: false) + end + + it_behaves_like 'returning a correct environment' + end + end end context 'when job starts a review app' do let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' } let(:expected_environment_name) { "review/#{job.ref}" } + let(:expected_auto_stop_in) { nil } let(:attributes) do { @@ -68,6 +116,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do context 'when job stops a review app' do let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' } let(:expected_environment_name) { "review/#{job.ref}" } + let(:expected_auto_stop_in) { nil } let(:attributes) do { diff --git a/spec/lib/gitlab/ci/reports/test_case_spec.rb b/spec/lib/gitlab/ci/reports/test_case_spec.rb index a142846fc18..668a475514e 100644 --- a/spec/lib/gitlab/ci/reports/test_case_spec.rb +++ b/spec/lib/gitlab/ci/reports/test_case_spec.rb @@ -2,13 +2,13 @@ require 'spec_helper' -RSpec.describe Gitlab::Ci::Reports::TestCase do +RSpec.describe Gitlab::Ci::Reports::TestCase, :aggregate_failures do describe '#initialize' do let(:test_case) { described_class.new(params) } context 'when required params are given' do let(:job) { build(:ci_build) } - let(:params) { attributes_for(:test_case).merge!(job: job) } + let(:params) { attributes_for(:report_test_case).merge!(job: job) } it 'initializes an instance', :aggregate_failures do expect { test_case }.not_to raise_error @@ -31,7 +31,7 @@ RSpec.describe Gitlab::Ci::Reports::TestCase do shared_examples 'param is missing' do |param| let(:job) { build(:ci_build) } - let(:params) { attributes_for(:test_case).merge!(job: job) } + let(:params) { attributes_for(:report_test_case).merge!(job: job) } it 'raises an error' do params.delete(param) @@ -55,7 +55,7 @@ RSpec.describe Gitlab::Ci::Reports::TestCase do context 'when attachment is present' do let_it_be(:job) { create(:ci_build) } - let(:attachment_test_case) { build(:test_case, :failed_with_attachment, job: job) } + let(:attachment_test_case) { build(:report_test_case, :failed_with_attachment, job: job) } it "initializes the attachment if present" do expect(attachment_test_case.attachment).to eq("some/path.png") @@ -71,7 +71,7 @@ RSpec.describe Gitlab::Ci::Reports::TestCase do end context 'when attachment is missing' do - let(:test_case) { build(:test_case) } + let(:test_case) { build(:report_test_case) } it '#has_attachment?' do expect(test_case.has_attachment?).to be_falsy @@ -82,4 +82,17 @@ RSpec.describe Gitlab::Ci::Reports::TestCase do end end end + + describe '#set_recent_failures' do + it 'sets the recent_failures information' do + test_case = build(:report_test_case) + + test_case.set_recent_failures(1, 'master') + + expect(test_case.recent_failures).to eq( + count: 1, + base_branch: 'master' + ) + end + end end diff --git a/spec/lib/gitlab/ci/reports/test_failure_history_spec.rb b/spec/lib/gitlab/ci/reports/test_failure_history_spec.rb new file mode 100644 index 00000000000..8df34eddffd --- /dev/null +++ b/spec/lib/gitlab/ci/reports/test_failure_history_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Reports::TestFailureHistory, :aggregate_failures do + include TestReportsHelper + + describe '#load!' do + let_it_be(:project) { create(:project) } + let(:failed_rspec) { create_test_case_rspec_failed } + let(:failed_java) { create_test_case_java_failed } + + subject(:load_history) { described_class.new([failed_rspec, failed_java], project).load! } + + before do + allow(Ci::TestCaseFailure) + .to receive(:recent_failures_count) + .with(project: project, test_case_keys: [failed_rspec.key, failed_java.key]) + .and_return( + failed_rspec.key => 2, + failed_java.key => 1 + ) + end + + it 'sets the recent failures for each matching failed test case in all test suites' do + load_history + + expect(failed_rspec.recent_failures).to eq(count: 2, base_branch: 'master') + expect(failed_java.recent_failures).to eq(count: 1, base_branch: 'master') + end + + context 'when feature flag is disabled' do + before do + stub_feature_flags(test_failure_history: false) + end + + it 'does not set recent failures' do + load_history + + expect(failed_rspec.recent_failures).to be_nil + expect(failed_java.recent_failures).to be_nil + end + end + end +end diff --git a/spec/lib/gitlab/ci/reports/test_reports_spec.rb b/spec/lib/gitlab/ci/reports/test_reports_spec.rb index 502859852f2..24c00de3731 100644 --- a/spec/lib/gitlab/ci/reports/test_reports_spec.rb +++ b/spec/lib/gitlab/ci/reports/test_reports_spec.rb @@ -110,7 +110,7 @@ RSpec.describe Gitlab::Ci::Reports::TestReports do end describe '#with_attachment' do - let(:test_case) { build(:test_case, :failed) } + let(:test_case) { build(:report_test_case, :failed) } subject { test_reports.with_attachment! } @@ -126,8 +126,8 @@ RSpec.describe Gitlab::Ci::Reports::TestReports do end context 'when test suites contain an attachment' do - let(:test_case_succes) { build(:test_case) } - let(:test_case_with_attachment) { build(:test_case, :failed_with_attachment) } + let(:test_case_succes) { build(:report_test_case) } + let(:test_case_with_attachment) { build(:report_test_case, :failed_with_attachment) } before do test_reports.get_suite('rspec').add_test_case(test_case_succes) 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 6bb6771678a..c44d32ddb7d 100644 --- a/spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb +++ b/spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb @@ -2,11 +2,11 @@ require 'spec_helper' -RSpec.describe Gitlab::Ci::Reports::TestSuiteComparer do +RSpec.describe Gitlab::Ci::Reports::TestSuiteComparer, :aggregate_failures do include TestReportsHelper let(:comparer) { described_class.new(name, base_suite, head_suite) } - let(:name) { 'rpsec' } + let(:name) { 'rspec' } let(:base_suite) { Gitlab::Ci::Reports::TestSuite.new(name) } let(:head_suite) { Gitlab::Ci::Reports::TestSuite.new(name) } let(:test_case_success) { create_test_case_java_success } @@ -16,7 +16,7 @@ RSpec.describe Gitlab::Ci::Reports::TestSuiteComparer do describe '#new_failures' do subject { comparer.new_failures } - context 'when head sutie has a newly failed test case which does not exist in base' do + context 'when head suite has a newly failed test case which does not exist in base' do before do base_suite.add_test_case(test_case_success) head_suite.add_test_case(test_case_failed) @@ -27,7 +27,7 @@ RSpec.describe Gitlab::Ci::Reports::TestSuiteComparer do end end - context 'when head sutie still has a failed test case which failed in base' do + context 'when head suite still has a failed test case which failed in base' do before do base_suite.add_test_case(test_case_failed) head_suite.add_test_case(test_case_failed) @@ -38,7 +38,7 @@ RSpec.describe Gitlab::Ci::Reports::TestSuiteComparer do end end - context 'when head sutie has a success test case which failed in base' do + context 'when head suite has a success test case which failed in base' do before do base_suite.add_test_case(test_case_failed) head_suite.add_test_case(test_case_success) @@ -53,7 +53,7 @@ RSpec.describe Gitlab::Ci::Reports::TestSuiteComparer do describe '#existing_failures' do subject { comparer.existing_failures } - context 'when head sutie has a newly failed test case which does not exist in base' do + context 'when head suite has a newly failed test case which does not exist in base' do before do base_suite.add_test_case(test_case_success) head_suite.add_test_case(test_case_failed) @@ -64,7 +64,7 @@ RSpec.describe Gitlab::Ci::Reports::TestSuiteComparer do end end - context 'when head sutie still has a failed test case which failed in base' do + context 'when head suite still has a failed test case which failed in base' do before do base_suite.add_test_case(test_case_failed) head_suite.add_test_case(test_case_failed) @@ -75,7 +75,7 @@ RSpec.describe Gitlab::Ci::Reports::TestSuiteComparer do end end - context 'when head sutie has a success test case which failed in base' do + context 'when head suite has a success test case which failed in base' do before do base_suite.add_test_case(test_case_failed) head_suite.add_test_case(test_case_success) @@ -90,7 +90,7 @@ RSpec.describe Gitlab::Ci::Reports::TestSuiteComparer do describe '#resolved_failures' do subject { comparer.resolved_failures } - context 'when head sutie has a newly failed test case which does not exist in base' do + context 'when head suite has a newly failed test case which does not exist in base' do before do base_suite.add_test_case(test_case_success) head_suite.add_test_case(test_case_failed) @@ -105,7 +105,7 @@ RSpec.describe Gitlab::Ci::Reports::TestSuiteComparer do end end - context 'when head sutie still has a failed test case which failed in base' do + context 'when head suite still has a failed test case which failed in base' do before do base_suite.add_test_case(test_case_failed) head_suite.add_test_case(test_case_failed) @@ -120,7 +120,7 @@ RSpec.describe Gitlab::Ci::Reports::TestSuiteComparer do end end - context 'when head sutie has a success test case which failed in base' do + context 'when head suite has a success test case which failed in base' do before do base_suite.add_test_case(test_case_failed) head_suite.add_test_case(test_case_success) @@ -347,4 +347,128 @@ RSpec.describe Gitlab::Ci::Reports::TestSuiteComparer do end end end + + describe '#limited_tests' do + subject(:limited_tests) { comparer.limited_tests } + + context 'limits amount of tests returned' do + before do + stub_const("#{described_class}::DEFAULT_MAX_TESTS", 2) + stub_const("#{described_class}::DEFAULT_MIN_TESTS", 1) + end + + context 'prefers new over existing and resolved' do + before do + 3.times { add_new_failure } + 3.times { add_new_error } + 3.times { add_existing_failure } + 3.times { add_existing_error } + 3.times { add_resolved_failure } + 3.times { add_resolved_error } + end + + it 'returns 2 of each new category, and 1 of each resolved and existing' do + expect(limited_tests.new_failures.count).to eq(2) + expect(limited_tests.new_errors.count).to eq(2) + expect(limited_tests.existing_failures.count).to eq(1) + expect(limited_tests.existing_errors.count).to eq(1) + expect(limited_tests.resolved_failures.count).to eq(1) + expect(limited_tests.resolved_errors.count).to eq(1) + end + + it 'does not affect the overall count' do + expect(summary).to include(total: 18, resolved: 6, failed: 6, errored: 6) + end + end + + context 'prefers existing over resolved' do + before do + 3.times { add_existing_failure } + 3.times { add_existing_error } + 3.times { add_resolved_failure } + 3.times { add_resolved_error } + end + + it 'returns 2 of each existing category, and 1 of each resolved' do + expect(limited_tests.new_failures.count).to eq(0) + expect(limited_tests.new_errors.count).to eq(0) + expect(limited_tests.existing_failures.count).to eq(2) + expect(limited_tests.existing_errors.count).to eq(2) + expect(limited_tests.resolved_failures.count).to eq(1) + expect(limited_tests.resolved_errors.count).to eq(1) + end + + it 'does not affect the overall count' do + expect(summary).to include(total: 12, resolved: 6, failed: 3, errored: 3) + end + end + + context 'limits amount of resolved' do + before do + 3.times { add_resolved_failure } + 3.times { add_resolved_error } + end + + it 'returns 2 of each resolved category' do + expect(limited_tests.new_failures.count).to eq(0) + expect(limited_tests.new_errors.count).to eq(0) + expect(limited_tests.existing_failures.count).to eq(0) + expect(limited_tests.existing_errors.count).to eq(0) + expect(limited_tests.resolved_failures.count).to eq(2) + expect(limited_tests.resolved_errors.count).to eq(2) + end + + it 'does not affect the overall count' do + expect(summary).to include(total: 6, resolved: 6, failed: 0, errored: 0) + end + end + end + + def summary + { + total: comparer.total_count, + resolved: comparer.resolved_count, + failed: comparer.failed_count, + errored: comparer.error_count + } + end + + def add_new_failure + failed_case = create_test_case_rspec_failed(SecureRandom.hex) + head_suite.add_test_case(failed_case) + end + + def add_new_error + error_case = create_test_case_rspec_error(SecureRandom.hex) + head_suite.add_test_case(error_case) + end + + def add_existing_failure + failed_case = create_test_case_rspec_failed(SecureRandom.hex) + base_suite.add_test_case(failed_case) + head_suite.add_test_case(failed_case) + end + + def add_existing_error + error_case = create_test_case_rspec_error(SecureRandom.hex) + base_suite.add_test_case(error_case) + head_suite.add_test_case(error_case) + end + + def add_resolved_failure + case_name = SecureRandom.hex + failed_case = create_test_case_java_failed(case_name) + success_case = create_test_case_java_success(case_name) + base_suite.add_test_case(failed_case) + head_suite.add_test_case(success_case) + end + + def add_resolved_error + case_name = SecureRandom.hex + error_case = create_test_case_java_error(case_name) + success_case = create_test_case_java_success(case_name) + base_suite.add_test_case(error_case) + head_suite.add_test_case(success_case) + end + end end diff --git a/spec/lib/gitlab/ci/reports/test_suite_spec.rb b/spec/lib/gitlab/ci/reports/test_suite_spec.rb index 50d1595da73..1d6b39a7831 100644 --- a/spec/lib/gitlab/ci/reports/test_suite_spec.rb +++ b/spec/lib/gitlab/ci/reports/test_suite_spec.rb @@ -91,7 +91,7 @@ RSpec.describe Gitlab::Ci::Reports::TestSuite do subject { test_suite.with_attachment! } context 'when test cases do not contain an attachment' do - let(:test_case) { build(:test_case, :failed)} + let(:test_case) { build(:report_test_case, :failed)} before do test_suite.add_test_case(test_case) @@ -103,7 +103,7 @@ RSpec.describe Gitlab::Ci::Reports::TestSuite do end context 'when test cases contain an attachment' do - let(:test_case_with_attachment) { build(:test_case, :failed_with_attachment)} + let(:test_case_with_attachment) { build(:report_test_case, :failed_with_attachment)} before do test_suite.add_test_case(test_case_with_attachment) diff --git a/spec/lib/gitlab/ci/runner_instructions_spec.rb b/spec/lib/gitlab/ci/runner_instructions_spec.rb index 32ee2ceb040..d1020026fe6 100644 --- a/spec/lib/gitlab/ci/runner_instructions_spec.rb +++ b/spec/lib/gitlab/ci/runner_instructions_spec.rb @@ -75,6 +75,13 @@ RSpec.describe Gitlab::Ci::RunnerInstructions do with_them do let(:params) { { os: os, arch: arch } } + around do |example| + # puma in production does not run from Rails.root, ensure file loading does not assume this + Dir.chdir(Rails.root.join('tmp').to_s) do + example.run + end + end + it 'returns string containing correct params' do result = subject.install_script diff --git a/spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb new file mode 100644 index 00000000000..4be92e8608e --- /dev/null +++ b/spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Deploy-ECS.gitlab-ci.yml' do + subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('AWS/Deploy-ECS') } + + describe 'the created pipeline' do + let_it_be(:user) { create(:admin) } + let(:default_branch) { 'master' } + let(:pipeline_branch) { default_branch } + let(:project) { create(:project, :auto_devops, :custom_repo, files: { 'README.md' => '' }) } + let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) } + let(:pipeline) { service.execute!(:push) } + let(:build_names) { pipeline.builds.pluck(:name) } + let(:platform_target) { 'ECS' } + + before do + create(:ci_variable, project: project, key: 'AUTO_DEVOPS_PLATFORM_TARGET', value: platform_target) + stub_ci_pipeline_yaml_file(template.content) + allow_any_instance_of(Ci::BuildScheduleWorker).to receive(:perform).and_return(true) + allow(project).to receive(:default_branch).and_return(default_branch) + end + + shared_examples 'no pipeline yaml error' do + it 'does not have any error' do + expect(pipeline.has_yaml_errors?).to be_falsey + end + end + + it_behaves_like 'no pipeline yaml error' + + it 'creates the expected jobs' do + expect(build_names).to include('production_ecs') + end + + context 'when running a pipeline for a branch' do + let(:pipeline_branch) { 'test_branch' } + + before do + project.repository.create_branch(pipeline_branch) + end + + it_behaves_like 'no pipeline yaml error' + + it 'creates the expected jobs' do + expect(build_names).to include('review_ecs', 'stop_review_ecs') + end + + context 'when deploying to ECS Fargate' do + let(:platform_target) { 'FARGATE' } + + it 'creates the expected jobs' do + expect(build_names).to include('review_fargate', 'stop_review_fargate') + end + end + end + end +end diff --git a/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb index 4d90e7ca9e6..793df55f45d 100644 --- a/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb +++ b/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb @@ -94,14 +94,14 @@ RSpec.describe 'Auto-DevOps.gitlab-ci.yml' do end it 'creates an ECS deployment job for review only' do - expect(review_prod_build_names).to contain_exactly('review_ecs') + expect(review_prod_build_names).to contain_exactly('review_ecs', 'stop_review_ecs') end context 'with FARGATE as a launch type' do let(:platform_value) { 'FARGATE' } it 'creates an FARGATE deployment job for review only' do - expect(review_prod_build_names).to contain_exactly('review_fargate') + expect(review_prod_build_names).to contain_exactly('review_fargate', 'stop_review_fargate') end end end @@ -122,6 +122,15 @@ RSpec.describe 'Auto-DevOps.gitlab-ci.yml' do end end end + + context 'when the platform target is EC2' do + let(:platform_value) { 'EC2' } + + it 'contains the build_artifact job, not the build job' do + expect(build_names).to include('build_artifact') + expect(build_names).not_to include('build') + end + end end context 'when the project has no active cluster' do diff --git a/spec/lib/gitlab/ci/variables/collection/item_spec.rb b/spec/lib/gitlab/ci/variables/collection/item_spec.rb index eba2f29836d..2e43f22830a 100644 --- a/spec/lib/gitlab/ci/variables/collection/item_spec.rb +++ b/spec/lib/gitlab/ci/variables/collection/item_spec.rb @@ -15,14 +15,14 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item do context 'when unknown keyword is specified' do it 'raises error' do expect { described_class.new(key: variable_key, value: 'abc', files: true) } - .to raise_error ArgumentError, 'unknown keyword: files' + .to raise_error ArgumentError, 'unknown keyword: :files' end end context 'when required keywords are not specified' do it 'raises error' do expect { described_class.new(key: variable_key) } - .to raise_error ArgumentError, 'missing keyword: value' + .to raise_error ArgumentError, 'missing keyword: :value' end end -- cgit v1.2.3