move_service_spec.rb 6.7 KB
Newer Older
1 2
# frozen_string_literal: true

3 4
require 'spec_helper'

5
describe Issues::MoveService do
6
  let(:user) { create(:user) }
7
  let(:author) { create(:user) }
8
  let(:title) { 'Some issue' }
9
  let(:description) { "Some issue description with mention to #{user.to_reference}" }
10 11 12 13 14
  let(:group) { create(:group, :private) }
  let(:sub_group_1) { create(:group, :private, parent: group) }
  let(:sub_group_2) { create(:group, :private, parent: group) }
  let(:old_project) { create(:project, namespace: sub_group_1) }
  let(:new_project) { create(:project, namespace: sub_group_2) }
15

16
  let(:old_issue) do
17
    create(:issue, title: title, description: description, project: old_project, author: author)
18 19
  end

20
  subject(:move_service) do
21
    described_class.new(old_project, user)
22
  end
23

24 25
  shared_context 'user can move issue' do
    before do
26 27
      old_project.add_reporter(user)
      new_project.add_reporter(user)
28
    end
29
  end
30

31 32
  describe '#execute' do
    shared_context 'issue move executed' do
33
      let!(:award_emoji) { create(:award_emoji, awardable: old_issue) }
L
Long Nguyen 已提交
34

35
      let!(:new_issue) { move_service.execute(old_issue, new_project) }
36 37
    end

38
    context 'issue movable' do
39 40 41
      let!(:note_with_mention) { create(:note, noteable: old_issue, author: author, project: old_project, note: "note with mention #{user.to_reference}") }
      let!(:note_with_no_mention) { create(:note, noteable: old_issue, author: author, project: old_project, note: "note without mention") }

42
      include_context 'user can move issue'
43

44 45 46 47 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

        it 'rewrites issue title' do
          expect(new_issue.title).to eq title
        end

        it 'rewrites issue description' do
56
          expect(new_issue.description).to eq description
57 58 59
        end

        it 'adds system note to old issue at the end' do
60
          expect(old_issue.notes.last.note).to start_with 'moved to'
61 62 63
        end

        it 'adds system note to new issue at the end' do
64
          expect(new_issue.notes.last.note).to start_with 'moved from'
65
        end
66 67 68 69 70

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

71 72
        it 'persists new issue' do
          expect(new_issue.persisted?).to be true
73
        end
74

75
        it 'persists all changes' do
76
          expect(old_issue.changed?).to be false
77
          expect(new_issue.changed?).to be false
78
        end
79

80 81
        it 'preserves author' do
          expect(new_issue.author).to eq author
82 83 84 85 86
        end

        it 'creates a new internal id for issue' do
          expect(new_issue.iid).to be 1
        end
87 88 89 90 91

        it 'marks issue as moved' do
          expect(old_issue.moved?).to eq true
          expect(old_issue.moved_to).to eq new_issue
        end
92 93 94 95

        it 'preserves create time' do
          expect(old_issue.created_at).to eq new_issue.created_at
        end
96 97 98 99

        it 'moves the award emoji' do
          expect(old_issue.award_emoji.first.name).to eq new_issue.reload.award_emoji.first.name
        end
100 101 102 103 104 105 106 107 108

        context 'when issue has notes with mentions' do
          it 'saves user mentions with actual mentions for new issue' do
            expect(new_issue.user_mentions.where(note_id: nil).first.mentioned_users_ids).to match_array([user.id])
            expect(new_issue.user_mentions.where.not(note_id: nil).first.mentioned_users_ids).to match_array([user.id])
            expect(new_issue.user_mentions.where.not(note_id: nil).count).to eq 1
            expect(new_issue.user_mentions.count).to eq 2
          end
        end
109
      end
110

111 112
      context 'issue with assignee' do
        let(:assignee) { create(:user) }
113

114 115
        before do
          old_issue.assignees = [assignee]
116
        end
117

118 119
        it 'preserves assignee with access to the new issue' do
          new_project.add_reporter(assignee)
120

121
          new_issue = move_service.execute(old_issue, new_project)
122

123
          expect(new_issue.assignees).to eq([assignee])
124
        end
T
Timothy Andrew 已提交
125

126 127
        it 'ignores assignee without access to the new issue' do
          new_issue = move_service.execute(old_issue, new_project)
T
Timothy Andrew 已提交
128

129
          expect(new_issue.assignees).to be_empty
T
Timothy Andrew 已提交
130
        end
131
      end
132

133 134
      context 'moving to same project' do
        let(:new_project) { old_project }
135

136 137 138 139
        it 'raises error' do
          expect { move_service.execute(old_issue, new_project) }
            .to raise_error(StandardError, /Cannot move issue/)
        end
140
      end
141 142

      context 'project issue hooks' do
143
        let!(:hook) { create(:project_hook, project: old_project, issues_events: true) }
144 145

        it 'executes project issue hooks' do
146 147 148
          allow_next_instance_of(WebHookService) do |instance|
            allow(instance).to receive(:execute)
          end
149

150 151 152 153 154 155 156
          # Ideally, we'd test that `WebHookWorker.jobs.size` increased by 1,
          # but since the entire spec run takes place in a transaction, we never
          # actually get to the `after_commit` hook that queues these jobs.
          expect { move_service.execute(old_issue, new_project) }
            .not_to raise_error # Sidekiq::Worker::EnqueueFromTransactionError
        end
      end
157 158
    end

159 160
    describe 'move permissions' do
      let(:move) { move_service.execute(old_issue, new_project) }
161

162
      context 'user is reporter in both projects' do
163
        include_context 'user can move issue'
164
        it { expect { move }.not_to raise_error }
165 166
      end

167
      context 'user is reporter only in new project' do
168
        before do
169
          new_project.add_reporter(user)
170 171
        end

172
        it { expect { move }.to raise_error(StandardError, /permissions/) }
173 174
      end

175
      context 'user is reporter only in old project' do
176
        before do
177
          old_project.add_reporter(user)
178 179
        end

180
        it { expect { move }.to raise_error(StandardError, /permissions/) }
181 182
      end

183
      context 'user is reporter in one project and guest in another' do
184
        before do
185 186
          new_project.add_guest(user)
          old_project.add_reporter(user)
187
        end
188

189
        it { expect { move }.to raise_error(StandardError, /permissions/) }
190
      end
191 192 193 194 195 196 197 198 199 200 201

      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

202
        it { expect { move }.to raise_error(StandardError, /permissions/) }
203
      end
204 205 206 207

      context 'issue is not persisted' do
        include_context 'user can move issue'
        let(:old_issue) { build(:issue, project: old_project, author: author) }
208

209 210
        it { expect { move }.to raise_error(StandardError, /permissions/) }
      end
211
    end
212 213
  end
end