summaryrefslogtreecommitdiff
path: root/spec/requests/api/issues/put_projects_issues_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/requests/api/issues/put_projects_issues_spec.rb')
-rw-r--r--spec/requests/api/issues/put_projects_issues_spec.rb392
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