2021-09-30 23:02:18 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require 'spec_helper'
|
|
|
|
|
2023-05-27 22:25:52 +05:30
|
|
|
RSpec.describe Ci::Pipelines::AddJobService, feature_category: :continuous_integration do
|
2021-10-27 15:23:28 +05:30
|
|
|
include ExclusiveLeaseHelpers
|
|
|
|
|
2023-03-04 22:38:38 +05:30
|
|
|
let_it_be_with_reload(:pipeline) { create(:ci_pipeline) }
|
2021-09-30 23:02:18 +05:30
|
|
|
|
|
|
|
let(:job) { build(:ci_build) }
|
|
|
|
|
|
|
|
subject(:service) { described_class.new(pipeline) }
|
|
|
|
|
|
|
|
context 'when the pipeline is not persisted' do
|
|
|
|
let(:pipeline) { build(:ci_pipeline) }
|
|
|
|
|
|
|
|
it 'raises error' do
|
|
|
|
expect { service }.to raise_error('Pipeline must be persisted for this service to be used')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#execute!' do
|
|
|
|
subject(:execute) do
|
|
|
|
service.execute!(job) do |job|
|
|
|
|
job.save!
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'assigns pipeline attributes to the job' do
|
|
|
|
expect do
|
|
|
|
execute
|
|
|
|
end.to change { job.slice(:pipeline, :project, :ref) }.to(
|
|
|
|
pipeline: pipeline, project: pipeline.project, ref: pipeline.ref
|
2022-03-02 08:16:31 +05:30
|
|
|
).and change { job.metadata.project }.to(pipeline.project)
|
2021-09-30 23:02:18 +05:30
|
|
|
end
|
|
|
|
|
2022-10-11 01:57:18 +05:30
|
|
|
it 'assigns partition_id to job and metadata' do
|
2023-03-04 22:38:38 +05:30
|
|
|
pipeline.partition_id = ci_testing_partition_id
|
2022-10-11 01:57:18 +05:30
|
|
|
|
|
|
|
expect { execute }
|
|
|
|
.to change(job, :partition_id).to(pipeline.partition_id)
|
|
|
|
.and change { job.metadata.partition_id }.to(pipeline.partition_id)
|
|
|
|
end
|
|
|
|
|
2021-09-30 23:02:18 +05:30
|
|
|
it 'returns a service response with the job as payload' do
|
|
|
|
expect(execute).to be_success
|
|
|
|
expect(execute.payload[:job]).to eq(job)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'calls update_older_statuses_retried!' do
|
|
|
|
expect(job).to receive(:update_older_statuses_retried!)
|
|
|
|
|
|
|
|
execute
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when the block raises an error' do
|
|
|
|
subject(:execute) do
|
|
|
|
service.execute!(job) do |job|
|
|
|
|
raise "this is an error"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns a service response with the error and the job as payload' do
|
|
|
|
expect(execute).to be_error
|
|
|
|
expect(execute.payload[:job]).to eq(job)
|
|
|
|
expect(execute.message).to eq('this is an error')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-10-27 15:23:28 +05:30
|
|
|
context 'exclusive lock' do
|
|
|
|
let(:lock_uuid) { 'test' }
|
|
|
|
let(:lock_key) { "ci:pipelines:#{pipeline.id}:add-job" }
|
|
|
|
let(:lock_timeout) { 1.minute }
|
|
|
|
|
|
|
|
before do
|
|
|
|
# "Please stub a default value first if message might be received with other args as well."
|
|
|
|
allow(Gitlab::ExclusiveLease).to receive(:new).and_call_original
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'uses exclusive lock' do
|
|
|
|
lease = stub_exclusive_lease(lock_key, lock_uuid, timeout: lock_timeout)
|
|
|
|
expect(lease).to receive(:try_obtain)
|
|
|
|
expect(lease).to receive(:cancel)
|
|
|
|
|
|
|
|
expect(execute).to be_success
|
|
|
|
expect(execute.payload[:job]).to eq(job)
|
|
|
|
end
|
|
|
|
end
|
2023-07-09 08:55:56 +05:30
|
|
|
|
|
|
|
it 'locks pipelines and stages before persisting builds', :aggregate_failures do
|
|
|
|
expect(job).not_to be_persisted
|
|
|
|
|
|
|
|
recorder = ActiveRecord::QueryRecorder.new(skip_cached: false) { execute }
|
|
|
|
entries = recorder.log.select { |query| query.match(/LOCK|INSERT INTO ".{0,2}ci_builds"/) }
|
|
|
|
|
|
|
|
expect(entries.size).to eq(2)
|
|
|
|
expect(entries.first).to match(/LOCK "ci_pipelines", "ci_stages" IN ROW SHARE MODE;/)
|
|
|
|
end
|
2021-09-30 23:02:18 +05:30
|
|
|
end
|
|
|
|
end
|