spec/lib/gdk/backup_spec.rb (153 lines of code) (raw):

# frozen_string_literal: true RSpec.describe GDK::Backup do let(:gdk_root_dir) { '/home/git/gdk' } let(:gdk_root_path) { Pathname.new(gdk_root_dir) } let(:backups_path_only) { '.backups' } let(:backups_path) { gdk_root_path.join('.backups') } let!(:now) { DateTime.parse('2021-05-06 18:50:31.279931 +0000').to_time } before do allow(GDK).to receive(:root).and_return(gdk_root_path) end describe 'initialize' do context 'when the source file is outside of the GDK' do it 'raises an exception' do expect { described_class.new(Tempfile.new.path) }.to raise_error(GDK::Backup::SourceFileOutsideOfGdk) end end context 'when the source file does not exist' do it 'raises an exception' do fake_source_file = gdk_root_path.join('tmp/filethatdoesntexist.txt') expect { described_class.new(fake_source_file) }.to raise_error(GDK::Backup::SourceFileDoesntExist) end end context 'when the source file does exist' do it 'does not raise an exception' do fake_source_file = gdk_root_path.join('Procfile') stub_source_file(fake_source_file) expect(described_class.new(fake_source_file)).to be_instance_of(described_class) end end end describe '.backup_root' do it 'is /Users/ash/src/gitlab/gitlab-development-kit/.backups' do fake_root_pathname = stub_backup_root expect(described_class.backup_root).to be(fake_root_pathname) end end describe '#source_file' do it 'returns a fully qualified Pathname to the backup source file' do fake_source_file = gdk_root_path.join('Procfile') stub_source_file(fake_source_file) stub_backup_root expect(described_class.new(fake_source_file).source_file.to_s).to eq(fake_source_file.to_s) end end describe '#destination_file' do it 'returns a fully qualified Pathname to the backup destination file' do fake_source_file = gdk_root_path.join('Procfile') stub_source_file(fake_source_file) stub_backup_root travel_to(now) do fake_destination_file = backups_path.join('Procfile.20210506185031') expect(described_class.new(fake_source_file).destination_file.to_s).to eq(fake_destination_file.to_s) end end end describe '#relative_source_file' do it 'returns a relative Pathname to the backup source file' do fake_source_file_only = 'Procfile' fake_source_file = gdk_root_path.join(fake_source_file_only) stub_source_file(fake_source_file) stub_backup_root expect(described_class.new(fake_source_file).relative_source_file.to_s).to eq(fake_source_file_only) end end describe '#recover_cmd_string' do it 'returns the cp command that recovers a backed up file' do fake_source_file = gdk_root_path.join('Procfile') stub_source_file(fake_source_file) stub_backup_root travel_to(now) do fake_destination_file = File.join(backups_path, 'Procfile.20210506185031') expect(described_class.new(fake_source_file).recover_cmd_string).to eq("cp -f '#{fake_destination_file}' \\\n'#{fake_source_file}'\n") end end end describe '#backup!' do shared_examples 'a file to be backed up' do |fake_source_file, fake_destination_file, advise| it 'and makes a backup' do fake_source_file_full = gdk_root_path.join(fake_source_file) stub_source_file(fake_source_file_full) fake_root_pathname = stub_backup_root allow(fake_root_pathname).to receive(:mkpath).and_return(true) travel_to(now) do fake_destination_file_full = File.join(gdk_root_dir, fake_destination_file) backup_params = [fake_source_file_full.to_s, fake_destination_file_full.to_s] expect(FileUtils).to receive(:mv).with(*backup_params).and_return(true) advise_message = "A backup of '#{fake_source_file}' has been made at '#{fake_destination_file}'." if advise expect(GDK::Output).to receive(:info).with(advise_message) else expect(GDK::Output).not_to receive(:info).with(advise_message) end expect(described_class.new(fake_source_file_full).backup!(advise: advise)).to be(true) end end end context 'is a file only' do it_behaves_like 'a file to be backed up', 'Procfile', '.backups/Procfile.20210506185031', true it_behaves_like 'a file to be backed up', 'Procfile', '.backups/Procfile.20210506185031', false end context 'is a file within a directory' do it_behaves_like 'a file to be backed up', 'gitlab/config/gitlab.yml', '.backups/gitlab__config__gitlab.yml.20210506185031', true it_behaves_like 'a file to be backed up', 'gitlab/config/gitlab.yml', '.backups/gitlab__config__gitlab.yml.20210506185031', false end end describe '#restore!' do let(:backup) { described_class.new(source_file) } let(:source_file) { gdk_root_path.join('gdk.yml') } before do stub_source_file(source_file) stub_backup_root end context 'with existing backup' do let(:backup_file) { backup.destination_file } it 'restores a backup' do travel_to(now) do allow(backup_file).to receive(:exist?).and_return(true) expect(FileUtils).to receive(:cp).with(backup_file.to_s, source_file.to_s) expect(GDK::Output).to receive(:info) .with("Backup '.backups/gdk.yml.20210506185031' has been restored to 'gdk.yml'.") expect(backup.restore!).to be(true) end end it 'restores a backup silently' do travel_to(now) do allow(backup_file).to receive(:exist?).and_return(true) expect(FileUtils).to receive(:cp).with(backup_file.to_s, source_file.to_s) expect(GDK::Output).not_to receive(:info) expect(backup.restore!(advise: false)).to be(true) end end end context 'without existing backup' do it 'does not restore anything' do expect(GDK::Output).not_to receive(:info) expect(backup.restore!).to be(false) end end end def stub_backup_root fake_root_pathname = Pathname.new(gdk_root_dir).join(backups_path_only) allow(gdk_root_path).to receive(:join).with('.backups').and_return(fake_root_pathname) allow(fake_root_pathname).to receive(:realpath).and_return(fake_root_pathname) fake_root_pathname end def stub_source_file(file) file = file.to_s fake_source_file_pathname = Pathname.new(file) allow(Pathname).to receive(:new).and_call_original allow(Pathname).to receive(:new).with(file).and_return(fake_source_file_pathname) allow(fake_source_file_pathname).to receive_messages(expand_path: fake_source_file_pathname, exist?: true) fake_source_file_pathname end end