#-------------------------------------------------------------------------
# # Copyright (c) Microsoft and contributors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#--------------------------------------------------------------------------
require "test_helper"
require "azure/service/storage_service"
require "azure/core/http/http_request"
require "azure/core/http/signer_filter"
require "azure/service/storage_service_properties"

describe Azure::Service::StorageService do

  let(:uri){ URI.parse "http://dummy.uri/resource" }
  let(:verb){ :get }

  subject do
    storage_service = Azure::Service::StorageService.new
    storage_service.host = "http://dumyhost.uri"
    storage_service
  end

  describe "#call" do
    let(:mock_request){ mock() }
    let(:mock_signer_filter){ mock() }

    before do 
      Azure::Core::Http::HttpRequest.stubs(:new).with(verb, uri, nil).returns(mock_request)
      Azure::Core::Http::SignerFilter.stubs(:new).returns(mock_signer_filter)

      mock_request.expects(:call)
    end

    it "adds a SignerFilter to the HTTP pipeline" do
      mock_request.expects(:with_filter).with(mock_signer_filter)
      subject.call(verb, uri)
    end

    describe "when passed the optional headers arguement" do
      before do
        mock_request.expects(:with_filter).with(mock_signer_filter)
      end

      let(:mock_headers) {{ 
        "Other-Header"=>"SomeValue", 
        "Custom-Header"=>"PreviousValue",
        "connection"=>"PreviousValue"
      }}

      it "merges the custom headers with the HttpRequest headers" do
        mock_request.expects(:headers).returns(mock_headers).at_least(2)
        subject.call(verb, uri, nil, { "Custom-Header"=>"CustomValue"} )

        mock_headers["Other-Header"].must_equal "SomeValue"
        mock_headers["Custom-Header"].must_equal "CustomValue"
        mock_headers["connection"].must_equal "keep-alive"
      end
    end

    describe "when passed the optional body arguement" do
      before do
        mock_request.expects(:with_filter).with(mock_signer_filter)
      end

      it "passes the body to the to HttpRequest" do
        Azure::Core::Http::HttpRequest.stubs(:new).with(verb, uri, 'body').returns(mock_request)
        subject.call(verb, uri, "body")
      end
    end

    describe "when with_filter was called" do
      before do
        mock_request.expects(:with_filter).with(mock_signer_filter)
      end

      it "builds the HTTP pipeline by passing the filters to the HTTPRequest" do
        filter = mock()
        filter1 = mock()

        subject.with_filter filter
        subject.with_filter filter1

        mock_request.expects(:with_filter).with(filter)
        mock_request.expects(:with_filter).with(filter1)
        
        subject.call(verb, uri)
      end
    end
  end

  describe "#with_filter" do
    it "appends filters to a list of filters that will be used in the #call method" do
      initial_length = subject.filters.length
      filter = mock()
      subject.with_filter filter
      subject.filters.length.must_equal initial_length + 1
    end

    it "accepts object instances as filters" do
      filter = mock()
      subject.with_filter filter
      subject.filters.last.must_equal filter
    end

    it "accepts blocks as filters" do
      subject.with_filter do |a,b| end
      subject.filters.last.class.must_equal Proc
    end

    it "preserves the order of the filters" do
      subject.filters = []
      
      filter = mock()
      filter1 = mock()
      
      subject.with_filter filter
      subject.with_filter filter1
      subject.with_filter do |a,b| end

      subject.filters.first.must_equal filter
      subject.filters[1].must_equal filter1
      subject.filters.last.class.must_equal Proc
    end
  end

  describe "#get_service_properties" do
    let(:service_properties_xml)  { Fixtures["storage_service_properties"] }
    let(:service_properties) { Azure::Service::StorageServiceProperties.new }
    let(:response) { 
      response = mock()
      response.stubs(:body).returns(service_properties_xml)
      response
    }

    let(:service_properties_uri) { URI.parse 'http://dummy.uri/service/properties' }
    let(:service_properties_headers) { {"x-ms-version" => "2013-08-15"} }

    before do 
      Azure::Service::Serialization.stubs(:service_properties_from_xml).with(service_properties_xml).returns(service_properties)
      subject.stubs(:service_properties_uri).returns(service_properties_uri)
      subject.stubs(:call).with(:get, service_properties_uri, nil, service_properties_headers).returns(response)
    end

    it "calls the service_properties_uri method to determine the correct uri" do
      subject.expects(:service_properties_uri).returns(service_properties_uri)
      subject.get_service_properties
    end

    it "gets the response from the HTTP API" do
      subject.expects(:call).with(:get, service_properties_uri, nil, service_properties_headers).returns(response)
      subject.get_service_properties
    end

    it "deserializes the response from xml" do
      Azure::Service::Serialization.expects(:service_properties_from_xml).with(service_properties_xml).returns(service_properties)
      subject.get_service_properties
    end

    it "returns a StorageServiceProperties instance" do
      result = subject.get_service_properties
      result.must_be_kind_of Azure::Service::StorageServiceProperties
    end
  end

  describe "#set_service_properties" do
    let(:service_properties_xml)  { Fixtures["storage_service_properties"] }
    let(:service_properties) { Azure::Service::StorageServiceProperties.new }
    let(:response) { 
      response = mock()
      response.stubs(:success?).returns(true)
      response
    }

    let(:service_properties_uri) { URI.parse 'http://dummy.uri/service/properties' }
    let(:service_properties_headers) { {"x-ms-version" => "2013-08-15"} }

    before do 
      Azure::Service::Serialization.stubs(:service_properties_to_xml).with(service_properties).returns(service_properties_xml)
      subject.stubs(:service_properties_uri).returns(service_properties_uri)
      subject.stubs(:call).with(:put, service_properties_uri, service_properties_xml, service_properties_headers).returns(response)
    end

    it "calls the service_properties_uri method to determine the correct uri" do
      subject.expects(:service_properties_uri).returns(service_properties_uri)
      subject.set_service_properties service_properties
    end

    it "posts to the HTTP API" do
      subject.expects(:call).with(:put, service_properties_uri, service_properties_xml, service_properties_headers).returns(response)
      subject.set_service_properties service_properties
    end

    it "serializes the StorageServiceProperties object to xml" do
      Azure::Service::Serialization.expects(:service_properties_to_xml).with(service_properties).returns(service_properties_xml)
      subject.set_service_properties service_properties
    end

    it "returns nil on success" do
      result = subject.set_service_properties service_properties
      result.must_equal nil
    end
  end

  describe "service_properties_uri" do
    it "returns an instance of URI" do
      subject.service_properties_uri.must_be_kind_of URI
    end

    it "uses the value of the host property as the base of the url" do
      subject.service_properties_uri.to_s.must_include subject.host
      subject.host = "http://something.else"
      subject.service_properties_uri.to_s.must_include subject.host
    end

    it "sets a query string that specifies the storage service properties endpoint" do
      subject.service_properties_uri.query.must_include "restype=service&comp=properties"
    end
  end

  describe "#add_metadata_to_headers" do
    it "prefixes header names with x-ms-meta- but does not modify the values" do
      headers = {}
      subject.add_metadata_to_headers({"Foo"=> "Bar"}, headers)
      headers.keys.must_include "x-ms-meta-Foo"
      headers["x-ms-meta-Foo"].must_equal "Bar"
    end

    it "updates any existing x-ms-meta-* headers with the new values" do
      headers = { "x-ms-meta-Foo"=> "Foo"}
      subject.add_metadata_to_headers({"Foo"=> "Bar"}, headers)
      headers["x-ms-meta-Foo"].must_equal "Bar"
    end
  end

  describe "#generate_uri" do
    it "returns a URI instance" do
      subject.generate_uri().must_be_kind_of ::URI
    end

    describe "when called with no arguments" do
      it "returns the StorageService host URL" do
        subject.generate_uri().to_s.must_equal "http://dumyhost.uri/"
      end
    end

    describe "when passed an optional path" do
      it "adds the path to the host url" do
        subject.generate_uri("resource/entity/").path.must_equal "/resource/entity/"
      end

      it "correctly joins the path if the host url contained a path" do
        subject.host = "http://dummy.uri/host/path"
        subject.generate_uri("resource/entity/").path.must_equal "/host/path/resource/entity/"
      end
    end

    describe "when passed an Hash of query parameters" do

      it "encodes the keys" do
        subject.generate_uri('',{"key !" => "value"}).query.must_include "key+%21=value"
      end

      it "encodes the values" do
        subject.generate_uri('',{"key" => "value !"}).query.must_include "key=value+%21"
      end

      it "sets the query string to the encoded result" do
        subject.generate_uri('',{ "key" => "value !", "key !"=>"value"}).query.must_equal "key=value+%21&key+%21=value"
      end

      describe "when the query parameters include a timeout key" do
        it "overrides the default timeout" do
          subject.generate_uri('', {"timeout" => 45}).query.must_equal "timeout=45"
        end
      end

      describe "when the query parameters are nil" do
        it "does not include any query parameters" do
          subject.generate_uri('', nil).query.must_equal nil
        end
      end
    end
  end
end