diff options
Diffstat (limited to 'spec/requests/api')
-rw-r--r-- | spec/requests/api/badges_spec.rb | 367 | ||||
-rw-r--r-- | spec/requests/api/branches_spec.rb | 21 | ||||
-rw-r--r-- | spec/requests/api/commits_spec.rb | 12 | ||||
-rw-r--r-- | spec/requests/api/discussions_spec.rb | 33 | ||||
-rw-r--r-- | spec/requests/api/group_boards_spec.rb | 54 | ||||
-rw-r--r-- | spec/requests/api/internal_spec.rb | 28 | ||||
-rw-r--r-- | spec/requests/api/issues_spec.rb | 40 | ||||
-rw-r--r-- | spec/requests/api/merge_requests_spec.rb | 144 | ||||
-rw-r--r-- | spec/requests/api/notes_spec.rb | 590 | ||||
-rw-r--r-- | spec/requests/api/pages_domains_spec.rb | 2 | ||||
-rw-r--r-- | spec/requests/api/project_export_spec.rb | 290 | ||||
-rw-r--r-- | spec/requests/api/project_hooks_spec.rb | 6 | ||||
-rw-r--r-- | spec/requests/api/runner_spec.rb | 28 | ||||
-rw-r--r-- | spec/requests/api/v3/issues_spec.rb | 4 | ||||
-rw-r--r-- | spec/requests/api/v3/project_hooks_spec.rb | 6 |
15 files changed, 1084 insertions, 541 deletions
diff --git a/spec/requests/api/badges_spec.rb b/spec/requests/api/badges_spec.rb new file mode 100644 index 00000000000..ae64a9ca162 --- /dev/null +++ b/spec/requests/api/badges_spec.rb @@ -0,0 +1,367 @@ +require 'spec_helper' + +describe API::Badges do + let(:master) { create(:user, username: 'master_user') } + let(:developer) { create(:user) } + let(:access_requester) { create(:user) } + let(:stranger) { create(:user) } + let(:project_group) { create(:group) } + let(:project) { setup_project } + let!(:group) { setup_group } + + shared_context 'source helpers' do + def get_source(source_type) + source_type == 'project' ? project : group + end + end + + shared_examples 'GET /:sources/:id/badges' do |source_type| + include_context 'source helpers' + + let(:source) { get_source(source_type) } + + context "with :sources == #{source_type.pluralize}" do + it_behaves_like 'a 404 response when source is private' do + let(:route) { get api("/#{source_type.pluralize}/#{source.id}/badges", stranger) } + end + + %i[master developer access_requester stranger].each do |type| + context "when authenticated as a #{type}" do + it 'returns 200' do + user = public_send(type) + badges_count = source_type == 'project' ? 3 : 2 + + get api("/#{source_type.pluralize}/#{source.id}/badges", user) + + 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(badges_count) + end + end + end + + it 'avoids N+1 queries' do + # Establish baseline + get api("/#{source_type.pluralize}/#{source.id}/badges", master) + + control = ActiveRecord::QueryRecorder.new do + get api("/#{source_type.pluralize}/#{source.id}/badges", master) + end + + project.add_developer(create(:user)) + + expect do + get api("/#{source_type.pluralize}/#{source.id}/badges", master) + end.not_to exceed_query_limit(control) + end + end + end + + shared_examples 'GET /:sources/:id/badges/:badge_id' do |source_type| + include_context 'source helpers' + + let(:source) { get_source(source_type) } + + context "with :sources == #{source_type.pluralize}" do + it_behaves_like 'a 404 response when source is private' do + let(:route) { get api("/#{source_type.pluralize}/#{source.id}/badges/#{developer.id}", stranger) } + end + + context 'when authenticated as a non-member' do + %i[master developer access_requester stranger].each do |type| + let(:badge) { source.badges.first } + + context "as a #{type}" do + it 'returns 200' do + user = public_send(type) + + get api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", user) + + expect(response).to have_gitlab_http_status(200) + expect(json_response['id']).to eq(badge.id) + expect(json_response['link_url']).to eq(badge.link_url) + expect(json_response['rendered_link_url']).to eq(badge.rendered_link_url) + expect(json_response['image_url']).to eq(badge.image_url) + expect(json_response['rendered_image_url']).to eq(badge.rendered_image_url) + expect(json_response['kind']).to eq source_type + end + end + end + end + end + end + + shared_examples 'POST /:sources/:id/badges' do |source_type| + include_context 'source helpers' + + let(:source) { get_source(source_type) } + let(:example_url) { 'http://www.example.com' } + let(:example_url2) { 'http://www.example1.com' } + + context "with :sources == #{source_type.pluralize}" do + it_behaves_like 'a 404 response when source is private' do + let(:route) do + post api("/#{source_type.pluralize}/#{source.id}/badges", stranger), + link_url: example_url, image_url: example_url2 + end + end + + context 'when authenticated as a non-member or member with insufficient rights' do + %i[access_requester stranger developer].each do |type| + context "as a #{type}" do + it 'returns 403' do + user = public_send(type) + + post api("/#{source_type.pluralize}/#{source.id}/badges", user), + link_url: example_url, image_url: example_url2 + + expect(response).to have_gitlab_http_status(403) + end + end + end + end + + context 'when authenticated as a master/owner' do + it 'creates a new badge' do + expect do + post api("/#{source_type.pluralize}/#{source.id}/badges", master), + link_url: example_url, image_url: example_url2 + + expect(response).to have_gitlab_http_status(201) + end.to change { source.badges.count }.by(1) + + expect(json_response['link_url']).to eq(example_url) + expect(json_response['image_url']).to eq(example_url2) + expect(json_response['kind']).to eq source_type + end + end + + it 'returns 400 when link_url is not given' do + post api("/#{source_type.pluralize}/#{source.id}/badges", master), + link_url: example_url + + expect(response).to have_gitlab_http_status(400) + end + + it 'returns 400 when image_url is not given' do + post api("/#{source_type.pluralize}/#{source.id}/badges", master), + image_url: example_url2 + + expect(response).to have_gitlab_http_status(400) + end + + it 'returns 400 when link_url or image_url is not valid' do + post api("/#{source_type.pluralize}/#{source.id}/badges", master), + link_url: 'whatever', image_url: 'whatever' + + expect(response).to have_gitlab_http_status(400) + end + end + end + + shared_examples 'PUT /:sources/:id/badges/:badge_id' do |source_type| + include_context 'source helpers' + + let(:source) { get_source(source_type) } + + context "with :sources == #{source_type.pluralize}" do + let(:badge) { source.badges.first } + let(:example_url) { 'http://www.example.com' } + let(:example_url2) { 'http://www.example1.com' } + + it_behaves_like 'a 404 response when source is private' do + let(:route) do + put api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", stranger), + link_url: example_url + end + end + + context 'when authenticated as a non-member or member with insufficient rights' do + %i[access_requester stranger developer].each do |type| + context "as a #{type}" do + it 'returns 403' do + user = public_send(type) + + put api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", user), + link_url: example_url + + expect(response).to have_gitlab_http_status(403) + end + end + end + end + + context 'when authenticated as a master/owner' do + it 'updates the member' do + put api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", master), + link_url: example_url, image_url: example_url2 + + expect(response).to have_gitlab_http_status(200) + expect(json_response['link_url']).to eq(example_url) + expect(json_response['image_url']).to eq(example_url2) + expect(json_response['kind']).to eq source_type + end + end + + it 'returns 400 when link_url or image_url is not valid' do + put api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", master), + link_url: 'whatever', image_url: 'whatever' + + expect(response).to have_gitlab_http_status(400) + end + end + end + + shared_examples 'DELETE /:sources/:id/badges/:badge_id' do |source_type| + include_context 'source helpers' + + let(:source) { get_source(source_type) } + + context "with :sources == #{source_type.pluralize}" do + let(:badge) { source.badges.first } + + it_behaves_like 'a 404 response when source is private' do + let(:route) { delete api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", stranger) } + end + + context 'when authenticated as a non-member or member with insufficient rights' do + %i[access_requester developer stranger].each do |type| + context "as a #{type}" do + it 'returns 403' do + user = public_send(type) + + delete api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", user) + + expect(response).to have_gitlab_http_status(403) + end + end + end + end + + context 'when authenticated as a master/owner' do + it 'deletes the badge' do + expect do + delete api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", master) + + expect(response).to have_gitlab_http_status(204) + end.to change { source.badges.count }.by(-1) + end + + it_behaves_like '412 response' do + let(:request) { api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", master) } + end + end + + it 'returns 404 if badge does not exist' do + delete api("/#{source_type.pluralize}/#{source.id}/badges/123", master) + + expect(response).to have_gitlab_http_status(404) + end + end + end + + shared_examples 'GET /:sources/:id/badges/render' do |source_type| + include_context 'source helpers' + + let(:source) { get_source(source_type) } + let(:example_url) { 'http://www.example.com' } + let(:example_url2) { 'http://www.example1.com' } + + context "with :sources == #{source_type.pluralize}" do + it_behaves_like 'a 404 response when source is private' do + let(:route) do + get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=#{example_url}&image_url=#{example_url2}", stranger) + end + end + + context 'when authenticated as a non-member or member with insufficient rights' do + %i[access_requester stranger developer].each do |type| + context "as a #{type}" do + it 'returns 403' do + user = public_send(type) + + get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=#{example_url}&image_url=#{example_url2}", user) + + expect(response).to have_gitlab_http_status(403) + end + end + end + end + + context 'when authenticated as a master/owner' do + it 'gets the rendered badge values' do + get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=#{example_url}&image_url=#{example_url2}", master) + + expect(response).to have_gitlab_http_status(200) + + expect(json_response.keys).to contain_exactly('link_url', 'rendered_link_url', 'image_url', 'rendered_image_url') + expect(json_response['link_url']).to eq(example_url) + expect(json_response['image_url']).to eq(example_url2) + expect(json_response['rendered_link_url']).to eq(example_url) + expect(json_response['rendered_image_url']).to eq(example_url2) + end + end + + it 'returns 400 when link_url is not given' do + get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=#{example_url}", master) + + expect(response).to have_gitlab_http_status(400) + end + + it 'returns 400 when image_url is not given' do + get api("/#{source_type.pluralize}/#{source.id}/badges/render?image_url=#{example_url}", master) + + expect(response).to have_gitlab_http_status(400) + end + + it 'returns 400 when link_url or image_url is not valid' do + get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=whatever&image_url=whatever", master) + + expect(response).to have_gitlab_http_status(400) + end + end + end + + context 'when deleting a badge' do + context 'and the source is a project' do + it 'cannot delete badges owned by the project group' do + delete api("/projects/#{project.id}/badges/#{project_group.badges.first.id}", master) + + expect(response).to have_gitlab_http_status(403) + end + end + end + + describe 'Endpoints' do + %w(project group).each do |source_type| + it_behaves_like 'GET /:sources/:id/badges', source_type + it_behaves_like 'GET /:sources/:id/badges/:badge_id', source_type + it_behaves_like 'GET /:sources/:id/badges/render', source_type + it_behaves_like 'POST /:sources/:id/badges', source_type + it_behaves_like 'PUT /:sources/:id/badges/:badge_id', source_type + it_behaves_like 'DELETE /:sources/:id/badges/:badge_id', source_type + end + end + + def setup_project + create(:project, :public, :access_requestable, creator_id: master.id, namespace: project_group) do |project| + project.add_developer(developer) + project.add_master(master) + project.request_access(access_requester) + project.project_badges << build(:project_badge, project: project) + project.project_badges << build(:project_badge, project: project) + project_group.badges << build(:group_badge, group: group) + end + end + + def setup_group + create(:group, :public, :access_requestable) do |group| + group.add_developer(developer) + group.add_owner(master) + group.request_access(access_requester) + group.badges << build(:group_badge, group: group) + group.badges << build(:group_badge, group: group) + end + end +end diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index e433597f58b..64f51d9843d 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -39,6 +39,27 @@ describe API::Branches do end end + context 'when search parameter is passed' do + context 'and branch exists' do + it 'returns correct branches' do + get api(route, user), per_page: 100, search: branch_name + + searched_branch_names = json_response.map { |branch| branch['name'] } + project_branch_names = project.repository.branch_names.grep(/#{branch_name}/) + + expect(searched_branch_names).to match_array(project_branch_names) + end + end + + context 'and branch does not exist' do + it 'returns an empty array' do + get api(route, user), per_page: 100, search: 'no_such_branch_name_entropy_of_jabadabadu' + + expect(json_response).to eq [] + end + end + end + context 'when unauthenticated', 'and project is public' do before do project.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC) diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index ad3eec88952..852f67db958 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -149,6 +149,18 @@ describe API::Commits do end end + context 'all optional parameter' do + it 'returns all project commits' do + commit_count = project.repository.count_commits(all: true) + + get api("/projects/#{project_id}/repository/commits?all=true", user) + + expect(response).to include_pagination_headers + expect(response.headers['X-Total']).to eq(commit_count.to_s) + expect(response.headers['X-Page']).to eql('1') + end + end + context 'with pagination params' do let(:page) { 1 } let(:per_page) { 5 } diff --git a/spec/requests/api/discussions_spec.rb b/spec/requests/api/discussions_spec.rb new file mode 100644 index 00000000000..4a44b219a67 --- /dev/null +++ b/spec/requests/api/discussions_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe API::Discussions do + let(:user) { create(:user) } + let!(:project) { create(:project, :public, namespace: user.namespace) } + let(:private_user) { create(:user) } + + before do + project.add_reporter(user) + end + + context "when noteable is an Issue" do + let!(:issue) { create(:issue, project: project, author: user) } + let!(:issue_note) { create(:discussion_note_on_issue, noteable: issue, project: project, author: user) } + + it_behaves_like "discussions API", 'projects', 'issues', 'iid' do + let(:parent) { project } + let(:noteable) { issue } + let(:note) { issue_note } + end + end + + context "when noteable is a Snippet" do + let!(:snippet) { create(:project_snippet, project: project, author: user) } + let!(:snippet_note) { create(:discussion_note_on_snippet, noteable: snippet, project: project, author: user) } + + it_behaves_like "discussions API", 'projects', 'snippets', 'id' do + let(:parent) { project } + let(:noteable) { snippet } + let(:note) { snippet_note } + end + end +end diff --git a/spec/requests/api/group_boards_spec.rb b/spec/requests/api/group_boards_spec.rb new file mode 100644 index 00000000000..894c94688ba --- /dev/null +++ b/spec/requests/api/group_boards_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +describe API::GroupBoards do + set(:user) { create(:user) } + set(:non_member) { create(:user) } + set(:guest) { create(:user) } + set(:admin) { create(:user, :admin) } + set(:board_parent) { create(:group, :public) } + + before do + board_parent.add_owner(user) + end + + set(:project) { create(:project, :public, namespace: board_parent ) } + + set(:dev_label) do + create(:group_label, title: 'Development', color: '#FFAABB', group: board_parent) + end + + set(:test_label) do + create(:group_label, title: 'Testing', color: '#FFAACC', group: board_parent) + end + + set(:ux_label) do + create(:group_label, title: 'UX', color: '#FF0000', group: board_parent) + end + + set(:dev_list) do + create(:list, label: dev_label, position: 1) + end + + set(:test_list) do + create(:list, label: test_label, position: 2) + end + + set(:milestone) { create(:milestone, group: board_parent) } + set(:board_label) { create(:group_label, group: board_parent) } + + set(:board) { create(:board, group: board_parent, lists: [dev_list, test_list]) } + + it_behaves_like 'group and project boards', "/groups/:id/boards", false + + describe 'POST /groups/:id/boards/lists' do + let(:url) { "/groups/#{board_parent.id}/boards/#{board.id}/lists" } + + it 'does not create lists for child project labels' do + project_label = create(:label, project: project) + + post api(url, user), label_id: project_label.id + + expect(response).to have_gitlab_http_status(400) + end + end +end diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb index 827f4c04324..ca0aac87ba9 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal_spec.rb @@ -335,21 +335,8 @@ describe API::Internal do end context "git push" do - context "gitaly disabled", :disable_gitaly do - it "has the correct payload" do - push(key, project) - - expect(response).to have_gitlab_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 + context 'project as namespace/project' do + it do push(key, project) expect(response).to have_gitlab_http_status(200) @@ -365,17 +352,6 @@ describe API::Internal do expect(user).not_to have_an_activity_record end end - - context 'project as namespace/project' do - it do - push(key, project) - - expect(response).to have_gitlab_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 end diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index e6d7b9fde02..6614e8cea43 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -163,6 +163,42 @@ describe API::Issues do expect(first_issue['id']).to eq(issue.id) end + context 'filtering before a specific date' do + let!(:issue2) { create(:issue, project: project, author: user, created_at: Date.new(2000, 1, 1), updated_at: Date.new(2000, 1, 1)) } + + it 'returns issues created before a specific date' do + get api('/issues?created_before=2000-01-02T00:00:00.060Z', user) + + expect(json_response.size).to eq(1) + expect(first_issue['id']).to eq(issue2.id) + end + + it 'returns issues updated before a specific date' do + get api('/issues?updated_before=2000-01-02T00:00:00.060Z', user) + + expect(json_response.size).to eq(1) + expect(first_issue['id']).to eq(issue2.id) + end + end + + context 'filtering after a specific date' do + let!(:issue2) { create(:issue, project: project, author: user, created_at: 1.week.from_now, updated_at: 1.week.from_now) } + + it 'returns issues created after a specific date' do + get api("/issues?created_after=#{issue2.created_at}", user) + + expect(json_response.size).to eq(1) + expect(first_issue['id']).to eq(issue2.id) + end + + it 'returns issues updated after a specific date' do + get api("/issues?updated_after=#{issue2.updated_at}", user) + + expect(json_response.size).to eq(1) + expect(first_issue['id']).to eq(issue2.id) + end + end + it 'returns an array of labeled issues' do get api("/issues", user), labels: label.title @@ -1417,7 +1453,7 @@ describe API::Issues do context 'when source project does not exist' do it 'returns 404 when trying to move an issue' do - post api("/projects/12345/issues/#{issue.iid}/move", user), + post api("/projects/0/issues/#{issue.iid}/move", user), to_project_id: target_project.id expect(response).to have_gitlab_http_status(404) @@ -1428,7 +1464,7 @@ describe API::Issues do context 'when target project does not exist' do it 'returns 404 when trying to move an issue' do post api("/projects/#{project.id}/issues/#{issue.iid}/move", user), - to_project_id: 12345 + to_project_id: 0 expect(response).to have_gitlab_http_status(404) end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 14dd9da119d..3764aec0c71 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -9,6 +9,7 @@ describe API::MergeRequests do let(:non_member) { create(:user) } let!(:project) { create(:project, :public, :repository, creator: user, namespace: user.namespace, only_allow_merge_if_pipeline_succeeds: false) } let(:milestone) { create(:milestone, title: '1.0.0', project: project) } + let(:pipeline) { create(:ci_empty_pipeline) } let(:milestone1) { create(:milestone, title: '0.9', project: project) } let!(:merge_request) { create(:merge_request, :simple, milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: "Test", created_at: base_time) } let!(:merge_request_closed) { create(:merge_request, state: "closed", milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: "Closed test", created_at: base_time + 1.second) } @@ -151,6 +152,62 @@ describe API::MergeRequests do expect(json_response.first['id']).to eq(merge_request3.id) end + context 'source_branch param' do + it 'returns merge requests with the given source branch' do + get api('/merge_requests', user), source_branch: merge_request_closed.source_branch, state: 'all' + + expect(json_response.length).to eq(2) + expect(json_response.map { |mr| mr['id'] }) + .to contain_exactly(merge_request_closed.id, merge_request_merged.id) + end + end + + context 'target_branch param' do + it 'returns merge requests with the given target branch' do + get api('/merge_requests', user), target_branch: merge_request_closed.target_branch, state: 'all' + + expect(json_response.length).to eq(2) + expect(json_response.map { |mr| mr['id'] }) + .to contain_exactly(merge_request_closed.id, merge_request_merged.id) + end + end + + it 'returns merge requests created before a specific date' do + merge_request2 = create(:merge_request, :simple, source_project: project, target_project: project, source_branch: 'feature_1', created_at: Date.new(2000, 1, 1)) + + get api('/merge_requests?created_before=2000-01-02T00:00:00.060Z', user) + + expect(json_response.size).to eq(1) + expect(json_response.first['id']).to eq(merge_request2.id) + end + + it 'returns merge requests created after a specific date' do + merge_request2 = create(:merge_request, :simple, source_project: project, target_project: project, source_branch: 'feature_1', created_at: 1.week.from_now) + + get api("/merge_requests?created_after=#{merge_request2.created_at}", user) + + expect(json_response.size).to eq(1) + expect(json_response.first['id']).to eq(merge_request2.id) + end + + it 'returns merge requests updated before a specific date' do + merge_request2 = create(:merge_request, :simple, source_project: project, target_project: project, source_branch: 'feature_1', updated_at: Date.new(2000, 1, 1)) + + get api('/merge_requests?updated_before=2000-01-02T00:00:00.060Z', user) + + expect(json_response.size).to eq(1) + expect(json_response.first['id']).to eq(merge_request2.id) + end + + it 'returns merge requests updated after a specific date' do + merge_request2 = create(:merge_request, :simple, source_project: project, target_project: project, source_branch: 'feature_1', updated_at: 1.week.from_now) + + get api("/merge_requests?updated_after=#{merge_request2.updated_at}", user) + + expect(json_response.size).to eq(1) + expect(json_response.first['id']).to eq(merge_request2.id) + end + context 'search params' do before do merge_request.update(title: 'Search title', description: 'Search description') @@ -426,6 +483,26 @@ describe API::MergeRequests do expect(response_dates).to eq(response_dates.sort) end end + + context 'source_branch param' do + it 'returns merge requests with the given source branch' do + get api('/merge_requests', user), source_branch: merge_request_closed.source_branch, state: 'all' + + expect(json_response.length).to eq(2) + expect(json_response.map { |mr| mr['id'] }) + .to contain_exactly(merge_request_closed.id, merge_request_merged.id) + end + end + + context 'target_branch param' do + it 'returns merge requests with the given target branch' do + get api('/merge_requests', user), target_branch: merge_request_closed.target_branch, state: 'all' + + expect(json_response.length).to eq(2) + expect(json_response.map { |mr| mr['id'] }) + .to contain_exactly(merge_request_closed.id, merge_request_merged.id) + end + end end end @@ -460,6 +537,45 @@ describe API::MergeRequests do expect(json_response['changes_count']).to eq(merge_request.merge_request_diff.real_size) end + context 'merge_request_metrics' do + before do + merge_request.metrics.update!(merged_by: user, + latest_closed_by: user, + latest_closed_at: 1.hour.ago, + merged_at: 2.hours.ago, + pipeline: pipeline, + latest_build_started_at: 3.hours.ago, + latest_build_finished_at: 1.hour.ago, + first_deployed_to_production_at: 3.minutes.ago) + end + + it 'has fields from merge request metrics' do + get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user) + + expect(json_response).to include('merged_by', + 'merged_at', + 'closed_by', + 'closed_at', + 'latest_build_started_at', + 'latest_build_finished_at', + 'first_deployed_to_production_at', + 'pipeline') + end + + it 'returns correct values' do + get api("/projects/#{project.id}/merge_requests/#{merge_request.reload.iid}", user) + + expect(json_response['merged_by']['id']).to eq(merge_request.metrics.merged_by_id) + expect(Time.parse json_response['merged_at']).to be_like_time(merge_request.metrics.merged_at) + expect(json_response['closed_by']['id']).to eq(merge_request.metrics.latest_closed_by_id) + expect(Time.parse json_response['closed_at']).to be_like_time(merge_request.metrics.latest_closed_at) + expect(json_response['pipeline']['id']).to eq(merge_request.metrics.pipeline_id) + expect(Time.parse json_response['latest_build_started_at']).to be_like_time(merge_request.metrics.latest_build_started_at) + expect(Time.parse json_response['latest_build_finished_at']).to be_like_time(merge_request.metrics.latest_build_finished_at) + expect(Time.parse json_response['first_deployed_to_production_at']).to be_like_time(merge_request.metrics.first_deployed_to_production_at) + end + end + 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_gitlab_http_status(404) @@ -500,6 +616,25 @@ describe API::MergeRequests do expect(json_response['changes_count']).to eq('5+') end end + + context 'for forked projects' do + let(:user2) { create(:user) } + let(:project) { create(:project, :public, :repository) } + let(:forked_project) { fork_project(project, user2, repository: true) } + let(:merge_request) do + create(:merge_request, + source_project: forked_project, + target_project: project, + source_branch: 'fixes', + allow_maintainer_to_push: true) + end + + it 'includes the `allow_maintainer_to_push` field' do + get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user) + + expect(json_response['allow_maintainer_to_push']).to be_truthy + end + end end describe 'GET /projects/:id/merge_requests/:merge_request_iid/participants' do @@ -699,6 +834,7 @@ describe API::MergeRequests do context 'forked projects' do let!(:user2) { create(:user) } + let(:project) { create(:project, :public, :repository) } let!(:forked_project) { fork_project(project, user2, repository: true) } let!(:unrelated_project) { create(:project, namespace: create(:user).namespace, creator_id: user2.id) } @@ -756,6 +892,14 @@ describe API::MergeRequests do expect(response).to have_gitlab_http_status(400) end + it 'allows setting `allow_maintainer_to_push`' do + post api("/projects/#{forked_project.id}/merge_requests", user2), + title: 'Test merge_request', source_branch: "feature_conflict", target_branch: "master", + author: user2, target_project_id: project.id, allow_maintainer_to_push: true + expect(response).to have_gitlab_http_status(201) + expect(json_response['allow_maintainer_to_push']).to be_truthy + end + context 'when target_branch and target_project_id is specified' do let(:params) do { title: 'Test merge_request', diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb index 981c9c27325..dd568c24c72 100644 --- a/spec/requests/api/notes_spec.rb +++ b/spec/requests/api/notes_spec.rb @@ -3,117 +3,86 @@ require 'spec_helper' describe API::Notes do let(:user) { create(:user) } 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) } - let!(:issue_note) { create(:note, noteable: issue, project: project, author: user) } - let!(:merge_request_note) { create(:note, noteable: merge_request, project: project, author: user) } - let!(:snippet_note) { create(:note, noteable: snippet, project: project, author: user) } - - # For testing the cross-reference of a private issue in a public issue let(:private_user) { create(:user) } - let(:private_project) do - create(:project, namespace: private_user.namespace) - .tap { |p| p.add_master(private_user) } - end - let(:private_issue) { create(:issue, project: private_project) } - - let(:ext_proj) { create(:project, :public) } - let(:ext_issue) { create(:issue, project: ext_proj) } - - let!(:cross_reference_note) do - create :note, - noteable: ext_issue, project: ext_proj, - note: "mentioned in issue #{private_issue.to_reference(ext_proj)}", - system: true - end before do project.add_reporter(user) end - describe "GET /projects/:id/noteable/:noteable_id/notes" do - context "when noteable is an Issue" do - context 'sorting' do - before do - create_list(:note, 3, noteable: issue, project: project, author: user) - end - - it 'sorts by created_at in descending order by default' do - get api("/projects/#{project.id}/issues/#{issue.iid}/notes", user) - - response_dates = json_response.map { |noteable| noteable['created_at'] } - - expect(json_response.length).to eq(4) - expect(response_dates).to eq(response_dates.sort.reverse) - end - - it 'sorts by ascending order when requested' do - get api("/projects/#{project.id}/issues/#{issue.iid}/notes?sort=asc", user) - - response_dates = json_response.map { |noteable| noteable['created_at'] } - - expect(json_response.length).to eq(4) - expect(response_dates).to eq(response_dates.sort) - end - - it 'sorts by updated_at in descending order when requested' do - get api("/projects/#{project.id}/issues/#{issue.iid}/notes?order_by=updated_at", user) - - response_dates = json_response.map { |noteable| noteable['updated_at'] } + context "when noteable is an Issue" do + let!(:issue) { create(:issue, project: project, author: user) } + let!(:issue_note) { create(:note, noteable: issue, project: project, author: user) } - expect(json_response.length).to eq(4) - expect(response_dates).to eq(response_dates.sort.reverse) - end + it_behaves_like "noteable API", 'projects', 'issues', 'iid' do + let(:parent) { project } + let(:noteable) { issue } + let(:note) { issue_note } + end - it 'sorts by updated_at in ascending order when requested' do - get api("/projects/#{project.id}/issues/#{issue.iid}/notes??order_by=updated_at&sort=asc", user) + context 'when user does not have access to create noteable' do + let(:private_issue) { create(:issue, project: create(:project, :private)) } - response_dates = json_response.map { |noteable| noteable['updated_at'] } + ## + # We are posting to project user has access to, but we use issue id + # from a different project, see #15577 + # + before do + post api("/projects/#{private_issue.project.id}/issues/#{private_issue.iid}/notes", user), + body: 'Hi!' + end - expect(json_response.length).to eq(4) - expect(response_dates).to eq(response_dates.sort) - end + it 'responds with resource not found error' do + expect(response.status).to eq 404 end - it "returns an array of issue notes" do - get api("/projects/#{project.id}/issues/#{issue.iid}/notes", user) + it 'does not create new note' do + expect(private_issue.notes.reload).to be_empty + end + end - expect(response).to have_gitlab_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.first['body']).to eq(issue_note.note) + context "when referencing other project" do + # For testing the cross-reference of a private issue in a public project + let(:private_project) do + create(:project, namespace: private_user.namespace) + .tap { |p| p.add_master(private_user) } end + let(:private_issue) { create(:issue, project: private_project) } - it "returns a 404 error when issue id not found" do - get api("/projects/#{project.id}/issues/12345/notes", user) + let(:ext_proj) { create(:project, :public) } + let(:ext_issue) { create(:issue, project: ext_proj) } - expect(response).to have_gitlab_http_status(404) + let!(:cross_reference_note) do + create :note, + noteable: ext_issue, project: ext_proj, + note: "mentioned in issue #{private_issue.to_reference(ext_proj)}", + system: true end - context "and current user cannot view the notes" do - it "returns an empty array" do - get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes", user) - - expect(response).to have_gitlab_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response).to be_empty - end + describe "GET /projects/:id/noteable/:noteable_id/notes" do + context "current user cannot view the notes" do + it "returns an empty array" do + get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes", user) - context "and issue is confidential" do - before do - ext_issue.update_attributes(confidential: true) + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response).to be_empty end - it "returns 404" do - get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes", user) + context "issue is confidential" do + before do + ext_issue.update_attributes(confidential: true) + end - expect(response).to have_gitlab_http_status(404) + it "returns 404" do + get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes", user) + + expect(response).to have_gitlab_http_status(404) + end end end - context "and current user can view the note" do + context "current user can view the note" do it "returns an empty array" do get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes", private_user) @@ -124,172 +93,29 @@ describe API::Notes do end end end - end - - context "when noteable is a Snippet" do - context 'sorting' do - before do - create_list(:note, 3, noteable: snippet, project: project, author: user) - end - - it 'sorts by created_at in descending order by default' do - get api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user) - - response_dates = json_response.map { |noteable| noteable['created_at'] } - - expect(json_response.length).to eq(4) - expect(response_dates).to eq(response_dates.sort.reverse) - end - - it 'sorts by ascending order when requested' do - get api("/projects/#{project.id}/snippets/#{snippet.id}/notes?sort=asc", user) - - response_dates = json_response.map { |noteable| noteable['created_at'] } - - expect(json_response.length).to eq(4) - expect(response_dates).to eq(response_dates.sort) - end - - it 'sorts by updated_at in descending order when requested' do - get api("/projects/#{project.id}/snippets/#{snippet.id}/notes?order_by=updated_at", user) - - response_dates = json_response.map { |noteable| noteable['updated_at'] } - - expect(json_response.length).to eq(4) - expect(response_dates).to eq(response_dates.sort.reverse) - end - it 'sorts by updated_at in ascending order when requested' do - get api("/projects/#{project.id}/snippets/#{snippet.id}/notes??order_by=updated_at&sort=asc", user) + describe "GET /projects/:id/noteable/:noteable_id/notes/:note_id" do + context "current user cannot view the notes" do + it "returns a 404 error" do + get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes/#{cross_reference_note.id}", user) - response_dates = json_response.map { |noteable| noteable['updated_at'] } - - expect(json_response.length).to eq(4) - expect(response_dates).to eq(response_dates.sort) - end - end - it "returns an array of snippet notes" do - get api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user) - - expect(response).to have_gitlab_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.first['body']).to eq(snippet_note.note) - end - - it "returns a 404 error when snippet id not found" do - get api("/projects/#{project.id}/snippets/42/notes", user) - - expect(response).to have_gitlab_http_status(404) - end - - it "returns 404 when not authorized" do - get api("/projects/#{project.id}/snippets/#{snippet.id}/notes", private_user) - - expect(response).to have_gitlab_http_status(404) - end - end - - context "when noteable is a Merge Request" do - context 'sorting' do - before do - create_list(:note, 3, noteable: merge_request, project: project, author: user) - end - - it 'sorts by created_at in descending order by default' do - get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/notes", user) - - response_dates = json_response.map { |noteable| noteable['created_at'] } - - expect(json_response.length).to eq(4) - expect(response_dates).to eq(response_dates.sort.reverse) - end - - it 'sorts by ascending order when requested' do - get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/notes?sort=asc", user) - - response_dates = json_response.map { |noteable| noteable['created_at'] } - - expect(json_response.length).to eq(4) - expect(response_dates).to eq(response_dates.sort) - end - - it 'sorts by updated_at in descending order when requested' do - get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/notes?order_by=updated_at", user) - - response_dates = json_response.map { |noteable| noteable['updated_at'] } - - expect(json_response.length).to eq(4) - expect(response_dates).to eq(response_dates.sort.reverse) - end - - it 'sorts by updated_at in ascending order when requested' do - get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/notes??order_by=updated_at&sort=asc", user) - - response_dates = json_response.map { |noteable| noteable['updated_at'] } - - expect(json_response.length).to eq(4) - expect(response_dates).to eq(response_dates.sort) - end - end - it "returns an array of merge_requests notes" do - get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/notes", user) - - expect(response).to have_gitlab_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.first['body']).to eq(merge_request_note.note) - end - - it "returns a 404 error if merge request id not found" do - get api("/projects/#{project.id}/merge_requests/4444/notes", user) - - expect(response).to have_gitlab_http_status(404) - end - - it "returns 404 when not authorized" do - get api("/projects/#{project.id}/merge_requests/4444/notes", private_user) - - expect(response).to have_gitlab_http_status(404) - end - end - end - - describe "GET /projects/:id/noteable/:noteable_id/notes/:note_id" do - context "when noteable is an Issue" do - it "returns an issue note by id" do - get api("/projects/#{project.id}/issues/#{issue.iid}/notes/#{issue_note.id}", user) - - expect(response).to have_gitlab_http_status(200) - expect(json_response['body']).to eq(issue_note.note) - end - - it "returns a 404 error if issue note not found" do - get api("/projects/#{project.id}/issues/#{issue.iid}/notes/12345", user) - - expect(response).to have_gitlab_http_status(404) - end - - context "and current user cannot view the note" do - it "returns a 404 error" do - get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes/#{cross_reference_note.id}", user) - - expect(response).to have_gitlab_http_status(404) - end - - context "when issue is confidential" do - before do - issue.update_attributes(confidential: true) + expect(response).to have_gitlab_http_status(404) end - it "returns 404" do - get api("/projects/#{project.id}/issues/#{issue.iid}/notes/#{issue_note.id}", private_user) + context "when issue is confidential" do + before do + issue.update_attributes(confidential: true) + end - expect(response).to have_gitlab_http_status(404) + it "returns 404" do + get api("/projects/#{project.id}/issues/#{issue.iid}/notes/#{issue_note.id}", private_user) + + expect(response).to have_gitlab_http_status(404) + end end end - context "and current user can view the note" do + context "current user can view the note" do it "returns an issue note by id" do get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes/#{cross_reference_note.id}", private_user) @@ -299,132 +125,27 @@ describe API::Notes do end end end - - context "when noteable is a Snippet" do - it "returns a snippet note by id" do - get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/#{snippet_note.id}", user) - - expect(response).to have_gitlab_http_status(200) - expect(json_response['body']).to eq(snippet_note.note) - end - - it "returns a 404 error if snippet note not found" do - get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/12345", user) - - expect(response).to have_gitlab_http_status(404) - end - end end - describe "POST /projects/:id/noteable/:noteable_id/notes" do - context "when noteable is an Issue" do - it "creates a new issue note" do - post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user), body: 'hi!' - - expect(response).to have_gitlab_http_status(201) - expect(json_response['body']).to eq('hi!') - expect(json_response['author']['username']).to eq(user.username) - end - - it "returns a 400 bad request error if body not given" do - post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user) - - expect(response).to have_gitlab_http_status(400) - end - - it "returns a 401 unauthorized error if user not authenticated" do - post api("/projects/#{project.id}/issues/#{issue.iid}/notes"), body: 'hi!' - - expect(response).to have_gitlab_http_status(401) - end - - context 'when an admin or owner makes the request' do - it 'accepts the creation date to be set' do - creation_time = 2.weeks.ago - post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user), - body: 'hi!', created_at: creation_time - - expect(response).to have_gitlab_http_status(201) - expect(json_response['body']).to eq('hi!') - expect(json_response['author']['username']).to eq(user.username) - expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time) - end - end - - context 'when the user is posting an award emoji on an issue created by someone else' do - let(:issue2) { create(:issue, project: project) } - - it 'creates a new issue note' do - post api("/projects/#{project.id}/issues/#{issue2.iid}/notes", user), body: ':+1:' - - expect(response).to have_gitlab_http_status(201) - expect(json_response['body']).to eq(':+1:') - end - end - - context 'when the user is posting an award emoji on his/her own issue' do - it 'creates a new issue note' do - post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user), body: ':+1:' - - expect(response).to have_gitlab_http_status(201) - expect(json_response['body']).to eq(':+1:') - end - end - end - - context "when noteable is a Snippet" do - it "creates a new snippet note" do - post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user), body: 'hi!' + context "when noteable is a Snippet" do + let!(:snippet) { create(:project_snippet, project: project, author: user) } + let!(:snippet_note) { create(:note, noteable: snippet, project: project, author: user) } - expect(response).to have_gitlab_http_status(201) - expect(json_response['body']).to eq('hi!') - expect(json_response['author']['username']).to eq(user.username) - end - - it "returns a 400 bad request error if body not given" do - post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user) - - expect(response).to have_gitlab_http_status(400) - end - - it "returns a 401 unauthorized error if user not authenticated" do - post api("/projects/#{project.id}/snippets/#{snippet.id}/notes"), body: 'hi!' - - expect(response).to have_gitlab_http_status(401) - end + it_behaves_like "noteable API", 'projects', 'snippets', 'id' do + let(:parent) { project } + let(:noteable) { snippet } + let(:note) { snippet_note } end + end - context 'when user does not have access to read the noteable' do - it 'responds with 404' do - 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), - body: 'Foo' - - expect(response).to have_gitlab_http_status(404) - end - end - - context 'when user does not have access to create noteable' do - let(:private_issue) { create(:issue, project: create(:project, :private)) } - - ## - # We are posting to project user has access to, but we use issue id - # from a different project, see #15577 - # - before do - post api("/projects/#{private_issue.project.id}/issues/#{private_issue.iid}/notes", user), - body: 'Hi!' - end - - it 'responds with resource not found error' do - expect(response.status).to eq 404 - end + context "when noteable is a Merge Request" do + let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) } + let!(:merge_request_note) { create(:note, noteable: merge_request, project: project, author: user) } - it 'does not create new note' do - expect(private_issue.notes.reload).to be_empty - end + it_behaves_like "noteable API", 'projects', 'merge_requests', 'iid' do + let(:parent) { project } + let(:noteable) { merge_request } + let(:note) { merge_request_note } end context 'when the merge request discussion is locked' do @@ -461,145 +182,4 @@ describe API::Notes do end end end - - describe "POST /projects/:id/noteable/:noteable_id/notes to test observer on create" do - it "creates an activity event when an issue note is created" do - expect(Event).to receive(:create!) - - post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user), body: 'hi!' - end - end - - describe 'PUT /projects/:id/noteable/:noteable_id/notes/:note_id' do - context 'when noteable is an Issue' do - it 'returns modified note' do - put api("/projects/#{project.id}/issues/#{issue.iid}/"\ - "notes/#{issue_note.id}", user), body: 'Hello!' - - expect(response).to have_gitlab_http_status(200) - expect(json_response['body']).to eq('Hello!') - end - - it 'returns a 404 error when note id not found' do - put api("/projects/#{project.id}/issues/#{issue.iid}/notes/12345", user), - body: 'Hello!' - - expect(response).to have_gitlab_http_status(404) - end - - it 'returns a 400 bad request error if body not given' do - put api("/projects/#{project.id}/issues/#{issue.iid}/"\ - "notes/#{issue_note.id}", user) - - expect(response).to have_gitlab_http_status(400) - end - end - - context 'when noteable is a Snippet' do - it 'returns modified note' do - put api("/projects/#{project.id}/snippets/#{snippet.id}/"\ - "notes/#{snippet_note.id}", user), body: 'Hello!' - - expect(response).to have_gitlab_http_status(200) - expect(json_response['body']).to eq('Hello!') - end - - it 'returns a 404 error when note id not found' do - put api("/projects/#{project.id}/snippets/#{snippet.id}/"\ - "notes/12345", user), body: "Hello!" - - expect(response).to have_gitlab_http_status(404) - end - end - - context 'when noteable is a Merge Request' do - it 'returns modified note' do - put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/"\ - "notes/#{merge_request_note.id}", user), body: 'Hello!' - - expect(response).to have_gitlab_http_status(200) - expect(json_response['body']).to eq('Hello!') - end - - it 'returns a 404 error when note id not found' do - put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/"\ - "notes/12345", user), body: "Hello!" - - expect(response).to have_gitlab_http_status(404) - end - end - end - - describe 'DELETE /projects/:id/noteable/:noteable_id/notes/:note_id' do - context 'when noteable is an Issue' do - it 'deletes a note' do - delete api("/projects/#{project.id}/issues/#{issue.iid}/"\ - "notes/#{issue_note.id}", user) - - expect(response).to have_gitlab_http_status(204) - # Check if note is really deleted - delete api("/projects/#{project.id}/issues/#{issue.iid}/"\ - "notes/#{issue_note.id}", user) - expect(response).to have_gitlab_http_status(404) - end - - it 'returns a 404 error when note id not found' do - delete api("/projects/#{project.id}/issues/#{issue.iid}/notes/12345", user) - - expect(response).to have_gitlab_http_status(404) - end - - it_behaves_like '412 response' do - let(:request) { api("/projects/#{project.id}/issues/#{issue.iid}/notes/#{issue_note.id}", user) } - end - end - - context 'when noteable is a Snippet' do - it 'deletes a note' do - delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\ - "notes/#{snippet_note.id}", user) - - expect(response).to have_gitlab_http_status(204) - # Check if note is really deleted - delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\ - "notes/#{snippet_note.id}", user) - expect(response).to have_gitlab_http_status(404) - end - - it 'returns a 404 error when note id not found' do - delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\ - "notes/12345", user) - - expect(response).to have_gitlab_http_status(404) - end - - it_behaves_like '412 response' do - let(:request) { api("/projects/#{project.id}/snippets/#{snippet.id}/notes/#{snippet_note.id}", user) } - end - end - - context 'when noteable is a Merge Request' do - it 'deletes a note' do - delete api("/projects/#{project.id}/merge_requests/"\ - "#{merge_request.iid}/notes/#{merge_request_note.id}", user) - - expect(response).to have_gitlab_http_status(204) - # Check if note is really deleted - delete api("/projects/#{project.id}/merge_requests/"\ - "#{merge_request.iid}/notes/#{merge_request_note.id}", user) - expect(response).to have_gitlab_http_status(404) - end - - it 'returns a 404 error when note id not found' do - delete api("/projects/#{project.id}/merge_requests/"\ - "#{merge_request.iid}/notes/12345", user) - - expect(response).to have_gitlab_http_status(404) - end - - it_behaves_like '412 response' do - let(:request) { api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/notes/#{merge_request_note.id}", user) } - end - end - end end diff --git a/spec/requests/api/pages_domains_spec.rb b/spec/requests/api/pages_domains_spec.rb index 025165622b7..dc3a116c060 100644 --- a/spec/requests/api/pages_domains_spec.rb +++ b/spec/requests/api/pages_domains_spec.rb @@ -16,7 +16,7 @@ describe API::PagesDomains do let(:route) { "/projects/#{project.id}/pages/domains" } let(:route_domain) { "/projects/#{project.id}/pages/domains/#{pages_domain.domain}" } - let(:route_domain_path) { "/projects/#{project.path_with_namespace.gsub('/', '%2F')}/pages/domains/#{pages_domain.domain}" } + let(:route_domain_path) { "/projects/#{project.full_path.gsub('/', '%2F')}/pages/domains/#{pages_domain.domain}" } let(:route_secure_domain) { "/projects/#{project.id}/pages/domains/#{pages_domain_secure.domain}" } let(:route_expired_domain) { "/projects/#{project.id}/pages/domains/#{pages_domain_expired.domain}" } let(:route_vacant_domain) { "/projects/#{project.id}/pages/domains/www.vacant-domain.test" } diff --git a/spec/requests/api/project_export_spec.rb b/spec/requests/api/project_export_spec.rb new file mode 100644 index 00000000000..fbed527963f --- /dev/null +++ b/spec/requests/api/project_export_spec.rb @@ -0,0 +1,290 @@ +require 'spec_helper' + +describe API::ProjectExport do + set(:project) { create(:project) } + set(:project_none) { create(:project) } + set(:project_started) { create(:project) } + set(:project_finished) { create(:project) } + set(:user) { create(:user) } + set(:admin) { create(:admin) } + + let(:path) { "/projects/#{project.id}/export" } + let(:path_none) { "/projects/#{project_none.id}/export" } + let(:path_started) { "/projects/#{project_started.id}/export" } + let(:path_finished) { "/projects/#{project_finished.id}/export" } + + let(:download_path) { "/projects/#{project.id}/export/download" } + let(:download_path_none) { "/projects/#{project_none.id}/export/download" } + let(:download_path_started) { "/projects/#{project_started.id}/export/download" } + let(:download_path_finished) { "/projects/#{project_finished.id}/export/download" } + + let(:export_path) { "#{Dir.tmpdir}/project_export_spec" } + + before do + allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) + + # simulate exporting work directory + FileUtils.mkdir_p File.join(project_started.export_path, 'securerandom-hex') + + # simulate exported + FileUtils.mkdir_p project_finished.export_path + FileUtils.touch File.join(project_finished.export_path, '_export.tar.gz') + end + + after do + FileUtils.rm_rf(export_path, secure: true) + end + + shared_examples_for 'when project export is disabled' do + before do + stub_application_setting(project_export_enabled?: false) + end + + it_behaves_like '404 response' + end + + describe 'GET /projects/:project_id/export' do + shared_examples_for 'get project export status not found' do + it_behaves_like '404 response' do + let(:request) { get api(path, user) } + end + end + + shared_examples_for 'get project export status denied' do + it_behaves_like '403 response' do + let(:request) { get api(path, user) } + end + end + + shared_examples_for 'get project export status ok' do + it 'is none' do + get api(path_none, user) + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/project/export_status') + expect(json_response['export_status']).to eq('none') + end + + it 'is started' do + get api(path_started, user) + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/project/export_status') + expect(json_response['export_status']).to eq('started') + end + + it 'is finished' do + get api(path_finished, user) + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/project/export_status') + expect(json_response['export_status']).to eq('finished') + end + end + + it_behaves_like 'when project export is disabled' do + let(:request) { get api(path, admin) } + end + + context 'when project export is enabled' do + context 'when user is an admin' do + let(:user) { admin } + + it_behaves_like 'get project export status ok' + end + + context 'when user is a master' do + before do + project.add_master(user) + project_none.add_master(user) + project_started.add_master(user) + project_finished.add_master(user) + end + + it_behaves_like 'get project export status ok' + end + + context 'when user is a developer' do + before do + project.add_developer(user) + end + + it_behaves_like 'get project export status denied' + end + + context 'when user is a reporter' do + before do + project.add_reporter(user) + end + + it_behaves_like 'get project export status denied' + end + + context 'when user is a guest' do + before do + project.add_guest(user) + end + + it_behaves_like 'get project export status denied' + end + + context 'when user is not a member' do + it_behaves_like 'get project export status not found' + end + end + end + + describe 'GET /projects/:project_id/export/download' do + shared_examples_for 'get project export download not found' do + it_behaves_like '404 response' do + let(:request) { get api(download_path, user) } + end + end + + shared_examples_for 'get project export download denied' do + it_behaves_like '403 response' do + let(:request) { get api(download_path, user) } + end + end + + shared_examples_for 'get project export download' do + it_behaves_like '404 response' do + let(:request) { get api(download_path_none, user) } + end + + it_behaves_like '404 response' do + let(:request) { get api(download_path_started, user) } + end + + it 'downloads' do + get api(download_path_finished, user) + + expect(response).to have_gitlab_http_status(200) + end + end + + it_behaves_like 'when project export is disabled' do + let(:request) { get api(download_path, admin) } + end + + context 'when project export is enabled' do + context 'when user is an admin' do + let(:user) { admin } + + it_behaves_like 'get project export download' + end + + context 'when user is a master' do + before do + project.add_master(user) + project_none.add_master(user) + project_started.add_master(user) + project_finished.add_master(user) + end + + it_behaves_like 'get project export download' + end + + context 'when user is a developer' do + before do + project.add_developer(user) + end + + it_behaves_like 'get project export download denied' + end + + context 'when user is a reporter' do + before do + project.add_reporter(user) + end + + it_behaves_like 'get project export download denied' + end + + context 'when user is a guest' do + before do + project.add_guest(user) + end + + it_behaves_like 'get project export download denied' + end + + context 'when user is not a member' do + it_behaves_like 'get project export download not found' + end + end + end + + describe 'POST /projects/:project_id/export' do + shared_examples_for 'post project export start not found' do + it_behaves_like '404 response' do + let(:request) { post api(path, user) } + end + end + + shared_examples_for 'post project export start denied' do + it_behaves_like '403 response' do + let(:request) { post api(path, user) } + end + end + + shared_examples_for 'post project export start' do + it 'starts' do + post api(path, user) + + expect(response).to have_gitlab_http_status(202) + end + end + + it_behaves_like 'when project export is disabled' do + let(:request) { post api(path, admin) } + end + + context 'when project export is enabled' do + context 'when user is an admin' do + let(:user) { admin } + + it_behaves_like 'post project export start' + end + + context 'when user is a master' do + before do + project.add_master(user) + project_none.add_master(user) + project_started.add_master(user) + project_finished.add_master(user) + end + + it_behaves_like 'post project export start' + end + + context 'when user is a developer' do + before do + project.add_developer(user) + end + + it_behaves_like 'post project export start denied' + end + + context 'when user is a reporter' do + before do + project.add_reporter(user) + end + + it_behaves_like 'post project export start denied' + end + + context 'when user is a guest' do + before do + project.add_guest(user) + end + + it_behaves_like 'post project export start denied' + end + + context 'when user is not a member' do + it_behaves_like 'post project export start not found' + end + end + end +end diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb index 1fd082ecc38..392cad667be 100644 --- a/spec/requests/api/project_hooks_spec.rb +++ b/spec/requests/api/project_hooks_spec.rb @@ -28,6 +28,7 @@ describe API::ProjectHooks, 'ProjectHooks' do expect(json_response.count).to eq(1) expect(json_response.first['url']).to eq("http://example.com") expect(json_response.first['issues_events']).to eq(true) + expect(json_response.first['confidential_issues_events']).to eq(true) expect(json_response.first['push_events']).to eq(true) expect(json_response.first['merge_requests_events']).to eq(true) expect(json_response.first['tag_push_events']).to eq(true) @@ -56,6 +57,7 @@ describe API::ProjectHooks, 'ProjectHooks' do expect(response).to have_gitlab_http_status(200) expect(json_response['url']).to eq(hook.url) expect(json_response['issues_events']).to eq(hook.issues_events) + expect(json_response['confidential_issues_events']).to eq(hook.confidential_issues_events) expect(json_response['push_events']).to eq(hook.push_events) expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events) expect(json_response['tag_push_events']).to eq(hook.tag_push_events) @@ -90,13 +92,14 @@ describe API::ProjectHooks, 'ProjectHooks' do it "adds hook to project" do expect do post api("/projects/#{project.id}/hooks", user), - url: "http://example.com", issues_events: true, wiki_page_events: true, + url: "http://example.com", issues_events: true, confidential_issues_events: true, wiki_page_events: true, job_events: true end.to change {project.hooks.count}.by(1) expect(response).to have_gitlab_http_status(201) expect(json_response['url']).to eq('http://example.com') expect(json_response['issues_events']).to eq(true) + expect(json_response['confidential_issues_events']).to eq(true) expect(json_response['push_events']).to eq(true) expect(json_response['merge_requests_events']).to eq(false) expect(json_response['tag_push_events']).to eq(false) @@ -144,6 +147,7 @@ describe API::ProjectHooks, 'ProjectHooks' do expect(response).to have_gitlab_http_status(200) expect(json_response['url']).to eq('http://example.org') expect(json_response['issues_events']).to eq(hook.issues_events) + expect(json_response['confidential_issues_events']).to eq(hook.confidential_issues_events) expect(json_response['push_events']).to eq(false) expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events) expect(json_response['tag_push_events']).to eq(hook.tag_push_events) diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index f10b6e43d09..95c23726a79 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -122,6 +122,15 @@ describe API::Runner do end end end + + it "sets the runner's ip_address" do + post api('/runners'), + { token: registration_token }, + { 'REMOTE_ADDR' => '123.111.123.111' } + + expect(response).to have_gitlab_http_status 201 + expect(Ci::Runner.first.ip_address).to eq('123.111.123.111') + end end describe 'DELETE /api/v4/runners' do @@ -422,6 +431,15 @@ describe API::Runner do end end + it "sets the runner's ip_address" do + post api('/jobs/request'), + { token: runner.token }, + { 'User-Agent' => user_agent, 'REMOTE_ADDR' => '123.222.123.222' } + + expect(response).to have_gitlab_http_status 201 + expect(runner.reload.ip_address).to eq('123.222.123.222') + end + context 'when concurrently updating a job' do before do expect_any_instance_of(Ci::Build).to receive(:run!) @@ -680,10 +698,10 @@ describe API::Runner do end end - context 'when tace is given' do + context 'when trace is given' do it 'creates a trace artifact' do - allow_any_instance_of(BuildFinishedWorker).to receive(:perform).with(job.id) do - CreateTraceArtifactWorker.new.perform(job.id) + allow(BuildFinishedWorker).to receive(:perform_async).with(job.id) do + ArchiveTraceWorker.new.perform(job.id) end update_job(state: 'success', trace: 'BUILD TRACE UPDATED') @@ -1082,11 +1100,13 @@ describe API::Runner do context 'posts artifacts file and metadata file' do let!(:artifacts) { file_upload } + let!(:artifacts_sha256) { Digest::SHA256.file(artifacts.path).hexdigest } let!(:metadata) { file_upload2 } let(:stored_artifacts_file) { job.reload.artifacts_file.file } let(:stored_metadata_file) { job.reload.artifacts_metadata.file } let(:stored_artifacts_size) { job.reload.artifacts_size } + let(:stored_artifacts_sha256) { job.reload.job_artifacts_archive.file_sha256 } before do post(api("/jobs/#{job.id}/artifacts"), post_data, headers_with_token) @@ -1096,6 +1116,7 @@ describe API::Runner do let(:post_data) do { 'file.path' => artifacts.path, 'file.name' => artifacts.original_filename, + 'file.sha256' => artifacts_sha256, 'metadata.path' => metadata.path, 'metadata.name' => metadata.original_filename } end @@ -1105,6 +1126,7 @@ describe API::Runner do expect(stored_artifacts_file.original_filename).to eq(artifacts.original_filename) expect(stored_metadata_file.original_filename).to eq(metadata.original_filename) expect(stored_artifacts_size).to eq(72821) + expect(stored_artifacts_sha256).to eq(artifacts_sha256) end end diff --git a/spec/requests/api/v3/issues_spec.rb b/spec/requests/api/v3/issues_spec.rb index 0e745c82395..11b5469be7b 100644 --- a/spec/requests/api/v3/issues_spec.rb +++ b/spec/requests/api/v3/issues_spec.rb @@ -1218,7 +1218,7 @@ describe API::V3::Issues do context 'when source project does not exist' do it 'returns 404 when trying to move an issue' do - post v3_api("/projects/123/issues/#{issue.id}/move", user), + post v3_api("/projects/0/issues/#{issue.id}/move", user), to_project_id: target_project.id expect(response).to have_gitlab_http_status(404) @@ -1229,7 +1229,7 @@ describe API::V3::Issues do context 'when target project does not exist' do it 'returns 404 when trying to move an issue' do post v3_api("/projects/#{project.id}/issues/#{issue.id}/move", user), - to_project_id: 123 + to_project_id: 0 expect(response).to have_gitlab_http_status(404) end diff --git a/spec/requests/api/v3/project_hooks_spec.rb b/spec/requests/api/v3/project_hooks_spec.rb index 248ae97f875..8f6a2330d25 100644 --- a/spec/requests/api/v3/project_hooks_spec.rb +++ b/spec/requests/api/v3/project_hooks_spec.rb @@ -27,6 +27,7 @@ describe API::ProjectHooks, 'ProjectHooks' do expect(json_response.count).to eq(1) expect(json_response.first['url']).to eq("http://example.com") expect(json_response.first['issues_events']).to eq(true) + expect(json_response.first['confidential_issues_events']).to eq(true) expect(json_response.first['push_events']).to eq(true) expect(json_response.first['merge_requests_events']).to eq(true) expect(json_response.first['tag_push_events']).to eq(true) @@ -54,6 +55,7 @@ describe API::ProjectHooks, 'ProjectHooks' do expect(response).to have_gitlab_http_status(200) expect(json_response['url']).to eq(hook.url) expect(json_response['issues_events']).to eq(hook.issues_events) + expect(json_response['confidential_issues_events']).to eq(hook.confidential_issues_events) expect(json_response['push_events']).to eq(hook.push_events) expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events) expect(json_response['tag_push_events']).to eq(hook.tag_push_events) @@ -87,12 +89,13 @@ 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, build_events: true + url: "http://example.com", issues_events: true, confidential_issues_events: true, wiki_page_events: true, build_events: true end.to change {project.hooks.count}.by(1) expect(response).to have_gitlab_http_status(201) expect(json_response['url']).to eq('http://example.com') expect(json_response['issues_events']).to eq(true) + expect(json_response['confidential_issues_events']).to eq(true) expect(json_response['push_events']).to eq(true) expect(json_response['merge_requests_events']).to eq(false) expect(json_response['tag_push_events']).to eq(false) @@ -139,6 +142,7 @@ describe API::ProjectHooks, 'ProjectHooks' do expect(response).to have_gitlab_http_status(200) expect(json_response['url']).to eq('http://example.org') expect(json_response['issues_events']).to eq(hook.issues_events) + expect(json_response['confidential_issues_events']).to eq(hook.confidential_issues_events) expect(json_response['push_events']).to eq(false) expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events) expect(json_response['tag_push_events']).to eq(hook.tag_push_events) |