2019-10-12 21:52:04 +05:30
# frozen_string_literal: true
2018-03-17 18:26:18 +05:30
require 'spec_helper'
2020-07-28 23:09:34 +05:30
RSpec . describe Backup :: Repository do
2020-10-24 23:57:45 +05:30
let_it_be ( :project ) { create ( :project , :wiki_repo ) }
2018-03-17 18:26:18 +05:30
let ( :progress ) { StringIO . new }
2020-01-01 13:55:28 +05:30
2018-11-08 19:23:39 +05:30
subject { described_class . new ( progress ) }
2018-03-17 18:26:18 +05:30
before do
allow ( progress ) . to receive ( :puts )
allow ( progress ) . to receive ( :print )
2018-05-09 12:01:36 +05:30
allow ( FileUtils ) . to receive ( :mv ) . and_return ( true )
2018-03-17 18:26:18 +05:30
2020-01-01 13:55:28 +05:30
allow_next_instance_of ( described_class ) do | instance |
allow ( instance ) . to receive ( :progress ) . and_return ( progress )
end
2018-03-17 18:26:18 +05:30
end
describe '#dump' do
2020-10-24 23:57:45 +05:30
before do
allow ( Gitlab . config . repositories . storages ) . to receive ( :keys ) . and_return ( storage_keys )
end
let_it_be ( :projects ) { create_list ( :project , 5 , :wiki_repo ) + [ project ] }
let ( :storage_keys ) { %w[ default test_second_storage ] }
context 'no concurrency' do
it 'creates the expected number of threads' do
expect ( Thread ) . not_to receive ( :new )
projects . each do | project |
expect ( subject ) . to receive ( :dump_project ) . with ( project ) . and_call_original
end
subject . dump ( max_concurrency : 1 , max_storage_concurrency : 1 )
2018-03-17 18:26:18 +05:30
end
2020-10-24 23:57:45 +05:30
describe 'command failure' do
it 'dump_project raises an error' do
allow ( subject ) . to receive ( :dump_project ) . and_raise ( IOError )
expect { subject . dump ( max_concurrency : 1 , max_storage_concurrency : 1 ) } . to raise_error ( IOError )
end
it 'project query raises an error' do
allow ( Project ) . to receive ( :find_each ) . and_raise ( ActiveRecord :: StatementTimeout )
expect { subject . dump ( max_concurrency : 1 , max_storage_concurrency : 1 ) } . to raise_error ( ActiveRecord :: StatementTimeout )
end
end
end
[ 4 , 10 ] . each do | max_storage_concurrency |
context " max_storage_concurrency #{ max_storage_concurrency } " , quarantine : 'https://gitlab.com/gitlab-org/gitlab/-/issues/241701' do
it 'creates the expected number of threads' do
expect ( Thread ) . to receive ( :new )
. exactly ( storage_keys . length * ( max_storage_concurrency + 1 ) ) . times
. and_call_original
projects . each do | project |
expect ( subject ) . to receive ( :dump_project ) . with ( project ) . and_call_original
end
subject . dump ( max_concurrency : 1 , max_storage_concurrency : max_storage_concurrency )
end
it 'creates the expected number of threads with extra max concurrency' do
expect ( Thread ) . to receive ( :new )
. exactly ( storage_keys . length * ( max_storage_concurrency + 1 ) ) . times
. and_call_original
projects . each do | project |
expect ( subject ) . to receive ( :dump_project ) . with ( project ) . and_call_original
end
subject . dump ( max_concurrency : 3 , max_storage_concurrency : max_storage_concurrency )
end
describe 'command failure' do
it 'dump_project raises an error' do
allow ( subject ) . to receive ( :dump_project )
. and_raise ( IOError )
expect { subject . dump ( max_concurrency : 1 , max_storage_concurrency : max_storage_concurrency ) } . to raise_error ( IOError )
end
it 'project query raises an error' do
allow ( Project ) . to receive_message_chain ( 'for_repository_storage.find_each' ) . and_raise ( ActiveRecord :: StatementTimeout )
expect { subject . dump ( max_concurrency : 1 , max_storage_concurrency : max_storage_concurrency ) } . to raise_error ( ActiveRecord :: StatementTimeout )
end
context 'misconfigured storages' do
let ( :storage_keys ) { %w[ test_second_storage ] }
it 'raises an error' do
expect { subject . dump ( max_concurrency : 1 , max_storage_concurrency : max_storage_concurrency ) } . to raise_error ( Backup :: Error , 'repositories.storages in gitlab.yml is misconfigured' )
end
end
end
2018-03-17 18:26:18 +05:30
end
end
end
describe '#restore' do
2018-03-27 19:54:05 +05:30
let ( :timestamp ) { Time . utc ( 2017 , 3 , 22 ) }
let ( :temp_dirs ) do
Gitlab . config . repositories . storages . map do | name , storage |
2018-11-08 19:23:39 +05:30
Gitlab :: GitalyClient :: StorageSettings . allow_disk_access do
File . join ( storage . legacy_disk_path , '..' , 'repositories.old.' + timestamp . to_i . to_s )
end
2018-03-27 19:54:05 +05:30
end
end
around do | example |
Timecop . freeze ( timestamp ) { example . run }
end
after do
temp_dirs . each { | path | FileUtils . rm_rf ( path ) }
end
2018-03-17 18:26:18 +05:30
describe 'command failure' do
before do
2020-04-08 14:13:33 +05:30
# Allow us to set expectations on the project directly
expect ( Project ) . to receive ( :find_each ) . and_yield ( project )
expect ( project . repository ) . to receive ( :create_repository ) { raise 'Fail in tests' }
2018-03-17 18:26:18 +05:30
end
2018-03-27 19:54:05 +05:30
context 'hashed storage' do
it 'shows the appropriate error' do
subject . restore
2018-11-08 19:23:39 +05:30
expect ( progress ) . to have_received ( :puts ) . with ( " [Failed] restoring #{ project . full_path } repository " )
2018-03-27 19:54:05 +05:30
end
end
2018-03-17 18:26:18 +05:30
2018-03-27 19:54:05 +05:30
context 'legacy storage' do
let! ( :project ) { create ( :project , :legacy_storage ) }
it 'shows the appropriate error' do
subject . restore
2018-11-08 19:23:39 +05:30
expect ( progress ) . to have_received ( :puts ) . with ( " [Failed] restoring #{ project . full_path } repository " )
2018-03-27 19:54:05 +05:30
end
2018-03-17 18:26:18 +05:30
end
end
2019-02-15 15:39:39 +05:30
context 'restoring object pools' do
2019-12-26 22:10:19 +05:30
it 'schedules restoring of the pool' , :sidekiq_might_not_need_inline do
2019-02-15 15:39:39 +05:30
pool_repository = create ( :pool_repository , :failed )
pool_repository . delete_object_pool
subject . restore
pool_repository . reload
expect ( pool_repository ) . not_to be_failed
expect ( pool_repository . object_pool . exists? ) . to be ( true )
end
end
2020-03-13 15:44:24 +05:30
it 'cleans existing repositories' do
wiki_repository_spy = spy ( :wiki )
allow_next_instance_of ( ProjectWiki ) do | project_wiki |
allow ( project_wiki ) . to receive ( :repository ) . and_return ( wiki_repository_spy )
end
expect_next_instance_of ( Repository ) do | repo |
expect ( repo ) . to receive ( :remove )
end
subject . restore
expect ( wiki_repository_spy ) . to have_received ( :remove )
end
2018-11-08 19:23:39 +05:30
end
2018-05-09 12:01:36 +05:30
2018-03-17 18:26:18 +05:30
describe '#empty_repo?' do
context 'for a wiki' do
let ( :wiki ) { create ( :project_wiki ) }
it 'invalidates the emptiness cache' do
expect ( wiki . repository ) . to receive ( :expire_emptiness_caches ) . once
2018-11-08 19:23:39 +05:30
subject . send ( :empty_repo? , wiki )
2018-03-17 18:26:18 +05:30
end
context 'wiki repo has content' do
let! ( :wiki_page ) { create ( :wiki_page , wiki : wiki ) }
it 'returns true, regardless of bad cache value' do
2018-11-08 19:23:39 +05:30
expect ( subject . send ( :empty_repo? , wiki ) ) . to be ( false )
2018-03-17 18:26:18 +05:30
end
end
context 'wiki repo does not have content' do
it 'returns true, regardless of bad cache value' do
2018-11-08 19:23:39 +05:30
expect ( subject . send ( :empty_repo? , wiki ) ) . to be_truthy
2018-03-17 18:26:18 +05:30
end
end
end
end
end