move_service_spec.rb 8.9 KB
Newer Older
1 2 3 4
require 'spec_helper'

describe Issues::MoveService, services: true do
  let(:user) { create(:user) }
5
  let(:author) { create(:user) }
6 7
  let(:title) { 'Some issue' }
  let(:description) { 'Some issue description' }
8
  let(:old_project) { create(:project) }
9
  let(:new_project) { create(:project) }
L
Long Nguyen 已提交
10
  let(:milestone1) { create(:milestone, project_id: old_project.id, title: 'v9.0') }
11

12 13
  let(:old_issue) do
    create(:issue, title: title, description: description,
L
Long Nguyen 已提交
14
                   project: old_project, author: author, milestone: milestone1)
15 16
  end

17
  let(:move_service) do
18
    described_class.new(old_project, user)
19
  end
20

21 22
  shared_context 'user can move issue' do
    before do
23 24
      old_project.team << [user, :reporter]
      new_project.team << [user, :reporter]
25

L
Long Nguyen 已提交
26 27 28
      ['label1', 'label2'].each do |label|
        old_issue.labels << create(:label,
          project_id: old_project.id,
L
Long Nguyen 已提交
29
          title: label)
L
Long Nguyen 已提交
30
      end
31 32 33

      new_project.labels << create(:label, title: 'label1')
      new_project.labels << create(:label, title: 'label2')
34 35
    end
  end
36

37 38
  describe '#execute' do
    shared_context 'issue move executed' do
L
Long Nguyen 已提交
39 40 41 42
      let!(:milestone2) do
        create(:milestone, project_id: new_project.id, title: 'v9.0')
      end

43
      let!(:new_issue) { move_service.execute(old_issue, new_project) }
44 45
    end

46 47
    context 'issue movable' do
      include_context 'user can move issue'
48

49 50 51 52 53 54 55
      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

56
        it 'assigns milestone to new issue' do
L
Long Nguyen 已提交
57
          expect(new_issue.reload.milestone.title).to eq 'v9.0'
L
Long Nguyen 已提交
58
          expect(new_issue.reload.milestone).to eq(milestone2)
59 60 61 62 63 64 65
        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
L
Long Nguyen 已提交
66 67 68 69 70

          new_issue.labels.each do |label|
            expect(new_project.labels).to include(label)
            expect(old_project.labels).not_to include(label)
          end
71 72
        end

73 74 75 76 77
        it 'rewrites issue title' do
          expect(new_issue.title).to eq title
        end

        it 'rewrites issue description' do
78
          expect(new_issue.description).to eq description
79 80 81 82 83 84 85 86 87
        end

        it 'adds system note to old issue at the end' do
          expect(old_issue.notes.last.note).to match /^Moved to/
        end

        it 'adds system note to new issue at the end' do
          expect(new_issue.notes.last.note).to match /^Moved from/
        end
88 89 90 91 92

        it 'closes old issue' do
          expect(old_issue.closed?).to be true
        end

93 94
        it 'persists new issue' do
          expect(new_issue.persisted?).to be true
95
        end
96

97
        it 'persists all changes' do
98
          expect(old_issue.changed?).to be false
99
          expect(new_issue.changed?).to be false
100
        end
101

102 103
        it 'preserves author' do
          expect(new_issue.author).to eq author
104 105 106 107 108
        end

        it 'creates a new internal id for issue' do
          expect(new_issue.iid).to be 1
        end
109 110 111 112 113

        it 'marks issue as moved' do
          expect(old_issue.moved?).to eq true
          expect(old_issue.moved_to).to eq new_issue
        end
114 115 116 117

        it 'preserves create time' do
          expect(old_issue.created_at).to eq new_issue.created_at
        end
118
      end
119

120
      context 'issue with notes' do
121 122 123 124 125 126
        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
127

128 129
          let(:notes_contents) { notes_params.map { |n| n[:note] } }

130 131 132 133 134 135
          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
136

137 138 139 140 141 142 143
          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
144
            expect(all_notes.pluck(:note).first(3)).to eq notes_contents
145
          end
146

147 148 149
          it 'adds a system note about move after rewritten notes' do
            expect(system_notes.last.note).to match /^Moved from/
          end
150

151 152 153
          it 'preserves orignal author of comment' do
            expect(user_notes.pluck(:author_id)).to all(eq(author.id))
          end
154 155 156 157 158 159 160 161 162 163
        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'
164 165

          it 'preserves time when note has been created at' do
166 167 168 169 170
            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
171
          end
172 173
        end

174 175 176 177
        context 'notes with references' do
          before do
            create(:merge_request, source_project: old_project)
            create(:note, noteable: old_issue, project: old_project, author: author,
178
                          note: 'Note with reference to merge request !1')
179 180 181 182 183 184 185 186 187
          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)
              .to eq "Note with reference to merge request #{old_project.to_reference}!1"
          end
188
        end
189 190

        context 'issue description with uploads' do
191
          let(:uploader) { build(:file_uploader, project: old_project) }
192
          let(:description) { "Text and #{uploader.to_markdown}" }
193 194 195 196

          include_context 'issue move executed'

          it 'rewrites uploads in description' do
197
            expect(new_issue.description).not_to eq description
198 199
            expect(new_issue.description)
              .to match(/Text and #{FileUploader::MARKDOWN_PATTERN}/)
200
            expect(new_issue.description).not_to include uploader.secret
201 202
          end
        end
203
      end
204 205 206 207 208 209 210 211 212 213 214 215 216 217

      describe 'rewritting references' do
        include_context 'issue move executed'

        context 'issue reference' do
          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)
              .to eq "Some description #{old_project.to_reference}#{another_issue.to_reference}"
          end
        end
      end
218

219 220
      context 'moving to same project' do
        let(:new_project) { old_project }
221

222 223 224 225
        it 'raises error' do
          expect { move_service.execute(old_issue, new_project) }
            .to raise_error(StandardError, /Cannot move issue/)
        end
226 227 228
      end
    end

229 230
    describe 'move permissions' do
      let(:move) { move_service.execute(old_issue, new_project) }
231

232
      context 'user is reporter in both projects' do
233
        include_context 'user can move issue'
234
        it { expect { move }.not_to raise_error }
235 236
      end

237 238
      context 'user is reporter only in new project' do
        before { new_project.team << [user, :reporter] }
239
        it { expect { move }.to raise_error(StandardError, /permissions/) }
240 241
      end

242 243
      context 'user is reporter only in old project' do
        before { old_project.team << [user, :reporter] }
244
        it { expect { move }.to raise_error(StandardError, /permissions/) }
245 246
      end

247
      context 'user is reporter in one project and guest in another' do
248
        before do
249 250
          new_project.team << [user, :guest]
          old_project.team << [user, :reporter]
251
        end
252

253
        it { expect { move }.to raise_error(StandardError, /permissions/) }
254
      end
255 256 257 258 259 260 261 262 263 264 265

      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

266
        it { expect { move }.to raise_error(StandardError, /permissions/) }
267
      end
268 269 270 271 272 273

      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
274
    end
275 276
  end
end