diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-17 11:59:07 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-17 11:59:07 +0000 |
commit | 8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca (patch) | |
tree | 544930fb309b30317ae9797a9683768705d664c4 /spec/services/ci | |
parent | 4b1de649d0168371549608993deac953eb692019 (diff) | |
download | gitlab-ce-8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca.tar.gz |
Add latest changes from gitlab-org/gitlab@13-7-stable-eev13.7.0-rc42
Diffstat (limited to 'spec/services/ci')
14 files changed, 574 insertions, 220 deletions
diff --git a/spec/services/ci/compare_accessibility_reports_service_spec.rb b/spec/services/ci/compare_accessibility_reports_service_spec.rb index 6903a633eeb..e0b84219834 100644 --- a/spec/services/ci/compare_accessibility_reports_service_spec.rb +++ b/spec/services/ci/compare_accessibility_reports_service_spec.rb @@ -29,34 +29,4 @@ RSpec.describe Ci::CompareAccessibilityReportsService do end end end - - describe '#latest?' do - subject { service.latest?(base_pipeline, head_pipeline, data) } - - let!(:base_pipeline) { nil } - let!(:head_pipeline) { create(:ci_pipeline, :with_accessibility_reports, project: project) } - let!(:key) { service.send(:key, base_pipeline, head_pipeline) } - - context 'when cache key is latest' do - let(:data) { { key: key } } - - it { is_expected.to be_truthy } - end - - context 'when cache key is outdated' do - before do - head_pipeline.update_column(:updated_at, 10.minutes.ago) - end - - let(:data) { { key: key } } - - it { is_expected.to be_falsy } - end - - context 'when cache key is empty' do - let(:data) { { key: nil } } - - it { is_expected.to be_falsy } - end - end end diff --git a/spec/services/ci/compare_codequality_reports_service_spec.rb b/spec/services/ci/compare_codequality_reports_service_spec.rb new file mode 100644 index 00000000000..ef762a2e9ad --- /dev/null +++ b/spec/services/ci/compare_codequality_reports_service_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::CompareCodequalityReportsService do + let(:service) { described_class.new(project) } + let(:project) { create(:project, :repository) } + + describe '#execute' do + subject { service.execute(base_pipeline, head_pipeline) } + + context 'when head pipeline has a codequality report' do + let(:base_pipeline) { nil } + let(:head_pipeline) { create(:ci_pipeline, :with_codequality_reports, project: project) } + + it 'returns status and data' do + expect(subject[:status]).to eq(:parsed) + expect(subject[:data]).to match_schema('entities/codequality_reports_comparer') + end + end + + context 'when base and head pipelines have codequality reports' do + let(:base_pipeline) { create(:ci_pipeline, :with_codequality_reports, project: project) } + let(:head_pipeline) { create(:ci_pipeline, :with_codequality_reports, project: project) } + + it 'returns status and data' do + expect(subject[:status]).to eq(:parsed) + expect(subject[:data]).to match_schema('entities/codequality_reports_comparer') + end + end + end +end diff --git a/spec/services/ci/compare_reports_base_service_spec.rb b/spec/services/ci/compare_reports_base_service_spec.rb new file mode 100644 index 00000000000..9ce58c4972d --- /dev/null +++ b/spec/services/ci/compare_reports_base_service_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::CompareReportsBaseService do + let(:service) { described_class.new(project) } + let(:project) { create(:project, :repository) } + + describe '#latest?' do + subject { service.latest?(base_pipeline, head_pipeline, data) } + + let!(:base_pipeline) { nil } + let!(:head_pipeline) { create(:ci_pipeline, :with_test_reports, project: project) } + let!(:key) { service.send(:key, base_pipeline, head_pipeline) } + + context 'when cache key is latest' do + let(:data) { { key: key } } + + it { is_expected.to be_truthy } + end + + context 'when cache key is outdated' do + before do + head_pipeline.update_column(:updated_at, 10.minutes.ago) + end + + let(:data) { { key: key } } + + it { is_expected.to be_falsy } + end + + context 'when cache key is empty' do + let(:data) { { key: nil } } + + it { is_expected.to be_falsy } + end + end +end diff --git a/spec/services/ci/compare_test_reports_service_spec.rb b/spec/services/ci/compare_test_reports_service_spec.rb index 377c801b008..01d58b2095f 100644 --- a/spec/services/ci/compare_test_reports_service_spec.rb +++ b/spec/services/ci/compare_test_reports_service_spec.rb @@ -67,7 +67,7 @@ RSpec.describe Ci::CompareTestReportsService do # The JUnit fixture for the given build has 3 failures. # This service will create 1 test case failure record for each. - Ci::TestCasesService.new.execute(build) + Ci::TestFailureHistoryService.new(head_pipeline).execute end it 'loads recent failures on limited test cases to avoid building up a huge DB query', :aggregate_failures do @@ -80,34 +80,4 @@ RSpec.describe Ci::CompareTestReportsService do end end end - - describe '#latest?' do - subject { service.latest?(base_pipeline, head_pipeline, data) } - - let!(:base_pipeline) { nil } - let!(:head_pipeline) { create(:ci_pipeline, :with_test_reports, project: project) } - let!(:key) { service.send(:key, base_pipeline, head_pipeline) } - - context 'when cache key is latest' do - let(:data) { { key: key } } - - it { is_expected.to be_truthy } - end - - context 'when cache key is outdated' do - before do - head_pipeline.update_column(:updated_at, 10.minutes.ago) - end - - let(:data) { { key: key } } - - it { is_expected.to be_falsy } - end - - context 'when cache key is empty' do - let(:data) { { key: nil } } - - it { is_expected.to be_falsy } - end - end end diff --git a/spec/services/ci/create_job_artifacts_service_spec.rb b/spec/services/ci/create_job_artifacts_service_spec.rb index 72b0d220b11..29e51a23dea 100644 --- a/spec/services/ci/create_job_artifacts_service_spec.rb +++ b/spec/services/ci/create_job_artifacts_service_spec.rb @@ -24,7 +24,7 @@ RSpec.describe Ci::CreateJobArtifactsService do upload = Tempfile.new('upload') FileUtils.copy(path, upload.path) - UploadedFile.new(upload.path, params) + UploadedFile.new(upload.path, **params) end describe '#execute' do diff --git a/spec/services/ci/create_pipeline_service/merge_requests_spec.rb b/spec/services/ci/create_pipeline_service/merge_requests_spec.rb new file mode 100644 index 00000000000..e5347faed6a --- /dev/null +++ b/spec/services/ci/create_pipeline_service/merge_requests_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::CreatePipelineService do + context 'merge requests handling' do + let_it_be(:project) { create(:project, :repository) } + let_it_be(:user) { project.owner } + + let(:ref) { 'refs/heads/feature' } + let(:source) { :push } + let(:service) { described_class.new(project, user, { ref: ref }) } + let(:pipeline) { service.execute(source) } + + before do + stub_ci_pipeline_yaml_file <<-EOS + workflow: + rules: + # do not create pipelines if merge requests are opened + - if: $CI_OPEN_MERGE_REQUESTS + when: never + + - if: $CI_COMMIT_BRANCH + + rspec: + script: echo Hello World + EOS + end + + context 'when pushing a change' do + context 'when a merge request already exists' do + let!(:merge_request) do + create(:merge_request, + source_project: project, + source_branch: 'feature', + target_project: project, + target_branch: 'master') + end + + it 'does not create a pipeline' do + expect(pipeline).not_to be_persisted + end + end + + context 'when no merge request exists' do + it 'does create a pipeline' do + expect(pipeline.errors).to be_empty + expect(pipeline).to be_persisted + end + end + end + end +end diff --git a/spec/services/ci/create_pipeline_service/rules_spec.rb b/spec/services/ci/create_pipeline_service/rules_spec.rb index a0ff2fff0ef..ac6c4c188e4 100644 --- a/spec/services/ci/create_pipeline_service/rules_spec.rb +++ b/spec/services/ci/create_pipeline_service/rules_spec.rb @@ -93,6 +93,148 @@ RSpec.describe Ci::CreatePipelineService do end end end + + context 'with allow_failure and exit_codes', :aggregate_failures do + def find_job(name) + pipeline.builds.find_by(name: name) + end + + let(:config) do + <<-EOY + job-1: + script: exit 42 + allow_failure: + exit_codes: 42 + rules: + - if: $CI_COMMIT_REF_NAME == "master" + allow_failure: false + + job-2: + script: exit 42 + allow_failure: + exit_codes: 42 + rules: + - if: $CI_COMMIT_REF_NAME == "master" + allow_failure: true + + job-3: + script: exit 42 + allow_failure: + exit_codes: 42 + rules: + - if: $CI_COMMIT_REF_NAME == "master" + when: manual + EOY + end + + it 'creates a pipeline' do + expect(pipeline).to be_persisted + expect(build_names).to contain_exactly( + 'job-1', 'job-2', 'job-3' + ) + end + + it 'assigns job:allow_failure values to the builds' do + expect(find_job('job-1').allow_failure).to eq(false) + expect(find_job('job-2').allow_failure).to eq(true) + expect(find_job('job-3').allow_failure).to eq(false) + end + + it 'removes exit_codes if allow_failure is specified' do + expect(find_job('job-1').options.dig(:allow_failure_criteria)).to be_nil + expect(find_job('job-2').options.dig(:allow_failure_criteria)).to be_nil + expect(find_job('job-3').options.dig(:allow_failure_criteria, :exit_codes)).to eq([42]) + end + + context 'with ci_allow_failure_with_exit_codes disabled' do + before do + stub_feature_flags(ci_allow_failure_with_exit_codes: false) + end + + it 'does not persist allow_failure_criteria' do + expect(pipeline).to be_persisted + + expect(find_job('job-1').options.key?(:allow_failure_criteria)).to be_falsey + expect(find_job('job-2').options.key?(:allow_failure_criteria)).to be_falsey + expect(find_job('job-3').options.key?(:allow_failure_criteria)).to be_falsey + end + end + end + + context 'if:' do + context 'variables:' do + let(:config) do + <<-EOY + job: + script: "echo job1" + variables: + VAR1: my var 1 + VAR2: my var 2 + rules: + - if: $CI_COMMIT_REF_NAME =~ /master/ + variables: + VAR1: overridden var 1 + - if: $CI_COMMIT_REF_NAME =~ /feature/ + variables: + VAR2: overridden var 2 + VAR3: new var 3 + - when: on_success + EOY + end + + let(:job) { pipeline.builds.find_by(name: 'job') } + + context 'when matching to the first rule' do + let(:ref) { 'refs/heads/master' } + + it 'overrides VAR1' do + variables = job.scoped_variables_hash + + expect(variables['VAR1']).to eq('overridden var 1') + expect(variables['VAR2']).to eq('my var 2') + expect(variables['VAR3']).to be_nil + end + + context 'when FF ci_rules_variables is disabled' do + before do + stub_feature_flags(ci_rules_variables: false) + end + + it 'does not affect variables' do + variables = job.scoped_variables_hash + + expect(variables['VAR1']).to eq('my var 1') + expect(variables['VAR2']).to eq('my var 2') + expect(variables['VAR3']).to be_nil + end + end + end + + context 'when matching to the second rule' do + let(:ref) { 'refs/heads/feature' } + + it 'overrides VAR2 and adds VAR3' do + variables = job.scoped_variables_hash + + expect(variables['VAR1']).to eq('my var 1') + expect(variables['VAR2']).to eq('overridden var 2') + expect(variables['VAR3']).to eq('new var 3') + end + end + + context 'when no match' do + let(:ref) { 'refs/heads/wip' } + + it 'does not affect vars' do + variables = job.scoped_variables_hash + + expect(variables['VAR1']).to eq('my var 1') + expect(variables['VAR2']).to eq('my var 2') + expect(variables['VAR3']).to be_nil + end + end + end + end end context 'when workflow:rules are used' do diff --git a/spec/services/ci/generate_coverage_reports_service_spec.rb b/spec/services/ci/generate_coverage_reports_service_spec.rb index d39053adebc..d12a9268e7e 100644 --- a/spec/services/ci/generate_coverage_reports_service_spec.rb +++ b/spec/services/ci/generate_coverage_reports_service_spec.rb @@ -52,34 +52,4 @@ RSpec.describe Ci::GenerateCoverageReportsService do end end end - - describe '#latest?' do - subject { service.latest?(base_pipeline, head_pipeline, data) } - - let!(:base_pipeline) { nil } - let!(:head_pipeline) { create(:ci_pipeline, :with_test_reports, project: project) } - let!(:key) { service.send(:key, base_pipeline, head_pipeline) } - - context 'when cache key is latest' do - let(:data) { { key: key } } - - it { is_expected.to be_truthy } - end - - context 'when cache key is outdated' do - before do - head_pipeline.update_column(:updated_at, 10.minutes.ago) - end - - let(:data) { { key: key } } - - it { is_expected.to be_falsy } - end - - context 'when cache key is empty' do - let(:data) { { key: nil } } - - it { is_expected.to be_falsy } - end - end end diff --git a/spec/services/ci/generate_terraform_reports_service_spec.rb b/spec/services/ci/generate_terraform_reports_service_spec.rb index 25bf96035b2..c9ac74e050c 100644 --- a/spec/services/ci/generate_terraform_reports_service_spec.rb +++ b/spec/services/ci/generate_terraform_reports_service_spec.rb @@ -64,33 +64,4 @@ RSpec.describe Ci::GenerateTerraformReportsService do end end end - - describe '#latest?' do - let_it_be(:head_pipeline) { create(:ci_pipeline, :with_test_reports, project: project) } - - subject { described_class.new(project) } - - it 'returns true when cache key is latest' do - cache_key = subject.send(:key, nil, head_pipeline) - - result = subject.latest?(nil, head_pipeline, key: cache_key) - - expect(result).to eq(true) - end - - it 'returns false when cache key is outdated' do - cache_key = subject.send(:key, nil, head_pipeline) - head_pipeline.update_column(:updated_at, 10.minutes.ago) - - result = subject.latest?(nil, head_pipeline, key: cache_key) - - expect(result).to eq(false) - end - - it 'returns false when cache key is nil' do - result = subject.latest?(nil, head_pipeline, key: nil) - - expect(result).to eq(false) - end - end end diff --git a/spec/services/ci/list_config_variables_service_spec.rb b/spec/services/ci/list_config_variables_service_spec.rb index 95c98c2b5ef..1735f4cfc97 100644 --- a/spec/services/ci/list_config_variables_service_spec.rb +++ b/spec/services/ci/list_config_variables_service_spec.rb @@ -2,7 +2,9 @@ require 'spec_helper' -RSpec.describe Ci::ListConfigVariablesService do +RSpec.describe Ci::ListConfigVariablesService, :use_clean_rails_memory_store_caching do + include ReactiveCachingHelpers + let(:project) { create(:project, :repository) } let(:user) { project.creator } let(:service) { described_class.new(project, user) } @@ -31,6 +33,10 @@ RSpec.describe Ci::ListConfigVariablesService do } end + before do + synchronous_reactive_cache(service) + end + it 'returns variable list' do expect(subject['KEY1']).to eq({ value: 'val 1', description: 'description 1' }) expect(subject['KEY2']).to eq({ value: 'val 2', description: '' }) @@ -65,6 +71,8 @@ RSpec.describe Ci::ListConfigVariablesService do HEREDOC end end + + synchronous_reactive_cache(service) end it 'returns variable list' do @@ -77,6 +85,10 @@ RSpec.describe Ci::ListConfigVariablesService do let(:sha) { 'invalid-sha' } let(:ci_config) { nil } + before do + synchronous_reactive_cache(service) + end + it 'returns empty json' do expect(subject).to eq({}) end @@ -96,11 +108,44 @@ RSpec.describe Ci::ListConfigVariablesService do } end + before do + synchronous_reactive_cache(service) + end + it 'returns empty result' do expect(subject).to eq({}) end end + context 'when reading from cache' do + let(:sha) { 'master' } + let(:ci_config) { {} } + let(:reactive_cache_params) { [sha] } + let(:return_value) { { 'KEY1' => { value: 'val 1', description: 'description 1' } } } + + before do + stub_reactive_cache(service, return_value, reactive_cache_params) + end + + it 'returns variable list' do + expect(subject).to eq(return_value) + end + end + + context 'when the cache is empty' do + let(:sha) { 'master' } + let(:ci_config) { {} } + let(:reactive_cache_params) { [sha] } + + it 'returns nil and enquques the worker to fill cache' do + expect(ExternalServiceReactiveCachingWorker) + .to receive(:perform_async) + .with(service.class, service.id, *reactive_cache_params) + + expect(subject).to be_nil + end + end + private def stub_gitlab_ci_yml_for_sha(sha, result) diff --git a/spec/services/ci/pipelines/create_artifact_service_spec.rb b/spec/services/ci/pipelines/create_artifact_service_spec.rb index 6f177889ed3..4e9248d9d1a 100644 --- a/spec/services/ci/pipelines/create_artifact_service_spec.rb +++ b/spec/services/ci/pipelines/create_artifact_service_spec.rb @@ -7,7 +7,8 @@ RSpec.describe ::Ci::Pipelines::CreateArtifactService do subject { described_class.new.execute(pipeline) } context 'when pipeline has coverage reports' do - let(:pipeline) { create(:ci_pipeline, :with_coverage_reports) } + let(:project) { create(:project, :repository) } + let(:pipeline) { create(:ci_pipeline, :with_coverage_reports, project: project) } context 'when pipeline is finished' do it 'creates a pipeline artifact' do diff --git a/spec/services/ci/test_cases_service_spec.rb b/spec/services/ci/test_cases_service_spec.rb deleted file mode 100644 index b61d308640f..00000000000 --- a/spec/services/ci/test_cases_service_spec.rb +++ /dev/null @@ -1,94 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Ci::TestCasesService, :aggregate_failures do - describe '#execute' do - subject(:execute_service) { described_class.new.execute(build) } - - context 'when build has test reports' do - let(:build) { create(:ci_build, :success, :test_reports) } # The test report has 2 test case failures - - it 'creates test case failures records' do - execute_service - - expect(Ci::TestCase.count).to eq(2) - expect(Ci::TestCaseFailure.count).to eq(2) - end - - context 'when feature flag for test failure history is disabled' do - before do - stub_feature_flags(test_failure_history: false) - end - - it 'does not persist data' do - execute_service - - expect(Ci::TestCase.count).to eq(0) - expect(Ci::TestCaseFailure.count).to eq(0) - end - end - - context 'when build is not for the default branch' do - before do - build.update_column(:ref, 'new-feature') - end - - it 'does not persist data' do - execute_service - - expect(Ci::TestCase.count).to eq(0) - expect(Ci::TestCaseFailure.count).to eq(0) - end - end - - context 'when test failure data have already been persisted with the same exact attributes' do - before do - execute_service - end - - it 'does not fail but does not persist new data' do - expect { described_class.new.execute(build) }.not_to raise_error - - expect(Ci::TestCase.count).to eq(2) - expect(Ci::TestCaseFailure.count).to eq(2) - end - end - - context 'when test failure data have duplicates within the same payload (happens when the JUnit report has duplicate test case names but have different failures)' do - let(:build) { create(:ci_build, :success, :test_reports_with_duplicate_failed_test_names) } # The test report has 2 test case failures but with the same test case keys - - it 'does not fail but does not persist duplicate data' do - expect { described_class.new.execute(build) }.not_to raise_error - - expect(Ci::TestCase.count).to eq(1) - expect(Ci::TestCaseFailure.count).to eq(1) - end - end - - context 'when number of failed test cases exceed the limit' do - before do - stub_const("#{described_class.name}::MAX_TRACKABLE_FAILURES", 1) - end - - it 'does not persist data' do - execute_service - - expect(Ci::TestCase.count).to eq(0) - expect(Ci::TestCaseFailure.count).to eq(0) - end - end - end - - context 'when build has no test reports' do - let(:build) { create(:ci_build, :running) } - - it 'does not persist data' do - execute_service - - expect(Ci::TestCase.count).to eq(0) - expect(Ci::TestCaseFailure.count).to eq(0) - end - end - end -end diff --git a/spec/services/ci/test_failure_history_service_spec.rb b/spec/services/ci/test_failure_history_service_spec.rb new file mode 100644 index 00000000000..e858c85490d --- /dev/null +++ b/spec/services/ci/test_failure_history_service_spec.rb @@ -0,0 +1,192 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::TestFailureHistoryService, :aggregate_failures do + describe '#execute' do + let(:project) { create(:project) } + let(:pipeline) { create(:ci_empty_pipeline, status: :created, project: project) } + + subject(:execute_service) { described_class.new(pipeline).execute } + + context 'when pipeline has failed builds with test reports' do + before do + # The test report has 2 test case failures + create(:ci_build, :failed, :test_reports, pipeline: pipeline, project: project) + end + + it 'creates test case failures records' do + execute_service + + expect(Ci::TestCase.count).to eq(2) + expect(Ci::TestCaseFailure.count).to eq(2) + end + + context 'when feature flag for test failure history is disabled' do + before do + stub_feature_flags(test_failure_history: false) + end + + it 'does not persist data' do + execute_service + + expect(Ci::TestCase.count).to eq(0) + expect(Ci::TestCaseFailure.count).to eq(0) + end + end + + context 'when pipeline is not for the default branch' do + before do + pipeline.update_column(:ref, 'new-feature') + end + + it 'does not persist data' do + execute_service + + expect(Ci::TestCase.count).to eq(0) + expect(Ci::TestCaseFailure.count).to eq(0) + end + end + + context 'when test failure data have already been persisted with the same exact attributes' do + before do + execute_service + end + + it 'does not fail but does not persist new data' do + expect { described_class.new(pipeline).execute }.not_to raise_error + + expect(Ci::TestCase.count).to eq(2) + expect(Ci::TestCaseFailure.count).to eq(2) + end + end + + context 'when number of failed test cases exceed the limit' do + before do + stub_const("#{described_class.name}::MAX_TRACKABLE_FAILURES", 1) + end + + it 'does not persist data' do + execute_service + + expect(Ci::TestCase.count).to eq(0) + expect(Ci::TestCaseFailure.count).to eq(0) + end + end + + context 'when number of failed test cases across multiple builds exceed the limit' do + before do + stub_const("#{described_class.name}::MAX_TRACKABLE_FAILURES", 2) + + # This other test report has 1 unique test case failure which brings us to 3 total failures across all builds + # thus exceeding the limit of 2 for MAX_TRACKABLE_FAILURES + create(:ci_build, :failed, :test_reports_with_duplicate_failed_test_names, pipeline: pipeline, project: project) + end + + it 'does not persist data' do + execute_service + + expect(Ci::TestCase.count).to eq(0) + expect(Ci::TestCaseFailure.count).to eq(0) + end + end + end + + context 'when test failure data have duplicates within the same payload (happens when the JUnit report has duplicate test case names but have different failures)' do + before do + # The test report has 2 test case failures but with the same test case keys + create(:ci_build, :failed, :test_reports_with_duplicate_failed_test_names, pipeline: pipeline, project: project) + end + + it 'does not fail but does not persist duplicate data' do + expect { execute_service }.not_to raise_error + + expect(Ci::TestCase.count).to eq(1) + expect(Ci::TestCaseFailure.count).to eq(1) + end + end + + context 'when pipeline has no failed builds with test reports' do + before do + create(:ci_build, :test_reports, pipeline: pipeline, project: project) + create(:ci_build, :failed, pipeline: pipeline, project: project) + end + + it 'does not persist data' do + execute_service + + expect(Ci::TestCase.count).to eq(0) + expect(Ci::TestCaseFailure.count).to eq(0) + end + end + end + + describe '#should_track_failures?' do + let(:project) { create(:project, :repository) } + let(:pipeline) { create(:ci_empty_pipeline, status: :created, project: project, ref: project.default_branch) } + + subject { described_class.new(pipeline).should_track_failures? } + + before do + create(:ci_build, :test_reports, :failed, pipeline: pipeline, project: project) + create(:ci_build, :test_reports, :failed, pipeline: pipeline, project: project) + end + + context 'when feature flag is enabled and pipeline ref is the default branch' do + it { is_expected.to eq(true) } + end + + context 'when feature flag is disabled' do + before do + stub_feature_flags(test_failure_history: false) + end + + it { is_expected.to eq(false) } + end + + context 'when pipeline is not equal to the project default branch' do + before do + pipeline.update_column(:ref, 'some-other-branch') + end + + it { is_expected.to eq(false) } + end + + context 'when total number of builds with failed tests exceeds the max number of trackable failures' do + before do + stub_const("#{described_class.name}::MAX_TRACKABLE_FAILURES", 1) + end + + it { is_expected.to eq(false) } + end + end + + describe '#async' do + let(:pipeline) { double(id: 1) } + let(:service) { described_class.new(pipeline) } + + context 'when service should track failures' do + before do + allow(service).to receive(:should_track_failures?).and_return(true) + end + + it 'enqueues the worker when #perform_if_needed is called' do + expect(Ci::TestFailureHistoryWorker).to receive(:perform_async).with(pipeline.id) + + service.async.perform_if_needed + end + end + + context 'when service should not track failures' do + before do + allow(service).to receive(:should_track_failures?).and_return(false) + end + + it 'does not enqueue the worker when #perform_if_needed is called' do + expect(Ci::TestFailureHistoryWorker).not_to receive(:perform_async) + + service.async.perform_if_needed + end + end + end +end diff --git a/spec/services/ci/update_build_state_service_spec.rb b/spec/services/ci/update_build_state_service_spec.rb index 2545909bf56..3112e5dda1b 100644 --- a/spec/services/ci/update_build_state_service_spec.rb +++ b/spec/services/ci/update_build_state_service_spec.rb @@ -80,7 +80,11 @@ RSpec.describe Ci::UpdateBuildStateService do context 'when build has a checksum' do let(:params) do - { checksum: 'crc32:12345678', state: 'failed', failure_reason: 'script_failure' } + { + output: { checksum: 'crc32:12345678', bytesize: 123 }, + failure_reason: 'script_failure', + state: 'failed' + } end context 'when build does not have associated trace chunks' do @@ -154,14 +158,74 @@ RSpec.describe Ci::UpdateBuildStateService do end context 'when trace checksum is valid' do - let(:params) { { checksum: 'crc32:ed82cd11', state: 'success' } } + let(:params) do + { output: { checksum: 'crc32:ed82cd11', bytesize: 4 }, state: 'success' } + end - it 'does not increment invalid trace metric' do + it 'does not increment invalid or corrupted trace metric' do execute_with_stubbed_metrics! expect(metrics) .not_to have_received(:increment_trace_operation) .with(operation: :invalid) + + expect(metrics) + .not_to have_received(:increment_trace_operation) + .with(operation: :corrupted) + end + + context 'when using deprecated parameters' do + let(:params) do + { checksum: 'crc32:ed82cd11', state: 'success' } + end + + it 'does not increment invalid or corrupted trace metric' do + execute_with_stubbed_metrics! + + expect(metrics) + .not_to have_received(:increment_trace_operation) + .with(operation: :invalid) + + expect(metrics) + .not_to have_received(:increment_trace_operation) + .with(operation: :corrupted) + end + end + end + + context 'when trace checksum is invalid and the log is corrupted' do + let(:params) do + { output: { checksum: 'crc32:12345678', bytesize: 1 }, state: 'success' } + end + + it 'increments invalid and corrupted trace metrics' do + execute_with_stubbed_metrics! + + expect(metrics) + .to have_received(:increment_trace_operation) + .with(operation: :invalid) + + expect(metrics) + .to have_received(:increment_trace_operation) + .with(operation: :corrupted) + end + end + + context 'when trace checksum is invalid but the log seems fine' do + let(:params) do + { output: { checksum: 'crc32:12345678', bytesize: 4 }, state: 'success' } + end + + it 'does not increment corrupted trace metric' do + execute_with_stubbed_metrics! + + expect(metrics) + .to have_received(:increment_trace_operation) + .with(operation: :invalid) + + expect(metrics) + .not_to have_received(:increment_trace_operation) + .with(operation: :corrupted) end end |