diff options
Diffstat (limited to 'spec/requests')
-rw-r--r-- | spec/requests/api/doorkeeper_access_spec.rb | 16 | ||||
-rw-r--r-- | spec/requests/api/environments_spec.rb | 5 | ||||
-rw-r--r-- | spec/requests/api/groups_spec.rb | 4 | ||||
-rw-r--r-- | spec/requests/api/jobs_spec.rb | 408 | ||||
-rw-r--r-- | spec/requests/api/project_hooks_spec.rb | 8 | ||||
-rw-r--r-- | spec/requests/api/projects_spec.rb | 4 | ||||
-rw-r--r-- | spec/requests/api/v3/builds_spec.rb (renamed from spec/requests/api/builds_spec.rb) | 30 | ||||
-rw-r--r-- | spec/requests/api/v3/deployments_spec.rb | 71 | ||||
-rw-r--r-- | spec/requests/api/v3/merge_request_diffs_spec.rb | 49 | ||||
-rw-r--r-- | spec/requests/api/v3/project_hooks_spec.rb | 216 |
10 files changed, 785 insertions, 26 deletions
diff --git a/spec/requests/api/doorkeeper_access_spec.rb b/spec/requests/api/doorkeeper_access_spec.rb index bd9ecaf2685..2974875510a 100644 --- a/spec/requests/api/doorkeeper_access_spec.rb +++ b/spec/requests/api/doorkeeper_access_spec.rb @@ -1,17 +1,23 @@ require 'spec_helper' -describe API::API, api: true do +describe API::API, api: true do include ApiHelpers let!(:user) { create(:user) } let!(:application) { Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user) } let!(:token) { Doorkeeper::AccessToken.create! application_id: application.id, resource_owner_id: user.id, scopes: "api" } - describe "when unauthenticated" do + describe "unauthenticated" do it "returns authentication success" do get api("/user"), access_token: token.token expect(response).to have_http_status(200) end + + include_examples 'user login request with unique ip limit' do + def request + get api('/user'), access_token: token.token + end + end end describe "when token invalid" do @@ -26,5 +32,11 @@ describe API::API, api: true do get api("/user", user) expect(response).to have_http_status(200) end + + include_examples 'user login request with unique ip limit' do + def request + get api('/user', user) + end + end end end diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb index f2fd1dfc8db..b54ee8e8b85 100644 --- a/spec/requests/api/environments_spec.rb +++ b/spec/requests/api/environments_spec.rb @@ -15,6 +15,8 @@ describe API::Environments, api: true do describe 'GET /projects/:id/environments' do context 'as member of the project' do it 'returns project environments' do + project_data_keys = %w(id http_url_to_repo web_url name name_with_namespace path path_with_namespace) + get api("/projects/#{project.id}/environments", user) expect(response).to have_http_status(200) @@ -23,8 +25,7 @@ describe API::Environments, api: true do expect(json_response.size).to eq(1) expect(json_response.first['name']).to eq(environment.name) expect(json_response.first['external_url']).to eq(environment.external_url) - expect(json_response.first['project']['id']).to eq(project.id) - expect(json_response.first['project']['visibility']).to be_present + expect(json_response.first['project'].keys).to contain_exactly(*project_data_keys) end end diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 2b8fd7e31a1..2545da7b1db 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -76,6 +76,8 @@ describe API::Groups, api: true do lfs_objects_size: 234, build_artifacts_size: 345, }.stringify_keys + exposed_attributes = attributes.dup + exposed_attributes['job_artifacts_size'] = exposed_attributes.delete('build_artifacts_size') project1.statistics.update!(attributes) @@ -85,7 +87,7 @@ describe API::Groups, api: true do expect(response).to include_pagination_headers expect(json_response).to be_an Array expect(json_response) - .to satisfy_one { |group| group['statistics'] == attributes } + .to satisfy_one { |group| group['statistics'] == exposed_attributes } end end diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb new file mode 100644 index 00000000000..a4d27734cc2 --- /dev/null +++ b/spec/requests/api/jobs_spec.rb @@ -0,0 +1,408 @@ +require 'spec_helper' + +describe API::Jobs, api: true do + include ApiHelpers + + let(:user) { create(:user) } + 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/jobs' do + let(:query) { Hash.new } + + before do + get api("/projects/#{project.id}/jobs", api_user), query + end + + context 'authorized user' do + it 'returns project jobs' 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 + + 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]' => 'unknown', 'scope[1]' => 'running' } } + + it { expect(response).to have_http_status(400) } + end + end + + context 'unauthorized user' do + let(:api_user) { nil } + + it 'does not return project builds' do + expect(response).to have_http_status(401) + end + end + end + + describe 'GET /projects/:id/jobs/:job_id' do + before do + get api("/projects/#{project.id}/jobs/#{build.id}", api_user) + end + + context 'authorized user' do + 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 + let(:api_user) { nil } + + it 'does not return specific job data' do + expect(response).to have_http_status(401) + end + end + end + + describe 'GET /projects/:id/jobs/:job_id/artifacts' do + before do + get api("/projects/#{project.id}/jobs/#{build.id}/artifacts", api_user) + end + + context 'job 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 job artifacts' do + expect(response).to have_http_status(200) + expect(response.headers).to include(download_headers) + expect(response.body).to match_file(build.artifacts_file.file.file) + end + end + + context 'unauthorized user' do + let(:api_user) { nil } + + it 'does not return specific job artifacts' do + expect(response).to have_http_status(401) + end + end + end + + it 'does not return job 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 get_for_ref(ref = pipeline.ref, job = build.name) + get api("/projects/#{project.id}/jobs/artifacts/#{ref}/download", api_user), job: job + end + + context 'when not logged in' do + let(:api_user) { nil } + + before do + get_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_for_ref + end + + it 'gives 403' do + expect(response).to have_http_status(403) + end + end + + context 'non-existing job' 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_for_ref('TAIL') + end + + it_behaves_like 'not found' + end + + context 'has no such job' do + before do + get_for_ref(pipeline.ref, 'NOBUILD') + end + + it_behaves_like 'not found' + end + end + + 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}" } + 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.reload + pipeline.update(ref: 'master', + sha: project.commit('master').sha) + + get_for_ref('master') + end + + it_behaves_like 'a valid file' + end + + context 'with branch name containing slash' do + before do + pipeline.reload + pipeline.update(ref: 'improve/awesome', + sha: project.commit('improve/awesome').sha) + end + + before do + get_for_ref('improve/awesome') + end + + it_behaves_like 'a valid file' + end + end + end + + describe 'GET /projects/:id/jobs/:job_id/trace' do + let(:build) { create(:ci_build, :trace, pipeline: pipeline) } + + before do + get api("/projects/#{project.id}/jobs/#{build.id}/trace", api_user) + end + + context 'authorized user' do + it 'returns specific job 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 job trace' do + expect(response).to have_http_status(401) + end + end + end + + describe 'POST /projects/:id/jobs/:job_id/cancel' do + before do + post api("/projects/#{project.id}/jobs/#{build.id}/cancel", api_user) + end + + context 'authorized user' do + context 'user with :update_build persmission' do + 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 } + + it 'does not cancel job' do + expect(response).to have_http_status(403) + end + end + end + + context 'unauthorized user' do + let(:api_user) { nil } + + it 'does not cancel job' do + expect(response).to have_http_status(401) + end + end + end + + describe 'POST /projects/:id/jobs/:job_id/retry' do + let(:build) { create(:ci_build, :canceled, pipeline: pipeline) } + + before do + post api("/projects/#{project.id}/jobs/#{build.id}/retry", api_user) + end + + context 'authorized user' do + context 'user with :update_build permission' do + 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 } + + it 'does not retry job' do + expect(response).to have_http_status(403) + end + end + end + + context 'unauthorized user' do + let(:api_user) { nil } + + it 'does not retry job' do + expect(response).to have_http_status(401) + end + end + end + + describe 'POST /projects/:id/jobs/:job_id/erase' do + before do + post api("/projects/#{project.id}/jobs/#{build.id}/erase", user) + end + + context 'job is erasable' do + let(:build) { create(:ci_build, :trace, :artifacts, :success, project: project, pipeline: pipeline) } + + it 'erases job content' do + expect(response).to have_http_status(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 job' do + build.reload + expect(build.erased_at).to be_truthy + expect(build.erased_by).to eq(user) + end + end + + context 'job is not erasable' do + let(:build) { create(:ci_build, :trace, project: project, pipeline: pipeline) } + + it 'responds with forbidden' do + expect(response).to have_http_status(403) + end + end + end + + describe 'POST /projects/:id/jobs/:build_id/artifacts/keep' do + before do + post api("/projects/#{project.id}/jobs/#{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).to have_http_status(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).to have_http_status(404) + end + end + end + + describe 'POST /projects/:id/jobs/:job_id/play' do + before do + post api("/projects/#{project.id}/jobs/#{build.id}/play", user) + end + + context 'on an playable job' do + let(:build) { create(:ci_build, :manual, project: project, pipeline: pipeline) } + + 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) + end + end + + context 'on a non-playable job' do + it 'returns a status code 400, Bad Request' do + expect(response).to have_http_status 400 + expect(response.body).to match("Unplayable Job") + end + end + end +end diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb index f286568547d..b1f8c249092 100644 --- a/spec/requests/api/project_hooks_spec.rb +++ b/spec/requests/api/project_hooks_spec.rb @@ -33,7 +33,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do expect(json_response.first['merge_requests_events']).to eq(true) expect(json_response.first['tag_push_events']).to eq(true) expect(json_response.first['note_events']).to eq(true) - expect(json_response.first['build_events']).to eq(true) + expect(json_response.first['job_events']).to eq(true) expect(json_response.first['pipeline_events']).to eq(true) expect(json_response.first['wiki_page_events']).to eq(true) expect(json_response.first['enable_ssl_verification']).to eq(true) @@ -59,7 +59,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events) expect(json_response['tag_push_events']).to eq(hook.tag_push_events) expect(json_response['note_events']).to eq(hook.note_events) - expect(json_response['build_events']).to eq(hook.build_events) + expect(json_response['job_events']).to eq(hook.build_events) expect(json_response['pipeline_events']).to eq(hook.pipeline_events) expect(json_response['wiki_page_events']).to eq(hook.wiki_page_events) expect(json_response['enable_ssl_verification']).to eq(hook.enable_ssl_verification) @@ -98,7 +98,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do expect(json_response['merge_requests_events']).to eq(false) expect(json_response['tag_push_events']).to eq(false) expect(json_response['note_events']).to eq(false) - expect(json_response['build_events']).to eq(false) + expect(json_response['job_events']).to eq(false) expect(json_response['pipeline_events']).to eq(false) expect(json_response['wiki_page_events']).to eq(true) expect(json_response['enable_ssl_verification']).to eq(true) @@ -144,7 +144,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events) expect(json_response['tag_push_events']).to eq(hook.tag_push_events) expect(json_response['note_events']).to eq(hook.note_events) - expect(json_response['build_events']).to eq(hook.build_events) + expect(json_response['job_events']).to eq(hook.build_events) expect(json_response['pipeline_events']).to eq(hook.pipeline_events) expect(json_response['wiki_page_events']).to eq(hook.wiki_page_events) expect(json_response['enable_ssl_verification']).to eq(hook.enable_ssl_verification) diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 03cae074803..77f79cd5bc7 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -594,7 +594,7 @@ describe API::Projects, api: true do expect(json_response['issues_enabled']).to be_present expect(json_response['merge_requests_enabled']).to be_present expect(json_response['wiki_enabled']).to be_present - expect(json_response['builds_enabled']).to be_present + expect(json_response['jobs_enabled']).to be_present expect(json_response['snippets_enabled']).to be_present expect(json_response['container_registry_enabled']).to be_present expect(json_response['created_at']).to be_present @@ -605,7 +605,7 @@ describe API::Projects, api: true do expect(json_response['avatar_url']).to be_nil expect(json_response['star_count']).to be_present expect(json_response['forks_count']).to be_present - expect(json_response['public_builds']).to be_present + expect(json_response['public_jobs']).to be_present expect(json_response['shared_with_groups']).to be_an Array expect(json_response['shared_with_groups'].length).to eq(1) expect(json_response['shared_with_groups'][0]['group_id']).to eq(group.id) diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/v3/builds_spec.rb index 76a10a2374c..a50c22a6dd1 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/v3/builds_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe API::Builds, api: true do +describe API::V3::Builds, api: true do include ApiHelpers let(:user) { create(:user) } @@ -18,7 +18,7 @@ describe API::Builds, api: true do before do create(:ci_build, :skipped, pipeline: pipeline) - get api("/projects/#{project.id}/builds?#{query}", api_user) + get v3_api("/projects/#{project.id}/builds?#{query}", api_user) end context 'authorized user' do @@ -91,7 +91,7 @@ describe API::Builds, api: true do 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) + get v3_api("/projects/#{project.id}/repository/commits/1a271fd1/builds", api_user) end it 'responds with 404' do @@ -107,7 +107,7 @@ describe API::Builds, api: true do create(:ci_build, pipeline: pipeline) create(:ci_build) - get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", api_user) + get v3_api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", api_user) end it 'returns project jobs for specific commit' do @@ -130,7 +130,7 @@ describe API::Builds, api: true do 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) + get v3_api("/projects/#{project.id}/repository/commits/#{branch_head}/builds", api_user) end it 'returns an empty array' do @@ -146,7 +146,7 @@ describe API::Builds, api: true 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) + get v3_api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", nil) end it 'does not return project jobs' do @@ -159,7 +159,7 @@ describe API::Builds, api: true do describe 'GET /projects/:id/builds/:build_id' do before do - get api("/projects/#{project.id}/builds/#{build.id}", api_user) + get v3_api("/projects/#{project.id}/builds/#{build.id}", api_user) end context 'authorized user' do @@ -189,7 +189,7 @@ describe API::Builds, api: true do describe 'GET /projects/:id/builds/:build_id/artifacts' do before do - get api("/projects/#{project.id}/builds/#{build.id}/artifacts", api_user) + get v3_api("/projects/#{project.id}/builds/#{build.id}/artifacts", api_user) end context 'job with artifacts' do @@ -231,7 +231,7 @@ describe API::Builds, api: true do end def path_for_ref(ref = pipeline.ref, job = build.name) - api("/projects/#{project.id}/builds/artifacts/#{ref}/download?job=#{job}", api_user) + v3_api("/projects/#{project.id}/builds/artifacts/#{ref}/download?job=#{job}", api_user) end context 'when not logged in' do @@ -324,7 +324,7 @@ describe API::Builds, api: true do let(:build) { create(:ci_build, :trace, pipeline: pipeline) } before do - get api("/projects/#{project.id}/builds/#{build.id}/trace", api_user) + get v3_api("/projects/#{project.id}/builds/#{build.id}/trace", api_user) end context 'authorized user' do @@ -345,7 +345,7 @@ describe API::Builds, api: true do describe 'POST /projects/:id/builds/:build_id/cancel' do before do - post api("/projects/#{project.id}/builds/#{build.id}/cancel", api_user) + post v3_api("/projects/#{project.id}/builds/#{build.id}/cancel", api_user) end context 'authorized user' do @@ -378,7 +378,7 @@ describe API::Builds, api: true do let(:build) { create(:ci_build, :canceled, pipeline: pipeline) } before do - post api("/projects/#{project.id}/builds/#{build.id}/retry", api_user) + post v3_api("/projects/#{project.id}/builds/#{build.id}/retry", api_user) end context 'authorized user' do @@ -410,7 +410,7 @@ describe API::Builds, api: true do describe 'POST /projects/:id/builds/:build_id/erase' do before do - post api("/projects/#{project.id}/builds/#{build.id}/erase", user) + post v3_api("/projects/#{project.id}/builds/#{build.id}/erase", user) end context 'job is erasable' do @@ -440,7 +440,7 @@ describe API::Builds, api: true do describe 'POST /projects/:id/builds/:build_id/artifacts/keep' do before do - post api("/projects/#{project.id}/builds/#{build.id}/artifacts/keep", user) + post v3_api("/projects/#{project.id}/builds/#{build.id}/artifacts/keep", user) end context 'artifacts did not expire' do @@ -466,7 +466,7 @@ describe API::Builds, api: true do describe 'POST /projects/:id/builds/:build_id/play' do before do - post api("/projects/#{project.id}/builds/#{build.id}/play", user) + post v3_api("/projects/#{project.id}/builds/#{build.id}/play", user) end context 'on an playable job' do diff --git a/spec/requests/api/v3/deployments_spec.rb b/spec/requests/api/v3/deployments_spec.rb new file mode 100644 index 00000000000..3c5ce407b32 --- /dev/null +++ b/spec/requests/api/v3/deployments_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' + +describe API::Deployments, api: true do + include ApiHelpers + + let(:user) { create(:user) } + let(:non_member) { create(:user) } + let(:project) { deployment.environment.project } + let!(:deployment) { create(:deployment) } + + before do + project.team << [user, :master] + end + + shared_examples 'a paginated resources' do + before do + # Fires the request + request + end + + it 'has pagination headers' do + expect(response).to include_pagination_headers + end + end + + describe 'GET /projects/:id/deployments' do + context 'as member of the project' do + it_behaves_like 'a paginated resources' do + let(:request) { get api("/projects/#{project.id}/deployments", user) } + end + + it 'returns projects deployments' do + get api("/projects/#{project.id}/deployments", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.size).to eq(1) + expect(json_response.first['iid']).to eq(deployment.iid) + expect(json_response.first['sha']).to match /\A\h{40}\z/ + end + end + + context 'as non member' do + it 'returns a 404 status code' do + get api("/projects/#{project.id}/deployments", non_member) + + expect(response).to have_http_status(404) + end + end + end + + describe 'GET /projects/:id/deployments/:deployment_id' do + context 'as a member of the project' do + it 'returns the projects deployment' do + get api("/projects/#{project.id}/deployments/#{deployment.id}", user) + + expect(response).to have_http_status(200) + expect(json_response['sha']).to match /\A\h{40}\z/ + expect(json_response['id']).to eq(deployment.id) + end + end + + context 'as non member' do + it 'returns a 404 status code' do + get api("/projects/#{project.id}/deployments/#{deployment.id}", non_member) + + expect(response).to have_http_status(404) + end + end + end +end diff --git a/spec/requests/api/v3/merge_request_diffs_spec.rb b/spec/requests/api/v3/merge_request_diffs_spec.rb new file mode 100644 index 00000000000..e1887138aab --- /dev/null +++ b/spec/requests/api/v3/merge_request_diffs_spec.rb @@ -0,0 +1,49 @@ +require "spec_helper" + +describe API::MergeRequestDiffs, 'MergeRequestDiffs', api: true do + include ApiHelpers + + let!(:user) { create(:user) } + let!(:merge_request) { create(:merge_request, importing: true) } + let!(:project) { merge_request.target_project } + + before do + merge_request.merge_request_diffs.create(head_commit_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') + merge_request.merge_request_diffs.create(head_commit_sha: '5937ac0a7beb003549fc5fd26fc247adbce4a52e') + project.team << [user, :master] + end + + describe 'GET /projects/:id/merge_requests/:merge_request_id/versions' do + it 'returns 200 for a valid merge request' do + get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions", user) + merge_request_diff = merge_request.merge_request_diffs.first + + expect(response.status).to eq 200 + expect(json_response.size).to eq(merge_request.merge_request_diffs.size) + expect(json_response.first['id']).to eq(merge_request_diff.id) + expect(json_response.first['head_commit_sha']).to eq(merge_request_diff.head_commit_sha) + end + + it 'returns a 404 when merge_request_id not found' do + get api("/projects/#{project.id}/merge_requests/999/versions", user) + expect(response).to have_http_status(404) + end + end + + describe 'GET /projects/:id/merge_requests/:merge_request_id/versions/:version_id' do + it 'returns a 200 for a valid merge request' do + merge_request_diff = merge_request.merge_request_diffs.first + get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions/#{merge_request_diff.id}", user) + + expect(response.status).to eq 200 + expect(json_response['id']).to eq(merge_request_diff.id) + expect(json_response['head_commit_sha']).to eq(merge_request_diff.head_commit_sha) + expect(json_response['diffs'].size).to eq(merge_request_diff.diffs.size) + end + + it 'returns a 404 when merge_request_id not found' do + get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions/999", user) + expect(response).to have_http_status(404) + end + end +end diff --git a/spec/requests/api/v3/project_hooks_spec.rb b/spec/requests/api/v3/project_hooks_spec.rb new file mode 100644 index 00000000000..a981119dc5a --- /dev/null +++ b/spec/requests/api/v3/project_hooks_spec.rb @@ -0,0 +1,216 @@ +require 'spec_helper' + +describe API::ProjectHooks, 'ProjectHooks', api: true do + include ApiHelpers + let(:user) { create(:user) } + let(:user3) { create(:user) } + let!(:project) { create(:project, creator_id: user.id, namespace: user.namespace) } + let!(:hook) do + create(:project_hook, + :all_events_enabled, + project: project, + url: 'http://example.com', + enable_ssl_verification: true) + end + + before do + project.team << [user, :master] + project.team << [user3, :developer] + end + + describe "GET /projects/:id/hooks" do + context "authorized user" do + it "returns project hooks" do + get v3_api("/projects/#{project.id}/hooks", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.count).to eq(1) + expect(json_response.first['url']).to eq("http://example.com") + expect(json_response.first['issues_events']).to eq(true) + expect(json_response.first['push_events']).to eq(true) + expect(json_response.first['merge_requests_events']).to eq(true) + expect(json_response.first['tag_push_events']).to eq(true) + expect(json_response.first['note_events']).to eq(true) + expect(json_response.first['build_events']).to eq(true) + expect(json_response.first['pipeline_events']).to eq(true) + expect(json_response.first['wiki_page_events']).to eq(true) + expect(json_response.first['enable_ssl_verification']).to eq(true) + end + end + + context "unauthorized user" do + it "does not access project hooks" do + get v3_api("/projects/#{project.id}/hooks", user3) + + expect(response).to have_http_status(403) + end + end + end + + describe "GET /projects/:id/hooks/:hook_id" do + context "authorized user" do + it "returns a project hook" do + get v3_api("/projects/#{project.id}/hooks/#{hook.id}", user) + expect(response).to have_http_status(200) + expect(json_response['url']).to eq(hook.url) + expect(json_response['issues_events']).to eq(hook.issues_events) + expect(json_response['push_events']).to eq(hook.push_events) + expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events) + expect(json_response['tag_push_events']).to eq(hook.tag_push_events) + expect(json_response['note_events']).to eq(hook.note_events) + expect(json_response['build_events']).to eq(hook.build_events) + expect(json_response['pipeline_events']).to eq(hook.pipeline_events) + expect(json_response['wiki_page_events']).to eq(hook.wiki_page_events) + expect(json_response['enable_ssl_verification']).to eq(hook.enable_ssl_verification) + end + + it "returns a 404 error if hook id is not available" do + get v3_api("/projects/#{project.id}/hooks/1234", user) + expect(response).to have_http_status(404) + end + end + + context "unauthorized user" do + it "does not access an existing hook" do + get v3_api("/projects/#{project.id}/hooks/#{hook.id}", user3) + expect(response).to have_http_status(403) + end + end + + it "returns a 404 error if hook id is not available" do + get v3_api("/projects/#{project.id}/hooks/1234", user) + expect(response).to have_http_status(404) + end + end + + describe "POST /projects/:id/hooks" do + it "adds hook to project" do + expect do + post v3_api("/projects/#{project.id}/hooks", user), + url: "http://example.com", issues_events: true, wiki_page_events: true + end.to change {project.hooks.count}.by(1) + + expect(response).to have_http_status(201) + expect(json_response['url']).to eq('http://example.com') + expect(json_response['issues_events']).to eq(true) + expect(json_response['push_events']).to eq(true) + expect(json_response['merge_requests_events']).to eq(false) + expect(json_response['tag_push_events']).to eq(false) + expect(json_response['note_events']).to eq(false) + expect(json_response['build_events']).to eq(false) + expect(json_response['pipeline_events']).to eq(false) + expect(json_response['wiki_page_events']).to eq(true) + expect(json_response['enable_ssl_verification']).to eq(true) + expect(json_response).not_to include('token') + end + + it "adds the token without including it in the response" do + token = "secret token" + + expect do + post v3_api("/projects/#{project.id}/hooks", user), url: "http://example.com", token: token + end.to change {project.hooks.count}.by(1) + + expect(response).to have_http_status(201) + expect(json_response["url"]).to eq("http://example.com") + expect(json_response).not_to include("token") + + hook = project.hooks.find(json_response["id"]) + + expect(hook.url).to eq("http://example.com") + expect(hook.token).to eq(token) + end + + it "returns a 400 error if url not given" do + post v3_api("/projects/#{project.id}/hooks", user) + expect(response).to have_http_status(400) + end + + it "returns a 422 error if url not valid" do + post v3_api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com" + expect(response).to have_http_status(422) + end + end + + describe "PUT /projects/:id/hooks/:hook_id" do + it "updates an existing project hook" do + put v3_api("/projects/#{project.id}/hooks/#{hook.id}", user), + url: 'http://example.org', push_events: false + expect(response).to have_http_status(200) + expect(json_response['url']).to eq('http://example.org') + expect(json_response['issues_events']).to eq(hook.issues_events) + expect(json_response['push_events']).to eq(false) + expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events) + expect(json_response['tag_push_events']).to eq(hook.tag_push_events) + expect(json_response['note_events']).to eq(hook.note_events) + expect(json_response['build_events']).to eq(hook.build_events) + expect(json_response['pipeline_events']).to eq(hook.pipeline_events) + expect(json_response['wiki_page_events']).to eq(hook.wiki_page_events) + expect(json_response['enable_ssl_verification']).to eq(hook.enable_ssl_verification) + end + + it "adds the token without including it in the response" do + token = "secret token" + + put v3_api("/projects/#{project.id}/hooks/#{hook.id}", user), url: "http://example.org", token: token + + expect(response).to have_http_status(200) + expect(json_response["url"]).to eq("http://example.org") + expect(json_response).not_to include("token") + + expect(hook.reload.url).to eq("http://example.org") + expect(hook.reload.token).to eq(token) + end + + it "returns 404 error if hook id not found" do + put v3_api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org' + expect(response).to have_http_status(404) + end + + it "returns 400 error if url is not given" do + put v3_api("/projects/#{project.id}/hooks/#{hook.id}", user) + expect(response).to have_http_status(400) + end + + it "returns a 422 error if url is not valid" do + put v3_api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'ftp://example.com' + expect(response).to have_http_status(422) + end + end + + describe "DELETE /projects/:id/hooks/:hook_id" do + it "deletes hook from project" do + expect do + delete v3_api("/projects/#{project.id}/hooks/#{hook.id}", user) + end.to change {project.hooks.count}.by(-1) + expect(response).to have_http_status(200) + end + + it "returns success when deleting hook" do + delete v3_api("/projects/#{project.id}/hooks/#{hook.id}", user) + expect(response).to have_http_status(200) + end + + it "returns a 404 error when deleting non existent hook" do + delete v3_api("/projects/#{project.id}/hooks/42", user) + expect(response).to have_http_status(404) + end + + it "returns a 404 error if hook id not given" do + delete v3_api("/projects/#{project.id}/hooks", user) + + expect(response).to have_http_status(404) + end + + it "returns a 404 if a user attempts to delete project hooks he/she does not own" do + test_user = create(:user) + other_project = create(:project) + other_project.team << [test_user, :master] + + delete v3_api("/projects/#{other_project.id}/hooks/#{hook.id}", test_user) + expect(response).to have_http_status(404) + expect(WebHook.exists?(hook.id)).to be_truthy + end + end +end |