local meta = require "kong.meta"
local helpers = require "spec.helpers"
local constants = require "kong.constants"
local cjson = require "cjson"
local uuid   = require("kong.tools.uuid").uuid


local default_server_header = meta._SERVER_TOKENS
local default_via_value =  "1.1 " .. default_server_header

for _, strategy in helpers.each_strategy() do
describe("headers [#" .. strategy .. "]", function()

  local function stop()
    helpers.stop_kong()
  end

  describe("Server/Via", function()
    local proxy_client
    local bp

    local function start(config)
      return function()
        bp.routes:insert {
          hosts = { "headers-inspect.test" },
        }

        local route = bp.routes:insert {
          hosts = { "short-circuit.test" },
        }

        bp.plugins:insert {
          route = route,
          name = "request-termination",
          config = {
            status_code = 200,
            message = "Terminated"
          },
        }

        bp.plugins:insert {
          route = route,
          name = "response-transformer",
          config = {
            add = {
              headers = {
                "Via:" .. default_server_header,
                "Server:" .. "Demo",
              },
            },
          },
        }

        config = config or {}
        config.database   = strategy
        config.nginx_conf = "spec/fixtures/custom_nginx.template"

        assert(helpers.start_kong(config))
      end
    end

    lazy_setup(function()
      bp = helpers.get_db_utils(strategy, {
        "routes",
        "services",
      }, {
        "error-generator",
      })
    end)

    before_each(function()
      proxy_client = helpers.proxy_client()
    end)

    after_each(function()
      if proxy_client then
        proxy_client:close()
      end
    end)

    describe("(with default configuration values)", function()

      lazy_setup(start())

      lazy_teardown(stop)

      it("should return Kong 'Via' header but not change the 'Server' header when request was proxied", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "headers-inspect.test",
          }
        })

        assert.res_status(200, res)
        assert.not_equal(default_server_header, res.headers["server"])
        assert.equal(default_via_value, res.headers["via"])
      end)

      it("should return Kong 'Server' header but not the Kong 'Via' header when no API matched (no proxy)", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "404.test",
          }
        })

        assert.res_status(404, res)
        assert.equal(default_server_header, res.headers["server"])
        assert.is_nil(res.headers["via"])
      end)

      it("should not return Kong 'Server' header when short-circuited with a matching Kong 'Via' #new", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "short-circuit.test",
          }
        })

        assert.res_status(200, res)
        assert.equal(default_server_header, res.headers["Via"])
        assert.equal("Demo", res.headers["Server"])
      end)

    end)

    describe("(with headers = Via)", function()

      lazy_setup(start {
        headers = "Via",
      })

      lazy_teardown(stop)

      it("should return Kong 'Via' header but not touch 'Server' header when request was proxied", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "headers-inspect.test",
          }
        })

        assert.res_status(200, res)
        assert.equal(default_via_value, res.headers["via"])
        assert.not_equal(default_via_value, res.headers["server"])
      end)

      it("should not return Kong 'Via' header or Kong 'Via' header when no API matched (no proxy)", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "404.test",
          }
        })

        assert.res_status(404, res)
        assert.is_nil(res.headers["via"])
        assert.is_nil(res.headers["server"])
      end)

    end)

    describe("(with headers = Server)", function()

      lazy_setup(start {
        headers = "Server",
      })

      lazy_teardown(stop)

      it("should not return Kong 'Via' header but not change the 'Server' header when request was proxied", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "headers-inspect.test",
          }
        })

        assert.res_status(200, res)
        assert.not_equal(default_server_header, res.headers["server"])
        assert.is_nil(res.headers["via"])
      end)

      it("should return Kong 'Server' header but not the Kong 'Via' header when no API matched (no proxy)", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "404.test",
          }
        })

        assert.res_status(404, res)
        assert.equal(default_server_header, res.headers["server"])
        assert.is_nil(res.headers["via"])
      end)

    end)

    describe("(with headers = server_tokens)", function()

      lazy_setup(start {
        headers = "server_tokens",
      })

      lazy_teardown(stop)

      it("should return Kong 'Via' header but not change the 'Server' header when request was proxied", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "headers-inspect.test",
          }
        })

        assert.res_status(200, res)
        assert.not_equal(default_server_header, res.headers["server"])
        assert.equal(default_via_value, res.headers["via"])
      end)

      it("should return Kong 'Server' header but not the Kong 'Via' header when no API matched (no proxy)", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "404.test",
          }
        })

        assert.res_status(404, res)
        assert.equal(default_server_header, res.headers["server"])
        assert.is_nil(res.headers["via"])
      end)

    end)

    describe("(with no server_tokens in headers)", function()

      lazy_setup(start {
        headers = "off",
      })

      lazy_teardown(stop)

      it("should not return Kong 'Via' header but it should forward the 'Server' header when request was proxied", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "headers-inspect.test",
          }
        })

        assert.res_status(200, res)
        assert.response(res).has.header "server"
        assert.response(res).has_not.header "via"
        assert.not_equal(default_server_header, res.headers["server"])
      end)

      it("should not return Kong 'Server' or 'Via' headers when no API matched (no proxy)", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "404.test",
          }
        })

        assert.res_status(404, res)
        assert.is_nil(res.headers["server"])
        assert.is_nil(res.headers["via"])
      end)

    end)
  end)

  describe("X-Kong-Proxy-Latency/X-Kong-Upstream-Latency", function()
    local proxy_client
    local bp
    local db

    local function start(config)
      return function()
        bp.routes:insert {
          hosts = { "headers-inspect.test" },
        }

        local service = bp.services:insert({
          protocol = helpers.mock_upstream_protocol,
          host     = helpers.mock_upstream_host,
          port     = 1, -- wrong port
        })

        bp.routes:insert({
          service = service,
          hosts = { "502.test" }
        })

        bp.routes:insert {
          hosts = { "error-rewrite.test" },
        }

        local access_error_route = bp.routes:insert {
          hosts = { "error-access.test" },
        }

        bp.plugins:insert {
          name = "error-generator",
          route = { id = access_error_route.id },
          config = {
            access = true,
          },
        }

        local header_filter_error_route = bp.routes:insert {
          hosts = { "error-header-filter.test" },
        }

        bp.plugins:insert {
          name = "error-generator",
          route = { id = header_filter_error_route.id },
          config = {
            header_filter = true,
          },
        }

        local request_termination_route = bp.routes:insert({
          service = service,
          hosts = { "request-termination.test" }
        })

        bp.plugins:insert {
          name = "request-termination",
          route = { id = request_termination_route.id },
        }

        config = config or {}
        config.database   = strategy
        config.nginx_conf = "spec/fixtures/custom_nginx.template"

        assert(helpers.start_kong(config))
      end
    end

    lazy_setup(function()
      bp, db = helpers.get_db_utils(strategy, {
        "routes",
        "services",
      }, {
        "error-generator",
      })
    end)

    before_each(function()
      proxy_client = helpers.proxy_client()
    end)

    after_each(function()
      if proxy_client then
        proxy_client:close()
      end
    end)

    describe("(with default configuration values)", function()

      lazy_setup(start())
      lazy_teardown(stop)

      it("should be returned when request was proxied", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "headers-inspect.test",
          }
        })

        assert.res_status(200, res)
        assert.is_not_nil(res.headers[constants.HEADERS.UPSTREAM_LATENCY])
        assert.is_not_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

      it("should not be returned when no API matched (no proxy)", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "404.test",
          }
        })

        assert.res_status(404, res)
        assert.is_nil(res.headers[constants.HEADERS.UPSTREAM_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_not_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

      it("should not be returned when request is short-circuited", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "request-termination.test",
          }
        })

        assert.res_status(503, res)
        assert.is_nil(res.headers[constants.HEADERS.UPSTREAM_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_not_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

      it("should be returned when response status code is included in error_page directive (error_page not executing)", function()
        for _, code in ipairs({ 400, 404, 408, 411, 412, 413, 414, 417, 494, 500, 502, 503, 504 }) do
          local res = assert(proxy_client:send {
            method  = "GET",
            path    = "/status/" .. code,
            headers = {
              host  = "headers-inspect.test",
            }
          })

          assert.res_status(code, res)
          assert.is_not_nil(res.headers[constants.HEADERS.UPSTREAM_LATENCY])
          assert.is_not_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
          assert.is_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
        end
      end)

      it("should be returned with 502 errors (error_page executing)", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "502.test",
          }
        })

        assert.res_status(502, res)
        assert.is_not_nil(res.headers[constants.HEADERS.UPSTREAM_LATENCY])
        assert.is_not_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

      -- Too painfull to get this to work with dbless (need to start new Kong process etc.)
      if strategy ~= "off" then
        it("should not be returned when plugin errors on rewrite phase", function()
          local admin_client = helpers.admin_client()
          local uuid = uuid()
          local res = assert(admin_client:send {
            method = "PUT",
            path = "/plugins/" .. uuid,
            body = {
              name = "error-generator",
              config = {
                rewrite = true,
              }
            },
            headers = {["Content-Type"] = "application/json"}
          })
          assert.res_status(200, res)
          admin_client:close()

          helpers.wait_until(function()
            res = assert(proxy_client:send {
              method  = "GET",
              path    = "/get",
              headers = {
                host  = "error-rewrite.test",
              }
            })

            return pcall(function()
              assert.res_status(500, res)
            end)
          end, 10)

          db.plugins:delete({ id = uuid })

          assert.res_status(500, res)
          assert.is_nil(res.headers[constants.HEADERS.UPSTREAM_LATENCY])
          assert.is_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
          assert.is_not_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
        end)
      end

      it("should not be returned when plugin errors on access phase", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "error-access.test",
          }
        })

        assert.res_status(500, res)
        assert.is_nil(res.headers[constants.HEADERS.UPSTREAM_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_not_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)


      -- TODO: currently we don't handle errors from plugins on header_filter or body_filter.
      pending("should be returned even when plugin errors on header filter phase", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "error-header-filter.test",
          }
        })

        assert.res_status(500, res)
        assert.is_not_nil(res.headers[constants.HEADERS.UPSTREAM_LATENCY])
        assert.is_not_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

    end)

    describe("(with headers = latency_tokens)", function()

      lazy_setup(start {
        headers = "latency_tokens",
      })

      lazy_teardown(stop)

      it("should be returned when request was proxied", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "headers-inspect.test"
          }
        })

        assert.res_status(200, res)
        assert.is_not_nil(res.headers[constants.HEADERS.UPSTREAM_LATENCY])
        assert.is_not_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

      it("should not be returned when no API matched (no proxy)", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "404.test",
          }
        })

        assert.res_status(404, res)
        assert.is_nil(res.headers[constants.HEADERS.UPSTREAM_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_not_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

    end)

    describe("(with headers = X-Kong-Upstream-Latency)", function()

      lazy_setup(start {
        headers = "X-Kong-Upstream-Latency",
      })

      lazy_teardown(stop)

      it("should return 'X-Kong-Upstream-Latency' header but not 'X-Kong-Proxy-Latency' when request was proxied", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "headers-inspect.test"
          }
        })

        assert.res_status(200, res)
        assert.is_not_nil(res.headers[constants.HEADERS.UPSTREAM_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

      it("should not return any latency header when no API matched (no proxy)", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "404.test",
          }
        })

        assert.res_status(404, res)
        assert.is_nil(res.headers[constants.HEADERS.UPSTREAM_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

    end)

    describe("(with headers = X-Kong-Proxy-Latency)", function()

      lazy_setup(start {
        headers = "X-Kong-Proxy-Latency",
      })

      lazy_teardown(stop)

      it("should return 'X-Kong-Proxy-Latency' header but not 'X-Kong-Upstream-Latency' when request was proxied", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "headers-inspect.test"
          }
        })

        assert.res_status(200, res)
        assert.is_nil(res.headers[constants.HEADERS.UPSTREAM_LATENCY])
        assert.is_not_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

      it("should not return any latency header when no API matched (no proxy)", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "404.test",
          }
        })

        assert.res_status(404, res)
        assert.is_nil(res.headers[constants.HEADERS.UPSTREAM_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

    end)

    describe("(with headers = X-Kong-Response-Latency)", function()

      lazy_setup(start {
        headers = "X-Kong-Response-Latency",
      })

      lazy_teardown(stop)

      it("should not return 'X-Kong-Proxy-Latency', 'X-Kong-Upstream-Latency' or 'X-Kong-Response-Latency' headers when request was proxied", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "headers-inspect.test"
          }
        })

        assert.res_status(200, res)
        assert.is_nil(res.headers[constants.HEADERS.UPSTREAM_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

      it("should return 'X-Kong-Response-Latency' when no API matched (no proxy)", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "404.test",
          }
        })

        assert.res_status(404, res)
        assert.is_nil(res.headers[constants.HEADERS.UPSTREAM_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_not_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

    end)

    describe("(with no latency_tokens in headers)", function()

      lazy_setup(start {
        headers = "off",
      })

      lazy_teardown(stop)

      it("should not be returned when request was proxied", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "headers-inspect.test",
          }
        })

        assert.res_status(200, res)
        assert.is_nil(res.headers[constants.HEADERS.UPSTREAM_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

      it("should not be returned when no API matched (no proxy)", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "404.test",
          }
        })

        assert.res_status(404, res)
        assert.is_nil(res.headers[constants.HEADERS.UPSTREAM_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

    end)

    describe("(with headers='server_tokens, X-Kong-Proxy-Latency')", function()

      lazy_setup(start{
        headers = "server_tokens, X-Kong-Proxy-Latency",
      })

      lazy_teardown(stop)

      it("should return Kong 'Via' and 'X-Kong-Proxy-Latency' header but not change the 'Server' header when request was proxied", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "headers-inspect.test",
          }
        })

        assert.res_status(200, res)
        assert.not_equal(default_server_header, res.headers["server"])
        assert.equal(default_via_value, res.headers["via"])
        assert.is_not_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

      it("should return Kong 'Server' header but not the Kong 'Via' or 'X-Kong-Proxy-Latency' header when no API matched (no proxy)", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "404.test",
          }
        })

        assert.res_status(404, res)
        assert.equal(default_server_header, res.headers["server"])
        assert.is_nil(res.headers["via"])
        assert.is_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

      it("can be specified via configuration file", function()
        -- A regression test added with https://github.com/Kong/kong/pull/3419
        -- to ensure that the `headers` configuration value can be specified
        -- via the configuration file (vs. environment variables as the rest
        -- of this test suite uses).
        -- This regression occurred because of the dumping of config values into
        -- .kong_env (and the lack of serialization for the `headers` table).
        assert(helpers.kong_exec("restart -c spec/fixtures/headers.conf"))

        local admin_client = helpers.admin_client()
        local res = assert(admin_client:send {
          method = "GET",
          path   = "/",
        })
        local body = assert.res_status(200, res)
        local json = cjson.decode(body)
        assert.equal("server_tokens", json.configuration.headers[1])
        assert.equal("X-Kong-Proxy-Latency", json.configuration.headers[2])
      end)
    end)

    describe("(with headers='server_tokens, off, X-Kong-Proxy-Latency')", function()

      lazy_setup(start{
        headers = "server_tokens, off, X-Kong-Proxy-Latency",
      })

      lazy_teardown(stop)

      it("should return Kong 'Via' and 'X-Kong-Proxy-Latency' header as 'off' will not take effect", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "headers-inspect.test",
          }
        })

        assert.res_status(200, res)
        assert.not_equal(default_server_header, res.headers["server"])
        assert.equal(default_via_value, res.headers["via"])
        assert.is_not_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

      it("should return Kong 'Server' header as 'off' will not take effect", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "404.test",
          }
        })

        assert.res_status(404, res)
        assert.equal(default_server_header, res.headers["server"])
        assert.is_nil(res.headers["via"])
        assert.is_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

    end)
  end)

  describe("case insensitiveness", function()
    local proxy_client
    local bp

    local function start(config)
      return function()
        bp.routes:insert {
          hosts = { "headers-inspect.test" },
        }

        config = config or {}
        config.database = strategy
        config.nginx_conf = "spec/fixtures/custom_nginx.template"

        assert(helpers.start_kong(config))
      end
    end

    lazy_setup(function()
      bp = helpers.get_db_utils(strategy, {
        "routes",
        "services",
      })
    end)

    before_each(function()
      proxy_client = helpers.proxy_client()
    end)

    after_each(function()
      if proxy_client then
        proxy_client:close()
      end
    end)

    describe("are case insensitive", function()

      lazy_setup(start{
        headers = "serVer_TokEns, x-kOng-pRoXy-lAtency"
      })

      lazy_teardown(stop)

      it("should return Kong 'Via' and 'X-Kong-Proxy-Latency' header", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "headers-inspect.test",
          }
        })

        assert.res_status(200, res)
        assert.not_equal(default_server_header, res.headers["server"])
        assert.equal(default_via_value, res.headers["via"])
        assert.is_not_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)

      it("should return Kong 'Server' header", function()
        local res = assert(proxy_client:send {
          method  = "GET",
          path    = "/get",
          headers = {
            host  = "404.test",
          }
        })

        assert.res_status(404, res)
        assert.equal(default_server_header, res.headers["server"])
        assert.is_nil(res.headers["via"])
        assert.is_nil(res.headers[constants.HEADERS.PROXY_LATENCY])
        assert.is_nil(res.headers[constants.HEADERS.RESPONSE_LATENCY])
      end)
    end)
  end)

  describe("Admin API", function()
    local admin_client

    local function start(config)
      return function()
        config = config or {}
        config.database   = strategy
        config.nginx_conf = "spec/fixtures/custom_nginx.template"

        assert(helpers.start_kong(config))
      end
    end

    before_each(function()
      admin_client = helpers.admin_client()
    end)

    after_each(function()
      if admin_client then
        admin_client:close()
      end
    end)

    describe("Server", function()
      describe("(with default configuration values)", function()
        lazy_setup(start())
        lazy_teardown(stop)

        it("should be returned when admin api is requested", function()
          local res = assert(admin_client:get("/"))
          assert.res_status(200, res)
          assert.is_not_nil(res.headers[constants.HEADERS.SERVER])
        end)
      end)

      describe("(with headers = server_tokens)", function()
        lazy_setup(start {
          headers = "server_tokens",
        })
        lazy_teardown(stop)

        it("should be returned when admin api is requested", function()
          local res = assert(admin_client:get("/"))
          assert.res_status(200, res)
          assert.is_not_nil(res.headers[constants.HEADERS.SERVER])
        end)
      end)

      describe("(with headers = Server)", function()
        lazy_setup(start {
          headers = "Server",
        })
        lazy_teardown(stop)

        it("should be returned when admin api is requested", function()
          local res = assert(admin_client:get("/"))
          assert.res_status(200, res)
          assert.is_not_nil(res.headers[constants.HEADERS.SERVER])
        end)
      end)

      describe("(with headers = off)", function()
        lazy_setup(start {
          headers = "off",
        })
        lazy_teardown(stop)

        it("should not be returned when admin api is requested", function()
          local res = assert(admin_client:get("/"))
          assert.res_status(200, res)
          assert.is_nil(res.headers[constants.HEADERS.SERVER])
        end)
      end)
    end)

    describe("X-Kong-Admin-Latency", function()
      describe("(with default configuration values)", function()
        lazy_setup(start())
        lazy_teardown(stop)

        it("should be returned when admin api is requested", function()
          local res = assert(admin_client:get("/"))
          assert.res_status(200, res)
          assert.is_not_nil(res.headers[constants.HEADERS.ADMIN_LATENCY])
        end)
      end)

      describe("(with headers = latency_tokens)", function()
        lazy_setup(start {
          headers = "latency_tokens",
        })
        lazy_teardown(stop)

        it("should be returned when admin api is requested", function()
          local res = assert(admin_client:get("/"))
          assert.res_status(200, res)
          assert.is_not_nil(res.headers[constants.HEADERS.ADMIN_LATENCY])
        end)
      end)

      describe("(with headers = X-Kong-Admin-Latency)", function()
        lazy_setup(start {
          headers = "latency_tokens",
        })
        lazy_teardown(stop)

        it("should be returned when admin api is requested", function()
          local res = assert(admin_client:get("/"))
          assert.res_status(200, res)
          assert.is_not_nil(res.headers[constants.HEADERS.ADMIN_LATENCY])
        end)
      end)

      describe("(with headers = off)", function()
        lazy_setup(start {
          headers = "off",
        })
        lazy_teardown(stop)

        it("should not be returned when admin api is requested", function()
          local res = assert(admin_client:get("/"))
          assert.res_status(200, res)
          assert.is_nil(res.headers[constants.HEADERS.ADMIN_LATENCY])
        end)
      end)
    end)
  end)
end)
end
