kong/spec/01-unit/16-runloop_handler_spec.lua (196 lines of code) (raw):
local mocker = require("spec.fixtures.mocker")
local function setup_it_block()
-- keep track of created semaphores
local semaphores = {}
local my_cache = {}
mocker.setup(finally, {
ngx = {
log = function()
-- avoid stdout output during test
end,
timer = {
at = function() end,
every = function() end,
},
var = {
},
},
kong = {
timer = _G.timerng,
log = {
err = function() end,
warn = function() end,
},
response = {
exit = function() end,
},
worker_events = {
register = function() end,
},
cluster_events = {
subscribe = function() end,
},
configuration = {
database = "dummy",
worker_consistency = "strict",
},
db = {
strategy = "dummy",
},
core_cache = {
_cache = my_cache,
get = function(_, k)
return my_cache[k] or "1"
end
}
},
modules = {
{ "kong.runloop.balancer", {
init = function() end
}},
{ "ngx.semaphore", {
_semaphores = semaphores,
new = function()
local s = {
value = 0,
wait = function(self, timeout)
self.value = self.value - 1
return true
end,
post = function(self, n)
n = n or 1
self.value = self.value + n
return true
end,
}
table.insert(semaphores, s)
return s
end,
}},
{ "kong.concurrency", {} },
{ "kong.runloop.handler", {} },
}
})
end
local mock_router = {
exec = function()
return nil
end
}
describe("runloop handler", function()
describe("router rebuilds", function()
it("releases the lock when rebuilding the router fails", function()
setup_it_block()
local semaphores = require "ngx.semaphore"._semaphores
local handler = require "kong.runloop.handler"
local update_router_spy = spy.new(function()
return nil, "error injected by test (feel free to ignore :) )"
end)
handler.init_worker.before({})
handler._set_router(mock_router)
handler._set_update_router(update_router_spy)
handler.access.before({})
assert.spy(update_router_spy).was_called(1)
-- check semaphore
assert.equal(1, semaphores[1].value)
end)
it("bypasses router_semaphore upon acquisition timeout", function()
setup_it_block()
local semaphores = require "ngx.semaphore"._semaphores
local handler = require "kong.runloop.handler"
handler.init_worker.before()
local update_router_spy = spy.new(function() end)
handler._set_update_router(update_router_spy)
handler._set_router(mock_router)
-- call it once to create a semaphore
handler.access.before({})
assert.spy(update_router_spy).was_called(1)
-- force a router rebuild
handler._set_router_version("old")
-- cause failure to acquire semaphore
semaphores[1].wait = function()
return nil, "timeout"
end
handler.access.before({})
-- was called even if semaphore timed out on acquisition
assert.spy(update_router_spy).was_called(2)
-- check semaphore
assert.equal(1, semaphores[1].value)
end)
it("does not call update_router if worker_consistency is eventual", function()
setup_it_block()
kong.configuration.worker_consistency = "eventual"
local handler = require "kong.runloop.handler"
local update_router_spy = spy.new(function() end)
handler._set_update_router(update_router_spy)
handler._set_router(mock_router)
handler.init_worker.before()
handler.access.before({})
assert.spy(update_router_spy).was_called(0)
assert.equal(mock_router, handler._get_updated_router())
end)
it("does not call register_balancer_events if role is control_plane", function()
setup_it_block()
kong.configuration.role = "control_plane"
local handler = require "kong.runloop.handler"
local register_balancer_events_spy = spy.new(function() end)
handler._set_router(mock_router)
handler._register_balancer_events(register_balancer_events_spy)
handler.init_worker.before()
assert.spy(register_balancer_events_spy).was_called(0)
end)
it("call register_balancer_events if role is data_plane", function()
setup_it_block()
kong.configuration.role = "data_plane"
local handler = require "kong.runloop.handler"
local register_balancer_events_spy = spy.new(function() end)
handler._set_router(mock_router)
handler._register_balancer_events(register_balancer_events_spy)
handler.init_worker.before()
assert.spy(register_balancer_events_spy).was_called(1)
end)
it("calls build_router if router version changes and worker_consistency is strict", function()
setup_it_block()
kong.configuration.worker_consistency = "strict"
local handler = require "kong.runloop.handler"
local latest_router
local build_router_spy = spy.new(function()
handler._set_router_version(kong.core_cache:get("router:version"))
latest_router = {
exec = function()
return nil
end
}
handler._set_router(latest_router)
end)
handler._set_build_router(build_router_spy)
handler.init_worker.before()
handler.access.before({})
assert.spy(build_router_spy).was_called(1)
assert.equal(latest_router, handler._get_updated_router())
local saved_router = latest_router
kong.core_cache._cache["router:version"] = "new_version"
handler.access.before({})
assert.spy(build_router_spy).was_called(2)
assert.equal(latest_router, handler._get_updated_router())
assert.not_equal(saved_router, latest_router)
end)
it("does not call build_router if router version does not change and worker_consistency is strict", function()
setup_it_block()
kong.configuration.worker_consistency = "strict"
local handler = require "kong.runloop.handler"
local latest_router
local build_router_spy = spy.new(function()
handler._set_router_version(kong.core_cache:get("router:version"))
latest_router = {
exec = function()
return nil
end
}
handler._set_router(latest_router)
end)
handler._set_build_router(build_router_spy)
handler._set_router(mock_router)
handler.init_worker.before()
handler.access.before({})
assert.spy(build_router_spy).was_called(1)
assert.equal(latest_router, handler._get_updated_router())
local saved_router = latest_router
handler.access.before({})
assert.spy(build_router_spy).was_called(1)
assert.equal(saved_router, latest_router)
end)
end)
end)