# frozen_string_literal: true

require_relative "test_helper"

class HTTPXAwsSigv4Test < Minitest::Test
  include ResponseHelpers

  def test_plugin_aws_sigv4_canonical_query
    r1 = sigv4_session.build_request("GET", "http://domain.com?b=c&a=b")
    assert r1.canonical_query == "a=b&b=c"
    r2 = sigv4_session.build_request("GET", "http://domain.com?a=c&a=b")
    assert r2.canonical_query == "a=b&a=c"
    r3 = sigv4_session.build_request("GET", "http://domain.com?a=b&a=b")
    assert r3.canonical_query == "a=b&a=b"
    r4 = sigv4_session.build_request("GET", "http://domain.com?b&a=b")
    assert r4.canonical_query == "a=b&b"
  end

  def test_plugin_aws_sigv4_x_amz_date
    request = sigv4_session.build_request("GET", "http://domain.com")
    # x-amz-date
    assert request.headers.key?("x-amz-date")
    amz_date = Time.parse(request.headers["x-amz-date"])
    assert_in_delta(amz_date, Time.now.utc, 3)

    # date already set
    date = Time.now.utc - (60 * 60 * 24)
    date_amz = date.strftime("%Y%m%dT%H%M%SZ")
    x_date_request = sigv4_session.build_request("GET", "http://domain.com", headers: { "x-amz-date" => date_amz })
    verify_header(x_date_request.headers, "x-amz-date", date_amz)
  end

  def test_plugin_aws_sigv4_x_amz_security_token
    request = sigv4_session.build_request("GET", "http://domain.com")
    assert !request.headers.key?("x-amz-security-token")

    tk_request = sigv4_session(security_token: "token").build_request("GET", "http://domain.com")
    assert tk_request.headers.key?("x-amz-security-token")
    verify_header(tk_request.headers, "x-amz-security-token", "token")

    # already set
    token_request = sigv4_session(security_token: "token").build_request("GET", "http://domain.com",
                                                                         headers: { "x-amz-security-token" => "TOKEN" })
    verify_header(token_request.headers, "x-amz-security-token", "TOKEN")
  end

  def test_plugin_aws_sigv4_x_amz_content_sha256
    request = sigv4_session.build_request("GET", "http://domain.com", body: "abcd")
    assert request.headers["x-amz-content-sha256"] == Digest::SHA256.hexdigest("abcd")

    # already set
    hashed_request = sigv4_session.build_request("GET", "http://domain.com", headers: { "x-amz-content-sha256" => "HASH" })
    verify_header(hashed_request.headers, "x-amz-content-sha256", "HASH")
  end

  def test_plugin_aws_sigv4_x_amz_content_sha256_stringio
    request = sigv4_session.build_request("GET", "http://domain.com", body: StringIO.new("abcd"))
    assert request.headers["x-amz-content-sha256"] == Digest::SHA256.hexdigest("abcd")
  end

  def test_plugin_aws_sigv4_x_amz_content_sha256_array
    request = sigv4_session.build_request("GET", "http://domain.com", body: %w[a b c d])
    assert request.headers["x-amz-content-sha256"] == Digest::SHA256.hexdigest("abcd")
  end

  def test_plugin_aws_sigv4_x_amz_content_sha256_endless
    body = Enumerator.new do |y|
      loop do
        y << "a"
      end
    end
    ex = assert_raises(HTTPX::Error) do
      sigv4_session.build_request("GET", "http://domain.com", body: body)
    end
    assert ex.message.include?("hexdigest for endless enumerators is not supported")
  end

  def test_plugin_aws_sigv4_x_amz_content_sha256_file
    body = Tempfile.new("httpx")
    body.write("abcd")
    body.flush

    request = sigv4_session.build_request("GET", "http://domain.com", body: body)
    assert request.headers["x-amz-content-sha256"] == Digest::SHA256.hexdigest("abcd")
  ensure
    if body
      body.close
      body.unlink
    end
  end

  def test_plugin_aws_sigv4_x_amz_content_sha256_compressed_file
    body = Tempfile.new("httpx")
    body.write("abcd")
    body.flush

    request = sigv4_session.build_request("GET", "http://domain.com", body: body, headers: { "content-encoding" => "gzip" })

    assert request.headers["x-amz-content-sha256"] == Digest::SHA256.hexdigest(request.body.read)
  ensure
    if defined?(body)
      body.close
      body.unlink
    end
  end

  def test_plugin_aws_sigv4_authorization_unsigned_headers
    request = sigv4_session(
      service: "SERVICE",
      region: "REGION",
      unsigned_headers: %w[accept-encoding accept user-agent content-type content-length]
    ).build_request(
      "PUT",
      "http://domain.com",
      headers: {
        "Host" => "domain.com",
        "Foo" => "foo",
        "Bar" => "bar  bar",
        "Bar2" => '"bar  bar"',
        "Content-Length" => 9,
        "X-Amz-Date" => "20120101T112233Z",
      },
      body: StringIO.new("http-body")
    )

    expected_authorization = "" \
                             "AWS4-HMAC-SHA256 Credential=akid/20120101/REGION/SERVICE/aws4_request, " \
                             "SignedHeaders=bar;bar2;foo;host;x-amz-content-sha256;x-amz-date, " \
                             "Signature=4a7d3e06d1950eb64a3daa1becaa8ba030d9099858516cb2fa4533fab4e8937d"
    assert request.headers["authorization"] == expected_authorization,
           "expected: \"#{expected_authorization}\", got \"#{request.headers["authorization"]}\" inspected"
  end

  private

  def sigv4_session(**options)
    HTTPX.plugin(:aws_sigv4).aws_sigv4_authentication(service: "s3", region: "us-east-1", username: "akid",
                                                      password: "secret", **options)
  end
end
