diff options
Diffstat (limited to 'spec/requests')
25 files changed, 719 insertions, 639 deletions
diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index c64499fc8c0..5a2e1b2cf2d 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -1,25 +1,31 @@ require 'spec_helper' -require 'mime/types' describe API::Branches do let(:user) { create(:user) } - let!(:project) { create(:project, :repository, creator: user) } - let!(:master) { create(:project_member, :master, user: user, project: project) } - let(:guest) { create(:user).tap { |u| create(:project_member, :guest, user: u, project: project) } } - let!(:branch_name) { 'feature' } - let!(:branch_sha) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } - let(:branch_with_dot) { CreateBranchService.new(project, user).execute("with.1.2.3", "master")[:branch] } + let(:guest) { create(:user).tap { |u| project.add_guest(u) } } + let(:project) { create(:project, :repository, creator: user, path: 'my.project') } + let(:branch_name) { 'feature' } + let(:branch_sha) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } + let(:branch_with_dot) { project.repository.find_branch('ends-with.json') } + let(:branch_with_slash) { project.repository.find_branch('improve/awesome') } + + let(:project_id) { project.id } + let(:current_user) { nil } + + before do + project.add_master(user) + end describe "GET /projects/:id/repository/branches" do - let(:route) { "/projects/#{project.id}/repository/branches" } + let(:route) { "/projects/#{project_id}/repository/branches" } shared_examples_for 'repository branches' do it 'returns the repository branches' do get api(route, current_user), per_page: 100 - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/branches') expect(response).to include_pagination_headers - expect(json_response).to be_an Array branch_names = json_response.map { |x| x['name'] } expect(branch_names).to match_array(project.repository.branch_names) end @@ -34,10 +40,9 @@ describe API::Branches do end context 'when unauthenticated', 'and project is public' do - it_behaves_like 'repository branches' do - let(:project) { create(:project, :public, :repository) } - let(:current_user) { nil } - end + let(:project) { create(:project, :public, :repository) } + + it_behaves_like 'repository branches' end context 'when unauthenticated', 'and project is private' do @@ -47,9 +52,15 @@ describe API::Branches do end end - context 'when authenticated', 'as a developer' do - it_behaves_like 'repository branches' do - let(:current_user) { user } + context 'when authenticated', 'as a master' do + let(:current_user) { user } + + it_behaves_like 'repository branches' + + context 'requesting with the escaped project full path' do + let(:project_id) { CGI.escape(project.full_path) } + + it_behaves_like 'repository branches' end end @@ -61,31 +72,15 @@ describe API::Branches do end describe "GET /projects/:id/repository/branches/:branch" do - let(:route) { "/projects/#{project.id}/repository/branches/#{branch_name}" } + let(:route) { "/projects/#{project_id}/repository/branches/#{branch_name}" } - shared_examples_for 'repository branch' do |merged: false| + shared_examples_for 'repository branch' do it 'returns the repository branch' do get api(route, current_user) - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['merged']).to eq(merged) - expect(json_response['protected']).to eq(false) - expect(json_response['developers_can_push']).to eq(false) - expect(json_response['developers_can_merge']).to eq(false) - - json_commit = json_response['commit'] - expect(json_commit['id']).to eq(branch_sha) - expect(json_commit).to have_key('short_id') - expect(json_commit).to have_key('title') - expect(json_commit).to have_key('message') - expect(json_commit).to have_key('author_name') - expect(json_commit).to have_key('author_email') - expect(json_commit).to have_key('authored_date') - expect(json_commit).to have_key('committer_name') - expect(json_commit).to have_key('committer_email') - expect(json_commit).to have_key('committed_date') - expect(json_commit).to have_key('parent_ids') + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq(CGI.unescape(branch_name)) end context 'when branch does not exist' do @@ -107,10 +102,9 @@ describe API::Branches do end context 'when unauthenticated', 'and project is public' do - it_behaves_like 'repository branch' do - let(:project) { create(:project, :public, :repository) } - let(:current_user) { nil } - end + let(:project) { create(:project, :public, :repository) } + + it_behaves_like 'repository branch' end context 'when unauthenticated', 'and project is private' do @@ -120,22 +114,41 @@ describe API::Branches do end end - context 'when authenticated', 'as a developer' do + context 'when authenticated', 'as a master' do let(:current_user) { user } + it_behaves_like 'repository branch' context 'when branch contains a dot' do let(:branch_name) { branch_with_dot.name } - let(:branch_sha) { project.commit('master').sha } it_behaves_like 'repository branch' end - context 'when branch is merged' do - let(:branch_name) { 'merge-test' } - let(:branch_sha) { project.commit('merge-test').sha } + context 'when branch contains a slash' do + let(:branch_name) { branch_with_slash.name } + + it_behaves_like '404 response' do + let(:request) { get api(route, current_user) } + end + end + + context 'when branch contains an escaped slash' do + let(:branch_name) { CGI.escape(branch_with_slash.name) } + + it_behaves_like 'repository branch' + end + + context 'requesting with the escaped project full path' do + let(:project_id) { CGI.escape(project.full_path) } + + it_behaves_like 'repository branch' - it_behaves_like 'repository branch', merged: true + context 'when branch contains a dot' do + let(:branch_name) { branch_with_dot.name } + + it_behaves_like 'repository branch' + end end end @@ -147,268 +160,348 @@ describe API::Branches do end describe 'PUT /projects/:id/repository/branches/:branch/protect' do - context "when a protected branch doesn't already exist" do - it 'protects a single branch' do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) + let(:route) { "/projects/#{project_id}/repository/branches/#{branch_name}/protect" } - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['commit']['id']).to eq(branch_sha) - expect(json_response['protected']).to eq(true) - expect(json_response['developers_can_push']).to eq(false) - expect(json_response['developers_can_merge']).to eq(false) - end - - it "protects a single branch with dots in the name" do - put api("/projects/#{project.id}/repository/branches/#{branch_with_dot.name}/protect", user) + shared_examples_for 'repository new protected branch' do + it 'protects a single branch' do + put api(route, current_user) - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_with_dot.name) + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq(CGI.unescape(branch_name)) expect(json_response['protected']).to eq(true) end it 'protects a single branch and developers can push' do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), - developers_can_push: true + put api(route, current_user), developers_can_push: true - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['commit']['id']).to eq(branch_sha) + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq(CGI.unescape(branch_name)) expect(json_response['protected']).to eq(true) expect(json_response['developers_can_push']).to eq(true) expect(json_response['developers_can_merge']).to eq(false) end it 'protects a single branch and developers can merge' do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), - developers_can_merge: true + put api(route, current_user), developers_can_merge: true - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['commit']['id']).to eq(branch_sha) + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq(CGI.unescape(branch_name)) expect(json_response['protected']).to eq(true) expect(json_response['developers_can_push']).to eq(false) expect(json_response['developers_can_merge']).to eq(true) end it 'protects a single branch and developers can push and merge' do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), - developers_can_push: true, developers_can_merge: true + put api(route, current_user), developers_can_push: true, developers_can_merge: true - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['commit']['id']).to eq(branch_sha) + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq(CGI.unescape(branch_name)) expect(json_response['protected']).to eq(true) expect(json_response['developers_can_push']).to eq(true) expect(json_response['developers_can_merge']).to eq(true) end + + context 'when branch does not exist' do + let(:branch_name) { 'unknown' } + + it_behaves_like '404 response' do + let(:request) { put api(route, current_user) } + let(:message) { '404 Branch Not Found' } + end + end + + context 'when repository is disabled' do + include_context 'disabled repository' + + it_behaves_like '403 response' do + let(:request) { put api(route, current_user) } + end + end end - context 'for an existing protected branch' do - before do - project.repository.add_branch(user, protected_branch.name, 'master') + context 'when unauthenticated', 'and project is private' do + it_behaves_like '404 response' do + let(:request) { put api(route) } + let(:message) { '404 Project Not Found' } end + end + + context 'when authenticated', 'as a guest' do + it_behaves_like '403 response' do + let(:request) { put api(route, guest) } + end + end + + context 'when authenticated', 'as a master' do + let(:current_user) { user } - context "when developers can push and merge" do - let(:protected_branch) { create(:protected_branch, :developers_can_push, :developers_can_merge, project: project, name: 'protected_branch') } + context "when a protected branch doesn't already exist" do + it_behaves_like 'repository new protected branch' - it 'updates that a developer cannot push or merge' do - put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user), - developers_can_push: false, developers_can_merge: false + context 'when branch contains a dot' do + let(:branch_name) { branch_with_dot.name } - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(protected_branch.name) - expect(json_response['protected']).to eq(true) - expect(json_response['developers_can_push']).to eq(false) - expect(json_response['developers_can_merge']).to eq(false) + it_behaves_like 'repository new protected branch' end - it "doesn't result in 0 access levels when 'developers_can_push' is switched off" do - put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user), - developers_can_push: false + context 'when branch contains a slash' do + let(:branch_name) { branch_with_slash.name } - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(protected_branch.name) - expect(protected_branch.reload.push_access_levels.first).to be_present - expect(protected_branch.reload.push_access_levels.first.access_level).to eq(Gitlab::Access::MASTER) + it_behaves_like '404 response' do + let(:request) { put api(route, current_user) } + end end - it "doesn't result in 0 access levels when 'developers_can_merge' is switched off" do - put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user), - developers_can_merge: false + context 'when branch contains an escaped slash' do + let(:branch_name) { CGI.escape(branch_with_slash.name) } - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(protected_branch.name) - expect(protected_branch.reload.merge_access_levels.first).to be_present - expect(protected_branch.reload.merge_access_levels.first.access_level).to eq(Gitlab::Access::MASTER) + it_behaves_like 'repository new protected branch' + end + + context 'requesting with the escaped project full path' do + let(:project_id) { CGI.escape(project.full_path) } + + it_behaves_like 'repository new protected branch' + + context 'when branch contains a dot' do + let(:branch_name) { branch_with_dot.name } + + it_behaves_like 'repository new protected branch' + end end end - context "when developers cannot push or merge" do - let(:protected_branch) { create(:protected_branch, project: project, name: 'protected_branch') } + context 'when protected branch already exists' do + before do + project.repository.add_branch(user, protected_branch.name, 'master') + end + + context 'when developers can push and merge' do + let(:protected_branch) { create(:protected_branch, :developers_can_push, :developers_can_merge, project: project, name: 'protected_branch') } + + it 'updates that a developer cannot push or merge' do + put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user), + developers_can_push: false, developers_can_merge: false + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq(protected_branch.name) + expect(json_response['protected']).to eq(true) + expect(json_response['developers_can_push']).to eq(false) + expect(json_response['developers_can_merge']).to eq(false) + expect(protected_branch.reload.push_access_levels.first.access_level).to eq(Gitlab::Access::MASTER) + expect(protected_branch.reload.merge_access_levels.first.access_level).to eq(Gitlab::Access::MASTER) + end + end + + context 'when developers cannot push or merge' do + let(:protected_branch) { create(:protected_branch, project: project, name: 'protected_branch') } - it 'updates that a developer can push and merge' do - put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user), - developers_can_push: true, developers_can_merge: true + it 'updates that a developer can push and merge' do + put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user), + developers_can_push: true, developers_can_merge: true - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(protected_branch.name) - expect(json_response['protected']).to eq(true) - expect(json_response['developers_can_push']).to eq(true) - expect(json_response['developers_can_merge']).to eq(true) + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq(protected_branch.name) + expect(json_response['protected']).to eq(true) + expect(json_response['developers_can_push']).to eq(true) + expect(json_response['developers_can_merge']).to eq(true) + end end end end + end - context "multiple API calls" do - it "returns success when `protect` is called twice" do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) + describe 'PUT /projects/:id/repository/branches/:branch/unprotect' do + let(:route) { "/projects/#{project_id}/repository/branches/#{branch_name}/unprotect" } - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['protected']).to eq(true) - expect(json_response['developers_can_push']).to eq(false) - expect(json_response['developers_can_merge']).to eq(false) + shared_examples_for 'repository unprotected branch' do + it 'unprotects a single branch' do + put api(route, current_user) + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq(CGI.unescape(branch_name)) + expect(json_response['protected']).to eq(false) end - it "returns success when `protect` is called twice with `developers_can_push` turned on" do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_push: true - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_push: true + context 'when branch does not exist' do + let(:branch_name) { 'unknown' } - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['protected']).to eq(true) - expect(json_response['developers_can_push']).to eq(true) - expect(json_response['developers_can_merge']).to eq(false) + it_behaves_like '404 response' do + let(:request) { put api(route, current_user) } + let(:message) { '404 Branch Not Found' } + end end - it "returns success when `protect` is called twice with `developers_can_merge` turned on" do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_merge: true - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_merge: true + context 'when repository is disabled' do + include_context 'disabled repository' - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['protected']).to eq(true) - expect(json_response['developers_can_push']).to eq(false) - expect(json_response['developers_can_merge']).to eq(true) + it_behaves_like '403 response' do + let(:request) { put api(route, current_user) } + end end end - it "returns a 404 error if branch not found" do - put api("/projects/#{project.id}/repository/branches/unknown/protect", user) - expect(response).to have_http_status(404) + context 'when unauthenticated', 'and project is private' do + it_behaves_like '404 response' do + let(:request) { put api(route) } + let(:message) { '404 Project Not Found' } + end end - it "returns a 403 error if guest" do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", guest) - expect(response).to have_http_status(403) + context 'when authenticated', 'as a guest' do + it_behaves_like '403 response' do + let(:request) { put api(route, guest) } + end end - end - describe "PUT /projects/:id/repository/branches/:branch/unprotect" do - it "unprotects a single branch" do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user) - expect(response).to have_http_status(200) + context 'when authenticated', 'as a master' do + let(:current_user) { user } + + context "when a protected branch doesn't already exist" do + it_behaves_like 'repository unprotected branch' + + context 'when branch contains a dot' do + let(:branch_name) { branch_with_dot.name } + + it_behaves_like 'repository unprotected branch' + end + + context 'when branch contains a slash' do + let(:branch_name) { branch_with_slash.name } + + it_behaves_like '404 response' do + let(:request) { put api(route, current_user) } + end + end + + context 'when branch contains an escaped slash' do + let(:branch_name) { CGI.escape(branch_with_slash.name) } - expect(json_response['name']).to eq(branch_name) - expect(json_response['commit']['id']).to eq(branch_sha) - expect(json_response['protected']).to eq(false) + it_behaves_like 'repository unprotected branch' + end + + context 'requesting with the escaped project full path' do + let(:project_id) { CGI.escape(project.full_path) } + + it_behaves_like 'repository unprotected branch' + + context 'when branch contains a dot' do + let(:branch_name) { branch_with_dot.name } + + it_behaves_like 'repository unprotected branch' + end + end + end end + end - it "update branches with dots in branch name" do - put api("/projects/#{project.id}/repository/branches/#{branch_with_dot.name}/unprotect", user) + describe 'POST /projects/:id/repository/branches' do + let(:route) { "/projects/#{project_id}/repository/branches" } - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_with_dot.name) - expect(json_response['protected']).to eq(false) + shared_examples_for 'repository new branch' do + it 'creates a new branch' do + post api(route, current_user), branch: 'feature1', ref: branch_sha + + expect(response).to have_gitlab_http_status(201) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq('feature1') + expect(json_response['commit']['id']).to eq(branch_sha) + end + + context 'when repository is disabled' do + include_context 'disabled repository' + + it_behaves_like '403 response' do + let(:request) { post api(route, current_user) } + end + end end - it "returns success when unprotect branch" do - put api("/projects/#{project.id}/repository/branches/unknown/unprotect", user) - expect(response).to have_http_status(404) + context 'when unauthenticated', 'and project is private' do + it_behaves_like '404 response' do + let(:request) { post api(route) } + let(:message) { '404 Project Not Found' } + end end - it "returns success when unprotect branch again" do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user) - put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user) - expect(response).to have_http_status(200) + context 'when authenticated', 'as a guest' do + it_behaves_like '403 response' do + let(:request) { post api(route, guest) } + end end - end - describe "POST /projects/:id/repository/branches" do - it "creates a new branch" do - post api("/projects/#{project.id}/repository/branches", user), - branch: 'feature1', - ref: branch_sha + context 'when authenticated', 'as a master' do + let(:current_user) { user } - expect(response).to have_http_status(201) + context "when a protected branch doesn't already exist" do + it_behaves_like 'repository new branch' - expect(json_response['name']).to eq('feature1') - expect(json_response['commit']['id']).to eq(branch_sha) - end + context 'requesting with the escaped project full path' do + let(:project_id) { CGI.escape(project.full_path) } - it "denies for user without push access" do - post api("/projects/#{project.id}/repository/branches", guest), - branch: branch_name, - ref: branch_sha - expect(response).to have_http_status(403) + it_behaves_like 'repository new branch' + end + end end it 'returns 400 if branch name is invalid' do - post api("/projects/#{project.id}/repository/branches", user), - branch: 'new design', - ref: branch_sha - expect(response).to have_http_status(400) + post api(route, user), branch: 'new design', ref: branch_sha + + expect(response).to have_gitlab_http_status(400) expect(json_response['message']).to eq('Branch name is invalid') end it 'returns 400 if branch already exists' do - post api("/projects/#{project.id}/repository/branches", user), - branch: 'new_design1', - ref: branch_sha - expect(response).to have_http_status(201) - - post api("/projects/#{project.id}/repository/branches", user), - branch: 'new_design1', - ref: branch_sha - expect(response).to have_http_status(400) + post api(route, user), branch: 'new_design1', ref: branch_sha + + expect(response).to have_gitlab_http_status(201) + + post api(route, user), branch: 'new_design1', ref: branch_sha + + expect(response).to have_gitlab_http_status(400) expect(json_response['message']).to eq('Branch already exists') end it 'returns 400 if ref name is invalid' do - post api("/projects/#{project.id}/repository/branches", user), - branch: 'new_design3', - ref: 'foo' - expect(response).to have_http_status(400) + post api(route, user), branch: 'new_design3', ref: 'foo' + + expect(response).to have_gitlab_http_status(400) expect(json_response['message']).to eq('Invalid reference name') end end - describe "DELETE /projects/:id/repository/branches/:branch" do + describe 'DELETE /projects/:id/repository/branches/:branch' do before do allow_any_instance_of(Repository).to receive(:rm_branch).and_return(true) end - it "removes branch" do + it 'removes branch' do delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user) - expect(response).to have_http_status(204) + expect(response).to have_gitlab_http_status(204) end - it "removes a branch with dots in the branch name" do + it 'removes a branch with dots in the branch name' do delete api("/projects/#{project.id}/repository/branches/#{branch_with_dot.name}", user) - expect(response).to have_http_status(204) + expect(response).to have_gitlab_http_status(204) end it 'returns 404 if branch not exists' do delete api("/projects/#{project.id}/repository/branches/foobar", user) - expect(response).to have_http_status(404) + + expect(response).to have_gitlab_http_status(404) end end - describe "DELETE /projects/:id/repository/merged_branches" do + describe 'DELETE /projects/:id/repository/merged_branches' do before do allow_any_instance_of(Repository).to receive(:rm_branch).and_return(true) end @@ -416,13 +509,14 @@ describe API::Branches do it 'returns 202 with json body' do delete api("/projects/#{project.id}/repository/merged_branches", user) - expect(response).to have_http_status(202) + expect(response).to have_gitlab_http_status(202) expect(json_response['message']).to eql('202 Accepted') end it 'returns a 403 error if guest' do delete api("/projects/#{project.id}/repository/merged_branches", guest) - expect(response).to have_http_status(403) + + expect(response).to have_gitlab_http_status(403) end end end diff --git a/spec/requests/api/events_spec.rb b/spec/requests/api/events_spec.rb index a19870a95e8..1754ba66a96 100644 --- a/spec/requests/api/events_spec.rb +++ b/spec/requests/api/events_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe API::Events, api: true do +describe API::Events do include ApiHelpers let(:user) { create(:user) } let(:non_member) { create(:user) } diff --git a/spec/requests/api/group_milestones_spec.rb b/spec/requests/api/group_milestones_spec.rb new file mode 100644 index 00000000000..9b24658771f --- /dev/null +++ b/spec/requests/api/group_milestones_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe API::GroupMilestones do + let(:user) { create(:user) } + let(:group) { create(:group, :private) } + let(:project) { create(:empty_project, namespace: group) } + let!(:group_member) { create(:group_member, group: group, user: user) } + let!(:closed_milestone) { create(:closed_milestone, group: group, title: 'version1', description: 'closed milestone') } + let!(:milestone) { create(:milestone, group: group, title: 'version2', description: 'open milestone') } + + it_behaves_like 'group and project milestones', "/groups/:id/milestones" do + let(:route) { "/groups/#{group.id}/milestones" } + end + + def setup_for_group + context_group.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + context_group.add_developer(user) + public_project.update(namespace: context_group) + context_group.reload + end +end diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 656f098aea8..1d7adc6ac45 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -510,7 +510,7 @@ describe API::Groups do describe "POST /groups/:id/projects/:project_id" do let(:project) { create(:empty_project) } - let(:project_path) { project.full_path.gsub('/', '%2F') } + let(:project_path) { CGI.escape(project.full_path) } before(:each) do allow_any_instance_of(Projects::TransferService) diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb index 25ec44fa036..7a1bd76af7a 100644 --- a/spec/requests/api/helpers_spec.rb +++ b/spec/requests/api/helpers_spec.rb @@ -10,8 +10,16 @@ describe API::Helpers do let(:key) { create(:key, user: user) } let(:params) { {} } - let(:env) { { 'REQUEST_METHOD' => 'GET' } } - let(:request) { Rack::Request.new(env) } + let(:csrf_token) { SecureRandom.base64(ActionController::RequestForgeryProtection::AUTHENTICITY_TOKEN_LENGTH) } + let(:env) do + { + 'rack.input' => '', + 'rack.session' => { + _csrf_token: csrf_token + }, + 'REQUEST_METHOD' => 'GET' + } + end let(:header) { } before do @@ -58,7 +66,7 @@ describe API::Helpers do describe ".current_user" do subject { current_user } - describe "Warden authentication" do + describe "Warden authentication", :allow_forgery_protection do before do doorkeeper_guard_returns false end @@ -99,7 +107,17 @@ describe API::Helpers do env['REQUEST_METHOD'] = 'PUT' end - it { is_expected.to be_nil } + context 'without CSRF token' do + it { is_expected.to be_nil } + end + + context 'with CSRF token' do + before do + env['HTTP_X_CSRF_TOKEN'] = csrf_token + end + + it { is_expected.to eq(user) } + end end context "POST request" do @@ -107,7 +125,17 @@ describe API::Helpers do env['REQUEST_METHOD'] = 'POST' end - it { is_expected.to be_nil } + context 'without CSRF token' do + it { is_expected.to be_nil } + end + + context 'with CSRF token' do + before do + env['HTTP_X_CSRF_TOKEN'] = csrf_token + end + + it { is_expected.to eq(user) } + end end context "DELETE request" do @@ -115,7 +143,17 @@ describe API::Helpers do env['REQUEST_METHOD'] = 'DELETE' end - it { is_expected.to be_nil } + context 'without CSRF token' do + it { is_expected.to be_nil } + end + + context 'with CSRF token' do + before do + env['HTTP_X_CSRF_TOKEN'] = csrf_token + end + + it { is_expected.to eq(user) } + end end end end diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb index cab3089c6b1..ce9b9ac1eb3 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal_spec.rb @@ -6,7 +6,7 @@ describe API::Internal do let(:project) { create(:project, :repository) } let(:secret_token) { Gitlab::Shell.secret_token } - describe "GET /internal/check", no_db: true do + describe "GET /internal/check" do it do get api("/internal/check"), secret_token: secret_token diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 9837fedb522..2c44be4e447 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -71,7 +71,6 @@ describe API::Issues do expect(response).to have_http_status(401) end end - context "when authenticated" do let(:first_issue) { json_response.first } @@ -105,6 +104,42 @@ describe API::Issues do expect(json_response.second['id']).to eq(closed_issue.id) end + it 'returns issues assigned to me' do + issue2 = create(:issue, assignees: [user2], project: project) + + get api('/issues', user2), scope: 'assigned-to-me' + + expect_paginated_array_response(size: 1) + expect(first_issue['id']).to eq(issue2.id) + end + + it 'returns issues authored by the given author id' do + issue2 = create(:issue, author: user2, project: project) + + get api('/issues', user), author_id: user2.id, scope: 'all' + + expect_paginated_array_response(size: 1) + expect(first_issue['id']).to eq(issue2.id) + end + + it 'returns issues assigned to the given assignee id' do + issue2 = create(:issue, assignees: [user2], project: project) + + get api('/issues', user), assignee_id: user2.id, scope: 'all' + + expect_paginated_array_response(size: 1) + expect(first_issue['id']).to eq(issue2.id) + end + + it 'returns issues authored by the given author id and assigned to the given assignee id' do + issue2 = create(:issue, author: user2, assignees: [user2], project: project) + + get api('/issues', user), author_id: user2.id, assignee_id: user2.id, scope: 'all' + + expect_paginated_array_response(size: 1) + expect(first_issue['id']).to eq(issue2.id) + end + it 'returns issues matching given search string for title' do get api("/issues", user), search: issue.title @@ -693,6 +728,19 @@ describe API::Issues do expect(json_response['confidential']).to be_falsy end + context 'links exposure' do + it 'exposes related resources full URIs' do + get api("/projects/#{project.id}/issues/#{issue.iid}", user) + + links = json_response['_links'] + + expect(links['self']).to end_with("/api/v4/projects/#{project.id}/issues/#{issue.iid}") + expect(links['notes']).to end_with("/api/v4/projects/#{project.id}/issues/#{issue.iid}/notes") + expect(links['award_emoji']).to end_with("/api/v4/projects/#{project.id}/issues/#{issue.iid}/award_emoji") + expect(links['project']).to end_with("/api/v4/projects/#{project.id}") + end + end + it "returns a project issue by internal id" do get api("/projects/#{project.id}/issues/#{issue.iid}", user) @@ -1211,7 +1259,7 @@ describe API::Issues do put api("/projects/#{project.id}/issues/#{closed_issue.iid}", user), state_event: 'reopen' expect(response).to have_http_status(200) - expect(json_response['state']).to eq 'reopened' + expect(json_response['state']).to eq 'opened' end context 'when an admin or owner makes the request' do diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb index 8d647eb1c7e..f56baf9663d 100644 --- a/spec/requests/api/jobs_spec.rb +++ b/spec/requests/api/jobs_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe API::Jobs, :api do +describe API::Jobs do let!(:project) do create(:project, :repository, public_builds: false) end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 9098ae6bcda..2760c4ffde2 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -26,6 +26,100 @@ describe API::MergeRequests do project.team << [user, :reporter] end + describe 'GET /merge_requests' do + context 'when unauthenticated' do + it 'returns authentication error' do + get api('/merge_requests') + + expect(response).to have_http_status(401) + end + end + + context 'when authenticated' do + let!(:project2) { create(:empty_project, :public, namespace: user.namespace) } + let!(:merge_request2) { create(:merge_request, :simple, author: user, assignee: user, source_project: project2, target_project: project2) } + let(:user2) { create(:user) } + + it 'returns an array of all merge requests' do + get api('/merge_requests', user), scope: :all + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |mr| mr['id'] }) + .to contain_exactly(merge_request.id, merge_request_closed.id, merge_request_merged.id, merge_request2.id) + end + + it 'does not return unauthorized merge requests' do + private_project = create(:empty_project, :private) + merge_request3 = create(:merge_request, :simple, source_project: private_project, target_project: private_project, source_branch: 'other-branch') + + get api('/merge_requests', user), scope: :all + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |mr| mr['id'] }) + .not_to include(merge_request3.id) + end + + it 'returns an array of merge requests created by current user if no scope is given' do + merge_request3 = create(:merge_request, :simple, author: user2, assignee: user, source_project: project2, target_project: project2, source_branch: 'other-branch') + + get api('/merge_requests', user2) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['id']).to eq(merge_request3.id) + end + + it 'returns an array of merge requests authored by the given user' do + merge_request3 = create(:merge_request, :simple, author: user2, assignee: user, source_project: project2, target_project: project2, source_branch: 'other-branch') + + get api('/merge_requests', user), author_id: user2.id, scope: :all + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['id']).to eq(merge_request3.id) + end + + it 'returns an array of merge requests assigned to the given user' do + merge_request3 = create(:merge_request, :simple, author: user, assignee: user2, source_project: project2, target_project: project2, source_branch: 'other-branch') + + get api('/merge_requests', user), assignee_id: user2.id, scope: :all + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['id']).to eq(merge_request3.id) + end + + it 'returns an array of merge requests assigned to me' do + merge_request3 = create(:merge_request, :simple, author: user, assignee: user2, source_project: project2, target_project: project2, source_branch: 'other-branch') + + get api('/merge_requests', user2), scope: 'assigned-to-me' + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['id']).to eq(merge_request3.id) + end + + it 'returns an array of merge requests created by me' do + merge_request3 = create(:merge_request, :simple, author: user2, assignee: user, source_project: project2, target_project: project2, source_branch: 'other-branch') + + get api('/merge_requests', user2), scope: 'created-by-me' + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['id']).to eq(merge_request3.id) + end + end + end + describe "GET /projects/:id/merge_requests" do context "when unauthenticated" do it "returns authentication error" do @@ -794,18 +888,24 @@ describe API::MergeRequests do it 'handles external issues' do jira_project = create(:jira_project, :public, name: 'JIR_EXT1') - issue = ExternalIssue.new("#{jira_project.name}-123", jira_project) - merge_request = create(:merge_request, :simple, author: user, assignee: user, source_project: jira_project) - merge_request.update_attribute(:description, "Closes #{issue.to_reference(jira_project)}") + ext_issue = ExternalIssue.new("#{jira_project.name}-123", jira_project) + issue = create(:issue, project: jira_project) + description = "Closes #{ext_issue.to_reference(jira_project)}\ncloses #{issue.to_reference}" + merge_request = create(:merge_request, + :simple, author: user, assignee: user, source_project: jira_project, description: description) get api("/projects/#{jira_project.id}/merge_requests/#{merge_request.iid}/closes_issues", user) expect(response).to have_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.length).to eq(1) + expect(json_response.length).to eq(2) + expect(json_response.second['title']).to eq(ext_issue.title) + expect(json_response.second['id']).to eq(ext_issue.id) + expect(json_response.second['confidential']).to be_nil expect(json_response.first['title']).to eq(issue.title) expect(json_response.first['id']).to eq(issue.id) + expect(json_response.first['confidential']).not_to be_nil end it 'returns 403 if the user has no access to the merge request' do diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb deleted file mode 100644 index ab5ea3e8f2c..00000000000 --- a/spec/requests/api/milestones_spec.rb +++ /dev/null @@ -1,385 +0,0 @@ -require 'spec_helper' - -describe API::Milestones do - let(:user) { create(:user) } - let!(:project) { create(:empty_project, namespace: user.namespace ) } - let!(:closed_milestone) { create(:closed_milestone, project: project, title: 'version1', description: 'closed milestone') } - let!(:milestone) { create(:milestone, project: project, title: 'version2', description: 'open milestone') } - let(:label_1) { create(:label, title: 'label_1', project: project, priority: 1) } - let(:label_2) { create(:label, title: 'label_2', project: project, priority: 2) } - let(:label_3) { create(:label, title: 'label_3', project: project) } - - before do - project.team << [user, :developer] - end - - describe 'GET /projects/:id/milestones' do - it 'returns project milestones' do - get api("/projects/#{project.id}/milestones", user) - - expect(response).to have_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.first['title']).to eq(milestone.title) - end - - it 'returns a 401 error if user not authenticated' do - get api("/projects/#{project.id}/milestones") - - expect(response).to have_http_status(401) - end - - it 'returns an array of active milestones' do - get api("/projects/#{project.id}/milestones?state=active", user) - - expect(response).to have_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.length).to eq(1) - expect(json_response.first['id']).to eq(milestone.id) - end - - it 'returns an array of closed milestones' do - get api("/projects/#{project.id}/milestones?state=closed", user) - - expect(response).to have_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.length).to eq(1) - expect(json_response.first['id']).to eq(closed_milestone.id) - end - - it 'returns an array of milestones specified by iids' do - other_milestone = create(:milestone, project: project) - - get api("/projects/#{project.id}/milestones", user), iids: [closed_milestone.iid, other_milestone.iid] - - expect(response).to have_http_status(200) - expect(json_response).to be_an Array - expect(json_response.length).to eq(2) - expect(json_response.map{ |m| m['id'] }).to match_array([closed_milestone.id, other_milestone.id]) - end - - it 'does not return any milestone if none found' do - get api("/projects/#{project.id}/milestones", user), iids: [Milestone.maximum(:iid).succ] - - expect(response).to have_http_status(200) - expect(json_response).to be_an Array - expect(json_response.length).to eq(0) - end - end - - describe 'GET /projects/:id/milestones/:milestone_id' do - it 'returns a project milestone by id' do - get api("/projects/#{project.id}/milestones/#{milestone.id}", user) - - expect(response).to have_http_status(200) - expect(json_response['title']).to eq(milestone.title) - expect(json_response['iid']).to eq(milestone.iid) - end - - it 'returns a project milestone by iids array' do - get api("/projects/#{project.id}/milestones?iids=#{closed_milestone.iid}", user) - - expect(response.status).to eq 200 - expect(response).to include_pagination_headers - expect(json_response.size).to eq(1) - expect(json_response.size).to eq(1) - expect(json_response.first['title']).to eq closed_milestone.title - expect(json_response.first['id']).to eq closed_milestone.id - end - - it 'returns a project milestone by searching for title' do - get api("/projects/#{project.id}/milestones", user), search: 'version2' - - expect(response).to have_http_status(200) - expect(response).to include_pagination_headers - expect(json_response.size).to eq(1) - expect(json_response.first['title']).to eq milestone.title - expect(json_response.first['id']).to eq milestone.id - end - - it 'returns a project milestones by searching for description' do - get api("/projects/#{project.id}/milestones", user), search: 'open' - - expect(response).to have_http_status(200) - expect(response).to include_pagination_headers - expect(json_response.size).to eq(1) - expect(json_response.first['title']).to eq milestone.title - expect(json_response.first['id']).to eq milestone.id - end - end - - describe 'GET /projects/:id/milestones/:milestone_id' do - it 'returns a project milestone by id' do - get api("/projects/#{project.id}/milestones/#{milestone.id}", user) - - expect(response).to have_http_status(200) - expect(json_response['title']).to eq(milestone.title) - expect(json_response['iid']).to eq(milestone.iid) - end - - it 'returns 401 error if user not authenticated' do - get api("/projects/#{project.id}/milestones/#{milestone.id}") - - expect(response).to have_http_status(401) - end - - it 'returns a 404 error if milestone id not found' do - get api("/projects/#{project.id}/milestones/1234", user) - - expect(response).to have_http_status(404) - end - end - - describe 'POST /projects/:id/milestones' do - it 'creates a new project milestone' do - post api("/projects/#{project.id}/milestones", user), title: 'new milestone' - - expect(response).to have_http_status(201) - expect(json_response['title']).to eq('new milestone') - expect(json_response['description']).to be_nil - end - - it 'creates a new project milestone with description and dates' do - post api("/projects/#{project.id}/milestones", user), - title: 'new milestone', description: 'release', due_date: '2013-03-02', start_date: '2013-02-02' - - expect(response).to have_http_status(201) - expect(json_response['description']).to eq('release') - expect(json_response['due_date']).to eq('2013-03-02') - expect(json_response['start_date']).to eq('2013-02-02') - end - - it 'returns a 400 error if title is missing' do - post api("/projects/#{project.id}/milestones", user) - - expect(response).to have_http_status(400) - end - - it 'returns a 400 error if params are invalid (duplicate title)' do - post api("/projects/#{project.id}/milestones", user), - title: milestone.title, description: 'release', due_date: '2013-03-02' - - expect(response).to have_http_status(400) - end - - it 'creates a new project with reserved html characters' do - post api("/projects/#{project.id}/milestones", user), title: 'foo & bar 1.1 -> 2.2' - - expect(response).to have_http_status(201) - expect(json_response['title']).to eq('foo & bar 1.1 -> 2.2') - expect(json_response['description']).to be_nil - end - end - - describe 'PUT /projects/:id/milestones/:milestone_id' do - it 'updates a project milestone' do - put api("/projects/#{project.id}/milestones/#{milestone.id}", user), - title: 'updated title' - - expect(response).to have_http_status(200) - expect(json_response['title']).to eq('updated title') - end - - it 'removes a due date if nil is passed' do - milestone.update!(due_date: "2016-08-05") - - put api("/projects/#{project.id}/milestones/#{milestone.id}", user), due_date: nil - - expect(response).to have_http_status(200) - expect(json_response['due_date']).to be_nil - end - - it 'returns a 404 error if milestone id not found' do - put api("/projects/#{project.id}/milestones/1234", user), - title: 'updated title' - - expect(response).to have_http_status(404) - end - end - - describe 'PUT /projects/:id/milestones/:milestone_id to close milestone' do - it 'updates a project milestone' do - put api("/projects/#{project.id}/milestones/#{milestone.id}", user), - state_event: 'close' - expect(response).to have_http_status(200) - - expect(json_response['state']).to eq('closed') - end - end - - describe 'PUT /projects/:id/milestones/:milestone_id to test observer on close' do - it 'creates an activity event when an milestone is closed' do - expect(Event).to receive(:create) - - put api("/projects/#{project.id}/milestones/#{milestone.id}", user), - state_event: 'close' - end - end - - describe 'GET /projects/:id/milestones/:milestone_id/issues' do - before do - milestone.issues << create(:issue, project: project) - end - it 'returns project issues for a particular milestone' do - get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user) - - expect(response).to have_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.first['milestone']['title']).to eq(milestone.title) - end - - it 'returns project issues sorted by label priority' do - issue_1 = create(:labeled_issue, project: project, milestone: milestone, labels: [label_3]) - issue_2 = create(:labeled_issue, project: project, milestone: milestone, labels: [label_1]) - issue_3 = create(:labeled_issue, project: project, milestone: milestone, labels: [label_2]) - - get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user) - - expect(json_response.first['id']).to eq(issue_2.id) - expect(json_response.second['id']).to eq(issue_3.id) - expect(json_response.third['id']).to eq(issue_1.id) - end - - it 'matches V4 response schema for a list of issues' do - get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user) - - expect(response).to have_http_status(200) - expect(response).to match_response_schema('public_api/v4/issues') - end - - it 'returns a 401 error if user not authenticated' do - get api("/projects/#{project.id}/milestones/#{milestone.id}/issues") - - expect(response).to have_http_status(401) - end - - describe 'confidential issues' do - let(:public_project) { create(:empty_project, :public) } - let(:milestone) { create(:milestone, project: public_project) } - let(:issue) { create(:issue, project: public_project) } - let(:confidential_issue) { create(:issue, confidential: true, project: public_project) } - - before do - public_project.team << [user, :developer] - milestone.issues << issue << confidential_issue - end - - it 'returns confidential issues to team members' do - get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", user) - - 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) - expect(json_response.map { |issue| issue['id'] }).to include(issue.id, confidential_issue.id) - end - - it 'does not return confidential issues to team members with guest role' do - member = create(:user) - project.team << [member, :guest] - - get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", member) - - 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(1) - expect(json_response.map { |issue| issue['id'] }).to include(issue.id) - end - - it 'does not return confidential issues to regular users' do - get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", create(:user)) - - 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(1) - expect(json_response.map { |issue| issue['id'] }).to include(issue.id) - end - - it 'returns issues ordered by label priority' do - issue.labels << label_2 - confidential_issue.labels << label_1 - - get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", user) - - 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) - expect(json_response.first['id']).to eq(confidential_issue.id) - expect(json_response.second['id']).to eq(issue.id) - end - end - end - - describe 'GET /projects/:id/milestones/:milestone_id/merge_requests' do - let(:merge_request) { create(:merge_request, source_project: project) } - let(:another_merge_request) { create(:merge_request, :simple, source_project: project) } - - before do - milestone.merge_requests << merge_request - end - - it 'returns project merge_requests for a particular milestone' do - # eager-load another_merge_request - another_merge_request - get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", 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['title']).to eq(merge_request.title) - expect(json_response.first['milestone']['title']).to eq(milestone.title) - end - - it 'returns project merge_requests sorted by label priority' do - merge_request_1 = create(:labeled_merge_request, source_branch: 'branch_1', source_project: project, milestone: milestone, labels: [label_2]) - merge_request_2 = create(:labeled_merge_request, source_branch: 'branch_2', source_project: project, milestone: milestone, labels: [label_1]) - merge_request_3 = create(:labeled_merge_request, source_branch: 'branch_3', source_project: project, milestone: milestone, labels: [label_3]) - - get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", user) - - expect(json_response.first['id']).to eq(merge_request_2.id) - expect(json_response.second['id']).to eq(merge_request_1.id) - expect(json_response.third['id']).to eq(merge_request_3.id) - end - - it 'returns a 404 error if milestone id not found' do - get api("/projects/#{project.id}/milestones/1234/merge_requests", user) - - expect(response).to have_http_status(404) - end - - it 'returns a 404 if the user has no access to the milestone' do - new_user = create :user - get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", new_user) - - expect(response).to have_http_status(404) - end - - it 'returns a 401 error if user not authenticated' do - get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests") - - expect(response).to have_http_status(401) - end - - it 'returns merge_requests ordered by position asc' do - milestone.merge_requests << another_merge_request - another_merge_request.labels << label_1 - merge_request.labels << label_2 - - get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", user) - - 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) - expect(json_response.first['id']).to eq(another_merge_request.id) - expect(json_response.second['id']).to eq(merge_request.id) - end - end -end diff --git a/spec/requests/api/project_milestones_spec.rb b/spec/requests/api/project_milestones_spec.rb new file mode 100644 index 00000000000..fe8fdbfd7e4 --- /dev/null +++ b/spec/requests/api/project_milestones_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe API::ProjectMilestones do + let(:user) { create(:user) } + let!(:project) { create(:empty_project, namespace: user.namespace ) } + let!(:closed_milestone) { create(:closed_milestone, project: project, title: 'version1', description: 'closed milestone') } + let!(:milestone) { create(:milestone, project: project, title: 'version2', description: 'open milestone') } + + before do + project.team << [user, :developer] + end + + it_behaves_like 'group and project milestones', "/projects/:id/milestones" do + let(:route) { "/projects/#{project.id}/milestones" } + end + + describe 'PUT /projects/:id/milestones/:milestone_id to test observer on close' do + it 'creates an activity event when an milestone is closed' do + expect(Event).to receive(:create) + + put api("/projects/#{project.id}/milestones/#{milestone.id}", user), + state_event: 'close' + end + end +end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 6dbde8bad31..6ed68fcff09 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -159,6 +159,31 @@ describe API::Projects do expect(json_response.first).to include 'statistics' end + context 'when external issue tracker is enabled' do + let!(:jira_service) { create(:jira_service, project: project) } + + it 'includes open_issues_count' do + get api('/projects', user) + + expect(response.status).to eq 200 + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.first.keys).to include('open_issues_count') + expect(json_response.find { |hash| hash['id'] == project.id }.keys).to include('open_issues_count') + end + + it 'does not include open_issues_count if issues are disabled' do + project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED) + + get api('/projects', user) + + expect(response.status).to eq 200 + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.find { |hash| hash['id'] == project.id }.keys).not_to include('open_issues_count') + end + end + context 'and with simple=true' do it 'returns a simplified version of all the projects' do expected_keys = %w(id http_url_to_repo web_url name name_with_namespace path path_with_namespace) @@ -743,7 +768,7 @@ describe API::Projects do dot_user = create(:user, username: 'dot.user') project = create(:empty_project, creator_id: dot_user.id, namespace: dot_user.namespace) - get api("/projects/#{dot_user.namespace.name}%2F#{project.path}", dot_user) + get api("/projects/#{CGI.escape(project.full_path)}", dot_user) expect(response).to have_http_status(200) expect(json_response['name']).to eq(project.name) end @@ -790,6 +815,38 @@ describe API::Projects do expect(json_response).not_to include("import_error") end + context 'links exposure' do + it 'exposes related resources full URIs' do + get api("/projects/#{project.id}", user) + + links = json_response['_links'] + + expect(links['self']).to end_with("/api/v4/projects/#{project.id}") + expect(links['issues']).to end_with("/api/v4/projects/#{project.id}/issues") + expect(links['merge_requests']).to end_with("/api/v4/projects/#{project.id}/merge_requests") + expect(links['repo_branches']).to end_with("/api/v4/projects/#{project.id}/repository/branches") + expect(links['labels']).to end_with("/api/v4/projects/#{project.id}/labels") + expect(links['events']).to end_with("/api/v4/projects/#{project.id}/events") + expect(links['members']).to end_with("/api/v4/projects/#{project.id}/members") + end + + it 'filters related URIs when their feature is not enabled' do + project = create(:empty_project, :public, + :merge_requests_disabled, + :issues_disabled, + creator_id: user.id, + namespace: user.namespace) + + get api("/projects/#{project.id}", user) + + links = json_response['_links'] + + expect(links.has_key?('merge_requests')).to be_falsy + expect(links.has_key?('issues')).to be_falsy + expect(links['self']).to end_with("/api/v4/projects/#{project.id}") + end + end + describe 'permissions' do context 'all projects' do before do diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index b71ac6c30b5..c3ed5cd8ece 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -11,7 +11,7 @@ describe API::Settings, 'Settings' do expect(json_response).to be_an Hash expect(json_response['default_projects_limit']).to eq(42) expect(json_response['password_authentication_enabled']).to be_truthy - expect(json_response['repository_storage']).to eq('default') + expect(json_response['repository_storages']).to eq(['default']) expect(json_response['koding_enabled']).to be_falsey expect(json_response['koding_url']).to be_nil expect(json_response['plantuml_enabled']).to be_falsey @@ -33,7 +33,7 @@ describe API::Settings, 'Settings' do put api("/application/settings", admin), default_projects_limit: 3, password_authentication_enabled: false, - repository_storage: 'custom', + repository_storages: ['custom'], koding_enabled: true, koding_url: 'http://koding.example.com', plantuml_enabled: true, @@ -47,7 +47,6 @@ describe API::Settings, 'Settings' do expect(response).to have_http_status(200) expect(json_response['default_projects_limit']).to eq(3) expect(json_response['password_authentication_enabled']).to be_falsey - expect(json_response['repository_storage']).to eq('custom') expect(json_response['repository_storages']).to eq(['custom']) expect(json_response['koding_enabled']).to be_truthy expect(json_response['koding_url']).to eq('http://koding.example.com') diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb index 92533f4dfea..9fc73c6e092 100644 --- a/spec/requests/api/todos_spec.rb +++ b/spec/requests/api/todos_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe API::Todos do - let(:project_1) { create(:empty_project, :test_repo) } + let(:project_1) { create(:project) } let(:project_2) { create(:empty_project) } let(:author_1) { create(:user) } let(:author_2) { create(:user) } diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb index 16ddade27d9..153596c2975 100644 --- a/spec/requests/api/triggers_spec.rb +++ b/spec/requests/api/triggers_spec.rb @@ -22,6 +22,7 @@ describe API::Triggers do before do stub_ci_pipeline_to_return_yaml_file + trigger.update(owner: user) end context 'Handles errors' do @@ -36,12 +37,6 @@ describe API::Triggers do expect(response).to have_http_status(404) end - - it 'returns unauthorized if token is for different project' do - post api("/projects/#{project2.id}/trigger/pipeline"), options.merge(ref: 'master') - - expect(response).to have_http_status(401) - end end context 'Have a commit' do @@ -61,7 +56,7 @@ describe API::Triggers do post api("/projects/#{project.id}/trigger/pipeline"), options.merge(ref: 'other-branch') expect(response).to have_http_status(400) - expect(json_response['message']).to eq('No pipeline created') + expect(json_response['message']).to eq('base' => ["Reference not found"]) end context 'Validates variables' do @@ -87,12 +82,18 @@ describe API::Triggers do post api("/projects/#{project.id}/trigger/pipeline"), options.merge(variables: variables, ref: 'master') expect(response).to have_http_status(201) - expect(pipeline.builds.reload.first.trigger_request.variables).to eq(variables) + expect(pipeline.variables.map { |v| { v.key => v.value } }.last).to eq(variables) end end end context 'when triggering a pipeline from a trigger token' do + it 'does not leak the presence of project when token is for different project' do + post api("/projects/#{project2.id}/ref/master/trigger/pipeline?token=#{trigger_token}"), { ref: 'refs/heads/other-branch' } + + expect(response).to have_http_status(404) + end + it 'creates builds from the ref given in the URL, not in the body' do expect do post api("/projects/#{project.id}/ref/master/trigger/pipeline?token=#{trigger_token}"), { ref: 'refs/heads/other-branch' } diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 877bde3b9a6..66b165b438b 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -55,17 +55,22 @@ describe API::Users do context "when public level is restricted" do before do stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) - allow_any_instance_of(API::Helpers).to receive(:authenticate!).and_return(true) end - it "renders 403" do - get api("/users") - expect(response).to have_http_status(403) + context 'when authenticate as a regular user' do + it "renders 403" do + get api("/users", user) + + expect(response).to have_gitlab_http_status(403) + end end - it "renders 404" do - get api("/users/#{user.id}") - expect(response).to have_http_status(404) + context 'when authenticate as an admin' do + it "renders 200" do + get api("/users", admin) + + expect(response).to have_gitlab_http_status(200) + end end end diff --git a/spec/requests/api/v3/groups_spec.rb b/spec/requests/api/v3/groups_spec.rb index 63c5707b2e4..5cdc528e190 100644 --- a/spec/requests/api/v3/groups_spec.rb +++ b/spec/requests/api/v3/groups_spec.rb @@ -502,7 +502,7 @@ describe API::V3::Groups do describe "POST /groups/:id/projects/:project_id" do let(:project) { create(:empty_project) } - let(:project_path) { "#{project.namespace.path}%2F#{project.path}" } + let(:project_path) { CGI.escape(project.full_path) } before(:each) do allow_any_instance_of(Projects::TransferService) diff --git a/spec/requests/api/v3/issues_spec.rb b/spec/requests/api/v3/issues_spec.rb index cc81922697a..4dff09b6df8 100644 --- a/spec/requests/api/v3/issues_spec.rb +++ b/spec/requests/api/v3/issues_spec.rb @@ -1114,7 +1114,7 @@ describe API::V3::Issues do put v3_api("/projects/#{project.id}/issues/#{closed_issue.id}", user), state_event: 'reopen' expect(response).to have_http_status(200) - expect(json_response['state']).to eq 'reopened' + expect(json_response['state']).to eq 'opened' end context 'when an admin or owner makes the request' do diff --git a/spec/requests/api/v3/project_hooks_spec.rb b/spec/requests/api/v3/project_hooks_spec.rb index 1969d1c7f2b..b0eddbb5dd2 100644 --- a/spec/requests/api/v3/project_hooks_spec.rb +++ b/spec/requests/api/v3/project_hooks_spec.rb @@ -87,7 +87,7 @@ describe API::ProjectHooks, 'ProjectHooks' 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 + url: "http://example.com", issues_events: true, wiki_page_events: true, build_events: true end.to change {project.hooks.count}.by(1) expect(response).to have_http_status(201) @@ -97,7 +97,7 @@ describe API::ProjectHooks, 'ProjectHooks' 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['build_events']).to eq(true) 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) @@ -135,7 +135,7 @@ describe API::ProjectHooks, 'ProjectHooks' do 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 + url: 'http://example.org', push_events: false, build_events: true 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) diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb index af44ffa2331..bbfcaab1ea1 100644 --- a/spec/requests/api/v3/projects_spec.rb +++ b/spec/requests/api/v3/projects_spec.rb @@ -720,7 +720,7 @@ describe API::V3::Projects do dot_user = create(:user, username: 'dot.user') project = create(:empty_project, creator_id: dot_user.id, namespace: dot_user.namespace) - get v3_api("/projects/#{dot_user.namespace.name}%2F#{project.path}", dot_user) + get v3_api("/projects/#{CGI.escape(project.full_path)}", dot_user) expect(response).to have_http_status(200) expect(json_response['name']).to eq(project.name) end diff --git a/spec/requests/api/v3/triggers_spec.rb b/spec/requests/api/v3/triggers_spec.rb index d3de6bf13bc..60212660fb6 100644 --- a/spec/requests/api/v3/triggers_spec.rb +++ b/spec/requests/api/v3/triggers_spec.rb @@ -52,7 +52,8 @@ describe API::V3::Triggers do it 'returns bad request with no builds created if there\'s no commit for that ref' do post v3_api("/projects/#{project.id}/trigger/builds"), options.merge(ref: 'other-branch') expect(response).to have_http_status(400) - expect(json_response['message']).to eq('No builds created') + expect(json_response['message']['base']) + .to contain_exactly('Reference not found') end context 'Validates variables' do diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index c969d08d0dd..49e815ee16c 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -69,6 +69,72 @@ describe Ci::API::Builds do end end + context 'when an old image syntax is used' do + before do + build.update!(options: { image: 'codeclimate' }) + end + + it 'starts a build' do + register_builds info: { platform: :darwin } + + expect(response).to have_http_status(201) + expect(json_response["options"]).to eq({ "image" => "codeclimate" }) + end + end + + context 'when a new image syntax is used' do + before do + build.update!(options: { image: { name: 'codeclimate' } }) + end + + it 'starts a build' do + register_builds info: { platform: :darwin } + + expect(response).to have_http_status(201) + expect(json_response["options"]).to eq({ "image" => "codeclimate" }) + end + end + + context 'when an old service syntax is used' do + before do + build.update!(options: { services: ['mysql'] }) + end + + it 'starts a build' do + register_builds info: { platform: :darwin } + + expect(response).to have_http_status(201) + expect(json_response["options"]).to eq({ "services" => ["mysql"] }) + end + end + + context 'when a new service syntax is used' do + before do + build.update!(options: { services: [name: 'mysql'] }) + end + + it 'starts a build' do + register_builds info: { platform: :darwin } + + expect(response).to have_http_status(201) + expect(json_response["options"]).to eq({ "services" => ["mysql"] }) + end + end + + context 'when no image or service is defined' do + before do + build.update!(options: {}) + end + + it 'starts a build' do + register_builds info: { platform: :darwin } + + expect(response).to have_http_status(201) + + expect(json_response["options"]).to be_empty + end + end + context 'when there is a pending build' do it 'starts a build' do register_builds info: { platform: :darwin } diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb index 26b03c0f148..e481ca916ab 100644 --- a/spec/requests/ci/api/triggers_spec.rb +++ b/spec/requests/ci/api/triggers_spec.rb @@ -5,7 +5,14 @@ describe Ci::API::Triggers do let!(:trigger_token) { 'secure token' } let!(:project) { create(:project, :repository, ci_id: 10) } let!(:project2) { create(:empty_project, ci_id: 11) } - let!(:trigger) { create(:ci_trigger, project: project, token: trigger_token) } + + let!(:trigger) do + create(:ci_trigger, + project: project, + token: trigger_token, + owner: create(:user)) + end + let(:options) do { token: trigger_token @@ -14,6 +21,8 @@ describe Ci::API::Triggers do before do stub_ci_pipeline_to_return_yaml_file + + project.add_developer(trigger.owner) end context 'Handles errors' do @@ -47,7 +56,8 @@ describe Ci::API::Triggers do it 'returns bad request with no builds created if there\'s no commit for that ref' do post ci_api("/projects/#{project.ci_id}/refs/other-branch/trigger"), options expect(response).to have_http_status(400) - expect(json_response['message']).to eq('No builds created') + expect(json_response['message']['base']) + .to contain_exactly('Reference not found') end context 'Validates variables' do diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index d043ab2a974..d4a3e8b13e1 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe 'Git HTTP requests', lib: true do +describe 'Git HTTP requests' do include GitHttpHelpers include WorkhorseHelpers include UserActivitiesHelpers diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb index e78d2cfdb33..e5d9d3df5a8 100644 --- a/spec/requests/projects/cycle_analytics_events_spec.rb +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'cycle analytics events', api: true do +describe 'cycle analytics events' do let(:user) { create(:user) } let(:project) { create(:project, :repository, public_builds: false) } let(:issue) { create(:issue, project: project, created_at: 2.days.ago) } |