require 'spec_helper'

describe Gitlab::SlashCommands::IssueMove, service: true do
  describe '#match' do
    shared_examples_for 'move command' do |text_command|
      it 'can be parsed to extract the needed fields' do
        match_data = described_class.match(text_command)

        expect(match_data['iid']).to eq('123456')
        expect(match_data['project_path']).to eq('gitlab/gitlab-ci')
      end
    end

    it_behaves_like 'move command', 'issue move #123456 to gitlab/gitlab-ci'
    it_behaves_like 'move command', 'issue move #123456 gitlab/gitlab-ci'
    it_behaves_like 'move command', 'issue move #123456 gitlab/gitlab-ci '
    it_behaves_like 'move command', 'issue move 123456 to gitlab/gitlab-ci'
    it_behaves_like 'move command', 'issue move 123456 gitlab/gitlab-ci'
    it_behaves_like 'move command', 'issue move 123456 gitlab/gitlab-ci '
  end

  describe '#execute' do
    set(:user) { create(:user) }
    set(:issue) { create(:issue) }
    set(:chat_name) { create(:chat_name, user: user) }
    set(:project) { issue.project }
    set(:other_project) { create(:project, namespace: project.namespace) }

    before do
      [project, other_project].each { |prj| prj.add_maintainer(user) }
    end

    subject { described_class.new(project, chat_name) }

    def process_message(message)
      subject.execute(described_class.match(message))
    end

    context 'when the user can move the issue' do
      context 'when the move fails' do
        it 'returns the error message' do
          message = "issue move #{issue.iid} #{project.full_path}"

          expect(process_message(message)).to include(response_type: :ephemeral,
                                                      text: a_string_matching('Cannot move issue'))
        end
      end

      context 'when the move succeeds' do
        let(:message) { "issue move #{issue.iid} #{other_project.full_path}" }

        it 'moves the issue to the new destination' do
          expect { process_message(message) }.to change { Issue.count }.by(1)

          new_issue = issue.reload.moved_to

          expect(new_issue.state).to eq('opened')
          expect(new_issue.project_id).to eq(other_project.id)
          expect(new_issue.author_id).to eq(issue.author_id)

          expect(issue.state).to eq('closed')
          expect(issue.project_id).to eq(project.id)
        end

        it 'returns the new issue' do
          expect(process_message(message))
            .to include(response_type: :in_channel,
                        attachments: [a_hash_including(title_link: a_string_including(other_project.full_path))])
        end

        it 'mentions the old issue' do
          expect(process_message(message))
            .to include(attachments: [a_hash_including(pretext: a_string_including(project.full_path))])
        end
      end
    end

    context 'when the issue does not exist' do
      it 'returns not found' do
        message = "issue move #{issue.iid.succ} #{other_project.full_path}"

        expect(process_message(message)).to include(response_type: :ephemeral,
                                                    text: a_string_matching('not found'))
      end
    end

    context 'when the target project does not exist' do
      it 'returns not found' do
        message = "issue move #{issue.iid} #{other_project.full_path}/foo"

        expect(process_message(message)).to include(response_type: :ephemeral,
                                                    text: a_string_matching('not found'))
      end
    end

    context 'when the user cannot see the target project' do
      it 'returns not found' do
        message = "issue move #{issue.iid} #{other_project.full_path}"
        other_project.team.truncate

        expect(process_message(message)).to include(response_type: :ephemeral,
                                                    text: a_string_matching('not found'))
      end
    end

    context 'when the user does not have the required permissions on the target project' do
      it 'returns the error message' do
        message = "issue move #{issue.iid} #{other_project.full_path}"
        other_project.team.truncate
        other_project.team.add_guest(user)

        expect(process_message(message)).to include(response_type: :ephemeral,
                                                    text: a_string_matching('Cannot move issue'))
      end
    end
  end
end
