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

164 lines
4.7 KiB
Lua

local setmetatable = setmetatable
local ipairs = ipairs
local pairs = pairs
local pcall = pcall
local error = error
local http_mock = {}
local build_in_checks = {}
local eventually_MT = {}
eventually_MT.__index = eventually_MT
local step_time = 0.01
-- example for a check function
-- local function(session, status)
-- -- must throw error if the assertion is not true
-- -- instead of return false
-- assert.same(session.resp.status, status)
-- -- return a string to tell what condition is satisfied
-- -- so we can construct an error message for reverse assertion
-- -- in this case it would be "we don't expect that: has a response with status 200"
-- return "has a response with status " .. status
-- end
local function eventually_has(check, mock, ...)
local time = 0
local ok, err
while time < mock.eventually_timeout do
local logs = mock:retrieve_mocking_logs()
for _, log in ipairs(logs) do
-- use pcall so the user may use lua assert like assert.same
ok, err = pcall(check, log, ...)
if ok then
return true
end
end
ngx.sleep(step_time)
time = time + step_time
end
error(err or "assertion fail. No request is sent and recorded.", 2)
end
-- wait until timeout to check if the assertion is true for all logs
local function eventually_all(check, mock, ...)
local time = 0
local ok, err
while time < mock.eventually_timeout do
local logs = mock:retrieve_mocking_logs()
for _, log in ipairs(logs) do
ok, err = pcall(check, log, ...)
if not ok then
error(err or "assertion fail", 2)
end
end
ngx.sleep(step_time)
time = time + step_time
end
return true
end
-- a session is a request/response pair
function build_in_checks.session_satisfy(session, f)
return f(session) or "session satisfy"
end
function build_in_checks.request_satisfy(session, f)
return f(session.req) or "request satisfy"
end
function build_in_checks.request()
return "request exist"
end
function build_in_checks.response_satisfy(session, f)
return f(session.resp) or "response satisfy"
end
function build_in_checks.error_satisfy(session, f)
return f(session.err) or "error satisfy"
end
function build_in_checks.error(session)
assert(session.err, "has no error")
return "has error"
end
local function register_assert(name, impl)
eventually_MT["has_" .. name] = function(self, ...)
return eventually_has(impl, self.__mock, ...)
end
eventually_MT["all_" .. name] = function(self, ...)
return eventually_all(impl, self.__mock, ...)
end
local function reverse_impl(session, ...)
local ok, err = pcall(impl, session, ...)
if ok then
error("we don't expect that: " .. (name or err), 2)
end
return true
end
eventually_MT["has_no_" .. name] = function(self, ...)
return eventually_all(reverse_impl, self.__mock, ...)
end
eventually_MT["not_all_" .. name] = function(self, ...)
return eventually_has(reverse_impl, self.__mock, ...)
end
eventually_MT["has_one_without_" .. name] = eventually_MT["not_all_" .. name]
end
for name, impl in pairs(build_in_checks) do
register_assert(name, impl)
end
function http_mock:_set_eventually_table()
local eventually = setmetatable({}, eventually_MT)
eventually.__mock = self
self.eventually = eventually
return eventually
end
-- usually this function is not called by a user. I will add more assertions in the future with it. @StarlightIbuki
-- @function http_mock.register_assert()
-- @param name: the name of the assertion
-- @param impl: the implementation of the assertion
-- implement a new eventually assertion
-- @usage:
-- impl is a function
-- -- @param session: the session object, with req, resp, err, start_time, end_time as fields
-- -- @param ...: the arguments passed to the assertion
-- -- @return: human readable message if the assertion is true, or throw error if not
--
-- a session means a request/response pair.
-- The impl callback throws error if the assertion is not true
-- and returns a string to tell what condition is satisfied
-- This design is to allow the user to use lua asserts in the callback
-- (or even callback the registered assertion accept as argument), like the example;
-- and for has_no/not_all assertions, we can construct an error message for it like:
-- "we don't expect that: has header foo"
-- @example:
-- http_mock.register_assert("req_has_header", function(mock, name)
-- assert.same(name, session.req.headers[name])
-- return "has header " .. name
-- end)
-- mock.eventually:has_req_has_header("foo")
-- mock.eventually:has_no_req_has_header("bar")
-- mock.eventually:all_req_has_header("baz")
-- mock.eventually:not_all_req_has_header("bar")
http_mock.register_assert = register_assert
return http_mock