# frozen_string_literal: true require 'spec_helper' RSpec.describe Gitlab::GithubImport::Importer::PullRequestsImporter do let(:url) { 'https://github.com/foo/bar.git' } let(:project) { create(:project, import_source: 'foo/bar', import_url: url) } let(:client) { double(:client) } let(:pull_request) do double( :response, number: 42, title: 'My Pull Request', body: 'This is my pull request', state: 'closed', head: double( :head, sha: '123abc', ref: 'my-feature', repo: double(:repo, id: 400), user: double(:user, id: 4, login: 'alice') ), base: double( :base, sha: '456def', ref: 'master', repo: double(:repo, id: 200) ), milestone: double(:milestone, number: 4), user: double(:user, id: 4, login: 'alice'), assignee: double(:user, id: 4, login: 'alice'), merged_by: double(:user, id: 4, login: 'alice'), created_at: 1.second.ago, updated_at: 1.second.ago, merged_at: 1.second.ago ) end describe '#parallel?' do it 'returns true when running in parallel mode' do importer = described_class.new(project, client) expect(importer).to be_parallel end it 'returns false when running in sequential mode' do importer = described_class.new(project, client, parallel: false) expect(importer).not_to be_parallel end end describe '#execute' do context 'when running in parallel mode' do it 'imports pull requests in parallel' do importer = described_class.new(project, client) expect(importer).to receive(:parallel_import) importer.execute end end context 'when running in sequential mode' do it 'imports pull requests in sequence' do importer = described_class.new(project, client, parallel: false) expect(importer).to receive(:sequential_import) importer.execute end end end describe '#sequential_import' do it 'imports each pull request in sequence' do importer = described_class.new(project, client, parallel: false) pull_request_importer = double(:pull_request_importer) allow(importer) .to receive(:each_object_to_import) .and_yield(pull_request) expect(Gitlab::GithubImport::Importer::PullRequestImporter) .to receive(:new) .with( an_instance_of(Gitlab::GithubImport::Representation::PullRequest), project, client ) .and_return(pull_request_importer) expect(pull_request_importer).to receive(:execute) importer.sequential_import end end describe '#parallel_import' do it 'imports each note in parallel' do importer = described_class.new(project, client) allow(importer) .to receive(:each_object_to_import) .and_yield(pull_request) expect(Gitlab::GithubImport::ImportPullRequestWorker) .to receive(:perform_async) .with(project.id, an_instance_of(Hash), an_instance_of(String)) waiter = importer.parallel_import expect(waiter).to be_an_instance_of(Gitlab::JobWaiter) expect(waiter.jobs_remaining).to eq(1) end end describe '#each_object_to_import', :clean_gitlab_redis_cache do let(:importer) { described_class.new(project, client) } before do page = double(:page, objects: [pull_request], number: 1) expect(client) .to receive(:each_page) .with( :pull_requests, 'foo/bar', { state: 'all', sort: 'created', direction: 'asc', page: 1 } ) .and_yield(page) end it 'yields every pull request to the supplied block' do expect { |b| importer.each_object_to_import(&b) } .to yield_with_args(pull_request) end it 'updates the repository if a pull request was updated after the last clone' do expect(importer) .to receive(:update_repository?) .with(pull_request) .and_return(true) expect(importer) .to receive(:update_repository) importer.each_object_to_import { } end end describe '#update_repository' do it 'updates the repository' do importer = described_class.new(project, client) expect_next_instance_of(Gitlab::Import::Logger) do |logger| expect(logger) .to receive(:info) .with(an_instance_of(Hash)) end expect(importer.repository_updates_counter) .to receive(:increment) .and_call_original expect(project.repository) .to receive(:fetch_remote) .with(url, forced: true, refmap: Gitlab::GithubImport.refmap) freeze_time do importer.update_repository expect(project.last_repository_updated_at).to be_like_time(Time.zone.now) end end end describe '#update_repository?' do let(:importer) { described_class.new(project, client) } context 'when the pull request was updated after the last update' do let(:pr) do double( :pr, updated_at: Time.zone.now, head: double(:head, sha: '123'), base: double(:base, sha: '456') ) end before do allow(project) .to receive(:last_repository_updated_at) .and_return(1.year.ago) end it 'returns true when the head SHA is not present' do expect(importer) .to receive(:commit_exists?) .with(pr.head.sha) .and_return(false) expect(importer.update_repository?(pr)).to eq(true) end it 'returns true when the base SHA is not present' do expect(importer) .to receive(:commit_exists?) .with(pr.head.sha) .and_return(true) expect(importer) .to receive(:commit_exists?) .with(pr.base.sha) .and_return(false) expect(importer.update_repository?(pr)).to eq(true) end it 'returns false if both the head and base SHAs are present' do expect(importer) .to receive(:commit_exists?) .with(pr.head.sha) .and_return(true) expect(importer) .to receive(:commit_exists?) .with(pr.base.sha) .and_return(true) expect(importer.update_repository?(pr)).to eq(false) end end context 'when the pull request was updated before the last update' do it 'returns false' do pr = double(:pr, updated_at: 1.year.ago) allow(project) .to receive(:last_repository_updated_at) .and_return(Time.zone.now) expect(importer.update_repository?(pr)).to eq(false) end end end describe '#commit_exists?' do let(:importer) { described_class.new(project, client) } it 'returns true when a commit exists' do expect(project.repository) .to receive(:commit) .with('123') .and_return(double(:commit)) expect(importer.commit_exists?('123')).to eq(true) end it 'returns false when a commit does not exist' do expect(project.repository) .to receive(:commit) .with('123') .and_return(nil) expect(importer.commit_exists?('123')).to eq(false) end end describe '#id_for_already_imported_cache' do it 'returns the PR number of the given PR' do importer = described_class.new(project, client) expect(importer.id_for_already_imported_cache(pull_request)) .to eq(42) end end end