diff options
Diffstat (limited to 'spec/requests/api/issues/put_projects_issues_spec.rb')
-rw-r--r-- | spec/requests/api/issues/put_projects_issues_spec.rb | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/spec/requests/api/issues/put_projects_issues_spec.rb b/spec/requests/api/issues/put_projects_issues_spec.rb new file mode 100644 index 00000000000..267cba93713 --- /dev/null +++ b/spec/requests/api/issues/put_projects_issues_spec.rb @@ -0,0 +1,392 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe API::Issues do + set(:user) { create(:user) } + set(:project) do + create(:project, :public, creator_id: user.id, namespace: user.namespace) + end + + let(:user2) { create(:user) } + let(:non_member) { create(:user) } + set(:guest) { create(:user) } + set(:author) { create(:author) } + set(:assignee) { create(:assignee) } + let(:admin) { create(:user, :admin) } + let(:issue_title) { 'foo' } + let(:issue_description) { 'closed' } + let!(:closed_issue) do + create :closed_issue, + author: user, + assignees: [user], + project: project, + state: :closed, + milestone: milestone, + created_at: generate(:past_time), + updated_at: 3.hours.ago, + closed_at: 1.hour.ago + end + let!(:confidential_issue) do + create :issue, + :confidential, + project: project, + author: author, + assignees: [assignee], + created_at: generate(:past_time), + updated_at: 2.hours.ago + end + let!(:issue) do + create :issue, + author: user, + assignees: [user], + project: project, + milestone: milestone, + created_at: generate(:past_time), + updated_at: 1.hour.ago, + title: issue_title, + description: issue_description + end + set(:label) do + create(:label, title: 'label', color: '#FFAABB', project: project) + end + let!(:label_link) { create(:label_link, label: label, target: issue) } + let(:milestone) { create(:milestone, title: '1.0.0', project: project) } + set(:empty_milestone) do + create(:milestone, title: '2.0.0', project: project) + end + let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) } + + let(:no_milestone_title) { 'None' } + let(:any_milestone_title) { 'Any' } + + before(:all) do + project.add_reporter(user) + project.add_guest(guest) + end + + before do + stub_licensed_features(multiple_issue_assignees: false, issue_weights: false) + end + + describe 'PUT /projects/:id/issues/:issue_iid to update only title' do + it 'updates a project issue' do + put api("/projects/#{project.id}/issues/#{issue.iid}", user), + params: { title: 'updated title' } + expect(response).to have_gitlab_http_status(200) + + expect(json_response['title']).to eq('updated title') + end + + it 'returns 404 error if issue iid not found' do + put api("/projects/#{project.id}/issues/44444", user), + params: { title: 'updated title' } + expect(response).to have_gitlab_http_status(404) + end + + it 'returns 404 error if issue id is used instead of the iid' do + put api("/projects/#{project.id}/issues/#{issue.id}", user), + params: { title: 'updated title' } + expect(response).to have_gitlab_http_status(404) + end + + it 'allows special label names' do + put api("/projects/#{project.id}/issues/#{issue.iid}", user), + params: { + title: 'updated title', + labels: 'label, label?, label&foo, ?, &' + } + + expect(response.status).to eq(200) + expect(json_response['labels']).to include 'label' + expect(json_response['labels']).to include 'label?' + expect(json_response['labels']).to include 'label&foo' + expect(json_response['labels']).to include '?' + expect(json_response['labels']).to include '&' + end + + it 'allows special label names with labels param as array' do + put api("/projects/#{project.id}/issues/#{issue.iid}", user), + params: { + title: 'updated title', + labels: ['label', 'label?', 'label&foo, ?, &'] + } + + expect(response.status).to eq(200) + expect(json_response['labels']).to include 'label' + expect(json_response['labels']).to include 'label?' + expect(json_response['labels']).to include 'label&foo' + expect(json_response['labels']).to include '?' + expect(json_response['labels']).to include '&' + end + + context 'confidential issues' do + it 'returns 403 for non project members' do + put api("/projects/#{project.id}/issues/#{confidential_issue.iid}", non_member), + params: { title: 'updated title' } + expect(response).to have_gitlab_http_status(403) + end + + it 'returns 403 for project members with guest role' do + put api("/projects/#{project.id}/issues/#{confidential_issue.iid}", guest), + params: { title: 'updated title' } + expect(response).to have_gitlab_http_status(403) + end + + it 'updates a confidential issue for project members' do + put api("/projects/#{project.id}/issues/#{confidential_issue.iid}", user), + params: { title: 'updated title' } + expect(response).to have_gitlab_http_status(200) + expect(json_response['title']).to eq('updated title') + end + + it 'updates a confidential issue for author' do + put api("/projects/#{project.id}/issues/#{confidential_issue.iid}", author), + params: { title: 'updated title' } + expect(response).to have_gitlab_http_status(200) + expect(json_response['title']).to eq('updated title') + end + + it 'updates a confidential issue for admin' do + put api("/projects/#{project.id}/issues/#{confidential_issue.iid}", admin), + params: { title: 'updated title' } + expect(response).to have_gitlab_http_status(200) + expect(json_response['title']).to eq('updated title') + end + + it 'sets an issue to confidential' do + put api("/projects/#{project.id}/issues/#{issue.iid}", user), + params: { confidential: true } + + expect(response).to have_gitlab_http_status(200) + expect(json_response['confidential']).to be_truthy + end + + it 'makes a confidential issue public' do + put api("/projects/#{project.id}/issues/#{confidential_issue.iid}", user), + params: { confidential: false } + + expect(response).to have_gitlab_http_status(200) + expect(json_response['confidential']).to be_falsy + end + + it 'does not update a confidential issue with wrong confidential flag' do + put api("/projects/#{project.id}/issues/#{confidential_issue.iid}", user), + params: { confidential: 'foo' } + + expect(response).to have_gitlab_http_status(400) + expect(json_response['error']).to eq('confidential is invalid') + end + end + end + + describe 'PUT /projects/:id/issues/:issue_iid with spam filtering' do + let(:params) do + { + title: 'updated title', + description: 'content here', + labels: 'label, label2' + } + end + + it 'does not create a new project issue' do + allow_any_instance_of(SpamService).to receive_messages(check_for_spam?: true) + allow_any_instance_of(AkismetService).to receive_messages(spam?: true) + + put api("/projects/#{project.id}/issues/#{issue.iid}", user), params: params + + expect(response).to have_gitlab_http_status(400) + expect(json_response['message']).to eq({ 'error' => 'Spam detected' }) + + spam_logs = SpamLog.all + expect(spam_logs.count).to eq(1) + expect(spam_logs[0].title).to eq('updated title') + expect(spam_logs[0].description).to eq('content here') + expect(spam_logs[0].user).to eq(user) + expect(spam_logs[0].noteable_type).to eq('Issue') + 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), + params: { assignee_id: 0 } + + expect(response).to have_gitlab_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), + params: { assignee_id: user2.id } + + expect(response).to have_gitlab_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), + params: { assignee_ids: [0] } + + expect(response).to have_gitlab_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), + params: { assignee_ids: [user2.id] } + + expect(response).to have_gitlab_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), + params: { assignee_ids: [user2.id, guest.id] } + + expect(response).to have_gitlab_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) } + + it 'does not update labels if not present' do + put api("/projects/#{project.id}/issues/#{issue.iid}", user), + params: { title: 'updated title' } + expect(response).to have_gitlab_http_status(200) + expect(json_response['labels']).to eq([label.title]) + end + + it 'removes all labels and touches the record' do + Timecop.travel(1.minute.from_now) do + put api("/projects/#{project.id}/issues/#{issue.iid}", user), params: { labels: '' } + end + + expect(response).to have_gitlab_http_status(200) + expect(json_response['labels']).to eq([]) + expect(json_response['updated_at']).to be > Time.now + end + + it 'removes all labels and touches the record with labels param as array' do + Timecop.travel(1.minute.from_now) do + put api("/projects/#{project.id}/issues/#{issue.iid}", user), params: { labels: [''] } + end + + expect(response).to have_gitlab_http_status(200) + expect(json_response['labels']).to eq([]) + expect(json_response['updated_at']).to be > Time.now + end + + it 'updates labels and touches the record' do + Timecop.travel(1.minute.from_now) do + put api("/projects/#{project.id}/issues/#{issue.iid}", user), + params: { labels: 'foo,bar' } + end + expect(response).to have_gitlab_http_status(200) + expect(json_response['labels']).to include 'foo' + expect(json_response['labels']).to include 'bar' + expect(json_response['updated_at']).to be > Time.now + end + + it 'updates labels and touches the record with labels param as array' do + Timecop.travel(1.minute.from_now) do + put api("/projects/#{project.id}/issues/#{issue.iid}", user), + params: { labels: %w(foo bar) } + end + expect(response).to have_gitlab_http_status(200) + expect(json_response['labels']).to include 'foo' + expect(json_response['labels']).to include 'bar' + expect(json_response['updated_at']).to be > Time.now + end + + it 'allows special label names' do + put api("/projects/#{project.id}/issues/#{issue.iid}", user), + params: { labels: 'label:foo, label-bar,label_bar,label/bar,label?bar,label&bar,?,&' } + expect(response.status).to eq(200) + expect(json_response['labels']).to include 'label:foo' + expect(json_response['labels']).to include 'label-bar' + expect(json_response['labels']).to include 'label_bar' + expect(json_response['labels']).to include 'label/bar' + expect(json_response['labels']).to include 'label?bar' + expect(json_response['labels']).to include 'label&bar' + expect(json_response['labels']).to include '?' + expect(json_response['labels']).to include '&' + end + + it 'allows special label names with labels param as array' do + put api("/projects/#{project.id}/issues/#{issue.iid}", user), + params: { labels: ['label:foo', 'label-bar', 'label_bar', 'label/bar,label?bar,label&bar,?,&'] } + expect(response.status).to eq(200) + expect(json_response['labels']).to include 'label:foo' + expect(json_response['labels']).to include 'label-bar' + expect(json_response['labels']).to include 'label_bar' + expect(json_response['labels']).to include 'label/bar' + expect(json_response['labels']).to include 'label?bar' + expect(json_response['labels']).to include 'label&bar' + expect(json_response['labels']).to include '?' + expect(json_response['labels']).to include '&' + end + + it 'returns 400 if title is too long' do + put api("/projects/#{project.id}/issues/#{issue.iid}", user), + params: { title: 'g' * 256 } + expect(response).to have_gitlab_http_status(400) + expect(json_response['message']['title']).to eq([ + 'is too long (maximum is 255 characters)' + ]) + end + end + + describe 'PUT /projects/:id/issues/:issue_iid to update state and label' do + it 'updates a project issue' do + put api("/projects/#{project.id}/issues/#{issue.iid}", user), + params: { labels: 'label2', state_event: 'close' } + expect(response).to have_gitlab_http_status(200) + + expect(json_response['labels']).to include 'label2' + expect(json_response['state']).to eq 'closed' + end + + it 'reopens a project isssue' do + put api("/projects/#{project.id}/issues/#{closed_issue.iid}", user), params: { state_event: 'reopen' } + + expect(response).to have_gitlab_http_status(200) + expect(json_response['state']).to eq 'opened' + end + + context 'when an admin or owner makes the request' do + it 'accepts the update date to be set' do + update_time = 2.weeks.ago + put api("/projects/#{project.id}/issues/#{issue.iid}", user), + params: { labels: 'label3', state_event: 'close', updated_at: update_time } + + expect(response).to have_gitlab_http_status(200) + expect(json_response['labels']).to include 'label3' + expect(Time.parse(json_response['updated_at'])).to be_like_time(update_time) + end + end + end + + describe 'PUT /projects/:id/issues/:issue_iid to update due date' do + it 'creates a new project issue' do + due_date = 2.weeks.from_now.strftime('%Y-%m-%d') + + put api("/projects/#{project.id}/issues/#{issue.iid}", user), params: { due_date: due_date } + + expect(response).to have_gitlab_http_status(200) + expect(json_response['due_date']).to eq(due_date) + end + end +end |