175 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Ruby
		
	
	
	
| # frozen_string_literal: true
 | |
| 
 | |
| require 'spec_helper'
 | |
| 
 | |
| RSpec.describe Gitlab::Database::NamespaceEachBatch, feature_category: :database do
 | |
|   let_it_be(:group) { create(:group) }
 | |
|   let_it_be(:other_group) { create(:group) }
 | |
|   let_it_be(:user) { create(:user, :admin) }
 | |
| 
 | |
|   let(:namespace_id) { group.id }
 | |
| 
 | |
|   let_it_be(:subgroup1) { create(:group, parent: group) }
 | |
|   let_it_be(:subgroup2) { create(:group, parent: group) }
 | |
| 
 | |
|   let_it_be(:subsubgroup1) { create(:group, parent: subgroup1) }
 | |
|   let_it_be(:subsubgroup2) { create(:group, parent: subgroup1) }
 | |
|   let_it_be(:subsubgroup3) { create(:group, parent: subgroup1) }
 | |
| 
 | |
|   let_it_be(:project1) { create(:project, namespace: group) }
 | |
|   let_it_be(:project2) { create(:project, namespace: group) }
 | |
|   let_it_be(:project3) { create(:project, namespace: subsubgroup2) }
 | |
|   let_it_be(:project4) { create(:project, namespace: subsubgroup3) }
 | |
|   let_it_be(:project5) { create(:project, namespace: subsubgroup3) }
 | |
| 
 | |
|   let(:namespace_class) { Namespace }
 | |
|   let(:batch_size) { 3 }
 | |
| 
 | |
|   def collected_ids(cursor = { current_id: namespace_id, depth: [namespace_id] })
 | |
|     [].tap do |ids|
 | |
|       described_class.new(namespace_class: namespace_class, cursor: cursor).each_batch(of: batch_size) do |batch_ids|
 | |
|         ids.concat(batch_ids)
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   shared_examples 'iteration over the hierarchy' do
 | |
|     it 'returns the correct namespace ids' do
 | |
|       expect(collected_ids).to eq([
 | |
|         group.id,
 | |
|         subgroup1.id,
 | |
|         subsubgroup1.id,
 | |
|         subsubgroup2.id,
 | |
|         project3.project_namespace_id,
 | |
|         subsubgroup3.id,
 | |
|         project4.project_namespace_id,
 | |
|         project5.project_namespace_id,
 | |
|         subgroup2.id,
 | |
|         project1.project_namespace_id,
 | |
|         project2.project_namespace_id
 | |
|       ])
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   it_behaves_like 'iteration over the hierarchy'
 | |
| 
 | |
|   context 'when batch size is larger than the hierarchy' do
 | |
|     let(:batch_size) { 100 }
 | |
| 
 | |
|     it_behaves_like 'iteration over the hierarchy'
 | |
|   end
 | |
| 
 | |
|   context 'when batch size is 1' do
 | |
|     let(:batch_size) { 1 }
 | |
| 
 | |
|     it_behaves_like 'iteration over the hierarchy'
 | |
|   end
 | |
| 
 | |
|   context 'when stopping the iteration in the middle and resuming' do
 | |
|     it 'returns the correct ids' do
 | |
|       ids = []
 | |
|       cursor = { current_id: namespace_id, depth: [namespace_id] }
 | |
| 
 | |
|       iterator = described_class.new(namespace_class: namespace_class, cursor: cursor)
 | |
|       iterator.each_batch(of: 5) do |batch_ids, new_cursor|
 | |
|         ids.concat(batch_ids)
 | |
|         cursor = new_cursor
 | |
|       end
 | |
| 
 | |
|       iterator = described_class.new(namespace_class: namespace_class, cursor: cursor)
 | |
|       iterator.each_batch(of: 500) do |batch_ids|
 | |
|         ids.concat(batch_ids)
 | |
|       end
 | |
| 
 | |
|       expect(collected_ids).to eq([
 | |
|         group.id,
 | |
|         subgroup1.id,
 | |
|         subsubgroup1.id,
 | |
|         subsubgroup2.id,
 | |
|         project3.project_namespace_id,
 | |
|         subsubgroup3.id,
 | |
|         project4.project_namespace_id,
 | |
|         project5.project_namespace_id,
 | |
|         subgroup2.id,
 | |
|         project1.project_namespace_id,
 | |
|         project2.project_namespace_id
 | |
|       ])
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   context 'when querying a subgroup' do
 | |
|     let(:namespace_id) { subgroup1.id }
 | |
| 
 | |
|     it 'returns the correct ids' do
 | |
|       expect(collected_ids).to eq([
 | |
|         subgroup1.id,
 | |
|         subsubgroup1.id,
 | |
|         subsubgroup2.id,
 | |
|         project3.project_namespace_id,
 | |
|         subsubgroup3.id,
 | |
|         project4.project_namespace_id,
 | |
|         project5.project_namespace_id
 | |
|       ])
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   context 'when querying a subgroup without descendants' do
 | |
|     let(:namespace_id) { subgroup2.id }
 | |
| 
 | |
|     it 'finds only the given namespace id' do
 | |
|       expect(collected_ids).to eq([subgroup2.id])
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   context 'when batching over groups only' do
 | |
|     let(:namespace_class) { Group }
 | |
| 
 | |
|     it 'returns the correct namespace ids' do
 | |
|       expect(collected_ids).to eq([
 | |
|         group.id,
 | |
|         subgroup1.id,
 | |
|         subsubgroup1.id,
 | |
|         subsubgroup2.id,
 | |
|         subsubgroup3.id,
 | |
|         subgroup2.id
 | |
|       ])
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   context 'when the cursor is invalid' do
 | |
|     context 'when non-integer current id is given' do
 | |
|       it 'raises error' do
 | |
|         cursor = { current_id: 'not int', depth: [group.id] }
 | |
| 
 | |
|         expect { collected_ids(cursor) }.to raise_error(ArgumentError)
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context 'when depth is not an array' do
 | |
|       it 'raises error' do
 | |
|         cursor = { current_id: group.id, depth: group.id }
 | |
| 
 | |
|         expect { collected_ids(cursor) }.to raise_error(ArgumentError)
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context 'when non-integer depth values are given' do
 | |
|       it 'raises error' do
 | |
|         cursor = { current_id: group.id, depth: ['not int'] }
 | |
| 
 | |
|         expect { collected_ids(cursor) }.to raise_error(ArgumentError)
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context 'when giving non-existing namespace id' do
 | |
|       it 'returns nothing', :enable_admin_mode do
 | |
|         cursor = { current_id: subgroup1.id, depth: [group.id, subgroup1.id] }
 | |
| 
 | |
|         Groups::DestroyService.new(group, user).execute
 | |
| 
 | |
|         expect(collected_ids(cursor)).to eq([])
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 |