2019-09-04 21:01:54 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require 'spec_helper'
|
|
|
|
|
2023-06-20 00:43:36 +05:30
|
|
|
RSpec.describe API::Issues, :aggregate_failures, feature_category: :team_planning do
|
2020-03-13 15:44:24 +05:30
|
|
|
let_it_be(:user) { create(:user) }
|
|
|
|
let_it_be(:project, reload: true) do
|
2019-09-04 21:01:54 +05:30
|
|
|
create(:project, :public, creator_id: user.id, namespace: user.namespace)
|
|
|
|
end
|
|
|
|
|
2021-11-18 22:05:49 +05:30
|
|
|
let_it_be(:user2) { create(:user) }
|
|
|
|
let_it_be(:non_member) { create(:user) }
|
|
|
|
let_it_be(:guest) { create(:user) }
|
|
|
|
let_it_be(:author) { create(:author) }
|
|
|
|
let_it_be(:milestone) { create(:milestone, title: '1.0.0', project: project) }
|
|
|
|
let_it_be(:assignee) { create(:assignee) }
|
|
|
|
let_it_be(:admin) { create(:user, :admin) }
|
|
|
|
|
|
|
|
let_it_be(:closed_issue) do
|
2019-09-04 21:01:54 +05:30
|
|
|
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
|
2020-10-24 23:57:45 +05:30
|
|
|
|
2021-11-18 22:05:49 +05:30
|
|
|
let_it_be(:confidential_issue) do
|
2019-09-04 21:01:54 +05:30
|
|
|
create :issue,
|
|
|
|
:confidential,
|
|
|
|
project: project,
|
|
|
|
author: author,
|
|
|
|
assignees: [assignee],
|
|
|
|
created_at: generate(:past_time),
|
|
|
|
updated_at: 2.hours.ago
|
|
|
|
end
|
2020-10-24 23:57:45 +05:30
|
|
|
|
2021-11-18 22:05:49 +05:30
|
|
|
let_it_be(:issue) do
|
2019-09-04 21:01:54 +05:30
|
|
|
create :issue,
|
|
|
|
author: user,
|
|
|
|
assignees: [user],
|
|
|
|
project: project,
|
|
|
|
milestone: milestone,
|
|
|
|
created_at: generate(:past_time),
|
|
|
|
updated_at: 1.hour.ago,
|
2021-11-18 22:05:49 +05:30
|
|
|
title: 'foo',
|
|
|
|
description: 'closed'
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
2020-10-24 23:57:45 +05:30
|
|
|
|
2021-11-18 22:05:49 +05:30
|
|
|
let_it_be(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
let_it_be(:label) do
|
2019-09-04 21:01:54 +05:30
|
|
|
create(:label, title: 'label', color: '#FFAABB', project: project)
|
|
|
|
end
|
2021-01-29 00:20:46 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
let!(:label_link) { create(:label_link, label: label, target: issue) }
|
2020-03-13 15:44:24 +05:30
|
|
|
let_it_be(:empty_milestone) do
|
2019-09-04 21:01:54 +05:30
|
|
|
create(:milestone, title: '2.0.0', project: project)
|
|
|
|
end
|
2021-01-29 00:20:46 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
let(:no_milestone_title) { 'None' }
|
|
|
|
let(:any_milestone_title) { 'Any' }
|
|
|
|
|
2020-04-22 19:07:51 +05:30
|
|
|
before_all do
|
2019-09-04 21:01:54 +05:30
|
|
|
project.add_reporter(user)
|
|
|
|
project.add_guest(guest)
|
|
|
|
end
|
|
|
|
|
|
|
|
before do
|
|
|
|
stub_licensed_features(multiple_issue_assignees: false, issue_weights: false)
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'POST /projects/:id/issues' do
|
|
|
|
context 'support for deprecated assignee_id' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'creates a new project issue' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues", user),
|
|
|
|
params: { title: 'new issue', assignee_id: user2.id }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['title']).to eq('new issue')
|
|
|
|
expect(json_response['assignee']['name']).to eq(user2.name)
|
|
|
|
expect(json_response['assignees'].first['name']).to eq(user2.name)
|
|
|
|
end
|
|
|
|
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'creates a new project issue when assignee_id is empty' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues", user),
|
|
|
|
params: { title: 'new issue', assignee_id: '' }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['title']).to eq('new issue')
|
|
|
|
expect(json_response['assignee']).to be_nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'single assignee restrictions' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'creates a new project issue with no more than one assignee' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues", user),
|
|
|
|
params: { title: 'new issue', assignee_ids: [user2.id, guest.id] }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['title']).to eq('new issue')
|
|
|
|
expect(json_response['assignees'].count).to eq(1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'user does not have permissions to create issue' do
|
|
|
|
let(:not_member) { create(:user) }
|
|
|
|
|
|
|
|
before do
|
2021-04-29 21:17:54 +05:30
|
|
|
project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it 'renders 403' do
|
|
|
|
post api("/projects/#{project.id}/issues", not_member), params: { title: 'new issue' }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:forbidden)
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'an internal ID is provided' do
|
|
|
|
context 'by an admin' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'sets the internal ID on the new issue' do
|
2023-05-27 22:25:52 +05:30
|
|
|
post api("/projects/#{project.id}/issues", admin, admin_mode: true),
|
2019-09-04 21:01:54 +05:30
|
|
|
params: { title: 'new issue', iid: 9001 }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['iid']).to eq 9001
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'by an owner' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'sets the internal ID on the new issue' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues", user),
|
|
|
|
params: { title: 'new issue', iid: 9001 }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['iid']).to eq 9001
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'by a group owner' do
|
|
|
|
let(:group) { create(:group) }
|
|
|
|
let(:group_project) { create(:project, :public, namespace: group) }
|
|
|
|
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'sets the internal ID on the new issue' do
|
2019-09-04 21:01:54 +05:30
|
|
|
group.add_owner(user2)
|
|
|
|
post api("/projects/#{group_project.id}/issues", user2),
|
|
|
|
params: { title: 'new issue', iid: 9001 }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['iid']).to eq 9001
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'by another user' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'ignores the given internal ID' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues", user2),
|
|
|
|
params: { title: 'new issue', iid: 9001 }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['iid']).not_to eq 9001
|
|
|
|
end
|
|
|
|
end
|
2020-03-13 15:44:24 +05:30
|
|
|
|
|
|
|
context 'when an issue with the same IID exists on database' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'returns 409' do
|
2023-05-27 22:25:52 +05:30
|
|
|
post api("/projects/#{project.id}/issues", admin, admin_mode: true),
|
2020-03-13 15:44:24 +05:30
|
|
|
params: { title: 'new issue', iid: issue.iid }
|
|
|
|
|
|
|
|
expect(response).to have_gitlab_http_status(:conflict)
|
|
|
|
expect(json_response['message']).to eq 'Duplicated issue'
|
|
|
|
end
|
|
|
|
end
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'creates a new project issue' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues", user),
|
|
|
|
params: { title: 'new issue', labels: 'label, label2', weight: 3, assignee_ids: [user2.id] }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['title']).to eq('new issue')
|
|
|
|
expect(json_response['description']).to be_nil
|
|
|
|
expect(json_response['labels']).to eq(%w(label label2))
|
|
|
|
expect(json_response['confidential']).to be_falsy
|
|
|
|
expect(json_response['assignee']['name']).to eq(user2.name)
|
|
|
|
expect(json_response['assignees'].first['name']).to eq(user2.name)
|
|
|
|
end
|
|
|
|
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'creates a new project issue with labels param as array' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues", user),
|
|
|
|
params: { title: 'new issue', labels: %w(label label2), weight: 3, assignee_ids: [user2.id] }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['title']).to eq('new issue')
|
|
|
|
expect(json_response['description']).to be_nil
|
|
|
|
expect(json_response['labels']).to eq(%w(label label2))
|
|
|
|
expect(json_response['confidential']).to be_falsy
|
|
|
|
expect(json_response['assignee']['name']).to eq(user2.name)
|
|
|
|
expect(json_response['assignees'].first['name']).to eq(user2.name)
|
|
|
|
end
|
|
|
|
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'creates a new confidential project issue' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues", user),
|
|
|
|
params: { title: 'new issue', confidential: true }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['title']).to eq('new issue')
|
|
|
|
expect(json_response['confidential']).to be_truthy
|
|
|
|
end
|
|
|
|
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'creates a new confidential project issue with a different param' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues", user),
|
|
|
|
params: { title: 'new issue', confidential: 'y' }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['title']).to eq('new issue')
|
|
|
|
expect(json_response['confidential']).to be_truthy
|
|
|
|
end
|
|
|
|
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'creates a public issue when confidential param is false' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues", user),
|
|
|
|
params: { title: 'new issue', confidential: false }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['title']).to eq('new issue')
|
|
|
|
expect(json_response['confidential']).to be_falsy
|
|
|
|
end
|
|
|
|
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'creates a public issue when confidential param is invalid' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues", user),
|
|
|
|
params: { title: 'new issue', confidential: 'foo' }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:bad_request)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['error']).to eq('confidential is invalid')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns a 400 bad request if title not given' do
|
|
|
|
post api("/projects/#{project.id}/issues", user), params: { labels: 'label, label2' }
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:bad_request)
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'allows special label names' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues", user),
|
|
|
|
params: {
|
|
|
|
title: 'new issue',
|
|
|
|
labels: 'label, label?, label&foo, ?, &'
|
|
|
|
}
|
2020-04-22 19:07:51 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
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
|
|
|
|
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'allows special label names with labels param as array' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues", user),
|
|
|
|
params: {
|
|
|
|
title: 'new issue',
|
|
|
|
labels: ['label', 'label?', 'label&foo, ?, &']
|
|
|
|
}
|
2020-04-22 19:07:51 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
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
|
|
|
|
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'returns 400 if title is too long' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues", user),
|
|
|
|
params: { title: 'g' * 256 }
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:bad_request)
|
2022-11-25 23:54:43 +05:30
|
|
|
expect(json_response['message']['title']).to eq(['is too long (maximum is 255 characters)'])
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'resolving discussions' do
|
|
|
|
let(:discussion) { create(:diff_note_on_merge_request).to_discussion }
|
|
|
|
let(:merge_request) { discussion.noteable }
|
|
|
|
let(:project) { merge_request.source_project }
|
|
|
|
|
|
|
|
before do
|
|
|
|
project.add_maintainer(user)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'resolving all discussions in a merge request' do
|
|
|
|
before do
|
|
|
|
post api("/projects/#{project.id}/issues", user),
|
|
|
|
params: {
|
|
|
|
title: 'New Issue',
|
|
|
|
merge_request_to_resolve_discussions_of: merge_request.iid
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'creating an issue resolving discussions through the API'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'resolving a single discussion' do
|
|
|
|
before do
|
|
|
|
post api("/projects/#{project.id}/issues", user),
|
|
|
|
params: {
|
|
|
|
title: 'New Issue',
|
|
|
|
merge_request_to_resolve_discussions_of: merge_request.iid,
|
|
|
|
discussion_to_resolve: discussion.id
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'creating an issue resolving discussions through the API'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with due date' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'creates a new project issue' do
|
2019-09-04 21:01:54 +05:30
|
|
|
due_date = 2.weeks.from_now.strftime('%Y-%m-%d')
|
|
|
|
|
|
|
|
post api("/projects/#{project.id}/issues", user),
|
|
|
|
params: { title: 'new issue', due_date: due_date }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['title']).to eq('new issue')
|
|
|
|
expect(json_response['description']).to be_nil
|
|
|
|
expect(json_response['due_date']).to eq(due_date)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'setting created_at' do
|
2021-04-28 17:22:55 +05:30
|
|
|
let(:fixed_time) { Time.new(2001, 1, 1) }
|
2019-09-04 21:01:54 +05:30
|
|
|
let(:creation_time) { 2.weeks.ago }
|
|
|
|
let(:params) { { title: 'new issue', labels: 'label, label2', created_at: creation_time } }
|
|
|
|
|
2021-04-28 17:22:55 +05:30
|
|
|
before do
|
|
|
|
travel_to fixed_time
|
|
|
|
end
|
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
context 'by an admin' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'sets the creation time on the new issue' do
|
2023-05-27 22:25:52 +05:30
|
|
|
post api("/projects/#{project.id}/issues", admin, admin_mode: true), params: params
|
2019-09-04 21:01:54 +05:30
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
|
2021-04-28 17:22:55 +05:30
|
|
|
expect(ResourceLabelEvent.last.created_at).to be_like_time(creation_time)
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'by a project owner' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'sets the creation time on the new issue' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues", user), params: params
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
|
2021-04-28 17:22:55 +05:30
|
|
|
expect(ResourceLabelEvent.last.created_at).to be_like_time(creation_time)
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'by a group owner' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'sets the creation time on the new issue' do
|
2019-09-04 21:01:54 +05:30
|
|
|
group = create(:group)
|
|
|
|
group_project = create(:project, :public, namespace: group)
|
|
|
|
group.add_owner(user2)
|
2021-04-28 17:22:55 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{group_project.id}/issues", user2), params: params
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
|
2021-04-28 17:22:55 +05:30
|
|
|
expect(ResourceLabelEvent.last.created_at).to be_like_time(creation_time)
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'by another user' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'ignores the given creation time' do
|
2021-04-28 17:22:55 +05:30
|
|
|
project.add_developer(user2)
|
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues", user2), params: params
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2021-04-28 17:22:55 +05:30
|
|
|
expect(Time.parse(json_response['created_at'])).to be_like_time(fixed_time)
|
|
|
|
expect(ResourceLabelEvent.last.created_at).to be_like_time(fixed_time)
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'the user can only read the issue' do
|
|
|
|
it 'cannot create new labels' do
|
|
|
|
expect do
|
|
|
|
post api("/projects/#{project.id}/issues", non_member), params: { title: 'new issue', labels: 'label, label2' }
|
|
|
|
end.not_to change { project.labels.count }
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'cannot create new labels with labels param as array' do
|
|
|
|
expect do
|
|
|
|
post api("/projects/#{project.id}/issues", non_member), params: { title: 'new issue', labels: %w(label label2) }
|
|
|
|
end.not_to change { project.labels.count }
|
|
|
|
end
|
|
|
|
end
|
2020-04-22 19:07:51 +05:30
|
|
|
|
|
|
|
context 'when request exceeds the rate limit' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'prevents users from creating more issues' do
|
2020-04-22 19:07:51 +05:30
|
|
|
allow(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true)
|
|
|
|
|
|
|
|
post api("/projects/#{project.id}/issues", user),
|
|
|
|
params: { title: 'new issue', labels: 'label, label2', weight: 3, assignee_ids: [user2.id] }
|
|
|
|
|
|
|
|
expect(json_response['message']['error']).to eq('This endpoint has been requested too many times. Try again later.')
|
2021-11-18 22:05:49 +05:30
|
|
|
|
|
|
|
expect(response).to have_gitlab_http_status(:too_many_requests)
|
2020-04-22 19:07:51 +05:30
|
|
|
end
|
|
|
|
end
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
describe 'POST /projects/:id/issues with spam filtering' do
|
2019-12-21 20:55:43 +05:30
|
|
|
def post_issue
|
|
|
|
post api("/projects/#{project.id}/issues", user), params: params
|
|
|
|
end
|
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
before do
|
2020-05-24 23:13:21 +05:30
|
|
|
expect_next_instance_of(Spam::SpamActionService) do |spam_service|
|
2019-12-21 20:55:43 +05:30
|
|
|
expect(spam_service).to receive_messages(check_for_spam?: true)
|
|
|
|
end
|
2020-03-13 15:44:24 +05:30
|
|
|
expect_next_instance_of(Spam::AkismetService) do |akismet_service|
|
2019-12-21 20:55:43 +05:30
|
|
|
expect(akismet_service).to receive_messages(spam?: true)
|
|
|
|
end
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
let(:params) do
|
|
|
|
{
|
|
|
|
title: 'new issue',
|
|
|
|
description: 'content here',
|
|
|
|
labels: 'label, label2'
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2023-04-23 21:23:45 +05:30
|
|
|
context 'when allow_possible_spam application setting is false' do
|
2019-12-21 20:55:43 +05:30
|
|
|
it 'does not create a new project issue' do
|
|
|
|
expect { post_issue }.not_to change(Issue, :count)
|
|
|
|
end
|
|
|
|
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'returns correct status and message' do
|
2019-12-21 20:55:43 +05:30
|
|
|
post_issue
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:bad_request)
|
2022-05-07 20:08:51 +05:30
|
|
|
expect(json_response['message']['base']).to match_array([/issue has been recognized as spam/])
|
2019-12-21 20:55:43 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it 'creates a new spam log entry' do
|
|
|
|
expect { post_issue }
|
|
|
|
.to log_spam(title: 'new issue', description: 'content here', user_id: user.id, noteable_type: 'Issue')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-04-23 21:23:45 +05:30
|
|
|
context 'when allow_possible_spam application setting is true' do
|
|
|
|
before do
|
|
|
|
stub_application_setting(allow_possible_spam: true)
|
|
|
|
end
|
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
it 'does creates a new project issue' do
|
|
|
|
expect { post_issue }.to change(Issue, :count).by(1)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns correct status' do
|
|
|
|
post_issue
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-12-21 20:55:43 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it 'creates a new spam log entry' do
|
|
|
|
expect { post_issue }
|
|
|
|
.to log_spam(title: 'new issue', description: 'content here', user_id: user.id, noteable_type: 'Issue')
|
|
|
|
end
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '/projects/:id/issues/:issue_iid/move' do
|
2023-01-13 00:05:48 +05:30
|
|
|
let!(:target_project) { create(:project, creator_id: user.id, namespace: user.namespace) }
|
|
|
|
let!(:target_project2) { create(:project, creator_id: non_member.id, namespace: non_member.namespace) }
|
2023-06-20 00:43:36 +05:30
|
|
|
let(:path) { "/projects/#{project.id}/issues/#{issue.iid}/move" }
|
2019-09-04 21:01:54 +05:30
|
|
|
|
2023-06-20 00:43:36 +05:30
|
|
|
it_behaves_like 'POST request permissions for admin mode' do
|
|
|
|
let(:params) { { to_project_id: target_project2.id } }
|
|
|
|
let(:failed_status_code) { 400 }
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'moves an issue' do
|
|
|
|
post api(path, user),
|
2019-09-04 21:01:54 +05:30
|
|
|
params: { to_project_id: target_project.id }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['project_id']).to eq(target_project.id)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when source and target projects are the same' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'returns 400 when trying to move an issue' do
|
|
|
|
post api(path, user),
|
2019-09-04 21:01:54 +05:30
|
|
|
params: { to_project_id: project.id }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:bad_request)
|
2020-01-01 13:55:28 +05:30
|
|
|
expect(json_response['message']).to eq(s_('MoveIssue|Cannot move issue to project it originates from!'))
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when the user does not have the permission to move issues' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'returns 400 when trying to move an issue' do
|
|
|
|
post api(path, user),
|
2019-09-04 21:01:54 +05:30
|
|
|
params: { to_project_id: target_project2.id }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:bad_request)
|
2020-01-01 13:55:28 +05:30
|
|
|
expect(json_response['message']).to eq(s_('MoveIssue|Cannot move issue due to insufficient permissions!'))
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'moves the issue to another namespace if I am admin' do
|
|
|
|
post api(path, admin, admin_mode: true),
|
2019-09-04 21:01:54 +05:30
|
|
|
params: { to_project_id: target_project2.id }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['project_id']).to eq(target_project2.id)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when using the issue ID instead of iid' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'returns 404 when trying to move an issue', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341520' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues/#{issue.id}/move", user),
|
|
|
|
params: { to_project_id: target_project.id }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['message']).to eq('404 Issue Not Found')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when issue does not exist' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'returns 404 when trying to move an issue' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues/123/move", user),
|
|
|
|
params: { to_project_id: target_project.id }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['message']).to eq('404 Issue Not Found')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when source project does not exist' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'returns 404 when trying to move an issue' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/0/issues/#{issue.iid}/move", user),
|
|
|
|
params: { to_project_id: target_project.id }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['message']).to eq('404 Project Not Found')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when target project does not exist' do
|
|
|
|
it 'returns 404 when trying to move an issue' do
|
2023-06-20 00:43:36 +05:30
|
|
|
post api(path, user),
|
2019-09-04 21:01:54 +05:30
|
|
|
params: { to_project_id: 0 }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-11-18 22:05:49 +05:30
|
|
|
describe '/projects/:id/issues/:issue_iid/clone' do
|
|
|
|
let_it_be(:valid_target_project) { create(:project) }
|
|
|
|
let_it_be(:invalid_target_project) { create(:project) }
|
|
|
|
|
|
|
|
before_all do
|
|
|
|
valid_target_project.add_maintainer(user)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when user can admin the issue' do
|
|
|
|
context 'when the user can admin the target project' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'clones the issue' do
|
2021-11-18 22:05:49 +05:30
|
|
|
expect do
|
|
|
|
post_clone_issue(user, issue, valid_target_project)
|
|
|
|
end.to change { valid_target_project.issues.count }.by(1)
|
|
|
|
|
|
|
|
cloned_issue = Issue.last
|
|
|
|
|
|
|
|
expect(cloned_issue.notes.count).to eq(2)
|
|
|
|
expect(cloned_issue.notes.pluck(:note)).not_to include(issue.notes.first.note)
|
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
|
|
|
expect(json_response['id']).to eq(cloned_issue.id)
|
|
|
|
expect(json_response['project_id']).to eq(valid_target_project.id)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when target project is the same source project' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'clones the issue' do
|
2021-11-18 22:05:49 +05:30
|
|
|
expect do
|
|
|
|
post_clone_issue(user, issue, issue.project)
|
|
|
|
end.to change { issue.reset.project.issues.count }.by(1)
|
|
|
|
|
|
|
|
cloned_issue = Issue.last
|
|
|
|
|
|
|
|
expect(cloned_issue.notes.count).to eq(2)
|
|
|
|
expect(cloned_issue.notes.pluck(:note)).not_to include(issue.notes.first.note)
|
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
|
|
|
expect(json_response['id']).to eq(cloned_issue.id)
|
|
|
|
expect(json_response['project_id']).to eq(issue.project.id)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when the user does not have the permission to clone issues' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'returns 400' do
|
2021-11-18 22:05:49 +05:30
|
|
|
post api("/projects/#{project.id}/issues/#{issue.iid}/clone", user),
|
|
|
|
params: { to_project_id: invalid_target_project.id }
|
|
|
|
|
|
|
|
expect(response).to have_gitlab_http_status(:bad_request)
|
|
|
|
expect(json_response['message']).to eq(s_('CloneIssue|Cannot clone issue due to insufficient permissions!'))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when using the issue ID instead of iid' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'returns 404', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341520' do
|
2021-11-18 22:05:49 +05:30
|
|
|
post api("/projects/#{project.id}/issues/#{issue.id}/clone", user),
|
|
|
|
params: { to_project_id: valid_target_project.id }
|
|
|
|
|
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
|
|
|
expect(json_response['message']).to eq('404 Issue Not Found')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when issue does not exist' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'returns 404' do
|
2021-11-18 22:05:49 +05:30
|
|
|
post api("/projects/#{project.id}/issues/12300/clone", user),
|
|
|
|
params: { to_project_id: valid_target_project.id }
|
|
|
|
|
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
|
|
|
expect(json_response['message']).to eq('404 Issue Not Found')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when source project does not exist' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'returns 404' do
|
2021-11-18 22:05:49 +05:30
|
|
|
post api("/projects/0/issues/#{issue.iid}/clone", user),
|
|
|
|
params: { to_project_id: valid_target_project.id }
|
|
|
|
|
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
|
|
|
expect(json_response['message']).to eq('404 Project Not Found')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when target project does not exist' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'returns 404' do
|
2021-11-18 22:05:49 +05:30
|
|
|
post api("/projects/#{project.id}/issues/#{issue.iid}/clone", user),
|
|
|
|
params: { to_project_id: 0 }
|
|
|
|
|
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
|
|
|
expect(json_response['message']).to eq('404 Project Not Found')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'clones the issue with notes when with_notes is true' do
|
2021-11-18 22:05:49 +05:30
|
|
|
expect do
|
|
|
|
post api("/projects/#{project.id}/issues/#{issue.iid}/clone", user),
|
|
|
|
params: { to_project_id: valid_target_project.id, with_notes: true }
|
|
|
|
end.to change { valid_target_project.issues.count }.by(1)
|
|
|
|
|
|
|
|
cloned_issue = Issue.last
|
|
|
|
|
|
|
|
expect(cloned_issue.notes.count).to eq(3)
|
|
|
|
expect(cloned_issue.notes.pluck(:note)).to include(issue.notes.first.note)
|
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
|
|
|
expect(json_response['id']).to eq(cloned_issue.id)
|
|
|
|
expect(json_response['project_id']).to eq(valid_target_project.id)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
describe 'POST :id/issues/:issue_iid/subscribe' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'subscribes to an issue' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues/#{issue.iid}/subscribe", user2)
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['subscribed']).to eq(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns 304 if already subscribed' do
|
|
|
|
post api("/projects/#{project.id}/issues/#{issue.iid}/subscribe", user)
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:not_modified)
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns 404 if the issue is not found' do
|
|
|
|
post api("/projects/#{project.id}/issues/123/subscribe", user)
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
|
2021-11-18 22:05:49 +05:30
|
|
|
it 'returns 404 if the issue ID is used instead of the iid', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341520' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues/#{issue.id}/subscribe", user)
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns 404 if the issue is confidential' do
|
|
|
|
post api("/projects/#{project.id}/issues/#{confidential_issue.iid}/subscribe", non_member)
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'POST :id/issues/:issue_id/unsubscribe' do
|
2023-06-20 00:43:36 +05:30
|
|
|
it 'unsubscribes from an issue' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues/#{issue.iid}/unsubscribe", user)
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:created)
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(json_response['subscribed']).to eq(false)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns 304 if not subscribed' do
|
|
|
|
post api("/projects/#{project.id}/issues/#{issue.iid}/unsubscribe", user2)
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:not_modified)
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns 404 if the issue is not found' do
|
|
|
|
post api("/projects/#{project.id}/issues/123/unsubscribe", user)
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
|
2021-11-18 22:05:49 +05:30
|
|
|
it 'returns 404 if using the issue ID instead of iid', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341520' do
|
2019-09-04 21:01:54 +05:30
|
|
|
post api("/projects/#{project.id}/issues/#{issue.id}/unsubscribe", user)
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns 404 if the issue is confidential' do
|
|
|
|
post api("/projects/#{project.id}/issues/#{confidential_issue.iid}/unsubscribe", non_member)
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
|
|
|
end
|
2021-11-18 22:05:49 +05:30
|
|
|
|
|
|
|
def post_clone_issue(current_user, issue, target_project)
|
|
|
|
post api("/projects/#{issue.project.id}/issues/#{issue.iid}/clone", current_user),
|
|
|
|
params: { to_project_id: target_project.id }
|
|
|
|
end
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|