diff options
Diffstat (limited to 'spec/requests/api/ci/runner/jobs_put_spec.rb')
-rw-r--r-- | spec/requests/api/ci/runner/jobs_put_spec.rb | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/spec/requests/api/ci/runner/jobs_put_spec.rb b/spec/requests/api/ci/runner/jobs_put_spec.rb new file mode 100644 index 00000000000..025747f2f0c --- /dev/null +++ b/spec/requests/api/ci/runner/jobs_put_spec.rb @@ -0,0 +1,196 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do + include StubGitlabCalls + include RedisHelpers + include WorkhorseHelpers + + let(:registration_token) { 'abcdefg123456' } + + before do + stub_feature_flags(ci_enable_live_trace: true) + stub_gitlab_calls + stub_application_setting(runners_registration_token: registration_token) + allow_any_instance_of(::Ci::Runner).to receive(:cache_attributes) + end + + describe '/api/v4/jobs' do + let(:root_namespace) { create(:namespace) } + let(:namespace) { create(:namespace, parent: root_namespace) } + let(:project) { create(:project, namespace: namespace, shared_runners_enabled: false) } + let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master') } + let(:runner) { create(:ci_runner, :project, projects: [project]) } + let(:user) { create(:user) } + let(:job) do + create(:ci_build, :artifacts, :extended_options, + pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) + end + + describe 'PUT /api/v4/jobs/:id' do + let(:job) do + create(:ci_build, :pending, :trace_live, pipeline: pipeline, project: project, user: user, runner_id: runner.id) + end + + before do + job.run! + end + + it_behaves_like 'API::CI::Runner application context metadata', '/api/:version/jobs/:id' do + let(:send_request) { update_job(state: 'success') } + end + + it 'updates runner info' do + expect { update_job(state: 'success') }.to change { runner.reload.contacted_at } + end + + context 'when status is given' do + it 'mark job as succeeded' do + update_job(state: 'success') + + job.reload + expect(job).to be_success + end + + it 'mark job as failed' do + update_job(state: 'failed') + + job.reload + expect(job).to be_failed + expect(job).to be_unknown_failure + end + + context 'when failure_reason is script_failure' do + before do + update_job(state: 'failed', failure_reason: 'script_failure') + job.reload + end + + it { expect(job).to be_script_failure } + end + + context 'when failure_reason is runner_system_failure' do + before do + update_job(state: 'failed', failure_reason: 'runner_system_failure') + job.reload + end + + it { expect(job).to be_runner_system_failure } + end + + context 'when failure_reason is unrecognized value' do + before do + update_job(state: 'failed', failure_reason: 'what_is_this') + job.reload + end + + it { expect(job).to be_unknown_failure } + end + + context 'when failure_reason is job_execution_timeout' do + before do + update_job(state: 'failed', failure_reason: 'job_execution_timeout') + job.reload + end + + it { expect(job).to be_job_execution_timeout } + end + + context 'when failure_reason is unmet_prerequisites' do + before do + update_job(state: 'failed', failure_reason: 'unmet_prerequisites') + job.reload + end + + it { expect(job).to be_unmet_prerequisites } + end + end + + context 'when trace is given' do + it 'creates a trace artifact' do + allow(BuildFinishedWorker).to receive(:perform_async).with(job.id) do + ArchiveTraceWorker.new.perform(job.id) + end + + update_job(state: 'success', trace: 'BUILD TRACE UPDATED') + + job.reload + expect(response).to have_gitlab_http_status(:ok) + expect(job.trace.raw).to eq 'BUILD TRACE UPDATED' + expect(job.job_artifacts_trace.open.read).to eq 'BUILD TRACE UPDATED' + end + + context 'when concurrent update of trace is happening' do + before do + job.trace.write('wb') do + update_job(state: 'success', trace: 'BUILD TRACE UPDATED') + end + end + + it 'returns that operation conflicts' do + expect(response).to have_gitlab_http_status(:conflict) + end + end + end + + context 'when no trace is given' do + it 'does not override trace information' do + update_job + + expect(job.reload.trace.raw).to eq 'BUILD TRACE' + end + + context 'when running state is sent' do + it 'updates update_at value' do + expect { update_job_after_time }.to change { job.reload.updated_at } + end + end + + context 'when other state is sent' do + it "doesn't update update_at value" do + expect { update_job_after_time(20.minutes, state: 'success') }.not_to change { job.reload.updated_at } + end + end + end + + context 'when job has been erased' do + let(:job) { create(:ci_build, runner_id: runner.id, erased_at: Time.now) } + + it 'responds with forbidden' do + update_job + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + + context 'when job has already been finished' do + before do + job.trace.set('Job failed') + job.drop!(:script_failure) + end + + it 'does not update job status and job trace' do + update_job(state: 'success', trace: 'BUILD TRACE UPDATED') + + job.reload + expect(response).to have_gitlab_http_status(:forbidden) + expect(response.header['Job-Status']).to eq 'failed' + expect(job.trace.raw).to eq 'Job failed' + expect(job).to be_failed + end + end + + def update_job(token = job.token, **params) + new_params = params.merge(token: token) + put api("/jobs/#{job.id}"), params: new_params + end + + def update_job_after_time(update_interval = 20.minutes, state = 'running') + Timecop.travel(job.updated_at + update_interval) do + update_job(job.token, state: state) + end + end + end + end +end |