require 'spec_helper'

describe Gitlab::GithubImport::Importer::IssueImporter, :clean_gitlab_redis_cache do
  let(:project) { create(:project) }
  let(:client) { double(:client) }
  let(:user) { create(:user) }
  let(:milestone) { create(:milestone, project: project) }
  let(:created_at) { Time.new(2017, 1, 1, 12, 00) }
  let(:updated_at) { Time.new(2017, 1, 1, 12, 15) }

  let(:issue) do
    Gitlab::GithubImport::Representation::Issue.new(
      iid: 42,
      title: 'My Issue',
      description: 'This is my issue',
      milestone_number: 1,
      state: :opened,
      assignees: [
        Gitlab::GithubImport::Representation::User.new(id: 4, login: 'alice'),
        Gitlab::GithubImport::Representation::User.new(id: 5, login: 'bob')
      ],
      label_names: %w[bug],
      author: Gitlab::GithubImport::Representation::User.new(id: 4, login: 'alice'),
      created_at: created_at,
      updated_at: updated_at,
      pull_request: false
    )
  end

  describe '.import_if_issue' do
    it 'imports an issuable if it is a regular issue' do
      importer = double(:importer)

      expect(described_class)
        .to receive(:new)
        .with(issue, project, client)
        .and_return(importer)

      expect(importer).to receive(:execute)

      described_class.import_if_issue(issue, project, client)
    end

    it 'does not import the issuable if it is a pull request' do
      expect(issue).to receive(:pull_request?).and_return(true)

      expect(described_class).not_to receive(:new)

      described_class.import_if_issue(issue, project, client)
    end
  end

  describe '#execute' do
    let(:importer) { described_class.new(issue, project, client) }

    it 'creates the issue and assignees' do
      expect(importer)
        .to receive(:create_issue)
        .and_return(10)

      expect(importer)
        .to receive(:create_assignees)
        .with(10)

      expect(importer.issuable_finder)
        .to receive(:cache_database_id)
        .with(10)

      importer.execute
    end
  end

  describe '#create_issue' do
    let(:importer) { described_class.new(issue, project, client) }

    before do
      allow(importer.milestone_finder)
        .to receive(:id_for)
        .with(issue)
        .and_return(milestone.id)
    end

    context 'when the issue author could be found' do
      it 'creates the issue with the found author as the issue author' do
        allow(importer.user_finder)
          .to receive(:author_id_for)
          .with(issue)
          .and_return([user.id, true])

        expect(Gitlab::GithubImport)
          .to receive(:insert_and_return_id)
          .with(
            {
              iid: 42,
              title: 'My Issue',
              author_id: user.id,
              project_id: project.id,
              description: 'This is my issue',
              milestone_id: milestone.id,
              state: :opened,
              created_at: created_at,
              updated_at: updated_at
            },
            project.issues
          )
          .and_call_original

        importer.create_issue
      end
    end

    context 'when the issue author could not be found' do
      it 'creates the issue with the project creator as the issue author' do
        allow(importer.user_finder)
          .to receive(:author_id_for)
          .with(issue)
          .and_return([project.creator_id, false])

        expect(Gitlab::GithubImport)
          .to receive(:insert_and_return_id)
          .with(
            {
              iid: 42,
              title: 'My Issue',
              author_id: project.creator_id,
              project_id: project.id,
              description: "*Created by: alice*\n\nThis is my issue",
              milestone_id: milestone.id,
              state: :opened,
              created_at: created_at,
              updated_at: updated_at
            },
            project.issues
          )
          .and_call_original

        importer.create_issue
      end
    end

    context 'when the import fails due to a foreign key error' do
      it 'does not raise any errors' do
        allow(importer.user_finder)
          .to receive(:author_id_for)
          .with(issue)
          .and_return([user.id, true])

        expect(Gitlab::GithubImport)
          .to receive(:insert_and_return_id)
          .and_raise(ActiveRecord::InvalidForeignKey, 'invalid foreign key')

        expect { importer.create_issue }.not_to raise_error
      end
    end

    it 'produces a valid Issue' do
      allow(importer.user_finder)
        .to receive(:author_id_for)
        .with(issue)
        .and_return([user.id, true])

      importer.create_issue

      expect(project.issues.take).to be_valid
    end

    it 'returns the ID of the created issue' do
      allow(importer.user_finder)
        .to receive(:author_id_for)
        .with(issue)
        .and_return([user.id, true])

      expect(importer.create_issue).to be_a_kind_of(Numeric)
    end
  end

  describe '#create_assignees' do
    it 'inserts the issue assignees in bulk' do
      importer = described_class.new(issue, project, client)

      allow(importer.user_finder)
        .to receive(:user_id_for)
        .ordered.with(issue.assignees[0])
        .and_return(4)

      allow(importer.user_finder)
        .to receive(:user_id_for)
        .ordered.with(issue.assignees[1])
        .and_return(5)

      expect(Gitlab::Database)
        .to receive(:bulk_insert)
        .with(
          IssueAssignee.table_name,
          [{ issue_id: 1, user_id: 4 }, { issue_id: 1, user_id: 5 }]
        )

      importer.create_assignees(1)
    end
  end
end
