spec/storage/dropbox_spec.rb in backup-3.0.20 vs spec/storage/dropbox_spec.rb in backup-3.0.21
- old
+ new
@@ -1,139 +1,370 @@
# encoding: utf-8
require File.expand_path('../../spec_helper.rb', __FILE__)
describe Backup::Storage::Dropbox do
-
- let(:db) do
- Backup::Storage::Dropbox.new do |db|
- db.api_key = 'my_api_key'
- db.api_secret = 'my_secret'
- db.keep = 20
- db.timeout = 500
+ let(:model) { Backup::Model.new(:test_trigger, 'test label') }
+ let(:storage) do
+ Backup::Storage::Dropbox.new(model) do |db|
+ db.api_key = 'my_api_key'
+ db.api_secret = 'my_api_secret'
+ db.keep = 5
end
end
- let(:connection) do
- c = mock("Dropbox::Session")
- db.stubs(:connection).returns(c); c
- end
+ describe '#initialize' do
+ it 'should set the correct values' do
+ storage.api_key.should == 'my_api_key'
+ storage.api_secret.should == 'my_api_secret'
+ storage.access_type.should == :app_folder
+ storage.path.should == 'backups'
- before do
- Backup::Configuration::Storage::Dropbox.clear_defaults!
- STDIN.stubs(:gets)
- end
+ storage.storage_id.should be_nil
+ storage.keep.should == 5
+ end
- it 'should have defined the configuration properly' do
- db.api_key.should == 'my_api_key'
- db.api_secret.should == 'my_secret'
- db.path.should == 'backups'
- db.keep.should == 20
- db.timeout.should == 500
- end
-
- it 'should overwrite the default timeout' do
- db = Backup::Storage::Dropbox.new do |db|
- db.timeout = 500
+ it 'should set a storage_id if given' do
+ db = Backup::Storage::Dropbox.new(model, 'my storage_id')
+ db.storage_id.should == 'my storage_id'
end
- db.timeout.should == 500
- end
+ context 'when setting configuration defaults' do
+ after { Backup::Configuration::Storage::Dropbox.clear_defaults! }
- it 'should provide a default timeout' do
- db = Backup::Storage::Dropbox.new
+ it 'should use the configured defaults' do
+ Backup::Configuration::Storage::Dropbox.defaults do |db|
+ db.api_key = 'some_api_key'
+ db.api_secret = 'some_api_secret'
+ db.access_type = 'some_access_type'
+ db.path = 'some_path'
+ db.keep = 15
+ end
+ storage = Backup::Storage::Dropbox.new(model)
+ storage.api_key.should == 'some_api_key'
+ storage.api_secret.should == 'some_api_secret'
+ storage.access_type.should == 'some_access_type'
+ storage.path.should == 'some_path'
- db.timeout.should == 300
- end
+ storage.storage_id.should be_nil
+ storage.keep.should == 15
+ end
- it 'should overwrite the default path' do
- db = Backup::Storage::Dropbox.new do |db|
- db.path = 'my/backups'
- end
+ it 'should override the configured defaults' do
+ Backup::Configuration::Storage::Dropbox.defaults do |db|
+ db.api_key = 'old_api_key'
+ db.api_secret = 'old_api_secret'
+ db.access_type = 'old_access_type'
+ db.path = 'old_path'
+ db.keep = 15
+ end
+ storage = Backup::Storage::Dropbox.new(model) do |db|
+ db.api_key = 'new_api_key'
+ db.api_secret = 'new_api_secret'
+ db.access_type = 'new_access_type'
+ db.path = 'new_path'
+ db.keep = 10
+ end
- db.path.should == 'my/backups'
- end
+ storage.api_key.should == 'new_api_key'
+ storage.api_secret.should == 'new_api_secret'
+ storage.access_type.should == 'new_access_type'
+ storage.path.should == 'new_path'
+ storage.storage_id.should be_nil
+ storage.keep.should == 10
+ end
+ end # context 'when setting configuration defaults'
+
+ end # describe '#initialize'
+
describe '#connection' do
- before do
- db.stubs(:gets)
+ let(:session) { mock }
+ let(:client) { mock }
+ let(:s) { sequence '' }
+
+ context 'when a cached session exists' do
+ before do
+ storage.expects(:cached_session).in_sequence(s).returns(session)
+ storage.expects(:create_write_and_return_new_session!).never
+ DropboxClient.expects(:new).in_sequence(s).
+ with(session, :app_folder).returns(client)
+ end
+
+ it 'should use the cached session to create the client' do
+ storage.send(:connection).should be(client)
+ end
+
+ it 'should return an already existing client' do
+ storage.send(:connection).should be(client)
+ storage.send(:connection).should be(client)
+ end
end
- context "when the session cache has not yet been written" do
- it do
- session = mock("Dropbox::Session")
- Dropbox::Session.expects(:new).with('my_api_key', 'my_secret').returns(session)
- session.expects(:mode=).with(:dropbox)
- session.expects(:authorize)
- db.expects(:cache_exists?).returns(false)
- db.expects(:write_cache!).with(session)
+ context 'when a cached session does not exist' do
+ before do
+ storage.expects(:cached_session).in_sequence(s).returns(false)
+ Backup::Logger.expects(:message).in_sequence(s).with(
+ 'Creating a new session!'
+ )
+ storage.expects(:create_write_and_return_new_session!).in_sequence(s).
+ returns(session)
+ DropboxClient.expects(:new).in_sequence(s).
+ with(session, :app_folder).returns(client)
+ end
- template = Backup::Template.new(db.send(:binding))
- Backup::Template.expects(:new).returns(template)
+ it 'should create a new session and return the client' do
+ storage.send(:connection).should be(client)
+ end
- template.expects(:render).times(3)
- db.send(:connection)
+ it 'should return an already existing client' do
+ storage.send(:connection).should be(client)
+ storage.send(:connection).should be(client)
end
end
- context "when the session cache has already been written" do
- it "should load the session from cache, instead of creating a new one" do
- db.expects(:cache_exists?).returns(true)
- File.expects(:read).with("#{ENV['HOME']}/Backup/.cache/my_api_keymy_secret").returns("foo")
- session = mock("Dropbox::Session")
- session.expects(:authorized?).returns(true)
- Dropbox::Session.expects(:deserialize).with("foo").returns(session)
+ context 'when an error is raised creating a client for the session' do
+ it 'should wrap and raise the error' do
+ storage.stubs(:cached_session).returns(true)
+ DropboxClient.expects(:new).raises('error')
- db.expects(:create_write_and_return_new_session!).never
- db.send(:connection)
+ expect do
+ storage.send(:connection)
+ end.to raise_error {|err|
+ err.should be_an_instance_of(
+ Backup::Errors::Storage::Dropbox::ConnectionError
+ )
+ err.message.should ==
+ 'Storage::Dropbox::ConnectionError: RuntimeError: error'
+ }
end
+ end
- it "should load it from cache, but if it's invalid/corrupt, the create a session anyway" do
- db.expects(:cache_exists?).returns(true)
- File.expects(:read).with("#{ENV['HOME']}/Backup/.cache/my_api_keymy_secret").returns("foo")
- session = mock("Dropbox::Session")
- session.expects(:authorized?).returns(false)
- Dropbox::Session.expects(:deserialize).with("foo").returns(session)
+ end # describe '#connection'
- db.expects(:create_write_and_return_new_session!)
- db.send(:connection)
+ describe '#cached_session' do
+ let(:session) { mock }
+
+ context 'when a cached session file exists' do
+ before do
+ storage.expects(:cache_exists?).returns(true)
+ storage.expects(:cached_file).returns('cached_file')
+ File.expects(:read).with('cached_file').returns('yaml_data')
end
+
+ context 'when the cached session is successfully loaded' do
+ it 'should return the sesssion' do
+ DropboxSession.expects(:deserialize).with('yaml_data').
+ returns(session)
+ Backup::Logger.expects(:message).with(
+ 'Session data loaded from cache!'
+ )
+
+ storage.send(:cached_session).should be(session)
+ end
+ end
+
+ context 'when errors occur loading the session' do
+ it 'should log a warning and return false' do
+ DropboxSession.expects(:deserialize).with('yaml_data').
+ raises('error message')
+ Backup::Logger.expects(:warn).with do |err|
+ err.should be_an_instance_of(
+ Backup::Errors::Storage::Dropbox::CacheError
+ )
+ err.message.should == 'Storage::Dropbox::CacheError: ' +
+ "Could not read session data from cache.\n" +
+ " Cache data might be corrupt.\n" +
+ " Reason: RuntimeError\n" +
+ " error message"
+ end
+
+ expect do
+ storage.send(:cached_session).should be_false
+ end.not_to raise_error
+ end
+ end
end
+
+ context 'when a cached session file does not exist' do
+ before { storage.stubs(:cache_exists?).returns(false) }
+ it 'should return false' do
+ storage.send(:cached_session).should be_false
+ end
+ end
end
describe '#transfer!' do
+ let(:connection) { mock }
+ let(:package) { mock }
+ let(:file) { mock }
+ let(:s) { sequence '' }
+
before do
- connection.stubs(:upload)
- connection.stubs(:delete)
+ storage.instance_variable_set(:@package, package)
+ storage.stubs(:storage_name).returns('Storage::Dropbox')
+ storage.stubs(:local_path).returns('/local/path')
+ storage.stubs(:connection).returns(connection)
end
- it do
- Backup::Logger.expects(:message).with(
+ it 'should transfer the package files' do
+ storage.expects(:remote_path_for).in_sequence(s).with(package).
+ returns('remote/path')
+ storage.expects(:files_to_transfer_for).in_sequence(s).with(package).
+ multiple_yields(
+ ['2011.12.31.11.00.02.backup.tar.enc-aa', 'backup.tar.enc-aa'],
+ ['2011.12.31.11.00.02.backup.tar.enc-ab', 'backup.tar.enc-ab']
+ )
+ # first yield
+ Backup::Logger.expects(:message).in_sequence(s).with(
"Storage::Dropbox started transferring " +
- "'#{ Backup::TIME }.#{ Backup::TRIGGER }.tar'."
+ "'2011.12.31.11.00.02.backup.tar.enc-aa'."
)
- db.send(:transfer!)
+ File.expects(:open).in_sequence(s).with(
+ File.join('/local/path', '2011.12.31.11.00.02.backup.tar.enc-aa'), 'r'
+ ).yields(file)
+ connection.expects(:put_file).in_sequence(s).with(
+ File.join('remote/path', 'backup.tar.enc-aa'), file
+ )
+ # second yield
+ Backup::Logger.expects(:message).in_sequence(s).with(
+ "Storage::Dropbox started transferring " +
+ "'2011.12.31.11.00.02.backup.tar.enc-ab'."
+ )
+ File.expects(:open).in_sequence(s).with(
+ File.join('/local/path', '2011.12.31.11.00.02.backup.tar.enc-ab'), 'r'
+ ).yields(file)
+ connection.expects(:put_file).in_sequence(s).with(
+ File.join('remote/path', 'backup.tar.enc-ab'), file
+ )
+
+ storage.send(:transfer!)
end
+ end # describe '#transfer!'
- it do
- connection.expects(:upload).with(
- File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar"),
- File.join('backups', Backup::TRIGGER, Backup::TIME),
- :as => "#{ Backup::TRIGGER }.tar",
- :timeout => db.timeout
+ describe '#remove!' do
+ let(:package) { mock }
+ let(:connection) { mock }
+ let(:s) { sequence '' }
+
+ before do
+ storage.stubs(:storage_name).returns('Storage::Dropbox')
+ storage.stubs(:connection).returns(connection)
+ end
+
+ it 'should remove the package files' do
+ storage.expects(:remote_path_for).in_sequence(s).with(package).
+ returns('remote/path')
+ storage.expects(:transferred_files_for).in_sequence(s).with(package).
+ multiple_yields(
+ ['2011.12.31.11.00.02.backup.tar.enc-aa', 'backup.tar.enc-aa'],
+ ['2011.12.31.11.00.02.backup.tar.enc-ab', 'backup.tar.enc-ab']
)
+ # after both yields
+ Backup::Logger.expects(:message).in_sequence(s).with(
+ "Storage::Dropbox started removing " +
+ "'2011.12.31.11.00.02.backup.tar.enc-aa' from Dropbox.\n" +
+ "Storage::Dropbox started removing " +
+ "'2011.12.31.11.00.02.backup.tar.enc-ab' from Dropbox."
+ )
+ connection.expects(:file_delete).in_sequence(s).with('remote/path')
- db.send(:transfer!)
+ storage.send(:remove!, package)
end
+ end # describe '#remove!'
+
+ describe '#cached_file' do
+ it 'should return the path to the cache file' do
+ storage.send(:cached_file).should ==
+ File.join(Backup::Config.cache_path, 'my_api_keymy_api_secret')
+ end
end
- describe '#remove!' do
- it do
- connection.expects(:delete).with(
- File.join('backups', Backup::TRIGGER, Backup::TIME)
+ describe '#cache_exists?' do
+ it 'should check if #cached_file exists' do
+ storage.expects(:cached_file).returns('/path/to/cache_file')
+ File.expects(:exist?).with('/path/to/cache_file')
+
+ storage.send(:cache_exists?)
+ end
+ end
+
+ describe '#write_cache!' do
+ let(:session) { mock }
+ let(:cache_file) { mock }
+
+ it 'should write a serialized session to file' do
+ storage.expects(:cached_file).returns('/path/to/cache_file')
+ session.expects(:serialize).returns('serialized_data')
+
+ File.expects(:open).with('/path/to/cache_file', 'w').yields(cache_file)
+ cache_file.expects(:write).with('serialized_data')
+
+ storage.send(:write_cache!, session)
+ end
+ end
+
+ describe '#create_write_and_return_new_session!' do
+ let(:session) { mock }
+ let(:template) { mock }
+ let(:s) { sequence '' }
+
+ before do
+ storage.stubs(:cached_file).returns('/path/to/cache_file')
+
+ DropboxSession.expects(:new).in_sequence(s).
+ with('my_api_key', 'my_api_secret').returns(session)
+ session.expects(:get_request_token).in_sequence(s)
+ Backup::Template.expects(:new).in_sequence(s).with(
+ {:session => session, :cached_file => '/path/to/cache_file'}
+ ).returns(template)
+ template.expects(:render).in_sequence(s).with(
+ 'storage/dropbox/authorization_url.erb'
)
+ Timeout.expects(:timeout).in_sequence(s).with(180).yields
+ STDIN.expects(:gets).in_sequence(s)
+ end
- db.send(:remove!)
+ context 'when session is authenticated' do
+ before do
+ session.expects(:get_access_token).in_sequence(s)
+ end
+
+ it 'should cache and return the new session' do
+ template.expects(:render).in_sequence(s).with(
+ 'storage/dropbox/authorized.erb'
+ )
+ storage.expects(:write_cache!).in_sequence(s).with(session)
+ template.expects(:render).in_sequence(s).with(
+ 'storage/dropbox/cache_file_written.erb'
+ )
+
+ storage.send(:create_write_and_return_new_session!).should be(session)
+ end
+ end
+
+ context 'when session is not authenticated' do
+ before do
+ session.expects(:get_access_token).in_sequence(s).raises('error message')
+ end
+
+ it 'should wrap and re-raise the error' do
+ template.expects(:render).with('storage/dropbox/authorized.erb').never
+ storage.expects(:write_cache!).never
+ template.expects(:render).with('storage/dropbox/cache_file_written.erb').never
+
+ expect do
+ storage.send(:create_write_and_return_new_session!)
+ end.to raise_error {|err|
+ err.should be_an_instance_of(
+ Backup::Errors::Storage::Dropbox::AuthenticationError
+ )
+ err.message.should == 'Storage::Dropbox::AuthenticationError: ' +
+ "Could not create or authenticate a new session\n" +
+ " Reason: RuntimeError\n" +
+ " error message"
+ }
+ end
end
end
end