local helpers = require "spec.helpers"

local LOG_LEVELS = {
  "debug",
  "info",
  -- any other log level behaves the same as "info"
}


local function trigger_plugin_new_table()
  local client = helpers.proxy_client()
  local res = client:get("/", {
    query = {
      new_tab = true,
    }
  })
  assert.response(res).has.status(200)
  assert.logfile().has.no.line("[error]", true)
  client:close()
end

local function trigger_plugin_clear_table()
  local client = helpers.proxy_client()
  local res = client:get("/", {
    query = {
      clear = true,
    }
  })
  assert.response(res).has.status(200)
  assert.logfile().has.no.line("[error]", true)
  client:close()
end

for _, log_level in ipairs(LOG_LEVELS) do
  local concurrency_checks = log_level == "debug"

  for _, strategy in helpers.each_strategy() do
    describe("request aware table tests [#" .. strategy .. "] concurrency checks: " .. tostring(concurrency_checks), function()
      local client
      lazy_setup(function()
        local bp = helpers.get_db_utils(strategy, {
          "plugins",
          "routes",
          "services",
        }, {
          "request-aware-table"
        })

        local service = assert(bp.services:insert({
          url = helpers.mock_upstream_url
        }))

        local route = bp.routes:insert({
          service = service,
          paths = { "/" }
        })

        bp.plugins:insert({
          name = "request-aware-table",
          route = { id = route.id },
        })

        helpers.start_kong({
          database = strategy,
          plugins = "bundled, request-aware-table",
          nginx_conf = "spec/fixtures/custom_nginx.template",
          log_level = log_level,
        })
      end)

      lazy_teardown(function()
        helpers.stop_kong()
      end)

      before_each(function()
        helpers.clean_logfile()
        trigger_plugin_new_table()
        client = helpers.proxy_client()
      end)

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

      it("allows access when there are no race conditions", function()
        local res = client:get("/")
        assert.response(res).has.status(200)
        assert.logfile().has.no.line("[error]", true)
      end)

      it("denies access when there are race conditions and checks are enabled (else allows)", function()
        -- access from request 1 (don't clear)
        local r = client:get("/")
        assert.response(r).has.status(200)

        -- access from request 2
        r = client:get("/")
        if concurrency_checks then
          assert.logfile().has.line("concurrent access from different request to shared table detected", true)
        else
          assert.response(r).has.status(200)
        end
      end)

      it("allows access when table is cleared between requests", function()
        -- access from request 1 (clear)
        local r = client:get("/")
        assert.response(r).has.status(200)
        trigger_plugin_clear_table()

        -- access from request 2 (clear)
        r = client:get("/")
        assert.response(r).has.status(200)
        trigger_plugin_clear_table()

        -- access from request 3
        r = client:get("/")
        assert.response(r).has.status(200)
        assert.logfile().has.no.line("[error]", true)
      end)
    end)
  end
end
