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
+101
-74
@@ -1,91 +1,118 @@
|
||||
--- Initialise semantics and updates from the controller
|
||||
local Env = ...
|
||||
local env = Env.new()
|
||||
local check, eq, R = env.check, env.eq, env.R
|
||||
local Roles = env.Roles
|
||||
local sd = Roles._script_data()
|
||||
local Test = Env.Test
|
||||
local test, check, eq = Test.test, Test.check, Test.eq
|
||||
|
||||
local alice = env.add_player("alice", 1)
|
||||
local carol = env.add_player("carol", 3)
|
||||
--- alice is a moderator and carol has only the default role, both connected
|
||||
local function setup(env)
|
||||
local players = {
|
||||
alice = env.add_player("alice", 1),
|
||||
carol = env.add_player("carol", 3),
|
||||
}
|
||||
env.admin_state = {}
|
||||
env.Roles.define_permission_trigger("exp_scenario.player.admin", function(player, state)
|
||||
env.admin_state[player.name] = state
|
||||
end)
|
||||
env.initialise{
|
||||
Env.assignment("alice", { 5 }),
|
||||
Env.assignment("zed", { 5, 6 }), -- has never joined this map
|
||||
}
|
||||
env.Roles.set_emit_events(true)
|
||||
return players
|
||||
end
|
||||
|
||||
local admin_state = {}
|
||||
Roles.define_permission_trigger("exp_scenario.player.admin", function(player, state)
|
||||
admin_state[player.name] = state
|
||||
test("initialise applies triggers and raises empty events", function(env)
|
||||
setup(env)
|
||||
check(env.admin_state.alice == true and env.admin_state.carol == false,
|
||||
"triggers run for connected players")
|
||||
check(#env.events == 2, "the event is raised once per connected player")
|
||||
check(#env.events[1].data.assigned == 0 and #env.events[1].data.unassigned == 0, "the events are empty")
|
||||
check(#env.printed == 0 and #env.sent == 0 and #env.sounds == 0, "initialise is silent and sends nothing")
|
||||
end)
|
||||
|
||||
env.initialise{
|
||||
Env.assignment("alice", { 5 }),
|
||||
Env.assignment("zed", { 5, 6 }), -- has never joined this map
|
||||
}
|
||||
Roles.set_emit_events(true)
|
||||
test("controller assignments apply and are announced", function(env)
|
||||
local players = setup(env)
|
||||
env.reset_log()
|
||||
env.Roles.receive_assignment_updates{ Env.assignment("carol", { 5 }) }
|
||||
check(env.R("Moderator"):has_player(players.carol), "the assignment applies")
|
||||
check(#env.events == 1 and eq(env.events[1].data.assigned, { 5 }), "the event is raised")
|
||||
check(env.events[1].data.by_player_index == 0, "controller changes have no by player")
|
||||
check(#env.printed == 1 and env.printed[1][4] == "<server>", "the change is announced by the server")
|
||||
check(env.admin_state.carol == true, "triggers follow the assignment")
|
||||
|
||||
check(admin_state.alice == true and admin_state.carol == false, "triggers run for connected players on initialise")
|
||||
check(#env.events == 2, "the roles changed event is raised once per connected player on initialise")
|
||||
check(#env.events[1].data.assigned == 0 and #env.events[1].data.unassigned == 0, "initialise events are empty")
|
||||
check(#env.printed == 0 and #env.sent == 0 and #env.sounds == 0, "initialise is silent and sends nothing")
|
||||
env.reset_log()
|
||||
env.Roles.receive_assignment_updates{ { name = "carol", role_ids = {}, is_deleted = true } }
|
||||
check(not env.R("Moderator"):has_player(players.carol), "a removal applies")
|
||||
check(#env.events == 1 and eq(env.events[1].data.unassigned, { 5 }), "the removal raises the event")
|
||||
check(env.admin_state.carol == false, "triggers follow the removal")
|
||||
end)
|
||||
|
||||
-- Changes to the roles a player holds, made on the controller
|
||||
env.reset_log()
|
||||
Roles.receive_assignment_updates{ Env.assignment("carol", { 5 }) }
|
||||
check(R("Moderator"):has_player(carol), "a controller assignment applies")
|
||||
check(#env.events == 1 and eq(env.events[1].data.assigned, { 5 }), "a controller assignment raises the event")
|
||||
check(env.events[1].data.by_player_index == 0, "controller changes have no by player")
|
||||
check(#env.printed == 1 and env.printed[1][4] == "<server>", "a controller assignment is announced by the server")
|
||||
check(admin_state.carol == true, "triggers follow controller assignments")
|
||||
test("changes for players not on this map raise nothing", function(env)
|
||||
setup(env)
|
||||
env.reset_log()
|
||||
env.Roles.receive_assignment_updates{ Env.assignment("zed", { 5 }) }
|
||||
check(#env.events == 0 and #env.printed == 0, "no event and no announcement")
|
||||
end)
|
||||
|
||||
env.reset_log()
|
||||
Roles.receive_assignment_updates{ { name = "carol", role_ids = {}, is_deleted = true } }
|
||||
check(not R("Moderator"):has_player(carol), "a controller removal applies")
|
||||
check(#env.events == 1 and eq(env.events[1].data.unassigned, { 5 }), "a controller removal raises the event")
|
||||
check(admin_state.carol == false, "triggers follow controller removals")
|
||||
test("role updates apply to their holders", function(env)
|
||||
setup(env)
|
||||
env.reset_log()
|
||||
env.Roles.receive_role_updates{
|
||||
{ id = 6, name = "Regular", permissions = { "exp_scenario.command.jail" }, meta = { id = 0, order = 3 } },
|
||||
}
|
||||
check(env.R("Regular"):has_permission("exp_scenario.command.jail"), "the permission change applies")
|
||||
check(#env.events == 2, "a role change raises for every connected player")
|
||||
check(#env.events[1].data.assigned == 0, "role change events are empty")
|
||||
|
||||
env.reset_log()
|
||||
Roles.receive_assignment_updates{ Env.assignment("zed", { 5 }) }
|
||||
check(#env.events == 0 and #env.printed == 0, "changes for players not on this map raise nothing")
|
||||
env.Roles.receive_role_updates{
|
||||
{ id = 6, name = "Regular", permissions = {}, meta = { id = 0, order = 3 }, is_deleted = true },
|
||||
}
|
||||
check(env.Roles.get_role(6) == nil, "a deleted role is removed")
|
||||
end)
|
||||
|
||||
-- Changes to the roles themselves
|
||||
env.reset_log()
|
||||
Roles.receive_role_updates{
|
||||
{ id = 6, name = "Regular", permissions = { "exp_scenario.command.jail" }, meta = { id = 0, order = 3 } },
|
||||
}
|
||||
check(R("Regular"):has_permission("exp_scenario.command.jail"), "a role permission change applies")
|
||||
check(#env.events == 2, "a role change raises for every connected player")
|
||||
check(#env.events[1].data.assigned == 0, "role change events are empty")
|
||||
test("the default role follows is_default", function(env)
|
||||
local players = setup(env)
|
||||
env.Roles.receive_role_updates{
|
||||
{ id = 1, name = "Player", permissions = {}, meta = { id = 0, order = 5 } },
|
||||
{ id = 8, name = "Guest", permissions = {}, meta = { id = 0, order = 7 }, is_default = true },
|
||||
}
|
||||
check(env.Roles.get_default_role() == env.R("Guest"), "the new default role is known")
|
||||
check(eq(Test.names(env.Roles.get_player_roles(players.carol)), { "Guest" }), "players pick up the new default")
|
||||
end)
|
||||
|
||||
env.reset_log()
|
||||
Roles.receive_role_updates{ { id = 6, name = "Regular", permissions = {}, meta = { id = 0, order = 3 }, is_deleted = true } }
|
||||
check(Roles.get_role(6) == nil, "a deleted role is removed")
|
||||
test("initialise is authoritative for pending roles", function(env)
|
||||
local players = setup(env)
|
||||
env.R("Moderator"):assign(players.carol)
|
||||
env.R("Jail"):assign(players.carol, { silent = true, local_only = true })
|
||||
|
||||
env.reset_log()
|
||||
Roles.receive_role_updates{
|
||||
{ id = 1, name = "Player", permissions = {}, meta = { id = 0, order = 5 } },
|
||||
{ id = 8, name = "Guest", permissions = {}, meta = { id = 0, order = 7 }, is_default = true },
|
||||
}
|
||||
check(Roles.get_default_role() == R("Guest"), "the default role follows is_default")
|
||||
check(eq(env.names(Roles.get_player_roles(carol)), { "Guest" }), "players pick up the new default role")
|
||||
local sd = env.Roles._script_data()
|
||||
check(eq(sd.pending.carol, { 5 }), "the role is pending before initialise")
|
||||
|
||||
-- Initialise is authoritative for pending roles
|
||||
R("Moderator"):assign(carol)
|
||||
R("Jail"):assign(carol, { silent = true, local_only = true })
|
||||
check(eq(sd.pending.carol, { 5 }), "the role is pending before initialise")
|
||||
env.reset_log()
|
||||
env.initialise{ Env.assignment("alice", { 5 }) }
|
||||
check(sd.pending.carol == nil, "initialise clears pending roles")
|
||||
check(not R("Moderator"):has_player(carol), "an unconfirmed role is given up")
|
||||
check(eq(sd.local_players.carol, { 7 }), "local only roles are kept")
|
||||
check(#env.sent == 0, "initialise sends nothing")
|
||||
env.reset_log()
|
||||
env.initialise{ Env.assignment("alice", { 5 }) }
|
||||
check(sd.pending.carol == nil, "pending roles are cleared")
|
||||
check(not env.R("Moderator"):has_player(players.carol), "an unconfirmed role is given up")
|
||||
check(eq(sd.local_players.carol, { 7 }), "local only roles are kept")
|
||||
check(#env.sent == 0, "initialise sends nothing")
|
||||
end)
|
||||
|
||||
-- Emit updates gate and load
|
||||
Roles.set_emit_events(false)
|
||||
env.reset_log()
|
||||
R("Regular"):assign(alice)
|
||||
check(#env.sent == 0, "nothing is sent while emit is disabled")
|
||||
Roles.on_load()
|
||||
check(R("Moderator"):is_higher_than(R("Regular")), "roles are usable after load")
|
||||
test("nothing is sent while emit is disabled", function(env)
|
||||
local players = setup(env)
|
||||
env.Roles.set_emit_events(false)
|
||||
env.reset_log()
|
||||
env.R("Regular"):assign(players.alice)
|
||||
check(#env.sent == 0, "the assignment is not sent")
|
||||
check(env.R("Regular"):has_player(players.alice), "the assignment still applies locally")
|
||||
end)
|
||||
|
||||
-- Triggers on join
|
||||
admin_state.alice = nil
|
||||
Roles.on_player_joined_game{ player_index = 1 }
|
||||
check(admin_state.alice ~= nil, "triggers run when a player joins")
|
||||
test("roles are usable after load and triggers run on join", function(env)
|
||||
setup(env)
|
||||
env.Roles.on_load()
|
||||
check(env.R("Moderator"):is_higher_than(env.R("Regular")), "roles are usable after load")
|
||||
|
||||
return Env.results_json(env)
|
||||
env.admin_state.alice = nil
|
||||
env.Roles.on_player_joined_game{ player_index = 1 }
|
||||
check(env.admin_state.alice ~= nil, "triggers run when a player joins")
|
||||
end)
|
||||
|
||||
return Env.finish()
|
||||
|
||||
Reference in New Issue
Block a user