Files
2026-07-13 12:32:21 +08:00

514 lines
14 KiB
Lua

local helpers = require "spec.helpers"
local cjson = require "cjson"
local mock_fn_one = [[
ngx.status = 503
ngx.exit(ngx.status)
]]
local mock_fn_two = [[
ngx.status = 404
ngx.say("Not Found")
ngx.exit(ngx.status)
]]
local mock_fn_three = [[
return kong.response.exit(406, { message = "Invalid" })
]]
local mock_fn_four = [[
ngx.status = 400
]]
local mock_fn_five = [[
ngx.exit(ngx.status)
]]
local mock_fn_six = [[
local count = 0
ngx.log(ngx.ERR, "mock_fn_six: initialization ran")
return function()
ngx.log(ngx.ERR, "mock_fn_six: function ran")
count = count + 1
ngx.status = 200
ngx.say(ngx.worker.pid() * 1000 + count)
ngx.exit(ngx.status)
end
]]
local mock_fn_seven = [[
ngx.req.read_body()
local count = tonumber(ngx.req.get_body_data())
count = count + 1
ngx.status = 200
ngx.say(count)
ngx.exit(ngx.status)
]]
-- same as 7, but with upvalue format
local mock_fn_eight = "return function() \n" .. mock_fn_seven .. "\n end"
local mock_fn_nine = [[
error("this should stop the request with a 500")
]]
local mock_fn_ten = [[
ngx.var.args = nil
]]
-- cache is accessible
local mock_fn_eleven = [[
local ok, err = kong.cache:get("foo", nil, function() return "val" end)
if err then
ngx.exit(500)
end
local v = kong.cache:get("foo")
ngx.status = 200
ngx.say(v)
ngx.exit(ngx.status)
]]
-- cache does not allow access to gateway information
local mock_fn_twelve = [[
ngx.status = 200
ngx.say(tostring(kong.cache.cluster_events))
ngx.exit(ngx.status)
]]
-- configuration is accessible
local mock_fn_thirteen = [[
ngx.status = 200
ngx.say(kong.configuration.plugins[1])
ngx.exit(ngx.status)
]]
-- configuration restricts access to properties
local mock_fn_fourteen = [[
ngx.status = 200
ngx.say(kong.configuration.pg_password)
ngx.exit(ngx.status)
]]
describe("Plugin: serverless-functions", function()
it("priority of plugins", function()
local pre = require "kong.plugins.pre-function.handler"
local post = require "kong.plugins.post-function.handler"
assert(pre.PRIORITY > post.PRIORITY, "expected the priority of PRE (" ..
tostring(pre.PRIORITY) .. ") to be higher than POST (" ..
tostring(post.PRIORITY)..")")
end)
end)
for _, plugin_name in ipairs({ "pre-function", "post-function" }) do
for _, method in ipairs({ "phase=functions" }) do
local function get_conf(functions)
return { access = functions }
end
describe("Plugin: " .. plugin_name .. string.format(" (by %s)", method) .. " access", function()
local client, admin_client
setup(function()
local bp, db = helpers.get_db_utils()
assert(db:truncate())
local service = bp.services:insert {
name = "service-1",
host = helpers.mock_upstream_host,
port = helpers.mock_upstream_port,
}
local route1 = bp.routes:insert {
service = { id = service.id },
hosts = { "one." .. plugin_name .. ".test" },
}
local route2 = bp.routes:insert {
service = { id = service.id },
hosts = { "two." .. plugin_name .. ".test" },
}
local route3 = bp.routes:insert {
service = { id = service.id },
hosts = { "three." .. plugin_name .. ".test" },
}
local route4 = bp.routes:insert {
service = { id = service.id },
hosts = { "four." .. plugin_name .. ".test" },
}
local route6 = bp.routes:insert {
service = { id = service.id },
hosts = { "six." .. plugin_name .. ".test" },
}
local route7 = bp.routes:insert {
service = { id = service.id },
hosts = { "seven." .. plugin_name .. ".test" },
}
local route8 = bp.routes:insert {
service = { id = service.id },
hosts = { "eight." .. plugin_name .. ".test" },
}
local route9 = bp.routes:insert {
service = { id = service.id },
hosts = { "nine." .. plugin_name .. ".test" },
}
local route10 = bp.routes:insert {
service = { id = service.id },
hosts = { "ten." .. plugin_name .. ".test" },
}
local route11 = bp.routes:insert {
service = { id = service.id },
hosts = { "eleven." .. plugin_name .. ".test" },
}
local route12 = bp.routes:insert {
service = { id = service.id },
hosts = { "twelve." .. plugin_name .. ".test" },
}
local route13 = bp.routes:insert {
service = { id = service.id },
hosts = { "thirteen." .. plugin_name .. ".test" },
}
local route14 = bp.routes:insert {
service = { id = service.id },
hosts = { "fourteen." .. plugin_name .. ".test" },
}
bp.plugins:insert {
name = plugin_name,
route = { id = route1.id },
config = get_conf { mock_fn_one },
}
bp.plugins:insert {
name = plugin_name,
route = { id = route2.id },
config = get_conf { mock_fn_two },
}
bp.plugins:insert {
name = plugin_name,
route = { id = route3.id },
config = get_conf { mock_fn_three },
}
bp.plugins:insert {
name = plugin_name,
route = { id = route4.id },
config = get_conf { mock_fn_four, mock_fn_five },
}
bp.plugins:insert {
name = plugin_name,
route = { id = route6.id },
config = get_conf { mock_fn_six },
}
bp.plugins:insert {
name = plugin_name,
route = { id = route7.id },
config = get_conf { mock_fn_seven },
}
bp.plugins:insert {
name = plugin_name,
route = { id = route8.id },
config = get_conf { mock_fn_eight },
}
bp.plugins:insert {
name = plugin_name,
route = { id = route9.id },
config = get_conf { mock_fn_nine },
}
bp.plugins:insert {
name = plugin_name,
route = { id = route10.id },
config = get_conf { mock_fn_ten },
}
bp.plugins:insert {
name = plugin_name,
route = { id = route11.id },
config = get_conf { mock_fn_eleven },
}
bp.plugins:insert {
name = plugin_name,
route = { id = route12.id },
config = get_conf { mock_fn_twelve },
}
bp.plugins:insert {
name = plugin_name,
route = { id = route13.id },
config = get_conf { mock_fn_thirteen },
}
bp.plugins:insert {
name = plugin_name,
route = { id = route14.id },
config = get_conf { mock_fn_fourteen },
}
assert(helpers.start_kong({
nginx_conf = "spec/fixtures/custom_nginx.template",
}))
end)
teardown(function()
helpers.stop_kong()
end)
before_each(function()
client = helpers.proxy_client()
admin_client = helpers.admin_client()
end)
after_each(function()
if client and admin_client then
client:close()
admin_client:close()
end
end)
describe("request termination", function()
it("using ngx.exit()", function()
local res = assert(client:send {
method = "GET",
path = "/status/200",
headers = {
["Host"] = "one." .. plugin_name .. ".test"
}
})
assert.res_status(503, res)
end)
it("with upvalues", function()
local results = {}
for i = 1, 50 do
local res = assert(client:send {
method = "GET",
path = "/status/200",
headers = {
["Host"] = "six." .. plugin_name .. ".test"
}
})
local body = assert.res_status(200, res)
assert.is_string(body)
--print(i, ": ", body)
assert.is_nil(results[body])
results[body] = true
end
end)
it("using ngx.status and exit", function()
local res = assert(client:send {
method = "GET",
path = "/status/200",
headers = {
["Host"] = "two." .. plugin_name .. ".test"
}
})
local body = assert.res_status(404, res)
assert.same("Not Found", body)
end)
it("import response utility and send message", function()
local res = assert(client:send {
method = "GET",
path = "/status/200",
headers = {
["Host"] = "three." .. plugin_name .. ".test"
}
})
local body = assert.res_status(406, res)
local json = cjson.decode(body)
assert.not_nil(json)
assert.matches("Invalid", json.message)
end)
it("cascading functions for a 400 and exit", function()
local res = assert(client:send {
method = "GET",
path = "/status/200",
headers = {
["Host"] = "four." .. plugin_name .. ".test"
}
})
local body = assert.res_status(400, res)
assert.matches("Bad request", body)
end)
it("runtime error aborts with a 500", function()
local res = assert(client:send {
method = "GET",
path = "/status/200",
headers = {
["Host"] = "nine." .. plugin_name .. ".test"
}
})
local body = assert.res_status(500, res)
local json = cjson.decode(body)
assert.not_nil(json)
assert.matches("An unexpected error occurred", json.message)
end)
end)
describe("invocation count", function()
it("once on initialization", function()
local count = 0
local res = assert(client:send {
method = "POST",
path = "/status/200",
headers = {
["Host"] = "seven." .. plugin_name .. ".test",
["Content-Length"] = #tostring(count),
},
body = count,
})
assert.equal(1, tonumber(res:read_body()))
end)
it("on repeated calls", function()
local count = 0
for i = 1, 10 do
local res = assert(client:send {
method = "POST",
path = "/status/200",
headers = {
["Host"] = "seven." .. plugin_name .. ".test",
["Content-Length"] = #tostring(count),
},
body = count,
})
count = tonumber(res:read_body())
end
assert.equal(10, count)
end)
it("once on initialization, with upvalues", function()
local count = 0
local res = assert(client:send {
method = "POST",
path = "/status/200",
headers = {
["Host"] = "eight." .. plugin_name .. ".test",
["Content-Length"] = #tostring(count),
},
body = count,
})
assert.equal(1, tonumber(res:read_body()))
end)
it("on repeated calls, with upvalues", function()
local count = 0
for i = 1, 10 do
local res = assert(client:send {
method = "POST",
path = "/status/200",
headers = {
["Host"] = "eight." .. plugin_name .. ".test",
["Content-Length"] = #tostring(count),
},
body = count,
})
count = tonumber(res:read_body())
end
assert.equal(10, count)
end)
end)
describe("sandbox access", function()
it("can access cache", function()
local res = assert(client:send {
method = "GET",
path = "/status/200",
headers = {
["Host"] = "eleven." .. plugin_name .. ".test",
},
})
local body = assert.res_status(200, res)
assert.is_not_nil(body)
assert.equal("val", body)
end)
it("cannot access gateway information through the cache", function()
local res = assert(client:send {
method = "GET",
path = "/status/200",
headers = {
["Host"] = "twelve." .. plugin_name .. ".test",
},
})
local body = assert.res_status(200, res)
assert.is_not_nil(body)
assert.equal("nil", body)
end)
it("can access kong.configuration fields", function()
local res = assert(client:send {
method = "GET",
path = "/status/200",
headers = {
["Host"] = "thirteen." .. plugin_name .. ".test",
},
})
local body = assert.res_status(200, res)
assert.is_not_nil(body)
assert.equal("bundled", body)
end)
it("redacts sensitive configuration fields", function()
local res = assert(client:send {
method = "GET",
path = "/status/200",
headers = {
["Host"] = "fourteen." .. plugin_name .. ".test",
},
})
local body = assert.res_status(200, res)
assert.is_not_nil(body)
assert.match("%*+", body)
end)
end)
describe("issues", function()
it("does not crash even when query is cleared, #9246", function()
local res = client:get("/status/200?a=b", {
headers = {
["Host"] = "ten." .. plugin_name .. ".test"
}
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.same({}, json.uri_args)
end)
end)
end)
end
end