Skip to content
Snippets Groups Projects
builds_spec.rb 14.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • require 'spec_helper'
    
    
    describe API::Builds, api: true do
    
      include ApiHelpers
    
      let(:user) { create(:user) }
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
      let(:api_user) { user }
    
      let!(:project) { create(:project, :repository, creator: user, public_builds: false) }
    
      let!(:developer) { create(:project_member, :developer, user: user, project: project) }
    
      let(:reporter) { create(:project_member, :reporter, project: project) }
      let(:guest) { create(:project_member, :guest, project: project) }
    
      let!(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id, ref: project.default_branch) }
    
      let!(:build) { create(:ci_build, pipeline: pipeline) }
    
    
      describe 'GET /projects/:id/builds ' do
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
        let(:query) { '' }
    
    
    Lin Jen-Shin's avatar
    Lin Jen-Shin committed
        before do
    
          create(:ci_build, :skipped, pipeline: pipeline)
    
    
    Lin Jen-Shin's avatar
    Lin Jen-Shin committed
          get api("/projects/#{project.id}/builds?#{query}", api_user)
        end
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
    
    
        context 'authorized user' do
    
          it 'returns project builds' do
    
            expect(response).to have_http_status(200)
    
            expect(response).to include_pagination_headers
    
            expect(json_response).to be_an Array
          end
    
    
          it 'returns correct values' do
            expect(json_response).not_to be_empty
            expect(json_response.first['commit']['id']).to eq project.commit.id
          end
    
    
          it 'returns pipeline data' do
            json_build = json_response.first
            expect(json_build['pipeline']).not_to be_empty
            expect(json_build['pipeline']['id']).to eq build.pipeline.id
            expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
            expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
            expect(json_build['pipeline']['status']).to eq build.pipeline.status
          end
    
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
          context 'filter project with one scope element' do
            let(:query) { 'scope=pending' }
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
            it do
    
              expect(response).to have_http_status(200)
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
              expect(json_response).to be_an Array
            end
    
          context 'filter project with scope skipped' do
            let(:query) { 'scope=skipped' }
            let(:json_build) { json_response.first }
    
            it 'return builds with status skipped' do
              expect(response).to have_http_status 200
              expect(json_response).to be_an Array
              expect(json_response.length).to eq 1
              expect(json_build['status']).to eq 'skipped'
            end
          end
    
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
          context 'filter project with array of scope elements' do
            let(:query) { 'scope[0]=pending&scope[1]=running' }
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
            it do
    
              expect(response).to have_http_status(200)
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
              expect(json_response).to be_an Array
            end
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
          context 'respond 400 when scope contains invalid state' do
            let(:query) { 'scope[0]=pending&scope[1]=unknown_status' }
    
            it { expect(response).to have_http_status(400) }
    
        end
    
        context 'unauthorized user' do
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
          let(:api_user) { nil }
    
          it 'does not return project builds' do
    
            expect(response).to have_http_status(401)
    
      describe 'GET /projects/:id/repository/commits/:sha/builds' do
    
        context 'when commit does not exist in repository' do
          before do
            get api("/projects/#{project.id}/repository/commits/1a271fd1/builds", api_user)
          end
    
          it 'responds with 404' do
            expect(response).to have_http_status(404)
          end
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
        end
    
    
        context 'when commit exists in repository' do
          context 'when user is authorized' do
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
            context 'when pipeline has jobs' do
    
                create(:ci_pipeline, project: project, sha: project.commit.id)
    
                create(:ci_build, pipeline: pipeline)
    
                create(:ci_build)
    
    
                get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", api_user)
              end
    
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
              it 'returns project jobs for specific commit' do
    
                expect(response).to have_http_status(200)
    
                expect(response).to include_pagination_headers
    
                expect(json_response).to be_an Array
                expect(json_response.size).to eq 2
              end
    
    
              it 'returns pipeline data' do
                json_build = json_response.first
                expect(json_build['pipeline']).not_to be_empty
                expect(json_build['pipeline']['id']).to eq build.pipeline.id
                expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
                expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
                expect(json_build['pipeline']['status']).to eq build.pipeline.status
              end
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
            context 'when pipeline has no jobs' do
    
              before do
                branch_head = project.commit('feature').id
                get api("/projects/#{project.id}/repository/commits/#{branch_head}/builds", api_user)
              end
    
              it 'returns an empty array' do
                expect(response).to have_http_status(200)
                expect(json_response).to be_an Array
                expect(json_response).to be_empty
              end
            end
    
          context 'when user is not authorized' do
            before do
    
              create(:ci_pipeline, project: project, sha: project.commit.id)
              create(:ci_build, pipeline: pipeline)
    
              get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", nil)
            end
    
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
            it 'does not return project jobs' do
    
              expect(response).to have_http_status(401)
              expect(json_response.except('message')).to be_empty
            end
    
      describe 'GET /projects/:id/builds/:build_id' do
    
    Lin Jen-Shin's avatar
    Lin Jen-Shin committed
        before do
          get api("/projects/#{project.id}/builds/#{build.id}", api_user)
        end
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
    
    
        context 'authorized user' do
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
          it 'returns specific job data' do
    
            expect(response).to have_http_status(200)
    
            expect(json_response['name']).to eq('test')
          end
    
    
          it 'returns pipeline data' do
            json_build = json_response
            expect(json_build['pipeline']).not_to be_empty
            expect(json_build['pipeline']['id']).to eq build.pipeline.id
            expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
            expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
            expect(json_build['pipeline']['status']).to eq build.pipeline.status
          end
    
        end
    
        context 'unauthorized user' do
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
          let(:api_user) { nil }
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
          it 'does not return specific job data' do
    
            expect(response).to have_http_status(401)
    
      describe 'GET /projects/:id/builds/:build_id/artifacts' do
    
    Lin Jen-Shin's avatar
    Lin Jen-Shin committed
        before do
          get api("/projects/#{project.id}/builds/#{build.id}/artifacts", api_user)
        end
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
        context 'job with artifacts' do
    
          let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
          context 'authorized user' do
            let(:download_headers) do
    
              { 'Content-Transfer-Encoding' => 'binary',
                'Content-Disposition' => 'attachment; filename=ci_build_artifacts.zip' }
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
            end
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
            it 'returns specific job artifacts' do
    
              expect(response).to have_http_status(200)
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
              expect(response.headers).to include(download_headers)
    
              expect(response.body).to match_file(build.artifacts_file.file.file)
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
            end
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
          context 'unauthorized user' do
            let(:api_user) { nil }
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
            it 'does not return specific job artifacts' do
    
              expect(response).to have_http_status(401)
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
            end
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
    
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
        it 'does not return job artifacts if not uploaded' do
    
          expect(response).to have_http_status(404)
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
        end
    
      describe 'GET /projects/:id/artifacts/:ref_name/download?job=name' do
    
        let(:api_user) { reporter.user }
    
        let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
    
        before do
          build.success
        end
    
        def path_for_ref(ref = pipeline.ref, job = build.name)
    
          api("/projects/#{project.id}/builds/artifacts/#{ref}/download?job=#{job}", api_user)
    
        context 'when not logged in' do
    
          let(:api_user) { nil }
    
            get path_for_ref
    
          it 'gives 401' do
    
            expect(response).to have_http_status(401)
          end
        end
    
    
        context 'when logging as guest' do
    
          let(:api_user) { guest.user }
    
    
          before do
            get path_for_ref
          end
    
          it 'gives 403' do
            expect(response).to have_http_status(403)
          end
        end
    
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
        context 'non-existing job' do
    
          shared_examples 'not found' do
            it { expect(response).to have_http_status(:not_found) }
    
          context 'has no such ref' do
            before do
    
              get path_for_ref('TAIL', build.name)
    
            it_behaves_like 'not found'
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
          context 'has no such job' do
    
              get path_for_ref(pipeline.ref, 'NOBUILD')
    
            it_behaves_like 'not found'
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
        context 'find proper job' do
    
          shared_examples 'a valid file' do
    
            let(:download_headers) do
    
              { 'Content-Transfer-Encoding' => 'binary',
                'Content-Disposition' =>
                  "attachment; filename=#{build.artifacts_file.filename}" }
    
            it { expect(response).to have_http_status(200) }
            it { expect(response.headers).to include(download_headers) }
    
          end
    
          context 'with regular branch' do
            before do
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
              pipeline.reload
    
              pipeline.update(ref: 'master',
                              sha: project.commit('master').sha)
    
    
              get path_for_ref('master')
    
            it_behaves_like 'a valid file'
    
          context 'with branch name containing slash' do
            before do
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
              pipeline.reload
    
              pipeline.update(ref: 'improve/awesome',
                              sha: project.commit('improve/awesome').sha)
            end
    
            before do
    
              get path_for_ref('improve/awesome')
    
            it_behaves_like 'a valid file'
    
      describe 'GET /projects/:id/builds/:build_id/trace' do
    
        let(:build) { create(:ci_build, :trace, pipeline: pipeline) }
    
    Lin Jen-Shin's avatar
    Lin Jen-Shin committed
        before do
          get api("/projects/#{project.id}/builds/#{build.id}/trace", api_user)
        end
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
    
    
        context 'authorized user' do
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
          it 'returns specific job trace' do
    
            expect(response).to have_http_status(200)
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
            expect(response.body).to eq(build.trace)
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
          let(:api_user) { nil }
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
          it 'does not return specific job trace' do
    
            expect(response).to have_http_status(401)
    
      describe 'POST /projects/:id/builds/:build_id/cancel' do
    
    Lin Jen-Shin's avatar
    Lin Jen-Shin committed
        before do
          post api("/projects/#{project.id}/builds/#{build.id}/cancel", api_user)
        end
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
    
    
        context 'authorized user' do
    
          context 'user with :update_build persmission' do
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
            it 'cancels running or pending job' do
    
              expect(response).to have_http_status(201)
    
              expect(project.builds.first.status).to eq('canceled')
            end
          end
    
    
          context 'user without :update_build permission' do
    
            let(:api_user) { reporter.user }
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
            it 'does not cancel job' do
    
              expect(response).to have_http_status(403)
    
            end
          end
        end
    
        context 'unauthorized user' do
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
          let(:api_user) { nil }
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
          it 'does not cancel job' do
    
            expect(response).to have_http_status(401)
    
      describe 'POST /projects/:id/builds/:build_id/retry' do
    
        let(:build) { create(:ci_build, :canceled, pipeline: pipeline) }
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
    
    
    Lin Jen-Shin's avatar
    Lin Jen-Shin committed
        before do
          post api("/projects/#{project.id}/builds/#{build.id}/retry", api_user)
        end
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
    
    
        context 'authorized user' do
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
          context 'user with :update_build permission' do
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
            it 'retries non-running job' do
    
              expect(response).to have_http_status(201)
    
              expect(project.builds.first.status).to eq('canceled')
              expect(json_response['status']).to eq('pending')
            end
          end
    
    
          context 'user without :update_build permission' do
    
            let(:api_user) { reporter.user }
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
            it 'does not retry job' do
    
              expect(response).to have_http_status(403)
    
            end
          end
        end
    
        context 'unauthorized user' do
    
    Kamil Trzcinski's avatar
    Kamil Trzcinski committed
          let(:api_user) { nil }
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
          it 'does not retry job' do
    
            expect(response).to have_http_status(401)
    
      describe 'POST /projects/:id/builds/:build_id/erase' do
    
          post api("/projects/#{project.id}/builds/#{build.id}/erase", user)
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
        context 'job is erasable' do
    
          let(:build) { create(:ci_build, :trace, :artifacts, :success, project: project, pipeline: pipeline) }
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
          it 'erases job content' do
    
            expect(response.status).to eq 201
    
            expect(build.trace).to be_empty
            expect(build.artifacts_file.exists?).to be_falsy
            expect(build.artifacts_metadata.exists?).to be_falsy
          end
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
          it 'updates job' do
    
            expect(build.reload.erased_at).to be_truthy
            expect(build.reload.erased_by).to eq user
          end
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
        context 'job is not erasable' do
    
          let(:build) { create(:ci_build, :trace, project: project, pipeline: pipeline) }
    
          it 'responds with forbidden' do
    
            expect(response.status).to eq 403
          end
        end
      end
    
    
      describe 'POST /projects/:id/builds/:build_id/artifacts/keep' do
        before do
          post api("/projects/#{project.id}/builds/#{build.id}/artifacts/keep", user)
        end
    
        context 'artifacts did not expire' do
          let(:build) do
            create(:ci_build, :trace, :artifacts, :success,
                   project: project, pipeline: pipeline, artifacts_expire_at: Time.now + 7.days)
          end
    
    
          it 'keeps artifacts' do
    
            expect(response.status).to eq 200
    
            expect(build.reload.artifacts_expire_at).to be_nil
    
          end
        end
    
        context 'no artifacts' do
          let(:build) { create(:ci_build, project: project, pipeline: pipeline) }
    
    
          it 'responds with not found' do
    
            expect(response.status).to eq 404
          end
        end
      end
    
    
      describe 'POST /projects/:id/builds/:build_id/play' do
        before do
          post api("/projects/#{project.id}/builds/#{build.id}/play", user)
        end
    
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
        context 'on an playable job' do
    
          let(:build) { create(:ci_build, :manual, project: project, pipeline: pipeline) }
    
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
          it 'plays the job' do
    
            expect(response).to have_http_status 200
            expect(json_response['user']['id']).to eq(user.id)
    
            expect(json_response['id']).to eq(build.id)
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
        context 'on a non-playable job' do
    
          it 'returns a status code 400, Bad Request' do
            expect(response).to have_http_status 400
    
    Filipa Lacerda's avatar
    Filipa Lacerda committed
            expect(response.body).to match("Unplayable Job")