mirror of
				https://github.com/HoneyryderChuck/httpx.git
				synced 2025-11-04 00:01:41 -05:00 
			
		
		
		
	* tests when tracer is disabled * tests distributed headers thoroughly * tests multiple requests spans * per host / split by domain
		
			
				
	
	
		
			291 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			291 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
# frozen_string_literal: true
 | 
						|
 | 
						|
require "test_helper"
 | 
						|
require "support/http_helpers"
 | 
						|
require "ddtrace"
 | 
						|
require "httpx/adapters/datadog"
 | 
						|
 | 
						|
class DatadogTest < Minitest::Test
 | 
						|
  include HTTPHelpers
 | 
						|
 | 
						|
  def test_datadog_disabled
 | 
						|
    set_datadog
 | 
						|
    tracer.configure(enabled: false)
 | 
						|
    uri = URI(build_uri("/status/200", "http://#{httpbin}"))
 | 
						|
 | 
						|
    response = HTTPX.get(uri)
 | 
						|
    verify_status(response, 200)
 | 
						|
 | 
						|
    assert fetch_spans.empty?, "expected not to have spans"
 | 
						|
    verify_no_distributed_headers(response)
 | 
						|
  end
 | 
						|
 | 
						|
  def test_datadog_successful_get_request
 | 
						|
    set_datadog
 | 
						|
    uri = URI(build_uri("/status/200", "http://#{httpbin}"))
 | 
						|
 | 
						|
    response = HTTPX.get(uri)
 | 
						|
    verify_status(response, 200)
 | 
						|
 | 
						|
    assert !fetch_spans.empty?, "expected to have spans"
 | 
						|
    verify_instrumented_request(response, verb: "GET", uri: uri)
 | 
						|
    verify_distributed_headers(response)
 | 
						|
  end
 | 
						|
 | 
						|
  def test_datadog_successful_post_request
 | 
						|
    set_datadog
 | 
						|
    uri = URI(build_uri("/status/200", "http://#{httpbin}"))
 | 
						|
 | 
						|
    response = HTTPX.post(uri, body: "bla")
 | 
						|
    verify_status(response, 200)
 | 
						|
 | 
						|
    assert !fetch_spans.empty?, "expected to have spans"
 | 
						|
    verify_instrumented_request(response, verb: "POST", uri: uri)
 | 
						|
    verify_distributed_headers(response)
 | 
						|
  end
 | 
						|
 | 
						|
  def test_datadog_successful_multiple_requests
 | 
						|
    set_datadog
 | 
						|
    uri = URI(build_uri("/status/200", "http://#{httpbin}"))
 | 
						|
 | 
						|
    get_response, post_response = HTTPX.request([[:get, uri], [:post, uri]])
 | 
						|
    verify_status(get_response, 200)
 | 
						|
    verify_status(post_response, 200)
 | 
						|
 | 
						|
    assert fetch_spans.size == 2, "expected to have 2 spans"
 | 
						|
    get_span, post_span = fetch_spans
 | 
						|
    verify_instrumented_request(get_response, span: get_span, verb: "GET", uri: uri)
 | 
						|
    verify_instrumented_request(post_response, span: post_span, verb: "POST", uri: uri)
 | 
						|
    verify_distributed_headers(get_response, span: get_span)
 | 
						|
    verify_distributed_headers(post_response, span: post_span)
 | 
						|
    verify_analytics_headers(get_span)
 | 
						|
    verify_analytics_headers(post_span)
 | 
						|
  end
 | 
						|
 | 
						|
  def test_datadog_server_error_request
 | 
						|
    set_datadog
 | 
						|
    uri = URI(build_uri("/status/500", "http://#{httpbin}"))
 | 
						|
 | 
						|
    response = HTTPX.get(uri)
 | 
						|
    verify_status(response, 500)
 | 
						|
 | 
						|
    assert !fetch_spans.empty?, "expected to have spans"
 | 
						|
    verify_instrumented_request(response, verb: "GET", uri: uri)
 | 
						|
    verify_distributed_headers(response)
 | 
						|
  end
 | 
						|
 | 
						|
  def test_datadog_client_error_request
 | 
						|
    set_datadog
 | 
						|
    uri = URI(build_uri("/status/404", "http://#{httpbin}"))
 | 
						|
 | 
						|
    response = HTTPX.get(uri)
 | 
						|
    verify_status(response, 404)
 | 
						|
 | 
						|
    assert !fetch_spans.empty?, "expected to have spans"
 | 
						|
    verify_instrumented_request(response, verb: "GET", uri: uri)
 | 
						|
    verify_distributed_headers(response)
 | 
						|
  end
 | 
						|
 | 
						|
  def test_datadog_some_other_error
 | 
						|
    set_datadog
 | 
						|
    uri = URI("http://unexisting/")
 | 
						|
 | 
						|
    response = HTTPX.get(uri)
 | 
						|
    assert response.is_a?(HTTPX::ErrorResponse), "response should contain errors"
 | 
						|
 | 
						|
    assert !fetch_spans.empty?, "expected to have spans"
 | 
						|
    verify_instrumented_request(response, verb: "GET", uri: uri, error: "HTTPX::NativeResolveError")
 | 
						|
    verify_distributed_headers(response)
 | 
						|
  end
 | 
						|
 | 
						|
  def test_datadog_host_config
 | 
						|
    uri = URI(build_uri("/status/200", "http://#{httpbin}"))
 | 
						|
    set_datadog(describe: /#{uri.host}/) do |http|
 | 
						|
      http.service_name = "httpbin"
 | 
						|
      http.split_by_domain = false
 | 
						|
    end
 | 
						|
 | 
						|
    response = HTTPX.get(uri)
 | 
						|
    verify_status(response, 200)
 | 
						|
 | 
						|
    assert !fetch_spans.empty?, "expected to have spans"
 | 
						|
    verify_instrumented_request(response, service: "httpbin", verb: "GET", uri: uri)
 | 
						|
    verify_distributed_headers(response)
 | 
						|
  end
 | 
						|
 | 
						|
  def test_datadog_split_by_domain
 | 
						|
    uri = URI(build_uri("/status/200", "http://#{httpbin}"))
 | 
						|
    set_datadog do |http|
 | 
						|
      http.split_by_domain = true
 | 
						|
    end
 | 
						|
 | 
						|
    response = HTTPX.get(uri)
 | 
						|
    verify_status(response, 200)
 | 
						|
 | 
						|
    assert !fetch_spans.empty?, "expected to have spans"
 | 
						|
    verify_instrumented_request(response, service: uri.host, verb: "GET", uri: uri)
 | 
						|
    verify_distributed_headers(response)
 | 
						|
  end
 | 
						|
 | 
						|
  def test_datadog_distributed_headers_disabled
 | 
						|
    set_datadog(distributed_tracing: false)
 | 
						|
    uri = URI(build_uri("/status/200", "http://#{httpbin}"))
 | 
						|
 | 
						|
    sampling_priority = 10
 | 
						|
    tracer.configure(enabled: true)
 | 
						|
    response = tracer.trace("foo.bar") do |span|
 | 
						|
      span.context.sampling_priority = sampling_priority
 | 
						|
      HTTPX.get(uri)
 | 
						|
    end
 | 
						|
    verify_status(response, 200)
 | 
						|
 | 
						|
    assert !fetch_spans.empty?, "expected to have spans"
 | 
						|
    span = fetch_spans.last
 | 
						|
    verify_instrumented_request(response, span: span, verb: "GET", uri: uri)
 | 
						|
    verify_no_distributed_headers(response)
 | 
						|
    verify_analytics_headers(span)
 | 
						|
  end
 | 
						|
 | 
						|
  def test_datadog_distributed_headers_sampling_priority
 | 
						|
    set_datadog
 | 
						|
    uri = URI(build_uri("/status/200", "http://#{httpbin}"))
 | 
						|
 | 
						|
    sampling_priority = 10
 | 
						|
    tracer.configure(enabled: true)
 | 
						|
    response = tracer.trace("foo.bar") do |span|
 | 
						|
      span.context.sampling_priority = sampling_priority
 | 
						|
      HTTPX.get(uri)
 | 
						|
    end
 | 
						|
    verify_status(response, 200)
 | 
						|
 | 
						|
    assert !fetch_spans.empty?, "expected to have spans"
 | 
						|
    span = fetch_spans.last
 | 
						|
    verify_instrumented_request(response, span: span, verb: "GET", uri: uri)
 | 
						|
    verify_distributed_headers(response, span: span, sampling_priority: sampling_priority)
 | 
						|
    verify_analytics_headers(span)
 | 
						|
  end
 | 
						|
 | 
						|
  def test_datadog_analytics_enabled
 | 
						|
    set_datadog(analytics_enabled: true)
 | 
						|
    uri = URI(build_uri("/status/200", "http://#{httpbin}"))
 | 
						|
 | 
						|
    response = HTTPX.get(uri)
 | 
						|
    verify_status(response, 200)
 | 
						|
 | 
						|
    assert !fetch_spans.empty?, "expected to have spans"
 | 
						|
    span = fetch_spans.last
 | 
						|
    verify_instrumented_request(response, span: span, verb: "GET", uri: uri)
 | 
						|
    verify_analytics_headers(span, sample_rate: 1.0)
 | 
						|
  end
 | 
						|
 | 
						|
  def test_datadog_analytics_sample_rate
 | 
						|
    set_datadog(analytics_enabled: true, analytics_sample_rate: 0.5)
 | 
						|
    uri = URI(build_uri("/status/200", "http://#{httpbin}"))
 | 
						|
 | 
						|
    response = HTTPX.get(uri)
 | 
						|
    verify_status(response, 200)
 | 
						|
 | 
						|
    assert !fetch_spans.empty?, "expected to have spans"
 | 
						|
    span = fetch_spans.last
 | 
						|
    verify_instrumented_request(response, span: span, verb: "GET", uri: uri)
 | 
						|
    verify_analytics_headers(span, sample_rate: 0.5)
 | 
						|
  end
 | 
						|
 | 
						|
  private
 | 
						|
 | 
						|
  def set_datadog(options = {}, &blk)
 | 
						|
    Datadog.reset!
 | 
						|
    Datadog.registry[:httpx].reset_configuration!
 | 
						|
 | 
						|
    Datadog.configure do |c|
 | 
						|
      c.use(:httpx, options, &blk)
 | 
						|
    end
 | 
						|
 | 
						|
    tracer # initialize tracer patches
 | 
						|
  end
 | 
						|
 | 
						|
  def verify_instrumented_request(response, verb:, uri:, span: fetch_spans.first, service: "httpx", error: nil)
 | 
						|
    assert span.is_a?(Datadog::Span)
 | 
						|
    assert span.span_type == "http"
 | 
						|
    assert span.name == "httpx.request"
 | 
						|
    assert span.service == service
 | 
						|
 | 
						|
    assert span.get_tag(Datadog::Ext::NET::TARGET_HOST) == uri.host
 | 
						|
    assert span.get_tag(Datadog::Ext::NET::TARGET_PORT) == "80"
 | 
						|
    assert span.get_tag(Datadog::Ext::HTTP::METHOD) == verb
 | 
						|
    assert span.get_tag(Datadog::Ext::HTTP::URL) == uri.path
 | 
						|
    if error
 | 
						|
      assert span.get_tag("error.type") == error
 | 
						|
      assert !span.get_tag("error.msg").nil?
 | 
						|
      assert span.status == 1
 | 
						|
    elsif response.status >= 400
 | 
						|
      assert span.get_tag(Datadog::Ext::HTTP::STATUS_CODE) == response.status.to_s
 | 
						|
      assert span.get_tag("error.type") == "HTTPX::HTTPError"
 | 
						|
      assert !span.get_tag("error.msg").nil?
 | 
						|
      assert span.status == 1
 | 
						|
    else
 | 
						|
      assert span.status.zero?
 | 
						|
      assert span.get_tag(Datadog::Ext::HTTP::STATUS_CODE) == response.status.to_s
 | 
						|
      # peer service
 | 
						|
      assert span.get_tag("peer.service") == span.service
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  def verify_no_distributed_headers(response)
 | 
						|
    request = response.instance_variable_get(:@request)
 | 
						|
 | 
						|
    assert !request.headers.key?(Datadog::Ext::DistributedTracing::HTTP_HEADER_PARENT_ID)
 | 
						|
    assert !request.headers.key?(Datadog::Ext::DistributedTracing::HTTP_HEADER_TRACE_ID)
 | 
						|
    assert !request.headers.key?(Datadog::Ext::DistributedTracing::HTTP_HEADER_SAMPLING_PRIORITY)
 | 
						|
  end
 | 
						|
 | 
						|
  def verify_distributed_headers(response, span: fetch_spans.first, sampling_priority: 1)
 | 
						|
    request = response.instance_variable_get(:@request)
 | 
						|
 | 
						|
    assert request.headers[Datadog::Ext::DistributedTracing::HTTP_HEADER_PARENT_ID] == span.span_id.to_s
 | 
						|
    assert request.headers[Datadog::Ext::DistributedTracing::HTTP_HEADER_TRACE_ID] == span.trace_id.to_s
 | 
						|
    assert request.headers[Datadog::Ext::DistributedTracing::HTTP_HEADER_SAMPLING_PRIORITY] == sampling_priority.to_s
 | 
						|
  end
 | 
						|
 | 
						|
  def verify_analytics_headers(span, sample_rate: nil)
 | 
						|
    assert span.get_metric(Datadog::Ext::Analytics::TAG_SAMPLE_RATE) == sample_rate
 | 
						|
  end
 | 
						|
 | 
						|
  def tracer
 | 
						|
    @tracer ||= begin
 | 
						|
      tr = Datadog.tracer
 | 
						|
      def tr.write(trace)
 | 
						|
        @spans ||= []
 | 
						|
        @spans << trace
 | 
						|
      end
 | 
						|
      tr
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  # Returns spans and caches it (similar to +let(:spans)+).
 | 
						|
  def spans
 | 
						|
    @spans ||= fetch_spans
 | 
						|
  end
 | 
						|
 | 
						|
  # Retrieves and sorts all spans in the current tracer instance.
 | 
						|
  # This method does not cache its results.
 | 
						|
  def fetch_spans
 | 
						|
    spans = tracer.instance_variable_get(:@spans) || []
 | 
						|
    spans.flatten.sort! do |a, b|
 | 
						|
      if a.name == b.name
 | 
						|
        if a.resource == b.resource
 | 
						|
          if a.start_time == b.start_time
 | 
						|
            a.end_time <=> b.end_time
 | 
						|
          else
 | 
						|
            a.start_time <=> b.start_time
 | 
						|
          end
 | 
						|
        else
 | 
						|
          a.resource <=> b.resource
 | 
						|
        end
 | 
						|
      else
 | 
						|
        a.name <=> b.name
 | 
						|
      end
 | 
						|
    end
 | 
						|
  end
 | 
						|
end
 |