require 'spec_helper'

describe "Typhoeus hook", :with_monkey_patches => :typhoeus, :if => (RUBY_INTERPRETER == :mri) do
  after(:each) do
    ::Typhoeus::Expectation.clear
  end

  def disable_real_connections
    ::Typhoeus::Config.block_connection = true
    ::Typhoeus::Errors::NoStub
  end

  def enable_real_connections
    ::Typhoeus::Config.block_connection = false
  end

  def directly_stub_request(method, url, response_body)
    response = ::Typhoeus::Response.new(:code => 200, :body => response_body)
    ::Typhoeus.stub(url, :method => method).and_return(response)
  end

  it_behaves_like 'a hook into an HTTP library', :typhoeus, 'typhoeus'

  describe "VCR.configuration.after_library_hooks_loaded hook" do
    it 'disables the webmock typhoeus adapter so it does not conflict with our typhoeus hook' do
      expect(::WebMock::HttpLibAdapters::TyphoeusAdapter).to receive(:disable!)
      $typhoeus_after_loaded_hook.conditionally_invoke
    end
  end

  context 'when there are nested hydra queues' do
    def make_requests
      VCR.use_cassette("nested") do
        response_1 = response_2 = nil

        hydra   = Typhoeus::Hydra.new
        request = Typhoeus::Request.new("http://localhost:#{VCR::SinatraApp.port}/")

        request.on_success do |r1|
          response_1 = r1

          nested = Typhoeus::Request.new("http://localhost:#{VCR::SinatraApp.port}/foo")
          nested.on_success { |r2| response_2 = r2 }

          hydra.queue(nested)
        end

        hydra.queue(request)
        hydra.run

        return body_for(response_1), body_for(response_2)
      end
    end

    def body_for(response)
      return :no_response if response.nil?
      response.body
    end

    it 'records and plays back properly' do
      recorded = make_requests
      played_back = make_requests

      expect(played_back).to eq(recorded)
    end
  end

  context "when used with a typhoeus-based faraday connection" do
    let(:base_url) { "http://localhost:#{VCR::SinatraApp.port}" }

    let(:conn) do
      Faraday.new(:url => base_url) do |faraday|
        faraday.adapter  :typhoeus
      end
    end

    def get_response
      # Ensure faraday hook doesn't handle the request.
      VCR.library_hooks.exclusively_enabled(:typhoeus) do
        VCR.use_cassette("faraday") do
          conn.get("/")
        end
      end
    end

    it 'records and replays headers correctly' do
      recorded = get_response
      played_back = get_response

      expect(played_back.headers).to eq(recorded.headers)
    end
  end

  context 'when a request is made with a hash for the POST body' do
    def make_request
      VCR.use_cassette("hash_body") do
        Typhoeus::Request.post(
          "http://localhost:#{VCR::SinatraApp.port}/return-request-body",
          :body => { :foo => "17" }
        )
      end
    end

    it 'records and replays correctly' do
      recorded = make_request
      played_back = make_request

      expect(recorded.body).to eq("foo=17")
      expect(played_back.body).to eq(recorded.body)
    end
  end

  context 'when using on_body callback' do
    def make_request
      VCR.use_cassette('no_body') do
        request = Typhoeus::Request.new("http://localhost:#{VCR::SinatraApp.port}/localhost_test")
        request.on_headers { on_headers_counter.increment }
        request.on_body { on_body_counter.increment }
        request.run
      end
    end

    let(:on_headers_counter) { double(:increment => nil) }
    let(:on_body_counter) { double(:increment => nil) }

    it 'records and replays correctly' do
      expect(on_headers_counter).to receive(:increment).exactly(2).times
      expect(on_body_counter).to receive(:increment).exactly(2).times

      recorded = make_request
      played_back = make_request

      expect(recorded.body).to eq('Localhost response')
      expect(played_back.body).to eq(recorded.body)
    end
  end

  context 'when using on_body callback returning :abort' do
    def make_request
      VCR.use_cassette('no_body') do
        request = Typhoeus::Request.new("http://localhost:#{VCR::SinatraApp.port}/localhost_test")
        request.on_body { next :abort }
        request.run
      end
    end

    it 'records and replays correctly' do
      recorded = make_request
      played_back = make_request

      expect(recorded.body).to eq('Localhost response')
      expect(played_back.body).to eq(recorded.body)
    end
  end

  context '#effective_url' do
    ResponseValues = Struct.new(:status, :body, :effective_url)

    def url_for(path)
      "http://localhost:#{VCR::SinatraApp.port}#{path}"
    end

    def make_single_request(path, options = {})
      VCR.use_cassette('single') do |cassette|
        response = Typhoeus::Request.new(url_for(path), options).run

        yield cassette if block_given?

        ResponseValues.new(
          response.code,
          response.body,
          response.effective_url
        )
      end
    end

    it 'records and plays back properly' do
      recorded = make_single_request('/')
      played_back = make_single_request('/')

      expect(recorded.effective_url).to eq(url_for('/'))
      expect(played_back).to eq(recorded)
    end

    it 'falls back to the request url when it was not recorded (e.g. on VCR <= 2.5.0)' do
      make_single_request('/') do |cassette|
        cassette.new_recorded_interactions.each { |i| i.response.adapter_metadata.clear }
      end

      played_back = make_single_request('/')
      expect(played_back.effective_url).to eq(url_for('/'))
    end

    context "when following redirects" do
      it 'records and plays back properly' do
        recorded = make_single_request('/redirect-to-root', :followlocation => true)
        played_back = make_single_request('/redirect-to-root', :followlocation => true)

        expect(recorded.effective_url).to eq(url_for('/'))
        expect(played_back).to eq(recorded)
      end
    end
  end
end

