# frozen_string_literal: true

# Copyright The OpenTelemetry Authors
#
# SPDX-License-Identifier: Apache-2.0

require 'test_helper'

require_relative '../../../../../lib/opentelemetry/instrumentation/http_client'
require_relative '../../../../../lib/opentelemetry/instrumentation/http_client/patches/dup/client'

describe OpenTelemetry::Instrumentation::HttpClient::Patches::Dup::Client do
  let(:instrumentation) { OpenTelemetry::Instrumentation::HttpClient::Instrumentation.instance }
  let(:exporter) { EXPORTER }
  let(:span) { exporter.finished_spans.first }

  before do
    skip unless ENV['BUNDLE_GEMFILE'].include?('dup')

    ENV['OTEL_SEMCONV_STABILITY_OPT_IN'] = 'http/dup'
    exporter.reset
    @orig_propagation = OpenTelemetry.propagation
    propagator = OpenTelemetry::Trace::Propagation::TraceContext.text_map_propagator
    OpenTelemetry.propagation = propagator
    instrumentation.install({})
    stub_request(:get, 'http://example.com/success').to_return(status: 200)
    stub_request(:get, 'http://example.com/success?hello=there').to_return(status: 200)
    stub_request(:post, 'http://example.com/failure').to_return(status: 500)
    stub_request(:get, 'https://example.com/timeout').to_timeout
  end

  after do
    # Force re-install of instrumentation
    instrumentation.instance_variable_set(:@installed, false)

    OpenTelemetry.propagation = @orig_propagation
  end

  describe '#do_request' do
    it 'traces a simple request' do
      http = HTTPClient.new
      http.receive_timeout = 1
      http.get('http://example.com/success')

      _(exporter.finished_spans.size).must_equal(1)
      _(span.name).must_equal 'GET'
      _(span.attributes['http.method']).must_equal 'GET'
      _(span.attributes['http.scheme']).must_equal 'http'
      _(span.attributes['http.status_code']).must_equal 200
      _(span.attributes['http.target']).must_equal '/success'
      _(span.attributes['net.peer.name']).must_equal 'example.com'
      _(span.attributes['net.peer.port']).must_equal 80
      # stable semantic conventions
      _(span.attributes['http.request.method']).must_equal 'GET'
      _(span.attributes['url.scheme']).must_equal 'http'
      _(span.attributes['http.response.status_code']).must_equal 200
      _(span.attributes['url.path']).must_equal '/success'
      _(span.attributes['server.address']).must_equal 'example.com'
      _(span.attributes['server.port']).must_equal 80
      assert_requested(
        :get,
        'http://example.com/success',
        headers: { 'Traceparent' => "00-#{span.hex_trace_id}-#{span.hex_span_id}-01" }
      )
    end

    it 'after request with failure code' do
      http = HTTPClient.new
      http.receive_timeout = 1
      http.post('http://example.com/failure')

      _(exporter.finished_spans.size).must_equal 1
      _(span.name).must_equal 'POST'
      _(span.attributes['http.method']).must_equal 'POST'
      _(span.attributes['http.scheme']).must_equal 'http'
      _(span.attributes['http.status_code']).must_equal 500
      _(span.attributes['http.target']).must_equal '/failure'
      _(span.attributes['net.peer.name']).must_equal 'example.com'
      _(span.attributes['net.peer.port']).must_equal 80
      # stable semantic conventions
      _(span.attributes['http.request.method']).must_equal 'POST'
      _(span.attributes['url.scheme']).must_equal 'http'
      _(span.attributes['http.response.status_code']).must_equal 500
      _(span.attributes['url.path']).must_equal '/failure'
      _(span.attributes['server.address']).must_equal 'example.com'
      _(span.attributes['server.port']).must_equal 80
      assert_requested(
        :post,
        'http://example.com/failure',
        headers: { 'Traceparent' => "00-#{span.hex_trace_id}-#{span.hex_span_id}-01" }
      )
    end

    it 'after request timeout' do
      expect do
        http = HTTPClient.new
        http.receive_timeout = 1
        http.get('https://example.com/timeout')
      end.must_raise HTTPClient::TimeoutError

      _(exporter.finished_spans.size).must_equal 1
      _(span.name).must_equal 'GET'
      _(span.attributes['http.method']).must_equal 'GET'
      _(span.attributes['http.scheme']).must_equal 'https'
      _(span.attributes['http.status_code']).must_be_nil
      _(span.attributes['http.target']).must_equal '/timeout'
      _(span.attributes['net.peer.name']).must_equal 'example.com'
      _(span.attributes['net.peer.port']).must_equal 443
      # stable semantic coventions
      _(span.attributes['http.request.method']).must_equal 'GET'
      _(span.attributes['url.scheme']).must_equal 'https'
      _(span.attributes['http.response.status_code']).must_be_nil
      _(span.attributes['url.path']).must_equal '/timeout'
      _(span.attributes['server.address']).must_equal 'example.com'
      _(span.attributes['server.port']).must_equal 443
      _(span.status.code).must_equal(
        OpenTelemetry::Trace::Status::ERROR
      )
      _(span.status.description).must_equal(
        'Unhandled exception of type: HTTPClient::TimeoutError'
      )
      assert_requested(
        :get,
        'https://example.com/timeout',
        headers: { 'Traceparent' => "00-#{span.hex_trace_id}-#{span.hex_span_id}-01" }
      )
    end

    it 'merges http client attributes' do
      OpenTelemetry::Common::HTTP::ClientContext.with_attributes('peer.service' => 'foo') do
        http = HTTPClient.new
        http.receive_timeout = 1
        http.get('http://example.com/success')
      end

      _(exporter.finished_spans.size).must_equal 1
      _(span.name).must_equal 'GET'
      _(span.attributes['http.method']).must_equal 'GET'
      _(span.attributes['http.scheme']).must_equal 'http'
      _(span.attributes['http.status_code']).must_equal 200
      _(span.attributes['http.target']).must_equal '/success'
      _(span.attributes['net.peer.name']).must_equal 'example.com'
      _(span.attributes['net.peer.port']).must_equal 80
      _(span.attributes['peer.service']).must_equal 'foo'
      # stable semantic conventions
      _(span.attributes['http.request.method']).must_equal 'GET'
      _(span.attributes['url.scheme']).must_equal 'http'
      _(span.attributes['http.response.status_code']).must_equal 200
      _(span.attributes['url.path']).must_equal '/success'
      _(span.attributes['server.address']).must_equal 'example.com'
      _(span.attributes['server.port']).must_equal 80
      _(span.attributes['peer.service']).must_equal 'foo'
      assert_requested(
        :get,
        'http://example.com/success',
        headers: { 'Traceparent' => "00-#{span.hex_trace_id}-#{span.hex_span_id}-01" }
      )
    end

    it 'merges http query attribute when present' do
      http = HTTPClient.new
      http.receive_timeout = 1
      http.get('http://example.com/success?hello=there')

      _(exporter.finished_spans.size).must_equal 1
      _(span.name).must_equal 'GET'
      _(span.attributes['url.path']).must_equal '/success'
      _(span.attributes['url.query']).must_equal 'hello=there'
      assert_requested(
        :get,
        'http://example.com/success?hello=there',
        headers: { 'Traceparent' => "00-#{span.hex_trace_id}-#{span.hex_span_id}-01" }
      )
    end
  end
end
