summaryrefslogtreecommitdiff
path: root/spec/models/ci/build_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models/ci/build_spec.rb')
-rw-r--r--spec/models/ci/build_spec.rb228
1 files changed, 203 insertions, 25 deletions
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index b9a12339e61..b8c5af5a911 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -565,6 +565,26 @@ RSpec.describe Ci::Build do
expect(build.reload.runtime_metadata).not_to be_present
end
end
+
+ context 'when a failure reason is provided' do
+ context 'when a failure reason is a symbol' do
+ it 'correctly sets a failure reason' do
+ build.drop!(:script_failure)
+
+ expect(build.failure_reason).to eq 'script_failure'
+ end
+ end
+
+ context 'when a failure reason is an object' do
+ it 'correctly sets a failure reason' do
+ reason = ::Gitlab::Ci::Build::Status::Reason.new(build, :script_failure)
+
+ build.drop!(reason)
+
+ expect(build.failure_reason).to eq 'script_failure'
+ end
+ end
+ end
end
describe '#schedulable?' do
@@ -2002,6 +2022,16 @@ RSpec.describe Ci::Build do
it { is_expected.not_to be_retryable }
end
+
+ context 'when build is waiting for deployment approval' do
+ subject { build_stubbed(:ci_build, :manual, environment: 'production') }
+
+ before do
+ create(:deployment, :blocked, deployable: subject)
+ end
+
+ it { is_expected.not_to be_retryable }
+ end
end
end
@@ -2064,6 +2094,31 @@ RSpec.describe Ci::Build do
end
describe 'build auto retry feature' do
+ context 'with deployment job' do
+ let(:build) do
+ create(:ci_build, :deploy_to_production, :with_deployment,
+ user: user, pipeline: pipeline, project: project)
+ end
+
+ before do
+ project.add_developer(user)
+ allow(build).to receive(:auto_retry_allowed?) { true }
+ end
+
+ it 'creates a deployment when a build is dropped' do
+ expect { build.drop!(:script_failure) }.to change { Deployment.count }.by(1)
+
+ retried_deployment = Deployment.last
+ expect(build.deployment.environment).to eq(retried_deployment.environment)
+ expect(build.deployment.ref).to eq(retried_deployment.ref)
+ expect(build.deployment.sha).to eq(retried_deployment.sha)
+ expect(build.deployment.tag).to eq(retried_deployment.tag)
+ expect(build.deployment.user).to eq(retried_deployment.user)
+ expect(build.deployment).to be_failed
+ expect(retried_deployment).to be_created
+ end
+ end
+
describe '#retries_count' do
subject { create(:ci_build, name: 'test', pipeline: pipeline) }
@@ -2152,6 +2207,28 @@ RSpec.describe Ci::Build do
end
end
+ describe '#auto_retry_expected?' do
+ subject { create(:ci_build, :failed) }
+
+ context 'when build is failed and auto retry is configured' do
+ before do
+ allow(subject)
+ .to receive(:auto_retry_allowed?)
+ .and_return(true)
+ end
+
+ it 'expects auto-retry to happen' do
+ expect(subject.auto_retry_expected?).to be true
+ end
+ end
+
+ context 'when build failed by auto retry is not configured' do
+ it 'does not expect auto-retry to happen' do
+ expect(subject.auto_retry_expected?).to be false
+ end
+ end
+ end
+
describe '#artifacts_file_for_type' do
let(:build) { create(:ci_build, :artifacts) }
let(:file_type) { :archive }
@@ -2443,6 +2520,16 @@ RSpec.describe Ci::Build do
it { is_expected.not_to be_playable }
end
+
+ context 'when build is waiting for deployment approval' do
+ subject { build_stubbed(:ci_build, :manual, environment: 'production') }
+
+ before do
+ create(:deployment, :blocked, deployable: subject)
+ end
+
+ it { is_expected.not_to be_playable }
+ end
end
describe 'project settings' do
@@ -2653,6 +2740,8 @@ RSpec.describe Ci::Build do
{ key: 'CI_DEPENDENCY_PROXY_USER', value: 'gitlab-ci-token', public: true, masked: false },
{ key: 'CI_DEPENDENCY_PROXY_PASSWORD', value: 'my-token', public: false, masked: true },
{ key: 'CI_JOB_JWT', value: 'ci.job.jwt', public: false, masked: true },
+ { key: 'CI_JOB_JWT_V1', value: 'ci.job.jwt', public: false, masked: true },
+ { key: 'CI_JOB_JWT_V2', value: 'ci.job.jwtv2', public: false, masked: true },
{ key: 'CI_JOB_NAME', value: 'test', public: true, masked: false },
{ key: 'CI_JOB_STAGE', value: 'test', public: true, masked: false },
{ key: 'CI_NODE_TOTAL', value: '1', public: true, masked: false },
@@ -2720,6 +2809,7 @@ RSpec.describe Ci::Build do
before do
allow(Gitlab::Ci::Jwt).to receive(:for_build).and_return('ci.job.jwt')
+ allow(Gitlab::Ci::JwtV2).to receive(:for_build).and_return('ci.job.jwtv2')
build.set_token('my-token')
build.yaml_variables = []
end
@@ -2771,6 +2861,8 @@ RSpec.describe Ci::Build do
let(:build_yaml_var) { { key: 'yaml', value: 'value', public: true, masked: false } }
let(:dependency_proxy_var) { { key: 'dependency_proxy', value: 'value', public: true, masked: false } }
let(:job_jwt_var) { { key: 'CI_JOB_JWT', value: 'ci.job.jwt', public: false, masked: true } }
+ let(:job_jwt_var_v1) { { key: 'CI_JOB_JWT_V1', value: 'ci.job.jwt', public: false, masked: true } }
+ let(:job_jwt_var_v2) { { key: 'CI_JOB_JWT_V2', value: 'ci.job.jwtv2', public: false, masked: true } }
let(:job_dependency_var) { { key: 'job_dependency', value: 'value', public: true, masked: false } }
before do
@@ -2784,7 +2876,7 @@ RSpec.describe Ci::Build do
allow(build).to receive(:dependency_variables) { [job_dependency_var] }
allow(build).to receive(:dependency_proxy_variables) { [dependency_proxy_var] }
- allow(build.project)
+ allow(build.pipeline.project)
.to receive(:predefined_variables) { [project_pre_var] }
project.variables.create!(key: 'secret', value: 'value')
@@ -3084,7 +3176,7 @@ RSpec.describe Ci::Build do
context 'when the branch is protected' do
before do
- allow(build.project).to receive(:protected_for?).with(ref).and_return(true)
+ allow(build.pipeline.project).to receive(:protected_for?).with(ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
@@ -3092,7 +3184,7 @@ RSpec.describe Ci::Build do
context 'when the tag is protected' do
before do
- allow(build.project).to receive(:protected_for?).with(ref).and_return(true)
+ allow(build.pipeline.project).to receive(:protected_for?).with(ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
@@ -3131,7 +3223,7 @@ RSpec.describe Ci::Build do
context 'when the branch is protected' do
before do
- allow(build.project).to receive(:protected_for?).with(ref).and_return(true)
+ allow(build.pipeline.project).to receive(:protected_for?).with(ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
@@ -3139,7 +3231,7 @@ RSpec.describe Ci::Build do
context 'when the tag is protected' do
before do
- allow(build.project).to receive(:protected_for?).with(ref).and_return(true)
+ allow(build.pipeline.project).to receive(:protected_for?).with(ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
@@ -3526,6 +3618,20 @@ RSpec.describe Ci::Build do
build.scoped_variables
end
+
+ context 'when variables builder is used' do
+ it 'returns the same variables' do
+ build.user = create(:user)
+
+ allow(build.pipeline).to receive(:use_variables_builder_definitions?).and_return(false)
+ legacy_variables = build.scoped_variables.to_hash
+
+ allow(build.pipeline).to receive(:use_variables_builder_definitions?).and_return(true)
+ new_variables = build.scoped_variables.to_hash
+
+ expect(new_variables).to eq(legacy_variables)
+ end
+ end
end
describe '#simple_variables_without_dependencies' do
@@ -3538,7 +3644,8 @@ RSpec.describe Ci::Build do
shared_examples "secret CI variables" do
context 'when ref is branch' do
- let(:build) { create(:ci_build, ref: 'master', tag: false, project: project) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, ref: 'master', tag: false, pipeline: pipeline, project: project) }
context 'when ref is protected' do
before do
@@ -3554,7 +3661,8 @@ RSpec.describe Ci::Build do
end
context 'when ref is tag' do
- let(:build) { create(:ci_build, ref: 'v1.1.0', tag: true, project: project) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, ref: 'v1.1.0', tag: true, pipeline: pipeline, project: project) }
context 'when ref is protected' do
before do
@@ -3652,8 +3760,6 @@ RSpec.describe Ci::Build do
.and_return(project_variables)
end
- it { is_expected.to eq(project_variables) }
-
context 'environment is nil' do
let(:environment) { nil }
@@ -3661,6 +3767,35 @@ RSpec.describe Ci::Build do
end
end
+ describe '#user_variables' do
+ subject { build.user_variables.to_hash }
+
+ context 'with user' do
+ let(:expected_variables) do
+ {
+ 'GITLAB_USER_EMAIL' => user.email,
+ 'GITLAB_USER_ID' => user.id.to_s,
+ 'GITLAB_USER_LOGIN' => user.username,
+ 'GITLAB_USER_NAME' => user.name
+ }
+ end
+
+ before do
+ build.user = user
+ end
+
+ it { is_expected.to eq(expected_variables) }
+ end
+
+ context 'without user' do
+ before do
+ expect(build).to receive(:user).and_return(nil)
+ end
+
+ it { is_expected.to be_empty }
+ end
+ end
+
describe '#any_unmet_prerequisites?' do
let(:build) { create(:ci_build, :created) }
@@ -3762,6 +3897,18 @@ RSpec.describe Ci::Build do
end
end
+ describe 'when the build is waiting for deployment approval' do
+ let(:build) { create(:ci_build, :manual, environment: 'production') }
+
+ before do
+ create(:deployment, :blocked, deployable: build)
+ end
+
+ it 'does not allow the build to be enqueued' do
+ expect { build.enqueue! }.to raise_error(StateMachines::InvalidTransition)
+ end
+ end
+
describe 'state transition: any => [:pending]' do
let(:build) { create(:ci_build, :created) }
@@ -5174,25 +5321,32 @@ RSpec.describe Ci::Build do
.to change { build.reload.failed? }
end
- it 'is executed inside a transaction' do
- expect(build).to receive(:drop!)
- .with(:unknown_failure)
- .and_raise(ActiveRecord::Rollback)
-
- expect(build).to receive(:conditionally_allow_failure!)
- .with(1)
- .and_call_original
-
- expect { drop_with_exit_code }
- .not_to change { build.reload.allow_failure }
- end
-
context 'when exit_code is nil' do
let(:exit_code) {}
it_behaves_like 'drops the build without changing allow_failure'
end
end
+
+ context 'when build is configured to be retried' do
+ let(:options) { { retry: 3 } }
+
+ context 'when there is an MR attached to the pipeline and a failed job todo for that MR' do
+ let!(:merge_request) { create(:merge_request, source_project: project, author: user, head_pipeline: pipeline) }
+ let!(:todo) { create(:todo, :build_failed, user: user, project: project, author: user, target: merge_request) }
+
+ before do
+ build.update!(user: user)
+ project.add_developer(user)
+ end
+
+ it 'resolves the todo for the old failed build' do
+ expect do
+ drop_with_exit_code
+ end.to change { todo.reload.state }.from('pending').to('done')
+ end
+ end
+ end
end
describe '#exit_codes_defined?' do
@@ -5377,7 +5531,8 @@ RSpec.describe Ci::Build do
describe '#doom!' do
subject { build.doom! }
- let_it_be(:build) { create(:ci_build, :queued) }
+ let(:traits) { [] }
+ let(:build) { create(:ci_build, *traits, pipeline: pipeline) }
it 'updates status and failure_reason', :aggregate_failures do
subject
@@ -5386,10 +5541,33 @@ RSpec.describe Ci::Build do
expect(build.failure_reason).to eq("data_integrity_failure")
end
- it 'drops associated pending build' do
+ it 'logs a message' do
+ expect(Gitlab::AppLogger)
+ .to receive(:info)
+ .with(a_hash_including(message: 'Build doomed', class: build.class.name, build_id: build.id))
+ .and_call_original
+
subject
+ end
+
+ context 'with queued builds' do
+ let(:traits) { [:queued] }
+
+ it 'drops associated pending build' do
+ subject
- expect(build.reload.queuing_entry).not_to be_present
+ expect(build.reload.queuing_entry).not_to be_present
+ end
+ end
+
+ context 'with running builds' do
+ let(:traits) { [:picked] }
+
+ it 'drops associated runtime metadata' do
+ subject
+
+ expect(build.reload.runtime_metadata).not_to be_present
+ end
end
end