# encoding: utf-8

require File.expand_path('../../spec_helper.rb', __FILE__)

##
# available S3 regions:
# eu-west-1, us-east-1, ap-southeast-1, us-west-1
describe Backup::Storage::S3 do
  let(:model)   { Backup::Model.new(:test_trigger, 'test label') }
  let(:storage) do
    Backup::Storage::S3.new(model) do |s3|
      s3.access_key_id      = 'my_access_key_id'
      s3.secret_access_key  = 'my_secret_access_key'
      s3.bucket             = 'my-bucket'
      s3.region             = 'us-east-1'
      s3.keep               = 5
    end
  end

  describe '#initialize' do
    it 'should set the correct values' do
      storage.access_key_id.should      == 'my_access_key_id'
      storage.secret_access_key.should  == 'my_secret_access_key'
      storage.bucket.should             == 'my-bucket'
      storage.path.should               == 'backups'
      storage.region.should             == 'us-east-1'

      storage.storage_id.should be_nil
      storage.keep.should       == 5
    end

    it 'should set a storage_id if given' do
      s3 = Backup::Storage::S3.new(model, 'my storage_id')
      s3.storage_id.should == 'my storage_id'
    end

    context 'when setting configuration defaults' do
      after { Backup::Configuration::Storage::S3.clear_defaults! }

      it 'should use the configured defaults' do
        Backup::Configuration::Storage::S3.defaults do |s3|
          s3.access_key_id      = 'some_access_key_id'
          s3.secret_access_key  = 'some_secret_access_key'
          s3.bucket             = 'some-bucket'
          s3.path               = 'some_path'
          s3.region             = 'some_region'
          s3.keep               = 15
        end
        storage = Backup::Storage::S3.new(model)
        storage.access_key_id.should      == 'some_access_key_id'
        storage.secret_access_key.should  == 'some_secret_access_key'
        storage.bucket.should             == 'some-bucket'
        storage.path.should               == 'some_path'
        storage.region.should             == 'some_region'

        storage.storage_id.should be_nil
        storage.keep.should       == 15
      end

      it 'should override the configured defaults' do
        Backup::Configuration::Storage::S3.defaults do |s3|
          s3.access_key_id      = 'old_access_key_id'
          s3.secret_access_key  = 'old_secret_access_key'
          s3.bucket             = 'old-bucket'
          s3.path               = 'old_path'
          s3.region             = 'old_region'
          s3.keep               = 15
        end
        storage = Backup::Storage::S3.new(model) do |s3|
          s3.access_key_id      = 'new_access_key_id'
          s3.secret_access_key  = 'new_secret_access_key'
          s3.bucket             = 'new-bucket'
          s3.path               = 'new_path'
          s3.region             = 'new_region'
          s3.keep               = 10
        end

        storage.access_key_id.should      == 'new_access_key_id'
        storage.secret_access_key.should  == 'new_secret_access_key'
        storage.bucket.should             == 'new-bucket'
        storage.path.should               == 'new_path'
        storage.region.should             == 'new_region'

        storage.storage_id.should be_nil
        storage.keep.should       == 10
      end
    end # context 'when setting configuration defaults'

  end # describe '#initialize'

  describe '#provider' do
    it 'should set the Fog provider' do
      storage.send(:provider).should == 'AWS'
    end
  end

  describe '#connection' do
    let(:connection) { mock }

    it 'should create a new connection' do
      Fog::Storage.expects(:new).once.with(
        :provider               => 'AWS',
        :aws_access_key_id      => 'my_access_key_id',
        :aws_secret_access_key  => 'my_secret_access_key',
        :region                 => 'us-east-1'
      ).returns(connection)
      storage.send(:connection).should == connection
    end

    it 'should return an existing connection' do
      Fog::Storage.expects(:new).once.returns(connection)
      storage.send(:connection).should == connection
      storage.send(:connection).should == connection
    end
  end # describe '#connection'

  describe '#remote_path_for' do
    let(:package) { mock }

    before do
      # for superclass method
      package.expects(:trigger).returns('trigger')
      package.expects(:time).returns('time')
    end

    it 'should remove any preceeding slash from the remote path' do
      storage.path = '/backups'
      storage.send(:remote_path_for, package).should == 'backups/trigger/time'
    end
  end

  describe '#transfer!' do
    let(:connection) { mock }
    let(:package) { mock }
    let(:file) { mock }
    let(:s) { sequence '' }

    before do
      storage.instance_variable_set(:@package, package)
      storage.stubs(:storage_name).returns('Storage::S3')
      storage.stubs(:local_path).returns('/local/path')
      storage.stubs(:connection).returns(connection)
    end

    it 'should transfer the package files' do
      storage.expects(:remote_path_for).in_sequence(s).with(package).
          returns('remote/path')
      connection.expects(:sync_clock).in_sequence(s)
      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::S3 started transferring " +
        "'2011.12.31.11.00.02.backup.tar.enc-aa' to bucket 'my-bucket'."
      )
      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_object).in_sequence(s).with(
        'my-bucket', File.join('remote/path', 'backup.tar.enc-aa'), file
      )
      # second yield
      Backup::Logger.expects(:message).in_sequence(s).with(
        "Storage::S3 started transferring " +
        "'2011.12.31.11.00.02.backup.tar.enc-ab' to bucket 'my-bucket'."
      )
      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_object).in_sequence(s).with(
        'my-bucket', File.join('remote/path', 'backup.tar.enc-ab'), file
      )

      storage.send(:transfer!)
    end
  end # describe '#transfer!'

  describe '#remove!' do
    let(:package) { mock }
    let(:connection) { mock }
    let(:s) { sequence '' }

    before do
      storage.stubs(:storage_name).returns('Storage::S3')
      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')
      connection.expects(:sync_clock).in_sequence(s)
      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']
      )
      # first yield
      Backup::Logger.expects(:message).in_sequence(s).with(
        "Storage::S3 started removing " +
        "'2011.12.31.11.00.02.backup.tar.enc-aa' from bucket 'my-bucket'."
      )
      connection.expects(:delete_object).in_sequence(s).with(
        'my-bucket', File.join('remote/path', 'backup.tar.enc-aa')
      )
      # second yield
      Backup::Logger.expects(:message).in_sequence(s).with(
        "Storage::S3 started removing " +
        "'2011.12.31.11.00.02.backup.tar.enc-ab' from bucket 'my-bucket'."
      )
      connection.expects(:delete_object).in_sequence(s).with(
        'my-bucket', File.join('remote/path', 'backup.tar.enc-ab')
      )

      storage.send(:remove!, package)
    end
  end # describe '#remove!'

end
