# frozen_string_literal: true

require 'spec_helper'

# rubocop:disable RSpec/FactoriesInMigrationSpecs
RSpec.describe Gitlab::BackgroundMigration::BackfillProjectRepositories do
  let(:group) { create(:group, name: 'foo', path: 'foo') }

  describe described_class::ShardFinder do
    let(:shard) { create(:shard, name: 'default') }

    describe '#find_shard_id' do
      it 'creates a new shard when it does not exist yet' do
        expect { subject.find_shard_id('other') }.to change(Shard, :count).by(1)
      end

      it 'returns the shard when it exists' do
        other_shard = create(:shard, name: 'other')

        shard_id = subject.find_shard_id('other')

        expect(shard_id).to eq(other_shard.id)
      end

      it 'only queries the database once to retrieve shards' do
        subject.find_shard_id('default')

        expect { subject.find_shard_id('default') }.not_to exceed_query_limit(0)
      end
    end
  end

  describe described_class::Project do
    let!(:project_hashed_storage_1) { create(:project, name: 'foo', path: 'foo', namespace: group, storage_version: 1) }
    let!(:project_hashed_storage_2) { create(:project, name: 'bar', path: 'bar', namespace: group, storage_version: 2) }
    let!(:project_legacy_storage_3) { create(:project, name: 'baz', path: 'baz', namespace: group, storage_version: 0) }
    let!(:project_legacy_storage_4) { create(:project, name: 'zoo', path: 'zoo', namespace: group, storage_version: nil) }
    let!(:project_legacy_storage_5) { create(:project, name: 'test', path: 'test', namespace: group, storage_version: nil) }

    describe '.on_hashed_storage' do
      it 'finds projects with repository on hashed storage' do
        projects = described_class.on_hashed_storage.pluck(:id)

        expect(projects).to match_array([project_hashed_storage_1.id, project_hashed_storage_2.id])
      end
    end

    describe '.on_legacy_storage' do
      it 'finds projects with repository on legacy storage' do
        projects = described_class.on_legacy_storage.pluck(:id)

        expect(projects).to match_array([project_legacy_storage_3.id, project_legacy_storage_4.id, project_legacy_storage_5.id])
      end
    end

    describe '.without_project_repository' do
      it 'finds projects which do not have a projects_repositories entry' do
        create(:project_repository, project: project_hashed_storage_1)
        create(:project_repository, project: project_legacy_storage_3)

        projects = described_class.without_project_repository.pluck(:id)

        expect(projects).to contain_exactly(project_hashed_storage_2.id, project_legacy_storage_4.id, project_legacy_storage_5.id)
      end
    end

    describe '#disk_path' do
      context 'for projects on hashed storage' do
        it 'returns the correct disk_path' do
          project = described_class.find(project_hashed_storage_1.id)

          expect(project.disk_path).to eq(project_hashed_storage_1.disk_path)
        end
      end

      context 'for projects on legacy storage' do
        it 'returns the correct disk_path' do
          project = described_class.find(project_legacy_storage_3.id)

          expect(project.disk_path).to eq(project_legacy_storage_3.disk_path)
        end

        it 'returns the correct disk_path using the route entry' do
          project_legacy_storage_5.route.update(path: 'zoo/new-test')
          project = described_class.find(project_legacy_storage_5.id)

          expect(project.disk_path).to eq('zoo/new-test')
        end

        it 'raises OrphanedNamespaceError when any parent namespace does not exist' do
          subgroup = create(:group, parent: group)
          project_orphaned_namespace = create(:project, name: 'baz', path: 'baz', namespace: subgroup, storage_version: nil)
          subgroup.update_column(:parent_id, non_existing_record_id)

          project = described_class.find(project_orphaned_namespace.id)
          project.route.destroy
          subgroup.route.destroy

          expect { project.reload.disk_path }
            .to raise_error(Gitlab::BackgroundMigration::BackfillProjectRepositories::OrphanedNamespaceError)
        end
      end
    end
  end
end
# rubocop:enable RSpec/FactoriesInMigrationSpecs
