diff --git a/lib/api/v3/deployments.rb b/lib/api/v3/deployments.rb new file mode 100644 index 0000000000000000000000000000000000000000..c5feb49b22fae593e890ec157d4f8b35f0b6aab8 --- /dev/null +++ b/lib/api/v3/deployments.rb @@ -0,0 +1,41 @@ +module API + # Deployments RESTfull API endpoints + class Deployments < Grape::API + include PaginationParams + + before { authenticate! } + + params do + requires :id, type: String, desc: 'The project ID' + end + resource :projects do + desc 'Get all deployments of the project' do + detail 'This feature was introduced in GitLab 8.11.' + success Entities::Deployment + end + params do + use :pagination + end + get ':id/deployments' do + authorize! :read_deployment, user_project + + present paginate(user_project.deployments), with: Entities::Deployment + end + + desc 'Gets a specific deployment' do + detail 'This feature was introduced in GitLab 8.11.' + success Entities::Deployment + end + params do + requires :deployment_id, type: Integer, desc: 'The deployment ID' + end + get ':id/deployments/:deployment_id' do + authorize! :read_deployment, user_project + + deployment = user_project.deployments.find(params[:deployment_id]) + + present deployment, with: Entities::Deployment + end + end + end +end diff --git a/lib/api/v3/merge_request_diffs.rb b/lib/api/v3/merge_request_diffs.rb new file mode 100644 index 0000000000000000000000000000000000000000..bc3d69f6904b6de2cbd08f20655f15c1cf2d40b7 --- /dev/null +++ b/lib/api/v3/merge_request_diffs.rb @@ -0,0 +1,41 @@ +module API + # MergeRequestDiff API + class MergeRequestDiffs < Grape::API + before { authenticate! } + + resource :projects do + desc 'Get a list of merge request diff versions' do + detail 'This feature was introduced in GitLab 8.12.' + success Entities::MergeRequestDiff + end + + params do + requires :id, type: String, desc: 'The ID of a project' + requires :merge_request_id, type: Integer, desc: 'The ID of a merge request' + end + + get ":id/merge_requests/:merge_request_id/versions" do + merge_request = find_merge_request_with_access(params[:merge_request_id]) + + present merge_request.merge_request_diffs, with: Entities::MergeRequestDiff + end + + desc 'Get a single merge request diff version' do + detail 'This feature was introduced in GitLab 8.12.' + success Entities::MergeRequestDiffFull + end + + params do + requires :id, type: String, desc: 'The ID of a project' + requires :merge_request_id, type: Integer, desc: 'The ID of a merge request' + requires :version_id, type: Integer, desc: 'The ID of a merge request diff version' + end + + get ":id/merge_requests/:merge_request_id/versions/:version_id" do + merge_request = find_merge_request_with_access(params[:merge_request_id]) + + present merge_request.merge_request_diffs.find(params[:version_id]), with: Entities::MergeRequestDiffFull + end + end + end +end diff --git a/lib/api/v3/project_hooks.rb b/lib/api/v3/project_hooks.rb new file mode 100644 index 0000000000000000000000000000000000000000..cb679e6658a3085a7e61f53fd8555a3f56ae3cc6 --- /dev/null +++ b/lib/api/v3/project_hooks.rb @@ -0,0 +1,104 @@ +module API + class ProjectHooks < Grape::API + include PaginationParams + + before { authenticate! } + before { authorize_admin_project } + + helpers do + params :project_hook_properties do + requires :url, type: String, desc: "The URL to send the request to" + optional :push_events, type: Boolean, desc: "Trigger hook on push events" + optional :issues_events, type: Boolean, desc: "Trigger hook on issues events" + optional :merge_requests_events, type: Boolean, desc: "Trigger hook on merge request events" + optional :tag_push_events, type: Boolean, desc: "Trigger hook on tag push events" + optional :note_events, type: Boolean, desc: "Trigger hook on note(comment) events" + optional :build_events, type: Boolean, desc: "Trigger hook on build events" + optional :pipeline_events, type: Boolean, desc: "Trigger hook on pipeline events" + optional :wiki_page_events, type: Boolean, desc: "Trigger hook on wiki events" + optional :enable_ssl_verification, type: Boolean, desc: "Do SSL verification when triggering the hook" + optional :token, type: String, desc: "Secret token to validate received payloads; this will not be returned in the response" + end + end + + params do + requires :id, type: String, desc: 'The ID of a project' + end + resource :projects do + desc 'Get project hooks' do + success Entities::ProjectHook + end + params do + use :pagination + end + get ":id/hooks" do + hooks = paginate user_project.hooks + + present hooks, with: Entities::ProjectHook + end + + desc 'Get a project hook' do + success Entities::ProjectHook + end + params do + requires :hook_id, type: Integer, desc: 'The ID of a project hook' + end + get ":id/hooks/:hook_id" do + hook = user_project.hooks.find(params[:hook_id]) + present hook, with: Entities::ProjectHook + end + + desc 'Add hook to project' do + success Entities::ProjectHook + end + params do + use :project_hook_properties + end + post ":id/hooks" do + hook = user_project.hooks.new(declared_params(include_missing: false)) + + if hook.save + present hook, with: Entities::ProjectHook + else + error!("Invalid url given", 422) if hook.errors[:url].present? + + not_found!("Project hook #{hook.errors.messages}") + end + end + + desc 'Update an existing project hook' do + success Entities::ProjectHook + end + params do + requires :hook_id, type: Integer, desc: "The ID of the hook to update" + use :project_hook_properties + end + put ":id/hooks/:hook_id" do + hook = user_project.hooks.find(params.delete(:hook_id)) + + if hook.update_attributes(declared_params(include_missing: false)) + present hook, with: Entities::ProjectHook + else + error!("Invalid url given", 422) if hook.errors[:url].present? + + not_found!("Project hook #{hook.errors.messages}") + end + end + + desc 'Deletes project hook' do + success Entities::ProjectHook + end + params do + requires :hook_id, type: Integer, desc: 'The ID of the hook to delete' + end + delete ":id/hooks/:hook_id" do + begin + present user_project.hooks.destroy(params[:hook_id]), with: Entities::ProjectHook + rescue + # ProjectHook can raise Error if hook_id not found + not_found!("Error deleting hook #{params[:hook_id]}") + end + end + end + end +end diff --git a/lib/api/v3/services.rb b/lib/api/v3/services.rb index af0a058f69bdea7a30621ce4a1710f742326ebf1..de24e6418c7b59252b0dce5ca99be9e4ec7f2680 100644 --- a/lib/api/v3/services.rb +++ b/lib/api/v3/services.rb @@ -561,13 +561,62 @@ module API end if service.update_attributes(attrs.merge(active: false)) - status(200) true else render_api_error!('400 Bad Request', 400) end end + + desc 'Get the service settings for project' do + success Entities::ProjectService + end + params do + requires :service_slug, type: String, values: services.keys, desc: 'The name of the service' + end + get ":id/services/:service_slug" do + service = user_project.find_or_initialize_service(params[:service_slug].underscore) + present service, with: Entities::ProjectService, include_passwords: current_user.is_admin? + end + end + + trigger_services.each do |service_slug, settings| + helpers do + def chat_command_service(project, service_slug, params) + project.services.active.where(template: false).find do |service| + service.try(:token) == params[:token] && service.to_param == service_slug.underscore + end + end + end + + params do + requires :id, type: String, desc: 'The ID of a project' + end + resource :projects do + desc "Trigger a slash command for #{service_slug}" do + detail 'Added in GitLab 8.13' + end + params do + settings.each do |setting| + requires setting[:name], type: setting[:type], desc: setting[:desc] + end + end + post ":id/services/#{service_slug.underscore}/trigger" do + project = find_project(params[:id]) + + # This is not accurate, but done to prevent leakage of the project names + not_found!('Service') unless project + + service = chat_command_service(project, service_slug, params) + result = service.try(:trigger, params) + + if result + status result[:status] || 200 + present result + else + not_found!('Service') + end + end + end end end end -end diff --git a/spec/requests/api/v3/deployments_spec.rb b/spec/requests/api/v3/deployments_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..31e3cfa1b2ff7f0c273f514058a6c61fb7d95be9 --- /dev/null +++ b/spec/requests/api/v3/deployments_spec.rb @@ -0,0 +1,60 @@ +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 + + 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 0000000000000000000000000000000000000000..e1887138aab527ced9ea697c37f414994f11ebaf --- /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/notes_spec.rb b/spec/requests/api/v3/notes_spec.rb index ddef2d5eb04a0a342433c455e4073741653b1dba..a2228132ba9f8c9c79d56ee8ec0e592d76d3e9a1 100644 --- a/spec/requests/api/v3/notes_spec.rb +++ b/spec/requests/api/v3/notes_spec.rb @@ -328,7 +328,11 @@ describe API::V3::Notes, api: true do end it 'returns a 400 bad request error if body not given' do +<<<<<<< HEAD put v3_api("/projects/#{project.id}/issues/#{issue.id}/"\ +======= + put api("/projects/#{project.id}/issues/#{issue.id}/"\ +>>>>>>> e306055d88... Pick API files from 8.16.6 "notes/#{issue_note.id}", user) expect(response).to have_http_status(400) @@ -337,7 +341,11 @@ describe API::V3::Notes, api: true do context 'when noteable is a Snippet' do it 'returns modified note' do +<<<<<<< HEAD put v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\ +======= + put api("/projects/#{project.id}/snippets/#{snippet.id}/"\ +>>>>>>> e306055d88... Pick API files from 8.16.6 "notes/#{snippet_note.id}", user), body: 'Hello!' expect(response).to have_http_status(200) @@ -345,7 +353,11 @@ describe API::V3::Notes, api: true do end it 'returns a 404 error when note id not found' do +<<<<<<< HEAD put v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\ +======= + put api("/projects/#{project.id}/snippets/#{snippet.id}/"\ +>>>>>>> e306055d88... Pick API files from 8.16.6 "notes/12345", user), body: "Hello!" expect(response).to have_http_status(404) @@ -354,7 +366,11 @@ describe API::V3::Notes, api: true do context 'when noteable is a Merge Request' do it 'returns modified note' do +<<<<<<< HEAD put v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\ +======= + put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\ +>>>>>>> e306055d88... Pick API files from 8.16.6 "notes/#{merge_request_note.id}", user), body: 'Hello!' expect(response).to have_http_status(200) @@ -362,7 +378,11 @@ describe API::V3::Notes, api: true do end it 'returns a 404 error when note id not found' do +<<<<<<< HEAD put v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\ +======= + put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\ +>>>>>>> e306055d88... Pick API files from 8.16.6 "notes/12345", user), body: "Hello!" expect(response).to have_http_status(404) @@ -373,6 +393,7 @@ describe API::V3::Notes, api: true do describe 'DELETE /projects/:id/noteable/:noteable_id/notes/:note_id' do context 'when noteable is an Issue' do it 'deletes a note' do +<<<<<<< HEAD delete v3_api("/projects/#{project.id}/issues/#{issue.id}/"\ "notes/#{issue_note.id}", user) @@ -380,11 +401,24 @@ describe API::V3::Notes, api: true do # Check if note is really deleted delete v3_api("/projects/#{project.id}/issues/#{issue.id}/"\ "notes/#{issue_note.id}", user) +======= + delete api("/projects/#{project.id}/issues/#{issue.id}/"\ + "notes/#{issue_note.id}", user) + + expect(response).to have_http_status(200) + # Check if note is really deleted + delete api("/projects/#{project.id}/issues/#{issue.id}/"\ + "notes/#{issue_note.id}", user) +>>>>>>> e306055d88... Pick API files from 8.16.6 expect(response).to have_http_status(404) end it 'returns a 404 error when note id not found' do +<<<<<<< HEAD delete v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user) +======= + delete api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user) +>>>>>>> e306055d88... Pick API files from 8.16.6 expect(response).to have_http_status(404) end @@ -392,6 +426,7 @@ describe API::V3::Notes, api: true do context 'when noteable is a Snippet' do it 'deletes a note' do +<<<<<<< HEAD delete v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\ "notes/#{snippet_note.id}", user) @@ -399,12 +434,26 @@ describe API::V3::Notes, api: true do # Check if note is really deleted delete v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\ "notes/#{snippet_note.id}", user) +======= + delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\ + "notes/#{snippet_note.id}", user) + + expect(response).to have_http_status(200) + # Check if note is really deleted + delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\ + "notes/#{snippet_note.id}", user) +>>>>>>> e306055d88... Pick API files from 8.16.6 expect(response).to have_http_status(404) end it 'returns a 404 error when note id not found' do +<<<<<<< HEAD delete v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\ "notes/12345", user) +======= + delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\ + "notes/12345", user) +>>>>>>> e306055d88... Pick API files from 8.16.6 expect(response).to have_http_status(404) end @@ -412,6 +461,7 @@ describe API::V3::Notes, api: true do context 'when noteable is a Merge Request' do it 'deletes a note' do +<<<<<<< HEAD delete v3_api("/projects/#{project.id}/merge_requests/"\ "#{merge_request.id}/notes/#{merge_request_note.id}", user) @@ -419,12 +469,26 @@ describe API::V3::Notes, api: true do # Check if note is really deleted delete v3_api("/projects/#{project.id}/merge_requests/"\ "#{merge_request.id}/notes/#{merge_request_note.id}", user) +======= + delete api("/projects/#{project.id}/merge_requests/"\ + "#{merge_request.id}/notes/#{merge_request_note.id}", user) + + expect(response).to have_http_status(200) + # Check if note is really deleted + delete api("/projects/#{project.id}/merge_requests/"\ + "#{merge_request.id}/notes/#{merge_request_note.id}", user) +>>>>>>> e306055d88... Pick API files from 8.16.6 expect(response).to have_http_status(404) end it 'returns a 404 error when note id not found' do +<<<<<<< HEAD delete v3_api("/projects/#{project.id}/merge_requests/"\ "#{merge_request.id}/notes/12345", user) +======= + delete api("/projects/#{project.id}/merge_requests/"\ + "#{merge_request.id}/notes/12345", user) +>>>>>>> e306055d88... Pick API files from 8.16.6 expect(response).to have_http_status(404) 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 0000000000000000000000000000000000000000..36fbcf088e704a59f93aec32ea7fdecc4e703dab --- /dev/null +++ b/spec/requests/api/v3/project_hooks_spec.rb @@ -0,0 +1,215 @@ +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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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