🤞 Sync 2026-11-10 13:45:37

This commit is contained in:
github-actions[bot] committed 2026-11-10 13:45:37 +08:00
1 parent 7822fc98cf
commit 97dc33e800
640 files changed
+108562 -5

No files matched your search

+173
View File
@@ -0,0 +1,173 @@
-- Loads the real LuCI controller against a recording stand-in for the daemon.
--
-- The controller's job is now translation: form values in, one RPC out, the
-- daemon's answer back to the browser. A harness that stubs the socket rather
-- than the filesystem is what makes that testable -- and it fails loudly if the
-- page ever goes back to writing a file, because nothing here provides one.
--
-- Usage:
-- local harness = dofile("tests/lua/controller_harness.lua")(repo_root)
-- harness.responses["config.get"] = { revision = 3, ... }
-- harness.form = { action = "manual_login" }
-- harness.controller.action_enqueue()
-- assert(harness.calls[1].method == "status.get")
return function(repo_root)
local harness = {
form = {},
output = nil,
calls = {},
responses = {},
writes = {},
commands = {},
helpers = {},
now = 1000,
security_allowed = true,
}
local function encode(value)
-- The controller hands one table to the response writer; keeping the
-- table itself is more useful to a test than a string of it.
return value
end
package.preload["luci.http"] = function()
return {
formvalue = function(key) return harness.form[key] end,
getenv = function(key) return harness.env and harness.env[key] end,
prepare_content = function() end,
header = function(key, value) harness.headers = harness.headers or {}; harness.headers[key] = value end,
write = function(value) harness.output = value end,
}
end
package.preload["luci.jsonc"] = function()
return {
parse = function(text) return harness.parsed and harness.parsed[text] or nil end,
stringify = encode,
}
end
package.preload["luci.dispatcher"] = function()
return { context = { authsession = "browser-session" },
test_post_security = function() return harness.security_allowed end }
end
package.preload["luci.sys"] = function()
return {
exec = function(command)
harness.commands[#harness.commands + 1] = command
return ""
end,
call = function(command)
harness.commands[#harness.commands + 1] = command
return 0
end,
}
end
package.preload["luci.util"] = function()
return {
trim = function(value) return tostring(value or ""):match("^%s*(.-)%s*$") end,
pcdata = function(value) return tostring(value or "") end,
shellquote = function(value) return "'" .. tostring(value or "") .. "'" end,
}
end
package.preload["nixio.fs"] = function()
return {
readfile = function() return nil end,
writefile = function(path, value)
harness.writes[#harness.writes + 1] = { path = path, value = value }
return true
end,
access = function() return false end,
mkdirr = function() return true end,
remove = function() return true end,
unlink = function() return true end,
rename = function() return true end,
stat = function() return nil end,
dir = function() return function() return nil end end,
}
end
package.preload["nixio"] = function()
return {
getpid = function() return 4242 end,
open_flags = function() return 0 end,
open = function() return { lock = function() return true end, close = function() end } end,
}
end
-- The stand-in daemon. Unset methods answer "not implemented" rather than
-- an empty success, so a call the test did not plan for cannot pass as one.
package.preload["luci.smart_srun.rpc"] = function()
local rpc = { NULL = "\1null\1", EMPTY_OBJECT = "\1{}\1" }
local function record(method, params, started)
harness.calls[#harness.calls + 1] =
{ method = method, params = params, started = started or false }
local canned = harness.responses[method]
if type(canned) == "function" then
return canned(params)
end
if canned == nil then
return nil, { code = "Internal", message = "本次测试没有为 " .. method .. " 准备响应" }
end
if canned.error then
return nil, canned.error
end
return canned
end
function rpc.call(method, params) return record(method, params, false) end
function rpc.call_started(method, params) return record(method, params, true) end
function rpc.stop_service()
harness.helpers[#harness.helpers + 1] = "service stop"
if harness.stop_failure then
return nil, harness.stop_failure
end
return true
end
function rpc.ensure_running()
harness.helpers[#harness.helpers + 1] = "service ensure-running"
return true
end
function rpc.service_stopped(err)
return type(err) == "table" and err.code == "ServiceStopped"
end
function rpc.message(err, fallback)
if type(err) == "table" and tostring(err.message or "") ~= "" then
return err.message
end
return fallback or "操作失败"
end
return rpc
end
package.path = repo_root .. "/root/usr/lib/lua/?.lua;" .. package.path
dofile(repo_root .. "/root/usr/lib/lua/luci/controller/smart_srun.lua")
harness.controller = package.loaded["luci.controller.smart_srun"]
harness.rpc = require "luci.smart_srun.rpc"
harness.bridge = require "luci.smart_srun.bridge"
-- A fixed clock, so an age computed from two timestamps is a stated number
-- rather than whatever the machine running the test happens to say.
local real_time = os.time
os.time = function(parts)
if parts then return real_time(parts) end
return harness.now
end
function harness.last_call(method)
for index = #harness.calls, 1, -1 do
if harness.calls[index].method == method then
return harness.calls[index]
end
end
return nil
end
function harness.reset()
harness.calls = {}
harness.output = nil
harness.writes = {}
harness.helpers = {}
end
return harness
end