mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-09-22 01:04:01 +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
+21
-130
@@ -1,14 +1,23 @@
|
||||
--[[-- Test environment for module/control.lua
|
||||
Stubs the factorio globals and the clusterio modules, then loads a fresh copy
|
||||
of the roles module. Each call to Env.new() is independent.
|
||||
Composes the shared stubs and framework from the repository test folder with
|
||||
the fixtures for this plugin. Each call to Env.new() is an independent
|
||||
environment with a fresh copy of the roles module.
|
||||
|
||||
Run as a chunk by test/helpers/lua.js, receiving the module directory and
|
||||
Run as a chunk by test/lua/runner.js, receiving the shared folder and
|
||||
returning this table, which is in turn passed to each test chunk.
|
||||
]]
|
||||
|
||||
local module_root = ... --- @type string
|
||||
local shared_root = ... --- @type string
|
||||
local source = assert(debug.getinfo(1, "S")).source
|
||||
local plugin_root = assert(source:match("^@(.*)/test/lua/env%.lua$"))
|
||||
|
||||
local Env = {}
|
||||
local Stubs = assert(loadfile(shared_root .. "/stubs.lua"))()
|
||||
local Framework = assert(loadfile(shared_root .. "/framework.lua"))()
|
||||
|
||||
local Env = {
|
||||
--- Test registry for the file being run, see framework.lua
|
||||
Test = Framework.new(),
|
||||
}
|
||||
|
||||
--- Standard fixture: permission names sent once and referenced by zero based index
|
||||
Env.permission_names = {
|
||||
@@ -44,83 +53,12 @@ end
|
||||
|
||||
--- Create an independent environment with a fresh copy of the roles module
|
||||
function Env.new()
|
||||
local env = {
|
||||
events = {}, -- events raised through script.raise_event
|
||||
printed = {}, -- game.print and player.print messages
|
||||
sent = {}, -- payloads sent to the controller
|
||||
sounds = {}, -- sounds played to players
|
||||
results = {}, -- test results collected by env.check
|
||||
}
|
||||
local env = Stubs.new()
|
||||
|
||||
local next_event_id = 100
|
||||
script = {
|
||||
generate_event_name = function()
|
||||
next_event_id = next_event_id + 1
|
||||
return next_event_id
|
||||
end,
|
||||
raise_event = function(id, data)
|
||||
env.events[#env.events + 1] = { id = id, data = data }
|
||||
end,
|
||||
register_metatable = function() end,
|
||||
}
|
||||
defines = { events = { on_player_joined_game = 1, on_multiplayer_init = 2 } }
|
||||
|
||||
local players_by_name, players_by_index, connected = {}, {}, {}
|
||||
game = {
|
||||
tick = 1,
|
||||
player = nil,
|
||||
players = players_by_name,
|
||||
connected_players = connected,
|
||||
print = function(message) env.printed[#env.printed + 1] = message end,
|
||||
get_player = function(key) return players_by_name[key] or players_by_index[key] end,
|
||||
}
|
||||
|
||||
local stubs = {
|
||||
["modules/clusterio/api"] = {
|
||||
send_json = function(channel, data)
|
||||
env.sent[#env.sent + 1] = { channel = channel, data = data }
|
||||
end,
|
||||
},
|
||||
["modules/clusterio/compat"] = { script_data = {} },
|
||||
["modules/exp_util/async"] = {
|
||||
register = function(callback)
|
||||
return function(...) return callback(...) end
|
||||
end,
|
||||
},
|
||||
}
|
||||
require = function(name)
|
||||
return assert(stubs[name], "Unexpected require: " .. name)
|
||||
end
|
||||
|
||||
--- Add a player to the stubbed game
|
||||
function env.add_player(name, index, is_connected)
|
||||
local player = {
|
||||
name = name,
|
||||
index = index,
|
||||
connected = is_connected ~= false,
|
||||
valid = true,
|
||||
play_sound = function(opts)
|
||||
env.sounds[#env.sounds + 1] = name .. ":" .. opts.path
|
||||
end,
|
||||
print = function(message)
|
||||
env.printed[#env.printed + 1] = { to = name, message }
|
||||
end,
|
||||
}
|
||||
players_by_name[name] = player
|
||||
players_by_index[index] = player
|
||||
if player.connected then connected[#connected + 1] = player end
|
||||
return player
|
||||
end
|
||||
|
||||
--- A player object which represents the server
|
||||
env.server = setmetatable({ index = 0, name = "<server>" }, {
|
||||
__index = function(_, key) error("Server player field accessed: " .. tostring(key)) end,
|
||||
})
|
||||
|
||||
env.Roles = assert(loadfile(module_root .. "/control.lua"))()
|
||||
env.Roles = assert(loadfile(plugin_root .. "/module/control.lua"))()
|
||||
env.Roles.on_server_startup()
|
||||
|
||||
--- Get a role by name, failing the test file when it does not exist
|
||||
--- Get a role by name, failing the test when it does not exist
|
||||
function env.R(name)
|
||||
return (assert(env.Roles.get_role_by_name(name), "No role named " .. name))
|
||||
end
|
||||
@@ -134,60 +72,13 @@ function Env.new()
|
||||
}
|
||||
end
|
||||
|
||||
--- Forget everything recorded so far
|
||||
function env.reset_log()
|
||||
env.events, env.printed, env.sent, env.sounds = {}, {}, {}, {}
|
||||
end
|
||||
|
||||
--- Record one test result
|
||||
function env.check(ok, name, detail)
|
||||
env.results[#env.results + 1] = { name = name, ok = not not ok, detail = detail }
|
||||
end
|
||||
|
||||
--- Shallow array equality
|
||||
function env.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
|
||||
|
||||
--- Names of an array of roles
|
||||
function env.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 env.sorted(list)
|
||||
local rtn = {}
|
||||
for index, value in ipairs(list) do rtn[index] = value end
|
||||
table.sort(rtn)
|
||||
return rtn
|
||||
end
|
||||
|
||||
return env
|
||||
end
|
||||
|
||||
--- Encode the results of an environment as JSON for the javascript side
|
||||
function Env.results_json(env)
|
||||
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(env.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, ",") .. "]"
|
||||
--- Run the declared tests, each against a fresh environment
|
||||
function Env.finish()
|
||||
Env.Test.run(Env.new)
|
||||
return Env.Test.results_json()
|
||||
end
|
||||
|
||||
return Env
|
||||
|
||||
Reference in New Issue
Block a user