summaryrefslogtreecommitdiff
path: root/spec/requests/api
diff options
context:
space:
mode:
Diffstat (limited to 'spec/requests/api')
-rw-r--r--spec/requests/api/access_requests_spec.rb2
-rw-r--r--spec/requests/api/api_internal_helpers_spec.rb32
-rw-r--r--spec/requests/api/award_emoji_spec.rb6
-rw-r--r--spec/requests/api/boards_spec.rb4
-rw-r--r--spec/requests/api/branches_spec.rb519
-rw-r--r--spec/requests/api/circuit_breakers_spec.rb57
-rw-r--r--spec/requests/api/commit_statuses_spec.rb114
-rw-r--r--spec/requests/api/commits_spec.rb820
-rw-r--r--spec/requests/api/deploy_keys_spec.rb95
-rw-r--r--spec/requests/api/doorkeeper_access_spec.rb2
-rw-r--r--spec/requests/api/environments_spec.rb11
-rw-r--r--spec/requests/api/events_spec.rb200
-rw-r--r--spec/requests/api/features_spec.rb252
-rw-r--r--spec/requests/api/files_spec.rb43
-rw-r--r--spec/requests/api/group_milestones_spec.rb21
-rw-r--r--spec/requests/api/group_variables_spec.rb221
-rw-r--r--spec/requests/api/groups_spec.rb26
-rw-r--r--spec/requests/api/helpers_spec.rb99
-rw-r--r--spec/requests/api/internal_spec.rb324
-rw-r--r--spec/requests/api/issues_spec.rb181
-rw-r--r--spec/requests/api/jobs_spec.rb167
-rw-r--r--spec/requests/api/keys_spec.rb4
-rw-r--r--spec/requests/api/labels_spec.rb14
-rw-r--r--spec/requests/api/members_spec.rb2
-rw-r--r--spec/requests/api/merge_requests_spec.rb406
-rw-r--r--spec/requests/api/milestones_spec.rb351
-rw-r--r--spec/requests/api/namespaces_spec.rb35
-rw-r--r--spec/requests/api/notes_spec.rb24
-rw-r--r--spec/requests/api/notification_settings_spec.rb6
-rw-r--r--spec/requests/api/oauth_tokens_spec.rb2
-rw-r--r--spec/requests/api/pipeline_schedules_spec.rb299
-rw-r--r--spec/requests/api/pipelines_spec.rb259
-rw-r--r--spec/requests/api/project_hooks_spec.rb8
-rw-r--r--spec/requests/api/project_milestones_spec.rb25
-rw-r--r--spec/requests/api/project_snippets_spec.rb84
-rw-r--r--spec/requests/api/projects_spec.rb405
-rw-r--r--spec/requests/api/protected_branches_spec.rb232
-rw-r--r--spec/requests/api/runner_spec.rb111
-rw-r--r--spec/requests/api/runners_spec.rb34
-rw-r--r--spec/requests/api/services_spec.rb4
-rw-r--r--spec/requests/api/settings_spec.rb22
-rw-r--r--spec/requests/api/snippets_spec.rb84
-rw-r--r--spec/requests/api/system_hooks_spec.rb7
-rw-r--r--spec/requests/api/tags_spec.rb471
-rw-r--r--spec/requests/api/templates_spec.rb12
-rw-r--r--spec/requests/api/todos_spec.rb4
-rw-r--r--spec/requests/api/triggers_spec.rb23
-rw-r--r--spec/requests/api/users_spec.rb399
-rw-r--r--spec/requests/api/v3/award_emoji_spec.rb2
-rw-r--r--spec/requests/api/v3/boards_spec.rb4
-rw-r--r--spec/requests/api/v3/branches_spec.rb13
-rw-r--r--spec/requests/api/v3/commits_spec.rb5
-rw-r--r--spec/requests/api/v3/deploy_keys_spec.rb19
-rw-r--r--spec/requests/api/v3/environments_spec.rb2
-rw-r--r--spec/requests/api/v3/files_spec.rb10
-rw-r--r--spec/requests/api/v3/groups_spec.rb26
-rw-r--r--spec/requests/api/v3/issues_spec.rb51
-rw-r--r--spec/requests/api/v3/labels_spec.rb2
-rw-r--r--spec/requests/api/v3/members_spec.rb2
-rw-r--r--spec/requests/api/v3/merge_requests_spec.rb55
-rw-r--r--spec/requests/api/v3/milestones_spec.rb4
-rw-r--r--spec/requests/api/v3/notes_spec.rb12
-rw-r--r--spec/requests/api/v3/project_hooks_spec.rb10
-rw-r--r--spec/requests/api/v3/project_snippets_spec.rb36
-rw-r--r--spec/requests/api/v3/projects_spec.rb148
-rw-r--r--spec/requests/api/v3/runners_spec.rb16
-rw-r--r--spec/requests/api/v3/services_spec.rb2
-rw-r--r--spec/requests/api/v3/settings_spec.rb6
-rw-r--r--spec/requests/api/v3/snippets_spec.rb16
-rw-r--r--spec/requests/api/v3/system_hooks_spec.rb3
-rw-r--r--spec/requests/api/v3/todos_spec.rb4
-rw-r--r--spec/requests/api/v3/triggers_spec.rb7
-rw-r--r--spec/requests/api/v3/users_spec.rb84
-rw-r--r--spec/requests/api/variables_spec.rb24
-rw-r--r--spec/requests/api/version_spec.rb4
75 files changed, 5157 insertions, 1933 deletions
diff --git a/spec/requests/api/access_requests_spec.rb b/spec/requests/api/access_requests_spec.rb
index c8eacb38e6f..6bd17697c33 100644
--- a/spec/requests/api/access_requests_spec.rb
+++ b/spec/requests/api/access_requests_spec.rb
@@ -7,7 +7,7 @@ describe API::AccessRequests do
let(:stranger) { create(:user) }
let(:project) do
- create(:empty_project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
+ create(:project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
project.team << [developer, :developer]
project.team << [master, :master]
project.request_access(access_requester)
diff --git a/spec/requests/api/api_internal_helpers_spec.rb b/spec/requests/api/api_internal_helpers_spec.rb
deleted file mode 100644
index f5265ea60ff..00000000000
--- a/spec/requests/api/api_internal_helpers_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require 'spec_helper'
-
-describe ::API::Helpers::InternalHelpers do
- include ::API::Helpers::InternalHelpers
-
- describe '.clean_project_path' do
- project = 'namespace/project'
- namespaced = File.join('namespace2', project)
-
- {
- File.join(Dir.pwd, project) => project,
- File.join(Dir.pwd, namespaced) => namespaced,
- project => project,
- namespaced => namespaced,
- project + '.git' => project,
- namespaced + '.git' => namespaced,
- "/" + project => project,
- "/" + namespaced => namespaced,
- }.each do |project_path, expected|
- context project_path do
- # Relative and absolute storage paths, with and without trailing /
- ['.', './', Dir.pwd, Dir.pwd + '/'].each do |storage_path|
- context "storage path is #{storage_path}" do
- subject { clean_project_path(project_path, [{ 'path' => storage_path }]) }
-
- it { is_expected.to eq(expected) }
- end
- end
- end
- end
- end
-end
diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb
index bbdef0aeb1b..1dd9f3f6ddc 100644
--- a/spec/requests/api/award_emoji_spec.rb
+++ b/spec/requests/api/award_emoji_spec.rb
@@ -2,14 +2,16 @@ require 'spec_helper'
describe API::AwardEmoji do
let(:user) { create(:user) }
- let!(:project) { create(:empty_project) }
+ let!(:project) { create(:project) }
let(:issue) { create(:issue, project: project) }
let!(:award_emoji) { create(:award_emoji, awardable: issue, user: user) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let!(:downvote) { create(:award_emoji, :downvote, awardable: merge_request, user: user) }
let!(:note) { create(:note, project: project, noteable: issue) }
- before { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
describe "GET /projects/:id/awardable/:awardable_id/award_emoji" do
context 'on an issue' do
diff --git a/spec/requests/api/boards_spec.rb b/spec/requests/api/boards_spec.rb
index c27db716ef8..43b381c2219 100644
--- a/spec/requests/api/boards_spec.rb
+++ b/spec/requests/api/boards_spec.rb
@@ -6,7 +6,7 @@ describe API::Boards do
let(:non_member) { create(:user) }
let(:guest) { create(:user) }
let(:admin) { create(:user, :admin) }
- let!(:project) { create(:empty_project, :public, creator_id: user.id, namespace: user.namespace ) }
+ let!(:project) { create(:project, :public, creator_id: user.id, namespace: user.namespace ) }
let!(:dev_label) do
create(:label, title: 'Development', color: '#FFAABB', project: project)
@@ -188,7 +188,7 @@ describe API::Boards do
context "when the user is project owner" do
let(:owner) { create(:user) }
- let(:project) { create(:empty_project, namespace: owner.namespace) }
+ let(:project) { create(:project, namespace: owner.namespace) }
it "deletes the list if an admin requests it" do
delete api("#{base_url}/#{dev_list.id}", owner)
diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb
index 7eaa89837c8..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 'repository branch', merged: true
+ 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'
+
+ context 'when branch contains a dot' do
+ let(:branch_name) { branch_with_dot.name }
+
+ it_behaves_like 'repository branch'
+ end
end
end
@@ -147,281 +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 '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 'for an existing protected branch' do
- before do
- project.repository.add_branch(user, protected_branch.name, 'master')
+ 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
- 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
+ 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
- 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)
+ 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
+
+ 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) }
+
+ 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 }
- 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
+ end
+ end
end
+ end
+
+ describe 'POST /projects/:id/repository/branches' do
+ let(:route) { "/projects/#{project_id}/repository/branches" }
+
+ shared_examples_for 'repository new branch' do
+ it 'creates a new branch' do
+ post api(route, current_user), branch: 'feature1', ref: branch_sha
- it "update branches with dots in branch name" do
- put api("/projects/#{project.id}/repository/branches/#{branch_with_dot.name}/unprotect", user)
+ 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
- expect(response).to have_http_status(200)
- expect(json_response['name']).to eq(branch_with_dot.name)
- expect(json_response['protected']).to eq(false)
+ 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)
- end
- it "removes protected branch" do
- create(:protected_branch, project: project, name: branch_name)
- delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user)
- expect(response).to have_http_status(405)
- expect(json_response['message']).to eq('Protected branch cant be removed')
- end
-
- it "does not remove HEAD branch" do
- delete api("/projects/#{project.id}/repository/branches/master", user)
- expect(response).to have_http_status(405)
- expect(json_response['message']).to eq('Cannot remove HEAD branch')
+ 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
@@ -429,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/circuit_breakers_spec.rb b/spec/requests/api/circuit_breakers_spec.rb
new file mode 100644
index 00000000000..76521e55994
--- /dev/null
+++ b/spec/requests/api/circuit_breakers_spec.rb
@@ -0,0 +1,57 @@
+require 'spec_helper'
+
+describe API::CircuitBreakers do
+ let(:user) { create(:user) }
+ let(:admin) { create(:admin) }
+
+ describe 'GET circuit_breakers/repository_storage' do
+ it 'returns a 401 for anonymous users' do
+ get api('/circuit_breakers/repository_storage')
+
+ expect(response).to have_http_status(401)
+ end
+
+ it 'returns a 403 for users' do
+ get api('/circuit_breakers/repository_storage', user)
+
+ expect(response).to have_http_status(403)
+ end
+
+ it 'returns an Array of storages' do
+ expect(Gitlab::Git::Storage::Health).to receive(:for_all_storages) do
+ [Gitlab::Git::Storage::Health.new('broken', [{ name: 'prefix:broken:web01', failure_count: 4 }])]
+ end
+
+ get api('/circuit_breakers/repository_storage', admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_kind_of(Array)
+ expect(json_response.first['storage_name']).to eq('broken')
+ expect(json_response.first['failing_on_hosts']).to eq(['web01'])
+ expect(json_response.first['total_failures']).to eq(4)
+ end
+
+ describe 'GET circuit_breakers/repository_storage/failing' do
+ it 'returns an array of failing storages' do
+ expect(Gitlab::Git::Storage::Health).to receive(:for_failing_storages) do
+ [Gitlab::Git::Storage::Health.new('broken', [{ name: 'prefix:broken:web01', failure_count: 4 }])]
+ end
+
+ get api('/circuit_breakers/repository_storage/failing', admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_kind_of(Array)
+ end
+ end
+ end
+
+ describe 'DELETE circuit_breakers/repository_storage' do
+ it 'clears all circuit_breakers' do
+ expect(Gitlab::Git::Storage::CircuitBreaker).to receive(:reset_all!)
+
+ delete api('/circuit_breakers/repository_storage', admin)
+
+ expect(response).to have_http_status(204)
+ end
+ end
+end
diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb
index 1233cdc64c4..3c02e6302b4 100644
--- a/spec/requests/api/commit_statuses_spec.rb
+++ b/spec/requests/api/commit_statuses_spec.rb
@@ -16,8 +16,8 @@ describe API::CommitStatuses do
let(:get_url) { "/projects/#{project.id}/repository/commits/#{sha}/statuses" }
context 'ci commit exists' do
- let!(:master) { project.pipelines.create(sha: commit.id, ref: 'master') }
- let!(:develop) { project.pipelines.create(sha: commit.id, ref: 'develop') }
+ let!(:master) { project.pipelines.create(source: :push, sha: commit.id, ref: 'master') }
+ let!(:develop) { project.pipelines.create(source: :push, sha: commit.id, ref: 'develop') }
context "reporter user" do
let(:statuses_id) { json_response.map { |status| status['id'] } }
@@ -26,15 +26,17 @@ describe API::CommitStatuses do
create(:commit_status, { pipeline: commit, ref: commit.ref }.merge(opts))
end
- let!(:status1) { create_status(master, status: 'running') }
- let!(:status2) { create_status(master, name: 'coverage', status: 'pending') }
+ let!(:status1) { create_status(master, status: 'running', retried: true) }
+ let!(:status2) { create_status(master, name: 'coverage', status: 'pending', retried: true) }
let!(:status3) { create_status(develop, status: 'running', allow_failure: true) }
let!(:status4) { create_status(master, name: 'coverage', status: 'success') }
let!(:status5) { create_status(develop, name: 'coverage', status: 'success') }
let!(:status6) { create_status(master, status: 'success') }
context 'latest commit statuses' do
- before { get api(get_url, reporter) }
+ before do
+ get api(get_url, reporter)
+ end
it 'returns latest commit statuses' do
expect(response).to have_http_status(200)
@@ -42,13 +44,15 @@ describe API::CommitStatuses do
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(statuses_id).to contain_exactly(status3.id, status4.id, status5.id, status6.id)
- json_response.sort_by!{ |status| status['id'] }
- expect(json_response.map{ |status| status['allow_failure'] }).to eq([true, false, false, false])
+ json_response.sort_by! { |status| status['id'] }
+ expect(json_response.map { |status| status['allow_failure'] }).to eq([true, false, false, false])
end
end
context 'all commit statuses' do
- before { get api(get_url, reporter), all: 1 }
+ before do
+ get api(get_url, reporter), all: 1
+ end
it 'returns all commit statuses' do
expect(response).to have_http_status(200)
@@ -61,7 +65,9 @@ describe API::CommitStatuses do
end
context 'latest commit statuses for specific ref' do
- before { get api(get_url, reporter), ref: 'develop' }
+ before do
+ get api(get_url, reporter), ref: 'develop'
+ end
it 'returns latest commit statuses for specific ref' do
expect(response).to have_http_status(200)
@@ -72,7 +78,9 @@ describe API::CommitStatuses do
end
context 'latest commit statues for specific name' do
- before { get api(get_url, reporter), name: 'coverage' }
+ before do
+ get api(get_url, reporter), name: 'coverage'
+ end
it 'return latest commit statuses for specific name' do
expect(response).to have_http_status(200)
@@ -85,7 +93,9 @@ describe API::CommitStatuses do
end
context 'ci commit does not exist' do
- before { get api(get_url, reporter) }
+ before do
+ get api(get_url, reporter)
+ end
it 'returns empty array' do
expect(response.status).to eq 200
@@ -95,7 +105,9 @@ describe API::CommitStatuses do
end
context "guest user" do
- before { get api(get_url, guest) }
+ before do
+ get api(get_url, guest)
+ end
it "does not return project commits" do
expect(response).to have_http_status(403)
@@ -103,7 +115,9 @@ describe API::CommitStatuses do
end
context "unauthorized user" do
- before { get api(get_url) }
+ before do
+ get api(get_url)
+ end
it "does not return project commits" do
expect(response).to have_http_status(401)
@@ -150,25 +164,40 @@ describe API::CommitStatuses do
context 'with all optional parameters' do
context 'when creating a commit status' do
- it 'creates commit status' do
+ subject do
post api(post_url, developer), {
state: 'success',
context: 'coverage',
- ref: 'develop',
+ ref: 'master',
description: 'test',
coverage: 80.0,
target_url: 'http://gitlab.com/status'
}
+ end
+
+ it 'creates commit status' do
+ subject
expect(response).to have_http_status(201)
expect(json_response['sha']).to eq(commit.id)
expect(json_response['status']).to eq('success')
expect(json_response['name']).to eq('coverage')
- expect(json_response['ref']).to eq('develop')
+ expect(json_response['ref']).to eq('master')
expect(json_response['coverage']).to eq(80.0)
expect(json_response['description']).to eq('test')
expect(json_response['target_url']).to eq('http://gitlab.com/status')
end
+
+ context 'when merge request exists for given branch' do
+ let!(:merge_request) { create(:merge_request, source_project: project, source_branch: 'master', target_branch: 'develop') }
+
+ it 'sets head pipeline' do
+ subject
+
+ expect(response).to have_http_status(201)
+ expect(merge_request.reload.head_pipeline).not_to be_nil
+ end
+ end
end
context 'when updatig a commit status' do
@@ -176,7 +205,7 @@ describe API::CommitStatuses do
post api(post_url, developer), {
state: 'running',
context: 'coverage',
- ref: 'develop',
+ ref: 'master',
description: 'coverage test',
coverage: 0.0,
target_url: 'http://gitlab.com/status'
@@ -185,7 +214,7 @@ describe API::CommitStatuses do
post api(post_url, developer), {
state: 'success',
name: 'coverage',
- ref: 'develop',
+ ref: 'master',
description: 'new description',
coverage: 90.0
}
@@ -196,7 +225,7 @@ describe API::CommitStatuses do
expect(json_response['sha']).to eq(commit.id)
expect(json_response['status']).to eq('success')
expect(json_response['name']).to eq('coverage')
- expect(json_response['ref']).to eq('develop')
+ expect(json_response['ref']).to eq('master')
expect(json_response['coverage']).to eq(90.0)
expect(json_response['description']).to eq('new description')
expect(json_response['target_url']).to eq('http://gitlab.com/status')
@@ -208,8 +237,32 @@ describe API::CommitStatuses do
end
end
+ context 'when retrying a commit status' do
+ before do
+ post api(post_url, developer),
+ { state: 'failed', name: 'test', ref: 'master' }
+
+ post api(post_url, developer),
+ { state: 'success', name: 'test', ref: 'master' }
+ end
+
+ it 'correctly posts a new commit status' do
+ expect(response).to have_http_status(201)
+ expect(json_response['sha']).to eq(commit.id)
+ expect(json_response['status']).to eq('success')
+ end
+
+ it 'retries a commit status' do
+ expect(CommitStatus.count).to eq 2
+ expect(CommitStatus.first).to be_retried
+ expect(CommitStatus.last.pipeline).to be_success
+ end
+ end
+
context 'when status is invalid' do
- before { post api(post_url, developer), state: 'invalid' }
+ before do
+ post api(post_url, developer), state: 'invalid'
+ end
it 'does not create commit status' do
expect(response).to have_http_status(400)
@@ -217,7 +270,9 @@ describe API::CommitStatuses do
end
context 'when request without a state made' do
- before { post api(post_url, developer) }
+ before do
+ post api(post_url, developer)
+ end
it 'does not create commit status' do
expect(response).to have_http_status(400)
@@ -226,7 +281,10 @@ describe API::CommitStatuses do
context 'when commit SHA is invalid' do
let(:sha) { 'invalid_sha' }
- before { post api(post_url, developer), state: 'running' }
+
+ before do
+ post api(post_url, developer), state: 'running'
+ end
it 'returns not found error' do
expect(response).to have_http_status(404)
@@ -248,7 +306,9 @@ describe API::CommitStatuses do
end
context 'reporter user' do
- before { post api(post_url, reporter), state: 'running' }
+ before do
+ post api(post_url, reporter), state: 'running'
+ end
it 'does not create commit status' do
expect(response).to have_http_status(403)
@@ -256,7 +316,9 @@ describe API::CommitStatuses do
end
context 'guest user' do
- before { post api(post_url, guest), state: 'running' }
+ before do
+ post api(post_url, guest), state: 'running'
+ end
it 'does not create commit status' do
expect(response).to have_http_status(403)
@@ -264,7 +326,9 @@ describe API::CommitStatuses do
end
context 'unauthorized user' do
- before { post api(post_url) }
+ before do
+ post api(post_url)
+ end
it 'does not create commit status' do
expect(response).to have_http_status(401)
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 0b0e4c2b112..dafe3f466a2 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -3,26 +3,29 @@ require 'mime/types'
describe API::Commits do
let(:user) { create(:user) }
- let(:user2) { create(:user) }
- let!(:project) { create(:project, :repository, creator: user, namespace: user.namespace) }
- let!(:master) { create(:project_member, :master, user: user, project: project) }
- let!(:guest) { create(:project_member, :guest, user: user2, project: project) }
- let!(:note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'a comment on a commit') }
- let!(:another_note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'another comment on a commit') }
+ let(:guest) { create(:user).tap { |u| project.add_guest(u) } }
+ let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
+ let(:branch_with_dot) { project.repository.find_branch('ends-with.json') }
+ let(:branch_with_slash) { project.repository.find_branch('improve/awesome') }
- before { project.team << [user, :reporter] }
+ let(:project_id) { project.id }
+ let(:current_user) { nil }
- describe "List repository commits" do
- context "authorized user" do
- before { project.team << [user2, :reporter] }
+ before do
+ project.add_master(user)
+ end
+
+ describe 'GET /projects/:id/repository/commits' do
+ let(:route) { "/projects/#{project_id}/repository/commits" }
+ shared_examples_for 'project commits' do
it "returns project commits" do
commit = project.repository.commit
- get api("/projects/#{project.id}/repository/commits", user)
+ get api(route, current_user)
expect(response).to have_http_status(200)
- expect(json_response).to be_an Array
+ expect(response).to match_response_schema('public_api/v4/commits')
expect(json_response.first['id']).to eq(commit.id)
expect(json_response.first['committer_name']).to eq(commit.committer_name)
expect(json_response.first['committer_email']).to eq(commit.committer_email)
@@ -31,7 +34,7 @@ describe API::Commits do
it 'include correct pagination headers' do
commit_count = project.repository.count_commits(ref: 'master').to_s
- get api("/projects/#{project.id}/repository/commits", user)
+ get api(route, current_user)
expect(response).to include_pagination_headers
expect(response.headers['X-Total']).to eq(commit_count)
@@ -39,149 +42,161 @@ describe API::Commits do
end
end
- context "unauthorized user" do
- it "does not return project commits" do
- get api("/projects/#{project.id}/repository/commits")
- expect(response).to have_http_status(401)
+ context 'when unauthenticated', 'and project is public' do
+ let(:project) { create(:project, :public, :repository) }
+
+ it_behaves_like 'project commits'
+ end
+
+ context 'when unauthenticated', 'and project is private' do
+ it_behaves_like '404 response' do
+ let(:request) { get api(route) }
+ let(:message) { '404 Project Not Found' }
end
end
- context "since optional parameter" do
- it "returns project commits since provided parameter" do
- commits = project.repository.commits("master")
- after = commits.second.created_at
+ context 'when authenticated', 'as a master' do
+ let(:current_user) { user }
- get api("/projects/#{project.id}/repository/commits?since=#{after.utc.iso8601}", user)
+ it_behaves_like 'project commits'
- expect(json_response.size).to eq 2
- expect(json_response.first["id"]).to eq(commits.first.id)
- expect(json_response.second["id"]).to eq(commits.second.id)
- end
+ context "since optional parameter" do
+ it "returns project commits since provided parameter" do
+ commits = project.repository.commits("master")
+ after = commits.second.created_at
- it 'include correct pagination headers' do
- commits = project.repository.commits("master")
- after = commits.second.created_at
- commit_count = project.repository.count_commits(ref: 'master', after: after).to_s
+ get api("/projects/#{project_id}/repository/commits?since=#{after.utc.iso8601}", user)
- get api("/projects/#{project.id}/repository/commits?since=#{after.utc.iso8601}", user)
+ expect(json_response.size).to eq 2
+ expect(json_response.first["id"]).to eq(commits.first.id)
+ expect(json_response.second["id"]).to eq(commits.second.id)
+ end
- expect(response).to include_pagination_headers
- expect(response.headers['X-Total']).to eq(commit_count)
- expect(response.headers['X-Page']).to eql('1')
+ it 'include correct pagination headers' do
+ commits = project.repository.commits("master")
+ after = commits.second.created_at
+ commit_count = project.repository.count_commits(ref: 'master', after: after).to_s
+
+ get api("/projects/#{project_id}/repository/commits?since=#{after.utc.iso8601}", user)
+
+ expect(response).to include_pagination_headers
+ expect(response.headers['X-Total']).to eq(commit_count)
+ expect(response.headers['X-Page']).to eql('1')
+ end
end
- end
- context "until optional parameter" do
- it "returns project commits until provided parameter" do
- commits = project.repository.commits("master")
- before = commits.second.created_at
+ context "until optional parameter" do
+ it "returns project commits until provided parameter" do
+ commits = project.repository.commits("master")
+ before = commits.second.created_at
+
+ get api("/projects/#{project_id}/repository/commits?until=#{before.utc.iso8601}", user)
- get api("/projects/#{project.id}/repository/commits?until=#{before.utc.iso8601}", user)
+ if commits.size >= 20
+ expect(json_response.size).to eq(20)
+ else
+ expect(json_response.size).to eq(commits.size - 1)
+ end
- if commits.size >= 20
- expect(json_response.size).to eq(20)
- else
- expect(json_response.size).to eq(commits.size - 1)
+ expect(json_response.first["id"]).to eq(commits.second.id)
+ expect(json_response.second["id"]).to eq(commits.third.id)
end
- expect(json_response.first["id"]).to eq(commits.second.id)
- expect(json_response.second["id"]).to eq(commits.third.id)
- end
+ it 'include correct pagination headers' do
+ commits = project.repository.commits("master")
+ before = commits.second.created_at
+ commit_count = project.repository.count_commits(ref: 'master', before: before).to_s
- it 'include correct pagination headers' do
- commits = project.repository.commits("master")
- before = commits.second.created_at
- commit_count = project.repository.count_commits(ref: 'master', before: before).to_s
+ get api("/projects/#{project_id}/repository/commits?until=#{before.utc.iso8601}", user)
- get api("/projects/#{project.id}/repository/commits?until=#{before.utc.iso8601}", user)
-
- expect(response).to include_pagination_headers
- expect(response.headers['X-Total']).to eq(commit_count)
- expect(response.headers['X-Page']).to eql('1')
+ expect(response).to include_pagination_headers
+ expect(response.headers['X-Total']).to eq(commit_count)
+ expect(response.headers['X-Page']).to eql('1')
+ end
end
- end
- context "invalid xmlschema date parameters" do
- it "returns an invalid parameter error message" do
- get api("/projects/#{project.id}/repository/commits?since=invalid-date", user)
+ context "invalid xmlschema date parameters" do
+ it "returns an invalid parameter error message" do
+ get api("/projects/#{project_id}/repository/commits?since=invalid-date", user)
- expect(response).to have_http_status(400)
- expect(json_response['error']).to eq('since is invalid')
+ expect(response).to have_http_status(400)
+ expect(json_response['error']).to eq('since is invalid')
+ end
end
- end
- context "path optional parameter" do
- it "returns project commits matching provided path parameter" do
- path = 'files/ruby/popen.rb'
- commit_count = project.repository.count_commits(ref: 'master', path: path).to_s
+ context "path optional parameter" do
+ it "returns project commits matching provided path parameter" do
+ path = 'files/ruby/popen.rb'
+ commit_count = project.repository.count_commits(ref: 'master', path: path).to_s
- get api("/projects/#{project.id}/repository/commits?path=#{path}", user)
+ get api("/projects/#{project_id}/repository/commits?path=#{path}", user)
- expect(json_response.size).to eq(3)
- expect(json_response.first["id"]).to eq("570e7b2abdd848b95f2f578043fc23bd6f6fd24d")
- expect(response).to include_pagination_headers
- expect(response.headers['X-Total']).to eq(commit_count)
- end
+ expect(json_response.size).to eq(3)
+ expect(json_response.first["id"]).to eq("570e7b2abdd848b95f2f578043fc23bd6f6fd24d")
+ expect(response).to include_pagination_headers
+ expect(response.headers['X-Total']).to eq(commit_count)
+ end
- it 'include correct pagination headers' do
- path = 'files/ruby/popen.rb'
- commit_count = project.repository.count_commits(ref: 'master', path: path).to_s
+ it 'include correct pagination headers' do
+ path = 'files/ruby/popen.rb'
+ commit_count = project.repository.count_commits(ref: 'master', path: path).to_s
- get api("/projects/#{project.id}/repository/commits?path=#{path}", user)
+ get api("/projects/#{project_id}/repository/commits?path=#{path}", user)
- expect(response).to include_pagination_headers
- expect(response.headers['X-Total']).to eq(commit_count)
- expect(response.headers['X-Page']).to eql('1')
+ expect(response).to include_pagination_headers
+ expect(response.headers['X-Total']).to eq(commit_count)
+ expect(response.headers['X-Page']).to eql('1')
+ end
end
- end
- context 'with pagination params' do
- let(:page) { 1 }
- let(:per_page) { 5 }
- let(:ref_name) { 'master' }
- let!(:request) do
- get api("/projects/#{project.id}/repository/commits?page=#{page}&per_page=#{per_page}&ref_name=#{ref_name}", user)
- end
+ context 'with pagination params' do
+ let(:page) { 1 }
+ let(:per_page) { 5 }
+ let(:ref_name) { 'master' }
+ let!(:request) do
+ get api("/projects/#{project_id}/repository/commits?page=#{page}&per_page=#{per_page}&ref_name=#{ref_name}", user)
+ end
- it 'returns correct headers' do
- commit_count = project.repository.count_commits(ref: ref_name).to_s
+ it 'returns correct headers' do
+ commit_count = project.repository.count_commits(ref: ref_name).to_s
- expect(response).to include_pagination_headers
- expect(response.headers['X-Total']).to eq(commit_count)
- expect(response.headers['X-Page']).to eq('1')
- expect(response.headers['Link']).to match(/page=1&per_page=5/)
- expect(response.headers['Link']).to match(/page=2&per_page=5/)
- end
+ expect(response).to include_pagination_headers
+ expect(response.headers['X-Total']).to eq(commit_count)
+ expect(response.headers['X-Page']).to eq('1')
+ expect(response.headers['Link']).to match(/page=1&per_page=5/)
+ expect(response.headers['Link']).to match(/page=2&per_page=5/)
+ end
- context 'viewing the first page' do
- it 'returns the first 5 commits' do
- commit = project.repository.commit
+ context 'viewing the first page' do
+ it 'returns the first 5 commits' do
+ commit = project.repository.commit
- expect(json_response.size).to eq(per_page)
- expect(json_response.first['id']).to eq(commit.id)
- expect(response.headers['X-Page']).to eq('1')
+ expect(json_response.size).to eq(per_page)
+ expect(json_response.first['id']).to eq(commit.id)
+ expect(response.headers['X-Page']).to eq('1')
+ end
end
- end
- context 'viewing the third page' do
- let(:page) { 3 }
+ context 'viewing the third page' do
+ let(:page) { 3 }
- it 'returns the third 5 commits' do
- commit = project.repository.commits('HEAD', offset: (page - 1) * per_page).first
+ it 'returns the third 5 commits' do
+ commit = project.repository.commits('HEAD', offset: (page - 1) * per_page).first
- expect(json_response.size).to eq(per_page)
- expect(json_response.first['id']).to eq(commit.id)
- expect(response.headers['X-Page']).to eq('3')
+ expect(json_response.size).to eq(per_page)
+ expect(json_response.first['id']).to eq(commit.id)
+ expect(response.headers['X-Page']).to eq('3')
+ end
end
end
end
end
describe "POST /projects/:id/repository/commits" do
- let!(:url) { "/projects/#{project.id}/repository/commits" }
+ let!(:url) { "/projects/#{project_id}/repository/commits" }
it 'returns a 403 unauthorized for user without permissions' do
- post api(url, user2)
+ post api(url, guest)
expect(response).to have_http_status(403)
end
@@ -224,7 +239,7 @@ describe API::Commits do
it 'a new file in project repo' do
post api(url, user), valid_c_params
- expect(response).to have_http_status(201)
+ expect(response).to have_gitlab_http_status(201)
expect(json_response['title']).to eq(message)
expect(json_response['committer_name']).to eq(user.name)
expect(json_response['committer_email']).to eq(user.email)
@@ -450,13 +465,17 @@ describe API::Commits do
end
end
- describe "Get a single commit" do
- context "authorized user" do
- it "returns a commit by sha" do
- get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
+ describe 'GET /projects/:id/repository/commits/:sha' do
+ let(:commit) { project.repository.commit }
+ let(:commit_id) { commit.id }
+ let(:route) { "/projects/#{project_id}/repository/commits/#{commit_id}" }
- expect(response).to have_http_status(200)
- commit = project.repository.commit
+ shared_examples_for 'ref commit' do
+ it 'returns the ref last commit' do
+ get api(route, current_user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/commit/detail')
expect(json_response['id']).to eq(commit.id)
expect(json_response['short_id']).to eq(commit.short_id)
expect(json_response['title']).to eq(commit.title)
@@ -471,220 +490,539 @@ describe API::Commits do
expect(json_response['stats']['additions']).to eq(commit.stats.additions)
expect(json_response['stats']['deletions']).to eq(commit.stats.deletions)
expect(json_response['stats']['total']).to eq(commit.stats.total)
+ expect(json_response['status']).to be_nil
end
- it "returns a 404 error if not found" do
- get api("/projects/#{project.id}/repository/commits/invalid_sha", user)
- expect(response).to have_http_status(404)
+ context 'when ref does not exist' do
+ let(:commit_id) { 'unknown' }
+
+ it_behaves_like '404 response' do
+ let(:request) { get api(route, current_user) }
+ let(:message) { '404 Commit Not Found' }
+ end
end
- it "returns nil for commit without CI" do
- get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
+ context 'when repository is disabled' do
+ include_context 'disabled repository'
- expect(response).to have_http_status(200)
- expect(json_response['status']).to be_nil
+ it_behaves_like '403 response' do
+ let(:request) { get api(route, current_user) }
+ end
end
+ end
- it "returns status for CI" do
- pipeline = project.ensure_pipeline('master', project.repository.commit.sha)
- pipeline.update(status: 'success')
+ context 'when unauthenticated', 'and project is public' do
+ let(:project) { create(:project, :public, :repository) }
- get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
+ it_behaves_like 'ref commit'
+ end
- expect(response).to have_http_status(200)
- expect(json_response['status']).to eq(pipeline.status)
+ context 'when unauthenticated', 'and project is private' do
+ it_behaves_like '404 response' do
+ let(:request) { get api(route) }
+ let(:message) { '404 Project Not Found' }
end
+ end
- it "returns status for CI when pipeline is created" do
- project.ensure_pipeline('master', project.repository.commit.sha)
+ context 'when authenticated', 'as a master' do
+ let(:current_user) { user }
- get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
+ it_behaves_like 'ref commit'
- expect(response).to have_http_status(200)
- expect(json_response['status']).to eq("created")
+ context 'when branch contains a dot' do
+ let(:commit) { project.repository.commit(branch_with_dot.name) }
+ let(:commit_id) { branch_with_dot.name }
+
+ it_behaves_like 'ref commit'
end
- end
- context "unauthorized user" do
- it "does not return the selected commit" do
- get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}")
- expect(response).to have_http_status(401)
+ context 'when branch contains a slash' do
+ let(:commit_id) { 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(:commit) { project.repository.commit(branch_with_slash.name) }
+ let(:commit_id) { CGI.escape(branch_with_slash.name) }
+
+ it_behaves_like 'ref commit'
+ end
+
+ context 'requesting with the escaped project full path' do
+ let(:project_id) { CGI.escape(project.full_path) }
+
+ it_behaves_like 'ref commit'
+
+ context 'when branch contains a dot' do
+ let(:commit) { project.repository.commit(branch_with_dot.name) }
+ let(:commit_id) { branch_with_dot.name }
+
+ it_behaves_like 'ref commit'
+ end
+ end
+
+ context 'when the ref has a pipeline' do
+ let!(:pipeline) { project.pipelines.create(source: :push, ref: 'master', sha: commit.sha) }
+
+ it 'includes a "created" status' do
+ get api(route, current_user)
+
+ expect(response).to have_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/commit/detail')
+ expect(json_response['status']).to eq('created')
+ end
+
+ context 'when pipeline succeeds' do
+ before do
+ pipeline.update(status: 'success')
+ end
+
+ it 'includes a "success" status' do
+ get api(route, current_user)
+
+ expect(response).to have_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/commit/detail')
+ expect(json_response['status']).to eq('success')
+ end
+ end
end
end
end
- describe "Get the diff of a commit" do
- context "authorized user" do
- before { project.team << [user2, :reporter] }
+ describe 'GET /projects/:id/repository/commits/:sha/diff' do
+ let(:commit) { project.repository.commit }
+ let(:commit_id) { commit.id }
+ let(:route) { "/projects/#{project_id}/repository/commits/#{commit_id}/diff" }
- it "returns the diff of the selected commit" do
- get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff", user)
- expect(response).to have_http_status(200)
+ shared_examples_for 'ref diff' do
+ it 'returns the diff of the selected commit' do
+ get api(route, current_user)
- expect(json_response).to be_an Array
- expect(json_response.length).to be >= 1
- expect(json_response.first.keys).to include "diff"
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response.size).to be >= 1
+ expect(json_response.first.keys).to include 'diff'
end
- it "returns a 404 error if invalid commit" do
- get api("/projects/#{project.id}/repository/commits/invalid_sha/diff", user)
- expect(response).to have_http_status(404)
+ context 'when ref does not exist' do
+ let(:commit_id) { 'unknown' }
+
+ it_behaves_like '404 response' do
+ let(:request) { get api(route, current_user) }
+ let(:message) { '404 Commit Not Found' }
+ end
+ end
+
+ context 'when repository is disabled' do
+ include_context 'disabled repository'
+
+ it_behaves_like '403 response' do
+ let(:request) { get api(route, current_user) }
+ end
end
end
- context "unauthorized user" do
- it "does not return the diff of the selected commit" do
- get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff")
- expect(response).to have_http_status(401)
+ context 'when unauthenticated', 'and project is public' do
+ let(:project) { create(:project, :public, :repository) }
+
+ it_behaves_like 'ref diff'
+ end
+
+ context 'when unauthenticated', 'and project is private' do
+ it_behaves_like '404 response' do
+ let(:request) { get api(route) }
+ let(:message) { '404 Project Not Found' }
+ end
+ end
+
+ context 'when authenticated', 'as a master' do
+ let(:current_user) { user }
+
+ it_behaves_like 'ref diff'
+
+ context 'when branch contains a dot' do
+ let(:commit_id) { branch_with_dot.name }
+
+ it_behaves_like 'ref diff'
+ end
+
+ context 'when branch contains a slash' do
+ let(:commit_id) { 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(:commit_id) { CGI.escape(branch_with_slash.name) }
+
+ it_behaves_like 'ref diff'
+ end
+
+ context 'requesting with the escaped project full path' do
+ let(:project_id) { CGI.escape(project.full_path) }
+
+ it_behaves_like 'ref diff'
+
+ context 'when branch contains a dot' do
+ let(:commit_id) { branch_with_dot.name }
+
+ it_behaves_like 'ref diff'
+ end
end
end
end
- describe 'Get the comments of a commit' do
- context 'authorized user' do
- it 'returns merge_request comments' do
- get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", 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(2)
- expect(json_response.first['note']).to eq('a comment on a commit')
- expect(json_response.first['author']['id']).to eq(user.id)
+ describe 'GET /projects/:id/repository/commits/:sha/comments' do
+ let(:commit) { project.repository.commit }
+ let(:commit_id) { commit.id }
+ let(:route) { "/projects/#{project_id}/repository/commits/#{commit_id}/comments" }
+
+ shared_examples_for 'ref comments' do
+ context 'when ref exists' do
+ before do
+ create(:note_on_commit, author: user, project: project, commit_id: commit.id, note: 'a comment on a commit')
+ create(:note_on_commit, author: user, project: project, commit_id: commit.id, note: 'another comment on a commit')
+ end
+
+ it 'returns the diff of the selected commit' do
+ get api(route, current_user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/commit_notes')
+ expect(json_response.size).to eq(2)
+ expect(json_response.first['note']).to eq('a comment on a commit')
+ expect(json_response.first['author']['id']).to eq(user.id)
+ end
end
- it 'returns a 404 error if merge_request_id not found' do
- get api("/projects/#{project.id}/repository/commits/1234ab/comments", user)
- expect(response).to have_http_status(404)
+ context 'when ref does not exist' do
+ let(:commit_id) { 'unknown' }
+
+ it_behaves_like '404 response' do
+ let(:request) { get api(route, current_user) }
+ let(:message) { '404 Commit Not Found' }
+ end
+ end
+
+ context 'when repository is disabled' do
+ include_context 'disabled repository'
+
+ it_behaves_like '403 response' do
+ let(:request) { get api(route, current_user) }
+ end
end
end
- context 'unauthorized user' do
- it 'does not return the diff of the selected commit' do
- get api("/projects/#{project.id}/repository/commits/1234ab/comments")
- expect(response).to have_http_status(401)
+ context 'when unauthenticated', 'and project is public' do
+ let(:project) { create(:project, :public, :repository) }
+
+ it_behaves_like 'ref comments'
+ end
+
+ context 'when unauthenticated', 'and project is private' do
+ it_behaves_like '404 response' do
+ let(:request) { get api(route) }
+ let(:message) { '404 Project Not Found' }
+ end
+ end
+
+ context 'when authenticated', 'as a master' do
+ let(:current_user) { user }
+
+ it_behaves_like 'ref comments'
+
+ context 'when branch contains a dot' do
+ let(:commit) { project.repository.commit(branch_with_dot.name) }
+ let(:commit_id) { branch_with_dot.name }
+
+ it_behaves_like 'ref comments'
+ end
+
+ context 'when branch contains a slash' do
+ let(:commit) { project.repository.commit(branch_with_slash.name) }
+ let(:commit_id) { 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(:commit) { project.repository.commit(branch_with_slash.name) }
+ let(:commit_id) { CGI.escape(branch_with_slash.name) }
+
+ it_behaves_like 'ref comments'
+ end
+
+ context 'requesting with the escaped project full path' do
+ let(:project_id) { CGI.escape(project.full_path) }
+
+ it_behaves_like 'ref comments'
+
+ context 'when branch contains a dot' do
+ let(:commit) { project.repository.commit(branch_with_dot.name) }
+ let(:commit_id) { branch_with_dot.name }
+
+ it_behaves_like 'ref comments'
+ end
end
end
context 'when the commit is present on two projects' do
- let(:forked_project) { create(:project, :repository, creator: user2, namespace: user2.namespace) }
- let!(:forked_project_note) { create(:note_on_commit, author: user2, project: forked_project, commit_id: forked_project.repository.commit.id, note: 'a comment on a commit for fork') }
+ let(:forked_project) { create(:project, :repository, creator: guest, namespace: guest.namespace) }
+ let!(:forked_project_note) { create(:note_on_commit, author: guest, project: forked_project, commit_id: forked_project.repository.commit.id, note: 'a comment on a commit for fork') }
+ let(:project_id) { forked_project.id }
+ let(:commit_id) { forked_project.repository.commit.id }
it 'returns the comments for the target project' do
- get api("/projects/#{forked_project.id}/repository/commits/#{forked_project.repository.commit.id}/comments", user2)
+ get api(route, guest)
- expect(response).to have_http_status(200)
- expect(json_response.length).to eq(1)
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/commit_notes')
+ expect(json_response.size).to eq(1)
expect(json_response.first['note']).to eq('a comment on a commit for fork')
- expect(json_response.first['author']['id']).to eq(user2.id)
+ expect(json_response.first['author']['id']).to eq(guest.id)
end
end
end
describe 'POST :id/repository/commits/:sha/cherry_pick' do
- let(:master_pickable_commit) { project.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') }
+ let(:commit) { project.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') }
+ let(:commit_id) { commit.id }
+ let(:branch) { 'master' }
+ let(:route) { "/projects/#{project_id}/repository/commits/#{commit_id}/cherry_pick" }
+
+ shared_examples_for 'ref cherry-pick' do
+ context 'when ref exists' do
+ it 'cherry-picks the ref commit' do
+ post api(route, current_user), branch: branch
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(response).to match_response_schema('public_api/v4/commit/basic')
+ expect(json_response['title']).to eq(commit.title)
+ expect(json_response['message']).to eq(commit.message)
+ expect(json_response['author_name']).to eq(commit.author_name)
+ expect(json_response['committer_name']).to eq(user.name)
+ end
+ end
- context 'authorized user' do
- it 'cherry picks a commit' do
- post api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user), branch: 'master'
+ context 'when repository is disabled' do
+ include_context 'disabled repository'
- expect(response).to have_http_status(201)
- expect(json_response['title']).to eq(master_pickable_commit.title)
- expect(json_response['message']).to eq(master_pickable_commit.message)
- expect(json_response['author_name']).to eq(master_pickable_commit.author_name)
- expect(json_response['committer_name']).to eq(user.name)
+ it_behaves_like '403 response' do
+ let(:request) { post api(route, current_user), branch: 'master' }
+ end
end
+ end
- it 'returns 400 if commit is already included in the target branch' do
- post api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user), branch: 'markdown'
+ context 'when unauthenticated', 'and project is public' do
+ let(:project) { create(:project, :public, :repository) }
- expect(response).to have_http_status(400)
- expect(json_response['message']).to include('Sorry, we cannot cherry-pick this commit automatically.')
+ it_behaves_like '403 response' do
+ let(:request) { post api(route), branch: 'master' }
end
+ end
- it 'returns 400 if you are not allowed to push to the target branch' do
- project.team << [user2, :developer]
- protected_branch = create(:protected_branch, project: project, name: 'feature')
+ context 'when unauthenticated', 'and project is private' do
+ it_behaves_like '404 response' do
+ let(:request) { post api(route), branch: 'master' }
+ let(:message) { '404 Project Not Found' }
+ end
+ end
- post api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user2), branch: protected_branch.name
+ context 'when authenticated', 'as an owner' do
+ let(:current_user) { user }
- expect(response).to have_http_status(400)
- expect(json_response['message']).to eq('You are not allowed to push into this branch')
+ it_behaves_like 'ref cherry-pick'
+
+ context 'when ref does not exist' do
+ let(:commit_id) { 'unknown' }
+
+ it_behaves_like '404 response' do
+ let(:request) { post api(route, current_user), branch: 'master' }
+ let(:message) { '404 Commit Not Found' }
+ end
+ end
+
+ context 'when branch is missing' do
+ it_behaves_like '400 response' do
+ let(:request) { post api(route, current_user) }
+ end
end
- it 'returns 400 for missing parameters' do
- post api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user)
+ context 'when branch does not exist' do
+ it_behaves_like '404 response' do
+ let(:request) { post api(route, current_user), branch: 'foo' }
+ let(:message) { '404 Branch Not Found' }
+ end
+ end
- expect(response).to have_http_status(400)
- expect(json_response['error']).to eq('branch is missing')
+ context 'when commit is already included in the target branch' do
+ it_behaves_like '400 response' do
+ let(:request) { post api(route, current_user), branch: 'markdown' }
+ end
end
- it 'returns 404 if commit is not found' do
- post api("/projects/#{project.id}/repository/commits/abcd0123/cherry_pick", user), branch: 'master'
+ context 'when ref contains a dot' do
+ let(:commit) { project.repository.commit(branch_with_dot.name) }
+ let(:commit_id) { branch_with_dot.name }
- expect(response).to have_http_status(404)
- expect(json_response['message']).to eq('404 Commit Not Found')
+ it_behaves_like 'ref cherry-pick'
end
- it 'returns 404 if branch is not found' do
- post api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user), branch: 'foo'
+ context 'when ref contains a slash' do
+ let(:commit_id) { branch_with_slash.name }
- expect(response).to have_http_status(404)
- expect(json_response['message']).to eq('404 Branch Not Found')
+ it_behaves_like '404 response' do
+ let(:request) { post api(route, current_user), branch: 'master' }
+ end
end
- it 'returns 400 for missing parameters' do
- post api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user)
+ context 'requesting with the escaped project full path' do
+ let(:project_id) { CGI.escape(project.full_path) }
- expect(response).to have_http_status(400)
- expect(json_response['error']).to eq('branch is missing')
+ it_behaves_like 'ref cherry-pick'
+
+ context 'when ref contains a dot' do
+ let(:commit) { project.repository.commit(branch_with_dot.name) }
+ let(:commit_id) { branch_with_dot.name }
+
+ it_behaves_like 'ref cherry-pick'
+ end
end
end
- context 'unauthorized user' do
- it 'does not cherry pick the commit' do
- post api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick"), branch: 'master'
+ context 'when authenticated', 'as a developer' do
+ let(:current_user) { guest }
- expect(response).to have_http_status(401)
+ before do
+ project.add_developer(guest)
+ end
+
+ context 'when branch is protected' do
+ before do
+ create(:protected_branch, project: project, name: 'feature')
+ end
+
+ it 'returns 400 if you are not allowed to push to the target branch' do
+ post api(route, current_user), branch: 'feature'
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']).to eq('You are not allowed to push into this branch')
+ end
end
end
end
- describe 'Post comment to commit' do
- context 'authorized user' do
- it 'returns comment' do
- post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user), note: 'My comment'
- expect(response).to have_http_status(201)
- expect(json_response['note']).to eq('My comment')
- expect(json_response['path']).to be_nil
- expect(json_response['line']).to be_nil
- expect(json_response['line_type']).to be_nil
+ describe 'POST /projects/:id/repository/commits/:sha/comments' do
+ let(:commit) { project.repository.commit }
+ let(:commit_id) { commit.id }
+ let(:note) { 'My comment' }
+ let(:route) { "/projects/#{project_id}/repository/commits/#{commit_id}/comments" }
+
+ shared_examples_for 'ref new comment' do
+ context 'when ref exists' do
+ it 'creates the comment' do
+ post api(route, current_user), note: note
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(response).to match_response_schema('public_api/v4/commit_note')
+ expect(json_response['note']).to eq('My comment')
+ expect(json_response['path']).to be_nil
+ expect(json_response['line']).to be_nil
+ expect(json_response['line_type']).to be_nil
+ end
+ end
+
+ context 'when repository is disabled' do
+ include_context 'disabled repository'
+
+ it_behaves_like '403 response' do
+ let(:request) { post api(route, current_user), note: 'My comment' }
+ end
+ end
+ end
+
+ context 'when unauthenticated', 'and project is public' do
+ let(:project) { create(:project, :public, :repository) }
+
+ it_behaves_like '400 response' do
+ let(:request) { post api(route), note: 'My comment' }
+ end
+ end
+
+ context 'when unauthenticated', 'and project is private' do
+ it_behaves_like '404 response' do
+ let(:request) { post api(route), note: 'My comment' }
+ let(:message) { '404 Project Not Found' }
end
+ end
+
+ context 'when authenticated', 'as an owner' do
+ let(:current_user) { user }
+
+ it_behaves_like 'ref new comment'
it 'returns the inline comment' do
- post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user), note: 'My comment', path: project.repository.commit.raw_diffs.first.new_path, line: 1, line_type: 'new'
+ post api(route, current_user), note: 'My comment', path: project.repository.commit.raw_diffs.first.new_path, line: 1, line_type: 'new'
- expect(response).to have_http_status(201)
+ expect(response).to have_gitlab_http_status(201)
+ expect(response).to match_response_schema('public_api/v4/commit_note')
expect(json_response['note']).to eq('My comment')
expect(json_response['path']).to eq(project.repository.commit.raw_diffs.first.new_path)
expect(json_response['line']).to eq(1)
expect(json_response['line_type']).to eq('new')
end
+ context 'when ref does not exist' do
+ let(:commit_id) { 'unknown' }
+
+ it_behaves_like '404 response' do
+ let(:request) { post api(route, current_user), note: 'My comment' }
+ let(:message) { '404 Commit Not Found' }
+ end
+ end
+
it 'returns 400 if note is missing' do
- post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user)
- expect(response).to have_http_status(400)
+ post api(route, current_user)
+
+ expect(response).to have_gitlab_http_status(400)
end
- it 'returns 404 if note is attached to non existent commit' do
- post api("/projects/#{project.id}/repository/commits/1234ab/comments", user), note: 'My comment'
- expect(response).to have_http_status(404)
+ context 'when ref contains a dot' do
+ let(:commit_id) { branch_with_dot.name }
+
+ it_behaves_like 'ref new comment'
end
- end
- context 'unauthorized user' do
- it 'does not return the diff of the selected commit' do
- post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments")
- expect(response).to have_http_status(401)
+ context 'when ref contains a slash' do
+ let(:commit_id) { branch_with_slash.name }
+
+ it_behaves_like '404 response' do
+ let(:request) { post api(route, current_user), note: 'My comment' }
+ end
+ end
+
+ context 'when ref contains an escaped slash' do
+ let(:commit_id) { CGI.escape(branch_with_slash.name) }
+
+ it_behaves_like 'ref new comment'
+ end
+
+ context 'requesting with the escaped project full path' do
+ let(:project_id) { CGI.escape(project.full_path) }
+
+ it_behaves_like 'ref new comment'
+
+ context 'when ref contains a dot' do
+ let(:commit_id) { branch_with_dot.name }
+
+ it_behaves_like 'ref new comment'
+ end
end
end
end
diff --git a/spec/requests/api/deploy_keys_spec.rb b/spec/requests/api/deploy_keys_spec.rb
index 843e9862b0c..e497ec333a2 100644
--- a/spec/requests/api/deploy_keys_spec.rb
+++ b/spec/requests/api/deploy_keys_spec.rb
@@ -3,8 +3,8 @@ require 'spec_helper'
describe API::DeployKeys do
let(:user) { create(:user) }
let(:admin) { create(:admin) }
- let(:project) { create(:empty_project, creator_id: user.id) }
- let(:project2) { create(:empty_project, creator_id: user.id) }
+ let(:project) { create(:project, creator_id: user.id) }
+ let(:project2) { create(:project, creator_id: user.id) }
let(:deploy_key) { create(:deploy_key, public: true) }
let!(:deploy_keys_project) do
@@ -13,7 +13,7 @@ describe API::DeployKeys do
describe 'GET /deploy_keys' do
context 'when unauthenticated' do
- it 'should return authentication error' do
+ it 'returns authentication error' do
get api('/deploy_keys')
expect(response.status).to eq(401)
@@ -21,7 +21,7 @@ describe API::DeployKeys do
end
context 'when authenticated as non-admin user' do
- it 'should return a 403 error' do
+ it 'returns a 403 error' do
get api('/deploy_keys', user)
expect(response.status).to eq(403)
@@ -29,7 +29,7 @@ describe API::DeployKeys do
end
context 'when authenticated as admin' do
- it 'should return all deploy keys' do
+ it 'returns all deploy keys' do
get api('/deploy_keys', admin)
expect(response.status).to eq(200)
@@ -41,9 +41,11 @@ describe API::DeployKeys do
end
describe 'GET /projects/:id/deploy_keys' do
- before { deploy_key }
+ before do
+ deploy_key
+ end
- it 'should return array of ssh keys' do
+ it 'returns array of ssh keys' do
get api("/projects/#{project.id}/deploy_keys", admin)
expect(response).to have_http_status(200)
@@ -54,14 +56,14 @@ describe API::DeployKeys do
end
describe 'GET /projects/:id/deploy_keys/:key_id' do
- it 'should return a single key' do
+ it 'returns a single key' do
get api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}", admin)
expect(response).to have_http_status(200)
expect(json_response['title']).to eq(deploy_key.title)
end
- it 'should return 404 Not Found with invalid ID' do
+ it 'returns 404 Not Found with invalid ID' do
get api("/projects/#{project.id}/deploy_keys/404", admin)
expect(response).to have_http_status(404)
@@ -69,26 +71,26 @@ describe API::DeployKeys do
end
describe 'POST /projects/:id/deploy_keys' do
- it 'should not create an invalid ssh key' do
+ it 'does not create an invalid ssh key' do
post api("/projects/#{project.id}/deploy_keys", admin), { title: 'invalid key' }
expect(response).to have_http_status(400)
expect(json_response['error']).to eq('key is missing')
end
- it 'should not create a key without title' do
+ it 'does not create a key without title' do
post api("/projects/#{project.id}/deploy_keys", admin), key: 'some key'
expect(response).to have_http_status(400)
expect(json_response['error']).to eq('title is missing')
end
- it 'should create new ssh key' do
+ it 'creates new ssh key' do
key_attrs = attributes_for :another_key
expect do
post api("/projects/#{project.id}/deploy_keys", admin), key_attrs
- end.to change{ project.deploy_keys.count }.by(1)
+ end.to change { project.deploy_keys.count }.by(1)
end
it 'returns an existing ssh key when attempting to add a duplicate' do
@@ -117,18 +119,73 @@ describe API::DeployKeys do
end
end
+ describe 'PUT /projects/:id/deploy_keys/:key_id' do
+ let(:private_deploy_key) { create(:another_deploy_key, public: false) }
+ let(:project_private_deploy_key) do
+ create(:deploy_keys_project, project: project, deploy_key: private_deploy_key)
+ end
+
+ it 'updates a public deploy key as admin' do
+ expect do
+ put api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}", admin), { title: 'new title' }
+ end.not_to change(deploy_key, :title)
+
+ expect(response).to have_http_status(200)
+ end
+
+ it 'does not update a public deploy key as non admin' do
+ expect do
+ put api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}", user), { title: 'new title' }
+ end.not_to change(deploy_key, :title)
+
+ expect(response).to have_http_status(404)
+ end
+
+ it 'does not update a private key with invalid title' do
+ project_private_deploy_key
+
+ expect do
+ put api("/projects/#{project.id}/deploy_keys/#{private_deploy_key.id}", admin), { title: '' }
+ end.not_to change(deploy_key, :title)
+
+ expect(response).to have_http_status(400)
+ end
+
+ it 'updates a private ssh key with correct attributes' do
+ project_private_deploy_key
+
+ put api("/projects/#{project.id}/deploy_keys/#{private_deploy_key.id}", admin), { title: 'new title', can_push: true }
+
+ expect(json_response['id']).to eq(private_deploy_key.id)
+ expect(json_response['title']).to eq('new title')
+ expect(json_response['can_push']).to eq(true)
+ end
+
+ it 'updates a private ssh key from projects user has access with correct attributes' do
+ create(:deploy_keys_project, project: project2, deploy_key: private_deploy_key)
+
+ put api("/projects/#{project.id}/deploy_keys/#{private_deploy_key.id}", admin), { title: 'new title', can_push: true }
+
+ expect(json_response['id']).to eq(private_deploy_key.id)
+ expect(json_response['title']).to eq('new title')
+ expect(json_response['can_push']).to eq(true)
+ end
+ end
+
describe 'DELETE /projects/:id/deploy_keys/:key_id' do
- before { deploy_key }
+ before do
+ deploy_key
+ end
- it 'should delete existing key' do
+ it 'deletes existing key' do
expect do
delete api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}", admin)
expect(response).to have_http_status(204)
- end.to change{ project.deploy_keys.count }.by(-1)
+ end.to change { project.deploy_keys.count }.by(-1)
end
- it 'should return 404 Not Found with invalid ID' do
+ it 'returns 404 Not Found with invalid ID' do
delete api("/projects/#{project.id}/deploy_keys/404", admin)
expect(response).to have_http_status(404)
@@ -136,7 +193,7 @@ describe API::DeployKeys do
end
describe 'POST /projects/:id/deploy_keys/:key_id/enable' do
- let(:project2) { create(:empty_project) }
+ let(:project2) { create(:project) }
context 'when the user can admin the project' do
it 'enables the key' do
@@ -150,7 +207,7 @@ describe API::DeployKeys do
end
context 'when authenticated as non-admin user' do
- it 'should return a 404 error' do
+ it 'returns a 404 error' do
post api("/projects/#{project2.id}/deploy_keys/#{deploy_key.id}/enable", user)
expect(response).to have_http_status(404)
diff --git a/spec/requests/api/doorkeeper_access_spec.rb b/spec/requests/api/doorkeeper_access_spec.rb
index b5897b2e346..868fef65c1c 100644
--- a/spec/requests/api/doorkeeper_access_spec.rb
+++ b/spec/requests/api/doorkeeper_access_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API do
+describe 'doorkeeper access' do
let!(:user) { create(:user) }
let!(:application) { Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user) }
let!(:token) { Doorkeeper::AccessToken.create! application_id: application.id, resource_owner_id: user.id, scopes: "api" }
diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb
index aae03c84e1f..87716c6fe3a 100644
--- a/spec/requests/api/environments_spec.rb
+++ b/spec/requests/api/environments_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe API::Environments do
let(:user) { create(:user) }
let(:non_member) { create(:user) }
- let(:project) { create(:empty_project, :private, namespace: user.namespace) }
+ let(:project) { create(:project, :private, namespace: user.namespace) }
let!(:environment) { create(:environment, project: project) }
before do
@@ -13,7 +13,14 @@ describe API::Environments do
describe 'GET /projects/:id/environments' do
context 'as member of the project' do
it 'returns project environments' do
- project_data_keys = %w(id http_url_to_repo web_url name name_with_namespace path path_with_namespace)
+ project_data_keys = %w(
+ id description default_branch tag_list
+ ssh_url_to_repo http_url_to_repo web_url
+ name name_with_namespace
+ path path_with_namespace
+ star_count forks_count
+ created_at last_activity_at
+ )
get api("/projects/#{project.id}/environments", user)
diff --git a/spec/requests/api/events_spec.rb b/spec/requests/api/events_spec.rb
new file mode 100644
index 00000000000..a23d28994ce
--- /dev/null
+++ b/spec/requests/api/events_spec.rb
@@ -0,0 +1,200 @@
+require 'spec_helper'
+
+describe API::Events do
+ include ApiHelpers
+ let(:user) { create(:user) }
+ let(:non_member) { create(:user) }
+ let(:other_user) { create(:user, username: 'otheruser') }
+ let(:private_project) { create(:project, :private, creator_id: user.id, namespace: user.namespace) }
+ let(:closed_issue) { create(:closed_issue, project: private_project, author: user) }
+ let!(:closed_issue_event) { create(:event, project: private_project, author: user, target: closed_issue, action: Event::CLOSED, created_at: Date.new(2016, 12, 30)) }
+
+ describe 'GET /events' do
+ context 'when unauthenticated' do
+ it 'returns authentication error' do
+ get api('/events')
+
+ expect(response).to have_http_status(401)
+ end
+ end
+
+ context 'when authenticated' do
+ it 'returns users events' do
+ get api('/events?action=closed&target_type=issue&after=2016-12-1&before=2016-12-31', 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)
+ end
+ end
+ end
+
+ describe 'GET /users/:id/events' do
+ context "as a user that cannot see the event's project" do
+ it 'returns no events' do
+ get api("/users/#{user.id}/events", other_user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_empty
+ end
+ end
+
+ context "as a user that can see the event's project" do
+ it 'accepts a username' do
+ get api("/users/#{user.username}/events", 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)
+ end
+
+ it 'returns the events' do
+ get api("/users/#{user.id}/events", 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)
+ end
+
+ context 'when the list of events includes push events' do
+ let(:event) do
+ create(:push_event, author: user, project: private_project)
+ end
+
+ let!(:payload) { create(:push_event_payload, event: event) }
+ let(:payload_hash) { json_response[0]['push_data'] }
+
+ before do
+ get api("/users/#{user.id}/events?action=pushed", user)
+ end
+
+ it 'responds with HTTP 200 OK' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'includes the push payload as a Hash' do
+ expect(payload_hash).to be_an_instance_of(Hash)
+ end
+
+ it 'includes the push payload details' do
+ expect(payload_hash['commit_count']).to eq(payload.commit_count)
+ expect(payload_hash['action']).to eq(payload.action)
+ expect(payload_hash['ref_type']).to eq(payload.ref_type)
+ expect(payload_hash['commit_to']).to eq(payload.commit_to)
+ end
+ end
+
+ context 'when there are multiple events from different projects' do
+ let(:second_note) { create(:note_on_issue, project: create(:project)) }
+
+ before do
+ second_note.project.add_user(user, :developer)
+
+ [second_note].each do |note|
+ EventCreateService.new.leave_note(note, user)
+ end
+ end
+
+ it 'returns events in the correct order (from newest to oldest)' do
+ get api("/users/#{user.id}/events", user)
+
+ comment_events = json_response.select { |e| e['action_name'] == 'commented on' }
+ close_events = json_response.select { |e| e['action_name'] == 'closed' }
+
+ expect(comment_events[0]['target_id']).to eq(second_note.id)
+ expect(close_events[0]['target_id']).to eq(closed_issue.id)
+ end
+
+ it 'accepts filter parameters' do
+ get api("/users/#{user.id}/events?action=closed&target_type=issue&after=2016-12-1&before=2016-12-31", user)
+
+ expect(json_response.size).to eq(1)
+ expect(json_response[0]['target_id']).to eq(closed_issue.id)
+ end
+ end
+ end
+
+ it 'returns a 404 error if not found' do
+ get api('/users/42/events', user)
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 User Not Found')
+ end
+ end
+
+ describe 'GET /projects/:id/events' do
+ context 'when unauthenticated ' do
+ it 'returns 404 for private project' do
+ get api("/projects/#{private_project.id}/events")
+
+ expect(response).to have_http_status(404)
+ end
+
+ it 'returns 200 status for a public project' do
+ public_project = create(:project, :public)
+
+ get api("/projects/#{public_project.id}/events")
+
+ expect(response).to have_http_status(200)
+ end
+ end
+
+ context 'when not permitted to read' do
+ it 'returns 404' do
+ get api("/projects/#{private_project.id}/events", non_member)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'when authenticated' do
+ it 'returns project events' do
+ get api("/projects/#{private_project.id}/events?action=closed&target_type=issue&after=2016-12-1&before=2016-12-31", 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)
+ end
+
+ it 'returns 404 if project does not exist' do
+ get api("/projects/1234/events", user)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'when exists some events' do
+ let(:merge_request1) { create(:merge_request, :closed, author: user, assignee: user, source_project: private_project, title: 'Test') }
+ let(:merge_request2) { create(:merge_request, :closed, author: user, assignee: user, source_project: private_project, title: 'Test') }
+
+ before do
+ create_event(merge_request1)
+ end
+
+ it 'avoids N+1 queries' do
+ control_count = ActiveRecord::QueryRecorder.new do
+ get api("/projects/#{private_project.id}/events", user), target_type: :merge_request
+ end.count
+
+ create_event(merge_request2)
+
+ expect do
+ get api("/projects/#{private_project.id}/events", user), target_type: :merge_request
+ end.not_to exceed_query_limit(control_count)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response.size).to eq(2)
+ expect(json_response.map { |r| r['target_id'] }).to match_array([merge_request1.id, merge_request2.id])
+ end
+
+ def create_event(target)
+ create(:event, project: private_project, author: user, target: target)
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/features_spec.rb b/spec/requests/api/features_spec.rb
new file mode 100644
index 00000000000..7e21006b254
--- /dev/null
+++ b/spec/requests/api/features_spec.rb
@@ -0,0 +1,252 @@
+require 'spec_helper'
+
+describe API::Features do
+ let(:user) { create(:user) }
+ let(:admin) { create(:admin) }
+
+ before do
+ Flipper.unregister_groups
+ Flipper.register(:perf_team) do |actor|
+ actor.respond_to?(:admin) && actor.admin?
+ end
+ end
+
+ describe 'GET /features' do
+ let(:expected_features) do
+ [
+ {
+ 'name' => 'feature_1',
+ 'state' => 'on',
+ 'gates' => [{ 'key' => 'boolean', 'value' => true }]
+ },
+ {
+ 'name' => 'feature_2',
+ 'state' => 'off',
+ 'gates' => [{ 'key' => 'boolean', 'value' => false }]
+ },
+ {
+ 'name' => 'feature_3',
+ 'state' => 'conditional',
+ 'gates' => [
+ { 'key' => 'boolean', 'value' => false },
+ { 'key' => 'groups', 'value' => ['perf_team'] }
+ ]
+ }
+ ]
+ end
+
+ before do
+ Feature.get('feature_1').enable
+ Feature.get('feature_2').disable
+ Feature.get('feature_3').enable Feature.group(:perf_team)
+ end
+
+ it 'returns a 401 for anonymous users' do
+ get api('/features')
+
+ expect(response).to have_http_status(401)
+ end
+
+ it 'returns a 403 for users' do
+ get api('/features', user)
+
+ expect(response).to have_http_status(403)
+ end
+
+ it 'returns the feature list for admins' do
+ get api('/features', admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to match_array(expected_features)
+ end
+ end
+
+ describe 'POST /feature' do
+ let(:feature_name) { 'my_feature' }
+
+ context 'when the feature does not exist' do
+ it 'returns a 401 for anonymous users' do
+ post api("/features/#{feature_name}")
+
+ expect(response).to have_http_status(401)
+ end
+
+ it 'returns a 403 for users' do
+ post api("/features/#{feature_name}", user)
+
+ expect(response).to have_http_status(403)
+ end
+
+ context 'when passed value=true' do
+ it 'creates an enabled feature' do
+ post api("/features/#{feature_name}", admin), value: 'true'
+
+ expect(response).to have_http_status(201)
+ expect(json_response).to eq(
+ 'name' => 'my_feature',
+ 'state' => 'on',
+ 'gates' => [{ 'key' => 'boolean', 'value' => true }])
+ end
+
+ it 'creates an enabled feature for the given Flipper group when passed feature_group=perf_team' do
+ post api("/features/#{feature_name}", admin), value: 'true', feature_group: 'perf_team'
+
+ expect(response).to have_http_status(201)
+ expect(json_response).to eq(
+ 'name' => 'my_feature',
+ 'state' => 'conditional',
+ 'gates' => [
+ { 'key' => 'boolean', 'value' => false },
+ { 'key' => 'groups', 'value' => ['perf_team'] }
+ ])
+ end
+
+ it 'creates an enabled feature for the given user when passed user=username' do
+ post api("/features/#{feature_name}", admin), value: 'true', user: user.username
+
+ expect(response).to have_http_status(201)
+ expect(json_response).to eq(
+ 'name' => 'my_feature',
+ 'state' => 'conditional',
+ 'gates' => [
+ { 'key' => 'boolean', 'value' => false },
+ { 'key' => 'actors', 'value' => ["User:#{user.id}"] }
+ ])
+ end
+
+ it 'creates an enabled feature for the given user and feature group when passed user=username and feature_group=perf_team' do
+ post api("/features/#{feature_name}", admin), value: 'true', user: user.username, feature_group: 'perf_team'
+
+ expect(response).to have_http_status(201)
+ expect(json_response).to eq(
+ 'name' => 'my_feature',
+ 'state' => 'conditional',
+ 'gates' => [
+ { 'key' => 'boolean', 'value' => false },
+ { 'key' => 'groups', 'value' => ['perf_team'] },
+ { 'key' => 'actors', 'value' => ["User:#{user.id}"] }
+ ])
+ end
+ end
+
+ it 'creates a feature with the given percentage if passed an integer' do
+ post api("/features/#{feature_name}", admin), value: '50'
+
+ expect(response).to have_http_status(201)
+ expect(json_response).to eq(
+ 'name' => 'my_feature',
+ 'state' => 'conditional',
+ 'gates' => [
+ { 'key' => 'boolean', 'value' => false },
+ { 'key' => 'percentage_of_time', 'value' => 50 }
+ ])
+ end
+ end
+
+ context 'when the feature exists' do
+ let(:feature) { Feature.get(feature_name) }
+
+ before do
+ feature.disable # This also persists the feature on the DB
+ end
+
+ context 'when passed value=true' do
+ it 'enables the feature' do
+ post api("/features/#{feature_name}", admin), value: 'true'
+
+ expect(response).to have_http_status(201)
+ expect(json_response).to eq(
+ 'name' => 'my_feature',
+ 'state' => 'on',
+ 'gates' => [{ 'key' => 'boolean', 'value' => true }])
+ end
+
+ it 'enables the feature for the given Flipper group when passed feature_group=perf_team' do
+ post api("/features/#{feature_name}", admin), value: 'true', feature_group: 'perf_team'
+
+ expect(response).to have_http_status(201)
+ expect(json_response).to eq(
+ 'name' => 'my_feature',
+ 'state' => 'conditional',
+ 'gates' => [
+ { 'key' => 'boolean', 'value' => false },
+ { 'key' => 'groups', 'value' => ['perf_team'] }
+ ])
+ end
+
+ it 'enables the feature for the given user when passed user=username' do
+ post api("/features/#{feature_name}", admin), value: 'true', user: user.username
+
+ expect(response).to have_http_status(201)
+ expect(json_response).to eq(
+ 'name' => 'my_feature',
+ 'state' => 'conditional',
+ 'gates' => [
+ { 'key' => 'boolean', 'value' => false },
+ { 'key' => 'actors', 'value' => ["User:#{user.id}"] }
+ ])
+ end
+ end
+
+ context 'when feature is enabled and value=false is passed' do
+ it 'disables the feature' do
+ feature.enable
+ expect(feature).to be_enabled
+
+ post api("/features/#{feature_name}", admin), value: 'false'
+
+ expect(response).to have_http_status(201)
+ expect(json_response).to eq(
+ 'name' => 'my_feature',
+ 'state' => 'off',
+ 'gates' => [{ 'key' => 'boolean', 'value' => false }])
+ end
+
+ it 'disables the feature for the given Flipper group when passed feature_group=perf_team' do
+ feature.enable(Feature.group(:perf_team))
+ expect(Feature.get(feature_name).enabled?(admin)).to be_truthy
+
+ post api("/features/#{feature_name}", admin), value: 'false', feature_group: 'perf_team'
+
+ expect(response).to have_http_status(201)
+ expect(json_response).to eq(
+ 'name' => 'my_feature',
+ 'state' => 'off',
+ 'gates' => [{ 'key' => 'boolean', 'value' => false }])
+ end
+
+ it 'disables the feature for the given user when passed user=username' do
+ feature.enable(user)
+ expect(Feature.get(feature_name).enabled?(user)).to be_truthy
+
+ post api("/features/#{feature_name}", admin), value: 'false', user: user.username
+
+ expect(response).to have_http_status(201)
+ expect(json_response).to eq(
+ 'name' => 'my_feature',
+ 'state' => 'off',
+ 'gates' => [{ 'key' => 'boolean', 'value' => false }])
+ end
+ end
+
+ context 'with a pre-existing percentage value' do
+ before do
+ feature.enable_percentage_of_time(50)
+ end
+
+ it 'updates the percentage of time if passed an integer' do
+ post api("/features/#{feature_name}", admin), value: '30'
+
+ expect(response).to have_http_status(201)
+ expect(json_response).to eq(
+ 'name' => 'my_feature',
+ 'state' => 'conditional',
+ 'gates' => [
+ { 'key' => 'boolean', 'value' => false },
+ { 'key' => 'percentage_of_time', 'value' => 30 }
+ ])
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index fa28047d49c..ea97c556430 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -13,7 +13,9 @@ describe API::Files do
let(:author_email) { 'user@example.org' }
let(:author_name) { 'John Doe' }
- before { project.team << [user, :developer] }
+ before do
+ project.team << [user, :developer]
+ end
def route(file_path = nil)
"/projects/#{project.id}/repository/files/#{file_path}"
@@ -31,6 +33,15 @@ describe API::Files do
expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n")
end
+ it 'returns json when file has txt extension' do
+ file_path = "bar%2Fbranch-test.txt"
+
+ get api(route(file_path), current_user), params
+
+ expect(response).to have_http_status(200)
+ expect(response.content_type).to eq('application/json')
+ end
+
it 'returns file by commit sha' do
# This file is deleted on HEAD
file_path = "files%2Fjs%2Fcommit%2Ejs%2Ecoffee"
@@ -78,7 +89,7 @@ describe API::Files do
context 'when unauthenticated', 'and project is public' do
it_behaves_like 'repository files' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
@@ -151,7 +162,7 @@ describe API::Files do
context 'when unauthenticated', 'and project is public' do
it_behaves_like 'repository raw files' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
@@ -203,8 +214,8 @@ describe API::Files do
end
it "returns a 400 if editor fails to create file" do
- allow_any_instance_of(Repository).to receive(:create_file).
- and_raise(Repository::CommitError, 'Cannot create file')
+ allow_any_instance_of(Repository).to receive(:create_file)
+ .and_raise(Repository::CommitError, 'Cannot create file')
post api(route("any%2Etxt"), user), valid_params
@@ -218,6 +229,7 @@ describe API::Files do
post api(route("new_file_with_author%2Etxt"), user), valid_params
expect(response).to have_http_status(201)
+ expect(response.content_type).to eq('application/json')
last_commit = project.repository.commit.raw
expect(last_commit.author_email).to eq(author_email)
expect(last_commit.author_name).to eq(author_name)
@@ -258,6 +270,25 @@ describe API::Files do
expect(last_commit.author_name).to eq(user.name)
end
+ it "returns a 400 bad request if update existing file with stale last commit id" do
+ params_with_stale_id = valid_params.merge(last_commit_id: 'stale')
+
+ put api(route(file_path), user), params_with_stale_id
+
+ expect(response).to have_http_status(400)
+ expect(json_response['message']).to eq('You are attempting to update a file that has changed since you started editing it.')
+ end
+
+ it "updates existing file in project repo with accepts correct last commit id" do
+ last_commit = Gitlab::Git::Commit
+ .last_for_path(project.repository, 'master', URI.unescape(file_path))
+ params_with_correct_id = valid_params.merge(last_commit_id: last_commit.id)
+
+ put api(route(file_path), user), params_with_correct_id
+
+ expect(response).to have_http_status(200)
+ end
+
it "returns a 400 bad request if no params given" do
put api(route(file_path), user)
@@ -329,7 +360,7 @@ describe API::Files do
end
let(:get_params) do
{
- ref: 'master',
+ ref: 'master'
}
end
diff --git a/spec/requests/api/group_milestones_spec.rb b/spec/requests/api/group_milestones_spec.rb
new file mode 100644
index 00000000000..108721c6655
--- /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(: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/group_variables_spec.rb b/spec/requests/api/group_variables_spec.rb
new file mode 100644
index 00000000000..2179790d098
--- /dev/null
+++ b/spec/requests/api/group_variables_spec.rb
@@ -0,0 +1,221 @@
+require 'spec_helper'
+
+describe API::GroupVariables do
+ let(:group) { create(:group) }
+ let(:user) { create(:user) }
+
+ describe 'GET /groups/:id/variables' do
+ let!(:variable) { create(:ci_group_variable, group: group) }
+
+ context 'authorized user with proper permissions' do
+ before do
+ group.add_master(user)
+ end
+
+ it 'returns group variables' do
+ get api("/groups/#{group.id}/variables", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_a(Array)
+ end
+ end
+
+ context 'authorized user with invalid permissions' do
+ it 'does not return group variables' do
+ get api("/groups/#{group.id}/variables", user)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'unauthorized user' do
+ it 'does not return group variables' do
+ get api("/groups/#{group.id}/variables")
+
+ expect(response).to have_http_status(401)
+ end
+ end
+ end
+
+ describe 'GET /groups/:id/variables/:key' do
+ let!(:variable) { create(:ci_group_variable, group: group) }
+
+ context 'authorized user with proper permissions' do
+ before do
+ group.add_master(user)
+ end
+
+ it 'returns group variable details' do
+ get api("/groups/#{group.id}/variables/#{variable.key}", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['value']).to eq(variable.value)
+ expect(json_response['protected']).to eq(variable.protected?)
+ end
+
+ it 'responds with 404 Not Found if requesting non-existing variable' do
+ get api("/groups/#{group.id}/variables/non_existing_variable", user)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'authorized user with invalid permissions' do
+ it 'does not return group variable details' do
+ get api("/groups/#{group.id}/variables/#{variable.key}", user)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'unauthorized user' do
+ it 'does not return group variable details' do
+ get api("/groups/#{group.id}/variables/#{variable.key}")
+
+ expect(response).to have_http_status(401)
+ end
+ end
+ end
+
+ describe 'POST /groups/:id/variables' do
+ context 'authorized user with proper permissions' do
+ let!(:variable) { create(:ci_group_variable, group: group) }
+
+ before do
+ group.add_master(user)
+ end
+
+ it 'creates variable' do
+ expect do
+ post api("/groups/#{group.id}/variables", user), key: 'TEST_VARIABLE_2', value: 'VALUE_2', protected: true
+ end.to change {group.variables.count}.by(1)
+
+ expect(response).to have_http_status(201)
+ expect(json_response['key']).to eq('TEST_VARIABLE_2')
+ expect(json_response['value']).to eq('VALUE_2')
+ expect(json_response['protected']).to be_truthy
+ end
+
+ it 'creates variable with optional attributes' do
+ expect do
+ post api("/groups/#{group.id}/variables", user), key: 'TEST_VARIABLE_2', value: 'VALUE_2'
+ end.to change {group.variables.count}.by(1)
+
+ expect(response).to have_http_status(201)
+ expect(json_response['key']).to eq('TEST_VARIABLE_2')
+ expect(json_response['value']).to eq('VALUE_2')
+ expect(json_response['protected']).to be_falsey
+ end
+
+ it 'does not allow to duplicate variable key' do
+ expect do
+ post api("/groups/#{group.id}/variables", user), key: variable.key, value: 'VALUE_2'
+ end.to change {group.variables.count}.by(0)
+
+ expect(response).to have_http_status(400)
+ end
+ end
+
+ context 'authorized user with invalid permissions' do
+ it 'does not create variable' do
+ post api("/groups/#{group.id}/variables", user)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'unauthorized user' do
+ it 'does not create variable' do
+ post api("/groups/#{group.id}/variables")
+
+ expect(response).to have_http_status(401)
+ end
+ end
+ end
+
+ describe 'PUT /groups/:id/variables/:key' do
+ let!(:variable) { create(:ci_group_variable, group: group) }
+
+ context 'authorized user with proper permissions' do
+ before do
+ group.add_master(user)
+ end
+
+ it 'updates variable data' do
+ initial_variable = group.variables.first
+ value_before = initial_variable.value
+
+ put api("/groups/#{group.id}/variables/#{variable.key}", user), value: 'VALUE_1_UP', protected: true
+
+ updated_variable = group.variables.first
+
+ expect(response).to have_http_status(200)
+ expect(value_before).to eq(variable.value)
+ expect(updated_variable.value).to eq('VALUE_1_UP')
+ expect(updated_variable).to be_protected
+ end
+
+ it 'responds with 404 Not Found if requesting non-existing variable' do
+ put api("/groups/#{group.id}/variables/non_existing_variable", user)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'authorized user with invalid permissions' do
+ it 'does not update variable' do
+ put api("/groups/#{group.id}/variables/#{variable.key}", user)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'unauthorized user' do
+ it 'does not update variable' do
+ put api("/groups/#{group.id}/variables/#{variable.key}")
+
+ expect(response).to have_http_status(401)
+ end
+ end
+ end
+
+ describe 'DELETE /groups/:id/variables/:key' do
+ let!(:variable) { create(:ci_group_variable, group: group) }
+
+ context 'authorized user with proper permissions' do
+ before do
+ group.add_master(user)
+ end
+
+ it 'deletes variable' do
+ expect do
+ delete api("/groups/#{group.id}/variables/#{variable.key}", user)
+
+ expect(response).to have_http_status(204)
+ end.to change {group.variables.count}.by(-1)
+ end
+
+ it 'responds with 404 Not Found if requesting non-existing variable' do
+ delete api("/groups/#{group.id}/variables/non_existing_variable", user)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'authorized user with invalid permissions' do
+ it 'does not delete variable' do
+ delete api("/groups/#{group.id}/variables/#{variable.key}", user)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'unauthorized user' do
+ it 'does not delete variable' do
+ delete api("/groups/#{group.id}/variables/#{variable.key}")
+
+ expect(response).to have_http_status(401)
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 3e27a3bee77..313c38cd29c 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -9,9 +9,9 @@ describe API::Groups do
let(:admin) { create(:admin) }
let!(:group1) { create(:group, avatar: File.open(uploaded_image_temp_path)) }
let!(:group2) { create(:group, :private) }
- let!(:project1) { create(:empty_project, namespace: group1) }
- let!(:project2) { create(:empty_project, namespace: group2) }
- let!(:project3) { create(:empty_project, namespace: group1, path: 'test', visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
+ let!(:project1) { create(:project, namespace: group1) }
+ let!(:project2) { create(:project, namespace: group2) }
+ let!(:project3) { create(:project, namespace: group1, path: 'test', visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
before do
group1.add_owner(user1)
@@ -73,7 +73,7 @@ describe API::Groups do
storage_size: 702,
repository_size: 123,
lfs_objects_size: 234,
- build_artifacts_size: 345,
+ build_artifacts_size: 345
}.stringify_keys
exposed_attributes = attributes.dup
exposed_attributes['job_artifacts_size'] = exposed_attributes.delete('build_artifacts_size')
@@ -167,7 +167,7 @@ describe API::Groups do
describe "GET /groups/:id" do
context "when authenticated as user" do
it "returns one of user1's groups" do
- project = create(:empty_project, namespace: group2, path: 'Foo')
+ project = create(:project, namespace: group2, path: 'Foo')
create(:project_group_link, project: project, group: group1)
get api("/groups/#{group1.id}", user1)
@@ -178,7 +178,7 @@ describe API::Groups do
expect(json_response['path']).to eq(group1.path)
expect(json_response['description']).to eq(group1.description)
expect(json_response['visibility']).to eq(Gitlab::VisibilityLevel.string_level(group1.visibility_level))
- expect(json_response['avatar_url']).to eq(group1.avatar_url)
+ expect(json_response['avatar_url']).to eq(group1.avatar_url(only_path: false))
expect(json_response['web_url']).to eq(group1.web_url)
expect(json_response['request_access_enabled']).to eq(group1.request_access_enabled)
expect(json_response['full_name']).to eq(group1.full_name)
@@ -311,7 +311,7 @@ describe API::Groups do
end
it 'filters the groups projects' do
- public_project = create(:empty_project, :public, path: 'test1', group: group1)
+ public_project = create(:project, :public, path: 'test1', group: group1)
get api("/groups/#{group1.id}/projects", user1), visibility: 'public'
@@ -429,7 +429,7 @@ describe API::Groups do
expect(json_response["request_access_enabled"]).to eq(group[:request_access_enabled])
end
- it "creates a nested group" do
+ it "creates a nested group", :nested_groups do
parent = create(:group)
parent.add_owner(user3)
group = attributes_for(:group, { parent_id: parent.id })
@@ -509,12 +509,12 @@ describe API::Groups do
end
describe "POST /groups/:id/projects/:project_id" do
- let(:project) { create(:empty_project) }
- let(:project_path) { project.full_path.gsub('/', '%2F') }
+ let(:project) { create(:project) }
+ let(:project_path) { CGI.escape(project.full_path) }
- before(:each) do
- allow_any_instance_of(Projects::TransferService).
- to receive(:execute).and_return(true)
+ before do
+ allow_any_instance_of(Projects::TransferService)
+ .to receive(:execute).and_return(true)
end
context "when authenticated as user" do
diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb
index 06c8eb1d0b7..d4006fe71a2 100644
--- a/spec/requests/api/helpers_spec.rb
+++ b/spec/requests/api/helpers_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe API::Helpers do
include API::APIGuard::HelperMethods
- include API::Helpers
+ include described_class
include SentryHelper
let(:user) { create(:user) }
@@ -10,10 +10,22 @@ 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
+ allow_any_instance_of(self.class).to receive(:options).and_return({})
+ end
+
def set_env(user_or_token, identifier)
clear_env
clear_param
@@ -44,7 +56,7 @@ describe API::Helpers do
end
def doorkeeper_guard_returns(value)
- allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ value }
+ allow_any_instance_of(self.class).to receive(:doorkeeper_guard) { value }
end
def error!(message, status, header)
@@ -54,42 +66,94 @@ describe API::Helpers do
describe ".current_user" do
subject { current_user }
- describe "Warden authentication" do
- before { doorkeeper_guard_returns false }
+ describe "Warden authentication", :allow_forgery_protection do
+ before do
+ doorkeeper_guard_returns false
+ end
context "with invalid credentials" do
context "GET request" do
- before { env['REQUEST_METHOD'] = 'GET' }
+ before do
+ env['REQUEST_METHOD'] = 'GET'
+ end
+
it { is_expected.to be_nil }
end
end
context "with valid credentials" do
- before { warden_authenticate_returns user }
+ before do
+ warden_authenticate_returns user
+ end
context "GET request" do
- before { env['REQUEST_METHOD'] = 'GET' }
+ before do
+ env['REQUEST_METHOD'] = 'GET'
+ end
+
it { is_expected.to eq(user) }
end
context "HEAD request" do
- before { env['REQUEST_METHOD'] = 'HEAD' }
+ before do
+ env['REQUEST_METHOD'] = 'HEAD'
+ end
+
it { is_expected.to eq(user) }
end
context "PUT request" do
- before { env['REQUEST_METHOD'] = 'PUT' }
- it { is_expected.to be_nil }
+ before do
+ env['REQUEST_METHOD'] = 'PUT'
+ end
+
+ 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
- before { env['REQUEST_METHOD'] = 'POST' }
- it { is_expected.to be_nil }
+ before do
+ env['REQUEST_METHOD'] = 'POST'
+ end
+
+ 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
- before { env['REQUEST_METHOD'] = 'DELETE' }
- it { is_expected.to be_nil }
+ before do
+ env['REQUEST_METHOD'] = 'DELETE'
+ end
+
+ 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
@@ -97,7 +161,7 @@ describe API::Helpers do
describe "when authenticating using a user's private token" do
it "returns nil for an invalid token" do
env[API::APIGuard::PRIVATE_TOKEN_HEADER] = 'invalid token'
- allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false }
+ allow_any_instance_of(self.class).to receive(:doorkeeper_guard) { false }
expect(current_user).to be_nil
end
@@ -145,7 +209,6 @@ describe API::Helpers do
it "returns nil for a token without the appropriate scope" do
personal_access_token = create(:personal_access_token, user: user, scopes: ['read_user'])
env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token
- allow_access_with_scope('write_user')
expect(current_user).to be_nil
end
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index 429f1a4e375..e9c30dba8d4 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
@@ -15,21 +15,54 @@ describe API::Internal do
end
end
- describe "GET /internal/broadcast_message" do
- context "broadcast message exists" do
- let!(:broadcast_message) { create(:broadcast_message, starts_at: Time.now.yesterday, ends_at: Time.now.tomorrow ) }
+ describe 'GET /internal/broadcast_message' do
+ context 'broadcast message exists' do
+ let!(:broadcast_message) { create(:broadcast_message, starts_at: 1.day.ago, ends_at: 1.day.from_now ) }
- it do
- get api("/internal/broadcast_message"), secret_token: secret_token
+ it 'returns one broadcast message' do
+ get api('/internal/broadcast_message'), secret_token: secret_token
expect(response).to have_http_status(200)
- expect(json_response["message"]).to eq(broadcast_message.message)
+ expect(json_response['message']).to eq(broadcast_message.message)
end
end
- context "broadcast message doesn't exist" do
- it do
- get api("/internal/broadcast_message"), secret_token: secret_token
+ context 'broadcast message does not exist' do
+ it 'returns nothing' do
+ get api('/internal/broadcast_message'), secret_token: secret_token
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_empty
+ end
+ end
+
+ context 'nil broadcast message' do
+ it 'returns nothing' do
+ allow(BroadcastMessage).to receive(:current).and_return(nil)
+
+ get api('/internal/broadcast_message'), secret_token: secret_token
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_empty
+ end
+ end
+ end
+
+ describe 'GET /internal/broadcast_messages' do
+ context 'broadcast message(s) exist' do
+ let!(:broadcast_message) { create(:broadcast_message, starts_at: 1.day.ago, ends_at: 1.day.from_now ) }
+
+ it 'returns active broadcast message(s)' do
+ get api('/internal/broadcast_messages'), secret_token: secret_token
+
+ expect(response).to have_http_status(200)
+ expect(json_response[0]['message']).to eq(broadcast_message.message)
+ end
+ end
+
+ context 'broadcast message does not exist' do
+ it 'returns nothing' do
+ get api('/internal/broadcast_messages'), secret_token: secret_token
expect(response).to have_http_status(200)
expect(json_response).to be_empty
@@ -146,15 +179,14 @@ describe API::Internal do
end
end
- describe "POST /internal/allowed", :redis do
+ describe "POST /internal/allowed", :clean_gitlab_redis_shared_state do
context "access granted" do
- before do
- project.team << [user, :developer]
- Timecop.freeze
+ around do |example|
+ Timecop.freeze { example.run }
end
- after do
- Timecop.return
+ before do
+ project.team << [user, :developer]
end
context 'with env passed as a JSON' do
@@ -180,6 +212,7 @@ describe API::Internal do
expect(response).to have_http_status(200)
expect(json_response["status"]).to be_truthy
expect(json_response["repository_path"]).to eq(project.wiki.repository.path_to_repo)
+ expect(json_response["gl_repository"]).to eq("wiki-#{project.id}")
expect(user).not_to have_an_activity_record
end
end
@@ -191,48 +224,99 @@ describe API::Internal do
expect(response).to have_http_status(200)
expect(json_response["status"]).to be_truthy
expect(json_response["repository_path"]).to eq(project.wiki.repository.path_to_repo)
+ expect(json_response["gl_repository"]).to eq("wiki-#{project.id}")
expect(user).to have_an_activity_record
end
end
context "git pull" do
- it do
- pull(key, project)
+ context "gitaly disabled" do
+ it "has the correct payload" do
+ allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:ssh_upload_pack).and_return(false)
+ pull(key, project)
- expect(response).to have_http_status(200)
- expect(json_response["status"]).to be_truthy
- expect(json_response["repository_path"]).to eq(project.repository.path_to_repo)
- expect(user).to have_an_activity_record
+ expect(response).to have_http_status(200)
+ expect(json_response["status"]).to be_truthy
+ expect(json_response["repository_path"]).to eq(project.repository.path_to_repo)
+ expect(json_response["gl_repository"]).to eq("project-#{project.id}")
+ expect(json_response["gitaly"]).to be_nil
+ expect(user).to have_an_activity_record
+ end
+ end
+
+ context "gitaly enabled" do
+ it "has the correct payload" do
+ allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:ssh_upload_pack).and_return(true)
+ pull(key, project)
+
+ expect(response).to have_http_status(200)
+ expect(json_response["status"]).to be_truthy
+ expect(json_response["repository_path"]).to eq(project.repository.path_to_repo)
+ expect(json_response["gl_repository"]).to eq("project-#{project.id}")
+ expect(json_response["gitaly"]).not_to be_nil
+ expect(json_response["gitaly"]["repository"]).not_to be_nil
+ expect(json_response["gitaly"]["repository"]["storage_name"]).to eq(project.repository.gitaly_repository.storage_name)
+ expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
+ expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
+ expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
+ expect(user).to have_an_activity_record
+ end
end
end
context "git push" do
- it do
- push(key, project)
+ context "gitaly disabled" do
+ it "has the correct payload" do
+ allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:ssh_receive_pack).and_return(false)
+ push(key, project)
- expect(response).to have_http_status(200)
- expect(json_response["status"]).to be_truthy
- expect(json_response["repository_path"]).to eq(project.repository.path_to_repo)
- expect(user).not_to have_an_activity_record
+ expect(response).to have_http_status(200)
+ expect(json_response["status"]).to be_truthy
+ expect(json_response["repository_path"]).to eq(project.repository.path_to_repo)
+ expect(json_response["gl_repository"]).to eq("project-#{project.id}")
+ expect(json_response["gitaly"]).to be_nil
+ expect(user).not_to have_an_activity_record
+ end
+ end
+
+ context "gitaly enabled" do
+ it "has the correct payload" do
+ allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:ssh_receive_pack).and_return(true)
+ push(key, project)
+
+ expect(response).to have_http_status(200)
+ expect(json_response["status"]).to be_truthy
+ expect(json_response["repository_path"]).to eq(project.repository.path_to_repo)
+ expect(json_response["gl_repository"]).to eq("project-#{project.id}")
+ expect(json_response["gitaly"]).not_to be_nil
+ expect(json_response["gitaly"]["repository"]).not_to be_nil
+ expect(json_response["gitaly"]["repository"]["storage_name"]).to eq(project.repository.gitaly_repository.storage_name)
+ expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
+ expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
+ expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
+ expect(user).not_to have_an_activity_record
+ end
end
context 'project as /namespace/project' do
it do
- pull(key, project_with_repo_path('/' + project.path_with_namespace))
+ pull(key, project_with_repo_path('/' + project.full_path))
expect(response).to have_http_status(200)
expect(json_response["status"]).to be_truthy
expect(json_response["repository_path"]).to eq(project.repository.path_to_repo)
+ expect(json_response["gl_repository"]).to eq("project-#{project.id}")
end
end
context 'project as namespace/project' do
it do
- pull(key, project_with_repo_path(project.path_with_namespace))
+ pull(key, project_with_repo_path(project.full_path))
expect(response).to have_http_status(200)
expect(json_response["status"]).to be_truthy
expect(json_response["repository_path"]).to eq(project.repository.path_to_repo)
+ expect(json_response["gl_repository"]).to eq("project-#{project.id}")
end
end
end
@@ -265,7 +349,7 @@ describe API::Internal do
end
context "blocked user" do
- let(:personal_project) { create(:empty_project, namespace: user.namespace) }
+ let(:personal_project) { create(:project, namespace: user.namespace) }
before do
user.block
@@ -293,8 +377,6 @@ describe API::Internal do
end
context "archived project" do
- let(:personal_project) { create(:empty_project, namespace: user.namespace) }
-
before do
project.team << [user, :developer]
project.archive!
@@ -417,6 +499,42 @@ describe API::Internal do
expect(json_response['status']).to be_truthy
end
end
+
+ context 'the project path was changed' do
+ let!(:old_path_to_repo) { project.repository.path_to_repo }
+ let!(:old_full_path) { project.full_path }
+ let(:project_moved_message) do
+ <<-MSG.strip_heredoc
+ Project '#{old_full_path}' was moved to '#{project.full_path}'.
+
+ Please update your Git remote and try again:
+
+ git remote set-url origin #{project.ssh_url_to_repo}
+ MSG
+ end
+
+ before do
+ project.team << [user, :developer]
+ project.path = 'new_path'
+ project.save!
+ end
+
+ it 'rejects the push' do
+ push_with_path(key, old_path_to_repo)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['status']).to be_falsey
+ expect(json_response['message']).to eq(project_moved_message)
+ end
+
+ it 'rejects the SSH pull' do
+ pull_with_path(key, old_path_to_repo)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['status']).to be_falsey
+ expect(json_response['message']).to eq(project_moved_message)
+ end
+ end
end
describe 'GET /internal/merge_request_urls' do
@@ -444,36 +562,104 @@ describe API::Internal do
expect(json_response).to eq([])
end
- end
-
- describe 'POST /notify_post_receive' do
- let(:valid_params) do
- { repo_path: project.repository.path, secret_token: secret_token }
- end
-
- before do
- allow(Gitlab.config.gitaly).to receive(:enabled).and_return(true)
- end
-
- it "calls the Gitaly client if it's enabled" do
- expect_any_instance_of(Gitlab::GitalyClient::Notifications).
- to receive(:post_receive)
- post api("/internal/notify_post_receive"), valid_params
+ context 'with a gl_repository parameter' do
+ let(:gl_repository) { "project-#{project.id}" }
- expect(response).to have_http_status(200)
- end
+ it 'returns link to create new merge request' do
+ get api("/internal/merge_request_urls?gl_repository=#{gl_repository}&changes=#{changes}"), secret_token: secret_token
- it "returns 500 if the gitaly call fails" do
- expect_any_instance_of(Gitlab::GitalyClient::Notifications).
- to receive(:post_receive).and_raise(GRPC::Unavailable)
-
- post api("/internal/notify_post_receive"), valid_params
-
- expect(response).to have_http_status(500)
+ expect(json_response).to match [{
+ "branch_name" => "new_branch",
+ "url" => "http://#{Gitlab.config.gitlab.host}/#{project.namespace.name}/#{project.path}/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch",
+ "new_merge_request" => true
+ }]
+ end
end
end
+ # TODO: Uncomment when the end-point is reenabled
+ # describe 'POST /notify_post_receive' do
+ # let(:valid_params) do
+ # { project: project.repository.path, secret_token: secret_token }
+ # end
+ #
+ # let(:valid_wiki_params) do
+ # { project: project.wiki.repository.path, secret_token: secret_token }
+ # end
+ #
+ # before do
+ # allow(Gitlab.config.gitaly).to receive(:enabled).and_return(true)
+ # end
+ #
+ # it "calls the Gitaly client with the project's repository" do
+ # expect(Gitlab::GitalyClient::NotificationService).
+ # to receive(:new).with(gitlab_git_repository_with(path: project.repository.path)).
+ # and_call_original
+ # expect_any_instance_of(Gitlab::GitalyClient::NotificationService).
+ # to receive(:post_receive)
+ #
+ # post api("/internal/notify_post_receive"), valid_params
+ #
+ # expect(response).to have_http_status(200)
+ # end
+ #
+ # it "calls the Gitaly client with the wiki's repository if it's a wiki" do
+ # expect(Gitlab::GitalyClient::NotificationService).
+ # to receive(:new).with(gitlab_git_repository_with(path: project.wiki.repository.path)).
+ # and_call_original
+ # expect_any_instance_of(Gitlab::GitalyClient::NotificationService).
+ # to receive(:post_receive)
+ #
+ # post api("/internal/notify_post_receive"), valid_wiki_params
+ #
+ # expect(response).to have_http_status(200)
+ # end
+ #
+ # it "returns 500 if the gitaly call fails" do
+ # expect_any_instance_of(Gitlab::GitalyClient::NotificationService).
+ # to receive(:post_receive).and_raise(GRPC::Unavailable)
+ #
+ # post api("/internal/notify_post_receive"), valid_params
+ #
+ # expect(response).to have_http_status(500)
+ # end
+ #
+ # context 'with a gl_repository parameter' do
+ # let(:valid_params) do
+ # { gl_repository: "project-#{project.id}", secret_token: secret_token }
+ # end
+ #
+ # let(:valid_wiki_params) do
+ # { gl_repository: "wiki-#{project.id}", secret_token: secret_token }
+ # end
+ #
+ # it "calls the Gitaly client with the project's repository" do
+ # expect(Gitlab::GitalyClient::NotificationService).
+ # to receive(:new).with(gitlab_git_repository_with(path: project.repository.path)).
+ # and_call_original
+ # expect_any_instance_of(Gitlab::GitalyClient::NotificationService).
+ # to receive(:post_receive)
+ #
+ # post api("/internal/notify_post_receive"), valid_params
+ #
+ # expect(response).to have_http_status(200)
+ # end
+ #
+ # it "calls the Gitaly client with the wiki's repository if it's a wiki" do
+ # expect(Gitlab::GitalyClient::NotificationService).
+ # to receive(:new).with(gitlab_git_repository_with(path: project.wiki.repository.path)).
+ # and_call_original
+ # expect_any_instance_of(Gitlab::GitalyClient::NotificationService).
+ # to receive(:post_receive)
+ #
+ # post api("/internal/notify_post_receive"), valid_wiki_params
+ #
+ # expect(response).to have_http_status(200)
+ # end
+ # end
+ # end
+
def project_with_repo_path(path)
double().tap do |fake_project|
allow(fake_project).to receive_message_chain('repository.path_to_repo' => path)
@@ -491,6 +677,17 @@ describe API::Internal do
)
end
+ def pull_with_path(key, path_to_repo, protocol = 'ssh')
+ post(
+ api("/internal/allowed"),
+ key_id: key.id,
+ project: path_to_repo,
+ action: 'git-upload-pack',
+ secret_token: secret_token,
+ protocol: protocol
+ )
+ end
+
def push(key, project, protocol = 'ssh', env: nil)
post(
api("/internal/allowed"),
@@ -504,6 +701,19 @@ describe API::Internal do
)
end
+ def push_with_path(key, path_to_repo, protocol = 'ssh', env: nil)
+ post(
+ api("/internal/allowed"),
+ changes: 'd14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/master',
+ key_id: key.id,
+ project: path_to_repo,
+ action: 'git-receive-pack',
+ secret_token: secret_token,
+ protocol: protocol,
+ env: env
+ )
+ end
+
def archive(key, project)
post(
api("/internal/allowed"),
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 3ca13111acb..7d120e4a234 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -1,11 +1,9 @@
require 'spec_helper'
-describe API::Issues do
- include EmailHelpers
-
+describe API::Issues, :mailer do
set(:user) { create(:user) }
set(:project) do
- create(:empty_project, :public, creator_id: user.id, namespace: user.namespace)
+ create(:project, :public, creator_id: user.id, namespace: user.namespace)
end
let(:user2) { create(:user) }
@@ -19,7 +17,7 @@ describe API::Issues do
let!(:closed_issue) do
create :closed_issue,
author: user,
- assignee: user,
+ assignees: [user],
project: project,
state: :closed,
milestone: milestone,
@@ -31,14 +29,14 @@ describe API::Issues do
:confidential,
project: project,
author: author,
- assignee: assignee,
+ assignees: [assignee],
created_at: generate(:past_time),
updated_at: 2.hours.ago
end
let!(:issue) do
create :issue,
author: user,
- assignee: user,
+ assignees: [user],
project: project,
milestone: milestone,
created_at: generate(:past_time),
@@ -71,7 +69,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 +102,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
@@ -261,11 +294,11 @@ describe API::Issues do
describe "GET /groups/:id/issues" do
let!(:group) { create(:group) }
- let!(:group_project) { create(:empty_project, :public, creator_id: user.id, namespace: group) }
+ let!(:group_project) { create(:project, :public, creator_id: user.id, namespace: group) }
let!(:group_closed_issue) do
create :closed_issue,
author: user,
- assignee: user,
+ assignees: [user],
project: group_project,
state: :closed,
milestone: group_milestone,
@@ -276,13 +309,13 @@ describe API::Issues do
:confidential,
project: group_project,
author: author,
- assignee: assignee,
+ assignees: [assignee],
updated_at: 2.hours.ago
end
let!(:group_issue) do
create :issue,
author: user,
- assignee: user,
+ assignees: [user],
project: group_project,
milestone: group_milestone,
updated_at: 1.hour.ago,
@@ -483,7 +516,7 @@ describe API::Issues do
end
it "returns 404 on private projects for other users" do
- private_project = create(:empty_project, :private)
+ private_project = create(:project, :private)
create(:issue, project: private_project)
get api("/projects/#{private_project.id}/issues", non_member)
@@ -492,7 +525,7 @@ describe API::Issues do
end
it 'returns no issues when user has access to project but not issues' do
- restricted_project = create(:empty_project, :public, :issues_private)
+ restricted_project = create(:project, :public, :issues_private)
create(:issue, project: restricted_project)
get api("/projects/#{restricted_project.id}/issues", non_member)
@@ -687,11 +720,25 @@ describe API::Issues do
expect(json_response['updated_at']).to be_present
expect(json_response['labels']).to eq(issue.label_names)
expect(json_response['milestone']).to be_a Hash
+ expect(json_response['assignees']).to be_a Array
expect(json_response['assignee']).to be_a Hash
expect(json_response['author']).to be_a Hash
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)
@@ -759,15 +806,41 @@ describe API::Issues do
end
describe "POST /projects/:id/issues" do
+ context 'support for deprecated assignee_id' do
+ it 'creates a new project issue' do
+ post api("/projects/#{project.id}/issues", user),
+ title: 'new issue', assignee_id: user2.id
+
+ expect(response).to have_http_status(201)
+ expect(json_response['title']).to eq('new issue')
+ expect(json_response['assignee']['name']).to eq(user2.name)
+ expect(json_response['assignees'].first['name']).to eq(user2.name)
+ end
+ end
+
+ context 'single assignee restrictions' do
+ it 'creates a new project issue with no more than one assignee' do
+ post api("/projects/#{project.id}/issues", user),
+ title: 'new issue', assignee_ids: [user2.id, guest.id]
+
+ expect(response).to have_http_status(201)
+ expect(json_response['title']).to eq('new issue')
+ expect(json_response['assignees'].count).to eq(1)
+ end
+ end
+
it 'creates a new project issue' do
post api("/projects/#{project.id}/issues", user),
- title: 'new issue', labels: 'label, label2'
+ title: 'new issue', labels: 'label, label2', weight: 3,
+ assignee_ids: [user2.id]
expect(response).to have_http_status(201)
expect(json_response['title']).to eq('new issue')
expect(json_response['description']).to be_nil
expect(json_response['labels']).to eq(%w(label label2))
expect(json_response['confidential']).to be_falsy
+ expect(json_response['assignee']['name']).to eq(user2.name)
+ expect(json_response['assignees'].first['name']).to eq(user2.name)
end
it 'creates a new confidential project issue' do
@@ -1057,6 +1130,57 @@ describe API::Issues do
end
end
+ describe 'PUT /projects/:id/issues/:issue_iid to update assignee' do
+ context 'support for deprecated assignee_id' do
+ it 'removes assignee' do
+ put api("/projects/#{project.id}/issues/#{issue.iid}", user),
+ assignee_id: 0
+
+ expect(response).to have_http_status(200)
+
+ expect(json_response['assignee']).to be_nil
+ end
+
+ it 'updates an issue with new assignee' do
+ put api("/projects/#{project.id}/issues/#{issue.iid}", user),
+ assignee_id: user2.id
+
+ expect(response).to have_http_status(200)
+
+ expect(json_response['assignee']['name']).to eq(user2.name)
+ end
+ end
+
+ it 'removes assignee' do
+ put api("/projects/#{project.id}/issues/#{issue.iid}", user),
+ assignee_ids: [0]
+
+ expect(response).to have_http_status(200)
+
+ expect(json_response['assignees']).to be_empty
+ end
+
+ it 'updates an issue with new assignee' do
+ put api("/projects/#{project.id}/issues/#{issue.iid}", user),
+ assignee_ids: [user2.id]
+
+ expect(response).to have_http_status(200)
+
+ expect(json_response['assignees'].first['name']).to eq(user2.name)
+ end
+
+ context 'single assignee restrictions' do
+ it 'updates an issue with several assignees but only one has been applied' do
+ put api("/projects/#{project.id}/issues/#{issue.iid}", user),
+ assignee_ids: [user2.id, guest.id]
+
+ expect(response).to have_http_status(200)
+
+ expect(json_response['assignees'].size).to eq(1)
+ end
+ end
+ end
+
describe 'PUT /projects/:id/issues/:issue_iid to update labels' do
let!(:label) { create(:label, title: 'dummy', project: project) }
let!(:label_link) { create(:label_link, label: label, target: issue) }
@@ -1133,7 +1257,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
@@ -1173,7 +1297,7 @@ describe API::Issues do
context "when the user is project owner" do
let(:owner) { create(:user) }
- let(:project) { create(:empty_project, namespace: owner.namespace) }
+ let(:project) { create(:project, namespace: owner.namespace) }
it "deletes the issue if an admin requests it" do
delete api("/projects/#{project.id}/issues/#{issue.iid}", owner)
@@ -1198,8 +1322,8 @@ describe API::Issues do
end
describe '/projects/:id/issues/:issue_iid/move' do
- let!(:target_project) { create(:empty_project, path: 'project2', creator_id: user.id, namespace: user.namespace ) }
- let!(:target_project2) { create(:empty_project, creator_id: non_member.id, namespace: non_member.namespace ) }
+ let!(:target_project) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace ) }
+ let!(:target_project2) { create(:project, creator_id: non_member.id, namespace: non_member.namespace ) }
it 'moves an issue' do
post api("/projects/#{project.id}/issues/#{issue.iid}/move", user),
@@ -1384,6 +1508,25 @@ describe API::Issues do
end
end
+ describe "GET /projects/:id/issues/:issue_iid/user_agent_detail" do
+ let!(:user_agent_detail) { create(:user_agent_detail, subject: issue) }
+
+ it 'exposes known attributes' do
+ get api("/projects/#{project.id}/issues/#{issue.iid}/user_agent_detail", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['user_agent']).to eq(user_agent_detail.user_agent)
+ expect(json_response['ip_address']).to eq(user_agent_detail.ip_address)
+ expect(json_response['akismet_submitted']).to eq(user_agent_detail.submitted)
+ end
+
+ it "returns unautorized for non-admin users" do
+ get api("/projects/#{project.id}/issues/#{issue.iid}/user_agent_detail", user)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
def expect_paginated_array_response(size: nil)
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index decb5b91941..f56baf9663d 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -1,14 +1,26 @@
require 'spec_helper'
describe API::Jobs do
+ let!(:project) do
+ create(:project, :repository, public_builds: false)
+ end
+
+ let!(:pipeline) do
+ create(:ci_empty_pipeline, project: project,
+ sha: project.commit.id,
+ ref: project.default_branch)
+ end
+
+ let!(:job) { create(:ci_build, pipeline: pipeline) }
+
let(:user) { create(:user) }
let(:api_user) { user }
- let!(:project) { create(:project, :repository, creator: user, public_builds: false) }
- let!(:developer) { create(:project_member, :developer, user: user, project: project) }
- let(:reporter) { create(:project_member, :reporter, project: project) }
- let(:guest) { create(:project_member, :guest, project: project) }
- let!(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id, ref: project.default_branch) }
- let!(:build) { create(:ci_build, pipeline: pipeline) }
+ let(:reporter) { create(:project_member, :reporter, project: project).user }
+ let(:guest) { create(:project_member, :guest, project: project).user }
+
+ before do
+ project.add_developer(user)
+ end
describe 'GET /projects/:id/jobs' do
let(:query) { Hash.new }
@@ -30,13 +42,13 @@ describe API::Jobs do
end
it 'returns pipeline data' do
- json_build = json_response.first
+ json_job = json_response.first
- expect(json_build['pipeline']).not_to be_empty
- expect(json_build['pipeline']['id']).to eq build.pipeline.id
- expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
- expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
- expect(json_build['pipeline']['status']).to eq build.pipeline.status
+ expect(json_job['pipeline']).not_to be_empty
+ expect(json_job['pipeline']['id']).to eq job.pipeline.id
+ expect(json_job['pipeline']['ref']).to eq job.pipeline.ref
+ expect(json_job['pipeline']['sha']).to eq job.pipeline.sha
+ expect(json_job['pipeline']['status']).to eq job.pipeline.status
end
context 'filter project with one scope element' do
@@ -67,7 +79,7 @@ describe API::Jobs do
context 'unauthorized user' do
let(:api_user) { nil }
- it 'does not return project builds' do
+ it 'does not return project jobs' do
expect(response).to have_http_status(401)
end
end
@@ -93,13 +105,13 @@ describe API::Jobs do
end
it 'returns pipeline data' do
- json_build = json_response.first
+ json_job = json_response.first
- expect(json_build['pipeline']).not_to be_empty
- expect(json_build['pipeline']['id']).to eq build.pipeline.id
- expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
- expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
- expect(json_build['pipeline']['status']).to eq build.pipeline.status
+ expect(json_job['pipeline']).not_to be_empty
+ expect(json_job['pipeline']['id']).to eq job.pipeline.id
+ expect(json_job['pipeline']['ref']).to eq job.pipeline.ref
+ expect(json_job['pipeline']['sha']).to eq job.pipeline.sha
+ expect(json_job['pipeline']['status']).to eq job.pipeline.status
end
context 'filter jobs with one scope element' do
@@ -128,7 +140,7 @@ describe API::Jobs do
context 'jobs in different pipelines' do
let!(:pipeline2) { create(:ci_empty_pipeline, project: project) }
- let!(:build2) { create(:ci_build, pipeline: pipeline2) }
+ let!(:job2) { create(:ci_build, pipeline: pipeline2) }
it 'excludes jobs from other pipelines' do
json_response.each { |job| expect(job['pipeline']['id']).to eq(pipeline.id) }
@@ -147,7 +159,7 @@ describe API::Jobs do
describe 'GET /projects/:id/jobs/:job_id' do
before do
- get api("/projects/#{project.id}/jobs/#{build.id}", api_user)
+ get api("/projects/#{project.id}/jobs/#{job.id}", api_user)
end
context 'authorized user' do
@@ -157,12 +169,13 @@ describe API::Jobs do
end
it 'returns pipeline data' do
- json_build = json_response
- expect(json_build['pipeline']).not_to be_empty
- expect(json_build['pipeline']['id']).to eq build.pipeline.id
- expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
- expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
- expect(json_build['pipeline']['status']).to eq build.pipeline.status
+ json_job = json_response
+
+ expect(json_job['pipeline']).not_to be_empty
+ expect(json_job['pipeline']['id']).to eq job.pipeline.id
+ expect(json_job['pipeline']['ref']).to eq job.pipeline.ref
+ expect(json_job['pipeline']['sha']).to eq job.pipeline.sha
+ expect(json_job['pipeline']['status']).to eq job.pipeline.status
end
end
@@ -177,11 +190,11 @@ describe API::Jobs do
describe 'GET /projects/:id/jobs/:job_id/artifacts' do
before do
- get api("/projects/#{project.id}/jobs/#{build.id}/artifacts", api_user)
+ get api("/projects/#{project.id}/jobs/#{job.id}/artifacts", api_user)
end
context 'job with artifacts' do
- let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :artifacts, pipeline: pipeline) }
context 'authorized user' do
let(:download_headers) do
@@ -192,7 +205,7 @@ describe API::Jobs do
it 'returns specific job artifacts' do
expect(response).to have_http_status(200)
expect(response.headers).to include(download_headers)
- expect(response.body).to match_file(build.artifacts_file.file.file)
+ expect(response.body).to match_file(job.artifacts_file.file.file)
end
end
@@ -211,15 +224,15 @@ describe API::Jobs do
end
describe 'GET /projects/:id/artifacts/:ref_name/download?job=name' do
- let(:api_user) { reporter.user }
- let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
+ let(:api_user) { reporter }
+ let(:job) { create(:ci_build, :artifacts, pipeline: pipeline) }
before do
- build.success
+ job.success
end
- def get_for_ref(ref = pipeline.ref, job = build.name)
- get api("/projects/#{project.id}/jobs/artifacts/#{ref}/download", api_user), job: job
+ def get_for_ref(ref = pipeline.ref, job_name = job.name)
+ get api("/projects/#{project.id}/jobs/artifacts/#{ref}/download", api_user), job: job_name
end
context 'when not logged in' do
@@ -235,7 +248,7 @@ describe API::Jobs do
end
context 'when logging as guest' do
- let(:api_user) { guest.user }
+ let(:api_user) { guest }
before do
get_for_ref
@@ -273,7 +286,7 @@ describe API::Jobs do
let(:download_headers) do
{ 'Content-Transfer-Encoding' => 'binary',
'Content-Disposition' =>
- "attachment; filename=#{build.artifacts_file.filename}" }
+ "attachment; filename=#{job.artifacts_file.filename}" }
end
it { expect(response).to have_http_status(200) }
@@ -309,16 +322,16 @@ describe API::Jobs do
end
describe 'GET /projects/:id/jobs/:job_id/trace' do
- let(:build) { create(:ci_build, :trace, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :trace, pipeline: pipeline) }
before do
- get api("/projects/#{project.id}/jobs/#{build.id}/trace", api_user)
+ get api("/projects/#{project.id}/jobs/#{job.id}/trace", api_user)
end
context 'authorized user' do
it 'returns specific job trace' do
expect(response).to have_http_status(200)
- expect(response.body).to eq(build.trace.raw)
+ expect(response.body).to eq(job.trace.raw)
end
end
@@ -333,7 +346,7 @@ describe API::Jobs do
describe 'POST /projects/:id/jobs/:job_id/cancel' do
before do
- post api("/projects/#{project.id}/jobs/#{build.id}/cancel", api_user)
+ post api("/projects/#{project.id}/jobs/#{job.id}/cancel", api_user)
end
context 'authorized user' do
@@ -345,7 +358,7 @@ describe API::Jobs do
end
context 'user without :update_build permission' do
- let(:api_user) { reporter.user }
+ let(:api_user) { reporter }
it 'does not cancel job' do
expect(response).to have_http_status(403)
@@ -363,10 +376,10 @@ describe API::Jobs do
end
describe 'POST /projects/:id/jobs/:job_id/retry' do
- let(:build) { create(:ci_build, :canceled, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :canceled, pipeline: pipeline) }
before do
- post api("/projects/#{project.id}/jobs/#{build.id}/retry", api_user)
+ post api("/projects/#{project.id}/jobs/#{job.id}/retry", api_user)
end
context 'authorized user' do
@@ -379,7 +392,7 @@ describe API::Jobs do
end
context 'user without :update_build permission' do
- let(:api_user) { reporter.user }
+ let(:api_user) { reporter }
it 'does not retry job' do
expect(response).to have_http_status(403)
@@ -398,28 +411,29 @@ describe API::Jobs do
describe 'POST /projects/:id/jobs/:job_id/erase' do
before do
- post api("/projects/#{project.id}/jobs/#{build.id}/erase", user)
+ post api("/projects/#{project.id}/jobs/#{job.id}/erase", user)
end
context 'job is erasable' do
- let(:build) { create(:ci_build, :trace, :artifacts, :success, project: project, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :trace, :artifacts, :success, project: project, pipeline: pipeline) }
it 'erases job content' do
expect(response).to have_http_status(201)
- expect(build).not_to have_trace
- expect(build.artifacts_file.exists?).to be_falsy
- expect(build.artifacts_metadata.exists?).to be_falsy
+ expect(job).not_to have_trace
+ expect(job.artifacts_file.exists?).to be_falsy
+ expect(job.artifacts_metadata.exists?).to be_falsy
end
it 'updates job' do
- build.reload
- expect(build.erased_at).to be_truthy
- expect(build.erased_by).to eq(user)
+ job.reload
+
+ expect(job.erased_at).to be_truthy
+ expect(job.erased_by).to eq(user)
end
end
context 'job is not erasable' do
- let(:build) { create(:ci_build, :trace, project: project, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :trace, project: project, pipeline: pipeline) }
it 'responds with forbidden' do
expect(response).to have_http_status(403)
@@ -427,25 +441,25 @@ describe API::Jobs do
end
end
- describe 'POST /projects/:id/jobs/:build_id/artifacts/keep' do
+ describe 'POST /projects/:id/jobs/:job_id/artifacts/keep' do
before do
- post api("/projects/#{project.id}/jobs/#{build.id}/artifacts/keep", user)
+ post api("/projects/#{project.id}/jobs/#{job.id}/artifacts/keep", user)
end
context 'artifacts did not expire' do
- let(:build) do
+ let(:job) do
create(:ci_build, :trace, :artifacts, :success,
project: project, pipeline: pipeline, artifacts_expire_at: Time.now + 7.days)
end
it 'keeps artifacts' do
expect(response).to have_http_status(200)
- expect(build.reload.artifacts_expire_at).to be_nil
+ expect(job.reload.artifacts_expire_at).to be_nil
end
end
context 'no artifacts' do
- let(:build) { create(:ci_build, project: project, pipeline: pipeline) }
+ let(:job) { create(:ci_build, project: project, pipeline: pipeline) }
it 'responds with not found' do
expect(response).to have_http_status(404)
@@ -455,16 +469,39 @@ describe API::Jobs do
describe 'POST /projects/:id/jobs/:job_id/play' do
before do
- post api("/projects/#{project.id}/jobs/#{build.id}/play", user)
+ post api("/projects/#{project.id}/jobs/#{job.id}/play", api_user)
end
context 'on an playable job' do
- let(:build) { create(:ci_build, :manual, project: project, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :manual, project: project, pipeline: pipeline) }
- it 'plays the job' do
- expect(response).to have_http_status(200)
- expect(json_response['user']['id']).to eq(user.id)
- expect(json_response['id']).to eq(build.id)
+ context 'when user is authorized to trigger a manual action' do
+ it 'plays the job' do
+ expect(response).to have_http_status(200)
+ expect(json_response['user']['id']).to eq(user.id)
+ expect(json_response['id']).to eq(job.id)
+ expect(job.reload).to be_pending
+ end
+ end
+
+ context 'when user is not authorized to trigger a manual action' do
+ context 'when user does not have access to the project' do
+ let(:api_user) { create(:user) }
+
+ it 'does not trigger a manual action' do
+ expect(job.reload).to be_manual
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'when user is not allowed to trigger the manual action' do
+ let(:api_user) { reporter }
+
+ it 'does not trigger a manual action' do
+ expect(job.reload).to be_manual
+ expect(response).to have_http_status(403)
+ end
+ end
end
end
diff --git a/spec/requests/api/keys_spec.rb b/spec/requests/api/keys_spec.rb
index ab957c72984..f534332ca6c 100644
--- a/spec/requests/api/keys_spec.rb
+++ b/spec/requests/api/keys_spec.rb
@@ -4,11 +4,9 @@ describe API::Keys do
let(:user) { create(:user) }
let(:admin) { create(:admin) }
let(:key) { create(:key, user: user) }
- let(:email) { create(:email, user: user) }
+ let(:email) { create(:email, user: user) }
describe 'GET /keys/:uid' do
- before { admin }
-
context 'when unauthenticated' do
it 'returns authentication error' do
get api("/keys/#{key.id}")
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index 0c6b55c1630..5a4257d1009 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe API::Labels do
let(:user) { create(:user) }
- let(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) }
+ let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
let!(:label1) { create(:label, title: 'label1', project: project) }
let!(:priority_label) { create(:label, title: 'bug', project: project, priority: 3) }
@@ -339,7 +339,9 @@ describe API::Labels do
end
context "when user is already subscribed to label" do
- before { label1.subscribe(user, project) }
+ before do
+ label1.subscribe(user, project)
+ end
it "returns 304" do
post api("/projects/#{project.id}/labels/#{label1.id}/subscribe", user)
@@ -358,7 +360,9 @@ describe API::Labels do
end
describe "POST /projects/:id/labels/:label_id/unsubscribe" do
- before { label1.subscribe(user, project) }
+ before do
+ label1.subscribe(user, project)
+ end
context "when label_id is a label title" do
it "unsubscribes from the label" do
@@ -381,7 +385,9 @@ describe API::Labels do
end
context "when user is already unsubscribed from label" do
- before { label1.unsubscribe(user, project) }
+ before do
+ label1.unsubscribe(user, project)
+ end
it "returns 304" do
post api("/projects/#{project.id}/labels/#{label1.id}/unsubscribe", user)
diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb
index e095053fa03..06aca698c91 100644
--- a/spec/requests/api/members_spec.rb
+++ b/spec/requests/api/members_spec.rb
@@ -7,7 +7,7 @@ describe API::Members do
let(:stranger) { create(:user) }
let(:project) do
- create(:empty_project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
+ create(:project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
project.team << [developer, :developer]
project.team << [master, :master]
project.request_access(access_requester)
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index c4bff1647b5..0db645863fb 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -16,26 +16,136 @@ describe API::MergeRequests do
let!(:label) do
create(:label, title: 'label', color: '#FFAABB', project: project)
end
+ let!(:label2) { create(:label, title: 'a-test', color: '#FFFFFF', project: project) }
let!(:label_link) { create(:label_link, label: label, target: merge_request) }
+ let!(:label_link2) { create(:label_link, label: label2, target: merge_request) }
+ let!(:downvote) { create(:award_emoji, :downvote, awardable: merge_request) }
+ let!(:upvote) { create(:award_emoji, :upvote, awardable: merge_request) }
before 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_gitlab_http_status(401)
+ end
+ end
+
+ context 'when authenticated' do
+ let!(:project2) { create(: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_gitlab_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(: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_gitlab_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_gitlab_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_gitlab_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_gitlab_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_gitlab_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_gitlab_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
get api("/projects/#{project.id}/merge_requests")
- expect(response).to have_http_status(401)
+ expect(response).to have_gitlab_http_status(401)
end
end
context "when authenticated" do
+ it 'avoids N+1 queries' do
+ control_count = ActiveRecord::QueryRecorder.new do
+ get api("/projects/#{project.id}/merge_requests", user)
+ end.count
+
+ create(:merge_request, state: 'closed', milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: "Test", created_at: base_time)
+
+ expect do
+ get api("/projects/#{project.id}/merge_requests", user)
+ end.not_to exceed_query_limit(control_count)
+ end
+
it "returns an array of all merge_requests" do
get api("/projects/#{project.id}/merge_requests", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.length).to eq(3)
@@ -44,16 +154,35 @@ describe API::MergeRequests do
expect(json_response.last['sha']).to eq(merge_request.diff_head_sha)
expect(json_response.last['merge_commit_sha']).to be_nil
expect(json_response.last['merge_commit_sha']).to eq(merge_request.merge_commit_sha)
+ expect(json_response.last['downvotes']).to eq(1)
+ expect(json_response.last['upvotes']).to eq(1)
+ expect(json_response.last['labels']).to eq([label2.title, label.title])
expect(json_response.first['title']).to eq(merge_request_merged.title)
expect(json_response.first['sha']).to eq(merge_request_merged.diff_head_sha)
expect(json_response.first['merge_commit_sha']).not_to be_nil
expect(json_response.first['merge_commit_sha']).to eq(merge_request_merged.merge_commit_sha)
end
+ it "returns an array of all merge_requests using simple mode" do
+ get api("/projects/#{project.id}/merge_requests?view=simple", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response.last.keys).to match_array(%w(id iid title web_url created_at description project_id state updated_at))
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(3)
+ expect(json_response.last['iid']).to eq(merge_request.iid)
+ expect(json_response.last['title']).to eq(merge_request.title)
+ expect(json_response.last).to have_key('web_url')
+ expect(json_response.first['iid']).to eq(merge_request_merged.iid)
+ expect(json_response.first['title']).to eq(merge_request_merged.title)
+ expect(json_response.first).to have_key('web_url')
+ end
+
it "returns an array of all merge_requests" do
get api("/projects/#{project.id}/merge_requests?state", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.length).to eq(3)
@@ -63,7 +192,7 @@ describe API::MergeRequests do
it "returns an array of open merge_requests" do
get api("/projects/#{project.id}/merge_requests?state=opened", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
@@ -73,7 +202,7 @@ describe API::MergeRequests do
it "returns an array of closed merge_requests" do
get api("/projects/#{project.id}/merge_requests?state=closed", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
@@ -83,7 +212,7 @@ describe API::MergeRequests do
it "returns an array of merged merge_requests" do
get api("/projects/#{project.id}/merge_requests?state=merged", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
@@ -93,7 +222,7 @@ describe API::MergeRequests do
it 'returns merge_request by "iids" array' do
get api("/projects/#{project.id}/merge_requests", user), iids: [merge_request.iid, merge_request_closed.iid]
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(2)
expect(json_response.first['title']).to eq merge_request_closed.title
@@ -103,14 +232,14 @@ describe API::MergeRequests do
it 'matches V4 response schema' do
get api("/projects/#{project.id}/merge_requests", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('public_api/v4/merge_requests')
end
it 'returns an empty array if no issue matches milestone' do
get api("/projects/#{project.id}/merge_requests", user), milestone: '1.0.0'
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(0)
end
@@ -118,7 +247,7 @@ describe API::MergeRequests do
it 'returns an empty array if milestone does not exist' do
get api("/projects/#{project.id}/merge_requests", user), milestone: 'foo'
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(0)
end
@@ -133,7 +262,7 @@ describe API::MergeRequests do
it 'returns an array of merge requests matching state in milestone' do
get api("/projects/#{project.id}/merge_requests", user), milestone: '0.9', state: 'closed'
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_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_request_closed.id)
@@ -142,16 +271,16 @@ describe API::MergeRequests do
it 'returns an array of labeled merge requests' do
get api("/projects/#{project.id}/merge_requests?labels=#{label.title}", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
- expect(json_response.first['labels']).to eq([label.title])
+ expect(json_response.first['labels']).to eq([label2.title, label.title])
end
it 'returns an array of labeled merge requests where all labels match' do
get api("/projects/#{project.id}/merge_requests?labels=#{label.title},foo,bar", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(0)
end
@@ -159,11 +288,31 @@ describe API::MergeRequests do
it 'returns an empty array if no merge request matches labels' do
get api("/projects/#{project.id}/merge_requests?labels=foo,bar", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(0)
end
+ it 'returns an array of labeled merge requests that are merged for a milestone' do
+ bug_label = create(:label, title: 'bug', color: '#FFAABB', project: project)
+
+ mr1 = create(:merge_request, state: "merged", source_project: project, target_project: project, milestone: milestone)
+ mr2 = create(:merge_request, state: "merged", source_project: project, target_project: project, milestone: milestone1)
+ mr3 = create(:merge_request, state: "closed", source_project: project, target_project: project, milestone: milestone1)
+ _mr = create(:merge_request, state: "merged", source_project: project, target_project: project, milestone: milestone1)
+
+ create(:label_link, label: bug_label, target: mr1)
+ create(:label_link, label: bug_label, target: mr2)
+ create(:label_link, label: bug_label, target: mr3)
+
+ get api("/projects/#{project.id}/merge_requests?labels=#{bug_label.title}&milestone=#{milestone1.title}&state=merged", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(mr2.id)
+ end
+
context "with ordering" do
before do
@mr_later = mr_with_later_created_and_updated_at_time
@@ -173,44 +322,44 @@ describe API::MergeRequests do
it "returns an array of merge_requests in ascending order" do
get api("/projects/#{project.id}/merge_requests?sort=asc", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.length).to eq(3)
- response_dates = json_response.map{ |merge_request| merge_request['created_at'] }
+ response_dates = json_response.map { |merge_request| merge_request['created_at'] }
expect(response_dates).to eq(response_dates.sort)
end
it "returns an array of merge_requests in descending order" do
get api("/projects/#{project.id}/merge_requests?sort=desc", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.length).to eq(3)
- response_dates = json_response.map{ |merge_request| merge_request['created_at'] }
+ response_dates = json_response.map { |merge_request| merge_request['created_at'] }
expect(response_dates).to eq(response_dates.sort.reverse)
end
it "returns an array of merge_requests ordered by updated_at" do
get api("/projects/#{project.id}/merge_requests?order_by=updated_at", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.length).to eq(3)
- response_dates = json_response.map{ |merge_request| merge_request['updated_at'] }
+ response_dates = json_response.map { |merge_request| merge_request['updated_at'] }
expect(response_dates).to eq(response_dates.sort.reverse)
end
it "returns an array of merge_requests ordered by created_at" do
get api("/projects/#{project.id}/merge_requests?order_by=created_at&sort=asc", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.length).to eq(3)
- response_dates = json_response.map{ |merge_request| merge_request['created_at'] }
+ response_dates = json_response.map { |merge_request| merge_request['created_at'] }
expect(response_dates).to eq(response_dates.sort)
end
end
@@ -221,7 +370,7 @@ describe API::MergeRequests do
it 'exposes known attributes' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response['id']).to eq(merge_request.id)
expect(json_response['iid']).to eq(merge_request.iid)
expect(json_response['project_id']).to eq(merge_request.project.id)
@@ -236,8 +385,8 @@ describe API::MergeRequests do
expect(json_response['author']).to be_a Hash
expect(json_response['target_branch']).to eq(merge_request.target_branch)
expect(json_response['source_branch']).to eq(merge_request.source_branch)
- expect(json_response['upvotes']).to eq(0)
- expect(json_response['downvotes']).to eq(0)
+ expect(json_response['upvotes']).to eq(1)
+ expect(json_response['downvotes']).to eq(1)
expect(json_response['source_project_id']).to eq(merge_request.source_project.id)
expect(json_response['target_project_id']).to eq(merge_request.target_project.id)
expect(json_response['work_in_progress']).to be_falsy
@@ -249,7 +398,7 @@ describe API::MergeRequests do
it "returns merge_request" do
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response['title']).to eq(merge_request.title)
expect(json_response['iid']).to eq(merge_request.iid)
expect(json_response['work_in_progress']).to eq(false)
@@ -260,13 +409,13 @@ describe API::MergeRequests do
it "returns a 404 error if merge_request_iid not found" do
get api("/projects/#{project.id}/merge_requests/999", user)
- expect(response).to have_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
end
it "returns a 404 error if merge_request `id` is used instead of iid" do
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user)
- expect(response).to have_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
end
context 'Work in Progress' do
@@ -274,7 +423,7 @@ describe API::MergeRequests do
it "returns merge_request" do
get api("/projects/#{project.id}/merge_requests/#{merge_request_wip.iid}", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response['work_in_progress']).to eq(true)
end
end
@@ -285,7 +434,7 @@ describe API::MergeRequests do
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/commits", user)
commit = merge_request.commits.first
- expect(response.status).to eq 200
+ expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(merge_request.commits.size)
@@ -295,13 +444,13 @@ describe API::MergeRequests do
it 'returns a 404 when merge_request_iid not found' do
get api("/projects/#{project.id}/merge_requests/999/commits", user)
- expect(response).to have_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
end
it 'returns a 404 when merge_request id is used instead of iid' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/commits", user)
- expect(response).to have_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
end
end
@@ -309,19 +458,19 @@ describe API::MergeRequests do
it 'returns the change information of the merge_request' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/changes", user)
- expect(response.status).to eq 200
+ expect(response).to have_gitlab_http_status(200)
expect(json_response['changes'].size).to eq(merge_request.diffs.size)
end
it 'returns a 404 when merge_request_iid not found' do
get api("/projects/#{project.id}/merge_requests/999/changes", user)
- expect(response).to have_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
end
it 'returns a 404 when merge_request id is used instead of iid' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/changes", user)
- expect(response).to have_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
end
end
@@ -334,38 +483,37 @@ describe API::MergeRequests do
target_branch: 'master',
author: user,
labels: 'label, label2',
- milestone_id: milestone.id,
- remove_source_branch: true
+ milestone_id: milestone.id
- expect(response).to have_http_status(201)
+ expect(response).to have_gitlab_http_status(201)
expect(json_response['title']).to eq('Test merge_request')
expect(json_response['labels']).to eq(%w(label label2))
expect(json_response['milestone']['id']).to eq(milestone.id)
- expect(json_response['force_remove_source_branch']).to be_truthy
+ expect(json_response['force_remove_source_branch']).to be_falsy
end
it "returns 422 when source_branch equals target_branch" do
post api("/projects/#{project.id}/merge_requests", user),
title: "Test merge_request", source_branch: "master", target_branch: "master", author: user
- expect(response).to have_http_status(422)
+ expect(response).to have_gitlab_http_status(422)
end
it "returns 400 when source_branch is missing" do
post api("/projects/#{project.id}/merge_requests", user),
title: "Test merge_request", target_branch: "master", author: user
- expect(response).to have_http_status(400)
+ expect(response).to have_gitlab_http_status(400)
end
it "returns 400 when target_branch is missing" do
post api("/projects/#{project.id}/merge_requests", user),
title: "Test merge_request", source_branch: "markdown", author: user
- expect(response).to have_http_status(400)
+ expect(response).to have_gitlab_http_status(400)
end
it "returns 400 when title is missing" do
post api("/projects/#{project.id}/merge_requests", user),
target_branch: 'master', source_branch: 'markdown'
- expect(response).to have_http_status(400)
+ expect(response).to have_gitlab_http_status(400)
end
it 'allows special label names' do
@@ -375,7 +523,7 @@ describe API::MergeRequests do
target_branch: 'master',
author: user,
labels: 'label, label?, label&foo, ?, &'
- expect(response.status).to eq(201)
+ expect(response).to have_gitlab_http_status(201)
expect(json_response['labels']).to include 'label'
expect(json_response['labels']).to include 'label?'
expect(json_response['labels']).to include 'label&foo'
@@ -401,25 +549,48 @@ describe API::MergeRequests do
target_branch: 'master',
author: user
end.to change { MergeRequest.count }.by(0)
- expect(response).to have_http_status(409)
+ expect(response).to have_gitlab_http_status(409)
+ end
+ end
+
+ context 'accepts remove_source_branch parameter' do
+ let(:params) do
+ { title: 'Test merge_request',
+ source_branch: 'markdown',
+ target_branch: 'master',
+ author: user }
+ end
+
+ it 'sets force_remove_source_branch to false' do
+ post api("/projects/#{project.id}/merge_requests", user), params.merge(remove_source_branch: false)
+
+ expect(json_response['force_remove_source_branch']).to be_falsy
+ end
+
+ it 'sets force_remove_source_branch to true' do
+ post api("/projects/#{project.id}/merge_requests", user), params.merge(remove_source_branch: true)
+
+ expect(json_response['force_remove_source_branch']).to be_truthy
end
end
end
context 'forked projects' do
let!(:user2) { create(:user) }
- let!(:fork_project) { create(:empty_project, forked_from_project: project, namespace: user2.namespace, creator_id: user2.id) }
- let!(:unrelated_project) { create(:empty_project, namespace: create(:user).namespace, creator_id: user2.id) }
+ let!(:fork_project) { create(:project, forked_from_project: project, namespace: user2.namespace, creator_id: user2.id) }
+ let!(:unrelated_project) { create(:project, namespace: create(:user).namespace, creator_id: user2.id) }
+
+ before do
+ fork_project.add_reporter(user2)
- before :each do |each|
- fork_project.team << [user2, :reporter]
+ allow_any_instance_of(MergeRequest).to receive(:write_ref)
end
it "returns merge_request" do
post api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', source_branch: "feature_conflict", target_branch: "master",
author: user2, target_project_id: project.id, description: 'Test description for Test merge_request'
- expect(response).to have_http_status(201)
+ expect(response).to have_gitlab_http_status(201)
expect(json_response['title']).to eq('Test merge_request')
expect(json_response['description']).to eq('Test description for Test merge_request')
end
@@ -430,26 +601,39 @@ describe API::MergeRequests do
expect(fork_project.forked_from_project).to eq(project)
post api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', source_branch: "master", target_branch: "master", author: user2, target_project_id: project.id
- expect(response).to have_http_status(201)
+ expect(response).to have_gitlab_http_status(201)
expect(json_response['title']).to eq('Test merge_request')
end
+ it 'returns 422 when target project has disabled merge requests' do
+ project.project_feature.update(merge_requests_access_level: 0)
+
+ post api("/projects/#{fork_project.id}/merge_requests", user2),
+ title: 'Test',
+ target_branch: 'master',
+ source_branch: 'markdown',
+ author: user2,
+ target_project_id: project.id
+
+ expect(response).to have_gitlab_http_status(422)
+ end
+
it "returns 400 when source_branch is missing" do
post api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id
- expect(response).to have_http_status(400)
+ expect(response).to have_gitlab_http_status(400)
end
it "returns 400 when target_branch is missing" do
post api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id
- expect(response).to have_http_status(400)
+ expect(response).to have_gitlab_http_status(400)
end
it "returns 400 when title is missing" do
post api("/projects/#{fork_project.id}/merge_requests", user2),
target_branch: 'master', source_branch: 'markdown', author: user2, target_project_id: project.id
- expect(response).to have_http_status(400)
+ expect(response).to have_gitlab_http_status(400)
end
context 'when target_branch is specified' do
@@ -460,7 +644,7 @@ describe API::MergeRequests do
source_branch: 'markdown',
author: user,
target_project_id: fork_project.id
- expect(response).to have_http_status(422)
+ expect(response).to have_gitlab_http_status(422)
end
it 'returns 422 if targeting a different fork' do
@@ -470,14 +654,14 @@ describe API::MergeRequests do
source_branch: 'markdown',
author: user2,
target_project_id: unrelated_project.id
- expect(response).to have_http_status(422)
+ expect(response).to have_gitlab_http_status(422)
end
end
it "returns 201 when target_branch is specified and for the same project" do
post api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', target_branch: 'master', source_branch: 'markdown', author: user2, target_project_id: fork_project.id
- expect(response).to have_http_status(201)
+ expect(response).to have_gitlab_http_status(201)
end
end
end
@@ -492,7 +676,7 @@ describe API::MergeRequests do
it "denies the deletion of the merge request" do
delete api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", developer)
- expect(response).to have_http_status(403)
+ expect(response).to have_gitlab_http_status(403)
end
end
@@ -500,19 +684,19 @@ describe API::MergeRequests do
it "destroys the merge request owners can destroy" do
delete api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user)
- expect(response).to have_http_status(204)
+ expect(response).to have_gitlab_http_status(204)
end
it "returns 404 for an invalid merge request IID" do
delete api("/projects/#{project.id}/merge_requests/12345", user)
- expect(response).to have_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
end
it "returns 404 if the merge request id is used instead of iid" do
delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user)
- expect(response).to have_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
end
end
end
@@ -523,30 +707,30 @@ describe API::MergeRequests do
it "returns merge_request in case of success" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
end
it "returns 406 if branch can't be merged" do
- allow_any_instance_of(MergeRequest).
- to receive(:can_be_merged?).and_return(false)
+ allow_any_instance_of(MergeRequest)
+ .to receive(:can_be_merged?).and_return(false)
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user)
- expect(response).to have_http_status(406)
+ expect(response).to have_gitlab_http_status(406)
expect(json_response['message']).to eq('Branch cannot be merged')
end
it "returns 405 if merge_request is not open" do
merge_request.close
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user)
- expect(response).to have_http_status(405)
+ expect(response).to have_gitlab_http_status(405)
expect(json_response['message']).to eq('405 Method Not Allowed')
end
it "returns 405 if merge_request is a work in progress" do
merge_request.update_attribute(:title, "WIP: #{merge_request.title}")
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user)
- expect(response).to have_http_status(405)
+ expect(response).to have_gitlab_http_status(405)
expect(json_response['message']).to eq('405 Method Not Allowed')
end
@@ -555,7 +739,7 @@ describe API::MergeRequests do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user)
- expect(response).to have_http_status(405)
+ expect(response).to have_gitlab_http_status(405)
expect(json_response['message']).to eq('405 Method Not Allowed')
end
@@ -563,21 +747,21 @@ describe API::MergeRequests do
user2 = create(:user)
project.team << [user2, :reporter]
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user2)
- expect(response).to have_http_status(401)
+ expect(response).to have_gitlab_http_status(401)
expect(json_response['message']).to eq('401 Unauthorized')
end
it "returns 409 if the SHA parameter doesn't match" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user), sha: merge_request.diff_head_sha.reverse
- expect(response).to have_http_status(409)
+ expect(response).to have_gitlab_http_status(409)
expect(json_response['message']).to start_with('SHA does not match HEAD of source branch')
end
it "succeeds if the SHA parameter matches" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user), sha: merge_request.diff_head_sha
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
end
it "enables merge when pipeline succeeds if the pipeline is active" do
@@ -586,7 +770,7 @@ describe API::MergeRequests do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user), merge_when_pipeline_succeeds: true
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response['title']).to eq('Test')
expect(json_response['merge_when_pipeline_succeeds']).to eq(true)
end
@@ -598,7 +782,7 @@ describe API::MergeRequests do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user), merge_when_pipeline_succeeds: true
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response['title']).to eq('Test')
expect(json_response['merge_when_pipeline_succeeds']).to eq(true)
end
@@ -606,13 +790,13 @@ describe API::MergeRequests do
it "returns 404 for an invalid merge request IID" do
put api("/projects/#{project.id}/merge_requests/12345/merge", user)
- expect(response).to have_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
end
it "returns 404 if the merge request id is used instead of iid" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
- expect(response).to have_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
end
end
@@ -621,39 +805,39 @@ describe API::MergeRequests do
it "returns merge_request" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), state_event: "close"
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response['state']).to eq('closed')
end
end
it "updates title and returns merge_request" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), title: "New title"
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response['title']).to eq('New title')
end
it "updates description and returns merge_request" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), description: "New description"
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response['description']).to eq('New description')
end
it "updates milestone_id and returns merge_request" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), milestone_id: milestone.id
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response['milestone']['id']).to eq(milestone.id)
end
it "returns merge_request with renamed target_branch" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), target_branch: "wiki"
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response['target_branch']).to eq('wiki')
end
it "returns merge_request that removes the source branch" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), remove_source_branch: true
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response['force_remove_source_branch']).to be_truthy
end
@@ -674,7 +858,7 @@ describe API::MergeRequests do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), state_event: 'close', title: nil
merge_request.reload
- expect(response).to have_http_status(400)
+ expect(response).to have_gitlab_http_status(400)
expect(merge_request.state).to eq('opened')
end
@@ -682,20 +866,20 @@ describe API::MergeRequests do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), state_event: 'close', target_branch: nil
merge_request.reload
- expect(response).to have_http_status(400)
+ expect(response).to have_gitlab_http_status(400)
expect(merge_request.state).to eq('opened')
end
it "returns 404 for an invalid merge request IID" do
put api("/projects/#{project.id}/merge_requests/12345", user), state_event: "close"
- expect(response).to have_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
end
it "returns 404 if the merge request id is used instead of iid" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), state_event: "close"
- expect(response).to have_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
end
end
@@ -708,7 +892,7 @@ describe API::MergeRequests do
get api("/projects/#{project.id}/merge_requests/#{mr.iid}/closes_issues", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
@@ -718,49 +902,55 @@ describe API::MergeRequests do
it 'returns an empty array when there are no issues to be closed' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/closes_issues", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.length).to eq(0)
end
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)}")
+ jira_project = create(:jira_project, :public, :repository, name: 'JIR_EXT1')
+ 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 have_gitlab_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
- project = create(:empty_project, :private)
+ project = create(:project, :private)
merge_request = create(:merge_request, :simple, source_project: project)
guest = create(:user)
project.team << [guest, :guest]
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/closes_issues", guest)
- expect(response).to have_http_status(403)
+ expect(response).to have_gitlab_http_status(403)
end
it "returns 404 for an invalid merge request IID" do
get api("/projects/#{project.id}/merge_requests/12345/closes_issues", user)
- expect(response).to have_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
end
it "returns 404 if the merge request id is used instead of iid" do
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/closes_issues", user)
- expect(response).to have_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
end
end
@@ -768,26 +958,26 @@ describe API::MergeRequests do
it 'subscribes to a merge request' do
post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/subscribe", admin)
- expect(response).to have_http_status(201)
+ expect(response).to have_gitlab_http_status(201)
expect(json_response['subscribed']).to eq(true)
end
it 'returns 304 if already subscribed' do
post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/subscribe", user)
- expect(response).to have_http_status(304)
+ expect(response).to have_gitlab_http_status(304)
end
it 'returns 404 if the merge request is not found' do
post api("/projects/#{project.id}/merge_requests/123/subscribe", user)
- expect(response).to have_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
end
it 'returns 404 if the merge request id is used instead of iid' do
post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscribe", user)
- expect(response).to have_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
end
it 'returns 403 if user has no access to read code' do
@@ -796,7 +986,7 @@ describe API::MergeRequests do
post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/subscribe", guest)
- expect(response).to have_http_status(403)
+ expect(response).to have_gitlab_http_status(403)
end
end
@@ -804,26 +994,26 @@ describe API::MergeRequests do
it 'unsubscribes from a merge request' do
post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/unsubscribe", user)
- expect(response).to have_http_status(201)
+ expect(response).to have_gitlab_http_status(201)
expect(json_response['subscribed']).to eq(false)
end
it 'returns 304 if not subscribed' do
post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/unsubscribe", admin)
- expect(response).to have_http_status(304)
+ expect(response).to have_gitlab_http_status(304)
end
it 'returns 404 if the merge request is not found' do
post api("/projects/#{project.id}/merge_requests/123/unsubscribe", user)
- expect(response).to have_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
end
it 'returns 404 if the merge request id is used instead of iid' do
post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/unsubscribe", user)
- expect(response).to have_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
end
it 'returns 403 if user has no access to read code' do
@@ -832,7 +1022,7 @@ describe API::MergeRequests do
post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/unsubscribe", guest)
- expect(response).to have_http_status(403)
+ expect(response).to have_gitlab_http_status(403)
end
end
diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb
deleted file mode 100644
index dd74351a2b1..00000000000
--- a/spec/requests/api/milestones_spec.rb
+++ /dev/null
@@ -1,351 +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') }
-
- before { project.team << [user, :developer] }
-
- 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 '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, position: 2) }
- let(:confidential_issue) { create(:issue, confidential: true, project: public_project, position: 1) }
-
- 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 position asc' 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.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, position: 2) }
- let(:another_merge_request) { create(:merge_request, :simple, source_project: project, position: 1) }
-
- 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 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
-
- 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/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb
index 3bf16a3ae27..26cf653ca8e 100644
--- a/spec/requests/api/namespaces_spec.rb
+++ b/spec/requests/api/namespaces_spec.rb
@@ -15,6 +15,20 @@ describe API::Namespaces do
end
context "when authenticated as admin" do
+ it "returns correct attributes" do
+ get api("/namespaces", admin)
+
+ group_kind_json_response = json_response.find { |resource| resource['kind'] == 'group' }
+ user_kind_json_response = json_response.find { |resource| resource['kind'] == 'user' }
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(group_kind_json_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path',
+ 'parent_id', 'members_count_with_descendants')
+
+ expect(user_kind_json_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path', 'parent_id')
+ end
+
it "admin: returns an array of all namespaces" do
get api("/namespaces", admin)
@@ -37,6 +51,27 @@ describe API::Namespaces do
end
context "when authenticated as a regular user" do
+ it "returns correct attributes when user can admin group" do
+ group1.add_owner(user)
+
+ get api("/namespaces", user)
+
+ owned_group_response = json_response.find { |resource| resource['id'] == group1.id }
+
+ expect(owned_group_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path',
+ 'parent_id', 'members_count_with_descendants')
+ end
+
+ it "returns correct attributes when user cannot admin group" do
+ group1.add_guest(user)
+
+ get api("/namespaces", user)
+
+ guest_group_response = json_response.find { |resource| resource['id'] == group1.id }
+
+ expect(guest_group_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path', 'parent_id')
+ end
+
it "user: returns an array of namespaces" do
get api("/namespaces", user)
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index 6afcd237c3c..75e5062a99c 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe API::Notes do
let(:user) { create(:user) }
- let!(:project) { create(:empty_project, :public, namespace: user.namespace) }
+ let!(:project) { create(:project, :public, namespace: user.namespace) }
let!(:issue) { create(:issue, project: project, author: user) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) }
let!(:snippet) { create(:project_snippet, project: project, author: user) }
@@ -13,12 +13,12 @@ describe API::Notes do
# For testing the cross-reference of a private issue in a public issue
let(:private_user) { create(:user) }
let(:private_project) do
- create(:empty_project, namespace: private_user.namespace).
- tap { |p| p.team << [private_user, :master] }
+ create(:project, namespace: private_user.namespace)
+ .tap { |p| p.team << [private_user, :master] }
end
let(:private_issue) { create(:issue, project: private_project) }
- let(:ext_proj) { create(:empty_project, :public) }
+ let(:ext_proj) { create(:project, :public) }
let(:ext_issue) { create(:issue, project: ext_proj) }
let!(:cross_reference_note) do
@@ -28,7 +28,9 @@ describe API::Notes do
system: true
end
- before { project.team << [user, :reporter] }
+ before do
+ project.team << [user, :reporter]
+ end
describe "GET /projects/:id/noteable/:noteable_id/notes" do
context "when noteable is an Issue" do
@@ -58,7 +60,9 @@ describe API::Notes do
end
context "and issue is confidential" do
- before { ext_issue.update_attributes(confidential: true) }
+ before do
+ ext_issue.update_attributes(confidential: true)
+ end
it "returns 404" do
get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes", user)
@@ -150,7 +154,9 @@ describe API::Notes do
end
context "when issue is confidential" do
- before { issue.update_attributes(confidential: true) }
+ before do
+ issue.update_attributes(confidential: true)
+ end
it "returns 404" do
get api("/projects/#{project.id}/issues/#{issue.iid}/notes/#{issue_note.id}", private_user)
@@ -266,7 +272,7 @@ describe API::Notes do
context 'when user does not have access to read the noteable' do
it 'responds with 404' do
- project = create(:empty_project, :private) { |p| p.add_guest(user) }
+ project = create(:project, :private) { |p| p.add_guest(user) }
issue = create(:issue, :confidential, project: project)
post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user),
@@ -277,7 +283,7 @@ describe API::Notes do
end
context 'when user does not have access to create noteable' do
- let(:private_issue) { create(:issue, project: create(:empty_project, :private)) }
+ let(:private_issue) { create(:issue, project: create(:project, :private)) }
##
# We are posting to project user has access to, but we use issue id
diff --git a/spec/requests/api/notification_settings_spec.rb b/spec/requests/api/notification_settings_spec.rb
index f619b7e6eaf..7968659a1ec 100644
--- a/spec/requests/api/notification_settings_spec.rb
+++ b/spec/requests/api/notification_settings_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe API::NotificationSettings do
let(:user) { create(:user) }
let!(:group) { create(:group) }
- let!(:project) { create(:empty_project, :public, creator_id: user.id, namespace: group) }
+ let!(:project) { create(:project, :public, creator_id: user.id, namespace: group) }
describe "GET /notification_settings" do
it "returns global notification settings for the current user" do
@@ -72,8 +72,8 @@ describe API::NotificationSettings do
expect(response).to have_http_status(200)
expect(json_response['level']).to eq(user.reload.notification_settings_for(project).level)
- expect(json_response['events']['new_note']).to eq(true)
- expect(json_response['events']['new_issue']).to eq(false)
+ expect(json_response['events']['new_note']).to be_truthy
+ expect(json_response['events']['new_issue']).to be_falsey
end
end
diff --git a/spec/requests/api/oauth_tokens_spec.rb b/spec/requests/api/oauth_tokens_spec.rb
index 819df105960..0d56e1f732e 100644
--- a/spec/requests/api/oauth_tokens_spec.rb
+++ b/spec/requests/api/oauth_tokens_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API do
+describe 'OAuth tokens' do
context 'Resource Owner Password Credentials' do
def request_oauth_token(user)
post '/oauth/token', username: user.username, password: user.password, grant_type: 'password'
diff --git a/spec/requests/api/pipeline_schedules_spec.rb b/spec/requests/api/pipeline_schedules_spec.rb
new file mode 100644
index 00000000000..1fc0ec528b9
--- /dev/null
+++ b/spec/requests/api/pipeline_schedules_spec.rb
@@ -0,0 +1,299 @@
+require 'spec_helper'
+
+describe API::PipelineSchedules do
+ set(:developer) { create(:user) }
+ set(:user) { create(:user) }
+ set(:project) { create(:project, :repository) }
+
+ before do
+ project.add_developer(developer)
+ end
+
+ describe 'GET /projects/:id/pipeline_schedules' do
+ context 'authenticated user with valid permissions' do
+ let(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: developer) }
+
+ before do
+ pipeline_schedule.pipelines << build(:ci_pipeline, project: project)
+ end
+
+ it 'returns list of pipeline_schedules' do
+ get api("/projects/#{project.id}/pipeline_schedules", developer)
+
+ expect(response).to have_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(response).to match_response_schema('pipeline_schedules')
+ end
+
+ it 'avoids N + 1 queries' do
+ control_count = ActiveRecord::QueryRecorder.new do
+ get api("/projects/#{project.id}/pipeline_schedules", developer)
+ end.count
+
+ create_list(:ci_pipeline_schedule, 10, project: project)
+ .each do |pipeline_schedule|
+ create(:user).tap do |user|
+ project.add_developer(user)
+ pipeline_schedule.update_attributes(owner: user)
+ end
+ pipeline_schedule.pipelines << build(:ci_pipeline, project: project)
+ end
+
+ expect do
+ get api("/projects/#{project.id}/pipeline_schedules", developer)
+ end.not_to exceed_query_limit(control_count)
+ end
+
+ %w[active inactive].each do |target|
+ context "when scope is #{target}" do
+ before do
+ create(:ci_pipeline_schedule, project: project, active: active?(target))
+ end
+
+ it 'returns matched pipeline schedules' do
+ get api("/projects/#{project.id}/pipeline_schedules", developer), scope: target
+
+ expect(json_response.map { |r| r['active'] }).to all(eq(active?(target)))
+ end
+ end
+
+ def active?(str)
+ (str == 'active') ? true : false
+ end
+ end
+ end
+
+ context 'authenticated user with invalid permissions' do
+ it 'does not return pipeline_schedules list' do
+ get api("/projects/#{project.id}/pipeline_schedules", user)
+
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ context 'unauthenticated user' do
+ it 'does not return pipeline_schedules list' do
+ get api("/projects/#{project.id}/pipeline_schedules")
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
+
+ describe 'GET /projects/:id/pipeline_schedules/:pipeline_schedule_id' do
+ let(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: developer) }
+
+ before do
+ pipeline_schedule.pipelines << build(:ci_pipeline, project: project)
+ end
+
+ context 'authenticated user with valid permissions' do
+ it 'returns pipeline_schedule details' do
+ get api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", developer)
+
+ expect(response).to have_http_status(:ok)
+ expect(response).to match_response_schema('pipeline_schedule')
+ end
+
+ it 'responds with 404 Not Found if requesting non-existing pipeline_schedule' do
+ get api("/projects/#{project.id}/pipeline_schedules/-5", developer)
+
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ context 'authenticated user with invalid permissions' do
+ it 'does not return pipeline_schedules list' do
+ get api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", user)
+
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ context 'unauthenticated user' do
+ it 'does not return pipeline_schedules list' do
+ get api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}")
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
+
+ describe 'POST /projects/:id/pipeline_schedules' do
+ let(:params) { attributes_for(:ci_pipeline_schedule) }
+
+ context 'authenticated user with valid permissions' do
+ context 'with required parameters' do
+ it 'creates pipeline_schedule' do
+ expect do
+ post api("/projects/#{project.id}/pipeline_schedules", developer),
+ params
+ end.to change { project.pipeline_schedules.count }.by(1)
+
+ expect(response).to have_http_status(:created)
+ expect(response).to match_response_schema('pipeline_schedule')
+ expect(json_response['description']).to eq(params[:description])
+ expect(json_response['ref']).to eq(params[:ref])
+ expect(json_response['cron']).to eq(params[:cron])
+ expect(json_response['cron_timezone']).to eq(params[:cron_timezone])
+ expect(json_response['owner']['id']).to eq(developer.id)
+ end
+ end
+
+ context 'without required parameters' do
+ it 'does not create pipeline_schedule' do
+ post api("/projects/#{project.id}/pipeline_schedules", developer)
+
+ expect(response).to have_http_status(:bad_request)
+ end
+ end
+
+ context 'when cron has validation error' do
+ it 'does not create pipeline_schedule' do
+ post api("/projects/#{project.id}/pipeline_schedules", developer),
+ params.merge('cron' => 'invalid-cron')
+
+ expect(response).to have_http_status(:bad_request)
+ expect(json_response['message']).to have_key('cron')
+ end
+ end
+ end
+
+ context 'authenticated user with invalid permissions' do
+ it 'does not create pipeline_schedule' do
+ post api("/projects/#{project.id}/pipeline_schedules", user), params
+
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ context 'unauthenticated user' do
+ it 'does not create pipeline_schedule' do
+ post api("/projects/#{project.id}/pipeline_schedules"), params
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
+
+ describe 'PUT /projects/:id/pipeline_schedules/:pipeline_schedule_id' do
+ let(:pipeline_schedule) do
+ create(:ci_pipeline_schedule, project: project, owner: developer)
+ end
+
+ context 'authenticated user with valid permissions' do
+ it 'updates cron' do
+ put api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", developer),
+ cron: '1 2 3 4 *'
+
+ expect(response).to have_http_status(:ok)
+ expect(response).to match_response_schema('pipeline_schedule')
+ expect(json_response['cron']).to eq('1 2 3 4 *')
+ end
+
+ context 'when cron has validation error' do
+ it 'does not update pipeline_schedule' do
+ put api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", developer),
+ cron: 'invalid-cron'
+
+ expect(response).to have_http_status(:bad_request)
+ expect(json_response['message']).to have_key('cron')
+ end
+ end
+ end
+
+ context 'authenticated user with invalid permissions' do
+ it 'does not update pipeline_schedule' do
+ put api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", user)
+
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ context 'unauthenticated user' do
+ it 'does not update pipeline_schedule' do
+ put api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}")
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
+
+ describe 'POST /projects/:id/pipeline_schedules/:pipeline_schedule_id/take_ownership' do
+ let(:pipeline_schedule) do
+ create(:ci_pipeline_schedule, project: project, owner: developer)
+ end
+
+ context 'authenticated user with valid permissions' do
+ it 'updates owner' do
+ post api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/take_ownership", developer)
+
+ expect(response).to have_http_status(:created)
+ expect(response).to match_response_schema('pipeline_schedule')
+ end
+ end
+
+ context 'authenticated user with invalid permissions' do
+ it 'does not update owner' do
+ post api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/take_ownership", user)
+
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ context 'unauthenticated user' do
+ it 'does not update owner' do
+ post api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/take_ownership")
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
+
+ describe 'DELETE /projects/:id/pipeline_schedules/:pipeline_schedule_id' do
+ let(:master) { create(:user) }
+
+ let!(:pipeline_schedule) do
+ create(:ci_pipeline_schedule, project: project, owner: developer)
+ end
+
+ before do
+ project.add_master(master)
+ end
+
+ context 'authenticated user with valid permissions' do
+ it 'deletes pipeline_schedule' do
+ expect do
+ delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", master)
+ end.to change { project.pipeline_schedules.count }.by(-1)
+
+ expect(response).to have_http_status(:accepted)
+ expect(response).to match_response_schema('pipeline_schedule')
+ end
+
+ it 'responds with 404 Not Found if requesting non-existing pipeline_schedule' do
+ delete api("/projects/#{project.id}/pipeline_schedules/-5", master)
+
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ context 'authenticated user with invalid permissions' do
+ let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: master) }
+
+ it 'does not delete pipeline_schedule' do
+ delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", developer)
+
+ expect(response).to have_http_status(:forbidden)
+ end
+ end
+
+ context 'unauthenticated user' do
+ it 'does not delete pipeline_schedule' do
+ delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}")
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb
index 762345cd41c..258085e503f 100644
--- a/spec/requests/api/pipelines_spec.rb
+++ b/spec/requests/api/pipelines_spec.rb
@@ -7,10 +7,12 @@ describe API::Pipelines do
let!(:pipeline) do
create(:ci_empty_pipeline, project: project, sha: project.commit.id,
- ref: project.default_branch)
+ ref: project.default_branch, user: user)
end
- before { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
describe 'GET /projects/:id/pipelines ' do
context 'authorized user' do
@@ -24,6 +26,251 @@ describe API::Pipelines do
expect(json_response.first['id']).to eq pipeline.id
expect(json_response.first.keys).to contain_exactly(*%w[id sha ref status])
end
+
+ context 'when parameter is passed' do
+ %w[running pending].each do |target|
+ context "when scope is #{target}" do
+ before do
+ create(:ci_pipeline, project: project, status: target)
+ end
+
+ it 'returns matched pipelines' do
+ get api("/projects/#{project.id}/pipelines", user), scope: target
+
+ expect(response).to have_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).not_to be_empty
+ json_response.each { |r| expect(r['status']).to eq(target) }
+ end
+ end
+ end
+
+ context 'when scope is finished' do
+ before do
+ create(:ci_pipeline, project: project, status: 'success')
+ create(:ci_pipeline, project: project, status: 'failed')
+ create(:ci_pipeline, project: project, status: 'canceled')
+ end
+
+ it 'returns matched pipelines' do
+ get api("/projects/#{project.id}/pipelines", user), scope: 'finished'
+
+ expect(response).to have_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).not_to be_empty
+ json_response.each { |r| expect(r['status']).to be_in(%w[success failed canceled]) }
+ end
+ end
+
+ context 'when scope is branches or tags' do
+ let!(:pipeline_branch) { create(:ci_pipeline, project: project) }
+ let!(:pipeline_tag) { create(:ci_pipeline, project: project, ref: 'v1.0.0', tag: true) }
+
+ context 'when scope is branches' do
+ it 'returns matched pipelines' do
+ get api("/projects/#{project.id}/pipelines", user), scope: 'branches'
+
+ expect(response).to have_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).not_to be_empty
+ expect(json_response.last['id']).to eq(pipeline_branch.id)
+ end
+ end
+
+ context 'when scope is tags' do
+ it 'returns matched pipelines' do
+ get api("/projects/#{project.id}/pipelines", user), scope: 'tags'
+
+ expect(response).to have_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).not_to be_empty
+ expect(json_response.last['id']).to eq(pipeline_tag.id)
+ end
+ end
+ end
+
+ context 'when scope is invalid' do
+ it 'returns bad_request' do
+ get api("/projects/#{project.id}/pipelines", user), scope: 'invalid-scope'
+
+ expect(response).to have_http_status(:bad_request)
+ end
+ end
+
+ HasStatus::AVAILABLE_STATUSES.each do |target|
+ context "when status is #{target}" do
+ before do
+ create(:ci_pipeline, project: project, status: target)
+ exception_status = HasStatus::AVAILABLE_STATUSES - [target]
+ create(:ci_pipeline, project: project, status: exception_status.sample)
+ end
+
+ it 'returns matched pipelines' do
+ get api("/projects/#{project.id}/pipelines", user), status: target
+
+ expect(response).to have_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).not_to be_empty
+ json_response.each { |r| expect(r['status']).to eq(target) }
+ end
+ end
+ end
+
+ context 'when status is invalid' do
+ it 'returns bad_request' do
+ get api("/projects/#{project.id}/pipelines", user), status: 'invalid-status'
+
+ expect(response).to have_http_status(:bad_request)
+ end
+ end
+
+ context 'when ref is specified' do
+ before do
+ create(:ci_pipeline, project: project)
+ end
+
+ context 'when ref exists' do
+ it 'returns matched pipelines' do
+ get api("/projects/#{project.id}/pipelines", user), ref: 'master'
+
+ expect(response).to have_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).not_to be_empty
+ json_response.each { |r| expect(r['ref']).to eq('master') }
+ end
+ end
+
+ context 'when ref does not exist' do
+ it 'returns empty' do
+ get api("/projects/#{project.id}/pipelines", user), ref: 'invalid-ref'
+
+ expect(response).to have_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_empty
+ end
+ end
+ end
+
+ context 'when name is specified' do
+ let!(:pipeline) { create(:ci_pipeline, project: project, user: user) }
+
+ context 'when name exists' do
+ it 'returns matched pipelines' do
+ get api("/projects/#{project.id}/pipelines", user), name: user.name
+
+ expect(response).to have_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response.first['id']).to eq(pipeline.id)
+ end
+ end
+
+ context 'when name does not exist' do
+ it 'returns empty' do
+ get api("/projects/#{project.id}/pipelines", user), name: 'invalid-name'
+
+ expect(response).to have_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_empty
+ end
+ end
+ end
+
+ context 'when username is specified' do
+ let!(:pipeline) { create(:ci_pipeline, project: project, user: user) }
+
+ context 'when username exists' do
+ it 'returns matched pipelines' do
+ get api("/projects/#{project.id}/pipelines", user), username: user.username
+
+ expect(response).to have_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response.first['id']).to eq(pipeline.id)
+ end
+ end
+
+ context 'when username does not exist' do
+ it 'returns empty' do
+ get api("/projects/#{project.id}/pipelines", user), username: 'invalid-username'
+
+ expect(response).to have_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_empty
+ end
+ end
+ end
+
+ context 'when yaml_errors is specified' do
+ let!(:pipeline1) { create(:ci_pipeline, project: project, yaml_errors: 'Syntax error') }
+ let!(:pipeline2) { create(:ci_pipeline, project: project) }
+
+ context 'when yaml_errors is true' do
+ it 'returns matched pipelines' do
+ get api("/projects/#{project.id}/pipelines", user), yaml_errors: true
+
+ expect(response).to have_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response.first['id']).to eq(pipeline1.id)
+ end
+ end
+
+ context 'when yaml_errors is false' do
+ it 'returns matched pipelines' do
+ get api("/projects/#{project.id}/pipelines", user), yaml_errors: false
+
+ expect(response).to have_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response.first['id']).to eq(pipeline2.id)
+ end
+ end
+
+ context 'when yaml_errors is invalid' do
+ it 'returns bad_request' do
+ get api("/projects/#{project.id}/pipelines", user), yaml_errors: 'invalid-yaml_errors'
+
+ expect(response).to have_http_status(:bad_request)
+ end
+ end
+ end
+
+ context 'when order_by and sort are specified' do
+ context 'when order_by user_id' do
+ before do
+ 3.times do
+ create(:ci_pipeline, project: project, user: create(:user))
+ end
+ end
+
+ context 'when sort parameter is valid' do
+ it 'sorts as user_id: :desc' do
+ get api("/projects/#{project.id}/pipelines", user), order_by: 'user_id', sort: 'desc'
+
+ expect(response).to have_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).not_to be_empty
+
+ pipeline_ids = Ci::Pipeline.all.order(user_id: :desc).pluck(:id)
+ expect(json_response.map { |r| r['id'] }).to eq(pipeline_ids)
+ end
+ end
+
+ context 'when sort parameter is invalid' do
+ it 'returns bad_request' do
+ get api("/projects/#{project.id}/pipelines", user), order_by: 'user_id', sort: 'invalid_sort'
+
+ expect(response).to have_http_status(:bad_request)
+ end
+ end
+ end
+
+ context 'when order_by is invalid' do
+ it 'returns bad_request' do
+ get api("/projects/#{project.id}/pipelines", user), order_by: 'lock_version', sort: 'asc'
+
+ expect(response).to have_http_status(:bad_request)
+ end
+ end
+ end
+ end
end
context 'unauthorized user' do
@@ -40,7 +287,9 @@ describe API::Pipelines do
describe 'POST /projects/:id/pipeline ' do
context 'authorized user' do
context 'with gitlab-ci.yml' do
- before { stub_ci_pipeline_to_return_yaml_file }
+ before do
+ stub_ci_pipeline_to_return_yaml_file
+ end
it 'creates and returns a new pipeline' do
expect do
@@ -174,7 +423,9 @@ describe API::Pipelines do
context 'user without proper access rights' do
let!(:reporter) { create(:user) }
- before { project.team << [reporter, :reporter] }
+ before do
+ project.team << [reporter, :reporter]
+ end
it 'rejects the action' do
post api("/projects/#{project.id}/pipelines/#{pipeline.id}/cancel", reporter)
diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb
index aee0e17a153..2829c243af3 100644
--- a/spec/requests/api/project_hooks_spec.rb
+++ b/spec/requests/api/project_hooks_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe API::ProjectHooks, 'ProjectHooks' do
let(:user) { create(:user) }
let(:user3) { create(:user) }
- let!(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) }
+ let!(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
let!(:hook) do
create(:project_hook,
:all_events_enabled,
@@ -60,7 +60,7 @@ describe API::ProjectHooks, 'ProjectHooks' do
expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events)
expect(json_response['tag_push_events']).to eq(hook.tag_push_events)
expect(json_response['note_events']).to eq(hook.note_events)
- expect(json_response['job_events']).to eq(hook.build_events)
+ expect(json_response['job_events']).to eq(hook.job_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)
@@ -148,7 +148,7 @@ describe API::ProjectHooks, 'ProjectHooks' do
expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events)
expect(json_response['tag_push_events']).to eq(hook.tag_push_events)
expect(json_response['note_events']).to eq(hook.note_events)
- expect(json_response['job_events']).to eq(hook.build_events)
+ expect(json_response['job_events']).to eq(hook.job_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)
@@ -205,7 +205,7 @@ describe API::ProjectHooks, 'ProjectHooks' do
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(:empty_project)
+ other_project = create(:project)
other_project.team << [test_user, :master]
delete api("/projects/#{other_project.id}/hooks/#{hook.id}", test_user)
diff --git a/spec/requests/api/project_milestones_spec.rb b/spec/requests/api/project_milestones_spec.rb
new file mode 100644
index 00000000000..72e1574b55f
--- /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(: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/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb
index 3ab1764f5c3..2b541f5719e 100644
--- a/spec/requests/api/project_snippets_spec.rb
+++ b/spec/requests/api/project_snippets_spec.rb
@@ -1,10 +1,30 @@
require 'rails_helper'
describe API::ProjectSnippets do
- let(:project) { create(:empty_project, :public) }
+ let(:project) { create(:project, :public) }
let(:user) { create(:user) }
let(:admin) { create(:admin) }
+ describe "GET /projects/:project_id/snippets/:id/user_agent_detail" do
+ let(:snippet) { create(:project_snippet, :public, project: project) }
+ let!(:user_agent_detail) { create(:user_agent_detail, subject: snippet) }
+
+ it 'exposes known attributes' do
+ get api("/projects/#{project.id}/snippets/#{snippet.id}/user_agent_detail", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['user_agent']).to eq(user_agent_detail.user_agent)
+ expect(json_response['ip_address']).to eq(user_agent_detail.ip_address)
+ expect(json_response['akismet_submitted']).to eq(user_agent_detail.submitted)
+ end
+
+ it "returns unautorized for non-admin users" do
+ get api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/user_agent_detail", user)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
describe 'GET /projects/:project_id/snippets/' do
let(:user) { create(:user) }
@@ -20,7 +40,7 @@ describe API::ProjectSnippets do
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(3)
- expect(json_response.map{ |snippet| snippet['id']} ).to include(public_snippet.id, internal_snippet.id, private_snippet.id)
+ expect(json_response.map { |snippet| snippet['id'] }).to include(public_snippet.id, internal_snippet.id, private_snippet.id)
expect(json_response.last).to have_key('web_url')
end
@@ -36,11 +56,34 @@ describe API::ProjectSnippets do
end
end
+ describe 'GET /projects/:project_id/snippets/:id' do
+ let(:user) { create(:user) }
+ let(:snippet) { create(:project_snippet, :public, project: project) }
+
+ it 'returns snippet json' do
+ get api("/projects/#{project.id}/snippets/#{snippet.id}", user)
+
+ expect(response).to have_http_status(200)
+
+ expect(json_response['title']).to eq(snippet.title)
+ expect(json_response['description']).to eq(snippet.description)
+ expect(json_response['file_name']).to eq(snippet.file_name)
+ end
+
+ it 'returns 404 for invalid snippet id' do
+ get api("/projects/#{project.id}/snippets/1234", user)
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Not found')
+ end
+ end
+
describe 'POST /projects/:project_id/snippets/' do
let(:params) do
{
title: 'Test Title',
file_name: 'test.rb',
+ description: 'test description',
code: 'puts "hello world"',
visibility: 'public'
}
@@ -52,6 +95,7 @@ describe API::ProjectSnippets do
expect(response).to have_http_status(201)
snippet = ProjectSnippet.find(json_response['id'])
expect(snippet.content).to eq(params[:code])
+ expect(snippet.description).to eq(params[:description])
expect(snippet.title).to eq(params[:title])
expect(snippet.file_name).to eq(params[:file_name])
expect(snippet.visibility_level).to eq(Snippet::PUBLIC)
@@ -78,23 +122,23 @@ describe API::ProjectSnippets do
context 'when the snippet is private' do
it 'creates the snippet' do
- expect { create_snippet(project, visibility: 'private') }.
- to change { Snippet.count }.by(1)
+ expect { create_snippet(project, visibility: 'private') }
+ .to change { Snippet.count }.by(1)
end
end
context 'when the snippet is public' do
it 'rejects the snippet' do
- expect { create_snippet(project, visibility: 'public') }.
- not_to change { Snippet.count }
+ expect { create_snippet(project, visibility: 'public') }
+ .not_to change { Snippet.count }
expect(response).to have_http_status(400)
expect(json_response['message']).to eq({ "error" => "Spam detected" })
end
it 'creates a spam log' do
- expect { create_snippet(project, visibility: 'public') }.
- to change { SpamLog.count }.by(1)
+ expect { create_snippet(project, visibility: 'public') }
+ .to change { SpamLog.count }.by(1)
end
end
end
@@ -106,12 +150,14 @@ describe API::ProjectSnippets do
it 'updates snippet' do
new_content = 'New content'
+ new_description = 'New description'
- put api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin), code: new_content
+ put api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin), code: new_content, description: new_description
expect(response).to have_http_status(200)
snippet.reload
expect(snippet.content).to eq(new_content)
+ expect(snippet.description).to eq(new_description)
end
it 'returns 404 for invalid snippet id' do
@@ -140,8 +186,8 @@ describe API::ProjectSnippets do
let(:visibility_level) { Snippet::PRIVATE }
it 'creates the snippet' do
- expect { update_snippet(title: 'Foo') }.
- to change { snippet.reload.title }.to('Foo')
+ expect { update_snippet(title: 'Foo') }
+ .to change { snippet.reload.title }.to('Foo')
end
end
@@ -149,13 +195,13 @@ describe API::ProjectSnippets do
let(:visibility_level) { Snippet::PUBLIC }
it 'rejects the snippet' do
- expect { update_snippet(title: 'Foo') }.
- not_to change { snippet.reload.title }
+ expect { update_snippet(title: 'Foo') }
+ .not_to change { snippet.reload.title }
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo') }.
- to change { SpamLog.count }.by(1)
+ expect { update_snippet(title: 'Foo') }
+ .to change { SpamLog.count }.by(1)
end
end
@@ -163,16 +209,16 @@ describe API::ProjectSnippets do
let(:visibility_level) { Snippet::PRIVATE }
it 'rejects the snippet' do
- expect { update_snippet(title: 'Foo', visibility: 'public') }.
- not_to change { snippet.reload.title }
+ expect { update_snippet(title: 'Foo', visibility: 'public') }
+ .not_to change { snippet.reload.title }
expect(response).to have_http_status(400)
expect(json_response['message']).to eq({ "error" => "Spam detected" })
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo', visibility: 'public') }.
- to change { SpamLog.count }.by(1)
+ expect { update_snippet(title: 'Foo', visibility: 'public') }
+ .to change { SpamLog.count }.by(1)
end
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index cc03d7a933b..a89a58ff713 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -8,11 +8,10 @@ describe API::Projects do
let(:user2) { create(:user) }
let(:user3) { create(:user) }
let(:admin) { create(:admin) }
- let(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) }
- let(:project2) { create(:empty_project, path: 'project2', creator_id: user.id, namespace: user.namespace) }
+ let(:project) { create(:project, namespace: user.namespace) }
+ let(:project2) { create(:project, path: 'project2', namespace: user.namespace) }
let(:snippet) { create(:project_snippet, :public, author: user, project: project, title: 'example') }
- let(:project_member) { create(:project_member, :master, user: user, project: project) }
- let(:project_member2) { create(:project_member, :developer, user: user3, project: project) }
+ let(:project_member) { create(:project_member, :developer, user: user3, project: project) }
let(:user4) { create(:user) }
let(:project3) do
create(:project,
@@ -27,14 +26,14 @@ describe API::Projects do
builds_enabled: false,
snippets_enabled: false)
end
- let(:project_member3) do
+ let(:project_member2) do
create(:project_member,
user: user4,
project: project3,
access_level: ProjectMember::MASTER)
end
let(:project4) do
- create(:empty_project,
+ create(:project,
name: 'third_project',
path: 'third_project',
creator_id: user4.id,
@@ -53,7 +52,25 @@ describe API::Projects do
end
end
- let!(:public_project) { create(:empty_project, :public, name: 'public_project') }
+ shared_examples_for 'projects response without N + 1 queries' do
+ it 'avoids N + 1 queries' do
+ control_count = ActiveRecord::QueryRecorder.new do
+ get api('/projects', current_user)
+ end.count
+
+ if defined?(additional_project)
+ additional_project
+ else
+ create(:project, :public)
+ end
+
+ expect do
+ get api('/projects', current_user)
+ end.not_to exceed_query_limit(control_count + 8)
+ end
+ end
+
+ let!(:public_project) { create(:project, :public, name: 'public_project') }
before do
project
project2
@@ -63,9 +80,13 @@ describe API::Projects do
context 'when unauthenticated' do
it_behaves_like 'projects response' do
- let(:filter) { {} }
+ let(:filter) { { search: project.name } }
+ let(:current_user) { user }
+ let(:projects) { [project] }
+ end
+
+ it_behaves_like 'projects response without N + 1 queries' do
let(:current_user) { nil }
- let(:projects) { [public_project] }
end
end
@@ -76,6 +97,21 @@ describe API::Projects do
let(:projects) { [public_project, project, project2, project3] }
end
+ it_behaves_like 'projects response without N + 1 queries' do
+ let(:current_user) { user }
+ end
+
+ context 'when some projects are in a group' do
+ before do
+ create(:project, :public, group: create(:group))
+ end
+
+ it_behaves_like 'projects response without N + 1 queries' do
+ let(:current_user) { user }
+ let(:additional_project) { create(:project, :public, group: create(:group)) }
+ end
+ end
+
it 'includes the project labels as the tag_list' do
get api('/projects', user)
@@ -123,9 +159,41 @@ 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)
+ expected_keys = %w(
+ id description default_branch tag_list
+ ssh_url_to_repo http_url_to_repo web_url
+ name name_with_namespace
+ path path_with_namespace
+ star_count forks_count
+ created_at last_activity_at
+ )
get api('/projects?simple=true', user)
@@ -207,10 +275,10 @@ describe API::Projects do
end
context 'and with starred=true' do
- let(:public_project) { create(:empty_project, :public) }
+ let(:public_project) { create(:project, :public) }
before do
- project_member2
+ project_member
user3.update_attributes(starred_projects: [project, project2, project3, public_project])
end
@@ -225,11 +293,11 @@ describe API::Projects do
end
context 'and with all query parameters' do
- let!(:project5) { create(:empty_project, :public, path: 'gitlab5', namespace: create(:namespace)) }
- let!(:project6) { create(:empty_project, :public, path: 'project6', namespace: user.namespace) }
- let!(:project7) { create(:empty_project, :public, path: 'gitlab7', namespace: user.namespace) }
- let!(:project8) { create(:empty_project, path: 'gitlab8', namespace: user.namespace) }
- let!(:project9) { create(:empty_project, :public, path: 'gitlab9') }
+ let!(:project5) { create(:project, :public, path: 'gitlab5', namespace: create(:namespace)) }
+ let!(:project6) { create(:project, :public, path: 'project6', namespace: user.namespace) }
+ let!(:project7) { create(:project, :public, path: 'gitlab7', namespace: user.namespace) }
+ let!(:project8) { create(:project, path: 'gitlab8', namespace: user.namespace) }
+ let!(:project9) { create(:project, :public, path: 'gitlab9') }
before do
user.update_attributes(starred_projects: [project5, project7, project8, project9])
@@ -289,15 +357,15 @@ describe API::Projects do
context 'maximum number of projects reached' do
it 'does not create new project and respond with 403' do
allow_any_instance_of(User).to receive(:projects_limit_left).and_return(0)
- expect { post api('/projects', user2), name: 'foo' }.
- to change {Project.count}.by(0)
+ expect { post api('/projects', user2), name: 'foo' }
+ .to change {Project.count}.by(0)
expect(response).to have_http_status(403)
end
end
it 'creates new project without path but with name and returns 201' do
- expect { post api('/projects', user), name: 'Foo Project' }.
- to change { Project.count }.by(1)
+ expect { post api('/projects', user), name: 'Foo Project' }
+ .to change { Project.count }.by(1)
expect(response).to have_http_status(201)
project = Project.first
@@ -307,8 +375,8 @@ describe API::Projects do
end
it 'creates new project without name but with path and returns 201' do
- expect { post api('/projects', user), path: 'foo_project' }.
- to change { Project.count }.by(1)
+ expect { post api('/projects', user), path: 'foo_project' }
+ .to change { Project.count }.by(1)
expect(response).to have_http_status(201)
project = Project.first
@@ -317,15 +385,15 @@ describe API::Projects do
expect(project.path).to eq('foo_project')
end
- it 'creates new project name and path and returns 201' do
- expect { post api('/projects', user), path: 'foo-Project', name: 'Foo Project' }.
- to change { Project.count }.by(1)
+ it 'creates new project with name and path and returns 201' do
+ expect { post api('/projects', user), path: 'path-project-Foo', name: 'Foo Project' }
+ .to change { Project.count }.by(1)
expect(response).to have_http_status(201)
project = Project.first
expect(project.name).to eq('Foo Project')
- expect(project.path).to eq('foo-Project')
+ expect(project.path).to eq('path-project-Foo')
end
it 'creates last project before reaching project limit' do
@@ -348,7 +416,8 @@ describe API::Projects do
wiki_enabled: false,
only_allow_merge_if_pipeline_succeeds: false,
request_access_enabled: true,
- only_allow_merge_if_all_discussions_are_resolved: false
+ only_allow_merge_if_all_discussions_are_resolved: false,
+ ci_config_path: 'a/custom/path'
})
post api('/projects', user), project
@@ -391,6 +460,23 @@ describe API::Projects do
expect(json_response['visibility']).to eq('private')
end
+ it 'sets tag list to a project' do
+ project = attributes_for(:project, tag_list: %w[tagFirst tagSecond])
+
+ post api('/projects', user), project
+
+ expect(json_response['tag_list']).to eq(%w[tagFirst tagSecond])
+ end
+
+ it 'uploads avatar for project a project' do
+ project = attributes_for(:project, avatar: fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif'))
+
+ post api('/projects', user), project
+
+ project_id = json_response['id']
+ expect(json_response['avatar_url']).to eq("http://localhost/uploads/-/system/project/avatar/#{project_id}/banana_sample.gif")
+ end
+
it 'sets a project as allowing merge even if build fails' do
project = attributes_for(:project, { only_allow_merge_if_pipeline_succeeds: false })
post api('/projects', user), project
@@ -459,18 +545,55 @@ describe API::Projects do
end
end
+ describe 'GET /users/:user_id/projects/' do
+ let!(:public_project) { create(:project, :public, name: 'public_project', creator_id: user4.id, namespace: user4.namespace) }
+
+ it 'returns error when user not found' do
+ get api('/users/9999/projects/')
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 User Not Found')
+ end
+
+ it 'returns projects filtered by user' do
+ get api("/users/#{user4.id}/projects/", user)
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id)
+ end
+ end
+
describe 'POST /projects/user/:id' do
- before { project }
- before { admin }
+ before do
+ expect(project).to be_persisted
+ end
+
+ it 'creates new project without path but with name and return 201' do
+ expect { post api("/projects/user/#{user.id}", admin), name: 'Foo Project' }.to change {Project.count}.by(1)
+ expect(response).to have_http_status(201)
+
+ project = Project.first
+
+ expect(project.name).to eq('Foo Project')
+ expect(project.path).to eq('foo-project')
+ end
- it 'creates new project without path and return 201' do
- expect { post api("/projects/user/#{user.id}", admin), name: 'foo' }.to change {Project.count}.by(1)
+ it 'creates new project with name and path and returns 201' do
+ expect { post api("/projects/user/#{user.id}", admin), path: 'path-project-Foo', name: 'Foo Project' }
+ .to change { Project.count }.by(1)
expect(response).to have_http_status(201)
+
+ project = Project.first
+
+ expect(project.name).to eq('Foo Project')
+ expect(project.path).to eq('path-project-Foo')
end
it 'responds with 400 on failure and not project' do
- expect { post api("/projects/user/#{user.id}", admin) }.
- not_to change { Project.count }
+ expect { post api("/projects/user/#{user.id}", admin) }
+ .not_to change { Project.count }
expect(response).to have_http_status(400)
expect(json_response['error']).to eq('name is missing')
@@ -549,7 +672,9 @@ describe API::Projects do
end
describe "POST /projects/:id/uploads" do
- before { project }
+ before do
+ project
+ end
it "uploads the file and returns its info" do
post api("/projects/#{project.id}/uploads", user), file: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")
@@ -564,13 +689,14 @@ describe API::Projects do
describe 'GET /projects/:id' do
context 'when unauthenticated' do
it 'returns the public projects' do
- public_project = create(:empty_project, :public)
+ public_project = create(:project, :public)
get api("/projects/#{public_project.id}")
expect(response).to have_http_status(200)
expect(json_response['id']).to eq(public_project.id)
expect(json_response['description']).to eq(public_project.description)
+ expect(json_response['default_branch']).to eq(public_project.default_branch)
expect(json_response.keys).not_to include('permissions')
end
end
@@ -612,10 +738,13 @@ describe API::Projects do
expect(json_response['shared_runners_enabled']).to be_present
expect(json_response['creator_id']).to be_present
expect(json_response['namespace']).to be_present
+ expect(json_response['import_status']).to be_present
+ expect(json_response).to include("import_error")
expect(json_response['avatar_url']).to be_nil
expect(json_response['star_count']).to be_present
expect(json_response['forks_count']).to be_present
expect(json_response['public_jobs']).to be_present
+ expect(json_response['ci_config_path']).to be_nil
expect(json_response['shared_with_groups']).to be_an Array
expect(json_response['shared_with_groups'].length).to eq(1)
expect(json_response['shared_with_groups'][0]['group_id']).to eq(group.id)
@@ -645,9 +774,9 @@ describe API::Projects do
it 'handles users with dots' do
dot_user = create(:user, username: 'dot.user')
- project = create(:empty_project, creator_id: dot_user.id, namespace: dot_user.namespace)
+ project = create(: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
@@ -662,19 +791,82 @@ describe API::Projects do
'path' => user.namespace.path,
'kind' => user.namespace.kind,
'full_path' => user.namespace.full_path,
+ 'parent_id' => nil
})
end
+ it "does not include statistics by default" do
+ get api("/projects/#{project.id}", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).not_to include 'statistics'
+ end
+
+ it "includes statistics if requested" do
+ get api("/projects/#{project.id}", user), statistics: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to include 'statistics'
+ end
+
+ it "includes import_error if user can admin project" do
+ get api("/projects/#{project.id}", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to include("import_error")
+ end
+
+ it "does not include import_error if user cannot admin project" do
+ get api("/projects/#{project.id}", user3)
+
+ expect(response).to have_http_status(200)
+ 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(: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 { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
it 'contains permission information' do
get api("/projects", user)
expect(response).to have_http_status(200)
- expect(json_response.first['permissions']['project_access']['access_level']).
- to eq(Gitlab::Access::MASTER)
+ expect(json_response.first['permissions']['project_access']['access_level'])
+ .to eq(Gitlab::Access::MASTER)
expect(json_response.first['permissions']['group_access']).to be_nil
end
end
@@ -685,111 +877,54 @@ describe API::Projects do
get api("/projects/#{project.id}", user)
expect(response).to have_http_status(200)
- expect(json_response['permissions']['project_access']['access_level']).
- to eq(Gitlab::Access::MASTER)
+ expect(json_response['permissions']['project_access']['access_level'])
+ .to eq(Gitlab::Access::MASTER)
expect(json_response['permissions']['group_access']).to be_nil
end
end
context 'group project' do
- let(:project2) { create(:empty_project, group: create(:group)) }
+ let(:project2) { create(:project, group: create(:group)) }
- before { project2.group.add_owner(user) }
+ before do
+ project2.group.add_owner(user)
+ end
it 'sets the owner and return 200' do
get api("/projects/#{project2.id}", user)
expect(response).to have_http_status(200)
expect(json_response['permissions']['project_access']).to be_nil
- expect(json_response['permissions']['group_access']['access_level']).
- to eq(Gitlab::Access::OWNER)
+ expect(json_response['permissions']['group_access']['access_level'])
+ .to eq(Gitlab::Access::OWNER)
end
end
end
end
end
- describe 'GET /projects/:id/events' do
- shared_examples_for 'project events response' do
- it 'returns the project events' do
- member = create(:user)
- create(:project_member, :developer, user: member, project: project)
- note = create(:note_on_issue, note: 'What an awesome day!', project: project)
- EventCreateService.new.leave_note(note, note.author)
-
- get api("/projects/#{project.id}/events", current_user)
-
- expect(response).to have_http_status(200)
- expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
-
- first_event = json_response.first
- expect(first_event['action_name']).to eq('commented on')
- expect(first_event['note']['body']).to eq('What an awesome day!')
-
- last_event = json_response.last
-
- expect(last_event['action_name']).to eq('joined')
- expect(last_event['project_id'].to_i).to eq(project.id)
- expect(last_event['author_username']).to eq(member.username)
- expect(last_event['author']['name']).to eq(member.name)
- end
- end
-
- context 'when unauthenticated' do
- it_behaves_like 'project events response' do
- let(:project) { create(:empty_project, :public) }
- let(:current_user) { nil }
- end
- end
-
- context 'when authenticated' do
- context 'valid request' do
- it_behaves_like 'project events response' do
- let(:current_user) { user }
- end
- end
-
- it 'returns a 404 error if not found' do
- get api('/projects/42/events', user)
-
- expect(response).to have_http_status(404)
- expect(json_response['message']).to eq('404 Project Not Found')
- end
-
- it 'returns a 404 error if user is not a member' do
- other_user = create(:user)
-
- get api("/projects/#{project.id}/events", other_user)
-
- expect(response).to have_http_status(404)
- end
- end
- end
-
describe 'GET /projects/:id/users' do
shared_examples_for 'project users response' do
it 'returns the project users' do
- member = create(:user)
- create(:project_member, :developer, user: member, project: project)
-
get api("/projects/#{project.id}/users", current_user)
+ user = project.namespace.owner
+
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)
first_user = json_response.first
- expect(first_user['username']).to eq(member.username)
- expect(first_user['name']).to eq(member.name)
+ expect(first_user['username']).to eq(user.username)
+ expect(first_user['name']).to eq(user.name)
expect(first_user.keys).to contain_exactly(*%w[name username id state avatar_url web_url])
end
end
context 'when unauthenticated' do
it_behaves_like 'project users response' do
- let(:project) { create(:empty_project, :public) }
+ let(:project) { create(:project, :public) }
let(:current_user) { nil }
end
end
@@ -819,7 +954,9 @@ describe API::Projects do
end
describe 'GET /projects/:id/snippets' do
- before { snippet }
+ before do
+ snippet
+ end
it 'returns an array of project snippets' do
get api("/projects/#{project.id}/snippets", user)
@@ -876,7 +1013,9 @@ describe API::Projects do
end
describe 'DELETE /projects/:id/snippets/:snippet_id' do
- before { snippet }
+ before do
+ snippet
+ end
it 'deletes existing project snippet' do
expect do
@@ -905,11 +1044,11 @@ describe API::Projects do
end
describe 'fork management' do
- let(:project_fork_target) { create(:empty_project) }
- let(:project_fork_source) { create(:empty_project, :public) }
+ let(:project_fork_target) { create(:project) }
+ let(:project_fork_source) { create(:project, :public) }
describe 'POST /projects/:id/fork/:forked_from_id' do
- let(:new_project_fork_source) { create(:empty_project, :public) }
+ let(:new_project_fork_source) { create(:project, :public) }
it "is not available for non admin users" do
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
@@ -926,6 +1065,14 @@ describe API::Projects do
expect(project_fork_target.forked?).to be_truthy
end
+ it 'refreshes the forks count cachce' do
+ expect(project_fork_source.forks_count).to be_zero
+
+ post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
+
+ expect(project_fork_source.forks_count).to eq(1)
+ end
+
it 'fails if forked_from project which does not exist' do
post api("/projects/#{project_fork_target.id}/fork/9999", admin)
expect(response).to have_http_status(404)
@@ -950,7 +1097,7 @@ describe API::Projects do
end
context 'when users belong to project group' do
- let(:project_fork_target) { create(:empty_project, group: create(:group)) }
+ let(:project_fork_target) { create(:project, group: create(:group)) }
before do
project_fork_target.group.add_owner user
@@ -1071,14 +1218,16 @@ describe API::Projects do
end
describe 'PUT /projects/:id' do
- before { project }
- before { user }
- before { user3 }
- before { user4 }
- before { project3 }
- before { project4 }
- before { project_member3 }
- before { project_member2 }
+ before do
+ expect(project).to be_persisted
+ expect(user).to be_persisted
+ expect(user3).to be_persisted
+ expect(user4).to be_persisted
+ expect(project3).to be_persisted
+ expect(project4).to be_persisted
+ expect(project_member2).to be_persisted
+ expect(project_member).to be_persisted
+ end
it 'returns 400 when nothing sent' do
project_param = {}
@@ -1428,6 +1577,8 @@ describe API::Projects do
expect(json_response['owner']['id']).to eq(user2.id)
expect(json_response['namespace']['id']).to eq(user2.namespace.id)
expect(json_response['forked_from_project']['id']).to eq(project.id)
+ expect(json_response['import_status']).to eq('scheduled')
+ expect(json_response).to include("import_error")
end
it 'forks if user is admin' do
@@ -1439,6 +1590,8 @@ describe API::Projects do
expect(json_response['owner']['id']).to eq(admin.id)
expect(json_response['namespace']['id']).to eq(admin.namespace.id)
expect(json_response['forked_from_project']['id']).to eq(project.id)
+ expect(json_response['import_status']).to eq('scheduled')
+ expect(json_response).to include("import_error")
end
it 'fails on missing project access for the project to fork' do
@@ -1559,7 +1712,7 @@ describe API::Projects do
context 'when authenticated as developer' do
before do
- project_member2
+ project_member
end
it 'returns forbidden error' do
diff --git a/spec/requests/api/protected_branches_spec.rb b/spec/requests/api/protected_branches_spec.rb
new file mode 100644
index 00000000000..1aa8a95780e
--- /dev/null
+++ b/spec/requests/api/protected_branches_spec.rb
@@ -0,0 +1,232 @@
+require 'spec_helper'
+
+describe API::ProtectedBranches do
+ let(:user) { create(:user) }
+ let!(:project) { create(:project, :repository) }
+ let(:protected_name) { 'feature' }
+ let(:branch_name) { protected_name }
+ let!(:protected_branch) do
+ create(:protected_branch, project: project, name: protected_name)
+ end
+
+ describe "GET /projects/:id/protected_branches" do
+ let(:route) { "/projects/#{project.id}/protected_branches" }
+
+ shared_examples_for 'protected branches' do
+ it 'returns the protected branches' do
+ get api(route, user), per_page: 100
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+
+ protected_branch_names = json_response.map { |x| x['name'] }
+ expected_branch_names = project.protected_branches.map { |x| x['name'] }
+ expect(protected_branch_names).to match_array(expected_branch_names)
+ end
+ end
+
+ context 'when authenticated as a master' do
+ before do
+ project.add_master(user)
+ end
+
+ it_behaves_like 'protected branches'
+ end
+
+ context 'when authenticated as a guest' do
+ before do
+ project.add_guest(user)
+ end
+
+ it_behaves_like '403 response' do
+ let(:request) { get api(route, user) }
+ end
+ end
+ end
+
+ describe "GET /projects/:id/protected_branches/:branch" do
+ let(:route) { "/projects/#{project.id}/protected_branches/#{branch_name}" }
+
+ shared_examples_for 'protected branch' do
+ it 'returns the protected branch' do
+ get api(route, user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['name']).to eq(branch_name)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(::Gitlab::Access::MASTER)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(::Gitlab::Access::MASTER)
+ end
+
+ context 'when protected branch does not exist' do
+ let(:branch_name) { 'unknown' }
+
+ it_behaves_like '404 response' do
+ let(:request) { get api(route, user) }
+ let(:message) { '404 Not found' }
+ end
+ end
+ end
+
+ context 'when authenticated as a master' do
+ before do
+ project.add_master(user)
+ end
+
+ it_behaves_like 'protected branch'
+
+ context 'when protected branch contains a wildcard' do
+ let(:protected_name) { 'feature*' }
+
+ it_behaves_like 'protected branch'
+ end
+ end
+
+ context 'when authenticated as a guest' do
+ before do
+ project.add_guest(user)
+ end
+
+ it_behaves_like '403 response' do
+ let(:request) { get api(route, user) }
+ end
+ end
+ end
+
+ describe 'POST /projects/:id/protected_branches' do
+ let(:branch_name) { 'new_branch' }
+
+ context 'when authenticated as a master' do
+ before do
+ project.add_master(user)
+ end
+
+ it 'protects a single branch' do
+ post api("/projects/#{project.id}/protected_branches", user), name: branch_name
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['name']).to eq(branch_name)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ end
+
+ it 'protects a single branch and developers can push' do
+ post api("/projects/#{project.id}/protected_branches", user),
+ name: branch_name, push_access_level: 30
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['name']).to eq(branch_name)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::DEVELOPER)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ end
+
+ it 'protects a single branch and developers can merge' do
+ post api("/projects/#{project.id}/protected_branches", user),
+ name: branch_name, merge_access_level: 30
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['name']).to eq(branch_name)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::DEVELOPER)
+ end
+
+ it 'protects a single branch and developers can push and merge' do
+ post api("/projects/#{project.id}/protected_branches", user),
+ name: branch_name, push_access_level: 30, merge_access_level: 30
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['name']).to eq(branch_name)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::DEVELOPER)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::DEVELOPER)
+ end
+
+ it 'protects a single branch and no one can push' do
+ post api("/projects/#{project.id}/protected_branches", user),
+ name: branch_name, push_access_level: 0
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['name']).to eq(branch_name)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::NO_ACCESS)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ end
+
+ it 'protects a single branch and no one can merge' do
+ post api("/projects/#{project.id}/protected_branches", user),
+ name: branch_name, merge_access_level: 0
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['name']).to eq(branch_name)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::NO_ACCESS)
+ end
+
+ it 'protects a single branch and no one can push or merge' do
+ post api("/projects/#{project.id}/protected_branches", user),
+ name: branch_name, push_access_level: 0, merge_access_level: 0
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['name']).to eq(branch_name)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::NO_ACCESS)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::NO_ACCESS)
+ end
+
+ it 'returns a 409 error if the same branch is protected twice' do
+ post api("/projects/#{project.id}/protected_branches", user), name: protected_name
+ expect(response).to have_gitlab_http_status(409)
+ end
+
+ context 'when branch has a wildcard in its name' do
+ let(:branch_name) { 'feature/*' }
+
+ it "protects multiple branches with a wildcard in the name" do
+ post api("/projects/#{project.id}/protected_branches", user), name: branch_name
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['name']).to eq(branch_name)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ end
+ end
+ end
+
+ context 'when authenticated as a guest' do
+ before do
+ project.add_guest(user)
+ end
+
+ it "returns a 403 error if guest" do
+ post api("/projects/#{project.id}/protected_branches/", user), name: branch_name
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+ end
+
+ describe "DELETE /projects/:id/protected_branches/unprotect/:branch" do
+ before do
+ project.add_master(user)
+ end
+
+ it "unprotects a single branch" do
+ delete api("/projects/#{project.id}/protected_branches/#{branch_name}", user)
+
+ expect(response).to have_gitlab_http_status(204)
+ end
+
+ it "returns 404 if branch does not exist" do
+ delete api("/projects/#{project.id}/protected_branches/barfoo", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ context 'when branch has a wildcard in its name' do
+ let(:protected_name) { 'feature*' }
+
+ it "unprotects a wildcard branch" do
+ delete api("/projects/#{project.id}/protected_branches/#{branch_name}", user)
+
+ expect(response).to have_gitlab_http_status(204)
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index be83514ed9c..e9ee3dd679d 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -42,7 +42,7 @@ describe API::Runner do
end
context 'when project token is used' do
- let(:project) { create(:empty_project) }
+ let(:project) { create(:project) }
it 'creates runner' do
post api('/runners'), token: project.runners_token
@@ -182,7 +182,7 @@ describe API::Runner do
end
describe '/api/v4/jobs' do
- let(:project) { create(:empty_project, shared_runners_enabled: false) }
+ let(:project) { create(:project, shared_runners_enabled: false) }
let(:pipeline) { create(:ci_pipeline_without_jobs, project: project, ref: 'master') }
let(:runner) { create(:ci_runner) }
let!(:job) do
@@ -190,17 +190,23 @@ describe API::Runner do
pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0, commands: "ls\ndate")
end
- before { project.runners << runner }
+ before do
+ project.runners << runner
+ end
describe 'POST /api/v4/jobs/request' do
let!(:last_update) {}
let!(:new_update) { }
let(:user_agent) { 'gitlab-runner 9.0.0 (9-0-stable; go1.7.4; linux/amd64)' }
- before { stub_container_registry_config(enabled: false) }
+ before do
+ stub_container_registry_config(enabled: false)
+ end
shared_examples 'no jobs available' do
- before { request_job }
+ before do
+ request_job
+ end
context 'when runner sends version in User-Agent' do
context 'for stable version' do
@@ -277,7 +283,9 @@ describe API::Runner do
end
context 'when jobs are finished' do
- before { job.success }
+ before do
+ job.success
+ end
it_behaves_like 'no jobs available'
end
@@ -343,7 +351,8 @@ describe API::Runner do
let(:expected_cache) do
[{ 'key' => 'cache_key',
'untracked' => false,
- 'paths' => ['vendor/*'] }]
+ 'paths' => ['vendor/*'],
+ 'policy' => 'pull-push' }]
end
it 'picks a job' do
@@ -356,8 +365,11 @@ describe API::Runner do
expect(json_response['token']).to eq(job.token)
expect(json_response['job_info']).to eq(expected_job_info)
expect(json_response['git_info']).to eq(expected_git_info)
- expect(json_response['image']).to eq({ 'name' => 'ruby:2.1' })
- expect(json_response['services']).to eq([{ 'name' => 'postgres' }])
+ expect(json_response['image']).to eq({ 'name' => 'ruby:2.1', 'entrypoint' => '/bin/sh' })
+ expect(json_response['services']).to eq([{ 'name' => 'postgres', 'entrypoint' => nil,
+ 'alias' => nil, 'command' => nil },
+ { 'name' => 'docker:dind', 'entrypoint' => '/bin/sh',
+ 'alias' => 'docker', 'command' => 'sleep 30' }])
expect(json_response['steps']).to eq(expected_steps)
expect(json_response['artifacts']).to eq(expected_artifacts)
expect(json_response['cache']).to eq(expected_cache)
@@ -403,8 +415,8 @@ describe API::Runner do
context 'when concurrently updating a job' do
before do
- expect_any_instance_of(Ci::Build).to receive(:run!).
- and_raise(ActiveRecord::StaleObjectError.new(nil, nil))
+ expect_any_instance_of(Ci::Build).to receive(:run!)
+ .and_raise(ActiveRecord::StaleObjectError.new(nil, nil))
end
it 'returns a conflict' do
@@ -431,8 +443,29 @@ describe API::Runner do
expect(response).to have_http_status(201)
expect(json_response['id']).to eq(test_job.id)
expect(json_response['dependencies'].count).to eq(2)
- expect(json_response['dependencies']).to include({ 'id' => job.id, 'name' => job.name, 'token' => job.token },
- { 'id' => job2.id, 'name' => job2.name, 'token' => job2.token })
+ expect(json_response['dependencies']).to include(
+ { 'id' => job.id, 'name' => job.name, 'token' => job.token },
+ { 'id' => job2.id, 'name' => job2.name, 'token' => job2.token })
+ end
+ end
+
+ context 'when pipeline have jobs with artifacts' do
+ let!(:job) { create(:ci_build_tag, :artifacts, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
+ let!(:test_job) { create(:ci_build, pipeline: pipeline, name: 'deploy', stage: 'deploy', stage_idx: 1) }
+
+ before do
+ job.success
+ end
+
+ it 'returns dependent jobs' do
+ request_job
+
+ expect(response).to have_http_status(201)
+ expect(json_response['id']).to eq(test_job.id)
+ expect(json_response['dependencies'].count).to eq(1)
+ expect(json_response['dependencies']).to include(
+ { 'id' => job.id, 'name' => job.name, 'token' => job.token,
+ 'artifacts_file' => { 'filename' => 'ci_build_artifacts.zip', 'size' => 106365 } })
end
end
@@ -484,10 +517,14 @@ describe API::Runner do
end
context 'when job has no tags' do
- before { job.update(tags: []) }
+ before do
+ job.update(tags: [])
+ end
context 'when runner is allowed to pick untagged jobs' do
- before { runner.update_column(:run_untagged, true) }
+ before do
+ runner.update_column(:run_untagged, true)
+ end
it 'picks job' do
request_job
@@ -497,7 +534,9 @@ describe API::Runner do
end
context 'when runner is not allowed to pick untagged jobs' do
- before { runner.update_column(:run_untagged, false) }
+ before do
+ runner.update_column(:run_untagged, false)
+ end
it_behaves_like 'no jobs available'
end
@@ -537,7 +576,9 @@ describe API::Runner do
end
context 'when registry is enabled' do
- before { stub_container_registry_config(enabled: true, host_port: registry_url) }
+ before do
+ stub_container_registry_config(enabled: true, host_port: registry_url)
+ end
it 'sends registry credentials key' do
request_job
@@ -548,7 +589,9 @@ describe API::Runner do
end
context 'when registry is disabled' do
- before { stub_container_registry_config(enabled: false, host_port: registry_url) }
+ before do
+ stub_container_registry_config(enabled: false, host_port: registry_url)
+ end
it 'does not send registry credentials' do
request_job
@@ -570,7 +613,9 @@ describe API::Runner do
describe 'PUT /api/v4/jobs/:id' do
let(:job) { create(:ci_build, :pending, :trace, pipeline: pipeline, runner_id: runner.id) }
- before { job.run! }
+ before do
+ job.run!
+ end
context 'when status is given' do
it 'mark job as succeeded' do
@@ -625,7 +670,9 @@ describe API::Runner do
let(:headers_with_range) { headers.merge({ 'Content-Range' => '11-20' }) }
let(:update_interval) { 10.seconds.to_i }
- before { initial_patch_the_trace }
+ before do
+ initial_patch_the_trace
+ end
context 'when request is valid' do
it 'gets correct response' do
@@ -636,7 +683,7 @@ describe API::Runner do
end
context 'when job has been updated recently' do
- it { expect{ patch_the_trace }.not_to change { job.updated_at }}
+ it { expect { patch_the_trace }.not_to change { job.updated_at }}
it "changes the job's trace" do
patch_the_trace
@@ -645,7 +692,7 @@ describe API::Runner do
end
context 'when Runner makes a force-patch' do
- it { expect{ force_patch_the_trace }.not_to change { job.updated_at }}
+ it { expect { force_patch_the_trace }.not_to change { job.updated_at }}
it "doesn't change the build.trace" do
force_patch_the_trace
@@ -767,7 +814,9 @@ describe API::Runner do
let(:file_upload) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
let(:file_upload2) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/gif') }
- before { job.run! }
+ before do
+ job.run!
+ end
describe 'POST /api/v4/jobs/:id/artifacts/authorize' do
context 'when using token as parameter' do
@@ -873,13 +922,17 @@ describe API::Runner do
end
context 'when uses regular file post' do
- before { upload_artifacts(file_upload, headers_with_token, false) }
+ before do
+ upload_artifacts(file_upload, headers_with_token, false)
+ end
it_behaves_like 'successful artifacts upload'
end
context 'when uses accelerated file post' do
- before { upload_artifacts(file_upload, headers_with_token, true) }
+ before do
+ upload_artifacts(file_upload, headers_with_token, true)
+ end
it_behaves_like 'successful artifacts upload'
end
@@ -1033,7 +1086,9 @@ describe API::Runner do
allow(ArtifactUploader).to receive(:artifacts_upload_path).and_return(@tmpdir)
end
- after { FileUtils.remove_entry @tmpdir }
+ after do
+ FileUtils.remove_entry @tmpdir
+ end
it' "fails to post artifacts for outside of tmp path"' do
upload_artifacts(file_upload, headers_with_token)
@@ -1055,7 +1110,9 @@ describe API::Runner do
describe 'GET /api/v4/jobs/:id/artifacts' do
let(:token) { job.token }
- before { download_artifact }
+ before do
+ download_artifact
+ end
context 'when job has artifacts' do
let(:job) { create(:ci_build, :artifacts) }
diff --git a/spec/requests/api/runners_spec.rb b/spec/requests/api/runners_spec.rb
index 645a5389850..c8ff25f70fa 100644
--- a/spec/requests/api/runners_spec.rb
+++ b/spec/requests/api/runners_spec.rb
@@ -5,8 +5,8 @@ describe API::Runners do
let(:user) { create(:user) }
let(:user2) { create(:user) }
- let(:project) { create(:empty_project, creator_id: user.id) }
- let(:project2) { create(:empty_project, creator_id: user.id) }
+ let(:project) { create(:project, creator_id: user.id) }
+ let(:project2) { create(:project, creator_id: user.id) }
let!(:shared_runner) { create(:ci_runner, :shared) }
let!(:unused_specific_runner) { create(:ci_runner) }
@@ -36,7 +36,7 @@ describe API::Runners do
it 'returns user available runners' do
get api('/runners', user)
- shared = json_response.any?{ |r| r['is_shared'] }
+ shared = json_response.any? { |r| r['is_shared'] }
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
@@ -46,7 +46,7 @@ describe API::Runners do
it 'filters runners by scope' do
get api('/runners?scope=active', user)
- shared = json_response.any?{ |r| r['is_shared'] }
+ shared = json_response.any? { |r| r['is_shared'] }
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
@@ -74,7 +74,7 @@ describe API::Runners do
it 'returns all runners' do
get api('/runners/all', admin)
- shared = json_response.any?{ |r| r['is_shared'] }
+ shared = json_response.any? { |r| r['is_shared'] }
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
@@ -93,7 +93,7 @@ describe API::Runners do
it 'filters runners by scope' do
get api('/runners/all?scope=specific', admin)
- shared = json_response.any?{ |r| r['is_shared'] }
+ shared = json_response.any? { |r| r['is_shared'] }
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
@@ -277,7 +277,7 @@ describe API::Runners do
delete api("/runners/#{shared_runner.id}", admin)
expect(response).to have_http_status(204)
- end.to change{ Ci::Runner.shared.count }.by(-1)
+ end.to change { Ci::Runner.shared.count }.by(-1)
end
end
@@ -287,7 +287,7 @@ describe API::Runners do
delete api("/runners/#{unused_specific_runner.id}", admin)
expect(response).to have_http_status(204)
- end.to change{ Ci::Runner.specific.count }.by(-1)
+ end.to change { Ci::Runner.specific.count }.by(-1)
end
it 'deletes used runner' do
@@ -295,7 +295,7 @@ describe API::Runners do
delete api("/runners/#{specific_runner.id}", admin)
expect(response).to have_http_status(204)
- end.to change{ Ci::Runner.specific.count }.by(-1)
+ end.to change { Ci::Runner.specific.count }.by(-1)
end
end
@@ -330,7 +330,7 @@ describe API::Runners do
delete api("/runners/#{specific_runner.id}", user)
expect(response).to have_http_status(204)
- end.to change{ Ci::Runner.specific.count }.by(-1)
+ end.to change { Ci::Runner.specific.count }.by(-1)
end
end
end
@@ -349,7 +349,7 @@ describe API::Runners do
it "returns project's runners" do
get api("/projects/#{project.id}/runners", user)
- shared = json_response.any?{ |r| r['is_shared'] }
+ shared = json_response.any? { |r| r['is_shared'] }
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
@@ -385,14 +385,14 @@ describe API::Runners do
it 'enables specific runner' do
expect do
post api("/projects/#{project.id}/runners", user), runner_id: specific_runner2.id
- end.to change{ project.runners.count }.by(+1)
+ end.to change { project.runners.count }.by(+1)
expect(response).to have_http_status(201)
end
it 'avoids changes when enabling already enabled runner' do
expect do
post api("/projects/#{project.id}/runners", user), runner_id: specific_runner.id
- end.to change{ project.runners.count }.by(0)
+ end.to change { project.runners.count }.by(0)
expect(response).to have_http_status(409)
end
@@ -401,7 +401,7 @@ describe API::Runners do
expect do
post api("/projects/#{project.id}/runners", user), runner_id: specific_runner2.id
- end.to change{ project.runners.count }.by(0)
+ end.to change { project.runners.count }.by(0)
expect(response).to have_http_status(403)
end
@@ -416,7 +416,7 @@ describe API::Runners do
it 'enables any specific runner' do
expect do
post api("/projects/#{project.id}/runners", admin), runner_id: unused_specific_runner.id
- end.to change{ project.runners.count }.by(+1)
+ end.to change { project.runners.count }.by(+1)
expect(response).to have_http_status(201)
end
end
@@ -461,7 +461,7 @@ describe API::Runners do
delete api("/projects/#{project.id}/runners/#{two_projects_runner.id}", user)
expect(response).to have_http_status(204)
- end.to change{ project.runners.count }.by(-1)
+ end.to change { project.runners.count }.by(-1)
end
end
@@ -469,7 +469,7 @@ describe API::Runners do
it "does not disable project's runner" do
expect do
delete api("/projects/#{project.id}/runners/#{specific_runner.id}", user)
- end.to change{ project.runners.count }.by(0)
+ end.to change { project.runners.count }.by(0)
expect(response).to have_http_status(403)
end
end
diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb
index 95df3429314..48d99841385 100644
--- a/spec/requests/api/services_spec.rb
+++ b/spec/requests/api/services_spec.rb
@@ -4,7 +4,7 @@ describe API::Services do
let(:user) { create(:user) }
let(:admin) { create(:admin) }
let(:user2) { create(:user) }
- let(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) }
+ let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
Service.available_services_names.each do |service|
describe "PUT /projects/:id/services/#{service.dasherize}" do
@@ -98,7 +98,7 @@ describe API::Services do
end
describe 'POST /projects/:id/services/:slug/trigger' do
- let!(:project) { create(:empty_project) }
+ let!(:project) { create(:project) }
describe 'Mattermost Service' do
let(:service_name) { 'mattermost_slash_commands' }
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 2398ae6219c..737c028ad53 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -10,8 +10,8 @@ describe API::Settings, 'Settings' do
expect(response).to have_http_status(200)
expect(json_response).to be_an Hash
expect(json_response['default_projects_limit']).to eq(42)
- expect(json_response['signin_enabled']).to be_truthy
- expect(json_response['repository_storage']).to eq('default')
+ expect(json_response['password_authentication_enabled']).to be_truthy
+ 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
@@ -32,19 +32,23 @@ describe API::Settings, 'Settings' do
it "updates application settings" do
put api("/application/settings", admin),
default_projects_limit: 3,
- signin_enabled: false,
- repository_storage: 'custom',
+ password_authentication_enabled: false,
+ repository_storages: ['custom'],
koding_enabled: true,
koding_url: 'http://koding.example.com',
plantuml_enabled: true,
plantuml_url: 'http://plantuml.example.com',
default_snippet_visibility: 'internal',
restricted_visibility_levels: ['public'],
- default_artifacts_expire_in: '2 days'
+ default_artifacts_expire_in: '2 days',
+ help_page_text: 'custom help text',
+ help_page_hide_commercial_content: true,
+ help_page_support_url: 'http://example.com/help',
+ project_export_enabled: false
+
expect(response).to have_http_status(200)
expect(json_response['default_projects_limit']).to eq(3)
- expect(json_response['signin_enabled']).to be_falsey
- expect(json_response['repository_storage']).to eq('custom')
+ expect(json_response['password_authentication_enabled']).to be_falsey
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')
@@ -53,6 +57,10 @@ describe API::Settings, 'Settings' do
expect(json_response['default_snippet_visibility']).to eq('internal')
expect(json_response['restricted_visibility_levels']).to eq(['public'])
expect(json_response['default_artifacts_expire_in']).to eq('2 days')
+ expect(json_response['help_page_text']).to eq('custom help text')
+ expect(json_response['help_page_hide_commercial_content']).to be_truthy
+ expect(json_response['help_page_support_url']).to eq('http://example.com/help')
+ expect(json_response['project_export_enabled']).to be_falsey
end
end
diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb
index e429cddcf6a..d09b8bc42f1 100644
--- a/spec/requests/api/snippets_spec.rb
+++ b/spec/requests/api/snippets_spec.rb
@@ -52,10 +52,10 @@ describe API::Snippets do
expect(json_response.map { |snippet| snippet['id']} ).to contain_exactly(
public_snippet.id,
public_snippet_other.id)
- expect(json_response.map{ |snippet| snippet['web_url']} ).to include(
+ expect(json_response.map { |snippet| snippet['web_url']} ).to include(
"http://localhost/snippets/#{public_snippet.id}",
"http://localhost/snippets/#{public_snippet_other.id}")
- expect(json_response.map{ |snippet| snippet['raw_url']} ).to include(
+ expect(json_response.map { |snippet| snippet['raw_url']} ).to include(
"http://localhost/snippets/#{public_snippet.id}/raw",
"http://localhost/snippets/#{public_snippet_other.id}/raw")
end
@@ -80,11 +80,33 @@ describe API::Snippets do
end
end
+ describe 'GET /snippets/:id' do
+ let(:snippet) { create(:personal_snippet, author: user) }
+
+ it 'returns snippet json' do
+ get api("/snippets/#{snippet.id}", user)
+
+ expect(response).to have_http_status(200)
+
+ expect(json_response['title']).to eq(snippet.title)
+ expect(json_response['description']).to eq(snippet.description)
+ expect(json_response['file_name']).to eq(snippet.file_name)
+ end
+
+ it 'returns 404 for invalid snippet id' do
+ get api("/snippets/1234", user)
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Not found')
+ end
+ end
+
describe 'POST /snippets/' do
let(:params) do
{
title: 'Test Title',
file_name: 'test.rb',
+ description: 'test description',
content: 'puts "hello world"',
visibility: 'public'
}
@@ -97,6 +119,7 @@ describe API::Snippets do
expect(response).to have_http_status(201)
expect(json_response['title']).to eq(params[:title])
+ expect(json_response['description']).to eq(params[:description])
expect(json_response['file_name']).to eq(params[:file_name])
end
@@ -119,23 +142,23 @@ describe API::Snippets do
context 'when the snippet is private' do
it 'creates the snippet' do
- expect { create_snippet(visibility: 'private') }.
- to change { Snippet.count }.by(1)
+ expect { create_snippet(visibility: 'private') }
+ .to change { Snippet.count }.by(1)
end
end
context 'when the snippet is public' do
it 'rejects the shippet' do
- expect { create_snippet(visibility: 'public') }.
- not_to change { Snippet.count }
+ expect { create_snippet(visibility: 'public') }
+ .not_to change { Snippet.count }
expect(response).to have_http_status(400)
expect(json_response['message']).to eq({ "error" => "Spam detected" })
end
it 'creates a spam log' do
- expect { create_snippet(visibility: 'public') }.
- to change { SpamLog.count }.by(1)
+ expect { create_snippet(visibility: 'public') }
+ .to change { SpamLog.count }.by(1)
end
end
end
@@ -150,12 +173,14 @@ describe API::Snippets do
it 'updates snippet' do
new_content = 'New content'
+ new_description = 'New description'
- put api("/snippets/#{snippet.id}", user), content: new_content
+ put api("/snippets/#{snippet.id}", user), content: new_content, description: new_description
expect(response).to have_http_status(200)
snippet.reload
expect(snippet.content).to eq(new_content)
+ expect(snippet.description).to eq(new_description)
end
it 'returns 404 for invalid snippet id' do
@@ -191,8 +216,8 @@ describe API::Snippets do
let(:visibility_level) { Snippet::PRIVATE }
it 'updates the snippet' do
- expect { update_snippet(title: 'Foo') }.
- to change { snippet.reload.title }.to('Foo')
+ expect { update_snippet(title: 'Foo') }
+ .to change { snippet.reload.title }.to('Foo')
end
end
@@ -200,16 +225,16 @@ describe API::Snippets do
let(:visibility_level) { Snippet::PUBLIC }
it 'rejects the shippet' do
- expect { update_snippet(title: 'Foo') }.
- not_to change { snippet.reload.title }
+ expect { update_snippet(title: 'Foo') }
+ .not_to change { snippet.reload.title }
expect(response).to have_http_status(400)
expect(json_response['message']).to eq({ "error" => "Spam detected" })
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo') }.
- to change { SpamLog.count }.by(1)
+ expect { update_snippet(title: 'Foo') }
+ .to change { SpamLog.count }.by(1)
end
end
@@ -217,13 +242,13 @@ describe API::Snippets do
let(:visibility_level) { Snippet::PRIVATE }
it 'rejects the snippet' do
- expect { update_snippet(title: 'Foo', visibility: 'public') }.
- not_to change { snippet.reload.title }
+ expect { update_snippet(title: 'Foo', visibility: 'public') }
+ .not_to change { snippet.reload.title }
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo', visibility: 'public') }.
- to change { SpamLog.count }.by(1)
+ expect { update_snippet(title: 'Foo', visibility: 'public') }
+ .to change { SpamLog.count }.by(1)
end
end
end
@@ -246,4 +271,25 @@ describe API::Snippets do
expect(json_response['message']).to eq('404 Snippet Not Found')
end
end
+
+ describe "GET /snippets/:id/user_agent_detail" do
+ let(:admin) { create(:admin) }
+ let(:snippet) { create(:personal_snippet, :public, author: user) }
+ let!(:user_agent_detail) { create(:user_agent_detail, subject: snippet) }
+
+ it 'exposes known attributes' do
+ get api("/snippets/#{snippet.id}/user_agent_detail", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['user_agent']).to eq(user_agent_detail.user_agent)
+ expect(json_response['ip_address']).to eq(user_agent_detail.ip_address)
+ expect(json_response['akismet_submitted']).to eq(user_agent_detail.submitted)
+ end
+
+ it "returns unautorized for non-admin users" do
+ get api("/snippets/#{snippet.id}/user_agent_detail", user)
+
+ expect(response).to have_http_status(403)
+ end
+ end
end
diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb
index c7b84173570..f65b475fe44 100644
--- a/spec/requests/api/system_hooks_spec.rb
+++ b/spec/requests/api/system_hooks_spec.rb
@@ -5,7 +5,9 @@ describe API::SystemHooks do
let(:admin) { create(:admin) }
let!(:hook) { create(:system_hook, url: "http://example.com") }
- before { stub_request(:post, hook.url) }
+ before do
+ stub_request(:post, hook.url)
+ end
describe "GET /hooks" do
context "when no user" do
@@ -32,8 +34,9 @@ describe API::SystemHooks do
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.first['url']).to eq(hook.url)
- expect(json_response.first['push_events']).to be true
+ expect(json_response.first['push_events']).to be false
expect(json_response.first['tag_push_events']).to be false
+ expect(json_response.first['repository_update_events']).to be true
end
end
end
diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb
index ef7d0c3ee41..9884c1ec206 100644
--- a/spec/requests/api/tags_spec.rb
+++ b/spec/requests/api/tags_spec.rb
@@ -1,66 +1,85 @@
require 'spec_helper'
-require 'mime/types'
describe API::Tags do
- include RepoHelpers
-
let(:user) { create(:user) }
- let(:user2) { create(:user) }
- let!(:project) { create(:project, :repository, creator: user) }
- let!(:master) { create(:project_member, :master, user: user, project: project) }
- let!(:guest) { create(:project_member, :guest, user: user2, project: project) }
+ let(:guest) { create(:user).tap { |u| project.add_guest(u) } }
+ let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
+ let(:tag_name) { project.repository.find_tag('v1.1.0').name }
- describe "GET /projects/:id/repository/tags" do
- let(:tag_name) { project.repository.tag_names.sort.reverse.first }
- let(:description) { 'Awesome release!' }
+ let(:project_id) { project.id }
+ let(:current_user) { nil }
+
+ before do
+ project.add_master(user)
+ end
+
+ describe 'GET /projects/:id/repository/tags' do
+ let(:route) { "/projects/#{project_id}/repository/tags" }
shared_examples_for 'repository tags' do
it 'returns the repository tags' do
- get api("/projects/#{project.id}/repository/tags", current_user)
+ get api(route, current_user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/tags')
expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
expect(json_response.first['name']).to eq(tag_name)
end
- end
- context 'when unauthenticated' do
- it_behaves_like 'repository tags' do
- let(:project) { create(:project, :public, :repository) }
- let(:current_user) { nil }
+ context 'when repository is disabled' do
+ include_context 'disabled repository'
+
+ it_behaves_like '403 response' do
+ let(:request) { get api(route, current_user) }
+ end
end
end
- context 'when authenticated' do
- it_behaves_like 'repository tags' do
- let(:current_user) { user }
+ context 'when unauthenticated', 'and project is public' do
+ let(:project) { create(:project, :public, :repository) }
+
+ it_behaves_like 'repository tags'
+ end
+
+ context 'when unauthenticated', 'and project is private' do
+ it_behaves_like '404 response' do
+ let(:request) { get api(route) }
+ let(:message) { '404 Project Not Found' }
end
end
- context 'without releases' do
- it "returns an array of project tags" do
- get api("/projects/#{project.id}/repository/tags", user)
+ context 'when authenticated', 'as a master' do
+ let(:current_user) { 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['name']).to eq(tag_name)
+ it_behaves_like 'repository tags'
+
+ context 'requesting with the escaped project full path' do
+ let(:project_id) { CGI.escape(project.full_path) }
+
+ it_behaves_like 'repository tags'
+ end
+ end
+
+ context 'when authenticated', 'as a guest' do
+ it_behaves_like '403 response' do
+ let(:request) { get api(route, guest) }
end
end
context 'with releases' do
+ let(:description) { 'Awesome release!' }
+
before do
release = project.releases.find_or_initialize_by(tag: tag_name)
release.update_attributes(description: description)
end
- it "returns an array of project tags with release info" do
- get api("/projects/#{project.id}/repository/tags", user)
+ it 'returns an array of project tags with release info' do
+ get api(route, user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/tags')
expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
expect(json_response.first['name']).to eq(tag_name)
expect(json_response.first['message']).to eq('Version 1.1.0')
expect(json_response.first['release']['description']).to eq(description)
@@ -69,210 +88,342 @@ describe API::Tags do
end
describe 'GET /projects/:id/repository/tags/:tag_name' do
- let(:tag_name) { project.repository.tag_names.sort.reverse.first }
+ let(:route) { "/projects/#{project_id}/repository/tags/#{tag_name}" }
shared_examples_for 'repository tag' do
- it 'returns the repository tag' do
- get api("/projects/#{project.id}/repository/tags/#{tag_name}", current_user)
-
- expect(response).to have_http_status(200)
+ it 'returns the repository branch' do
+ get api(route, current_user)
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/tag')
expect(json_response['name']).to eq(tag_name)
end
- it 'returns 404 for an invalid tag name' do
- get api("/projects/#{project.id}/repository/tags/foobar", current_user)
+ context 'when tag does not exist' do
+ let(:tag_name) { 'unknown' }
- expect(response).to have_http_status(404)
+ it_behaves_like '404 response' do
+ let(:request) { get api(route, current_user) }
+ let(:message) { '404 Tag Not Found' }
+ end
+ end
+
+ context 'when repository is disabled' do
+ include_context 'disabled repository'
+
+ it_behaves_like '403 response' do
+ let(:request) { get api(route, current_user) }
+ end
end
end
- context 'when unauthenticated' do
- it_behaves_like 'repository tag' do
- let(:project) { create(:project, :public, :repository) }
- let(:current_user) { nil }
+ context 'when unauthenticated', 'and project is public' do
+ let(:project) { create(:project, :public, :repository) }
+
+ it_behaves_like 'repository tag'
+ end
+
+ context 'when unauthenticated', 'and project is private' do
+ it_behaves_like '404 response' do
+ let(:request) { get api(route) }
+ let(:message) { '404 Project Not Found' }
end
end
- context 'when authenticated' do
- it_behaves_like 'repository tag' do
- let(:current_user) { user }
+ context 'when authenticated', 'as a master' do
+ let(:current_user) { user }
+
+ it_behaves_like 'repository tag'
+
+ context 'requesting with the escaped project full path' do
+ let(:project_id) { CGI.escape(project.full_path) }
+
+ it_behaves_like 'repository tag'
+ end
+ end
+
+ context 'when authenticated', 'as a guest' do
+ it_behaves_like '403 response' do
+ let(:request) { get api(route, guest) }
end
end
end
describe 'POST /projects/:id/repository/tags' do
- context 'lightweight tags' do
+ let(:tag_name) { 'new_tag' }
+ let(:route) { "/projects/#{project_id}/repository/tags" }
+
+ shared_examples_for 'repository new tag' do
it 'creates a new tag' do
- post api("/projects/#{project.id}/repository/tags", user),
- tag_name: 'v7.0.1',
- ref: 'master'
+ post api(route, current_user), tag_name: tag_name, ref: 'master'
- expect(response).to have_http_status(201)
- expect(json_response['name']).to eq('v7.0.1')
+ expect(response).to have_gitlab_http_status(201)
+ expect(response).to match_response_schema('public_api/v4/tag')
+ expect(json_response['name']).to eq(tag_name)
end
- end
- context 'lightweight tags with release notes' do
- it 'creates a new tag' do
- post api("/projects/#{project.id}/repository/tags", user),
- tag_name: 'v7.0.1',
- ref: 'master',
- release_description: 'Wow'
+ context 'when repository is disabled' do
+ include_context 'disabled repository'
- expect(response).to have_http_status(201)
- expect(json_response['name']).to eq('v7.0.1')
- expect(json_response['release']['description']).to eq('Wow')
+ it_behaves_like '403 response' do
+ let(:request) { post api(route, current_user) }
+ end
end
end
- describe 'DELETE /projects/:id/repository/tags/:tag_name' do
- let(:tag_name) { project.repository.tag_names.sort.reverse.first }
+ 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
- before do
- allow_any_instance_of(Repository).to receive(:rm_tag).and_return(true)
+ context 'when authenticated', 'as a guest' do
+ it_behaves_like '403 response' do
+ let(:request) { post api(route, guest) }
end
+ end
+
+ 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 new tag'
- context 'delete tag' do
- it 'deletes an existing tag' do
- delete api("/projects/#{project.id}/repository/tags/#{tag_name}", user)
+ context 'when tag contains a dot' do
+ let(:tag_name) { 'v7.0.1' }
- expect(response).to have_http_status(204)
+ it_behaves_like 'repository new tag'
end
- it 'raises 404 if the tag does not exist' do
- delete api("/projects/#{project.id}/repository/tags/foobar", user)
- expect(response).to have_http_status(404)
+ context 'requesting with the escaped project full path' do
+ let(:project_id) { CGI.escape(project.full_path) }
+
+ it_behaves_like 'repository new tag'
+
+ context 'when tag contains a dot' do
+ let(:tag_name) { 'v7.0.1' }
+
+ it_behaves_like 'repository new tag'
+ end
end
end
- end
- context 'annotated tag' do
- it 'creates a new annotated tag' do
- # Identity must be set in .gitconfig to create annotated tag.
- repo_path = project.repository.path_to_repo
- system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.name #{user.name}))
- system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.email #{user.email}))
+ it 'returns 400 if tag name is invalid' do
+ post api(route, current_user), tag_name: 'new design', ref: 'master'
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']).to eq('Tag name invalid')
+ end
+
+ it 'returns 400 if tag already exists' do
+ post api(route, current_user), tag_name: 'new_design1', ref: 'master'
- post api("/projects/#{project.id}/repository/tags", user),
- tag_name: 'v7.1.0',
- ref: 'master',
- message: 'Release 7.1.0'
+ expect(response).to have_gitlab_http_status(201)
+ expect(response).to match_response_schema('public_api/v4/tag')
- expect(response).to have_http_status(201)
- expect(json_response['name']).to eq('v7.1.0')
- expect(json_response['message']).to eq('Release 7.1.0')
+ post api(route, current_user), tag_name: 'new_design1', ref: 'master'
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']).to eq('Tag new_design1 already exists')
end
- end
- it 'denies for user without push access' do
- post api("/projects/#{project.id}/repository/tags", user2),
- tag_name: 'v1.9.0',
- ref: '621491c677087aa243f165eab467bfdfbee00be1'
- expect(response).to have_http_status(403)
+ it 'returns 400 if ref name is invalid' do
+ post api(route, current_user), tag_name: 'new_design3', ref: 'foo'
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']).to eq('Target foo is invalid')
+ end
+
+ context 'lightweight tags with release notes' do
+ it 'creates a new tag' do
+ post api(route, current_user), tag_name: tag_name, ref: 'master', release_description: 'Wow'
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(response).to match_response_schema('public_api/v4/tag')
+ expect(json_response['name']).to eq(tag_name)
+ expect(json_response['release']['description']).to eq('Wow')
+ end
+ end
+
+ context 'annotated tag' do
+ it 'creates a new annotated tag' do
+ # Identity must be set in .gitconfig to create annotated tag.
+ repo_path = project.repository.path_to_repo
+ system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.name #{user.name}))
+ system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.email #{user.email}))
+
+ post api(route, current_user), tag_name: 'v7.1.0', ref: 'master', message: 'Release 7.1.0'
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(response).to match_response_schema('public_api/v4/tag')
+ expect(json_response['name']).to eq('v7.1.0')
+ expect(json_response['message']).to eq('Release 7.1.0')
+ end
+ end
end
+ end
+
+ describe 'DELETE /projects/:id/repository/tags/:tag_name' do
+ let(:route) { "/projects/#{project_id}/repository/tags/#{tag_name}" }
- it 'returns 400 if tag name is invalid' do
- post api("/projects/#{project.id}/repository/tags", user),
- tag_name: 'v 1.0.0',
- ref: 'master'
- expect(response).to have_http_status(400)
- expect(json_response['message']).to eq('Tag name invalid')
+ before do
+ allow_any_instance_of(Repository).to receive(:rm_tag).and_return(true)
end
- it 'returns 400 if tag already exists' do
- post api("/projects/#{project.id}/repository/tags", user),
- tag_name: 'v8.0.0',
- ref: 'master'
- expect(response).to have_http_status(201)
- post api("/projects/#{project.id}/repository/tags", user),
- tag_name: 'v8.0.0',
- ref: 'master'
- expect(response).to have_http_status(400)
- expect(json_response['message']).to eq('Tag v8.0.0 already exists')
+ shared_examples_for 'repository delete tag' do
+ it 'deletes a tag' do
+ delete api(route, current_user)
+
+ expect(response).to have_gitlab_http_status(204)
+ end
+
+ context 'when tag does not exist' do
+ let(:tag_name) { 'unknown' }
+
+ it_behaves_like '404 response' do
+ let(:request) { delete api(route, current_user) }
+ let(:message) { 'No such tag' }
+ end
+ end
+
+ context 'when repository is disabled' do
+ include_context 'disabled repository'
+
+ it_behaves_like '403 response' do
+ let(:request) { delete api(route, current_user) }
+ end
+ end
end
- it 'returns 400 if ref name is invalid' do
- post api("/projects/#{project.id}/repository/tags", user),
- tag_name: 'mytag',
- ref: 'foo'
- expect(response).to have_http_status(400)
- expect(json_response['message']).to eq('Target foo is invalid')
+ context 'when authenticated', 'as a master' do
+ let(:current_user) { user }
+
+ it_behaves_like 'repository delete tag'
+
+ context 'requesting with the escaped project full path' do
+ let(:project_id) { CGI.escape(project.full_path) }
+
+ it_behaves_like 'repository delete tag'
+ end
end
end
describe 'POST /projects/:id/repository/tags/:tag_name/release' do
- let(:tag_name) { project.repository.tag_names.first }
+ let(:route) { "/projects/#{project_id}/repository/tags/#{tag_name}/release" }
let(:description) { 'Awesome release!' }
- it 'creates description for existing git tag' do
- post api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user),
- description: description
+ shared_examples_for 'repository new release' do
+ it 'creates description for existing git tag' do
+ post api(route, user), description: description
- expect(response).to have_http_status(201)
- expect(json_response['tag_name']).to eq(tag_name)
- expect(json_response['description']).to eq(description)
- end
+ expect(response).to have_gitlab_http_status(201)
+ expect(response).to match_response_schema('public_api/v4/release')
+ expect(json_response['tag_name']).to eq(tag_name)
+ expect(json_response['description']).to eq(description)
+ end
+
+ context 'when tag does not exist' do
+ let(:tag_name) { 'unknown' }
+
+ it_behaves_like '404 response' do
+ let(:request) { post api(route, current_user), description: description }
+ let(:message) { 'Tag does not exist' }
+ end
+ end
- it 'returns 404 if the tag does not exist' do
- post api("/projects/#{project.id}/repository/tags/foobar/release", user),
- description: description
+ context 'when repository is disabled' do
+ include_context 'disabled repository'
- expect(response).to have_http_status(404)
- expect(json_response['message']).to eq('Tag does not exist')
+ it_behaves_like '403 response' do
+ let(:request) { post api(route, current_user), description: description }
+ end
+ end
end
- context 'on tag with existing release' do
- before do
- release = project.releases.find_or_initialize_by(tag: tag_name)
- release.update_attributes(description: description)
+ context 'when authenticated', 'as a master' do
+ let(:current_user) { user }
+
+ it_behaves_like 'repository new release'
+
+ context 'requesting with the escaped project full path' do
+ let(:project_id) { CGI.escape(project.full_path) }
+
+ it_behaves_like 'repository new release'
end
- it 'returns 409 if there is already a release' do
- post api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user),
- description: description
+ context 'on tag with existing release' do
+ before do
+ release = project.releases.find_or_initialize_by(tag: tag_name)
+ release.update_attributes(description: description)
+ end
+
+ it 'returns 409 if there is already a release' do
+ post api(route, user), description: description
- expect(response).to have_http_status(409)
- expect(json_response['message']).to eq('Release already exists')
+ expect(response).to have_gitlab_http_status(409)
+ expect(json_response['message']).to eq('Release already exists')
+ end
end
end
end
describe 'PUT id/repository/tags/:tag_name/release' do
- let(:tag_name) { project.repository.tag_names.first }
+ let(:route) { "/projects/#{project_id}/repository/tags/#{tag_name}/release" }
let(:description) { 'Awesome release!' }
let(:new_description) { 'The best release!' }
- context 'on tag with existing release' do
- before do
- release = project.releases.find_or_initialize_by(tag: tag_name)
- release.update_attributes(description: description)
+ shared_examples_for 'repository update release' do
+ context 'on tag with existing release' do
+ before do
+ release = project.releases.find_or_initialize_by(tag: tag_name)
+ release.update_attributes(description: description)
+ end
+
+ it 'updates the release description' do
+ put api(route, current_user), description: new_description
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['tag_name']).to eq(tag_name)
+ expect(json_response['description']).to eq(new_description)
+ end
end
- it 'updates the release description' do
- put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user),
- description: new_description
+ context 'when tag does not exist' do
+ let(:tag_name) { 'unknown' }
- expect(response).to have_http_status(200)
- expect(json_response['tag_name']).to eq(tag_name)
- expect(json_response['description']).to eq(new_description)
+ it_behaves_like '404 response' do
+ let(:request) { put api(route, current_user), description: new_description }
+ let(:message) { 'Tag does not exist' }
+ end
end
- end
- it 'returns 404 if the tag does not exist' do
- put api("/projects/#{project.id}/repository/tags/foobar/release", user),
- description: new_description
+ context 'when repository is disabled' do
+ include_context 'disabled repository'
- expect(response).to have_http_status(404)
- expect(json_response['message']).to eq('Tag does not exist')
+ it_behaves_like '403 response' do
+ let(:request) { put api(route, current_user), description: new_description }
+ end
+ end
end
- it 'returns 404 if the release does not exist' do
- put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user),
- description: new_description
+ context 'when authenticated', 'as a master' do
+ let(:current_user) { user }
+
+ it_behaves_like 'repository update release'
- expect(response).to have_http_status(404)
- expect(json_response['message']).to eq('Release does not exist')
+ context 'requesting with the escaped project full path' do
+ let(:project_id) { CGI.escape(project.full_path) }
+
+ it_behaves_like 'repository update release'
+ end
+
+ context 'when release does not exist' do
+ it_behaves_like '404 response' do
+ let(:request) { put api(route, current_user), description: new_description }
+ let(:message) { 'Release does not exist' }
+ end
+ end
end
end
end
diff --git a/spec/requests/api/templates_spec.rb b/spec/requests/api/templates_spec.rb
index cb55985e3f5..f8af9295842 100644
--- a/spec/requests/api/templates_spec.rb
+++ b/spec/requests/api/templates_spec.rb
@@ -2,14 +2,18 @@ require 'spec_helper'
describe API::Templates do
context 'the Template Entity' do
- before { get api('/templates/gitignores/Ruby') }
+ before do
+ get api('/templates/gitignores/Ruby')
+ end
it { expect(json_response['name']).to eq('Ruby') }
it { expect(json_response['content']).to include('*.gem') }
end
context 'the TemplateList Entity' do
- before { get api('/templates/gitignores') }
+ before do
+ get api('/templates/gitignores')
+ end
it { expect(json_response.first['name']).not_to be_nil }
it { expect(json_response.first['content']).to be_nil }
@@ -47,7 +51,9 @@ describe API::Templates do
end
context 'the License Template Entity' do
- before { get api('/templates/licenses/mit') }
+ before do
+ get api('/templates/licenses/mit')
+ end
it 'returns a license template' do
expect(json_response['key']).to eq('mit')
diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb
index 92533f4dfea..25d7f6dffcf 100644
--- a/spec/requests/api/todos_spec.rb
+++ b/spec/requests/api/todos_spec.rb
@@ -1,8 +1,8 @@
require 'spec_helper'
describe API::Todos do
- let(:project_1) { create(:empty_project, :test_repo) }
- let(:project_2) { create(:empty_project) }
+ let(:project_1) { create(:project, :repository) }
+ let(:project_2) { create(:project) }
let(:author_1) { create(:user) }
let(:author_2) { create(:user) }
let(:john_doe) { create(:user, username: 'john_doe') }
diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb
index 16ddade27d9..572e9a0fd07 100644
--- a/spec/requests/api/triggers_spec.rb
+++ b/spec/requests/api/triggers_spec.rb
@@ -13,7 +13,7 @@ describe API::Triggers do
let!(:trigger_request) { create(:ci_trigger_request, trigger: trigger, created_at: '2015-01-01 12:13:14') }
describe 'POST /projects/:project_id/trigger/pipeline' do
- let!(:project2) { create(:project) }
+ let!(:project2) { create(:project, :repository) }
let(:options) do
{
token: trigger_token
@@ -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' }
@@ -184,7 +185,7 @@ describe API::Triggers do
expect do
post api("/projects/#{project.id}/triggers", user),
description: 'trigger'
- end.to change{project.triggers.count}.by(1)
+ end.to change {project.triggers.count}.by(1)
expect(response).to have_http_status(201)
expect(json_response).to include('description' => 'trigger')
@@ -287,7 +288,7 @@ describe API::Triggers do
delete api("/projects/#{project.id}/triggers/#{trigger.id}", user)
expect(response).to have_http_status(204)
- end.to change{project.triggers.count}.by(-1)
+ end.to change {project.triggers.count}.by(-1)
end
it 'responds with 404 Not Found if requesting non-existing trigger' do
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 4919ad19833..49739a1601a 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -11,11 +11,48 @@ describe API::Users do
let(:not_existing_user_id) { (User.maximum('id') || 0 ) + 10 }
let(:not_existing_pat_id) { (PersonalAccessToken.maximum('id') || 0 ) + 10 }
- describe "GET /users" do
+ describe 'GET /users' do
context "when unauthenticated" do
- it "returns authentication error" do
+ it "returns authorization error when the `username` parameter is not passed" do
get api("/users")
- expect(response).to have_http_status(401)
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+
+ it "returns the user when a valid `username` parameter is passed" do
+ get api("/users"), username: user.username
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(1)
+ expect(json_response[0]['id']).to eq(user.id)
+ expect(json_response[0]['username']).to eq(user.username)
+ end
+
+ it "returns an empty response when an invalid `username` parameter is passed" do
+ get api("/users"), username: 'invalid'
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(0)
+ end
+
+ context "when public level is restricted" do
+ before do
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
+ end
+
+ it "returns authorization error when the `username` parameter refers to an inaccessible user" do
+ get api("/users"), username: user.username
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+
+ it "returns authorization error when the `username` parameter is not passed" do
+ get api("/users")
+
+ expect(response).to have_gitlab_http_status(403)
+ end
end
end
@@ -24,17 +61,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 200" do
+ get api("/users", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ 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
@@ -76,6 +118,12 @@ describe API::Users do
expect(response).to have_http_status(403)
end
+
+ it 'does not reveal the `is_admin` flag of the user' do
+ get api('/users', user)
+
+ expect(json_response.first.keys).not_to include 'is_admin'
+ end
end
context "when admin" do
@@ -92,6 +140,7 @@ describe API::Users do
expect(json_response.first.keys).to include 'two_factor_enabled'
expect(json_response.first.keys).to include 'last_sign_in_at'
expect(json_response.first.keys).to include 'confirmed_at'
+ expect(json_response.first.keys).to include 'is_admin'
end
it "returns an array of external users" do
@@ -125,12 +174,42 @@ describe API::Users do
expect(response).to have_http_status(400)
end
+
+ it "returns a user created before a specific date" do
+ user = create(:user, created_at: Date.new(2000, 1, 1))
+
+ get api("/users?created_before=2000-01-02T00:00:00.060Z", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response.size).to eq(1)
+ expect(json_response.first['username']).to eq(user.username)
+ end
+
+ it "returns no users created before a specific date" do
+ create(:user, created_at: Date.new(2001, 1, 1))
+
+ get api("/users?created_before=2000-01-02T00:00:00.060Z", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response.size).to eq(0)
+ end
+
+ it "returns users created before and after a specific date" do
+ user = create(:user, created_at: Date.new(2001, 1, 1))
+
+ get api("/users?created_before=2001-01-02T00:00:00.060Z&created_after=1999-01-02T00:00:00.060", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response.size).to eq(1)
+ expect(json_response.first['username']).to eq(user.username)
+ end
end
end
describe "GET /users/:id" do
it "returns a user by id" do
get api("/users/#{user.id}", user)
+
expect(response).to have_http_status(200)
expect(json_response['username']).to eq(user.username)
end
@@ -138,12 +217,35 @@ describe API::Users do
it "does not return the user's `is_admin` flag" do
get api("/users/#{user.id}", user)
+ expect(response).to have_http_status(200)
expect(json_response['is_admin']).to be_nil
end
- it "returns a 401 if unauthenticated" do
- get api("/users/9998")
- expect(response).to have_http_status(401)
+ context 'when authenticated as admin' do
+ it 'includes the `is_admin` field' do
+ get api("/users/#{user.id}", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['is_admin']).to be(false)
+ end
+ end
+
+ context 'for an anonymous user' do
+ it "returns a user by id" do
+ get api("/users/#{user.id}")
+
+ expect(response).to have_http_status(200)
+ expect(json_response['username']).to eq(user.username)
+ end
+
+ it "returns a 404 if the target user is present but inaccessible" do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(nil, :read_user, user).and_return(false)
+
+ get api("/users/#{user.id}")
+
+ expect(response).to have_http_status(404)
+ end
end
it "returns a 404 error if user id not found" do
@@ -160,7 +262,9 @@ describe API::Users do
end
describe "POST /users" do
- before { admin }
+ before do
+ admin
+ end
it "creates user" do
expect do
@@ -280,14 +384,14 @@ describe API::Users do
bio: 'g' * 256,
projects_limit: -1
expect(response).to have_http_status(400)
- expect(json_response['message']['password']).
- to eq(['is too short (minimum is 8 characters)'])
- expect(json_response['message']['bio']).
- to eq(['is too long (maximum is 255 characters)'])
- expect(json_response['message']['projects_limit']).
- to eq(['must be greater than or equal to 0'])
- expect(json_response['message']['username']).
- to eq([Gitlab::Regex.namespace_regex_message])
+ expect(json_response['message']['password'])
+ .to eq(['is too short (minimum is 8 characters)'])
+ expect(json_response['message']['bio'])
+ .to eq(['is too long (maximum is 255 characters)'])
+ expect(json_response['message']['projects_limit'])
+ .to eq(['must be greater than or equal to 0'])
+ expect(json_response['message']['username'])
+ .to eq([Gitlab::PathRegex.namespace_format_message])
end
it "is not available for non admin users" do
@@ -336,6 +440,14 @@ describe API::Users do
expect(json_response['identities'].first['provider']).to eq('github')
end
end
+
+ context "scopes" do
+ let(:user) { admin }
+ let(:path) { '/users' }
+ let(:api_call) { method(:api) }
+
+ include_examples 'does not allow the "read_user" scope'
+ end
end
describe "GET /users/sign_up" do
@@ -349,10 +461,13 @@ describe API::Users do
describe "PUT /users/:id" do
let!(:admin_user) { create(:admin) }
- before { admin }
+ before do
+ admin
+ end
it "updates user with new bio" do
put api("/users/#{user.id}", admin), { bio: 'new test bio' }
+
expect(response).to have_http_status(200)
expect(json_response['bio']).to eq('new test bio')
expect(user.reload.bio).to eq('new test bio')
@@ -373,15 +488,34 @@ describe API::Users do
expect(user.reload.organization).to eq('GitLab')
end
+ it 'updates user with avatar' do
+ put api("/users/#{user.id}", admin), { avatar: fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
+
+ user.reload
+
+ expect(user.avatar).to be_present
+ expect(response).to have_http_status(200)
+ expect(json_response['avatar_url']).to include(user.avatar_path)
+ end
+
it 'updates user with his own email' do
put api("/users/#{user.id}", admin), email: user.email
+
expect(response).to have_http_status(200)
expect(json_response['email']).to eq(user.email)
expect(user.reload.email).to eq(user.email)
end
+ it 'updates user with a new email' do
+ put api("/users/#{user.id}", admin), email: 'new@email.com'
+
+ expect(response).to have_http_status(200)
+ expect(user.reload.notification_email).to eq('new@email.com')
+ end
+
it 'updates user with his own username' do
put api("/users/#{user.id}", admin), username: user.username
+
expect(response).to have_http_status(200)
expect(json_response['username']).to eq(user.username)
expect(user.reload.username).to eq(user.username)
@@ -389,12 +523,14 @@ describe API::Users do
it "updates user's existing identity" do
put api("/users/#{omniauth_user.id}", admin), provider: 'ldapmain', extern_uid: '654321'
+
expect(response).to have_http_status(200)
expect(omniauth_user.reload.identities.first.extern_uid).to eq('654321')
end
it 'updates user with new identity' do
put api("/users/#{user.id}", admin), provider: 'github', extern_uid: 'john'
+
expect(response).to have_http_status(200)
expect(user.reload.identities.first.extern_uid).to eq('john')
expect(user.reload.identities.first.provider).to eq('github')
@@ -402,12 +538,14 @@ describe API::Users do
it "updates admin status" do
put api("/users/#{user.id}", admin), { admin: true }
+
expect(response).to have_http_status(200)
expect(user.reload.admin).to eq(true)
end
it "updates external status" do
put api("/users/#{user.id}", admin), { external: true }
+
expect(response.status).to eq 200
expect(json_response['external']).to eq(true)
expect(user.reload.external?).to be_truthy
@@ -415,6 +553,7 @@ describe API::Users do
it "does not update admin status" do
put api("/users/#{admin_user.id}", admin), { can_create_group: false }
+
expect(response).to have_http_status(200)
expect(admin_user.reload.admin).to eq(true)
expect(admin_user.can_create_group).to eq(false)
@@ -422,17 +561,24 @@ describe API::Users do
it "does not allow invalid update" do
put api("/users/#{user.id}", admin), { email: 'invalid email' }
+
expect(response).to have_http_status(400)
expect(user.reload.email).not_to eq('invalid email')
end
- it "is not available for non admin users" do
- put api("/users/#{user.id}", user), attributes_for(:user)
- expect(response).to have_http_status(403)
+ context 'when the current user is not an admin' do
+ it "is not available" do
+ expect do
+ put api("/users/#{user.id}", user), attributes_for(:user)
+ end.not_to change { user.reload.attributes }
+
+ expect(response).to have_http_status(403)
+ end
end
it "returns 404 for non-existing user" do
put api("/users/999999", admin), { bio: 'update should fail' }
+
expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
end
@@ -452,14 +598,14 @@ describe API::Users do
bio: 'g' * 256,
projects_limit: -1
expect(response).to have_http_status(400)
- expect(json_response['message']['password']).
- to eq(['is too short (minimum is 8 characters)'])
- expect(json_response['message']['bio']).
- to eq(['is too long (maximum is 255 characters)'])
- expect(json_response['message']['projects_limit']).
- to eq(['must be greater than or equal to 0'])
- expect(json_response['message']['username']).
- to eq([Gitlab::Regex.namespace_regex_message])
+ expect(json_response['message']['password'])
+ .to eq(['is too short (minimum is 8 characters)'])
+ expect(json_response['message']['bio'])
+ .to eq(['is too long (maximum is 255 characters)'])
+ expect(json_response['message']['projects_limit'])
+ .to eq(['must be greater than or equal to 0'])
+ expect(json_response['message']['username'])
+ .to eq([Gitlab::PathRegex.namespace_format_message])
end
it 'returns 400 if provider is missing for identity update' do
@@ -483,6 +629,7 @@ describe API::Users do
it 'returns 409 conflict error if email address exists' do
put api("/users/#{@user.id}", admin), email: 'test@example.com'
+
expect(response).to have_http_status(409)
expect(@user.reload.email).to eq(@user.email)
end
@@ -490,6 +637,7 @@ describe API::Users do
it 'returns 409 conflict error if username taken' do
@user_id = User.all.last.id
put api("/users/#{@user.id}", admin), username: 'test'
+
expect(response).to have_http_status(409)
expect(@user.reload.username).to eq(@user.username)
end
@@ -497,7 +645,9 @@ describe API::Users do
end
describe "POST /users/:id/keys" do
- before { admin }
+ before do
+ admin
+ end
it "does not create invalid ssh key" do
post api("/users/#{user.id}/keys", admin), { title: "invalid key" }
@@ -527,7 +677,9 @@ describe API::Users do
end
describe 'GET /user/:id/keys' do
- before { admin }
+ before do
+ admin
+ end
context 'when unauthenticated' do
it 'returns authentication error' do
@@ -558,7 +710,9 @@ describe API::Users do
end
describe 'DELETE /user/:id/keys/:key_id' do
- before { admin }
+ before do
+ admin
+ end
context 'when unauthenticated' do
it 'returns authentication error' do
@@ -596,7 +750,9 @@ describe API::Users do
end
describe "POST /users/:id/emails" do
- before { admin }
+ before do
+ admin
+ end
it "does not create invalid email" do
post api("/users/#{user.id}/emails", admin), {}
@@ -620,7 +776,9 @@ describe API::Users do
end
describe 'GET /user/:id/emails' do
- before { admin }
+ before do
+ admin
+ end
context 'when unauthenticated' do
it 'returns authentication error' do
@@ -649,7 +807,7 @@ describe API::Users do
end
it "returns a 404 for invalid ID" do
- put api("/users/ASDF/emails", admin)
+ get api("/users/ASDF/emails", admin)
expect(response).to have_http_status(404)
end
@@ -657,7 +815,9 @@ describe API::Users do
end
describe 'DELETE /user/:id/emails/:email_id' do
- before { admin }
+ before do
+ admin
+ end
context 'when unauthenticated' do
it 'returns authentication error' do
@@ -702,7 +862,11 @@ describe API::Users do
describe "DELETE /users/:id" do
let!(:namespace) { user.namespace }
- before { admin }
+ let!(:issue) { create(:issue, author: user) }
+
+ before do
+ admin
+ end
it "deletes user" do
Sidekiq::Testing.inline! { delete api("/users/#{user.id}", admin) }
@@ -733,6 +897,25 @@ describe API::Users do
expect(response).to have_http_status(404)
end
+
+ context "hard delete disabled" do
+ it "moves contributions to the ghost user" do
+ Sidekiq::Testing.inline! { delete api("/users/#{user.id}", admin) }
+
+ expect(response).to have_http_status(204)
+ expect(issue.reload).to be_persisted
+ expect(issue.author.ghost?).to be_truthy
+ end
+ end
+
+ context "hard delete enabled" do
+ it "removes contributions" do
+ Sidekiq::Testing.inline! { delete api("/users/#{user.id}?hard_delete=true", admin) }
+
+ expect(response).to have_http_status(204)
+ expect(Issue.exists?(issue.id)).to be_falsy
+ end
+ end
end
describe "GET /user" do
@@ -762,6 +945,13 @@ describe API::Users do
expect(response).to match_response_schema('public_api/v4/user/public')
expect(json_response['id']).to eq(user.id)
end
+
+ context "scopes" do
+ let(:path) { "/user" }
+ let(:api_call) { method(:api) }
+
+ include_examples 'allows the "read_user" scope'
+ end
end
context 'with admin' do
@@ -774,11 +964,11 @@ describe API::Users do
expect(response).to have_http_status(403)
end
- it 'returns initial current user without private token when sudo not defined' do
+ it 'returns initial current user without private token but with is_admin when sudo not defined' do
get api("/user?private_token=#{admin_personal_access_token}")
expect(response).to have_http_status(200)
- expect(response).to match_response_schema('public_api/v4/user/public')
+ expect(response).to match_response_schema('public_api/v4/user/admin')
expect(json_response['id']).to eq(admin.id)
end
end
@@ -792,11 +982,11 @@ describe API::Users do
expect(json_response['id']).to eq(user.id)
end
- it 'returns initial current user without private token when sudo not defined' do
+ it 'returns initial current user without private token but with is_admin when sudo not defined' do
get api("/user?private_token=#{admin.private_token}")
expect(response).to have_http_status(200)
- expect(response).to match_response_schema('public_api/v4/user/public')
+ expect(response).to match_response_schema('public_api/v4/user/admin')
expect(json_response['id']).to eq(admin.id)
end
end
@@ -831,6 +1021,13 @@ describe API::Users do
expect(json_response).to be_an Array
expect(json_response.first["title"]).to eq(key.title)
end
+
+ context "scopes" do
+ let(:path) { "/user/keys" }
+ let(:api_call) { method(:api) }
+
+ include_examples 'allows the "read_user" scope'
+ end
end
end
@@ -864,6 +1061,13 @@ describe API::Users do
expect(response).to have_http_status(404)
end
+
+ context "scopes" do
+ let(:path) { "/user/keys/#{key.id}" }
+ let(:api_call) { method(:api) }
+
+ include_examples 'allows the "read_user" scope'
+ end
end
describe "POST /user/keys" do
@@ -953,6 +1157,13 @@ describe API::Users do
expect(json_response).to be_an Array
expect(json_response.first["email"]).to eq(email.email)
end
+
+ context "scopes" do
+ let(:path) { "/user/emails" }
+ let(:api_call) { method(:api) }
+
+ include_examples 'allows the "read_user" scope'
+ end
end
end
@@ -985,6 +1196,13 @@ describe API::Users do
expect(response).to have_http_status(404)
end
+
+ context "scopes" do
+ let(:path) { "/user/emails/#{email.id}" }
+ let(:api_call) { method(:api) }
+
+ include_examples 'allows the "read_user" scope'
+ end
end
describe "POST /user/emails" do
@@ -1043,7 +1261,10 @@ describe API::Users do
end
describe 'POST /users/:id/block' do
- before { admin }
+ before do
+ admin
+ end
+
it 'blocks existing user' do
post api("/users/#{user.id}/block", admin)
expect(response).to have_http_status(201)
@@ -1071,7 +1292,10 @@ describe API::Users do
describe 'POST /users/:id/unblock' do
let(:blocked_user) { create(:user, state: 'blocked') }
- before { admin }
+
+ before do
+ admin
+ end
it 'unblocks existing user' do
post api("/users/#{user.id}/unblock", admin)
@@ -1110,84 +1334,7 @@ describe API::Users do
end
end
- describe 'GET /users/:id/events' do
- let(:user) { create(:user) }
- let(:project) { create(:empty_project) }
- let(:note) { create(:note_on_issue, note: 'What an awesome day!', project: project) }
-
- before do
- project.add_user(user, :developer)
- EventCreateService.new.leave_note(note, user)
- end
-
- context "as a user than cannot see the event's project" do
- it 'returns no events' do
- other_user = create(:user)
-
- get api("/users/#{user.id}/events", other_user)
-
- expect(response).to have_http_status(200)
- expect(json_response).to be_empty
- end
- end
-
- context "as a user than can see the event's project" do
- context 'joined event' do
- it 'returns the "joined" event' do
- get api("/users/#{user.id}/events", user)
-
- expect(response).to have_http_status(200)
- expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
-
- comment_event = json_response.find { |e| e['action_name'] == 'commented on' }
-
- expect(comment_event['project_id'].to_i).to eq(project.id)
- expect(comment_event['author_username']).to eq(user.username)
- expect(comment_event['note']['id']).to eq(note.id)
- expect(comment_event['note']['body']).to eq('What an awesome day!')
-
- joined_event = json_response.find { |e| e['action_name'] == 'joined' }
-
- expect(joined_event['project_id'].to_i).to eq(project.id)
- expect(joined_event['author_username']).to eq(user.username)
- expect(joined_event['author']['name']).to eq(user.name)
- end
- end
-
- context 'when there are multiple events from different projects' do
- let(:second_note) { create(:note_on_issue, project: create(:empty_project)) }
- let(:third_note) { create(:note_on_issue, project: project) }
-
- before do
- second_note.project.add_user(user, :developer)
-
- [second_note, third_note].each do |note|
- EventCreateService.new.leave_note(note, user)
- end
- end
-
- it 'returns events in the correct order (from newest to oldest)' do
- get api("/users/#{user.id}/events", user)
-
- comment_events = json_response.select { |e| e['action_name'] == 'commented on' }
-
- expect(comment_events[0]['target_id']).to eq(third_note.id)
- expect(comment_events[1]['target_id']).to eq(second_note.id)
- expect(comment_events[2]['target_id']).to eq(note.id)
- end
- end
- end
-
- it 'returns a 404 error if not found' do
- get api('/users/42/events', user)
-
- expect(response).to have_http_status(404)
- expect(json_response['message']).to eq('404 User Not Found')
- end
- end
-
- context "user activities", :redis do
+ context "user activities", :clean_gitlab_redis_shared_state do
let!(:old_active_user) { create(:user, last_activity_on: Time.utc(2000, 1, 1)) }
let!(:newly_active_user) { create(:user, last_activity_on: 2.days.ago.midday) }
diff --git a/spec/requests/api/v3/award_emoji_spec.rb b/spec/requests/api/v3/award_emoji_spec.rb
index 9234710f488..681e8e04295 100644
--- a/spec/requests/api/v3/award_emoji_spec.rb
+++ b/spec/requests/api/v3/award_emoji_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe API::V3::AwardEmoji do
let(:user) { create(:user) }
- let!(:project) { create(:empty_project) }
+ let!(:project) { create(:project) }
let(:issue) { create(:issue, project: project) }
let!(:award_emoji) { create(:award_emoji, awardable: issue, user: user) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
diff --git a/spec/requests/api/v3/boards_spec.rb b/spec/requests/api/v3/boards_spec.rb
index 4d786331d1b..b86aab2ec70 100644
--- a/spec/requests/api/v3/boards_spec.rb
+++ b/spec/requests/api/v3/boards_spec.rb
@@ -4,7 +4,7 @@ describe API::V3::Boards do
let(:user) { create(:user) }
let(:guest) { create(:user) }
let(:non_member) { create(:user) }
- let!(:project) { create(:empty_project, :public, creator_id: user.id, namespace: user.namespace ) }
+ let!(:project) { create(:project, :public, creator_id: user.id, namespace: user.namespace ) }
let!(:dev_label) do
create(:label, title: 'Development', color: '#FFAABB', project: project)
@@ -99,7 +99,7 @@ describe API::V3::Boards do
context "when the user is project owner" do
let(:owner) { create(:user) }
- let(:project) { create(:empty_project, namespace: owner.namespace) }
+ let(:project) { create(:project, namespace: owner.namespace) }
it "deletes the list if an admin requests it" do
delete v3_api("#{base_url}/#{dev_list.id}", owner)
diff --git a/spec/requests/api/v3/branches_spec.rb b/spec/requests/api/v3/branches_spec.rb
index 72f8fbe71fb..c88f7788697 100644
--- a/spec/requests/api/v3/branches_spec.rb
+++ b/spec/requests/api/v3/branches_spec.rb
@@ -47,19 +47,6 @@ describe API::V3::Branches do
delete v3_api("/projects/#{project.id}/repository/branches/foobar", user)
expect(response).to have_http_status(404)
end
-
- it "removes protected branch" do
- create(:protected_branch, project: project, name: branch_name)
- delete v3_api("/projects/#{project.id}/repository/branches/#{branch_name}", user)
- expect(response).to have_http_status(405)
- expect(json_response['message']).to eq('Protected branch cant be removed')
- end
-
- it "does not remove HEAD branch" do
- delete v3_api("/projects/#{project.id}/repository/branches/master", user)
- expect(response).to have_http_status(405)
- expect(json_response['message']).to eq('Cannot remove HEAD branch')
- end
end
describe "DELETE /projects/:id/repository/merged_branches" do
diff --git a/spec/requests/api/v3/commits_spec.rb b/spec/requests/api/v3/commits_spec.rb
index c2e8c3ae6f7..4a4a5dc5c7c 100644
--- a/spec/requests/api/v3/commits_spec.rb
+++ b/spec/requests/api/v3/commits_spec.rb
@@ -5,7 +5,6 @@ describe API::V3::Commits do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let!(:project) { create(:project, :repository, creator: user, namespace: user.namespace) }
- let!(:master) { create(:project_member, :master, user: user, project: project) }
let!(:guest) { create(:project_member, :guest, user: user2, project: project) }
let!(:note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'a comment on a commit') }
let!(:another_note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'another comment on a commit') }
@@ -387,7 +386,7 @@ describe API::V3::Commits do
end
it "returns status for CI" do
- pipeline = project.ensure_pipeline('master', project.repository.commit.sha)
+ pipeline = project.pipelines.create(source: :push, ref: 'master', sha: project.repository.commit.sha)
pipeline.update(status: 'success')
get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
@@ -397,7 +396,7 @@ describe API::V3::Commits do
end
it "returns status for CI when pipeline is created" do
- project.ensure_pipeline('master', project.repository.commit.sha)
+ project.pipelines.create(source: :push, ref: 'master', sha: project.repository.commit.sha)
get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
diff --git a/spec/requests/api/v3/deploy_keys_spec.rb b/spec/requests/api/v3/deploy_keys_spec.rb
index b61b2b618a6..2affd0cfa51 100644
--- a/spec/requests/api/v3/deploy_keys_spec.rb
+++ b/spec/requests/api/v3/deploy_keys_spec.rb
@@ -3,8 +3,8 @@ require 'spec_helper'
describe API::V3::DeployKeys do
let(:user) { create(:user) }
let(:admin) { create(:admin) }
- let(:project) { create(:empty_project, creator_id: user.id) }
- let(:project2) { create(:empty_project, creator_id: user.id) }
+ let(:project) { create(:project, creator_id: user.id) }
+ let(:project2) { create(:project, creator_id: user.id) }
let(:deploy_key) { create(:deploy_key, public: true) }
let!(:deploy_keys_project) do
@@ -87,7 +87,7 @@ describe API::V3::DeployKeys do
expect do
post v3_api("/projects/#{project.id}/#{path}", admin), key_attrs
- end.to change{ project.deploy_keys.count }.by(1)
+ end.to change { project.deploy_keys.count }.by(1)
end
it 'returns an existing ssh key when attempting to add a duplicate' do
@@ -105,6 +105,15 @@ describe API::V3::DeployKeys do
expect(response).to have_http_status(201)
end
+
+ it 'accepts can_push parameter' do
+ key_attrs = attributes_for :write_access_key
+
+ post v3_api("/projects/#{project.id}/#{path}", admin), key_attrs
+
+ expect(response).to have_http_status(201)
+ expect(json_response['can_push']).to eq(true)
+ end
end
describe "DELETE /projects/:id/#{path}/:key_id" do
@@ -113,7 +122,7 @@ describe API::V3::DeployKeys do
it 'should delete existing key' do
expect do
delete v3_api("/projects/#{project.id}/#{path}/#{deploy_key.id}", admin)
- end.to change{ project.deploy_keys.count }.by(-1)
+ end.to change { project.deploy_keys.count }.by(-1)
end
it 'should return 404 Not Found with invalid ID' do
@@ -124,7 +133,7 @@ describe API::V3::DeployKeys do
end
describe "POST /projects/:id/#{path}/:key_id/enable" do
- let(:project2) { create(:empty_project) }
+ let(:project2) { create(:project) }
context 'when the user can admin the project' do
it 'enables the key' do
diff --git a/spec/requests/api/v3/environments_spec.rb b/spec/requests/api/v3/environments_spec.rb
index 99f35723974..39264e819a3 100644
--- a/spec/requests/api/v3/environments_spec.rb
+++ b/spec/requests/api/v3/environments_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe API::V3::Environments do
let(:user) { create(:user) }
let(:non_member) { create(:user) }
- let(:project) { create(:empty_project, :private, namespace: user.namespace) }
+ let(:project) { create(:project, :private, namespace: user.namespace) }
let!(:environment) { create(:environment, project: project) }
before do
diff --git a/spec/requests/api/v3/files_spec.rb b/spec/requests/api/v3/files_spec.rb
index 5bcbb441979..4ffa5d1784e 100644
--- a/spec/requests/api/v3/files_spec.rb
+++ b/spec/requests/api/v3/files_spec.rb
@@ -53,7 +53,7 @@ describe API::V3::Files do
let(:params) do
{
file_path: 'app/models/application.rb',
- ref: 'master',
+ ref: 'master'
}
end
@@ -74,7 +74,7 @@ describe API::V3::Files do
context 'when unauthenticated', 'and project is public' do
it_behaves_like 'repository files' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
@@ -126,8 +126,8 @@ describe API::V3::Files do
end
it "returns a 400 if editor fails to create file" do
- allow_any_instance_of(Repository).to receive(:create_file).
- and_raise(Repository::CommitError, 'Cannot create file')
+ allow_any_instance_of(Repository).to receive(:create_file)
+ .and_raise(Repository::CommitError, 'Cannot create file')
post v3_api("/projects/#{project.id}/repository/files", user), valid_params
@@ -263,7 +263,7 @@ describe API::V3::Files do
let(:get_params) do
{
file_path: file_path,
- ref: 'master',
+ ref: 'master'
}
end
diff --git a/spec/requests/api/v3/groups_spec.rb b/spec/requests/api/v3/groups_spec.rb
index 2862580cc70..778fcc73c30 100644
--- a/spec/requests/api/v3/groups_spec.rb
+++ b/spec/requests/api/v3/groups_spec.rb
@@ -9,9 +9,9 @@ describe API::V3::Groups do
let(:admin) { create(:admin) }
let!(:group1) { create(:group, avatar: File.open(uploaded_image_temp_path)) }
let!(:group2) { create(:group, :private) }
- let!(:project1) { create(:empty_project, namespace: group1) }
- let!(:project2) { create(:empty_project, namespace: group2) }
- let!(:project3) { create(:empty_project, namespace: group1, path: 'test', visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
+ let!(:project1) { create(:project, namespace: group1) }
+ let!(:project2) { create(:project, namespace: group2) }
+ let!(:project3) { create(:project, namespace: group1, path: 'test', visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
before do
group1.add_owner(user1)
@@ -69,7 +69,7 @@ describe API::V3::Groups do
storage_size: 702,
repository_size: 123,
lfs_objects_size: 234,
- build_artifacts_size: 345,
+ build_artifacts_size: 345
}.stringify_keys
project1.statistics.update!(attributes)
@@ -165,7 +165,7 @@ describe API::V3::Groups do
describe "GET /groups/:id" do
context "when authenticated as user" do
it "returns one of user1's groups" do
- project = create(:empty_project, namespace: group2, path: 'Foo')
+ project = create(:project, namespace: group2, path: 'Foo')
create(:project_group_link, project: project, group: group1)
get v3_api("/groups/#{group1.id}", user1)
@@ -176,7 +176,7 @@ describe API::V3::Groups do
expect(json_response['path']).to eq(group1.path)
expect(json_response['description']).to eq(group1.description)
expect(json_response['visibility_level']).to eq(group1.visibility_level)
- expect(json_response['avatar_url']).to eq(group1.avatar_url)
+ expect(json_response['avatar_url']).to eq(group1.avatar_url(only_path: false))
expect(json_response['web_url']).to eq(group1.web_url)
expect(json_response['request_access_enabled']).to eq(group1.request_access_enabled)
expect(json_response['full_name']).to eq(group1.full_name)
@@ -307,7 +307,7 @@ describe API::V3::Groups do
end
it 'filters the groups projects' do
- public_project = create(:empty_project, :public, path: 'test1', group: group1)
+ public_project = create(:project, :public, path: 'test1', group: group1)
get v3_api("/groups/#{group1.id}/projects", user1), visibility: 'public'
@@ -421,7 +421,7 @@ describe API::V3::Groups do
expect(json_response["request_access_enabled"]).to eq(group[:request_access_enabled])
end
- it "creates a nested group" do
+ it "creates a nested group", :nested_groups do
parent = create(:group)
parent.add_owner(user3)
group = attributes_for(:group, { parent_id: parent.id })
@@ -501,12 +501,12 @@ describe API::V3::Groups do
end
describe "POST /groups/:id/projects/:project_id" do
- let(:project) { create(:empty_project) }
- let(:project_path) { "#{project.namespace.path}%2F#{project.path}" }
+ let(:project) { create(:project) }
+ let(:project_path) { CGI.escape(project.full_path) }
- before(:each) do
- allow_any_instance_of(Projects::TransferService).
- to receive(:execute).and_return(true)
+ before do
+ allow_any_instance_of(Projects::TransferService)
+ .to receive(:execute).and_return(true)
end
context "when authenticated as user" do
diff --git a/spec/requests/api/v3/issues_spec.rb b/spec/requests/api/v3/issues_spec.rb
index ef5b10a1615..9eb538c4b09 100644
--- a/spec/requests/api/v3/issues_spec.rb
+++ b/spec/requests/api/v3/issues_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
-describe API::V3::Issues do
- include EmailHelpers
-
+describe API::V3::Issues, :mailer do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:non_member) { create(:user) }
@@ -10,11 +8,11 @@ describe API::V3::Issues do
let(:author) { create(:author) }
let(:assignee) { create(:assignee) }
let(:admin) { create(:user, :admin) }
- let!(:project) { create(:empty_project, :public, creator_id: user.id, namespace: user.namespace ) }
+ let!(:project) { create(:project, :public, creator_id: user.id, namespace: user.namespace ) }
let!(:closed_issue) do
create :closed_issue,
author: user,
- assignee: user,
+ assignees: [user],
project: project,
state: :closed,
milestone: milestone,
@@ -26,14 +24,14 @@ describe API::V3::Issues do
:confidential,
project: project,
author: author,
- assignee: assignee,
+ assignees: [assignee],
created_at: generate(:past_time),
updated_at: 2.hours.ago
end
let!(:issue) do
create :issue,
author: user,
- assignee: user,
+ assignees: [user],
project: project,
milestone: milestone,
created_at: generate(:past_time),
@@ -243,11 +241,11 @@ describe API::V3::Issues do
describe "GET /groups/:id/issues" do
let!(:group) { create(:group) }
- let!(:group_project) { create(:empty_project, :public, creator_id: user.id, namespace: group) }
+ let!(:group_project) { create(:project, :public, creator_id: user.id, namespace: group) }
let!(:group_closed_issue) do
create :closed_issue,
author: user,
- assignee: user,
+ assignees: [user],
project: group_project,
state: :closed,
milestone: group_milestone,
@@ -258,13 +256,13 @@ describe API::V3::Issues do
:confidential,
project: group_project,
author: author,
- assignee: assignee,
+ assignees: [assignee],
updated_at: 2.hours.ago
end
let!(:group_issue) do
create :issue,
author: user,
- assignee: user,
+ assignees: [user],
project: group_project,
milestone: group_milestone,
updated_at: 1.hour.ago
@@ -453,7 +451,7 @@ describe API::V3::Issues do
end
it "returns 404 on private projects for other users" do
- private_project = create(:empty_project, :private)
+ private_project = create(:project, :private)
create(:issue, project: private_project)
get v3_api("/projects/#{private_project.id}/issues", non_member)
@@ -462,7 +460,7 @@ describe API::V3::Issues do
end
it 'returns no issues when user has access to project but not issues' do
- restricted_project = create(:empty_project, :public, issues_access_level: ProjectFeature::PRIVATE)
+ restricted_project = create(:project, :public, issues_access_level: ProjectFeature::PRIVATE)
create(:issue, project: restricted_project)
get v3_api("/projects/#{restricted_project.id}/issues", non_member)
@@ -737,13 +735,14 @@ describe API::V3::Issues do
describe "POST /projects/:id/issues" do
it 'creates a new project issue' do
post v3_api("/projects/#{project.id}/issues", user),
- title: 'new issue', labels: 'label, label2'
+ title: 'new issue', labels: 'label, label2', assignee_id: assignee.id
expect(response).to have_http_status(201)
expect(json_response['title']).to eq('new issue')
expect(json_response['description']).to be_nil
expect(json_response['labels']).to eq(%w(label label2))
expect(json_response['confidential']).to be_falsy
+ expect(json_response['assignee']['name']).to eq(assignee.name)
end
it 'creates a new confidential project issue' do
@@ -1113,7 +1112,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
@@ -1140,6 +1139,22 @@ describe API::V3::Issues do
end
end
+ describe 'PUT /projects/:id/issues/:issue_id to update assignee' do
+ it 'updates an issue with no assignee' do
+ put v3_api("/projects/#{project.id}/issues/#{issue.id}", user), assignee_id: 0
+
+ expect(response).to have_http_status(200)
+ expect(json_response['assignee']).to eq(nil)
+ end
+
+ it 'updates an issue with assignee' do
+ put v3_api("/projects/#{project.id}/issues/#{issue.id}", user), assignee_id: user2.id
+
+ expect(response).to have_http_status(200)
+ expect(json_response['assignee']['name']).to eq(user2.name)
+ end
+ end
+
describe "DELETE /projects/:id/issues/:issue_id" do
it "rejects a non member from deleting an issue" do
delete v3_api("/projects/#{project.id}/issues/#{issue.id}", non_member)
@@ -1155,7 +1170,7 @@ describe API::V3::Issues do
context "when the user is project owner" do
let(:owner) { create(:user) }
- let(:project) { create(:empty_project, namespace: owner.namespace) }
+ let(:project) { create(:project, namespace: owner.namespace) }
it "deletes the issue if an admin requests it" do
delete v3_api("/projects/#{project.id}/issues/#{issue.id}", owner)
@@ -1175,8 +1190,8 @@ describe API::V3::Issues do
end
describe '/projects/:id/issues/:issue_id/move' do
- let!(:target_project) { create(:empty_project, path: 'project2', creator_id: user.id, namespace: user.namespace ) }
- let!(:target_project2) { create(:empty_project, creator_id: non_member.id, namespace: non_member.namespace ) }
+ let!(:target_project) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace ) }
+ let!(:target_project2) { create(:project, creator_id: non_member.id, namespace: non_member.namespace ) }
it 'moves an issue' do
post v3_api("/projects/#{project.id}/issues/#{issue.id}/move", user),
diff --git a/spec/requests/api/v3/labels_spec.rb b/spec/requests/api/v3/labels_spec.rb
index 62faa1cb129..32f37a08024 100644
--- a/spec/requests/api/v3/labels_spec.rb
+++ b/spec/requests/api/v3/labels_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe API::V3::Labels do
let(:user) { create(:user) }
- let(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) }
+ let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
let!(:label1) { create(:label, title: 'label1', project: project) }
let!(:priority_label) { create(:label, title: 'bug', project: project, priority: 3) }
diff --git a/spec/requests/api/v3/members_spec.rb b/spec/requests/api/v3/members_spec.rb
index 623f02902b8..bc918a8eb02 100644
--- a/spec/requests/api/v3/members_spec.rb
+++ b/spec/requests/api/v3/members_spec.rb
@@ -7,7 +7,7 @@ describe API::V3::Members do
let(:stranger) { create(:user) }
let(:project) do
- create(:empty_project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
+ create(:project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
project.team << [developer, :developer]
project.team << [master, :master]
project.request_access(access_requester)
diff --git a/spec/requests/api/v3/merge_requests_spec.rb b/spec/requests/api/v3/merge_requests_spec.rb
index 6c2950a6e6f..86f38dd4ec1 100644
--- a/spec/requests/api/v3/merge_requests_spec.rb
+++ b/spec/requests/api/v3/merge_requests_spec.rb
@@ -90,7 +90,7 @@ describe API::MergeRequests do
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(3)
- response_dates = json_response.map{ |merge_request| merge_request['created_at'] }
+ response_dates = json_response.map { |merge_request| merge_request['created_at'] }
expect(response_dates).to eq(response_dates.sort)
end
@@ -99,7 +99,7 @@ describe API::MergeRequests do
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(3)
- response_dates = json_response.map{ |merge_request| merge_request['created_at'] }
+ response_dates = json_response.map { |merge_request| merge_request['created_at'] }
expect(response_dates).to eq(response_dates.sort.reverse)
end
@@ -108,7 +108,7 @@ describe API::MergeRequests do
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(3)
- response_dates = json_response.map{ |merge_request| merge_request['updated_at'] }
+ response_dates = json_response.map { |merge_request| merge_request['updated_at'] }
expect(response_dates).to eq(response_dates.sort.reverse)
end
@@ -117,7 +117,7 @@ describe API::MergeRequests do
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(3)
- response_dates = json_response.map{ |merge_request| merge_request['created_at'] }
+ response_dates = json_response.map { |merge_request| merge_request['created_at'] }
expect(response_dates).to eq(response_dates.sort)
end
end
@@ -312,18 +312,20 @@ describe API::MergeRequests do
context 'forked projects' do
let!(:user2) { create(:user) }
- let!(:fork_project) { create(:empty_project, forked_from_project: project, namespace: user2.namespace, creator_id: user2.id) }
- let!(:unrelated_project) { create(:empty_project, namespace: create(:user).namespace, creator_id: user2.id) }
+ let!(:fork_project) { create(:project, forked_from_project: project, namespace: user2.namespace, creator_id: user2.id) }
+ let!(:unrelated_project) { create(:project, namespace: create(:user).namespace, creator_id: user2.id) }
- before :each do |each|
- fork_project.team << [user2, :reporter]
+ before do
+ fork_project.add_reporter(user2)
+
+ allow_any_instance_of(MergeRequest).to receive(:write_ref)
end
it "returns merge_request" do
post v3_api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', source_branch: "feature_conflict", target_branch: "master",
author: user2, target_project_id: project.id, description: 'Test description for Test merge_request'
- expect(response).to have_http_status(201)
+ expect(response).to have_gitlab_http_status(201)
expect(json_response['title']).to eq('Test merge_request')
expect(json_response['description']).to eq('Test description for Test merge_request')
end
@@ -334,26 +336,39 @@ describe API::MergeRequests do
expect(fork_project.forked_from_project).to eq(project)
post v3_api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', source_branch: "master", target_branch: "master", author: user2, target_project_id: project.id
- expect(response).to have_http_status(201)
+ expect(response).to have_gitlab_http_status(201)
expect(json_response['title']).to eq('Test merge_request')
end
+ it "returns 422 when target project has disabled merge requests" do
+ project.project_feature.update(merge_requests_access_level: 0)
+
+ post v3_api("/projects/#{fork_project.id}/merge_requests", user2),
+ title: 'Test',
+ target_branch: "master",
+ source_branch: 'markdown',
+ author: user2,
+ target_project_id: project.id
+
+ expect(response).to have_gitlab_http_status(422)
+ end
+
it "returns 400 when source_branch is missing" do
post v3_api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id
- expect(response).to have_http_status(400)
+ expect(response).to have_gitlab_http_status(400)
end
it "returns 400 when target_branch is missing" do
post v3_api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id
- expect(response).to have_http_status(400)
+ expect(response).to have_gitlab_http_status(400)
end
it "returns 400 when title is missing" do
post v3_api("/projects/#{fork_project.id}/merge_requests", user2),
target_branch: 'master', source_branch: 'markdown', author: user2, target_project_id: project.id
- expect(response).to have_http_status(400)
+ expect(response).to have_gitlab_http_status(400)
end
context 'when target_branch is specified' do
@@ -364,7 +379,7 @@ describe API::MergeRequests do
source_branch: 'markdown',
author: user,
target_project_id: fork_project.id
- expect(response).to have_http_status(422)
+ expect(response).to have_gitlab_http_status(422)
end
it 'returns 422 if targeting a different fork' do
@@ -374,14 +389,14 @@ describe API::MergeRequests do
source_branch: 'markdown',
author: user2,
target_project_id: unrelated_project.id
- expect(response).to have_http_status(422)
+ expect(response).to have_gitlab_http_status(422)
end
end
it "returns 201 when target_branch is specified and for the same project" do
post v3_api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', target_branch: 'master', source_branch: 'markdown', author: user2, target_project_id: fork_project.id
- expect(response).to have_http_status(201)
+ expect(response).to have_gitlab_http_status(201)
end
end
end
@@ -419,8 +434,8 @@ describe API::MergeRequests do
end
it "returns 406 if branch can't be merged" do
- allow_any_instance_of(MergeRequest).
- to receive(:can_be_merged?).and_return(false)
+ allow_any_instance_of(MergeRequest)
+ .to receive(:can_be_merged?).and_return(false)
put v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
@@ -622,7 +637,7 @@ describe API::MergeRequests do
end
it 'handles external issues' do
- jira_project = create(:jira_project, :public, name: 'JIR_EXT1')
+ jira_project = create(:jira_project, :public, :repository, 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)}")
@@ -637,7 +652,7 @@ describe API::MergeRequests do
end
it 'returns 403 if the user has no access to the merge request' do
- project = create(:empty_project, :private)
+ project = create(:project, :private, :repository)
merge_request = create(:merge_request, :simple, source_project: project)
guest = create(:user)
project.team << [guest, :guest]
diff --git a/spec/requests/api/v3/milestones_spec.rb b/spec/requests/api/v3/milestones_spec.rb
index f04efc990a7..feaa87faec7 100644
--- a/spec/requests/api/v3/milestones_spec.rb
+++ b/spec/requests/api/v3/milestones_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe API::V3::Milestones do
let(:user) { create(:user) }
- let!(:project) { create(:empty_project, namespace: user.namespace ) }
+ let!(:project) { create(:project, namespace: user.namespace ) }
let!(:closed_milestone) { create(:closed_milestone, project: project) }
let!(:milestone) { create(:milestone, project: project) }
@@ -194,7 +194,7 @@ describe API::V3::Milestones do
end
describe 'confidential issues' do
- let(:public_project) { create(:empty_project, :public) }
+ let(:public_project) { create(: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) }
diff --git a/spec/requests/api/v3/notes_spec.rb b/spec/requests/api/v3/notes_spec.rb
index 2bae4a60931..56729692eed 100644
--- a/spec/requests/api/v3/notes_spec.rb
+++ b/spec/requests/api/v3/notes_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe API::V3::Notes do
let(:user) { create(:user) }
- let!(:project) { create(:empty_project, :public, namespace: user.namespace) }
+ let!(:project) { create(:project, :public, namespace: user.namespace) }
let!(:issue) { create(:issue, project: project, author: user) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) }
let!(:snippet) { create(:project_snippet, project: project, author: user) }
@@ -13,12 +13,12 @@ describe API::V3::Notes do
# For testing the cross-reference of a private issue in a public issue
let(:private_user) { create(:user) }
let(:private_project) do
- create(:empty_project, namespace: private_user.namespace).
- tap { |p| p.team << [private_user, :master] }
+ create(:project, namespace: private_user.namespace)
+ .tap { |p| p.team << [private_user, :master] }
end
let(:private_issue) { create(:issue, project: private_project) }
- let(:ext_proj) { create(:empty_project, :public) }
+ let(:ext_proj) { create(:project, :public) }
let(:ext_issue) { create(:issue, project: ext_proj) }
let!(:cross_reference_note) do
@@ -268,7 +268,7 @@ describe API::V3::Notes do
context 'when user does not have access to read the noteable' do
it 'responds with 404' do
- project = create(:empty_project, :private) { |p| p.add_guest(user) }
+ project = create(:project, :private) { |p| p.add_guest(user) }
issue = create(:issue, :confidential, project: project)
post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user),
@@ -279,7 +279,7 @@ describe API::V3::Notes do
end
context 'when user does not have access to create noteable' do
- let(:private_issue) { create(:issue, project: create(:empty_project, :private)) }
+ let(:private_issue) { create(:issue, project: create(:project, :private)) }
##
# We are posting to project user has access to, but we use issue id
diff --git a/spec/requests/api/v3/project_hooks_spec.rb b/spec/requests/api/v3/project_hooks_spec.rb
index a3a4c77d09d..b0eddbb5dd2 100644
--- a/spec/requests/api/v3/project_hooks_spec.rb
+++ b/spec/requests/api/v3/project_hooks_spec.rb
@@ -58,7 +58,7 @@ describe API::ProjectHooks, 'ProjectHooks' do
expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events)
expect(json_response['tag_push_events']).to eq(hook.tag_push_events)
expect(json_response['note_events']).to eq(hook.note_events)
- expect(json_response['build_events']).to eq(hook.build_events)
+ expect(json_response['build_events']).to eq(hook.job_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)
@@ -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)
@@ -143,7 +143,7 @@ describe API::ProjectHooks, 'ProjectHooks' do
expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events)
expect(json_response['tag_push_events']).to eq(hook.tag_push_events)
expect(json_response['note_events']).to eq(hook.note_events)
- expect(json_response['build_events']).to eq(hook.build_events)
+ expect(json_response['build_events']).to eq(hook.job_events)
expect(json_response['pipeline_events']).to eq(hook.pipeline_events)
expect(json_response['wiki_page_events']).to eq(hook.wiki_page_events)
expect(json_response['enable_ssl_verification']).to eq(hook.enable_ssl_verification)
diff --git a/spec/requests/api/v3/project_snippets_spec.rb b/spec/requests/api/v3/project_snippets_spec.rb
index 365e7365fda..3963924a066 100644
--- a/spec/requests/api/v3/project_snippets_spec.rb
+++ b/spec/requests/api/v3/project_snippets_spec.rb
@@ -1,7 +1,7 @@
require 'rails_helper'
describe API::ProjectSnippets do
- let(:project) { create(:empty_project, :public) }
+ let(:project) { create(:project, :public) }
let(:user) { create(:user) }
let(:admin) { create(:admin) }
@@ -30,7 +30,7 @@ describe API::ProjectSnippets do
expect(response).to have_http_status(200)
expect(json_response.size).to eq(3)
- expect(json_response.map{ |snippet| snippet['id']} ).to include(public_snippet.id, internal_snippet.id, private_snippet.id)
+ expect(json_response.map { |snippet| snippet['id']} ).to include(public_snippet.id, internal_snippet.id, private_snippet.id)
expect(json_response.last).to have_key('web_url')
end
@@ -85,23 +85,23 @@ describe API::ProjectSnippets do
context 'when the snippet is private' do
it 'creates the snippet' do
- expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }.
- to change { Snippet.count }.by(1)
+ expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }
+ .to change { Snippet.count }.by(1)
end
end
context 'when the snippet is public' do
it 'rejects the shippet' do
- expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }.
- not_to change { Snippet.count }
+ expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }
+ .not_to change { Snippet.count }
expect(response).to have_http_status(400)
expect(json_response['message']).to eq({ "error" => "Spam detected" })
end
it 'creates a spam log' do
- expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }.
- to change { SpamLog.count }.by(1)
+ expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }
+ .to change { SpamLog.count }.by(1)
end
end
end
@@ -147,8 +147,8 @@ describe API::ProjectSnippets do
let(:visibility_level) { Snippet::PRIVATE }
it 'creates the snippet' do
- expect { update_snippet(title: 'Foo') }.
- to change { snippet.reload.title }.to('Foo')
+ expect { update_snippet(title: 'Foo') }
+ .to change { snippet.reload.title }.to('Foo')
end
end
@@ -156,13 +156,13 @@ describe API::ProjectSnippets do
let(:visibility_level) { Snippet::PUBLIC }
it 'rejects the snippet' do
- expect { update_snippet(title: 'Foo') }.
- not_to change { snippet.reload.title }
+ expect { update_snippet(title: 'Foo') }
+ .not_to change { snippet.reload.title }
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo') }.
- to change { SpamLog.count }.by(1)
+ expect { update_snippet(title: 'Foo') }
+ .to change { SpamLog.count }.by(1)
end
end
@@ -170,16 +170,16 @@ describe API::ProjectSnippets do
let(:visibility_level) { Snippet::PRIVATE }
it 'rejects the snippet' do
- expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
- not_to change { snippet.reload.title }
+ expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }
+ .not_to change { snippet.reload.title }
expect(response).to have_http_status(400)
expect(json_response['message']).to eq({ "error" => "Spam detected" })
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
- to change { SpamLog.count }.by(1)
+ expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }
+ .to change { SpamLog.count }.by(1)
end
end
end
diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb
index e15b90d7a9e..a514166274a 100644
--- a/spec/requests/api/v3/projects_spec.rb
+++ b/spec/requests/api/v3/projects_spec.rb
@@ -7,11 +7,10 @@ describe API::V3::Projects do
let(:user2) { create(:user) }
let(:user3) { create(:user) }
let(:admin) { create(:admin) }
- let(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) }
- let(:project2) { create(:empty_project, path: 'project2', creator_id: user.id, namespace: user.namespace) }
+ let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
+ let(:project2) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace) }
let(:snippet) { create(:project_snippet, :public, author: user, project: project, title: 'example') }
- let(:project_member) { create(:project_member, :master, user: user, project: project) }
- let(:project_member2) { create(:project_member, :developer, user: user3, project: project) }
+ let(:project_member) { create(:project_member, :developer, user: user3, project: project) }
let(:user4) { create(:user) }
let(:project3) do
create(:project,
@@ -25,14 +24,14 @@ describe API::V3::Projects do
issues_enabled: false, wiki_enabled: false,
snippets_enabled: false)
end
- let(:project_member3) do
+ let(:project_member2) do
create(:project_member,
user: user4,
project: project3,
access_level: ProjectMember::MASTER)
end
let(:project4) do
- create(:empty_project,
+ create(:project,
name: 'third_project',
path: 'third_project',
creator_id: user4.id,
@@ -83,7 +82,14 @@ describe API::V3::Projects do
context 'GET /projects?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)
+ expected_keys = %w(
+ id description default_branch tag_list
+ ssh_url_to_repo http_url_to_repo web_url
+ name name_with_namespace
+ path path_with_namespace
+ star_count forks_count
+ created_at last_activity_at
+ )
get v3_api('/projects?simple=true', user)
@@ -125,6 +131,36 @@ describe API::V3::Projects do
end
end
+ context 'and using archived' do
+ let!(:archived_project) { create(:project, creator_id: user.id, namespace: user.namespace, archived: true) }
+
+ it 'returns archived project' do
+ get v3_api('/projects?archived=true', user)
+
+ 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(archived_project.id)
+ end
+
+ it 'returns non-archived project' do
+ get v3_api('/projects?archived=false', user)
+
+ 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(project.id)
+ end
+
+ it 'returns all project' do
+ get v3_api('/projects', user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(2)
+ end
+ end
+
context 'and using sorting' do
before do
project2
@@ -166,7 +202,7 @@ describe API::V3::Projects do
expect(json_response).to satisfy do |response|
response.one? do |entry|
- entry.has_key?('permissions') &&
+ entry.key?('permissions') &&
entry['name'] == project.name &&
entry['owner']['username'] == user.username
end
@@ -227,7 +263,7 @@ describe API::V3::Projects do
storage_size: 702,
repository_size: 123,
lfs_objects_size: 234,
- build_artifacts_size: 345,
+ build_artifacts_size: 345
}
project4.statistics.update!(attributes)
@@ -252,7 +288,7 @@ describe API::V3::Projects do
end
end
- let!(:public_project) { create(:empty_project, :public) }
+ let!(:public_project) { create(:project, :public) }
before do
project
project2
@@ -283,10 +319,10 @@ describe API::V3::Projects do
end
describe 'GET /projects/starred' do
- let(:public_project) { create(:empty_project, :public) }
+ let(:public_project) { create(:project, :public) }
before do
- project_member2
+ project_member
user3.update_attributes(starred_projects: [project, project2, project3, public_project])
end
@@ -302,15 +338,15 @@ describe API::V3::Projects do
context 'maximum number of projects reached' do
it 'does not create new project and respond with 403' do
allow_any_instance_of(User).to receive(:projects_limit_left).and_return(0)
- expect { post v3_api('/projects', user2), name: 'foo' }.
- to change {Project.count}.by(0)
+ expect { post v3_api('/projects', user2), name: 'foo' }
+ .to change {Project.count}.by(0)
expect(response).to have_http_status(403)
end
end
it 'creates new project without path but with name and returns 201' do
- expect { post v3_api('/projects', user), name: 'Foo Project' }.
- to change { Project.count }.by(1)
+ expect { post v3_api('/projects', user), name: 'Foo Project' }
+ .to change { Project.count }.by(1)
expect(response).to have_http_status(201)
project = Project.first
@@ -320,8 +356,8 @@ describe API::V3::Projects do
end
it 'creates new project without name but with path and returns 201' do
- expect { post v3_api('/projects', user), path: 'foo_project' }.
- to change { Project.count }.by(1)
+ expect { post v3_api('/projects', user), path: 'foo_project' }
+ .to change { Project.count }.by(1)
expect(response).to have_http_status(201)
project = Project.first
@@ -331,8 +367,8 @@ describe API::V3::Projects do
end
it 'creates new project name and path and returns 201' do
- expect { post v3_api('/projects', user), path: 'foo-Project', name: 'Foo Project' }.
- to change { Project.count }.by(1)
+ expect { post v3_api('/projects', user), path: 'foo-Project', name: 'Foo Project' }
+ .to change { Project.count }.by(1)
expect(response).to have_http_status(201)
project = Project.first
@@ -490,8 +526,8 @@ describe API::V3::Projects do
end
it 'responds with 400 on failure and not project' do
- expect { post v3_api("/projects/user/#{user.id}", admin) }.
- not_to change { Project.count }
+ expect { post v3_api("/projects/user/#{user.id}", admin) }
+ .not_to change { Project.count }
expect(response).to have_http_status(400)
expect(json_response['error']).to eq('name is missing')
@@ -608,13 +644,14 @@ describe API::V3::Projects do
describe 'GET /projects/:id' do
context 'when unauthenticated' do
it 'returns the public projects' do
- public_project = create(:empty_project, :public)
+ public_project = create(:project, :public)
get v3_api("/projects/#{public_project.id}")
expect(response).to have_http_status(200)
expect(json_response['id']).to eq(public_project.id)
expect(json_response['description']).to eq(public_project.description)
+ expect(json_response['default_branch']).to eq(public_project.default_branch)
expect(json_response.keys).not_to include('permissions')
end
end
@@ -622,7 +659,6 @@ describe API::V3::Projects do
context 'when authenticated' do
before do
project
- project_member
end
it 'returns a project by id' do
@@ -690,9 +726,9 @@ describe API::V3::Projects do
it 'handles users with dots' do
dot_user = create(:user, username: 'dot.user')
- project = create(:empty_project, creator_id: dot_user.id, namespace: dot_user.namespace)
+ project = create(: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
@@ -707,6 +743,7 @@ describe API::V3::Projects do
'path' => user.namespace.path,
'kind' => user.namespace.kind,
'full_path' => user.namespace.full_path,
+ 'parent_id' => nil
})
end
@@ -718,8 +755,8 @@ describe API::V3::Projects do
get v3_api("/projects", user)
expect(response).to have_http_status(200)
- expect(json_response.first['permissions']['project_access']['access_level']).
- to eq(Gitlab::Access::MASTER)
+ expect(json_response.first['permissions']['project_access']['access_level'])
+ .to eq(Gitlab::Access::MASTER)
expect(json_response.first['permissions']['group_access']).to be_nil
end
end
@@ -730,14 +767,14 @@ describe API::V3::Projects do
get v3_api("/projects/#{project.id}", user)
expect(response).to have_http_status(200)
- expect(json_response['permissions']['project_access']['access_level']).
- to eq(Gitlab::Access::MASTER)
+ expect(json_response['permissions']['project_access']['access_level'])
+ .to eq(Gitlab::Access::MASTER)
expect(json_response['permissions']['group_access']).to be_nil
end
end
context 'group project' do
- let(:project2) { create(:empty_project, group: create(:group)) }
+ let(:project2) { create(:project, group: create(:group)) }
before { project2.group.add_owner(user) }
@@ -746,8 +783,8 @@ describe API::V3::Projects do
expect(response).to have_http_status(200)
expect(json_response['permissions']['project_access']).to be_nil
- expect(json_response['permissions']['group_access']['access_level']).
- to eq(Gitlab::Access::OWNER)
+ expect(json_response['permissions']['group_access']['access_level'])
+ .to eq(Gitlab::Access::OWNER)
end
end
end
@@ -782,7 +819,7 @@ describe API::V3::Projects do
context 'when unauthenticated' do
it_behaves_like 'project events response' do
- let(:project) { create(:empty_project, :public) }
+ let(:project) { create(:project, :public) }
let(:current_user) { nil }
end
end
@@ -814,8 +851,7 @@ describe API::V3::Projects do
describe 'GET /projects/:id/users' do
shared_examples_for 'project users response' do
it 'returns the project users' do
- member = create(:user)
- create(:project_member, :developer, user: member, project: project)
+ member = project.owner
get v3_api("/projects/#{project.id}/users", current_user)
@@ -833,7 +869,7 @@ describe API::V3::Projects do
context 'when unauthenticated' do
it_behaves_like 'project users response' do
- let(:project) { create(:empty_project, :public) }
+ let(:project) { create(:project, :public) }
let(:current_user) { nil }
end
end
@@ -947,11 +983,11 @@ describe API::V3::Projects do
end
describe 'fork management' do
- let(:project_fork_target) { create(:empty_project) }
- let(:project_fork_source) { create(:empty_project, :public) }
+ let(:project_fork_target) { create(:project) }
+ let(:project_fork_source) { create(:project, :public) }
describe 'POST /projects/:id/fork/:forked_from_id' do
- let(:new_project_fork_source) { create(:empty_project, :public) }
+ let(:new_project_fork_source) { create(:project, :public) }
it "is not available for non admin users" do
post v3_api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
@@ -968,6 +1004,14 @@ describe API::V3::Projects do
expect(project_fork_target.forked?).to be_truthy
end
+ it 'refreshes the forks count cachce' do
+ expect(project_fork_source.forks_count).to be_zero
+
+ post v3_api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
+
+ expect(project_fork_source.forks_count).to eq(1)
+ end
+
it 'fails if forked_from project which does not exist' do
post v3_api("/projects/#{project_fork_target.id}/fork/9999", admin)
expect(response).to have_http_status(404)
@@ -992,7 +1036,7 @@ describe API::V3::Projects do
end
context 'when users belong to project group' do
- let(:project_fork_target) { create(:empty_project, group: create(:group)) }
+ let(:project_fork_target) { create(:project, group: create(:group)) }
before do
project_fork_target.group.add_owner user
@@ -1112,16 +1156,16 @@ describe API::V3::Projects do
describe 'GET /projects/search/:query' do
let!(:query) { 'query'}
- let!(:search) { create(:empty_project, name: query, creator_id: user.id, namespace: user.namespace) }
- let!(:pre) { create(:empty_project, name: "pre_#{query}", creator_id: user.id, namespace: user.namespace) }
- let!(:post) { create(:empty_project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) }
- let!(:pre_post) { create(:empty_project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) }
- let!(:unfound) { create(:empty_project, name: 'unfound', creator_id: user.id, namespace: user.namespace) }
- let!(:internal) { create(:empty_project, :internal, name: "internal #{query}") }
- let!(:unfound_internal) { create(:empty_project, :internal, name: 'unfound internal') }
- let!(:public) { create(:empty_project, :public, name: "public #{query}") }
- let!(:unfound_public) { create(:empty_project, :public, name: 'unfound public') }
- let!(:one_dot_two) { create(:empty_project, :public, name: "one.dot.two") }
+ let!(:search) { create(:project, name: query, creator_id: user.id, namespace: user.namespace) }
+ let!(:pre) { create(:project, name: "pre_#{query}", creator_id: user.id, namespace: user.namespace) }
+ let!(:post) { create(:project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) }
+ let!(:pre_post) { create(:project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) }
+ let!(:unfound) { create(:project, name: 'unfound', creator_id: user.id, namespace: user.namespace) }
+ let!(:internal) { create(:project, :internal, name: "internal #{query}") }
+ let!(:unfound_internal) { create(:project, :internal, name: 'unfound internal') }
+ let!(:public) { create(:project, :public, name: "public #{query}") }
+ let!(:unfound_public) { create(:project, :public, name: 'unfound public') }
+ let!(:one_dot_two) { create(:project, :public, name: "one.dot.two") }
shared_examples_for 'project search response' do |args = {}|
it 'returns project search responses' do
@@ -1163,8 +1207,8 @@ describe API::V3::Projects do
before { user4 }
before { project3 }
before { project4 }
- before { project_member3 }
before { project_member2 }
+ before { project_member }
context 'when unauthenticated' do
it 'returns authentication error' do
diff --git a/spec/requests/api/v3/runners_spec.rb b/spec/requests/api/v3/runners_spec.rb
index dbda2cf34c3..a31eb3f1d43 100644
--- a/spec/requests/api/v3/runners_spec.rb
+++ b/spec/requests/api/v3/runners_spec.rb
@@ -5,8 +5,8 @@ describe API::V3::Runners do
let(:user) { create(:user) }
let(:user2) { create(:user) }
- let(:project) { create(:empty_project, creator_id: user.id) }
- let(:project2) { create(:empty_project, creator_id: user.id) }
+ let(:project) { create(:project, creator_id: user.id) }
+ let(:project2) { create(:project, creator_id: user.id) }
let!(:shared_runner) { create(:ci_runner, :shared) }
let!(:unused_specific_runner) { create(:ci_runner) }
@@ -38,7 +38,7 @@ describe API::V3::Runners do
delete v3_api("/runners/#{shared_runner.id}", admin)
expect(response).to have_http_status(200)
- end.to change{ Ci::Runner.shared.count }.by(-1)
+ end.to change { Ci::Runner.shared.count }.by(-1)
end
end
@@ -48,7 +48,7 @@ describe API::V3::Runners do
delete v3_api("/runners/#{unused_specific_runner.id}", admin)
expect(response).to have_http_status(200)
- end.to change{ Ci::Runner.specific.count }.by(-1)
+ end.to change { Ci::Runner.specific.count }.by(-1)
end
it 'deletes used runner' do
@@ -56,7 +56,7 @@ describe API::V3::Runners do
delete v3_api("/runners/#{specific_runner.id}", admin)
expect(response).to have_http_status(200)
- end.to change{ Ci::Runner.specific.count }.by(-1)
+ end.to change { Ci::Runner.specific.count }.by(-1)
end
end
@@ -91,7 +91,7 @@ describe API::V3::Runners do
delete v3_api("/runners/#{specific_runner.id}", user)
expect(response).to have_http_status(200)
- end.to change{ Ci::Runner.specific.count }.by(-1)
+ end.to change { Ci::Runner.specific.count }.by(-1)
end
end
end
@@ -113,7 +113,7 @@ describe API::V3::Runners do
delete v3_api("/projects/#{project.id}/runners/#{two_projects_runner.id}", user)
expect(response).to have_http_status(200)
- end.to change{ project.runners.count }.by(-1)
+ end.to change { project.runners.count }.by(-1)
end
end
@@ -121,7 +121,7 @@ describe API::V3::Runners do
it "does not disable project's runner" do
expect do
delete v3_api("/projects/#{project.id}/runners/#{specific_runner.id}", user)
- end.to change{ project.runners.count }.by(0)
+ end.to change { project.runners.count }.by(0)
expect(response).to have_http_status(403)
end
end
diff --git a/spec/requests/api/v3/services_spec.rb b/spec/requests/api/v3/services_spec.rb
index 3ba62de822a..f0fa48e22df 100644
--- a/spec/requests/api/v3/services_spec.rb
+++ b/spec/requests/api/v3/services_spec.rb
@@ -2,7 +2,7 @@ require "spec_helper"
describe API::V3::Services do
let(:user) { create(:user) }
- let(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) }
+ let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
available_services = Service.available_services_names
available_services.delete('prometheus')
diff --git a/spec/requests/api/v3/settings_spec.rb b/spec/requests/api/v3/settings_spec.rb
index 41d039b7da0..291f6dcc2aa 100644
--- a/spec/requests/api/v3/settings_spec.rb
+++ b/spec/requests/api/v3/settings_spec.rb
@@ -10,7 +10,7 @@ describe API::V3::Settings, 'Settings' do
expect(response).to have_http_status(200)
expect(json_response).to be_an Hash
expect(json_response['default_projects_limit']).to eq(42)
- expect(json_response['signin_enabled']).to be_truthy
+ expect(json_response['password_authentication_enabled']).to be_truthy
expect(json_response['repository_storage']).to eq('default')
expect(json_response['koding_enabled']).to be_falsey
expect(json_response['koding_url']).to be_nil
@@ -28,11 +28,11 @@ describe API::V3::Settings, 'Settings' do
it "updates application settings" do
put v3_api("/application/settings", admin),
- default_projects_limit: 3, signin_enabled: false, repository_storage: 'custom', koding_enabled: true, koding_url: 'http://koding.example.com',
+ default_projects_limit: 3, password_authentication_enabled: false, repository_storage: 'custom', koding_enabled: true, koding_url: 'http://koding.example.com',
plantuml_enabled: true, plantuml_url: 'http://plantuml.example.com'
expect(response).to have_http_status(200)
expect(json_response['default_projects_limit']).to eq(3)
- expect(json_response['signin_enabled']).to be_falsey
+ 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
diff --git a/spec/requests/api/v3/snippets_spec.rb b/spec/requests/api/v3/snippets_spec.rb
index 4f02b7b1a54..9ead3cad8bb 100644
--- a/spec/requests/api/v3/snippets_spec.rb
+++ b/spec/requests/api/v3/snippets_spec.rb
@@ -45,10 +45,10 @@ describe API::V3::Snippets do
expect(json_response.map { |snippet| snippet['id']} ).to contain_exactly(
public_snippet.id,
public_snippet_other.id)
- expect(json_response.map{ |snippet| snippet['web_url']} ).to include(
+ expect(json_response.map { |snippet| snippet['web_url']} ).to include(
"http://localhost/snippets/#{public_snippet.id}",
"http://localhost/snippets/#{public_snippet_other.id}")
- expect(json_response.map{ |snippet| snippet['raw_url']} ).to include(
+ expect(json_response.map { |snippet| snippet['raw_url']} ).to include(
"http://localhost/snippets/#{public_snippet.id}/raw",
"http://localhost/snippets/#{public_snippet_other.id}/raw")
end
@@ -112,21 +112,21 @@ describe API::V3::Snippets do
context 'when the snippet is private' do
it 'creates the snippet' do
- expect { create_snippet(visibility_level: Snippet::PRIVATE) }.
- to change { Snippet.count }.by(1)
+ expect { create_snippet(visibility_level: Snippet::PRIVATE) }
+ .to change { Snippet.count }.by(1)
end
end
context 'when the snippet is public' do
it 'rejects the shippet' do
- expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
- not_to change { Snippet.count }
+ expect { create_snippet(visibility_level: Snippet::PUBLIC) }
+ .not_to change { Snippet.count }
expect(response).to have_http_status(400)
end
it 'creates a spam log' do
- expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
- to change { SpamLog.count }.by(1)
+ expect { create_snippet(visibility_level: Snippet::PUBLIC) }
+ .to change { SpamLog.count }.by(1)
end
end
end
diff --git a/spec/requests/api/v3/system_hooks_spec.rb b/spec/requests/api/v3/system_hooks_spec.rb
index 72c7d14b8ba..ae427541abb 100644
--- a/spec/requests/api/v3/system_hooks_spec.rb
+++ b/spec/requests/api/v3/system_hooks_spec.rb
@@ -31,8 +31,9 @@ describe API::V3::SystemHooks do
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first['url']).to eq(hook.url)
- expect(json_response.first['push_events']).to be true
+ expect(json_response.first['push_events']).to be false
expect(json_response.first['tag_push_events']).to be false
+ expect(json_response.first['repository_update_events']).to be true
end
end
end
diff --git a/spec/requests/api/v3/todos_spec.rb b/spec/requests/api/v3/todos_spec.rb
index 9c2c4d64257..8f5c3fbf8dd 100644
--- a/spec/requests/api/v3/todos_spec.rb
+++ b/spec/requests/api/v3/todos_spec.rb
@@ -1,8 +1,8 @@
require 'spec_helper'
describe API::V3::Todos do
- let(:project_1) { create(:empty_project) }
- let(:project_2) { create(:empty_project) }
+ let(:project_1) { create(:project) }
+ let(:project_2) { create(:project) }
let(:author_1) { create(:user) }
let(:author_2) { create(:user) }
let(:john_doe) { create(:user, username: 'john_doe') }
diff --git a/spec/requests/api/v3/triggers_spec.rb b/spec/requests/api/v3/triggers_spec.rb
index d3de6bf13bc..075de2c0cba 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
@@ -170,7 +171,7 @@ describe API::V3::Triggers do
it 'creates trigger' do
expect do
post v3_api("/projects/#{project.id}/triggers", user)
- end.to change{project.triggers.count}.by(1)
+ end.to change {project.triggers.count}.by(1)
expect(response).to have_http_status(201)
expect(json_response).to be_a(Hash)
@@ -201,7 +202,7 @@ describe API::V3::Triggers do
delete v3_api("/projects/#{project.id}/triggers/#{trigger.token}", user)
expect(response).to have_http_status(200)
- end.to change{project.triggers.count}.by(-1)
+ end.to change {project.triggers.count}.by(-1)
end
it 'responds with 404 Not Found if requesting non-existing trigger' do
diff --git a/spec/requests/api/v3/users_spec.rb b/spec/requests/api/v3/users_spec.rb
index e9c57f7c6c3..227b8d1b0c1 100644
--- a/spec/requests/api/v3/users_spec.rb
+++ b/spec/requests/api/v3/users_spec.rb
@@ -7,6 +7,38 @@ describe API::V3::Users do
let(:email) { create(:email, user: user) }
let(:ldap_blocked_user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') }
+ describe 'GET /users' do
+ context 'when authenticated' do
+ it 'returns an array of users' do
+ get v3_api('/users', user)
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ username = user.username
+ expect(json_response.detect do |user|
+ user['username'] == username
+ end['username']).to eq(username)
+ end
+ end
+
+ context 'when authenticated as user' do
+ it 'does not reveal the `is_admin` flag of the user' do
+ get v3_api('/users', user)
+
+ expect(json_response.first.keys).not_to include 'is_admin'
+ end
+ end
+
+ context 'when authenticated as admin' do
+ it 'reveals the `is_admin` flag of the user' do
+ get v3_api('/users', admin)
+
+ expect(json_response.first.keys).to include 'is_admin'
+ end
+ end
+ end
+
describe 'GET /user/:id/keys' do
before { admin }
@@ -35,6 +67,19 @@ describe API::V3::Users do
expect(json_response.first['title']).to eq(key.title)
end
end
+
+ context "scopes" do
+ let(:user) { admin }
+ let(:path) { "/users/#{user.id}/keys" }
+ let(:api_call) { method(:v3_api) }
+
+ before do
+ user.keys << key
+ user.save
+ end
+
+ include_examples 'allows the "read_user" scope'
+ end
end
describe 'GET /user/:id/emails' do
@@ -187,7 +232,7 @@ describe API::V3::Users do
describe 'GET /users/:id/events' do
let(:user) { create(:user) }
- let(:project) { create(:empty_project) }
+ let(:project) { create(:project) }
let(:note) { create(:note_on_issue, note: 'What an awesome day!', project: project) }
before do
@@ -207,6 +252,31 @@ describe API::V3::Users do
end
context "as a user than can see the event's project" do
+ context 'when the list of events includes push events' do
+ let(:event) { create(:push_event, author: user, project: project) }
+ let!(:payload) { create(:push_event_payload, event: event) }
+ let(:payload_hash) { json_response[0]['push_data'] }
+
+ before do
+ get api("/users/#{user.id}/events?action=pushed", user)
+ end
+
+ it 'responds with HTTP 200 OK' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'includes the push payload as a Hash' do
+ expect(payload_hash).to be_an_instance_of(Hash)
+ end
+
+ it 'includes the push payload details' do
+ expect(payload_hash['commit_count']).to eq(payload.commit_count)
+ expect(payload_hash['action']).to eq(payload.action)
+ expect(payload_hash['ref_type']).to eq(payload.ref_type)
+ expect(payload_hash['commit_to']).to eq(payload.commit_to)
+ end
+ end
+
context 'joined event' do
it 'returns the "joined" event' do
get v3_api("/users/#{user.id}/events", user)
@@ -231,7 +301,7 @@ describe API::V3::Users do
end
context 'when there are multiple events from different projects' do
- let(:second_note) { create(:note_on_issue, project: create(:empty_project)) }
+ let(:second_note) { create(:note_on_issue, project: create(:project)) }
let(:third_note) { create(:note_on_issue, project: project) }
before do
@@ -255,7 +325,7 @@ describe API::V3::Users do
end
it 'returns a 404 error if not found' do
- get v3_api('/users/42/events', user)
+ get v3_api('/users/420/events', user)
expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
@@ -280,5 +350,13 @@ describe API::V3::Users do
expect(json_response['is_admin']).to be_nil
end
+
+ context "scopes" do
+ let(:user) { admin }
+ let(:path) { '/users' }
+ let(:api_call) { method(:v3_api) }
+
+ include_examples 'does not allow the "read_user" scope'
+ end
end
end
diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb
index 63d6d3001ac..48592e12822 100644
--- a/spec/requests/api/variables_spec.rb
+++ b/spec/requests/api/variables_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe API::Variables do
let(:user) { create(:user) }
let(:user2) { create(:user) }
- let!(:project) { create(:empty_project, creator_id: user.id) }
+ let!(:project) { create(:project, creator_id: user.id) }
let!(:master) { create(:project_member, :master, user: user, project: project) }
let!(:developer) { create(:project_member, :developer, user: user2, project: project) }
let!(:variable) { create(:ci_variable, project: project) }
@@ -42,6 +42,7 @@ describe API::Variables do
expect(response).to have_http_status(200)
expect(json_response['value']).to eq(variable.value)
+ expect(json_response['protected']).to eq(variable.protected?)
end
it 'responds with 404 Not Found if requesting non-existing variable' do
@@ -72,18 +73,30 @@ describe API::Variables do
context 'authorized user with proper permissions' do
it 'creates variable' do
expect do
+ post api("/projects/#{project.id}/variables", user), key: 'TEST_VARIABLE_2', value: 'VALUE_2', protected: true
+ end.to change {project.variables.count}.by(1)
+
+ expect(response).to have_http_status(201)
+ expect(json_response['key']).to eq('TEST_VARIABLE_2')
+ expect(json_response['value']).to eq('VALUE_2')
+ expect(json_response['protected']).to be_truthy
+ end
+
+ it 'creates variable with optional attributes' do
+ expect do
post api("/projects/#{project.id}/variables", user), key: 'TEST_VARIABLE_2', value: 'VALUE_2'
- end.to change{project.variables.count}.by(1)
+ end.to change {project.variables.count}.by(1)
expect(response).to have_http_status(201)
expect(json_response['key']).to eq('TEST_VARIABLE_2')
expect(json_response['value']).to eq('VALUE_2')
+ expect(json_response['protected']).to be_falsey
end
it 'does not allow to duplicate variable key' do
expect do
post api("/projects/#{project.id}/variables", user), key: variable.key, value: 'VALUE_2'
- end.to change{project.variables.count}.by(0)
+ end.to change {project.variables.count}.by(0)
expect(response).to have_http_status(400)
end
@@ -112,13 +125,14 @@ describe API::Variables do
initial_variable = project.variables.first
value_before = initial_variable.value
- put api("/projects/#{project.id}/variables/#{variable.key}", user), value: 'VALUE_1_UP'
+ put api("/projects/#{project.id}/variables/#{variable.key}", user), value: 'VALUE_1_UP', protected: true
updated_variable = project.variables.first
expect(response).to have_http_status(200)
expect(value_before).to eq(variable.value)
expect(updated_variable.value).to eq('VALUE_1_UP')
+ expect(updated_variable).to be_protected
end
it 'responds with 404 Not Found if requesting non-existing variable' do
@@ -152,7 +166,7 @@ describe API::Variables do
delete api("/projects/#{project.id}/variables/#{variable.key}", user)
expect(response).to have_http_status(204)
- end.to change{project.variables.count}.by(-1)
+ end.to change {project.variables.count}.by(-1)
end
it 'responds with 404 Not Found if requesting non-existing variable' do
diff --git a/spec/requests/api/version_spec.rb b/spec/requests/api/version_spec.rb
index 8870d48bbc9..7bbf34422b8 100644
--- a/spec/requests/api/version_spec.rb
+++ b/spec/requests/api/version_spec.rb
@@ -6,7 +6,7 @@ describe API::Version do
it 'returns authentication error' do
get api('/version')
- expect(response).to have_http_status(401)
+ expect(response).to have_gitlab_http_status(401)
end
end
@@ -16,7 +16,7 @@ describe API::Version do
it 'returns the version information' do
get api('/version', user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response['version']).to eq(Gitlab::VERSION)
expect(json_response['revision']).to eq(Gitlab::REVISION)
end