require_relative '../spec_helper'
require 'base64'
require 'openssl'

module Aws
  module S3
    module EncryptionV2
      describe EncryptHandler do
        let(:next_handler) { double(call: nil) }
        let(:handler) { EncryptHandler.new(next_handler) }

        let(:cipher) { double('cipher', update: '', final: '', auth_tag: '') }
        let(:envelope) { {'x-amz-key-v2' => 'env-key'} }
        let(:cipher_provider) { double('cipher_provider', encryption_cipher: [envelope, cipher]) }
        let(:context_enc) { { cipher_provider: cipher_provider, envelope_location: :metadata } }
        let(:params) { {bucket: 'bucket', key: 'key'} }
        let(:client) { double('client') }
        let(:http_response) { double(on_headers: nil) }
        let(:config) { Struct.new(:user_agent_suffix).new }
        let(:context) { double(params: params, client: client, :[] => context_enc, http_response: http_response, config: config ) }
        describe '#call' do

          context 'when envelope_location is :metadata' do
            it 'sets the envelope on metadata' do
              handler.call(context)
              expect(params[:metadata]).to include('x-amz-key-v2')
            end
          end

          context 'when envelope_location is :instruction_file' do
            it 'puts the envelope to the bucket' do
              context_enc.merge!(envelope_location: :instruction_file, instruction_file_suffix: '.instruction')
              expect(client).to receive(:put_object).with(
                bucket: 'bucket', key: 'key.instruction',
                body: Json.dump(envelope))
              handler.call(context)
            end
          end

          it 'raises an error if content_md5 is set' do
            params[:content_md5] = 'md5'
            expect do
              handler.call(context)
            end.to raise_exception(ArgumentError)
          end

          it 'adds unencrypted-content-length to metadata' do
            handler.call(context)
            expect(params[:metadata]).to include('x-amz-key-v2')
          end

          it 'adds a user_agent_suffix' do
            handler.call(context)
            expect(config.user_agent_suffix).to include('S3CryptoV2')
          end

          it 'sets the body to the IOEncrypter and calls close on_headers' do
            encrypter = double()
            expect(IOEncrypter).to receive(:new).with(cipher, kind_of(StringIO)).and_return(encrypter)
            expect(http_response).to receive(:on_headers) { |&block| block.call }
            expect(encrypter).to receive(:close)
            handler.call(context)
          end

          it 'calls the next handler in the stack' do
            expect(next_handler).to receive(:call).with(context)
            handler.call(context)
          end

        end
      end
    end
  end
end
