e80e4cb8b9
add pipeline ref, sha, and status to the build API response add tests of build API (pipeline data) change API documentation for builds API log change to builds API in CHANGELOG CHANGELOG: add reference to pull request and contributor's name
470 lines
14 KiB
Ruby
470 lines
14 KiB
Ruby
require 'spec_helper'
|
|
|
|
describe API::API, api: true do
|
|
include ApiHelpers
|
|
|
|
let(:user) { create(:user) }
|
|
let(:api_user) { user }
|
|
let!(:project) { create(:project, creator_id: user.id) }
|
|
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
|
|
let(:query) { '' }
|
|
|
|
before do
|
|
get api("/projects/#{project.id}/builds?#{query}", api_user)
|
|
end
|
|
|
|
context 'authorized user' do
|
|
it 'returns project builds' do
|
|
expect(response).to have_http_status(200)
|
|
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
|
|
|
|
context 'filter project with one scope element' do
|
|
let(:query) { 'scope=pending' }
|
|
|
|
it do
|
|
expect(response).to have_http_status(200)
|
|
expect(json_response).to be_an Array
|
|
end
|
|
end
|
|
|
|
context 'filter project with array of scope elements' do
|
|
let(:query) { 'scope[0]=pending&scope[1]=running' }
|
|
|
|
it do
|
|
expect(response).to have_http_status(200)
|
|
expect(json_response).to be_an Array
|
|
end
|
|
end
|
|
|
|
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
|
|
end
|
|
|
|
context 'unauthorized user' do
|
|
let(:api_user) { nil }
|
|
|
|
it 'should not return project builds' do
|
|
expect(response).to have_http_status(401)
|
|
end
|
|
end
|
|
end
|
|
|
|
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
|
|
end
|
|
|
|
context 'when commit exists in repository' do
|
|
context 'when user is authorized' do
|
|
context 'when pipeline has builds' do
|
|
before 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
|
|
|
|
it 'returns project builds for specific commit' do
|
|
expect(response).to have_http_status(200)
|
|
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
|
|
end
|
|
|
|
context 'when pipeline has no builds' 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
|
|
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
|
|
|
|
it 'does not return project builds' do
|
|
expect(response).to have_http_status(401)
|
|
expect(json_response.except('message')).to be_empty
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'GET /projects/:id/builds/:build_id' do
|
|
before do
|
|
get api("/projects/#{project.id}/builds/#{build.id}", api_user)
|
|
end
|
|
|
|
context 'authorized user' do
|
|
it 'returns specific build 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
|
|
let(:api_user) { nil }
|
|
|
|
it 'does not return specific build data' do
|
|
expect(response).to have_http_status(401)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'GET /projects/:id/builds/:build_id/artifacts' do
|
|
before do
|
|
get api("/projects/#{project.id}/builds/#{build.id}/artifacts", api_user)
|
|
end
|
|
|
|
context 'build with artifacts' do
|
|
let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
|
|
|
|
context 'authorized user' do
|
|
let(:download_headers) do
|
|
{ 'Content-Transfer-Encoding' => 'binary',
|
|
'Content-Disposition' => 'attachment; filename=ci_build_artifacts.zip' }
|
|
end
|
|
|
|
it 'returns specific build artifacts' do
|
|
expect(response).to have_http_status(200)
|
|
expect(response.headers).to include(download_headers)
|
|
end
|
|
end
|
|
|
|
context 'unauthorized user' do
|
|
let(:api_user) { nil }
|
|
|
|
it 'does not return specific build artifacts' do
|
|
expect(response).to have_http_status(401)
|
|
end
|
|
end
|
|
end
|
|
|
|
it 'does not return build artifacts if not uploaded' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
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)
|
|
end
|
|
|
|
context 'when not logged in' do
|
|
let(:api_user) { nil }
|
|
|
|
before do
|
|
get path_for_ref
|
|
end
|
|
|
|
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
|
|
|
|
context 'non-existing build' do
|
|
shared_examples 'not found' do
|
|
it { expect(response).to have_http_status(:not_found) }
|
|
end
|
|
|
|
context 'has no such ref' do
|
|
before do
|
|
get path_for_ref('TAIL', build.name)
|
|
end
|
|
|
|
it_behaves_like 'not found'
|
|
end
|
|
|
|
context 'has no such build' do
|
|
before do
|
|
get path_for_ref(pipeline.ref, 'NOBUILD')
|
|
end
|
|
|
|
it_behaves_like 'not found'
|
|
end
|
|
end
|
|
|
|
context 'find proper build' do
|
|
shared_examples 'a valid file' do
|
|
let(:download_headers) do
|
|
{ 'Content-Transfer-Encoding' => 'binary',
|
|
'Content-Disposition' =>
|
|
"attachment; filename=#{build.artifacts_file.filename}" }
|
|
end
|
|
|
|
it { expect(response).to have_http_status(200) }
|
|
it { expect(response.headers).to include(download_headers) }
|
|
end
|
|
|
|
context 'with regular branch' do
|
|
before do
|
|
pipeline.update(ref: 'master',
|
|
sha: project.commit('master').sha)
|
|
|
|
get path_for_ref('master')
|
|
end
|
|
|
|
it_behaves_like 'a valid file'
|
|
end
|
|
|
|
context 'with branch name containing slash' do
|
|
before do
|
|
pipeline.update(ref: 'improve/awesome',
|
|
sha: project.commit('improve/awesome').sha)
|
|
end
|
|
|
|
before do
|
|
get path_for_ref('improve/awesome')
|
|
end
|
|
|
|
it_behaves_like 'a valid file'
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'GET /projects/:id/builds/:build_id/trace' do
|
|
let(:build) { create(:ci_build, :trace, pipeline: pipeline) }
|
|
|
|
before do
|
|
get api("/projects/#{project.id}/builds/#{build.id}/trace", api_user)
|
|
end
|
|
|
|
context 'authorized user' do
|
|
it 'returns specific build trace' do
|
|
expect(response).to have_http_status(200)
|
|
expect(response.body).to eq(build.trace)
|
|
end
|
|
end
|
|
|
|
context 'unauthorized user' do
|
|
let(:api_user) { nil }
|
|
|
|
it 'does not return specific build trace' do
|
|
expect(response).to have_http_status(401)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'POST /projects/:id/builds/:build_id/cancel' do
|
|
before do
|
|
post api("/projects/#{project.id}/builds/#{build.id}/cancel", api_user)
|
|
end
|
|
|
|
context 'authorized user' do
|
|
context 'user with :update_build persmission' do
|
|
it 'cancels running or pending build' 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 }
|
|
|
|
it 'does not cancel build' do
|
|
expect(response).to have_http_status(403)
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'unauthorized user' do
|
|
let(:api_user) { nil }
|
|
|
|
it 'does not cancel build' do
|
|
expect(response).to have_http_status(401)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'POST /projects/:id/builds/:build_id/retry' do
|
|
let(:build) { create(:ci_build, :canceled, pipeline: pipeline) }
|
|
|
|
before do
|
|
post api("/projects/#{project.id}/builds/#{build.id}/retry", api_user)
|
|
end
|
|
|
|
context 'authorized user' do
|
|
context 'user with :update_build permission' do
|
|
it 'retries non-running build' 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 }
|
|
|
|
it 'does not retry build' do
|
|
expect(response).to have_http_status(403)
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'unauthorized user' do
|
|
let(:api_user) { nil }
|
|
|
|
it 'does not retry build' do
|
|
expect(response).to have_http_status(401)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'POST /projects/:id/builds/:build_id/erase' do
|
|
before do
|
|
post api("/projects/#{project.id}/builds/#{build.id}/erase", user)
|
|
end
|
|
|
|
context 'build is erasable' do
|
|
let(:build) { create(:ci_build, :trace, :artifacts, :success, project: project, pipeline: pipeline) }
|
|
|
|
it 'erases build 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
|
|
|
|
it 'updates build' do
|
|
expect(build.reload.erased_at).to be_truthy
|
|
expect(build.reload.erased_by).to eq user
|
|
end
|
|
end
|
|
|
|
context 'build 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
|
|
|
|
context 'on an playable build' do
|
|
let(:build) { create(:ci_build, :manual, project: project, pipeline: pipeline) }
|
|
|
|
it 'plays the build' 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)
|
|
end
|
|
end
|
|
|
|
context 'on a non-playable build' do
|
|
it 'returns a status code 400, Bad Request' do
|
|
expect(response).to have_http_status 400
|
|
expect(response.body).to match("Unplayable Build")
|
|
end
|
|
end
|
|
end
|
|
end
|