require "abstract_unit"

class HeaderTest < ActiveSupport::TestCase
  def make_headers(hash)
    ActionDispatch::Http::Headers.new ActionDispatch::Request.new hash
  end

  setup do
    @headers = make_headers(
      "CONTENT_TYPE" => "text/plain",
      "HTTP_REFERER" => "/some/page"
    )
  end

  test "#new does not normalize the data" do
    headers = make_headers(
      "Content-Type" => "application/json",
      "HTTP_REFERER" => "/some/page",
      "Host" => "http://test.com")

    assert_equal({"Content-Type" => "application/json",
                  "HTTP_REFERER" => "/some/page",
                  "Host" => "http://test.com"}, headers.env)
  end

  test "#env returns the headers as env variables" do
    assert_equal({"CONTENT_TYPE" => "text/plain",
                  "HTTP_REFERER" => "/some/page"}, @headers.env)
  end

  test "#each iterates through the env variables" do
    headers = []
    @headers.each { |pair| headers << pair }
    assert_equal [["CONTENT_TYPE", "text/plain"],
                  ["HTTP_REFERER", "/some/page"]], headers
  end

  test "set new headers" do
    @headers["Host"] = "127.0.0.1"

    assert_equal "127.0.0.1", @headers["Host"]
    assert_equal "127.0.0.1", @headers["HTTP_HOST"]
  end

  test "add to multivalued headers" do
    # Sets header when not present
    @headers.add 'Foo', '1'
    assert_equal '1', @headers['Foo']

    # Ignores nil values
    @headers.add 'Foo', nil
    assert_equal '1', @headers['Foo']

    # Converts value to string
    @headers.add 'Foo', 1
    assert_equal '1,1', @headers['Foo']

    # Case-insensitive
    @headers.add 'fOo', 2
    assert_equal '1,1,2', @headers['foO']
  end

  test "headers can contain numbers" do
    @headers["Content-MD5"] = "Q2hlY2sgSW50ZWdyaXR5IQ=="

    assert_equal "Q2hlY2sgSW50ZWdyaXR5IQ==", @headers["Content-MD5"]
    assert_equal "Q2hlY2sgSW50ZWdyaXR5IQ==", @headers["HTTP_CONTENT_MD5"]
  end

  test "set new env variables" do
    @headers["HTTP_HOST"] = "127.0.0.1"

    assert_equal "127.0.0.1", @headers["Host"]
    assert_equal "127.0.0.1", @headers["HTTP_HOST"]
  end

  test "key?" do
    assert @headers.key?("CONTENT_TYPE")
    assert @headers.include?("CONTENT_TYPE")
    assert @headers.key?("Content-Type")
    assert @headers.include?("Content-Type")
  end

  test "fetch with block" do
    assert_equal "omg", @headers.fetch("notthere") { "omg" }
  end

  test "accessing http header" do
    assert_equal "/some/page", @headers["Referer"]
    assert_equal "/some/page", @headers["referer"]
    assert_equal "/some/page", @headers["HTTP_REFERER"]
  end

  test "accessing special header" do
    assert_equal "text/plain", @headers["Content-Type"]
    assert_equal "text/plain", @headers["content-type"]
    assert_equal "text/plain", @headers["CONTENT_TYPE"]
  end

  test "fetch" do
    assert_equal "text/plain", @headers.fetch("content-type", nil)
    assert_equal "not found", @headers.fetch("not-found", "not found")
  end

  test "#merge! headers with mutation" do
    @headers.merge!("Host" => "http://example.test",
                    "Content-Type" => "text/html")
    assert_equal({"HTTP_HOST" => "http://example.test",
                  "CONTENT_TYPE" => "text/html",
                  "HTTP_REFERER" => "/some/page"}, @headers.env)
  end

  test "#merge! env with mutation" do
    @headers.merge!("HTTP_HOST" => "http://first.com",
                    "CONTENT_TYPE" => "text/html")
    assert_equal({"HTTP_HOST" => "http://first.com",
                  "CONTENT_TYPE" => "text/html",
                  "HTTP_REFERER" => "/some/page"}, @headers.env)
  end

  test "merge without mutation" do
    combined = @headers.merge("HTTP_HOST" => "http://example.com",
                              "CONTENT_TYPE" => "text/html")
    assert_equal({"HTTP_HOST" => "http://example.com",
                  "CONTENT_TYPE" => "text/html",
                  "HTTP_REFERER" => "/some/page"}, combined.env)

    assert_equal({"CONTENT_TYPE" => "text/plain",
                  "HTTP_REFERER" => "/some/page"}, @headers.env)
  end

  test "env variables with . are not modified" do
    headers = make_headers({})
    headers.merge! "rack.input" => "",
     "rack.request.cookie_hash" => "",
     "action_dispatch.logger" => ""

    assert_equal(["action_dispatch.logger",
                  "rack.input",
                  "rack.request.cookie_hash"], headers.env.keys.sort)
  end

  test "symbols are treated as strings" do
    headers = make_headers({})
    headers.merge!(:SERVER_NAME => "example.com",
                   "HTTP_REFERER" => "/",
                   :Host => "test.com")
    assert_equal "example.com", headers["SERVER_NAME"]
    assert_equal "/", headers[:HTTP_REFERER]
    assert_equal "test.com", headers["HTTP_HOST"]
  end

  test "headers directly modifies the passed environment" do
    env = {"HTTP_REFERER" => "/"}
    headers = make_headers(env)
    headers['Referer'] = "http://example.com/"
    headers.merge! "CONTENT_TYPE" => "text/plain"
    assert_equal({"HTTP_REFERER"=>"http://example.com/",
                  "CONTENT_TYPE"=>"text/plain"}, env)
  end
end
