# Copyright (c) 2015 AppNeta, Inc.
# All rights reserved.

unless defined?(JRUBY_VERSION)
  require 'minitest_helper'
  require 'traceview/inst/rack'
  require File.expand_path(File.dirname(__FILE__) + '../../frameworks/apps/sinatra_simple')

  class HTTPClientTest < Minitest::Test
    include Rack::Test::Methods

    def app
      SinatraSimple
    end

    def test_reports_version_init
      init_kvs = ::TraceView::Util.build_init_report
      assert init_kvs.key?('Ruby.HTTPClient.Version')
      assert_equal init_kvs['Ruby.HTTPClient.Version'], "HTTPClient-#{::HTTPClient::VERSION}"
    end

    def test_get_request
      clear_all_traces

      response = nil

      TraceView::API.start_trace('httpclient_tests') do
        clnt = HTTPClient.new
        response = clnt.get('http://127.0.0.1:8101/', :query => { :keyword => 'ruby', :lang => 'en' })
      end

      traces = get_all_traces

      # Validate returned xtrace
      assert response.headers.key?("X-Trace")
      assert TraceView::XTrace.valid?(response.headers["X-Trace"])

      assert_equal traces.count, 7
      valid_edges?(traces)
      validate_outer_layers(traces, "httpclient_tests")

      assert_equal traces[1]['IsService'], 1
      assert_equal traces[1]['RemoteURL'], 'http://127.0.0.1:8101/?keyword=ruby&lang=en'
      assert_equal traces[1]['HTTPMethod'], 'GET'
      assert traces[1].key?('Backtrace')

      assert_equal traces[5]['Layer'], 'httpclient'
      assert_equal traces[5]['Label'], 'exit'
      assert_equal traces[5]['HTTPStatus'], 200
    end

    def test_get_with_header_hash
      clear_all_traces

      response = nil

      TraceView::API.start_trace('httpclient_tests') do
        clnt = HTTPClient.new
        response = clnt.get('http://127.0.0.1:8101/', nil, { "SOAPAction" => "HelloWorld" })
      end

      traces = get_all_traces

      xtrace = response.headers['X-Trace']
      assert xtrace
      assert TraceView::XTrace.valid?(xtrace)

      assert_equal traces.count, 7
      valid_edges?(traces)
      validate_outer_layers(traces, "httpclient_tests")

      assert_equal traces[1]['IsService'], 1
      assert_equal traces[1]['RemoteURL'], 'http://127.0.0.1:8101/'
      assert_equal traces[1]['HTTPMethod'], 'GET'
      assert traces[1].key?('Backtrace')

      assert_equal traces[5]['Layer'], 'httpclient'
      assert_equal traces[5]['Label'], 'exit'
      assert_equal traces[5]['HTTPStatus'], 200
    end

    def test_get_with_header_array
      clear_all_traces

      response = nil

      TraceView::API.start_trace('httpclient_tests') do
        clnt = HTTPClient.new
        response = clnt.get('http://127.0.0.1:8101/', nil, [["Accept", "text/plain"], ["Accept", "text/html"]])
      end

      traces = get_all_traces

      xtrace = response.headers['X-Trace']
      assert xtrace
      assert TraceView::XTrace.valid?(xtrace)

      assert_equal traces.count, 7
      valid_edges?(traces)
      validate_outer_layers(traces, "httpclient_tests")

      assert_equal traces[1]['IsService'], 1
      assert_equal traces[1]['RemoteURL'], 'http://127.0.0.1:8101/'
      assert_equal traces[1]['HTTPMethod'], 'GET'
      assert traces[1].key?('Backtrace')

      assert_equal traces[5]['Layer'], 'httpclient'
      assert_equal traces[5]['Label'], 'exit'
      assert_equal traces[5]['HTTPStatus'], 200
    end

    def test_post_request
      clear_all_traces

      response = nil

      TraceView::API.start_trace('httpclient_tests') do
        clnt = HTTPClient.new
        response = clnt.post('http://127.0.0.1:8101/')
      end

      traces = get_all_traces

      xtrace = response.headers['X-Trace']
      assert xtrace
      assert TraceView::XTrace.valid?(xtrace)

      assert_equal traces.count, 7
      valid_edges?(traces)
      validate_outer_layers(traces, "httpclient_tests")

      assert_equal traces[1]['IsService'], 1
      assert_equal traces[1]['RemoteURL'], 'http://127.0.0.1:8101/'
      assert_equal traces[1]['HTTPMethod'], 'POST'
      assert traces[1].key?('Backtrace')

      assert_equal traces[5]['Layer'], 'httpclient'
      assert_equal traces[5]['Label'], 'exit'
      assert_equal traces[5]['HTTPStatus'], 200
    end

    def test_async_get
      skip if RUBY_VERSION < '1.9.2'

      clear_all_traces

      conn = nil

      TraceView::API.start_trace('httpclient_tests') do
        clnt = HTTPClient.new
        conn = clnt.get_async('http://127.0.0.1:8101/?blah=1')
      end

      # Allow async request to finish
      Thread.pass until conn.finished?

      traces = get_all_traces
      #require 'byebug'; debugger
      assert_equal traces.count, 7
      valid_edges?(traces)

      # FIXME: validate_outer_layers assumes that the traces
      # are ordered which in the case of async, they aren't
      # validate_outer_layers(traces, "httpclient_tests")

      assert_equal traces[2]['Async'], 1
      assert_equal traces[2]['IsService'], 1
      assert_equal traces[2]['RemoteURL'], 'http://127.0.0.1:8101/?blah=1'
      assert_equal traces[2]['HTTPMethod'], 'GET'
      assert traces[2].key?('Backtrace')

      assert_equal traces[6]['Layer'], 'httpclient'
      assert_equal traces[6]['Label'], 'exit'
      assert_equal traces[6]['HTTPStatus'], 200
    end

    def test_cross_app_tracing
      clear_all_traces

      response = nil

      TraceView::API.start_trace('httpclient_tests') do
        clnt = HTTPClient.new
        response = clnt.get('http://127.0.0.1:8101/', :query => { :keyword => 'ruby', :lang => 'en' })
      end

      xtrace = response.headers['X-Trace']
      assert xtrace
      assert TraceView::XTrace.valid?(xtrace)

      traces = get_all_traces

      assert_equal traces.count, 7
      valid_edges?(traces)
      validate_outer_layers(traces, "httpclient_tests")

      assert_equal traces[1]['IsService'], 1
      assert_equal traces[1]['RemoteURL'], 'http://127.0.0.1:8101/?keyword=ruby&lang=en'
      assert_equal traces[1]['HTTPMethod'], 'GET'
      assert traces[1].key?('Backtrace')

      assert_equal traces[2]['Layer'], 'rack'
      assert_equal traces[2]['Label'], 'entry'
      assert_equal traces[3]['Layer'], 'rack'
      assert_equal traces[3]['Label'], 'info'
      assert_equal traces[4]['Layer'], 'rack'
      assert_equal traces[4]['Label'], 'exit'

      assert_equal traces[5]['Layer'], 'httpclient'
      assert_equal traces[5]['Label'], 'exit'
      assert_equal traces[5]['HTTPStatus'], 200
    end

    def test_requests_with_errors
      clear_all_traces

      result = nil
      begin
        TraceView::API.start_trace('httpclient_tests') do
          clnt = HTTPClient.new
          result = clnt.get('http://asfjalkfjlajfljkaljf/')
        end
      rescue
      end

      traces = get_all_traces
      assert_equal traces.count, 5
      valid_edges?(traces)
      validate_outer_layers(traces, "httpclient_tests")

      assert_equal traces[1]['IsService'], 1
      assert_equal traces[1]['RemoteURL'], 'http://asfjalkfjlajfljkaljf/'
      assert_equal traces[1]['HTTPMethod'], 'GET'
      assert traces[1].key?('Backtrace')

      assert_equal traces[2]['Layer'], 'httpclient'
      assert_equal traces[2]['Label'], 'error'
      assert_equal traces[2]['ErrorClass'], "SocketError"
      assert traces[2].key?('ErrorMsg')
      assert traces[2].key?('Backtrace')

      assert_equal traces[3]['Layer'], 'httpclient'
      assert_equal traces[3]['Label'], 'exit'
    end

    def test_log_args_when_true
      clear_all_traces

      @log_args = TraceView::Config[:httpclient][:log_args]
      TraceView::Config[:httpclient][:log_args] = true

      response = nil

      TraceView::API.start_trace('httpclient_tests') do
        clnt = HTTPClient.new
        response = clnt.get('http://127.0.0.1:8101/', :query => { :keyword => 'ruby', :lang => 'en' })
      end

      traces = get_all_traces

      xtrace = response.headers['X-Trace']
      assert xtrace
      assert TraceView::XTrace.valid?(xtrace)

      assert_equal traces.count, 7
      valid_edges?(traces)

      assert_equal traces[1]['RemoteURL'], 'http://127.0.0.1:8101/?keyword=ruby&lang=en'

      TraceView::Config[:httpclient][:log_args] = @log_args
    end

    def test_log_args_when_false
      clear_all_traces

      @log_args = TraceView::Config[:httpclient][:log_args]
      TraceView::Config[:httpclient][:log_args] = false

      response = nil

      TraceView::API.start_trace('httpclient_tests') do
        clnt = HTTPClient.new
        response = clnt.get('http://127.0.0.1:8101/', :query => { :keyword => 'ruby', :lang => 'en' })
      end

      traces = get_all_traces

      xtrace = response.headers['X-Trace']
      assert xtrace
      assert TraceView::XTrace.valid?(xtrace)

      assert_equal traces.count, 7
      valid_edges?(traces)

      assert_equal traces[1]['RemoteURL'], 'http://127.0.0.1:8101/'

      TraceView::Config[:httpclient][:log_args] = @log_args
    end

    def test_without_tracing
      clear_all_traces

      @tm = TraceView::Config[:tracing_mode]
      TraceView::Config[:tracing_mode] = :never

      response = nil

      TraceView::API.start_trace('httpclient_tests') do
        clnt = HTTPClient.new
        response = clnt.get('http://127.0.0.1:8101/', :query => { :keyword => 'ruby', :lang => 'en' })
      end

      xtrace = response.headers['X-Trace']
      assert xtrace == nil

      traces = get_all_traces

      assert_equal traces.count, 0

      TraceView::Config[:tracing_mode] = @tm
    end
  end
end