kong/spec/02-integration/04-admin_api/09-routes_routes_spec.lua (1,744 lines of code) (raw):
local cjson = require "cjson"
local utils = require "kong.tools.utils"
local helpers = require "spec.helpers"
local Errors = require "kong.db.errors"
local unindent = helpers.unindent
local function it_content_types(title, fn)
local test_form_encoded = fn("application/x-www-form-urlencoded")
local test_multipart = fn("multipart/form-data")
local test_json = fn("application/json")
it(title .. " with application/www-form-urlencoded", test_form_encoded)
it(title .. " with multipart/form-data", test_multipart)
it(title .. " with application/json", test_json)
end
for _, strategy in helpers.each_strategy() do
describe("Admin API #" .. strategy, function()
local bp
local db
local client
lazy_setup(function()
bp, db = helpers.get_db_utils(strategy, {
"routes",
"services",
})
assert(helpers.start_kong({
database = strategy,
}))
end)
lazy_teardown(function()
helpers.stop_kong(nil, true)
end)
before_each(function()
client = assert(helpers.admin_client())
end)
after_each(function()
if client then
client:close()
end
end)
describe("/routes", function()
describe("OPTIONS", function()
it("returns allow and CORS headers with OPTIONS method", function()
local res = assert(client:send {
method = "OPTIONS",
path = "/routes"
})
local body = assert.res_status(204, res)
assert.equal("", body)
assert.equal("GET, HEAD, OPTIONS, POST", res.headers["Allow"])
assert.equal("GET, HEAD, OPTIONS, POST", res.headers["Access-Control-Allow-Methods"])
assert.equal("Content-Type", res.headers["Access-Control-Allow-Headers"])
assert.equal("*", res.headers["Access-Control-Allow-Origin"])
assert.not_nil(res.headers["X-Kong-Admin-Latency"])
end)
end)
describe("POST", function()
it_content_types("creates a route", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local res = client:post("/routes", {
body = {
protocols = { "http" },
hosts = { "my.route.com" },
headers = { location = { "my-location" } },
service = bp.services:insert(),
},
headers = { ["Content-Type"] = content_type }
})
local body = assert.res_status(201, res)
local json = cjson.decode(body)
assert.same({ "my.route.com" }, json.hosts)
assert.same({ location = { "my-location" } }, json.headers)
assert.is_number(json.created_at)
assert.is_number(json.regex_priority)
assert.is_string(json.id)
assert.equals(cjson.null, json.name)
assert.equals(cjson.null, json.paths)
assert.False(json.preserve_host)
assert.True(json.strip_path)
end
end)
it_content_types("creates a route #grpc", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local res = client:post("/routes", {
body = {
protocols = { "grpc", "grpcs" },
hosts = { "my.route.com" },
service = bp.services:insert(),
},
headers = { ["Content-Type"] = content_type }
})
local body = assert.res_status(201, res)
local json = cjson.decode(body)
assert.same({ "my.route.com" }, json.hosts)
assert.is_number(json.created_at)
assert.is_number(json.regex_priority)
assert.is_string(json.id)
assert.equals(cjson.null, json.name)
assert.equals(cjson.null, json.paths)
assert.False(json.preserve_host)
assert.False(json.strip_path)
assert.same({ "grpc", "grpcs" }, json.protocols)
end
end)
it_content_types("creates a route without service", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local res = client:post("/routes", {
body = {
protocols = { "http" },
hosts = { "my.route.com" },
},
headers = { ["Content-Type"] = content_type }
})
local body = assert.res_status(201, res)
local json = cjson.decode(body)
assert.same({ "my.route.com" }, json.hosts)
assert.is_number(json.created_at)
assert.is_number(json.regex_priority)
assert.is_string(json.id)
assert.equals(cjson.null, json.name)
assert.equals(cjson.null, json.paths)
assert.equals(cjson.null, json.service)
assert.False(json.preserve_host)
assert.True(json.strip_path)
end
end)
it_content_types("creates a route without service #grpc", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local res = client:post("/routes", {
body = {
protocols = { "grpc", "grpcs" },
hosts = { "my.route.com" },
},
headers = { ["Content-Type"] = content_type }
})
local body = assert.res_status(201, res)
local json = cjson.decode(body)
assert.same({ "my.route.com" }, json.hosts)
assert.is_number(json.created_at)
assert.is_number(json.regex_priority)
assert.is_string(json.id)
assert.equals(cjson.null, json.name)
assert.equals(cjson.null, json.paths)
assert.equals(cjson.null, json.service)
assert.False(json.preserve_host)
assert.False(json.strip_path)
assert.same({ "grpc", "grpcs" }, json.protocols)
end
end)
it_content_types("creates a complex route", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local s = bp.services:insert()
local res = client:post("/routes", {
body = {
protocols = { "http" },
methods = { "GET", "POST", "PATCH" },
hosts = { "foo.api.com", "bar.api.com" },
paths = { "/foo", "/bar" },
service = { id = s.id },
},
headers = { ["Content-Type"] = content_type }
})
local body = assert.res_status(201, res)
local json = cjson.decode(body)
assert.same({ "foo.api.com", "bar.api.com" }, json.hosts)
assert.same({ "/foo","/bar" }, json.paths)
assert.same({ "GET", "POST", "PATCH" }, json.methods)
assert.same(s.id, json.service.id)
end
end)
it_content_types("creates a complex route #grpc", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local s = bp.services:insert()
local res = client:post("/routes", {
body = {
protocols = { "grpc", "grpcs" },
hosts = { "foo.api.com", "bar.api.com" },
paths = { "/foo", "/bar" },
service = { id = s.id },
},
headers = { ["Content-Type"] = content_type }
})
local body = assert.res_status(201, res)
local json = cjson.decode(body)
assert.same({ "foo.api.com", "bar.api.com" }, json.hosts)
assert.same({ "/foo","/bar" }, json.paths)
assert.same(s.id, json.service.id)
assert.same({ "grpc", "grpcs"}, json.protocols)
end
end)
it_content_types("creates a complex route by referencing a service by name", function(content_type, name)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local s = bp.named_services:insert()
local res = client:post("/routes", {
body = {
protocols = { "http" },
methods = { "GET", "POST", "PATCH" },
hosts = { "foo.api.com", "bar.api.com" },
paths = { "/foo", "/bar" },
service = { name = s.name },
},
headers = { ["Content-Type"] = content_type }
})
local body = assert.res_status(201, res)
local json = cjson.decode(body)
assert.same({ "foo.api.com", "bar.api.com" }, json.hosts)
assert.same({ "/foo","/bar" }, json.paths)
assert.same({ "GET", "POST", "PATCH" }, json.methods)
assert.same(s.id, json.service.id)
end
end)
it_content_types("creates a complex route by referencing a service by name #grpc", function(content_type, name)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local s = bp.named_services:insert()
local res = client:post("/routes", {
body = {
protocols = { "grpc", "grpcs" },
hosts = { "foo.api.com", "bar.api.com" },
paths = { "/foo", "/bar" },
service = { name = s.name },
},
headers = { ["Content-Type"] = content_type }
})
local body = assert.res_status(201, res)
local json = cjson.decode(body)
assert.same({ "foo.api.com", "bar.api.com" }, json.hosts)
assert.same({ "/foo","/bar" }, json.paths)
assert.same(s.id, json.service.id)
assert.same({ "grpc", "grpcs"}, json.protocols)
end
end)
describe("errors", function()
it("handles malformed JSON body", function()
local res = client:post("/routes", {
body = '{"hello": "world"',
headers = { ["Content-Type"] = "application/json" }
})
local body = assert.res_status(400, res)
assert.equal('{"message":"Cannot parse JSON body"}', body)
end)
it_content_types("handles invalid input", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
-- Missing params
local res = client:post("/routes", {
body = {},
headers = { ["Content-Type"] = content_type }
})
local body = assert.res_status(400, res)
assert.same({
code = Errors.codes.SCHEMA_VIOLATION,
name = "schema violation",
message = unindent([[
schema violation
(must set one of 'methods', 'hosts', 'headers', 'paths', 'snis' when 'protocols' is 'https')
]], true, true),
fields = {
["@entity"] = {
"must set one of 'methods', 'hosts', 'headers', 'paths', 'snis' when 'protocols' is 'https'",
}
}
}, cjson.decode(body))
-- Missing https params
res = client:post("/routes", {
body = {
protocols = { "https" },
},
headers = { ["content-type"] = content_type }
})
body = assert.res_status(400, res)
assert.same({
code = Errors.codes.SCHEMA_VIOLATION,
name = "schema violation",
message = unindent([[
schema violation
(must set one of 'methods', 'hosts', 'headers', 'paths', 'snis' when 'protocols' is 'https')
]], true, true),
fields = {
["@entity"] = {
"must set one of 'methods', 'hosts', 'headers', 'paths', 'snis' when 'protocols' is 'https'"
}
}
}, cjson.decode(body))
-- Invalid parameter
res = client:post("/routes", {
body = {
methods = { "GET" },
protocols = { "foo", "http" },
},
headers = { ["Content-Type"] = content_type }
})
body = assert.res_status(400, res)
assert.same({
code = Errors.codes.SCHEMA_VIOLATION,
name = "schema violation",
message = "schema violation " ..
"(protocols.1: expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp)",
fields = {
protocols = { "expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp" },
}
}, cjson.decode(body))
-- Invalid foreign entity
res = client:post("/routes", {
body = {
methods = { "GET" },
protocols = { "foo", "http" },
service = { protocol = "foo" },
},
headers = { ["Content-Type"] = content_type }
})
body = assert.res_status(400, res)
assert.same({
code = Errors.codes.SCHEMA_VIOLATION,
name = "schema violation",
message = "2 schema violations " ..
"(protocols.1: expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp; " ..
"service.protocol: expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp)",
fields = {
protocols = { "expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp" },
service = {
protocol = "expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp"
}
}
}, cjson.decode(body))
-- Invalid foreign entity reference
res = client:post("/routes", {
body = {
methods = { "GET" },
service = { name = "non-existing" },
},
headers = { ["Content-Type"] = content_type }
})
body = assert.res_status(400, res)
assert.same({
code = Errors.codes.FOREIGN_KEYS_UNRESOLVED,
name = "foreign keys unresolved",
message = [[foreign key unresolved (service.name: the foreign key cannot be resolved with ]] ..
[['{name="non-existing"}' for an existing 'services' entity)]],
fields = {
service = {
name = [[the foreign key cannot be resolved with '{name="non-existing"}' ]] ..
[[for an existing 'services' entity]]
}
}
}, cjson.decode(body))
local service_name = content_type == "application/json" and cjson.null or ""
-- Invalid foreign entity reference
res = client:post("/routes", {
body = {
methods = { "GET" },
service = { name = service_name },
},
headers = { ["Content-Type"] = content_type }
})
body = assert.res_status(400, res)
assert.same({
code = Errors.codes.SCHEMA_VIOLATION,
name = "schema violation",
message = "schema violation " ..
"(service.id: missing primary key)",
fields = {
service = {
id = "missing primary key"
}
}
}, cjson.decode(body))
-- Foreign entity cannot be resolved
res = client:post("/routes", {
body = {
methods = { "GET" },
service = { protocol = "http" },
},
headers = { ["Content-Type"] = content_type }
})
body = assert.res_status(400, res)
assert.same({
code = Errors.codes.SCHEMA_VIOLATION,
name = "schema violation",
message = "schema violation " ..
"(service.id: missing primary key)",
fields = {
service = {
id = "missing primary key"
}
}
}, cjson.decode(body))
end
end)
end)
end)
describe("GET", function()
describe("with data", function()
lazy_setup(function()
db:truncate("routes")
for i = 1, 10 do
bp.routes:insert({ paths = { "/route-" .. i } })
end
end)
it("retrieves the first page", function()
local res = assert(client:send {
method = "GET",
path = "/routes"
})
local res = assert.res_status(200, res)
local json = cjson.decode(res)
assert.equal(10, #json.data)
end)
it("paginates a set", function()
local pages = {}
local offset
for i = 1, 4 do
local res = assert(client:send {
method = "GET",
path = "/routes",
query = { size = 3, offset = offset }
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
if i < 4 then
assert.equal(3, #json.data)
else
assert.equal(1, #json.data)
end
if i > 1 then
-- check all pages are different
assert.not_same(pages[i-1], json)
end
offset = json.offset
pages[i] = json
end
end)
end)
describe("with no data", function()
lazy_setup(function()
db:truncate("routes")
end)
it("data property is an empty array and not an empty hash", function()
local res = assert(client:send {
method = "GET",
path = "/routes"
})
local body = assert.res_status(200, res)
assert.matches('"data":%[%]', body)
local json = cjson.decode(body)
assert.same({ data = {}, next = cjson.null }, json)
end)
end)
describe("errors", function()
it("handles invalid filters", function()
local res = assert(client:send {
method = "GET",
path = "/routes",
query = {foo = "bar"}
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.same({ data = {}, next = cjson.null }, json)
end)
it("handles invalid size", function()
local res = client:get("/routes", { query = { size = "x" } })
local body = assert.res_status(400, res)
assert.same({
code = Errors.codes.INVALID_SIZE,
name = "invalid size",
message = "size must be a number"
}, cjson.decode(body))
res = client:get("/routes", { query = { size = "potato" } })
body = assert.res_status(400, res)
local json = cjson.decode(body)
json.message = nil
assert.same({
code = Errors.codes.INVALID_SIZE,
name = "invalid size",
}, json)
end)
it("handles invalid offsets", function()
local res = client:get("/routes", { query = { offset = "x" } })
local body = assert.res_status(400, res)
assert.same({
code = Errors.codes.INVALID_OFFSET,
name = "invalid offset",
message = "'x' is not a valid offset: bad base64 encoding"
}, cjson.decode(body))
res = client:get("/routes", { query = { offset = "|potato|" } })
body = assert.res_status(400, res)
local json = cjson.decode(body)
json.message = nil
assert.same({
code = Errors.codes.INVALID_OFFSET,
name = "invalid offset",
}, json)
end)
it("ignores an invalid body", function()
local res = assert(client:send {
method = "GET",
path = "/routes",
body = "this fails if decoded as json",
headers = {
["Content-Type"] = "application/json",
}
})
assert.res_status(200, res)
end)
end)
end)
it("returns HTTP 405 on invalid method", function()
local methods = { "DELETE", "PUT", "PATCH" }
for i = 1, #methods do
local res = assert(client:send {
method = methods[i],
path = "/routes",
body = {}, -- tmp: body to allow POST/PUT to work
headers = {
["Content-Type"] = "application/json"
}
})
local body = assert.response(res).has.status(405)
local json = cjson.decode(body)
assert.same({ message = "Method not allowed" }, json)
end
end)
describe("/routes/{route}", function()
describe("OPTIONS", function()
it("returns allow and CORS headers with OPTIONS method", function()
local res = assert(client:send {
method = "OPTIONS",
path = "/routes/test"
})
local body = assert.res_status(204, res)
assert.equal("", body)
assert.equal("DELETE, GET, HEAD, OPTIONS, PATCH, PUT", res.headers["Allow"])
assert.equal("DELETE, GET, HEAD, OPTIONS, PATCH, PUT", res.headers["Access-Control-Allow-Methods"])
assert.equal("Content-Type", res.headers["Access-Control-Allow-Headers"])
assert.equal("*", res.headers["Access-Control-Allow-Origin"])
assert.not_nil(res.headers["X-Kong-Admin-Latency"])
end)
end)
describe("GET", function()
it("retrieves by id", function()
local route = bp.routes:insert({ paths = { "/my-route" } }, { nulls = true })
local res = client:get("/routes/" .. route.id)
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.same(route, json)
end)
it("retrieves by name", function()
local route = bp.named_routes:insert(nil, { nulls = true })
local res = client:get("/routes/" .. route.name)
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.same(route, json)
end)
it("retrieves by utf-8 name and percent-escaped utf-8 name", function()
local route = bp.routes:insert({ methods = {"GET"}, name = "円" }, { nulls = true })
local res = client:get("/routes/" .. route.name)
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.same(route, json)
res = client:get("/routes/%E5%86%86")
body = assert.res_status(200, res)
json = cjson.decode(body)
assert.same(route, json)
end)
it("returns 404 if not found", function()
local res = client:get("/routes/" .. utils.uuid())
assert.res_status(404, res)
end)
it("returns 404 if not found by name", function()
local res = client:get("/routes/not-found")
assert.res_status(404, res)
end)
it("ignores an invalid body", function()
local route = bp.routes:insert({ paths = { "/my-route" } })
local res = client:get("/routes/" .. route.id, {
headers = {
["Content-Type"] = "application/json"
},
body = "this fails if decoded as json",
})
assert.res_status(200, res)
end)
it("ignores an invalid body by name", function()
local route = bp.named_routes:insert()
local res = client:get("/routes/" .. route.name, {
headers = {
["Content-Type"] = "application/json"
},
body = "this fails if decoded as json",
})
assert.res_status(200, res)
end)
end)
describe("PUT", function()
it_content_types("creates if not found", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local service = bp.services:insert()
local id = utils.uuid()
local res = client:put("/routes/" .. id, {
headers = {
["Content-Type"] = content_type
},
body = {
paths = { "/updated-paths" },
service = service
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.same({ "/updated-paths" }, json.paths)
assert.same(cjson.null, json.hosts)
assert.same(cjson.null, json.methods)
assert.equal(id, json.id)
local in_db = assert(db.routes:select({ id = id }, { nulls = true }))
assert.same(json, in_db)
end
end)
it_content_types("creates without service if not found", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local id = utils.uuid()
local res = client:put("/routes/" .. id, {
headers = {
["Content-Type"] = content_type
},
body = {
paths = { "/updated-paths" },
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.same({ "/updated-paths" }, json.paths)
assert.same(cjson.null, json.hosts)
assert.same(cjson.null, json.methods)
assert.same(cjson.null, json.service)
assert.equal(id, json.id)
local in_db = assert(db.routes:select({ id = id }, { nulls = true }))
assert.same(json, in_db)
end
end)
it_content_types("creates if not found by name", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local service = bp.services:insert()
local name = "my-route"
local res = client:put("/routes/" .. name, {
headers = {
["Content-Type"] = content_type
},
body = {
paths = { "/updated-paths" },
service = service
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.same({ "/updated-paths" }, json.paths)
assert.same(cjson.null, json.hosts)
assert.same(cjson.null, json.methods)
assert.equal(name, json.name)
local in_db = assert(db.routes:select_by_name(name, { nulls = true }))
assert.same(json, in_db)
end
end)
it_content_types("updates if found", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local route = bp.routes:insert({ paths = { "/my-route" } })
local res = client:put("/routes/" .. route.id, {
headers = {
["Content-Type"] = content_type
},
body = {
paths = { "/updated-paths" },
service = route.service
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.same({ "/updated-paths" }, json.paths)
assert.same(cjson.null, json.hosts)
assert.same(cjson.null, json.methods)
assert.equal(route.id, json.id)
local in_db = assert(db.routes:select({ id = route.id }, { nulls = true }))
assert.same(json, in_db)
end
end)
it_content_types("updates if found by name", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local route = bp.routes:insert({
name = "my-put-route",
paths = { "/my-route" }
})
local res = client:put("/routes/my-put-route", {
headers = {
["Content-Type"] = content_type
},
body = {
paths = { "/updated-paths" },
service = route.service
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.same({ "/updated-paths" }, json.paths)
assert.same(cjson.null, json.hosts)
assert.same(cjson.null, json.methods)
assert.equal(route.id, json.id)
assert.equal(route.name, json.name)
local in_db = assert(db.routes:select_by_name(route.name, { nulls = true }))
assert.same(json, in_db)
db.routes:delete({ id = route.id })
end
end)
it_content_types("handles same parameter in url and params gracefully", function(content_type)
return function()
local res = client:put("/routes/my-put-route", {
headers = {
["Content-Type"] = content_type
},
body = {
routes = {
test = {
{
test = "test",
}
}
},
tags = "test",
},
})
assert.res_status(400, res)
end
end)
describe("errors", function()
it("handles malformed JSON body", function()
local route = bp.routes:insert({ paths = { "/my-route" } })
local res = client:put("/routes/" .. route.id, {
body = '{"hello": "world"',
headers = { ["Content-Type"] = "application/json" }
})
local body = assert.res_status(400, res)
assert.equal('{"message":"Cannot parse JSON body"}', body)
end)
it_content_types("handles invalid input", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
-- Missing params
local res = client:put("/routes/" .. utils.uuid(), {
body = {},
headers = { ["Content-Type"] = content_type }
})
local body = assert.res_status(400, res)
assert.same({
code = Errors.codes.SCHEMA_VIOLATION,
name = "schema violation",
message = unindent([[
schema violation
(must set one of 'methods', 'hosts', 'headers', 'paths', 'snis' when 'protocols' is 'https')
]], true, true),
fields = {
["@entity"] = {
"must set one of 'methods', 'hosts', 'headers', 'paths', 'snis' when 'protocols' is 'https'"
}
}
}, cjson.decode(body))
-- Invalid parameter
res = client:put("/routes/" .. utils.uuid(), {
body = {
methods = { "GET" },
protocols = { "foo", "http" },
},
headers = { ["Content-Type"] = content_type }
})
body = assert.res_status(400, res)
assert.same({
code = Errors.codes.SCHEMA_VIOLATION,
name = "schema violation",
message = "schema violation " ..
"(protocols.1: expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp)",
fields = {
protocols = { "expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp" },
}
}, cjson.decode(body))
local route = bp.routes:insert({ paths = { "/my-route" } })
local res = client:put("/routes/" .. route.id, {
headers = {
["Content-Type"] = content_type
},
body = {
service = route.service,
paths = { "/" },
regex_priority = "foobar",
},
})
local body = assert.res_status(400, res)
assert.same({
code = Errors.codes.SCHEMA_VIOLATION,
name = "schema violation",
message = "schema violation (regex_priority: expected an integer)",
fields = {
regex_priority = "expected an integer"
},
}, cjson.decode(body))
end
end)
it_content_types("handles invalid input #grpc", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
-- Missing grpc/grpcs routing attributes
local res = client:post("/routes", {
body = {
protocols = { "grpc", "grpcs" },
},
headers = { ["Content-Type"] = content_type }
})
local body = assert.res_status(400, res)
assert.same({
code = Errors.codes.SCHEMA_VIOLATION,
name = "schema violation",
message = unindent([[
schema violation
(must set one of 'hosts', 'headers', 'paths', 'snis' when 'protocols' is 'grpcs')
]], true, true),
fields = {
["@entity"] = {
"must set one of 'hosts', 'headers', 'paths', 'snis' when 'protocols' is 'grpcs'",
}
}
}, cjson.decode(body))
-- Doesn't accept 'strip_path' attribute
local res = client:post("/routes", {
body = {
protocols = { "grpc", "grpcs" },
paths = { "/" },
strip_path = true,
},
headers = { ["Content-Type"] = content_type }
})
local body = assert.res_status(400, res)
assert.same({
code = Errors.codes.SCHEMA_VIOLATION,
name = "schema violation",
message = unindent([[
schema violation (strip_path: cannot set 'strip_path' when 'protocols' is 'grpc' or 'grpcs')
]], true, true),
fields = {
strip_path = "cannot set 'strip_path' when 'protocols' is 'grpc' or 'grpcs'",
}
}, cjson.decode(body))
-- Doesn't accept 'methods' attribute
local res = client:post("/routes", {
body = {
protocols = { "grpc", "grpcs" },
paths = { "/" },
methods = { "GET" }
},
headers = { ["Content-Type"] = content_type }
})
local body = assert.res_status(400, res)
assert.same({
code = Errors.codes.SCHEMA_VIOLATION,
name = "schema violation",
message = unindent([[
schema violation (methods: cannot set 'methods' when 'protocols' is 'grpc' or 'grpcs')
]], true, true),
fields = {
methods = "cannot set 'methods' when 'protocols' is 'grpc' or 'grpcs'",
}
}, cjson.decode(body))
end
end)
end)
end)
describe("PATCH", function()
it_content_types("updates if found", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local route = bp.routes:insert({ paths = { "/my-route" } })
local res = client:patch("/routes/" .. route.id, {
headers = {
["Content-Type"] = content_type
},
body = {
methods = cjson.null,
hosts = cjson.null,
paths = { "/updated-paths" },
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.same({ "/updated-paths" }, json.paths)
assert.same(cjson.null, json.hosts)
assert.same(cjson.null, json.methods)
assert.equal(route.id, json.id)
local in_db = assert(db.routes:select({ id = route.id }, { nulls = true }))
assert.same(json, in_db)
end
end)
it_content_types("updates if found by name", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local route = bp.routes:insert({
name = "my-patch-route",
paths = { "/my-route" },
})
local res = client:patch("/routes/my-patch-route", {
headers = {
["Content-Type"] = content_type
},
body = {
methods = cjson.null,
hosts = cjson.null,
paths = { "/updated-paths" },
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.same({ "/updated-paths" }, json.paths)
assert.same(cjson.null, json.hosts)
assert.same(cjson.null, json.methods)
assert.equal(route.id, json.id)
local in_db = assert(db.routes:select({ id = route.id }, { nulls = true }))
assert.same(json, in_db)
db.routes:delete({ id = route.id })
end
end)
it_content_types("updates strip_path if not previously set", function(content_type)
return function()
local route = bp.routes:insert({ paths = { "/my-route" } })
local res = client:patch("/routes/" .. route.id, {
headers = {
["Content-Type"] = content_type
},
body = {
strip_path = true
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.True(json.strip_path)
assert.equal(route.id, json.id)
local in_db = assert(db.routes:select({id = route.id}, { nulls = true }))
assert.same(json, in_db)
end
end)
it_content_types("updates multiple fields at once", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local route = bp.routes:insert({ paths = { "/my-route" } })
local res = client:patch("/routes/" .. route.id, {
headers = {
["Content-Type"] = content_type
},
body = {
methods = cjson.null,
paths = { "/my-updated-path" },
hosts = { "my-updated.tld" },
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.same({ "/my-updated-path" }, json.paths)
assert.same({ "my-updated.tld" }, json.hosts)
assert.same(cjson.null, json.methods)
assert.equal(route.id, json.id)
local in_db = assert(db.routes:select({id = route.id}, { nulls = true }))
assert.same(json, in_db)
end
end)
it("with application/json removes optional field with ngx.null", function()
local route = bp.routes:insert({ paths = { "/my-route" } })
local res = client:patch("/routes/" .. route.id, {
headers = {
["Content-Type"] = "application/json"
},
body = {
methods = cjson.null,
paths = cjson.null,
hosts = { "my-updated.tld" },
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.same(cjson.null, json.paths)
assert.same({ "my-updated.tld" }, json.hosts)
assert.same(cjson.null, json.methods)
assert.equal(route.id, json.id)
local in_db = assert(db.routes:select({id = route.id}, { nulls = true }))
assert.same(json, in_db)
end)
it("allows updating sets and arrays with en empty array", function()
local route = bp.routes:insert({ paths = { "/my-route" } })
local res = client:patch("/routes/" .. route.id, {
headers = {
["Content-Type"] = "application/json"
},
body = {
methods = {},
paths = {},
hosts = { "my-updated.tld" },
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
if strategy == "cassandra" then
assert.equals(ngx.null, json.paths)
assert.equals(ngx.null, json.methods)
else
assert.matches('"methods":%[%]', body)
assert.matches('"paths":%[%]', body)
assert.same({}, json.paths)
assert.same({}, json.methods)
end
assert.same({ "my-updated.tld" }, json.hosts)
assert.equal(route.id, json.id)
end)
it_content_types("removes service association", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local route = bp.routes:insert({
name = "my-patch-route",
paths = { "/my-route" },
})
local res = client:patch("/routes/my-patch-route", {
headers = {
["Content-Type"] = content_type
},
body = {
methods = cjson.null,
hosts = cjson.null,
service = cjson.null,
paths = { "/updated-paths" },
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.same({ "/updated-paths" }, json.paths)
assert.same(cjson.null, json.hosts)
assert.same(cjson.null, json.methods)
assert.same(cjson.null, json.service)
assert.equal(route.id, json.id)
local in_db = assert(db.routes:select({ id = route.id }, { nulls = true }))
assert.same(json, in_db)
db.routes:delete({ id = route.id })
end
end)
describe("errors", function()
it_content_types("returns 404 if not found", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local res = client:patch("/routes/" .. utils.uuid(), {
headers = {
["Content-Type"] = content_type
},
body = {
methods = cjson.null,
hosts = cjson.null,
paths = { "/my-updated-path" },
},
})
assert.res_status(404, res)
end
end)
it_content_types("handles invalid input", function(content_type)
return function()
local route = bp.routes:insert({ paths = { "/my-route" } })
local res = client:patch("/routes/" .. route.id, {
headers = {
["Content-Type"] = content_type
},
body = {
regex_priority = "foobar"
},
})
local body = assert.res_status(400, res)
assert.same({
code = Errors.codes.SCHEMA_VIOLATION,
name = "schema violation",
message = "schema violation (regex_priority: expected an integer)",
fields = {
regex_priority = "expected an integer"
},
}, cjson.decode(body))
end
end)
end)
end)
describe("DELETE", function()
it("deletes a route", function()
local route = bp.routes:insert({ paths = { "/my-route" } })
local res = client:delete("/routes/" .. route.id)
local body = assert.res_status(204, res)
assert.equal("", body)
local in_db, err = db.routes:select({id = route.id}, { nulls = true })
assert.is_nil(err)
assert.is_nil(in_db)
end)
it("deletes a route by name", function()
local route = bp.routes:insert({
name = "my-delete-route",
paths = { "/my-route" }
})
local res = client:delete("/routes/my-delete-route")
local body = assert.res_status(204, res)
assert.equal("", body)
local in_db, err = db.routes:select({id = route.id}, { nulls = true })
assert.is_nil(err)
assert.is_nil(in_db)
end)
describe("errors", function()
it("returns HTTP 204 even if not found", function()
local res = client:delete("/routes/" .. utils.uuid())
assert.res_status(204, res)
end)
end)
end)
end)
describe("/routes/{route}/service", function()
describe("GET", function()
it("retrieves by id", function()
local service = bp.services:insert({ host = "example.com", path = "/" }, { nulls = true })
local route = bp.routes:insert({ paths = { "/my-route" }, service = service })
local res = client:get("/routes/" .. route.id .. "/service")
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.same(service, json)
end)
it("retrieves by name", function()
local service = bp.services:insert({ host = "example.com", path = "/" }, { nulls = true })
bp.routes:insert({ name = "my-get-route", paths = { "/my-route" }, service = service })
local res = client:get("/routes/my-get-route/service")
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.same(service, json)
end)
it("returns 404 if not found", function()
local res = client:get("/routes/" .. utils.uuid() .. "/service")
assert.res_status(404, res)
end)
it("returns 404 if not found by name", function()
local res = client:get("/routes/my-in-existent-route/service")
assert.res_status(404, res)
end)
it("ignores an invalid body", function()
local route = bp.routes:insert({ paths = { "/my-route" } })
local res = client:get("/routes/" .. route.id .. "/service", {
headers = {
["Content-Type"] = "application/json"
},
body = "this fails if decoded as json",
})
assert.res_status(200, res)
end)
end)
describe("PATCH", function()
it_content_types("updates if found", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local service = bp.named_services:insert({ path = "/" })
local route = bp.routes:insert({ paths = { "/my-route" }, service = service })
local edited_name = "name-" .. service.name
local edited_host = "edited-" .. service.host
local res = client:patch("/routes/" .. route.id .. "/service", {
headers = {
["Content-Type"] = content_type
},
body = {
name = edited_name,
host = edited_host,
path = cjson.null,
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.equal(edited_name, json.name)
assert.equal(edited_host, json.host)
assert.same(cjson.null, json.path)
local in_db = assert(db.services:select({ id = service.id }, { nulls = true }))
assert.same(json, in_db)
end
end)
it_content_types("updates if found by name", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local service = bp.named_services:insert({ path = "/" })
local route = bp.routes:insert({ name = "my-service-patch-route", paths = { "/my-route" }, service = service })
local edited_name = "name-" .. service.name
local edited_host = "edited-" .. service.host
local res = client:patch("/routes/my-service-patch-route/service", {
headers = {
["Content-Type"] = content_type
},
body = {
name = edited_name,
host = edited_host,
path = cjson.null,
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.equal(edited_name, json.name)
assert.equal(edited_host, json.host)
assert.same(cjson.null, json.path)
local in_db = assert(db.services:select({ id = service.id }, { nulls = true }))
assert.same(json, in_db)
db.routes:delete({ id = route.id })
db.services:delete({ id = service.id })
end
end)
it_content_types("updates with url", function(content_type)
return function()
local service = bp.services:insert({ host = "example.com", path = "/" })
local route = bp.routes:insert({ paths = { "/my-route" }, service = service })
local res = client:patch("/routes/" .. route.id .. "/service", {
headers = {
["Content-Type"] = content_type
},
body = {
url = "http://edited2.com:1234/foo",
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.equal("edited2.com", json.host)
assert.equal(1234, json.port)
assert.equal("/foo", json.path)
local in_db = assert(db.services:select({ id = service.id }, { nulls = true }))
assert.same(json, in_db)
end
end)
describe("errors", function()
it_content_types("returns 404 if not found", function(content_type)
return function()
local res = client:patch("/routes/" .. utils.uuid() .. "/service", {
headers = {
["Content-Type"] = content_type
},
body = {
name = "edited",
host = "edited.com",
path = cjson.null,
},
})
assert.res_status(404, res)
end
end)
it_content_types("handles invalid input", function(content_type)
return function()
local service = bp.services:insert({ host = "example.com", path = "/" })
local route = bp.routes:insert({ paths = { "/my-route" }, service = service })
local res = client:patch("/routes/" .. route.id .. "/service", {
headers = {
["Content-Type"] = content_type
},
body = {
connect_timeout = "foobar"
},
})
local body = assert.res_status(400, res)
assert.same({
code = Errors.codes.SCHEMA_VIOLATION,
name = "schema violation",
message = "schema violation (connect_timeout: expected an integer)",
fields = {
connect_timeout = "expected an integer",
},
}, cjson.decode(body))
end
end)
end)
end)
describe("DELETE", function()
describe("errors", function()
it("returns HTTP 405 when trying to delete a service that is referenced", function()
local route = bp.routes:insert({ paths = { "/my-route" } })
local res = client:delete("/routes/" .. route.id .. "/service")
local body = assert.res_status(405, res)
assert.same({ message = 'Method not allowed' }, cjson.decode(body))
end)
it("returns HTTP 404 with non-existing route", function()
local res = client:delete("/routes/" .. utils.uuid() .. "/service")
assert.res_status(404, res)
end)
it("returns HTTP 404 with non-existing route by name", function()
local res = client:delete("/routes/in-existent-route/service")
assert.res_status(404, res)
end)
end)
end)
end)
describe("PUT", function()
it_content_types("creates if not found", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local route = bp.routes:insert({ paths = { "/my-route" } })
local res = client:put("/routes/" .. route.id .. "/service", {
headers = {
["Content-Type"] = content_type
},
body = {
url = "http://httpbin.org",
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.same("httpbin.org", json.host)
local in_db = assert(db.services:select({ id = json.id }, { nulls = true }))
assert.same(json, in_db)
end
end)
it_content_types("updates if found", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local service = bp.named_services:insert({ path = "/" })
local route = bp.routes:insert({ paths = { "/my-route" }, service = service })
local edited_name = "name-" .. service.name
local edited_host = "edited-" .. service.host
local res = client:put("/routes/" .. route.id .. "/service", {
headers = {
["Content-Type"] = content_type
},
body = {
name = edited_name,
host = edited_host,
path = cjson.null,
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.equal(edited_name, json.name)
assert.equal(edited_host, json.host)
assert.same(cjson.null, json.path)
local in_db = assert(db.services:select({ id = service.id }, { nulls = true }))
assert.same(json, in_db)
end
end)
it_content_types("updates if found by name", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local service = bp.named_services:insert({ path = "/" })
local route = bp.routes:insert({ name = "my-service-patch-route", paths = { "/my-route" }, service = service })
local edited_name = "name-" .. service.name
local edited_host = "edited-" .. service.host
local res = client:put("/routes/my-service-patch-route/service", {
headers = {
["Content-Type"] = content_type
},
body = {
name = edited_name,
host = edited_host,
path = cjson.null,
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.equal(edited_name, json.name)
assert.equal(edited_host, json.host)
assert.same(cjson.null, json.path)
local in_db = assert(db.services:select({ id = service.id }, { nulls = true }))
assert.same(json, in_db)
db.routes:delete({ id = route.id })
db.services:delete({ id = service.id })
end
end)
it_content_types("updates with url", function(content_type)
return function()
local service = bp.services:insert({ host = "example.com", path = "/" })
local route = bp.routes:insert({ paths = { "/my-route" }, service = service })
local res = client:put("/routes/" .. route.id .. "/service", {
headers = {
["Content-Type"] = content_type
},
body = {
url = "http://edited2.com:1234/foo",
},
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.equal("edited2.com", json.host)
assert.equal(1234, json.port)
assert.equal("/foo", json.path)
local in_db = assert(db.services:select({ id = service.id }, { nulls = true }))
assert.same(json, in_db)
end
end)
describe("errors", function()
it_content_types("returns 404 if not found", function(content_type)
return function()
local res = client:put("/routes/" .. utils.uuid() .. "/service", {
headers = {
["Content-Type"] = content_type
},
body = {
name = "edited",
host = "edited.com",
path = cjson.null,
},
})
assert.res_status(404, res)
end
end)
it_content_types("handles invalid input", function(content_type)
return function()
local service = bp.services:insert({ host = "example.com", path = "/" })
local route = bp.routes:insert({ paths = { "/my-route" }, service = service })
local res = client:put("/routes/" .. route.id .. "/service", {
headers = {
["Content-Type"] = content_type
},
body = {
connect_timeout = "foobar"
},
})
local body = assert.res_status(400, res)
assert.same({
code = Errors.codes.SCHEMA_VIOLATION,
name = "schema violation",
message = "2 schema violations (connect_timeout: expected an integer; host: required field missing)",
fields = {
connect_timeout = "expected an integer",
host = "required field missing",
},
}, cjson.decode(body))
end
end)
end)
end)
describe("/routes/{route}/plugins", function()
describe("POST", function()
it_content_types("creates a plugin config on a Route", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local route = bp.routes:insert({ paths = { "/my-route" } })
local bodies = {
["application/x-www-form-urlencoded"] = {
name = "key-auth",
["config.key_names[1]"] = "apikey",
["config.key_names[2]"] = "key",
},
["application/json"] = {
name = "key-auth",
config = {
key_names = { "apikey", "key" },
}
},
}
local res = assert(client:send {
method = "POST",
path = "/routes/" .. route.id .. "/plugins",
body = bodies[content_type],
headers = { ["Content-Type"] = content_type }
})
local body = assert.res_status(201, res)
local json = cjson.decode(body)
assert.equal("key-auth", json.name)
assert.same({ "apikey", "key" }, json.config.key_names)
end
end)
describe("errors", function()
it_content_types("handles invalid input", function(content_type)
return function()
local route = bp.routes:insert({ paths = { "/my-route" } })
local res = assert(client:send {
method = "POST",
path = "/routes/" .. route.id .. "/plugins",
body = {},
headers = { ["Content-Type"] = content_type }
})
local body = assert.res_status(400, res)
local json = cjson.decode(body)
assert.same({
code = 2,
fields = {
name = "required field missing",
},
message = "schema violation (name: required field missing)",
name = "schema violation",
}, json)
end
end)
it_content_types("returns 409 on conflict (same plugin name)", function(content_type)
return function()
local route = bp.routes:insert({ paths = { "/my-route" } })
-- insert initial plugin
local res = assert(client:send {
method = "POST",
path = "/routes/" .. route.id .. "/plugins",
body = {
name = "basic-auth",
},
headers = {["Content-Type"] = content_type}
})
assert.response(res).has.status(201)
assert.response(res).has.jsonbody()
-- do it again, to provoke the error
local res = assert(client:send {
method = "POST",
path = "/routes/" .. route.id .. "/plugins",
body = {
name = "basic-auth",
},
headers = { ["Content-Type"] = content_type }
})
assert.response(res).has.status(409)
local json = assert.response(res).has.jsonbody()
assert.same({
code = 5,
fields = {
consumer = ngx.null,
name = "basic-auth",
route = {
id = route.id,
},
service = ngx.null,
},
message = [[UNIQUE violation detected on '{consumer=null,]] ..
[[name="basic-auth",route={id="]] ..
route.id .. [["},service=null}']],
name = "unique constraint violation",
}, json)
end
end)
-- Cassandra doesn't fail on this because its insert is an upsert
pending("returns 409 on id conflict (same plugin id)", function(content_type)
return function()
local route = bp.routes:insert({ paths = { "/my-route" } })
-- insert initial plugin
local res = assert(client:send {
method = "POST",
path = "/routes/"..route.id.."/plugins",
body = {
name = "basic-auth",
},
headers = {["Content-Type"] = content_type}
})
local body = assert.res_status(201, res)
local plugin = cjson.decode(body)
ngx.sleep(1)
-- do it again, to provoke the error
local conflict_res = assert(client:send {
method = "POST",
path = "/routes/" .. route.id .. "/plugins",
body = {
name = "key-auth",
id = plugin.id,
},
headers = { ["Content-Type"] = content_type }
})
local conflict_body = assert.res_status(409, conflict_res)
local json = cjson.decode(conflict_body)
assert.same({
code = Errors.codes.PRIMARY_KEY_VIOLATION,
fields = {
id = plugin.id,
},
message = [[primary key violation on key '{id="]] ..
plugin.id .. [["}']],
name = "primary key violation",
}, json)
end
end)
end)
end)
describe("GET", function()
it("retrieves the first page", function()
local route = bp.routes:insert({ paths = { "/my-route" } })
assert(db.plugins:insert {
name = "key-auth",
route = { id = route.id },
})
local res = assert(client:send {
method = "GET",
path = "/routes/" .. route.id .. "/plugins"
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.equal(1, #json.data)
end)
it("retrieves the first page by name", function()
local route = bp.routes:insert({ name = "my-plugins-route", paths = { "/my-route" } })
assert(db.plugins:insert {
name = "key-auth",
route = { id = route.id },
})
local res = assert(client:send {
method = "GET",
path = "/routes/my-plugins-route/plugins"
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.equal(1, #json.data)
db.routes:delete({ id = route.id })
end)
it("ignores an invalid body", function()
local route = bp.routes:insert({ paths = { "/my-route" } })
local res = assert(client:send {
method = "GET",
path = "/routes/" .. route.id .. "/plugins",
body = "this fails if decoded as json",
headers = {
["Content-Type"] = "application/json",
}
})
assert.res_status(200, res)
end)
end)
end)
end)
end)
describe("Admin API Override #" .. strategy, function()
local bp
local db
local client
lazy_setup(function()
bp, db = helpers.get_db_utils(strategy, {
"routes",
"services",
}, {
"api-override",
})
assert(helpers.start_kong({
database = strategy,
plugins = "bundled,api-override",
nginx_conf = "spec/fixtures/custom_nginx.template",
}))
end)
lazy_teardown(function()
helpers.stop_kong(nil, true)
end)
before_each(function()
client = assert(helpers.admin_client())
end)
after_each(function()
if client then
client:close()
end
end)
describe("/routes", function()
describe("POST", function()
it_content_types("creates a route", function(content_type)
return function()
if content_type == "multipart/form-data" then
-- the client doesn't play well with this
return
end
local res = client:post("/routes", {
body = {
protocols = { "http" },
hosts = { "my.route.com" },
headers = { location = { "my-location" } },
service = bp.services:insert(),
},
headers = { ["Content-Type"] = content_type }
})
local body = assert.res_status(201, res)
local json = cjson.decode(body)
assert.same({ "my.route.com" }, json.hosts)
assert.same({ location = { "my-location" } }, json.headers)
assert.is_number(json.created_at)
assert.is_number(json.regex_priority)
assert.is_string(json.id)
assert.equals(cjson.null, json.name)
assert.equals(cjson.null, json.paths)
assert.False(json.preserve_host)
assert.True(json.strip_path)
assert.equal("ok", res.headers["Kong-Api-Override"])
end
end)
end)
describe("GET", function()
describe("with data", function()
lazy_setup(function()
db:truncate("services")
db:truncate("routes")
for i = 1, 10 do
bp.routes:insert({ paths = { "/route-" .. i } })
end
end)
it("retrieves the first page", function()
local res = assert(client:send {
method = "GET",
path = "/routes"
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.equal(10, #json.data)
assert.equal("ok", res.headers["Kong-Api-Override"])
local res = assert(client:send {
method = "GET",
path = "/services"
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
assert.equal(10, #json.data)
assert.equal("ok", res.headers["Kong-Api-Override"])
end)
end)
end)
end)
end)
end