# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Gitlab::GithubImport::ObjectImporter, :aggregate_failures do
  let(:worker) do
    Class.new do
      def self.name
        'DummyWorker'
      end

      include(Gitlab::GithubImport::ObjectImporter)

      def object_type
        :dummy
      end

      def representation_class
        MockRepresantation
      end
    end.new
  end

  let_it_be(:project) { create(:project, :import_started) }

  let(:importer_class) { double(:importer_class, name: 'klass_name') }
  let(:importer_instance) { double(:importer_instance) }
  let(:client) { double(:client) }
  let(:github_identifiers) do
    {
      some_id: 1,
      some_type: '_some_type_'
    }
  end

  let(:representation_class) do
    Class.new do
      include Gitlab::GithubImport::Representation::ToHash
      include Gitlab::GithubImport::Representation::ExposeAttribute

      def self.from_json_hash(raw_hash)
        new(Gitlab::GithubImport::Representation.symbolize_hash(raw_hash))
      end

      attr_reader :attributes

      def initialize(attributes)
        @attributes = attributes
      end

      def github_identifiers
        {
          some_id: 1,
          some_type: '_some_type_'
        }
      end
    end
  end

  let(:stubbed_representation) { representation_class }

  before do
    stub_const('MockRepresantation', stubbed_representation)
  end

  describe '#import', :clean_gitlab_redis_cache do
    before do
      expect(worker)
        .to receive(:importer_class)
        .at_least(:once)
        .and_return(importer_class)
    end

    it 'imports the object' do
      expect(importer_class)
        .to receive(:new)
        .with(instance_of(MockRepresantation), project, client)
        .and_return(importer_instance)

      expect(importer_instance)
        .to receive(:execute)

      expect(Gitlab::GithubImport::Logger)
        .to receive(:info)
        .with(
          github_identifiers: github_identifiers,
          message: 'starting importer',
          project_id: project.id,
          importer: 'klass_name'
        )

      expect(Gitlab::GithubImport::Logger)
        .to receive(:info)
        .with(
          github_identifiers: github_identifiers,
          message: 'importer finished',
          project_id: project.id,
          importer: 'klass_name'
        )

      worker.import(project, client, { 'number' => 10, 'github_id' => 1 })

      expect(Gitlab::GithubImport::ObjectCounter.summary(project)).to eq({
        'fetched' => {},
        'imported' => { 'dummy' => 1 }
      })
    end

    it 'logs error when the import fails' do
      expect(importer_class)
        .to receive(:new)
        .with(instance_of(MockRepresantation), project, client)
        .and_return(importer_instance)

      exception = StandardError.new('some error')
      expect(importer_instance)
        .to receive(:execute)
        .and_raise(exception)

      expect(Gitlab::GithubImport::Logger)
        .to receive(:info)
        .with(
          github_identifiers: github_identifiers,
          message: 'starting importer',
          project_id: project.id,
          importer: 'klass_name'
        )

      expect(Gitlab::Import::ImportFailureService)
        .to receive(:track)
        .with(
          project_id: project.id,
          exception: exception,
          error_source: 'klass_name'
        )
        .and_call_original

      worker.import(project, client, { 'number' => 10, 'github_id' => 1 })

      expect(project.import_state.reload.status).to eq('started')

      expect(project.import_failures).not_to be_empty
      expect(project.import_failures.last.exception_class).to eq('StandardError')
      expect(project.import_failures.last.exception_message).to eq('some error')
    end

    context 'without github_identifiers defined' do
      let(:stubbed_representation) { representation_class.instance_eval { undef_method :github_identifiers } }

      it 'logs error when representation does not have a github_id' do
        expect(importer_class).not_to receive(:new)

        expect(Gitlab::Import::ImportFailureService)
          .to receive(:track)
          .with(
            project_id: project.id,
            exception: a_kind_of(NoMethodError),
            error_source: 'klass_name',
            fail_import: true
          )
          .and_call_original

        expect { worker.import(project, client, { 'number' => 10 }) }
          .to raise_error(NoMethodError, /^undefined method `github_identifiers/)
      end
    end
  end
end
