2016-02-15 08:41:40 -05:00
|
|
|
require 'spec_helper'
|
|
|
|
|
|
|
|
describe Issues::MoveService, services: true do
|
|
|
|
let(:user) { create(:user) }
|
2016-02-24 07:20:08 -05:00
|
|
|
let(:author) { create(:user) }
|
2016-02-22 05:00:40 -05:00
|
|
|
let(:title) { 'Some issue' }
|
|
|
|
let(:description) { 'Some issue description' }
|
2017-03-27 17:14:01 -04:00
|
|
|
let(:old_project) { create(:empty_project) }
|
|
|
|
let(:new_project) { create(:empty_project) }
|
2016-05-05 02:59:09 -04:00
|
|
|
let(:milestone1) { create(:milestone, project_id: old_project.id, title: 'v9.0') }
|
2016-02-24 05:52:02 -05:00
|
|
|
|
2016-02-24 07:20:08 -05:00
|
|
|
let(:old_issue) do
|
|
|
|
create(:issue, title: title, description: description,
|
2016-04-26 13:03:09 -04:00
|
|
|
project: old_project, author: author, milestone: milestone1)
|
2016-02-24 07:20:08 -05:00
|
|
|
end
|
|
|
|
|
2016-02-24 05:52:02 -05:00
|
|
|
let(:move_service) do
|
2016-03-20 04:28:06 -04:00
|
|
|
described_class.new(old_project, user)
|
2016-02-22 06:41:33 -05:00
|
|
|
end
|
2016-02-15 08:41:40 -05:00
|
|
|
|
2016-02-22 06:41:33 -05:00
|
|
|
shared_context 'user can move issue' do
|
|
|
|
before do
|
2016-03-14 03:30:37 -04:00
|
|
|
old_project.team << [user, :reporter]
|
|
|
|
new_project.team << [user, :reporter]
|
2016-04-26 12:20:19 -04:00
|
|
|
|
2016-10-24 04:05:21 -04:00
|
|
|
labels = Array.new(2) { |x| "label%d" % (x + 1) }
|
2016-10-23 23:57:06 -04:00
|
|
|
|
|
|
|
labels.each do |label|
|
2016-04-28 04:52:23 -04:00
|
|
|
old_issue.labels << create(:label,
|
|
|
|
project_id: old_project.id,
|
2016-04-28 05:21:41 -04:00
|
|
|
title: label)
|
2016-04-26 12:20:19 -04:00
|
|
|
|
2016-10-23 23:57:06 -04:00
|
|
|
new_project.labels << create(:label, title: label)
|
|
|
|
end
|
2016-10-24 04:05:21 -04:00
|
|
|
end
|
2016-02-22 06:41:33 -05:00
|
|
|
end
|
2016-03-15 10:01:26 -04:00
|
|
|
|
2016-03-18 09:48:55 -04:00
|
|
|
describe '#execute' do
|
|
|
|
shared_context 'issue move executed' do
|
2016-04-28 04:52:23 -04:00
|
|
|
let!(:milestone2) do
|
|
|
|
create(:milestone, project_id: new_project.id, title: 'v9.0')
|
|
|
|
end
|
2016-06-01 12:33:49 -04:00
|
|
|
let!(:award_emoji) { create(:award_emoji, awardable: old_issue) }
|
2016-04-28 04:52:23 -04:00
|
|
|
|
2016-03-18 09:48:55 -04:00
|
|
|
let!(:new_issue) { move_service.execute(old_issue, new_project) }
|
2016-02-15 08:41:40 -05:00
|
|
|
end
|
|
|
|
|
2016-03-18 09:48:55 -04:00
|
|
|
context 'issue movable' do
|
|
|
|
include_context 'user can move issue'
|
2016-02-17 09:59:25 -05:00
|
|
|
|
2016-02-22 05:00:40 -05:00
|
|
|
context 'generic issue' do
|
|
|
|
include_context 'issue move executed'
|
|
|
|
|
|
|
|
it 'creates a new issue in a new project' do
|
|
|
|
expect(new_issue.project).to eq new_project
|
|
|
|
end
|
|
|
|
|
2016-04-26 12:20:19 -04:00
|
|
|
it 'assigns milestone to new issue' do
|
2016-04-26 13:03:09 -04:00
|
|
|
expect(new_issue.reload.milestone.title).to eq 'v9.0'
|
2016-05-05 02:59:09 -04:00
|
|
|
expect(new_issue.reload.milestone).to eq(milestone2)
|
2016-04-26 12:20:19 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'assign labels to new issue' do
|
|
|
|
expected_label_titles = new_issue.reload.labels.map(&:title)
|
|
|
|
expect(expected_label_titles).to include 'label1'
|
|
|
|
expect(expected_label_titles).to include 'label2'
|
|
|
|
expect(expected_label_titles.size).to eq 2
|
2016-05-05 02:59:09 -04:00
|
|
|
|
|
|
|
new_issue.labels.each do |label|
|
|
|
|
expect(new_project.labels).to include(label)
|
|
|
|
expect(old_project.labels).not_to include(label)
|
|
|
|
end
|
2016-04-26 12:20:19 -04:00
|
|
|
end
|
|
|
|
|
2016-02-22 05:00:40 -05:00
|
|
|
it 'rewrites issue title' do
|
|
|
|
expect(new_issue.title).to eq title
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'rewrites issue description' do
|
2016-03-15 06:35:40 -04:00
|
|
|
expect(new_issue.description).to eq description
|
2016-02-22 05:00:40 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'adds system note to old issue at the end' do
|
2016-11-23 01:55:23 -05:00
|
|
|
expect(old_issue.notes.last.note).to start_with 'moved to'
|
2016-02-22 05:00:40 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'adds system note to new issue at the end' do
|
2016-11-23 01:55:23 -05:00
|
|
|
expect(new_issue.notes.last.note).to start_with 'moved from'
|
2016-02-22 05:00:40 -05:00
|
|
|
end
|
2016-02-22 09:18:39 -05:00
|
|
|
|
|
|
|
it 'closes old issue' do
|
|
|
|
expect(old_issue.closed?).to be true
|
|
|
|
end
|
|
|
|
|
2016-02-23 08:18:54 -05:00
|
|
|
it 'persists new issue' do
|
|
|
|
expect(new_issue.persisted?).to be true
|
2016-02-22 09:18:39 -05:00
|
|
|
end
|
2016-02-24 06:18:46 -05:00
|
|
|
|
2016-03-14 03:25:37 -04:00
|
|
|
it 'persists all changes' do
|
2016-02-24 06:18:46 -05:00
|
|
|
expect(old_issue.changed?).to be false
|
2016-03-15 05:14:39 -04:00
|
|
|
expect(new_issue.changed?).to be false
|
2016-02-24 06:18:46 -05:00
|
|
|
end
|
2016-02-24 07:20:08 -05:00
|
|
|
|
2016-03-14 03:25:37 -04:00
|
|
|
it 'preserves author' do
|
|
|
|
expect(new_issue.author).to eq author
|
2016-02-24 07:20:08 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'creates a new internal id for issue' do
|
|
|
|
expect(new_issue.iid).to be 1
|
|
|
|
end
|
2016-03-17 06:11:22 -04:00
|
|
|
|
|
|
|
it 'marks issue as moved' do
|
|
|
|
expect(old_issue.moved?).to eq true
|
|
|
|
expect(old_issue.moved_to).to eq new_issue
|
|
|
|
end
|
2016-03-23 07:41:53 -04:00
|
|
|
|
|
|
|
it 'preserves create time' do
|
|
|
|
expect(old_issue.created_at).to eq new_issue.created_at
|
|
|
|
end
|
2016-06-01 12:33:49 -04:00
|
|
|
|
|
|
|
it 'moves the award emoji' do
|
|
|
|
expect(old_issue.award_emoji.first.name).to eq new_issue.reload.award_emoji.first.name
|
|
|
|
end
|
2016-02-17 09:59:25 -05:00
|
|
|
end
|
2016-02-15 09:14:57 -05:00
|
|
|
|
2016-02-23 08:18:54 -05:00
|
|
|
context 'issue with notes' do
|
2016-02-24 09:38:52 -05:00
|
|
|
context 'notes without references' do
|
|
|
|
let(:notes_params) do
|
|
|
|
[{ system: false, note: 'Some comment 1' },
|
|
|
|
{ system: true, note: 'Some system note' },
|
|
|
|
{ system: false, note: 'Some comment 2' }]
|
|
|
|
end
|
2016-02-22 05:00:40 -05:00
|
|
|
|
2016-02-24 10:27:06 -05:00
|
|
|
let(:notes_contents) { notes_params.map { |n| n[:note] } }
|
|
|
|
|
2016-02-24 09:38:52 -05:00
|
|
|
before do
|
|
|
|
note_params = { noteable: old_issue, project: old_project, author: author }
|
|
|
|
notes_params.each do |note|
|
|
|
|
create(:note, note_params.merge(note))
|
|
|
|
end
|
|
|
|
end
|
2016-02-22 05:00:40 -05:00
|
|
|
|
2016-02-24 09:38:52 -05:00
|
|
|
include_context 'issue move executed'
|
|
|
|
|
|
|
|
let(:all_notes) { new_issue.notes.order('id ASC') }
|
|
|
|
let(:system_notes) { all_notes.system }
|
|
|
|
let(:user_notes) { all_notes.user }
|
|
|
|
|
|
|
|
it 'rewrites existing notes in valid order' do
|
2016-02-24 10:27:06 -05:00
|
|
|
expect(all_notes.pluck(:note).first(3)).to eq notes_contents
|
2016-02-24 09:38:52 -05:00
|
|
|
end
|
2016-02-22 05:00:40 -05:00
|
|
|
|
2016-02-24 09:38:52 -05:00
|
|
|
it 'adds a system note about move after rewritten notes' do
|
2016-11-23 01:55:23 -05:00
|
|
|
expect(system_notes.last.note).to match /^moved from/
|
2016-02-24 09:38:52 -05:00
|
|
|
end
|
2016-02-22 05:00:40 -05:00
|
|
|
|
2016-02-24 09:38:52 -05:00
|
|
|
it 'preserves orignal author of comment' do
|
|
|
|
expect(user_notes.pluck(:author_id)).to all(eq(author.id))
|
|
|
|
end
|
2016-03-23 07:41:53 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'note that has been updated' do
|
|
|
|
let!(:note) do
|
|
|
|
create(:note, noteable: old_issue, project: old_project,
|
|
|
|
author: author, updated_at: Date.yesterday,
|
|
|
|
created_at: Date.yesterday)
|
|
|
|
end
|
|
|
|
|
|
|
|
include_context 'issue move executed'
|
2016-03-20 12:05:21 -04:00
|
|
|
|
|
|
|
it 'preserves time when note has been created at' do
|
2016-03-23 07:41:53 -04:00
|
|
|
expect(new_issue.notes.first.created_at).to eq note.created_at
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'preserves time when note has been updated at' do
|
|
|
|
expect(new_issue.notes.first.updated_at).to eq note.updated_at
|
2016-03-20 12:05:21 -04:00
|
|
|
end
|
2016-02-22 05:00:40 -05:00
|
|
|
end
|
|
|
|
|
2016-02-24 09:38:52 -05:00
|
|
|
context 'notes with references' do
|
|
|
|
before do
|
|
|
|
create(:merge_request, source_project: old_project)
|
|
|
|
create(:note, noteable: old_issue, project: old_project, author: author,
|
2016-02-26 06:04:29 -05:00
|
|
|
note: 'Note with reference to merge request !1')
|
2016-02-24 09:38:52 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
include_context 'issue move executed'
|
|
|
|
let(:new_note) { new_issue.notes.first }
|
|
|
|
|
|
|
|
it 'rewrites references using a cross reference to old project' do
|
|
|
|
expect(new_note.note)
|
2016-11-02 19:49:13 -04:00
|
|
|
.to eq "Note with reference to merge request #{old_project.to_reference(new_project)}!1"
|
2016-02-24 09:38:52 -05:00
|
|
|
end
|
2016-02-22 05:00:40 -05:00
|
|
|
end
|
2016-03-24 07:28:43 -04:00
|
|
|
|
|
|
|
context 'issue description with uploads' do
|
2016-03-29 07:21:57 -04:00
|
|
|
let(:uploader) { build(:file_uploader, project: old_project) }
|
2016-03-31 03:47:05 -04:00
|
|
|
let(:description) { "Text and #{uploader.to_markdown}" }
|
2016-03-24 07:28:43 -04:00
|
|
|
|
|
|
|
include_context 'issue move executed'
|
|
|
|
|
|
|
|
it 'rewrites uploads in description' do
|
2016-05-23 19:37:59 -04:00
|
|
|
expect(new_issue.description).not_to eq description
|
2016-03-24 07:28:43 -04:00
|
|
|
expect(new_issue.description)
|
|
|
|
.to match(/Text and #{FileUploader::MARKDOWN_PATTERN}/)
|
2016-05-23 19:37:59 -04:00
|
|
|
expect(new_issue.description).not_to include uploader.secret
|
2016-03-24 07:28:43 -04:00
|
|
|
end
|
|
|
|
end
|
2016-02-17 09:59:25 -05:00
|
|
|
end
|
2016-02-23 08:18:54 -05:00
|
|
|
|
2016-10-25 01:33:49 -04:00
|
|
|
describe 'rewriting references' do
|
2016-02-23 08:18:54 -05:00
|
|
|
include_context 'issue move executed'
|
|
|
|
|
2016-10-25 01:33:49 -04:00
|
|
|
context 'issue references' do
|
2016-02-23 08:18:54 -05:00
|
|
|
let(:another_issue) { create(:issue, project: old_project) }
|
|
|
|
let(:description) { "Some description #{another_issue.to_reference}" }
|
|
|
|
|
|
|
|
it 'rewrites referenced issues creating cross project reference' do
|
|
|
|
expect(new_issue.description)
|
2016-11-02 19:49:13 -04:00
|
|
|
.to eq "Some description #{another_issue.to_reference(new_project)}"
|
2016-02-23 08:18:54 -05:00
|
|
|
end
|
|
|
|
end
|
2016-10-25 01:33:49 -04:00
|
|
|
|
|
|
|
context "user references" do
|
|
|
|
let(:another_issue) { create(:issue, project: old_project) }
|
|
|
|
let(:description) { "Some description #{user.to_reference}" }
|
|
|
|
|
|
|
|
it "doesn't throw any errors for issues containing user references" do
|
|
|
|
expect(new_issue.description)
|
|
|
|
.to eq "Some description #{user.to_reference}"
|
|
|
|
end
|
|
|
|
end
|
2016-02-23 08:18:54 -05:00
|
|
|
end
|
2016-02-22 06:41:33 -05:00
|
|
|
|
2016-03-18 09:48:55 -04:00
|
|
|
context 'moving to same project' do
|
|
|
|
let(:new_project) { old_project }
|
2016-02-22 06:41:33 -05:00
|
|
|
|
2016-03-18 09:48:55 -04:00
|
|
|
it 'raises error' do
|
|
|
|
expect { move_service.execute(old_issue, new_project) }
|
|
|
|
.to raise_error(StandardError, /Cannot move issue/)
|
|
|
|
end
|
2016-02-22 06:41:33 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-03-18 09:48:55 -04:00
|
|
|
describe 'move permissions' do
|
|
|
|
let(:move) { move_service.execute(old_issue, new_project) }
|
2016-02-22 06:41:33 -05:00
|
|
|
|
2016-03-14 03:30:37 -04:00
|
|
|
context 'user is reporter in both projects' do
|
2016-02-22 06:41:33 -05:00
|
|
|
include_context 'user can move issue'
|
2016-05-23 19:37:59 -04:00
|
|
|
it { expect { move }.not_to raise_error }
|
2016-02-22 06:41:33 -05:00
|
|
|
end
|
|
|
|
|
2016-03-14 03:30:37 -04:00
|
|
|
context 'user is reporter only in new project' do
|
|
|
|
before { new_project.team << [user, :reporter] }
|
2016-03-18 09:48:55 -04:00
|
|
|
it { expect { move }.to raise_error(StandardError, /permissions/) }
|
2016-02-22 06:41:33 -05:00
|
|
|
end
|
|
|
|
|
2016-03-14 03:30:37 -04:00
|
|
|
context 'user is reporter only in old project' do
|
|
|
|
before { old_project.team << [user, :reporter] }
|
2016-03-18 09:48:55 -04:00
|
|
|
it { expect { move }.to raise_error(StandardError, /permissions/) }
|
2016-02-22 06:41:33 -05:00
|
|
|
end
|
|
|
|
|
2016-03-14 03:30:37 -04:00
|
|
|
context 'user is reporter in one project and guest in another' do
|
2016-02-22 06:41:33 -05:00
|
|
|
before do
|
2016-03-14 03:30:37 -04:00
|
|
|
new_project.team << [user, :guest]
|
|
|
|
old_project.team << [user, :reporter]
|
2016-02-22 06:41:33 -05:00
|
|
|
end
|
2016-02-16 05:47:00 -05:00
|
|
|
|
2016-03-18 09:48:55 -04:00
|
|
|
it { expect { move }.to raise_error(StandardError, /permissions/) }
|
2016-02-17 09:59:25 -05:00
|
|
|
end
|
2016-03-17 06:11:22 -04:00
|
|
|
|
|
|
|
context 'issue has already been moved' do
|
|
|
|
include_context 'user can move issue'
|
|
|
|
|
|
|
|
let(:moved_to_issue) { create(:issue) }
|
|
|
|
|
|
|
|
let(:old_issue) do
|
|
|
|
create(:issue, project: old_project, author: author,
|
|
|
|
moved_to: moved_to_issue)
|
|
|
|
end
|
|
|
|
|
2016-03-18 09:48:55 -04:00
|
|
|
it { expect { move }.to raise_error(StandardError, /permissions/) }
|
2016-03-17 06:11:22 -04:00
|
|
|
end
|
2016-03-23 04:39:37 -04:00
|
|
|
|
|
|
|
context 'issue is not persisted' do
|
|
|
|
include_context 'user can move issue'
|
|
|
|
let(:old_issue) { build(:issue, project: old_project, author: author) }
|
|
|
|
it { expect { move }.to raise_error(StandardError, /permissions/) }
|
|
|
|
end
|
2016-02-15 09:14:57 -05:00
|
|
|
end
|
2016-10-23 23:57:06 -04:00
|
|
|
|
|
|
|
context 'movable issue with no assigned labels' do
|
|
|
|
before do
|
|
|
|
old_project.team << [user, :reporter]
|
|
|
|
new_project.team << [user, :reporter]
|
|
|
|
|
2016-10-24 04:05:21 -04:00
|
|
|
labels = Array.new(2) { |x| "label%d" % (x + 1) }
|
2016-10-23 23:57:06 -04:00
|
|
|
|
|
|
|
labels.each do |label|
|
|
|
|
new_project.labels << create(:label, title: label)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
include_context 'issue move executed'
|
|
|
|
|
|
|
|
it 'does not assign labels to new issue' do
|
|
|
|
expected_label_titles = new_issue.reload.labels.map(&:title)
|
|
|
|
expect(expected_label_titles.size).to eq 0
|
|
|
|
end
|
|
|
|
end
|
2016-02-15 08:41:40 -05:00
|
|
|
end
|
|
|
|
end
|