1785 lines
61 KiB
Lua
1785 lines
61 KiB
Lua
local kong_table = require "kong.tools.table"
|
|
local pl_path = require "pl.path"
|
|
local tools_ip = require "kong.tools.ip"
|
|
local tools_http = require "kong.tools.http"
|
|
|
|
describe("Utils", function()
|
|
|
|
describe("get_system_infos()", function()
|
|
local utils = require "kong.tools.system"
|
|
it("retrieves various host infos", function()
|
|
local infos = utils.get_system_infos()
|
|
assert.is_number(infos.cores)
|
|
assert.is_string(infos.uname)
|
|
assert.not_matches("\n$", infos.uname)
|
|
end)
|
|
it("caches the result", function()
|
|
assert.equal(
|
|
utils.get_system_infos(),
|
|
utils.get_system_infos()
|
|
)
|
|
end)
|
|
end)
|
|
|
|
describe("get_system_trusted_certs_filepath()", function()
|
|
local utils = require "kong.tools.system"
|
|
local old_exists = pl_path.exists
|
|
after_each(function()
|
|
pl_path.exists = old_exists
|
|
end)
|
|
local tests = {
|
|
Debian = "/etc/ssl/certs/ca-certificates.crt",
|
|
Fedora = "/etc/pki/tls/certs/ca-bundle.crt",
|
|
OpenSuse = "/etc/ssl/ca-bundle.pem",
|
|
OpenElec = "/etc/pki/tls/cacert.pem",
|
|
CentOS = "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem",
|
|
Alpine = "/etc/ssl/cert.pem",
|
|
}
|
|
|
|
for distro, test_path in pairs(tests) do
|
|
it("retrieves the default filepath in " .. distro, function()
|
|
pl_path.exists = function(path)
|
|
return path == test_path
|
|
end
|
|
assert.same(test_path, utils.get_system_trusted_certs_filepath())
|
|
end)
|
|
end
|
|
|
|
it("errors if file is somewhere else", function()
|
|
pl_path.exists = function(path)
|
|
return path == "/some/unknown/location.crt"
|
|
end
|
|
|
|
local ok, err = utils.get_system_trusted_certs_filepath()
|
|
assert.is_nil(ok)
|
|
assert.matches("Could not find trusted certs file", err)
|
|
end)
|
|
end)
|
|
|
|
describe("is_valid_uuid()", function()
|
|
local utils = require "kong.tools.uuid"
|
|
it("validates UUIDs from jit-uuid", function()
|
|
assert.True (utils.is_valid_uuid("cbb297c0-a956-486d-ad1d-f9b42df9465a"))
|
|
assert.False(utils.is_valid_uuid("cbb297c0-a956486d-ad1d-f9b42df9465a"))
|
|
end)
|
|
pending("invalidates UUIDs with invalid variants", function()
|
|
-- this is disabled because existing uuids in the database fail the check upon migrations
|
|
-- see https://github.com/thibaultcha/lua-resty-jit-uuid/issues/8
|
|
assert.False(utils.is_valid_uuid("cbb297c0-a956-486d-dd1d-f9b42df9465a")) -- invalid variant
|
|
end)
|
|
it("validates UUIDs with invalid variants for backwards-compatibility reasons", function()
|
|
-- See pending test just above ^^
|
|
-- see https://github.com/thibaultcha/lua-resty-jit-uuid/issues/8
|
|
assert.True(utils.is_valid_uuid("cbb297c0-a956-486d-dd1d-f9b42df9465a"))
|
|
end)
|
|
it("considers the null UUID a valid one", function()
|
|
-- we use the null UUID for plugins' consumer_id when none is set
|
|
assert.True(utils.is_valid_uuid("00000000-0000-0000-0000-000000000000"))
|
|
end)
|
|
end)
|
|
|
|
describe("https_check", function()
|
|
local old_ngx
|
|
|
|
lazy_setup(function()
|
|
old_ngx = ngx
|
|
_G.ngx = {
|
|
var = {
|
|
scheme = nil,
|
|
http_x_forwarded_proto = nil,
|
|
},
|
|
}
|
|
end)
|
|
|
|
lazy_teardown(function()
|
|
_G.ngx = old_ngx
|
|
end)
|
|
|
|
describe("without X-Forwarded-Proto header", function()
|
|
lazy_setup(function()
|
|
ngx.var.http_x_forwarded_proto = nil
|
|
end)
|
|
|
|
it("should validate an HTTPS scheme", function()
|
|
ngx.var.scheme = "hTTps" -- mixed casing to ensure case insensitiveness
|
|
assert.is.truthy(tools_http.check_https(true, false))
|
|
end)
|
|
|
|
it("should invalidate non-HTTPS schemes", function()
|
|
ngx.var.scheme = "hTTp"
|
|
assert.is.falsy(tools_http.check_https(true, false))
|
|
ngx.var.scheme = "something completely different"
|
|
assert.is.falsy(tools_http.check_https(true, false))
|
|
end)
|
|
|
|
it("should invalidate non-HTTPS schemes with proto header allowed", function()
|
|
ngx.var.scheme = "hTTp"
|
|
assert.is.falsy(tools_http.check_https(true, true))
|
|
end)
|
|
end)
|
|
|
|
describe("with X-Forwarded-Proto header", function()
|
|
|
|
lazy_teardown(function()
|
|
ngx.var.http_x_forwarded_proto = nil
|
|
end)
|
|
|
|
it("should validate any scheme with X-Forwarded_Proto as HTTPS", function()
|
|
ngx.var.http_x_forwarded_proto = "hTTPs" -- check mixed casing for case insensitiveness
|
|
ngx.var.scheme = "hTTps"
|
|
assert.is.truthy(tools_http.check_https(true, true))
|
|
ngx.var.scheme = "hTTp"
|
|
assert.is.truthy(tools_http.check_https(true, true))
|
|
ngx.var.scheme = "something completely different"
|
|
assert.is.truthy(tools_http.check_https(true, true))
|
|
end)
|
|
|
|
it("should validate only https scheme with X-Forwarded_Proto as non-HTTPS", function()
|
|
ngx.var.http_x_forwarded_proto = "hTTP"
|
|
ngx.var.scheme = "hTTps"
|
|
assert.is.truthy(tools_http.check_https(true, true))
|
|
ngx.var.scheme = "hTTp"
|
|
assert.is.falsy(tools_http.check_https(true, true))
|
|
ngx.var.scheme = "something completely different"
|
|
assert.is.falsy(tools_http.check_https(true, true))
|
|
end)
|
|
|
|
it("should return an error with multiple X-Forwarded_Proto headers", function()
|
|
ngx.var.http_x_forwarded_proto = "hTTP, https"
|
|
ngx.var.scheme = "hTTps"
|
|
assert.is.truthy(tools_http.check_https(true, true))
|
|
ngx.var.scheme = "hTTp"
|
|
assert.are.same({ nil, "Only one X-Forwarded-Proto header allowed" },
|
|
{ tools_http.check_https(true, true) })
|
|
end)
|
|
|
|
it("should not use X-Forwarded-Proto when the client is untrusted", function()
|
|
ngx.var.http_x_forwarded_proto = "https"
|
|
ngx.var.scheme = "http"
|
|
assert.is_false(tools_http.check_https(false, false))
|
|
assert.is_false(tools_http.check_https(false, true))
|
|
|
|
ngx.var.http_x_forwarded_proto = "https"
|
|
ngx.var.scheme = "https"
|
|
assert.is_true(tools_http.check_https(false, false))
|
|
assert.is_true(tools_http.check_https(false, true))
|
|
end)
|
|
|
|
it("should use X-Forwarded-Proto when the client is trusted", function()
|
|
ngx.var.http_x_forwarded_proto = "https"
|
|
ngx.var.scheme = "http"
|
|
|
|
-- trusted client but do not allow terminated
|
|
assert.is_false(tools_http.check_https(true, false))
|
|
|
|
assert.is_true(tools_http.check_https(true, true))
|
|
|
|
ngx.var.http_x_forwarded_proto = "https"
|
|
ngx.var.scheme = "https"
|
|
assert.is_true(tools_http.check_https(true, false))
|
|
assert.is_true(tools_http.check_https(true, true))
|
|
end)
|
|
|
|
it("test parsing directive header", function()
|
|
-- EMPTY table return by the function with `nil` should be read-only
|
|
assert.is_false(pcall(function()
|
|
tools_http.parse_directive_header(nil)["foo"] = "bar"
|
|
end))
|
|
|
|
-- test null
|
|
assert.same(tools_http.parse_directive_header(nil), {})
|
|
|
|
-- test empty string
|
|
assert.same(tools_http.parse_directive_header(""), {})
|
|
|
|
-- test string
|
|
assert.same(tools_http.parse_directive_header("cache-key=kong-cache,cache-age=300"), {
|
|
["cache-age"] = 300,
|
|
["cache-key"] = "kong-cache",
|
|
})
|
|
|
|
-- test table
|
|
assert.same(tools_http.parse_directive_header({
|
|
"cache-age=300",
|
|
"cache-key=kong-cache",
|
|
}), {
|
|
["cache-age"] = 300,
|
|
["cache-key"] = "kong-cache",
|
|
})
|
|
end)
|
|
end)
|
|
end)
|
|
|
|
describe("string", function()
|
|
it("checks valid UTF8 values", function()
|
|
local validate_utf8 = require("kong.tools.string").validate_utf8
|
|
|
|
assert.True(validate_utf8("hello"))
|
|
assert.True(validate_utf8(123))
|
|
assert.True(validate_utf8(true))
|
|
assert.False(validate_utf8(string.char(105, 213, 205, 149)))
|
|
assert.False(validate_utf8(string.char(128))) -- unexpected continuation byte
|
|
assert.False(validate_utf8(string.char(192, 32))) -- 2-byte sequence 0xc0 followed by space
|
|
assert.False(validate_utf8(string.char(192))) -- 2-byte sequence with last byte missing
|
|
assert.False(validate_utf8(string.char(254))) -- impossible byte
|
|
assert.False(validate_utf8(string.char(255))) -- impossible byte
|
|
assert.False(validate_utf8(string.char(237, 160, 128))) -- Single UTF-16 surrogate
|
|
end)
|
|
|
|
it("checks valid nginx size values", function()
|
|
local parse_ngx_size = require("kong.tools.string").parse_ngx_size
|
|
|
|
assert.equal(1024 * 1024 * 1024, parse_ngx_size("1G"))
|
|
assert.equal(1024 * 1024 * 1024, parse_ngx_size("1g"))
|
|
assert.equal(1024 * 1024, parse_ngx_size("1M"))
|
|
assert.equal(1024 * 1024, parse_ngx_size("1m"))
|
|
assert.equal(1024, parse_ngx_size("1k"))
|
|
assert.equal(1024, parse_ngx_size("1K"))
|
|
assert.equal(10, parse_ngx_size("10"))
|
|
end)
|
|
|
|
describe("random_string()", function()
|
|
local utils = require "kong.tools.rand"
|
|
it("should return a random string", function()
|
|
local first = utils.random_string()
|
|
assert.is_string(first)
|
|
|
|
-- build the same length string as previous implementations
|
|
assert.equals(32, #first)
|
|
|
|
-- ensure we don't find anything that isnt alphanumeric
|
|
assert.not_matches("^[^%a%d]+$", first)
|
|
|
|
-- at some point in the universe this test will fail ;)
|
|
local second = utils.random_string()
|
|
assert.not_equal(first, second)
|
|
end)
|
|
end)
|
|
|
|
describe("encode_args()", function()
|
|
it("should encode a Lua table to a querystring", function()
|
|
local str = tools_http.encode_args {
|
|
foo = "bar",
|
|
hello = "world"
|
|
}
|
|
assert.equal("foo=bar&hello=world", str)
|
|
end)
|
|
it("should encode multi-value query args", function()
|
|
local str = tools_http.encode_args {
|
|
foo = {"bar", "zoo"},
|
|
hello = "world"
|
|
}
|
|
assert.equal("foo%5b1%5d=bar&foo%5b2%5d=zoo&hello=world", str)
|
|
end)
|
|
it("should percent-encode given values", function()
|
|
local str = tools_http.encode_args {
|
|
encode = {"abc|def", ",$@|`"}
|
|
}
|
|
assert.equal("encode%5b1%5d=abc%7cdef&encode%5b2%5d=%2c%24%40%7c%60", str)
|
|
end)
|
|
it("should percent-encode given query args keys", function()
|
|
local str = tools_http.encode_args {
|
|
["hello world"] = "foo"
|
|
}
|
|
assert.equal("hello%20world=foo", str)
|
|
end)
|
|
it("should support Lua numbers", function()
|
|
local str = tools_http.encode_args {
|
|
a = 1,
|
|
b = 2
|
|
}
|
|
assert.equal("a=1&b=2", str)
|
|
end)
|
|
it("should support a boolean argument", function()
|
|
local str = tools_http.encode_args {
|
|
a = true,
|
|
b = 1
|
|
}
|
|
assert.equal("a=true&b=1", str)
|
|
end)
|
|
it("should ignore nil and false values", function()
|
|
local str = tools_http.encode_args {
|
|
a = nil,
|
|
b = false
|
|
}
|
|
assert.equal("b=false", str)
|
|
end)
|
|
it("should encode complex query args", function()
|
|
local encode = tools_http.encode_args
|
|
assert.equal("falsy=false",
|
|
encode({ falsy = false }))
|
|
assert.equal("multiple%20values=true",
|
|
encode({ ["multiple values"] = true }))
|
|
assert.equal("array%5b1%5d=hello%2c%20world",
|
|
encode({ array = {"hello, world"} }))
|
|
assert.equal("hash%2eanswer=42",
|
|
encode({ hash = { answer = 42 } }))
|
|
assert.equal("hash_array%2earr%5b1%5d=one&hash_array%2earr%5b2%5d=two",
|
|
encode({ hash_array = { arr = { "one", "two" } } }))
|
|
assert.equal("array_hash%5b1%5d%2ename=peter",
|
|
encode({ array_hash = { { name = "peter" } } }))
|
|
assert.equal("array_array%5b1%5d%5b1%5d=x&array_array%5b1%5d%5b2%5d=y",
|
|
encode({ array_array = { { "x", "y" } } }))
|
|
assert.equal("hybrid%5b1%5d=1&hybrid%5b2%5d=2&hybrid%2en=3",
|
|
encode({ hybrid = { 1, 2, n = 3 } }))
|
|
end)
|
|
it("should not interpret the `%` character followed by 2 characters in the [0-9a-f] group as an hexadecimal value", function()
|
|
local str = tools_http.encode_args {
|
|
foo = "%bar%"
|
|
}
|
|
assert.equal("foo=%25bar%25", str)
|
|
end)
|
|
it("does not percent-encode if given a `raw` option", function()
|
|
local encode = tools_http.encode_args
|
|
-- this is useful for kong.tools.http_client
|
|
assert.equal("hello world=foo, bar",
|
|
encode({ ["hello world"] = "foo, bar" }, true))
|
|
assert.equal("hash.answer=42",
|
|
encode({ hash = { answer = 42 } }, true))
|
|
assert.equal("hash_array.arr[1]=one&hash_array.arr[2]=two",
|
|
encode({ hash_array = { arr = { "one", "two" } } }, true))
|
|
assert.equal("array_hash[1].name=peter",
|
|
encode({ array_hash = { { name = "peter" } } }, true))
|
|
assert.equal("array_array[1][1]=x&array_array[1][2]=y",
|
|
encode({ array_array = { { "x", "y" } } }, true))
|
|
assert.equal("hybrid[1]=1&hybrid[2]=2&hybrid.n=3",
|
|
encode({ hybrid = { 1, 2, n = 3 } }, true))
|
|
end)
|
|
it("does not include index numbers in arrays if given the `no_array_indexes` flag", function()
|
|
local encode = tools_http.encode_args
|
|
assert.equal("falsy=false",
|
|
encode({ falsy = false }, nil, true))
|
|
assert.equal("multiple%20values=true",
|
|
encode({ ["multiple values"] = true }, nil, true))
|
|
assert.equal("array%5b%5d=hello%2c%20world",
|
|
encode({ array = {"hello, world"} }, nil, true))
|
|
assert.equal("hash%2eanswer=42",
|
|
encode({ hash = { answer = 42 } }, nil, true))
|
|
assert.equal("hash_array%2earr%5b%5d=one&hash_array%2earr%5b%5d=two",
|
|
encode({ hash_array = { arr = { "one", "two" } } }, nil, true))
|
|
assert.equal("array_hash%5b%5d%2ename=peter",
|
|
encode({ array_hash = { { name = "peter" } } }, nil, true))
|
|
assert.equal("array_array%5b%5d%5b%5d=x&array_array%5b%5d%5b%5d=y",
|
|
encode({ array_array = { { "x", "y" } } }, nil, true))
|
|
assert.equal("hybrid%5b%5d=1&hybrid%5b%5d=2&hybrid%2en=3",
|
|
encode({ hybrid = { 1, 2, n = 3 } }, nil, true))
|
|
end)
|
|
it("does not percent-encode and does not add index numbers if both `raw` and `no_array_indexes` are active", function()
|
|
local encode = tools_http.encode_args
|
|
-- this is useful for kong.tools.http_client
|
|
assert.equal("hello world=foo, bar",
|
|
encode({ ["hello world"] = "foo, bar" }, true, true))
|
|
assert.equal("hash.answer=42",
|
|
encode({ hash = { answer = 42 } }, true, true))
|
|
assert.equal("hash_array.arr[]=one&hash_array.arr[]=two",
|
|
encode({ hash_array = { arr = { "one", "two" } } }, true, true))
|
|
assert.equal("array_hash[].name=peter",
|
|
encode({ array_hash = { { name = "peter" } } }, true, true))
|
|
assert.equal("array_array[][]=x&array_array[][]=y",
|
|
encode({ array_array = { { "x", "y" } } }, true, true))
|
|
assert.equal("hybrid[]=1&hybrid[]=2&hybrid.n=3",
|
|
encode({ hybrid = { 1, 2, n = 3 } }, true, true))
|
|
end)
|
|
it("transforms ngx.null into empty string", function()
|
|
local str = tools_http.encode_args({ x = ngx.null, y = "foo" })
|
|
assert.equal("x=&y=foo", str)
|
|
end)
|
|
-- while this method's purpose is to mimic 100% the behavior of ngx.encode_args,
|
|
-- it is also used by Kong specs' http_client, to encode both querystrings and *bodies*.
|
|
-- Hence, a `raw` parameter allows encoding for bodies.
|
|
describe("raw", function()
|
|
it("should not percent-encode values", function()
|
|
local str = tools_http.encode_args({
|
|
foo = "hello world"
|
|
}, true)
|
|
assert.equal("foo=hello world", str)
|
|
end)
|
|
it("should not percent-encode keys", function()
|
|
local str = tools_http.encode_args({
|
|
["hello world"] = "foo"
|
|
}, true)
|
|
assert.equal("hello world=foo", str)
|
|
end)
|
|
it("should plainly include true and false values", function()
|
|
local str = tools_http.encode_args({
|
|
a = true,
|
|
b = false
|
|
}, true)
|
|
assert.equal("a=true&b=false", str)
|
|
end)
|
|
it("should prevent double percent-encoding", function()
|
|
local str = tools_http.encode_args({
|
|
foo = "hello%20world"
|
|
}, true)
|
|
assert.equal("foo=hello%20world", str)
|
|
end)
|
|
end)
|
|
end)
|
|
end)
|
|
|
|
describe("table", function()
|
|
describe("table_contains()", function()
|
|
it("should return false if a value is not contained in a nil table", function()
|
|
assert.False(kong_table.table_contains(nil, "foo"))
|
|
end)
|
|
it("should return true if a value is contained in a table", function()
|
|
local t = { foo = "hello", bar = "world" }
|
|
assert.True(kong_table.table_contains(t, "hello"))
|
|
end)
|
|
it("should return false if a value is not contained in a table", function()
|
|
local t = { foo = "hello", bar = "world" }
|
|
assert.False(kong_table.table_contains(t, "foo"))
|
|
end)
|
|
end)
|
|
|
|
describe("is_array()", function()
|
|
it("should know when an array (strict)", function()
|
|
assert.True(kong_table.is_array({ "a", "b", "c", "d" }))
|
|
assert.False(kong_table.is_array({ "a", "b", nil, "c", "d" }))
|
|
assert.False(kong_table.is_array({ [-1] = "a", [0] = "b", [1] = "c", [2] = "d" }))
|
|
assert.False(kong_table.is_array({ [0] = "a", [1] = "b", [2] = "c", [3] = "d" }))
|
|
assert.True(kong_table.is_array({ [1] = "a", [2] = "b", [3] = "c", [4] = "d" }))
|
|
assert.True(kong_table.is_array({ [1.0] = "a", [2.0] = "b", [3.0] = "c", [4.0] = "d" }))
|
|
assert.False(kong_table.is_array({ [1] = "a", [2] = "b", nil, [3] = "c", [4] = "d" })) --luacheck: ignore
|
|
assert.False(kong_table.is_array({ [1] = "a", [2] = "b", nil, [4] = "c", [5] = "d" })) --luacheck: ignore
|
|
assert.False(kong_table.is_array({ [1.1] = "a", [2.1] = "b", [3.1] = "c", [4.1] = "d" }))
|
|
assert.False(kong_table.is_array({ ["1"] = "a", ["2"] = "b", ["3"] = "c", ["4"] = "d" }))
|
|
assert.False(kong_table.is_array({ "a", "b", "c", foo = "d" }))
|
|
assert.False(kong_table.is_array())
|
|
assert.False(kong_table.is_array(false))
|
|
assert.False(kong_table.is_array(true))
|
|
end)
|
|
|
|
it("should know when an array (fast)", function()
|
|
assert.True(kong_table.is_array({ "a", "b", "c", "d" }, "fast"))
|
|
assert.True(kong_table.is_array({ "a", "b", nil, "c", "d" }, "fast"))
|
|
assert.True(kong_table.is_array({ [-1] = "a", [0] = "b", [1] = "c", [2] = "d" }, "fast"))
|
|
assert.True(kong_table.is_array({ [0] = "a", [1] = "b", [2] = "c", [3] = "d" }, "fast"))
|
|
assert.True(kong_table.is_array({ [1] = "a", [2] = "b", [3] = "c", [4] = "d" }, "fast"))
|
|
assert.True(kong_table.is_array({ [1.0] = "a", [2.0] = "b", [3.0] = "c", [4.0] = "d" }, "fast"))
|
|
assert.True(kong_table.is_array({ [1] = "a", [2] = "b", nil, [3] = "c", [4] = "d" }, "fast")) --luacheck: ignore
|
|
assert.True(kong_table.is_array({ [1] = "a", [2] = "b", nil, [4] = "c", [5] = "d" }, "fast")) --luacheck: ignore
|
|
assert.False(kong_table.is_array({ [1.1] = "a", [2.1] = "b", [3.1] = "c", [4.1] = "d" }, "fast"))
|
|
assert.False(kong_table.is_array({ ["1"] = "a", ["2"] = "b", ["3"] = "c", ["4"] = "d" }, "fast"))
|
|
assert.False(kong_table.is_array({ "a", "b", "c", foo = "d" }, "fast"))
|
|
assert.False(kong_table.is_array(nil, "fast"))
|
|
assert.False(kong_table.is_array(false, "fast"))
|
|
assert.False(kong_table.is_array(true, "fast"))
|
|
end)
|
|
|
|
it("should know when an array (lapis)", function()
|
|
assert.True(kong_table.is_array({ "a", "b", "c", "d" }, "lapis"))
|
|
assert.False(kong_table.is_array({ "a", "b", nil, "c", "d" }, "lapis"))
|
|
assert.False(kong_table.is_array({ [-1] = "a", [0] = "b", [1] = "c", [2] = "d" }, "lapis"))
|
|
assert.False(kong_table.is_array({ [0] = "a", [1] = "b", [2] = "c", [3] = "d" }, "lapis"))
|
|
assert.True(kong_table.is_array({ [1] = "a", [2] = "b", [3] = "c", [4] = "d" }, "lapis"))
|
|
assert.True(kong_table.is_array({ [1.0] = "a", [2.0] = "b", [3.0] = "c", [4.0] = "d" }, "lapis"))
|
|
assert.False(kong_table.is_array({ [1] = "a", [2] = "b", nil, [3] = "c", [4] = "d" }, "lapis")) --luacheck: ignore
|
|
assert.False(kong_table.is_array({ [1] = "a", [2] = "b", nil, [4] = "c", [5] = "d" }, "lapis")) --luacheck: ignore
|
|
assert.False(kong_table.is_array({ [1.1] = "a", [2.1] = "b", [3.1] = "c", [4.1] = "d" }, "lapis"))
|
|
assert.True(kong_table.is_array({ ["1"] = "a", ["2"] = "b", ["3"] = "c", ["4"] = "d" }, "lapis"))
|
|
assert.False(kong_table.is_array({ "a", "b", "c", foo = "d" }, "lapis"))
|
|
assert.False(kong_table.is_array(nil, "lapis"))
|
|
assert.False(kong_table.is_array(false, "lapis"))
|
|
assert.False(kong_table.is_array(true, "lapis"))
|
|
end)
|
|
|
|
end)
|
|
|
|
describe("add_error()", function()
|
|
local add_error = kong_table.add_error
|
|
|
|
it("should create a table if given `errors` is nil", function()
|
|
assert.same({hello = "world"}, add_error(nil, "hello", "world"))
|
|
end)
|
|
it("should add a key/value when the key does not exists", function()
|
|
local errors = {hello = "world"}
|
|
assert.same({
|
|
hello = "world",
|
|
foo = "bar"
|
|
}, add_error(errors, "foo", "bar"))
|
|
end)
|
|
it("should transform previous values to a list if the same key is given again", function()
|
|
local e = nil -- initialize for luacheck
|
|
e = add_error(e, "key1", "value1")
|
|
e = add_error(e, "key2", "value2")
|
|
assert.same({key1 = "value1", key2 = "value2"}, e)
|
|
|
|
e = add_error(e, "key1", "value3")
|
|
e = add_error(e, "key1", "value4")
|
|
assert.same({key1 = {"value1", "value3", "value4"}, key2 = "value2"}, e)
|
|
|
|
e = add_error(e, "key1", "value5")
|
|
e = add_error(e, "key1", "value6")
|
|
e = add_error(e, "key2", "value7")
|
|
assert.same({key1 = {"value1", "value3", "value4", "value5", "value6"}, key2 = {"value2", "value7"}}, e)
|
|
end)
|
|
it("should also list tables pushed as errors", function()
|
|
local e = nil -- initialize for luacheck
|
|
e = add_error(e, "key1", "value1")
|
|
e = add_error(e, "key2", "value2")
|
|
e = add_error(e, "key1", "value3")
|
|
e = add_error(e, "key1", "value4")
|
|
|
|
e = add_error(e, "keyO", {message = "some error"})
|
|
e = add_error(e, "keyO", {message = "another"})
|
|
|
|
assert.same({
|
|
key1 = {"value1", "value3", "value4"},
|
|
key2 = "value2",
|
|
keyO = {{message = "some error"}, {message = "another"}}
|
|
}, e)
|
|
end)
|
|
end)
|
|
|
|
describe("load_module_if_exists()", function()
|
|
local load_module_if_exists = require "kong.tools.module".load_module_if_exists
|
|
|
|
it("should return false if the module does not exist", function()
|
|
local loaded, mod
|
|
assert.has_no.errors(function()
|
|
loaded, mod = load_module_if_exists("kong.does.not.exist")
|
|
end)
|
|
assert.False(loaded)
|
|
assert.is.string(mod)
|
|
end)
|
|
it("should throw an error with a traceback if the module is invalid", function()
|
|
local pok, perr = pcall(load_module_if_exists, "spec.fixtures.invalid-module")
|
|
assert.falsy(pok)
|
|
assert.match("error loading module 'spec.fixtures.invalid-module'", perr, 1, true)
|
|
assert.match("./spec/fixtures/invalid-module.lua:", perr, 1, true)
|
|
end)
|
|
it("should load a module if it was found and valid", function()
|
|
local loaded, mod
|
|
assert.has_no.errors(function()
|
|
loaded, mod = load_module_if_exists("spec.fixtures.valid-module")
|
|
end)
|
|
assert.True(loaded)
|
|
assert.truthy(mod)
|
|
assert.are.same("All your base are belong to us.", mod.exposed)
|
|
end)
|
|
end)
|
|
end)
|
|
|
|
describe("hostnames and ip addresses", function()
|
|
describe("hostname_type", function()
|
|
-- no check on "name" type as anything not ipv4 and not ipv6 will be labelled as 'name' anyway
|
|
it("checks valid IPv4 address types", function()
|
|
assert.are.same("ipv4", tools_ip.hostname_type("123.123.123.123"))
|
|
assert.are.same("ipv4", tools_ip.hostname_type("1.2.3.4"))
|
|
assert.are.same("ipv4", tools_ip.hostname_type("1.2.3.4:80"))
|
|
end)
|
|
it("checks valid IPv6 address types", function()
|
|
assert.are.same("ipv6", tools_ip.hostname_type("::1"))
|
|
assert.are.same("ipv6", tools_ip.hostname_type("2345::6789"))
|
|
assert.are.same("ipv6", tools_ip.hostname_type("0001:0001:0001:0001:0001:0001:0001:0001"))
|
|
assert.are.same("ipv6", tools_ip.hostname_type("[2345::6789]:80"))
|
|
end)
|
|
end)
|
|
describe("parsing", function()
|
|
it("normalizes IPv4 address types", function()
|
|
assert.are.same({"123.123.123.123"}, {tools_ip.normalize_ipv4("123.123.123.123")})
|
|
assert.are.same({"123.123.123.123", 80}, {tools_ip.normalize_ipv4("123.123.123.123:80")})
|
|
assert.are.same({"1.1.1.1"}, {tools_ip.normalize_ipv4("1.1.1.1")})
|
|
assert.are.same({"1.1.1.1", 80}, {tools_ip.normalize_ipv4("001.001.001.001:00080")})
|
|
end)
|
|
it("fails normalizing bad IPv4 address types", function()
|
|
assert.is_nil(tools_ip.normalize_ipv4("123.123:80"))
|
|
assert.is_nil(tools_ip.normalize_ipv4("123.123.123.999"))
|
|
assert.is_nil(tools_ip.normalize_ipv4("123.123.123.123:80a"))
|
|
assert.is_nil(tools_ip.normalize_ipv4("123.123.123.123.123:80"))
|
|
assert.is_nil(tools_ip.normalize_ipv4("localhost:80"))
|
|
assert.is_nil(tools_ip.normalize_ipv4("[::1]:80"))
|
|
assert.is_nil(tools_ip.normalize_ipv4("123.123.123.123:99999"))
|
|
end)
|
|
it("normalizes IPv6 address types", function()
|
|
assert.are.same({"0000:0000:0000:0000:0000:0000:0000:0001"}, {tools_ip.normalize_ipv6("::1")})
|
|
assert.are.same({"0000:0000:0000:0000:0000:0000:0000:0001"}, {tools_ip.normalize_ipv6("[::1]")})
|
|
assert.are.same({"0000:0000:0000:0000:0000:0000:0000:0001", 80}, {tools_ip.normalize_ipv6("[::1]:80")})
|
|
assert.are.same({"0000:0000:0000:0000:0000:0000:0000:0001", 80}, {tools_ip.normalize_ipv6("[0000:0000:0000:0000:0000:0000:0000:0001]:80")})
|
|
end)
|
|
it("fails normalizing bad IPv6 address types", function()
|
|
assert.is_nil(tools_ip.normalize_ipv6("123.123.123.123"))
|
|
assert.is_nil(tools_ip.normalize_ipv6("localhost:80"))
|
|
assert.is_nil(tools_ip.normalize_ipv6("::x"))
|
|
assert.is_nil(tools_ip.normalize_ipv6("[::x]:80"))
|
|
assert.is_nil(tools_ip.normalize_ipv6("[::1]:80a"))
|
|
assert.is_nil(tools_ip.normalize_ipv6("1"))
|
|
assert.is_nil(tools_ip.normalize_ipv6("[::1]:99999"))
|
|
end)
|
|
it("validates hostnames", function()
|
|
local valids = {"hello.com", "hello.fr", "test.hello.com", "1991.io", "hello.COM",
|
|
"HELLO.com", "123helloWORLD.com", "example.123", "example-api.com",
|
|
"hello.abcd", "example_api.com", "localhost", "example.",
|
|
-- punycode examples from RFC3492; https://tools.ietf.org/html/rfc3492#page-14
|
|
-- specifically the japanese ones as they mix ascii with escaped characters
|
|
"3B-ww4c5e180e575a65lsy2b", "-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n",
|
|
"Hello-Another-Way--fc4qua05auwb3674vfr0b", "2-u9tlzr9756bt3uc0v",
|
|
"MajiKoi5-783gue6qz075azm5e", "de-jg4avhby1noc0d", "d9juau41awczczp",
|
|
}
|
|
local invalids = {"/example", ".example", "exam;ple",
|
|
"example.com/org",
|
|
"example-.org", "example.org-",
|
|
"hello..example.com", "hello-.example.com",
|
|
}
|
|
for _, name in ipairs(valids) do
|
|
assert.are.same(name, (tools_ip.check_hostname(name)))
|
|
end
|
|
for _, name in ipairs(valids) do
|
|
assert.are.same({ [1] = name, [2] = 80}, { tools_ip.check_hostname(name .. ":80")})
|
|
end
|
|
for _, name in ipairs(valids) do
|
|
assert.is_nil((tools_ip.check_hostname(name .. ":xx")))
|
|
assert.is_nil((tools_ip.check_hostname(name .. ":99999")))
|
|
end
|
|
for _, name in ipairs(invalids) do
|
|
assert.is_nil((tools_ip.check_hostname(name)))
|
|
assert.is_nil((tools_ip.check_hostname(name .. ":80")))
|
|
end
|
|
end)
|
|
it("validates addresses", function()
|
|
assert.are.same({host = "1.2.3.4", type = "ipv4", port = 80}, tools_ip.normalize_ip("1.2.3.4:80"))
|
|
assert.are.same({host = "1.2.3.4", type = "ipv4", port = nil}, tools_ip.normalize_ip("1.2.3.4"))
|
|
assert.are.same({host = "0000:0000:0000:0000:0000:0000:0000:0001", type = "ipv6", port = 80}, tools_ip.normalize_ip("[::1]:80"))
|
|
assert.are.same({host = "0000:0000:0000:0000:0000:0000:0000:0001", type = "ipv6", port = nil}, tools_ip.normalize_ip("::1"))
|
|
assert.are.same({host = "localhost", type = "name", port = 80}, tools_ip.normalize_ip("localhost:80"))
|
|
assert.are.same({host = "mashape.test", type = "name", port = nil}, tools_ip.normalize_ip("mashape.test"))
|
|
|
|
assert.is_nil((tools_ip.normalize_ip("1.2.3.4:8x0")))
|
|
assert.is_nil((tools_ip.normalize_ip("1.2.3.400")))
|
|
assert.is_nil((tools_ip.normalize_ip("[::1]:8x0")))
|
|
assert.is_nil((tools_ip.normalize_ip(":x:1")))
|
|
assert.is_nil((tools_ip.normalize_ip("localhost:8x0")))
|
|
assert.is_nil((tools_ip.normalize_ip("mashape..test")))
|
|
end)
|
|
end)
|
|
describe("formatting", function()
|
|
it("correctly formats addresses", function()
|
|
assert.are.equal("1.2.3.4", tools_ip.format_host("1.2.3.4"))
|
|
assert.are.equal("1.2.3.4:80", tools_ip.format_host("1.2.3.4", 80))
|
|
assert.are.equal("[0000:0000:0000:0000:0000:0000:0000:0001]", tools_ip.format_host("::1"))
|
|
assert.are.equal("[0000:0000:0000:0000:0000:0000:0000:0001]:80", tools_ip.format_host("::1", 80))
|
|
assert.are.equal("localhost", tools_ip.format_host("localhost"))
|
|
assert.are.equal("mashape.test:80", tools_ip.format_host("mashape.test", 80))
|
|
-- passthrough (string)
|
|
assert.are.equal("1.2.3.4", tools_ip.format_host(tools_ip.normalize_ipv4("1.2.3.4")))
|
|
assert.are.equal("1.2.3.4:80", tools_ip.format_host(tools_ip.normalize_ipv4("1.2.3.4:80")))
|
|
assert.are.equal("[0000:0000:0000:0000:0000:0000:0000:0001]", tools_ip.format_host(tools_ip.normalize_ipv6("::1")))
|
|
assert.are.equal("[0000:0000:0000:0000:0000:0000:0000:0001]:80", tools_ip.format_host(tools_ip.normalize_ipv6("[::1]:80")))
|
|
assert.are.equal("localhost", tools_ip.format_host(tools_ip.check_hostname("localhost")))
|
|
assert.are.equal("mashape.test:80", tools_ip.format_host(tools_ip.check_hostname("mashape.test:80")))
|
|
-- passthrough general (table)
|
|
assert.are.equal("1.2.3.4", tools_ip.format_host(tools_ip.normalize_ip("1.2.3.4")))
|
|
assert.are.equal("1.2.3.4:80", tools_ip.format_host(tools_ip.normalize_ip("1.2.3.4:80")))
|
|
assert.are.equal("[0000:0000:0000:0000:0000:0000:0000:0001]", tools_ip.format_host(tools_ip.normalize_ip("::1")))
|
|
assert.are.equal("[0000:0000:0000:0000:0000:0000:0000:0001]:80", tools_ip.format_host(tools_ip.normalize_ip("[::1]:80")))
|
|
assert.are.equal("localhost", tools_ip.format_host(tools_ip.normalize_ip("localhost")))
|
|
assert.are.equal("mashape.test:80", tools_ip.format_host(tools_ip.normalize_ip("mashape.test:80")))
|
|
-- passthrough errors
|
|
local one, two = tools_ip.format_host(tools_ip.normalize_ipv4("1.2.3.4.5"))
|
|
assert.are.equal("nilstring", type(one) .. type(two))
|
|
local one, two = tools_ip.format_host(tools_ip.normalize_ipv6("not ipv6..."))
|
|
assert.are.equal("nilstring", type(one) .. type(two))
|
|
local one, two = tools_ip.format_host(tools_ip.check_hostname("//bad..name\\:123"))
|
|
assert.are.equal("nilstring", type(one) .. type(two))
|
|
local one, two = tools_ip.format_host(tools_ip.normalize_ip("m a s h a p e.test:80"))
|
|
assert.are.equal("nilstring", type(one) .. type(two))
|
|
end)
|
|
end)
|
|
end)
|
|
|
|
it("validate_header_name() validates header names", function()
|
|
local header_chars = [[_-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]]
|
|
|
|
for i = 1, 255 do
|
|
local c = string.char(i)
|
|
|
|
if string.find(header_chars, c, nil, true) then
|
|
assert(tools_http.validate_header_name(c) == c,
|
|
"ascii character '" .. c .. "' (" .. i .. ") should have been allowed")
|
|
else
|
|
assert(tools_http.validate_header_name(c) == nil,
|
|
"ascii character " .. i .. " should not have been allowed")
|
|
end
|
|
end
|
|
end)
|
|
it("validate_cookie_name() validates cookie names", function()
|
|
local cookie_chars = [[~`|!#$%&'*+-._-^0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]]
|
|
|
|
for i = 1, 255 do
|
|
local c = string.char(i)
|
|
|
|
if string.find(cookie_chars, c, nil, true) then
|
|
assert(tools_http.validate_cookie_name(c) == c,
|
|
"ascii character '" .. c .. "' (" .. i .. ") should have been allowed")
|
|
else
|
|
assert(tools_http.validate_cookie_name(c) == nil,
|
|
"ascii character " .. i .. " should not have been allowed")
|
|
end
|
|
end
|
|
end)
|
|
it("pack() stores results, including nils, properly", function()
|
|
assert.same({ n = 0 }, kong_table.pack())
|
|
assert.same({ n = 1 }, kong_table.pack(nil))
|
|
assert.same({ n = 3, "1", "2", "3" }, kong_table.pack("1", "2", "3"))
|
|
assert.same({ n = 3, [1] = "1", [3] = "3" }, kong_table.pack("1", nil, "3"))
|
|
end)
|
|
it("unpack() unwraps results, including nils, properly", function()
|
|
local a,b,c
|
|
a,b,c = kong_table.unpack({})
|
|
assert.is_nil(a)
|
|
assert.is_nil(b)
|
|
assert.is_nil(c)
|
|
|
|
a,b,c = unpack({ n = 1 })
|
|
assert.is_nil(a)
|
|
assert.is_nil(b)
|
|
assert.is_nil(c)
|
|
|
|
a,b,c = kong_table.unpack({ n = 3, "1", "2", "3" })
|
|
assert.equal("1", a)
|
|
assert.equal("2", b)
|
|
assert.equal("3", c)
|
|
|
|
a,b,c = kong_table.unpack({ n = 3, [1] = "1", [3] = "3" })
|
|
assert.equal("1", a)
|
|
assert.is_nil(b)
|
|
assert.equal("3", c)
|
|
end)
|
|
|
|
describe("bytes_to_str()", function()
|
|
local bytes_to_str = require("kong.tools.string").bytes_to_str
|
|
|
|
it("converts bytes to the desired unit", function()
|
|
assert.equal("5497558", bytes_to_str(5497558, "b"))
|
|
assert.equal("5368.71 KiB", bytes_to_str(5497558, "k"))
|
|
assert.equal("5.24 MiB", bytes_to_str(5497558, "m"))
|
|
assert.equal("0.01 GiB", bytes_to_str(5497558, "g"))
|
|
assert.equal("5.12 GiB", bytes_to_str(5497558998, "g"))
|
|
end)
|
|
|
|
it("defaults unit arg to bytes", function()
|
|
assert.equal("5497558", bytes_to_str(5497558))
|
|
assert.equal("5497558", bytes_to_str(5497558, ""))
|
|
end)
|
|
|
|
it("unit arg is case-insensitive", function()
|
|
assert.equal("5497558", bytes_to_str(5497558, "B"))
|
|
assert.equal("5368.71 KiB", bytes_to_str(5497558, "K"))
|
|
assert.equal("5.24 MiB", bytes_to_str(5497558, "M"))
|
|
assert.equal("0.01 GiB", bytes_to_str(5497558, "G"))
|
|
assert.equal("5.12 GiB", bytes_to_str(5497558998, "G"))
|
|
end)
|
|
|
|
it("scale arg", function()
|
|
-- 3
|
|
assert.equal("5497558", bytes_to_str(5497558, "b", 3))
|
|
assert.equal("5368.709 KiB", bytes_to_str(5497558, "k", 3))
|
|
assert.equal("5.243 MiB", bytes_to_str(5497558, "m", 3))
|
|
assert.equal("0.005 GiB", bytes_to_str(5497558, "g", 3))
|
|
assert.equal("5.120 GiB", bytes_to_str(5497558998, "g", 3))
|
|
|
|
-- 0
|
|
assert.equal("5 GiB", bytes_to_str(5497558998, "g", 0))
|
|
|
|
-- decimals
|
|
assert.equal("5.12 GiB", bytes_to_str(5497558998, "g", 2.2))
|
|
end)
|
|
|
|
it("errors on invalid unit arg", function()
|
|
assert.has_error(function()
|
|
bytes_to_str(1234, "V")
|
|
end, "invalid unit 'V' (expected 'k/K', 'm/M', or 'g/G')")
|
|
end)
|
|
|
|
it("errors on invalid scale arg", function()
|
|
assert.has_error(function()
|
|
bytes_to_str(1234, "k", -1)
|
|
end, "scale must be equal or greater than 0")
|
|
|
|
assert.has_error(function()
|
|
bytes_to_str(1234, "k", "")
|
|
end, "scale must be equal or greater than 0")
|
|
end)
|
|
end)
|
|
|
|
describe("gzip_[de_in]flate()", function()
|
|
local utils = require "kong.tools.gzip"
|
|
|
|
it("empty string", function()
|
|
local gz = assert(utils.deflate_gzip(""))
|
|
assert.equal(utils.inflate_gzip(gz), "")
|
|
end)
|
|
|
|
it("small string (< 1 buffer)", function()
|
|
local gz = assert(utils.deflate_gzip("aabbccddeeffgg"))
|
|
assert.equal(utils.inflate_gzip(gz), "aabbccddeeffgg")
|
|
end)
|
|
|
|
it("long string (> 1 buffer)", function()
|
|
local s = string.rep("a", 70000) -- > 64KB
|
|
|
|
local gz = assert(utils.deflate_gzip(s))
|
|
|
|
assert(#gz < #s)
|
|
|
|
assert.equal(utils.inflate_gzip(gz), s)
|
|
end)
|
|
|
|
it("bad gzipped data", function()
|
|
local res, err = utils.inflate_gzip("bad")
|
|
assert.is_nil(res)
|
|
assert.equal(err, "INFLATE: data error")
|
|
end)
|
|
end)
|
|
|
|
describe("get_mime_type()", function()
|
|
it("with valid mime types", function()
|
|
assert.equal("application/json; charset=utf-8", tools_http.get_mime_type("application/json"))
|
|
assert.equal("application/json; charset=utf-8", tools_http.get_mime_type("application/json; charset=utf-8"))
|
|
assert.equal("application/json; charset=utf-8", tools_http.get_mime_type("application/*"))
|
|
assert.equal("application/json; charset=utf-8", tools_http.get_mime_type("application/*; charset=utf-8"))
|
|
assert.equal("text/html; charset=utf-8", tools_http.get_mime_type("text/html"))
|
|
assert.equal("text/plain; charset=utf-8", tools_http.get_mime_type("text/plain"))
|
|
assert.equal("text/plain; charset=utf-8", tools_http.get_mime_type("text/*"))
|
|
assert.equal("text/plain; charset=utf-8", tools_http.get_mime_type("text/*; charset=utf-8"))
|
|
assert.equal("application/xml; charset=utf-8", tools_http.get_mime_type("application/xml"))
|
|
assert.equal("application/json; charset=utf-8", tools_http.get_mime_type("*/*; charset=utf-8"))
|
|
assert.equal("application/json; charset=utf-8", tools_http.get_mime_type("*/*"))
|
|
assert.equal("", tools_http.get_mime_type("application/grpc"))
|
|
end)
|
|
|
|
it("with unsupported or invalid mime types", function()
|
|
assert.equal("application/json; charset=utf-8", tools_http.get_mime_type("audio/*", true))
|
|
assert.equal("application/json; charset=utf-8", tools_http.get_mime_type("text/css"))
|
|
assert.equal("application/json; charset=utf-8", tools_http.get_mime_type("default"))
|
|
assert.is_nil(tools_http.get_mime_type("video/json", false))
|
|
assert.is_nil(tools_http.get_mime_type("text/javascript", false))
|
|
end)
|
|
end)
|
|
|
|
describe("nginx_conf_time_to_seconds()", function()
|
|
local nginx_conf_time_to_seconds = require("kong.tools.time").nginx_conf_time_to_seconds
|
|
|
|
it("returns value in seconds", function()
|
|
assert.equal(5, nginx_conf_time_to_seconds("5"))
|
|
assert.equal(5, nginx_conf_time_to_seconds("5s"))
|
|
assert.equal(60, nginx_conf_time_to_seconds("60s"))
|
|
assert.equal(60, nginx_conf_time_to_seconds("1m"))
|
|
assert.equal(120, nginx_conf_time_to_seconds("2m"))
|
|
assert.equal(7200, nginx_conf_time_to_seconds("2h"))
|
|
assert.equal(172800, nginx_conf_time_to_seconds("2d"))
|
|
assert.equal(1209600, nginx_conf_time_to_seconds("2w"))
|
|
assert.equal(5184000, nginx_conf_time_to_seconds("2M"))
|
|
assert.equal(63072000, nginx_conf_time_to_seconds("2y"))
|
|
end)
|
|
|
|
it("throws an error on bad argument", function()
|
|
assert.has_error(function()
|
|
nginx_conf_time_to_seconds("abcd")
|
|
end, "bad argument #1 'str'")
|
|
end)
|
|
end)
|
|
|
|
describe("topological_sort", function()
|
|
local get_neighbors = function(x) return x end
|
|
local ts = require("kong.db.utils").topological_sort
|
|
|
|
it("it puts destinations first", function()
|
|
local a = { id = "a" }
|
|
local b = { id = "b", a }
|
|
local c = { id = "c", a, b }
|
|
local d = { id = "d", c }
|
|
|
|
local x = ts({ c, d, a, b }, get_neighbors)
|
|
assert.same({ a, b, c, d }, x)
|
|
end)
|
|
|
|
it("returns an error if cycles are found", function()
|
|
local a = { id = "a" }
|
|
local b = { id = "b", a }
|
|
a[1] = b
|
|
local x, err = ts({ a, b }, get_neighbors)
|
|
assert.is_nil(x)
|
|
assert.equals("Cycle detected, cannot sort topologically", err)
|
|
end)
|
|
end)
|
|
|
|
local function count_keys(t, n)
|
|
n = n or 0
|
|
if type(t) ~= "table" then
|
|
return n
|
|
end
|
|
for k, v in pairs(t) do
|
|
n = count_keys(k, n)
|
|
n = count_keys(v, n)
|
|
n = n + 1
|
|
end
|
|
return n
|
|
end
|
|
|
|
describe("deep_copy(t)", function()
|
|
it("copies values, keys and metatables and sets metatables", function()
|
|
local meta = {}
|
|
local meta2 = {}
|
|
local ref = {}
|
|
local ref2 = setmetatable({}, meta)
|
|
|
|
ref[1] = 1
|
|
ref[2] = ref2
|
|
ref[3] = nil
|
|
ref[4] = 4
|
|
|
|
local a = setmetatable({
|
|
a = setmetatable({
|
|
a = "clone",
|
|
}, meta2),
|
|
b = ref,
|
|
}, meta)
|
|
|
|
local b = {
|
|
[a] = a,
|
|
a = a,
|
|
b = ref,
|
|
}
|
|
|
|
local c = kong_table.deep_copy(b)
|
|
|
|
assert.not_same(b, c)
|
|
assert.not_equal(b, c)
|
|
|
|
assert.equal(b[a], b.a)
|
|
assert.not_equal(c[a], c.a)
|
|
|
|
assert.equal(b.b, ref)
|
|
assert.not_equal(c.b, ref)
|
|
|
|
assert.equal(b.b[1], 1)
|
|
assert.equal(c.b[1], 1)
|
|
|
|
assert.equal(b.b[2], ref2)
|
|
assert.not_equal(c.b[2], ref2)
|
|
|
|
assert.equal(getmetatable(b.b[2]), meta)
|
|
assert.not_equal(getmetatable(c.b[2]), meta)
|
|
|
|
assert.equal(b.b[3], nil)
|
|
assert.equal(c.b[3], nil)
|
|
|
|
assert.equal(b.b[4], 4)
|
|
assert.equal(c.b[4], 4)
|
|
|
|
assert.equal(getmetatable(b[a]), meta)
|
|
assert.is_nil(getmetatable(c[a]))
|
|
|
|
assert.equal(getmetatable(b.a), meta)
|
|
assert.not_equal(getmetatable(c.a), meta)
|
|
assert.is_table(getmetatable(c.a), meta)
|
|
|
|
assert.not_equal(getmetatable(b[a]), getmetatable(c[a]))
|
|
assert.not_equal(getmetatable(b.a), getmetatable(c.a))
|
|
|
|
assert.is_table(getmetatable(b[a]))
|
|
assert.is_nil(getmetatable(c[a]))
|
|
|
|
assert.is_table(getmetatable(b.a))
|
|
assert.is_table(getmetatable(c.a))
|
|
|
|
assert.equal(getmetatable(b[a].a), meta2)
|
|
assert.is_nil(getmetatable(c[a] and c[a].a or nil))
|
|
assert.not_equal(getmetatable(b[a].a), getmetatable(c[a] and c[a].a or nil))
|
|
|
|
assert.equal(getmetatable(b.a.a), meta2)
|
|
assert.not_equal(getmetatable(c.a.a), meta2)
|
|
assert.not_equal(getmetatable(b.a.a), getmetatable(c.a.a))
|
|
|
|
assert.not_equal(b[a], c[a])
|
|
assert.not_equal(b.a, c.a)
|
|
assert.not_equal(b[a].a, c[a] and c[a].a or nil)
|
|
assert.not_equal(b.a.a, c.a.a)
|
|
assert.not_equal(b[a].a.a, c[a] and c[a].a and c[a].a.a or nil)
|
|
assert.equal(b.a.a.a, c.a.a.a)
|
|
|
|
local key_found
|
|
for k in pairs(b) do
|
|
key_found = nil
|
|
for k2 in pairs(c) do
|
|
if k == k2 then
|
|
key_found = true
|
|
break
|
|
end
|
|
end
|
|
if type(k) == "table" then
|
|
assert.is_nil(key_found)
|
|
else
|
|
assert.is_true(key_found)
|
|
end
|
|
end
|
|
|
|
key_found = nil
|
|
for k in pairs(b) do
|
|
if k == a then
|
|
key_found = true
|
|
break
|
|
end
|
|
end
|
|
assert.is_true(key_found)
|
|
|
|
key_found = nil
|
|
for k in pairs(c) do
|
|
if k == a then
|
|
key_found = true
|
|
break
|
|
end
|
|
end
|
|
assert.is_nil(key_found)
|
|
|
|
assert.equal(count_keys(b), 24)
|
|
assert.equal(count_keys(c), 24)
|
|
end)
|
|
end)
|
|
|
|
describe("deep_copy(t, false)", function()
|
|
it("copies values and keys and removes metatables", function()
|
|
local meta = {}
|
|
local meta2 = {}
|
|
local ref = {}
|
|
local ref2 = setmetatable({}, meta)
|
|
|
|
ref[1] = 1
|
|
ref[2] = ref2
|
|
ref[3] = nil
|
|
ref[4] = 4
|
|
|
|
local a = setmetatable({
|
|
a = setmetatable({
|
|
a = "clone",
|
|
}, meta2),
|
|
b = ref,
|
|
}, meta)
|
|
|
|
local b = {
|
|
[a] = a,
|
|
a = a,
|
|
b = ref,
|
|
}
|
|
|
|
local c = kong_table.deep_copy(b, false)
|
|
|
|
assert.not_same(b, c)
|
|
assert.not_equal(b, c)
|
|
|
|
assert.equal(b[a], b.a)
|
|
assert.not_equal(c[a], c.a)
|
|
|
|
assert.equal(b.b, ref)
|
|
assert.not_equal(c.b, ref)
|
|
|
|
assert.equal(b.b[1], 1)
|
|
assert.equal(c.b[1], 1)
|
|
|
|
assert.equal(b.b[2], ref2)
|
|
assert.not_equal(c.b[2], ref2)
|
|
|
|
assert.equal(getmetatable(b.b[2]), meta)
|
|
assert.not_equal(getmetatable(c.b[2]), meta)
|
|
|
|
assert.equal(b.b[3], nil)
|
|
assert.equal(c.b[3], nil)
|
|
|
|
assert.equal(b.b[4], 4)
|
|
assert.equal(c.b[4], 4)
|
|
|
|
assert.equal(getmetatable(b[a]), meta)
|
|
assert.is_nil(getmetatable(c[a]))
|
|
|
|
assert.equal(getmetatable(b.a), meta)
|
|
assert.not_equal(getmetatable(c.a), meta)
|
|
assert.is_nil(getmetatable(c.a), meta)
|
|
|
|
assert.not_equal(getmetatable(b[a]), getmetatable(c[a]))
|
|
assert.not_equal(getmetatable(b.a), getmetatable(c.a))
|
|
|
|
assert.is_table(getmetatable(b[a]))
|
|
assert.is_nil(getmetatable(c[a]))
|
|
|
|
assert.is_table(getmetatable(b.a))
|
|
assert.is_nil(getmetatable(c.a))
|
|
|
|
assert.equal(getmetatable(b[a].a), meta2)
|
|
assert.is_nil(getmetatable(c[a] and c[a].a or nil))
|
|
assert.not_equal(getmetatable(b[a].a), getmetatable(c[a] and c[a].a or nil))
|
|
|
|
assert.equal(getmetatable(b.a.a), meta2)
|
|
assert.not_equal(getmetatable(c.a.a), meta2)
|
|
assert.not_equal(getmetatable(b.a.a), getmetatable(c.a.a))
|
|
|
|
assert.not_equal(b[a], c[a])
|
|
assert.not_equal(b.a, c.a)
|
|
assert.not_equal(b[a].a, c[a] and c[a].a or nil)
|
|
assert.not_equal(b.a.a, c.a.a)
|
|
assert.not_equal(b[a].a.a, c[a] and c[a].a and c[a].a.a or nil)
|
|
assert.equal(b.a.a.a, c.a.a.a)
|
|
|
|
local key_found
|
|
for k in pairs(b) do
|
|
key_found = nil
|
|
for k2 in pairs(c) do
|
|
if k == k2 then
|
|
key_found = true
|
|
break
|
|
end
|
|
end
|
|
if type(k) == "table" then
|
|
assert.is_nil(key_found)
|
|
else
|
|
assert.is_true(key_found)
|
|
end
|
|
end
|
|
|
|
key_found = nil
|
|
for k in pairs(b) do
|
|
if k == a then
|
|
key_found = true
|
|
break
|
|
end
|
|
end
|
|
assert.is_true(key_found)
|
|
|
|
key_found = nil
|
|
for k in pairs(c) do
|
|
if k == a then
|
|
key_found = true
|
|
break
|
|
end
|
|
end
|
|
assert.is_nil(key_found)
|
|
|
|
assert.equal(count_keys(b), 24)
|
|
assert.equal(count_keys(c), 24)
|
|
end)
|
|
end)
|
|
|
|
describe("cycle_aware_deep_copy(t)", function()
|
|
it("cycle aware copies values and sets the metatables but does not copy keys or metatables", function()
|
|
local meta = {}
|
|
local meta2 = {}
|
|
local ref = {}
|
|
local ref2 = setmetatable({}, meta)
|
|
|
|
ref[1] = 1
|
|
ref[2] = ref2
|
|
ref[3] = nil
|
|
ref[4] = 4
|
|
|
|
local a = setmetatable({
|
|
a = setmetatable({
|
|
a = "clone",
|
|
}, meta2),
|
|
b = ref,
|
|
}, meta)
|
|
|
|
local b = {
|
|
[a] = a,
|
|
a = a,
|
|
b = ref,
|
|
}
|
|
|
|
local c = kong_table.cycle_aware_deep_copy(b)
|
|
|
|
assert.same(b, c)
|
|
assert.not_equal(b, c)
|
|
|
|
assert.equal(b[a], b.a)
|
|
assert.equal(c[a], c.a)
|
|
|
|
assert.equal(b.b, ref)
|
|
assert.not_equal(c.b, ref)
|
|
|
|
assert.equal(b.b[1], 1)
|
|
assert.equal(c.b[1], 1)
|
|
|
|
assert.equal(b.b[2], ref2)
|
|
assert.not_equal(c.b[2], ref2)
|
|
|
|
assert.equal(getmetatable(b.b[2]), meta)
|
|
assert.equal(getmetatable(c.b[2]), meta)
|
|
|
|
assert.equal(b.b[3], nil)
|
|
assert.equal(c.b[3], nil)
|
|
|
|
assert.equal(b.b[4], 4)
|
|
assert.equal(c.b[4], 4)
|
|
|
|
assert.equal(getmetatable(b[a]), meta)
|
|
assert.equal(getmetatable(c[a]), meta)
|
|
|
|
assert.equal(getmetatable(b.a), meta)
|
|
assert.equal(getmetatable(c.a), meta)
|
|
|
|
assert.equal(getmetatable(b[a]), getmetatable(c[a]))
|
|
assert.equal(getmetatable(b.a), getmetatable(c.a))
|
|
|
|
assert.equal(getmetatable(b[a].a), meta2)
|
|
assert.equal(getmetatable(c[a].a), meta2)
|
|
assert.equal(getmetatable(b[a].a), getmetatable(c[a].a))
|
|
|
|
assert.equal(getmetatable(b.a.a), meta2)
|
|
assert.equal(getmetatable(c.a.a), meta2)
|
|
assert.equal(getmetatable(b.a.a), getmetatable(c.a.a))
|
|
|
|
assert.not_equal(b[a], c[a])
|
|
assert.not_equal(b.a, c.a)
|
|
assert.not_equal(b[a].a, c[a].a)
|
|
assert.not_equal(b.a.a, c.a.a)
|
|
assert.equal(b[a].a.a, c[a].a.a)
|
|
assert.equal(b.a.a.a, c.a.a.a)
|
|
|
|
local key_found
|
|
for k in pairs(b) do
|
|
key_found = nil
|
|
for k2 in pairs(c) do
|
|
if k == k2 then
|
|
key_found = true
|
|
break
|
|
end
|
|
end
|
|
assert.is_true(key_found)
|
|
end
|
|
|
|
key_found = nil
|
|
for k in pairs(b) do
|
|
if k == a then
|
|
key_found = true
|
|
break
|
|
end
|
|
end
|
|
assert.is_true(key_found)
|
|
|
|
key_found = nil
|
|
for k in pairs(c) do
|
|
if k == a then
|
|
key_found = true
|
|
break
|
|
end
|
|
end
|
|
assert.is_true(key_found)
|
|
|
|
assert.equal(count_keys(b), 24)
|
|
assert.equal(count_keys(c), 24)
|
|
end)
|
|
end)
|
|
|
|
describe("cycle_aware_deep_copy(t, true)", function()
|
|
it("cycle aware copies values and removes metatables but does not copy keys", function()
|
|
local meta = {}
|
|
local meta2 = {}
|
|
local ref = {}
|
|
local ref2 = setmetatable({}, meta)
|
|
|
|
ref[1] = 1
|
|
ref[2] = ref2
|
|
ref[3] = nil
|
|
ref[4] = 4
|
|
|
|
local a = setmetatable({
|
|
a = setmetatable({
|
|
a = "clone",
|
|
}, meta2),
|
|
b = ref,
|
|
}, meta)
|
|
|
|
local b = {
|
|
[a] = a,
|
|
a = a,
|
|
b = ref,
|
|
}
|
|
|
|
local c = kong_table.cycle_aware_deep_copy(b, true)
|
|
|
|
assert.same(b, c)
|
|
assert.not_equal(b, c)
|
|
|
|
assert.equal(b[a], b.a)
|
|
assert.equal(c[a], c.a)
|
|
|
|
assert.equal(b.b, ref)
|
|
assert.not_equal(c.b, ref)
|
|
|
|
assert.equal(b.b[1], 1)
|
|
assert.equal(c.b[1], 1)
|
|
|
|
assert.equal(b.b[2], ref2)
|
|
assert.not_equal(c.b[2], ref2)
|
|
|
|
assert.equal(getmetatable(b.b[2]), meta)
|
|
assert.is_nil(getmetatable(c.b[2]))
|
|
|
|
assert.equal(b.b[3], nil)
|
|
assert.equal(c.b[3], nil)
|
|
|
|
assert.equal(b.b[4], 4)
|
|
assert.equal(c.b[4], 4)
|
|
|
|
assert.equal(getmetatable(b[a]), meta)
|
|
assert.is_nil(getmetatable(c[a]), meta)
|
|
|
|
assert.equal(getmetatable(b.a), meta)
|
|
assert.is_nil(getmetatable(c.a))
|
|
|
|
assert.not_equal(getmetatable(b[a]), getmetatable(c[a]))
|
|
assert.not_equal(getmetatable(b.a), getmetatable(c.a))
|
|
|
|
assert.equal(getmetatable(b[a].a), meta2)
|
|
assert.is_nil(getmetatable(c[a].a))
|
|
assert.not_equal(getmetatable(b[a].a), getmetatable(c[a].a))
|
|
|
|
assert.equal(getmetatable(b.a.a), meta2)
|
|
assert.is_nil(getmetatable(c.a.a))
|
|
assert.not_equal(getmetatable(b.a.a), getmetatable(c.a.a))
|
|
|
|
assert.not_equal(b[a], c[a])
|
|
assert.not_equal(b.a, c.a)
|
|
assert.not_equal(b[a].a, c[a].a)
|
|
assert.not_equal(b.a.a, c.a.a)
|
|
assert.equal(b[a].a.a, c[a].a.a)
|
|
assert.equal(b.a.a.a, c.a.a.a)
|
|
|
|
local key_found
|
|
for k in pairs(b) do
|
|
key_found = nil
|
|
for k2 in pairs(c) do
|
|
if k == k2 then
|
|
key_found = true
|
|
break
|
|
end
|
|
end
|
|
assert.is_true(key_found)
|
|
end
|
|
|
|
key_found = nil
|
|
for k in pairs(b) do
|
|
if k == a then
|
|
key_found = true
|
|
break
|
|
end
|
|
end
|
|
assert.is_true(key_found)
|
|
|
|
key_found = nil
|
|
for k in pairs(c) do
|
|
if k == a then
|
|
key_found = true
|
|
break
|
|
end
|
|
end
|
|
assert.is_true(key_found)
|
|
|
|
assert.equal(count_keys(b), 24)
|
|
assert.equal(count_keys(c), 24)
|
|
end)
|
|
end)
|
|
|
|
describe("cycle_aware_deep_copy(t, nil, true)", function()
|
|
it("cycle aware copies values and keys, and sets metatables", function()
|
|
local meta = {}
|
|
local meta2 = {}
|
|
local ref = {}
|
|
local ref2 = setmetatable({}, meta)
|
|
|
|
ref[1] = 1
|
|
ref[2] = ref2
|
|
ref[3] = nil
|
|
ref[4] = 4
|
|
|
|
local a = setmetatable({
|
|
a = setmetatable({
|
|
a = "clone",
|
|
}, meta2),
|
|
b = ref,
|
|
}, meta)
|
|
|
|
local b = {
|
|
[a] = a,
|
|
a = a,
|
|
b = ref,
|
|
}
|
|
|
|
local c = kong_table.cycle_aware_deep_copy(b, nil, true)
|
|
|
|
assert.not_same(b, c)
|
|
assert.not_equal(b, c)
|
|
|
|
assert.equal(b[a], b.a)
|
|
assert.is_nil(c[a])
|
|
|
|
assert.equal(b.b, ref)
|
|
assert.not_equal(c.b, ref)
|
|
|
|
assert.equal(b.b[1], 1)
|
|
assert.equal(c.b[1], 1)
|
|
|
|
assert.equal(b.b[2], ref2)
|
|
assert.not_equal(c.b[2], ref2)
|
|
|
|
assert.equal(getmetatable(b.b[2]), meta)
|
|
assert.equal(getmetatable(c.b[2]), meta)
|
|
|
|
assert.equal(b.b[3], nil)
|
|
assert.equal(c.b[3], nil)
|
|
|
|
assert.equal(b.b[4], 4)
|
|
assert.equal(c.b[4], 4)
|
|
|
|
assert.equal(getmetatable(b[a]), meta)
|
|
assert.is_nil(getmetatable(c[a]))
|
|
|
|
assert.equal(getmetatable(b.a), meta)
|
|
assert.equal(getmetatable(c.a), meta)
|
|
|
|
assert.not_equal(getmetatable(b[a]), getmetatable(c[a]))
|
|
assert.equal(getmetatable(b.a), getmetatable(c.a))
|
|
|
|
assert.equal(getmetatable(b[a].a), meta2)
|
|
assert.is_nil(getmetatable(c[a] and c[a].a))
|
|
assert.not_equal(getmetatable(b[a].a), getmetatable(c[a] and c[a].a or nil))
|
|
|
|
assert.equal(getmetatable(b.a.a), meta2)
|
|
assert.equal(getmetatable(c.a.a), meta2)
|
|
assert.equal(getmetatable(b.a.a), getmetatable(c.a.a))
|
|
|
|
assert.not_equal(b[a], c[a])
|
|
assert.not_equal(b.a, c.a)
|
|
assert.not_equal(b[a].a, c[a] and c[a].a or nil)
|
|
assert.not_equal(b.a.a, c.a.a)
|
|
assert.not_equal(b[a].a.a, c[a] and c[a].a and c[a].a.a or nil)
|
|
assert.equal(b.a.a.a, c.a.a.a)
|
|
|
|
local key_found
|
|
for k in pairs(b) do
|
|
key_found = nil
|
|
for k2 in pairs(c) do
|
|
if k == k2 then
|
|
key_found = true
|
|
break
|
|
end
|
|
end
|
|
if type(k) == "table" then
|
|
assert.is_nil(key_found)
|
|
else
|
|
assert.is_true(key_found)
|
|
end
|
|
end
|
|
|
|
key_found = nil
|
|
for k in pairs(b) do
|
|
if k == a then
|
|
key_found = true
|
|
break
|
|
end
|
|
end
|
|
assert.is_true(key_found)
|
|
|
|
key_found = nil
|
|
for k in pairs(c) do
|
|
if k == a then
|
|
key_found = true
|
|
break
|
|
end
|
|
end
|
|
assert.is_nil(key_found)
|
|
|
|
assert.equal(count_keys(b), 24)
|
|
assert.equal(count_keys(c), 24)
|
|
end)
|
|
end)
|
|
|
|
describe("cycle_aware_deep_copy(t, nil, nil, cache)", function()
|
|
it("cycle aware copies values that are not already cached and sets metatables but does not copy keys or metatables", function()
|
|
local cache = {}
|
|
local meta = {}
|
|
local meta2 = {}
|
|
local ref = {}
|
|
local ref2 = setmetatable({}, meta)
|
|
|
|
cache[ref] = ref
|
|
cache[ref2] = ref2
|
|
|
|
ref[1] = 1
|
|
ref[2] = ref2
|
|
ref[3] = nil
|
|
ref[4] = 4
|
|
|
|
local a = setmetatable({
|
|
a = setmetatable({
|
|
a = "clone",
|
|
}, meta2),
|
|
b = ref,
|
|
}, meta)
|
|
|
|
cache[a] = a
|
|
|
|
local b = {
|
|
[a] = a,
|
|
a = a,
|
|
b = ref,
|
|
}
|
|
|
|
local c = kong_table.cycle_aware_deep_copy(b, nil, nil, cache)
|
|
|
|
assert.same(b, c)
|
|
assert.not_equal(b, c)
|
|
|
|
assert.equal(b[a], b.a)
|
|
assert.equal(c[a], c.a)
|
|
|
|
assert.equal(b.b, ref)
|
|
assert.equal(c.b, ref)
|
|
|
|
assert.equal(b.b[1], 1)
|
|
assert.equal(c.b[1], 1)
|
|
|
|
assert.equal(b.b[2], ref2)
|
|
assert.equal(c.b[2], ref2)
|
|
|
|
assert.equal(getmetatable(b.b[2]), meta)
|
|
assert.equal(getmetatable(c.b[2]), meta)
|
|
|
|
assert.equal(b.b[3], nil)
|
|
assert.equal(c.b[3], nil)
|
|
|
|
assert.equal(b.b[4], 4)
|
|
assert.equal(c.b[4], 4)
|
|
|
|
assert.equal(getmetatable(b[a]), meta)
|
|
assert.equal(getmetatable(c[a]), meta)
|
|
|
|
assert.equal(getmetatable(b.a), meta)
|
|
assert.equal(getmetatable(c.a), meta)
|
|
|
|
assert.equal(getmetatable(b[a]), getmetatable(c[a]))
|
|
assert.equal(getmetatable(b.a), getmetatable(c.a))
|
|
|
|
assert.equal(getmetatable(b[a].a), meta2)
|
|
assert.equal(getmetatable(c[a].a), meta2)
|
|
assert.equal(getmetatable(b[a].a), getmetatable(c[a].a))
|
|
|
|
assert.equal(getmetatable(b.a.a), meta2)
|
|
assert.equal(getmetatable(c.a.a), meta2)
|
|
assert.equal(getmetatable(b.a.a), getmetatable(c.a.a))
|
|
|
|
assert.equal(b[a], c[a])
|
|
assert.equal(b.a, c.a)
|
|
assert.equal(b[a].a, c[a].a)
|
|
assert.equal(b.a.a, c.a.a)
|
|
assert.equal(b[a].a.a, c[a].a.a)
|
|
assert.equal(b.a.a.a, c.a.a.a)
|
|
|
|
local key_found
|
|
for k in pairs(b) do
|
|
key_found = nil
|
|
for k2 in pairs(c) do
|
|
if k == k2 then
|
|
key_found = true
|
|
break
|
|
end
|
|
end
|
|
assert.is_true(key_found)
|
|
end
|
|
|
|
key_found = nil
|
|
for k in pairs(b) do
|
|
if k == a then
|
|
key_found = true
|
|
break
|
|
end
|
|
end
|
|
assert.is_true(key_found)
|
|
|
|
key_found = nil
|
|
for k in pairs(c) do
|
|
if k == a then
|
|
key_found = true
|
|
break
|
|
end
|
|
end
|
|
assert.is_true(key_found)
|
|
|
|
assert.equal(count_keys(b), 24)
|
|
assert.equal(count_keys(c), 24)
|
|
end)
|
|
end)
|
|
|
|
describe("deep_merge(t1, t2)", function()
|
|
it("deep merges t2 into copy of t1", function()
|
|
local meta = {}
|
|
local ref = setmetatable({
|
|
a = "ref",
|
|
}, meta)
|
|
|
|
local t1 = {
|
|
a = "t1",
|
|
b = {
|
|
a = ref,
|
|
},
|
|
c = {
|
|
a = "t1",
|
|
},
|
|
}
|
|
|
|
local t2 = {
|
|
a = "t2",
|
|
b = {
|
|
a = ref,
|
|
b = "t2",
|
|
},
|
|
c = "t2",
|
|
}
|
|
|
|
local t3 = kong_table.deep_merge(t1, t2)
|
|
|
|
assert.not_equal(t3, t1)
|
|
assert.not_equal(t3, t2)
|
|
|
|
assert.same({
|
|
a = "t2",
|
|
b = {
|
|
a = ref,
|
|
b = "t2",
|
|
},
|
|
c = "t2",
|
|
}, t3)
|
|
|
|
assert.not_equal(meta, getmetatable(t3.b.a))
|
|
assert.is_table(getmetatable(t3.b.a))
|
|
end)
|
|
end)
|
|
|
|
describe("cycle_aware_deep_merge(t1, t2)", function()
|
|
it("cycle aware deep merges t2 into copy of t1", function()
|
|
local meta = {}
|
|
local ref = setmetatable({
|
|
a = "ref",
|
|
}, meta)
|
|
|
|
local t1 = {
|
|
a = "t1",
|
|
b = {
|
|
a = ref,
|
|
},
|
|
c = {
|
|
a = "t1",
|
|
},
|
|
}
|
|
|
|
local t2 = {
|
|
a = "t2",
|
|
b = {
|
|
a = ref,
|
|
b = "t2",
|
|
},
|
|
c = "t2",
|
|
}
|
|
|
|
local t3 = kong_table.cycle_aware_deep_merge(t1, t2)
|
|
|
|
assert.not_equal(t3, t1)
|
|
assert.not_equal(t3, t2)
|
|
|
|
assert.same({
|
|
a = "t2",
|
|
b = {
|
|
a = ref,
|
|
b = "t2",
|
|
},
|
|
c = "t2",
|
|
}, t3)
|
|
|
|
assert.equal(meta, getmetatable(t3.b.a))
|
|
end)
|
|
end)
|
|
|
|
describe("table_path(t, path)", function()
|
|
local t = {
|
|
x = 1,
|
|
a = {
|
|
b = {
|
|
c = 200
|
|
},
|
|
},
|
|
z = 2
|
|
}
|
|
|
|
it("retrieves value from table based on path - single level", function()
|
|
local path = { "x" }
|
|
|
|
assert.equal(1, kong_table.table_path(t, path))
|
|
end)
|
|
|
|
it("retrieves value from table based on path - deep value", function()
|
|
local path = { "a", "b", "c" }
|
|
|
|
assert.equal(200, kong_table.table_path(t, path))
|
|
end)
|
|
|
|
it("returns nil if element is not found - leaf not found", function()
|
|
local path = { "a", "b", "x" }
|
|
|
|
assert.equal(nil, kong_table.table_path(t, path))
|
|
end)
|
|
|
|
it("returns nil if element is not found - root branch not found", function()
|
|
local path = { "o", "j", "k" }
|
|
|
|
assert.equal(nil, kong_table.table_path(t, path))
|
|
end)
|
|
end)
|
|
|
|
it("test parse_directive_header function", function()
|
|
-- test null
|
|
assert.same(tools_http.parse_directive_header(nil), {})
|
|
|
|
-- test empty string
|
|
assert.same(tools_http.parse_directive_header(""), {})
|
|
|
|
-- test string
|
|
assert.same(tools_http.parse_directive_header("cache-key=kong-cache,cache-age=300"), {
|
|
["cache-age"] = 300,
|
|
["cache-key"] = "kong-cache",
|
|
})
|
|
end)
|
|
|
|
it("test calculate_resource_ttl function", function()
|
|
-- test max-age header
|
|
_G.ngx = {
|
|
var = {
|
|
sent_http_expires = "60",
|
|
},
|
|
}
|
|
local access_control_header = tools_http.parse_directive_header("cache-key=kong-cache,max-age=300")
|
|
|
|
assert.same(tools_http.calculate_resource_ttl(access_control_header), 300)
|
|
|
|
-- test s-maxage header
|
|
_G.ngx = {
|
|
var = {
|
|
sent_http_expires = "60",
|
|
},
|
|
}
|
|
local access_control_header = tools_http.parse_directive_header("cache-key=kong-cache,s-maxage=310")
|
|
|
|
assert.same(tools_http.calculate_resource_ttl(access_control_header), 310)
|
|
|
|
-- test empty headers
|
|
local expiry_year = os.date("%Y") + 1
|
|
_G.ngx = {
|
|
var = {
|
|
sent_http_expires = os.date("!%a, %d %b ") .. expiry_year .. " " .. os.date("!%X GMT") -- format: "Thu, 18 Nov 2099 11:27:35 GMT",
|
|
},
|
|
}
|
|
|
|
-- chop the last digit to avoid flaky tests (clock skew)
|
|
assert.same(string.sub(tools_http.calculate_resource_ttl(), 0, -2), "3153600")
|
|
end)
|
|
end)
|