mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-09-21 17:04:00 +00:00
Address review on the test harness
- The generic parts move to test/ in the repository root so other plugins can reuse them: the factorio and clusterio stubs, the test framework, the fengari runner, and clusterio's testMatrix and round trip helpers. A plugin composes them from its own env.lua, which adds its stubs and fixtures. - Tests are declared with Test.test(name, fn) and every test function receives a fresh environment, so nothing carries over between them. A test which errors is reported as a failure rather than aborting the file. - Every stub raises on properties it does not implement, which mirrors the game api. game.player is the one property allowed to read as nil. - Test.deep_eq compares tables recursively with keys checked from both sides. - The message records round trip through the same testMatrix and testRoundTripJsonSerialisable helpers the clusterio tests use, covering every optional field combination of the records, events and requests. - controller.test.js and instance.test.js cover the node side of the plugin against faked controller and instance internals: property creation and sweeping, record building, broadcasts, subscription replay, assignment validation, auto assignment and its blocking role, seeding, the initialise payload, sync mode gating, and the rejection rollback. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
59a0e0be13
commit
24d36db601
@@ -0,0 +1,109 @@
|
||||
--[[-- Test framework for plugin module tests
|
||||
Test files declare named tests with `Test.test(name, function(env) ... end)`.
|
||||
The runner creates a fresh environment for every test, so tests are
|
||||
independent and can not leak state into each other.
|
||||
]]
|
||||
|
||||
local Framework = {}
|
||||
|
||||
--- Create a test registry, one is made per test file
|
||||
function Framework.new()
|
||||
--- @class Test
|
||||
local Test = {
|
||||
tests = {}, -- { name, fn } in declaration order
|
||||
results = {}, -- { name, ok, detail? } accumulated across tests
|
||||
}
|
||||
|
||||
local current_test --- @type string?
|
||||
|
||||
--- Declare a named test, its function receives a fresh environment
|
||||
--- @param name string
|
||||
--- @param fn fun(env: table)
|
||||
function Test.test(name, fn)
|
||||
Test.tests[#Test.tests + 1] = { name = name, fn = fn }
|
||||
end
|
||||
|
||||
--- Record one check within the current test
|
||||
--- @param ok any Truthy when the check passed
|
||||
--- @param name string
|
||||
--- @param detail string?
|
||||
function Test.check(ok, name, detail)
|
||||
Test.results[#Test.results + 1] = {
|
||||
name = current_test and (current_test .. ": " .. name) or name,
|
||||
ok = not not ok,
|
||||
detail = detail,
|
||||
}
|
||||
end
|
||||
|
||||
--- Shallow array equality
|
||||
function Test.eq(a, b)
|
||||
if type(a) ~= "table" or type(b) ~= "table" then return a == b end
|
||||
if #a ~= #b then return false end
|
||||
for i = 1, #a do
|
||||
if a[i] ~= b[i] then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Recursive table equality, keys checked from both sides
|
||||
function Test.deep_eq(a, b)
|
||||
if type(a) ~= "table" or type(b) ~= "table" then return a == b end
|
||||
for key, value in pairs(a) do
|
||||
if not Test.deep_eq(value, b[key]) then return false end
|
||||
end
|
||||
for key in pairs(b) do
|
||||
if a[key] == nil then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Names of an array of roles
|
||||
function Test.names(roles)
|
||||
local rtn = {}
|
||||
for index, role in ipairs(roles) do
|
||||
rtn[index] = role.name
|
||||
end
|
||||
return rtn
|
||||
end
|
||||
|
||||
--- Sorted copy of an array of strings
|
||||
function Test.sorted(list)
|
||||
local rtn = {}
|
||||
for index, value in ipairs(list) do rtn[index] = value end
|
||||
table.sort(rtn)
|
||||
return rtn
|
||||
end
|
||||
|
||||
--- Run every declared test against a fresh environment
|
||||
--- @param make_env fun(): table
|
||||
function Test.run(make_env)
|
||||
for _, test in ipairs(Test.tests) do
|
||||
current_test = test.name
|
||||
local ok, err = pcall(test.fn, make_env())
|
||||
if not ok then
|
||||
Test.check(false, "did not error", tostring(err))
|
||||
end
|
||||
end
|
||||
current_test = nil
|
||||
end
|
||||
|
||||
--- Encode the results as JSON for the javascript side
|
||||
function Test.results_json()
|
||||
local function escape(value)
|
||||
return (value:gsub('[%c"\\]', function(c)
|
||||
return string.format("\\u%04x", c:byte())
|
||||
end))
|
||||
end
|
||||
|
||||
local parts = {}
|
||||
for index, result in ipairs(Test.results) do
|
||||
local detail = result.detail and string.format(',"detail":"%s"', escape(result.detail)) or ""
|
||||
parts[index] = string.format('{"name":"%s","ok":%s%s}', escape(result.name), tostring(result.ok), detail)
|
||||
end
|
||||
return "[" .. table.concat(parts, ",") .. "]"
|
||||
end
|
||||
|
||||
return Test
|
||||
end
|
||||
|
||||
return Framework
|
||||
Reference in New Issue
Block a user