mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-09-22 17:24: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
@@ -1,93 +1,119 @@
|
||||
--- Assignment through role methods, confirmation, and rejection
|
||||
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 bob = env.add_player("bob", 2)
|
||||
env.initialise{
|
||||
Env.assignment("alice", { 5 }),
|
||||
Env.assignment("bob", { 6 }),
|
||||
}
|
||||
Roles.set_emit_events(true)
|
||||
env.reset_log()
|
||||
--- alice is a moderator and bob a regular, with changes sent to the controller
|
||||
local function setup(env)
|
||||
local players = {
|
||||
alice = env.add_player("alice", 1),
|
||||
bob = env.add_player("bob", 2),
|
||||
}
|
||||
env.initialise{
|
||||
Env.assignment("alice", { 5 }),
|
||||
Env.assignment("bob", { 6 }),
|
||||
}
|
||||
env.Roles.set_emit_events(true)
|
||||
env.reset_log()
|
||||
return players
|
||||
end
|
||||
|
||||
-- Assign with sync
|
||||
R("Moderator"):assign(bob, { by_player_name = "alice" })
|
||||
check(#env.sent == 1 and env.sent[1].channel == "exp_roles:assignment_update", "assignment is sent to the controller")
|
||||
check(eq(env.sent[1].data.assign, { 5 }) and env.sent[1].data.name == "bob", "assignment payload")
|
||||
check(R("Moderator"):has_player(bob), "the role applies before confirmation")
|
||||
check(#env.events == 1 and eq(env.events[1].data.assigned, { 5 }), "event carries the assigned role id")
|
||||
check(env.events[1].data.by_player_index == 1, "event carries who made the change")
|
||||
check(#env.printed == 1 and env.printed[1][1] == "exp-roles.game-message-assign", "the change is announced")
|
||||
check(#env.sounds == 1 and env.sounds[1] == "bob:utility/achievement_unlocked", "assign sound")
|
||||
check(eq(sd.local_players.bob, { 5 }) and eq(sd.pending.bob, { 5 }), "held locally and pending")
|
||||
test("assign applies locally and is sent to the controller", function(env)
|
||||
local players = setup(env)
|
||||
env.R("Moderator"):assign(players.bob, { by_player_name = "alice" })
|
||||
check(#env.sent == 1 and env.sent[1].channel == "exp_roles:assignment_update", "the assignment is sent")
|
||||
check(eq(env.sent[1].data.assign, { 5 }) and env.sent[1].data.name == "bob", "the payload names the role and player")
|
||||
check(env.R("Moderator"):has_player(players.bob), "the role applies before confirmation")
|
||||
check(#env.events == 1 and eq(env.events[1].data.assigned, { 5 }), "the event carries the assigned role id")
|
||||
check(env.events[1].data.by_player_index == 1, "the event carries who made the change")
|
||||
check(#env.printed == 1 and env.printed[1][1] == "exp-roles.game-message-assign", "the change is announced")
|
||||
check(#env.sounds == 1 and env.sounds[1] == "bob:utility/achievement_unlocked", "the assign sound plays")
|
||||
|
||||
env.reset_log()
|
||||
R("Moderator"):assign(bob)
|
||||
check(#env.sent == 0 and #env.events == 0, "assigning a held role is a no-op")
|
||||
local sd = env.Roles._script_data()
|
||||
check(eq(sd.local_players.bob, { 5 }) and eq(sd.pending.bob, { 5 }), "the role is held locally and pending")
|
||||
|
||||
-- Controller confirms
|
||||
env.reset_log()
|
||||
Roles.receive_assignment_updates{ Env.assignment("bob", { 6, 5 }) }
|
||||
check(#env.events == 0 and #env.printed == 0, "a confirmation raises nothing")
|
||||
check(sd.local_players.bob == nil and sd.pending.bob == nil, "a confirmed role is no longer held locally")
|
||||
check(R("Moderator"):has_player(bob), "the role is still held after confirmation")
|
||||
env.reset_log()
|
||||
env.R("Moderator"):assign(players.bob)
|
||||
check(#env.sent == 0 and #env.events == 0, "assigning a held role is a no-op")
|
||||
end)
|
||||
|
||||
-- Unassign a synced role
|
||||
env.reset_log()
|
||||
R("Moderator"):unassign(bob, { by_player_name = "alice", silent = true })
|
||||
check(#env.sent == 1 and eq(env.sent[1].data.unassign, { 5 }), "unassignment is sent to the controller")
|
||||
check(not R("Moderator"):has_player(bob), "the role is removed locally")
|
||||
check(#env.events == 1 and eq(env.events[1].data.unassigned, { 5 }), "event carries the unassigned role id")
|
||||
check(#env.printed == 0, "silent suppresses the announcement")
|
||||
check(#env.sounds == 1 and env.sounds[1] == "bob:utility/game_lost", "unassign sound")
|
||||
Roles.receive_assignment_updates{ Env.assignment("bob", { 6 }) }
|
||||
test("a confirmation releases the local hold", function(env)
|
||||
local players = setup(env)
|
||||
env.R("Moderator"):assign(players.bob)
|
||||
env.reset_log()
|
||||
env.Roles.receive_assignment_updates{ Env.assignment("bob", { 6, 5 }) }
|
||||
check(#env.events == 0 and #env.printed == 0, "a confirmation raises nothing")
|
||||
|
||||
-- Rejection rolls back
|
||||
env.reset_log()
|
||||
R("Jail"):assign(alice)
|
||||
check(R("Jail"):has_player(alice), "the role applies before rejection")
|
||||
env.reset_log()
|
||||
Roles.reject_assignment{ name = "alice", role_ids = { 7 } }
|
||||
check(not R("Jail"):has_player(alice), "a rejected role is rolled back")
|
||||
check(#env.sent == 0, "the rollback is not sent to the controller")
|
||||
check(#env.events == 1 and eq(env.events[1].data.unassigned, { 7 }), "the rollback raises the event")
|
||||
check(sd.local_players.alice == nil and sd.pending.alice == nil, "the rollback clears the local state")
|
||||
local sd = env.Roles._script_data()
|
||||
check(sd.local_players.bob == nil and sd.pending.bob == nil, "the role is no longer held locally")
|
||||
check(env.R("Moderator"):has_player(players.bob), "the role is still held after confirmation")
|
||||
end)
|
||||
|
||||
-- Local only roles
|
||||
env.reset_log()
|
||||
R("Regular"):assign(alice, { silent = true, local_only = true })
|
||||
check(#env.sent == 0, "a local only role is not sent")
|
||||
check(R("Regular"):has_player(alice), "a local only role applies")
|
||||
check(sd.pending.alice == nil and eq(sd.local_players.alice, { 6 }), "a local only role is not pending")
|
||||
Roles.receive_assignment_updates{ Env.assignment("alice", { 5 }) }
|
||||
check(R("Regular"):has_player(alice), "a local only role survives controller updates")
|
||||
env.reset_log()
|
||||
R("Regular"):unassign(alice, { local_only = true })
|
||||
check(not R("Regular"):has_player(alice) and #env.sent == 0, "a local only role is removed without sync")
|
||||
test("unassign a synced role", function(env)
|
||||
local players = setup(env)
|
||||
env.R("Regular"):unassign(players.bob, { by_player_name = "alice", silent = true })
|
||||
check(#env.sent == 1 and eq(env.sent[1].data.unassign, { 6 }), "the unassignment is sent")
|
||||
check(not env.R("Regular"):has_player(players.bob), "the role is removed locally")
|
||||
check(#env.events == 1 and eq(env.events[1].data.unassigned, { 6 }), "the event carries the unassigned role id")
|
||||
check(#env.printed == 0, "silent suppresses the announcement")
|
||||
check(#env.sounds == 1 and env.sounds[1] == "bob:utility/game_lost", "the unassign sound plays")
|
||||
end)
|
||||
|
||||
-- Jail priority
|
||||
env.reset_log()
|
||||
R("Jail"):assign(alice, { by_player_name = "<server>", silent = true })
|
||||
check(eq(env.names(Roles.get_player_roles(alice)), { "Jail" }), "jail suppresses every other role")
|
||||
check(R("Moderator"):has_player(alice), "a suppressed role is still held")
|
||||
check(not Roles.player_has_permission(alice, "exp_scenario.command.kill"), "a jailed player loses permissions")
|
||||
check(not Roles.player_has_permission(alice, "exp_scenario.gui.readme"), "a jailed player loses the default role")
|
||||
check(not Roles.player_outranks(alice, bob), "a jailed player no longer outranks")
|
||||
check(eq(env.events[1].data.assigned, { 7 }) and #env.events[1].data.unassigned == 0,
|
||||
"the jail event lists only the jail role")
|
||||
env.reset_log()
|
||||
R("Jail"):unassign(alice, { by_player_name = "<server>", silent = true })
|
||||
check(eq(env.sorted(env.names(Roles.get_player_roles(alice))), { "Moderator", "Player" }), "unjail restores the roles")
|
||||
check(#env.events[1].data.assigned == 0 and eq(env.events[1].data.unassigned, { 7 }),
|
||||
"the unjail event lists only the jail role")
|
||||
test("a rejection rolls the assignment back", function(env)
|
||||
local players = setup(env)
|
||||
env.R("Jail"):assign(players.alice)
|
||||
check(env.R("Jail"):has_player(players.alice), "the role applies before rejection")
|
||||
|
||||
-- The server can not be assigned roles
|
||||
env.reset_log()
|
||||
R("Moderator"):assign(env.server)
|
||||
check(#env.sent == 0 and #env.events == 0, "assigning to the server is a no-op")
|
||||
env.reset_log()
|
||||
env.Roles.reject_assignment{ name = "alice", role_ids = { 7 } }
|
||||
check(not env.R("Jail"):has_player(players.alice), "the rejected role is rolled back")
|
||||
check(#env.sent == 0, "the rollback is not sent to the controller")
|
||||
check(#env.events == 1 and eq(env.events[1].data.unassigned, { 7 }), "the rollback raises the event")
|
||||
|
||||
return Env.results_json(env)
|
||||
local sd = env.Roles._script_data()
|
||||
check(sd.local_players.alice == nil and sd.pending.alice == nil, "the rollback clears the local state")
|
||||
end)
|
||||
|
||||
test("local only roles never reach the controller", function(env)
|
||||
local players = setup(env)
|
||||
env.R("Regular"):assign(players.alice, { silent = true, local_only = true })
|
||||
check(#env.sent == 0, "a local only role is not sent")
|
||||
check(env.R("Regular"):has_player(players.alice), "a local only role applies")
|
||||
|
||||
local sd = env.Roles._script_data()
|
||||
check(sd.pending.alice == nil and eq(sd.local_players.alice, { 6 }), "a local only role is not pending")
|
||||
|
||||
env.Roles.receive_assignment_updates{ Env.assignment("alice", { 5 }) }
|
||||
check(env.R("Regular"):has_player(players.alice), "a local only role survives controller updates")
|
||||
|
||||
env.reset_log()
|
||||
env.R("Regular"):unassign(players.alice, { local_only = true })
|
||||
check(not env.R("Regular"):has_player(players.alice) and #env.sent == 0, "removed without sync")
|
||||
end)
|
||||
|
||||
test("jail suppresses every other role", function(env)
|
||||
local players = setup(env)
|
||||
env.R("Jail"):assign(players.alice, { by_player_name = "<server>", silent = true })
|
||||
check(eq(Test.names(env.Roles.get_player_roles(players.alice)), { "Jail" }), "only the jail role applies")
|
||||
check(env.R("Moderator"):has_player(players.alice), "a suppressed role is still held")
|
||||
check(not env.Roles.player_has_permission(players.alice, "exp_scenario.command.kill"), "permissions are lost")
|
||||
check(not env.Roles.player_has_permission(players.alice, "exp_scenario.gui.readme"), "the default role is lost")
|
||||
check(not env.Roles.player_outranks(players.alice, players.bob), "a jailed player no longer outranks")
|
||||
check(eq(env.events[1].data.assigned, { 7 }) and #env.events[1].data.unassigned == 0,
|
||||
"the event lists only the jail role")
|
||||
|
||||
env.reset_log()
|
||||
env.R("Jail"):unassign(players.alice, { by_player_name = "<server>", silent = true })
|
||||
check(eq(Test.sorted(Test.names(env.Roles.get_player_roles(players.alice))), { "Moderator", "Player" }),
|
||||
"unjail restores the roles")
|
||||
check(#env.events[1].data.assigned == 0 and eq(env.events[1].data.unassigned, { 7 }),
|
||||
"the unjail event lists only the jail role")
|
||||
end)
|
||||
|
||||
test("the server can not be assigned roles", function(env)
|
||||
setup(env)
|
||||
env.R("Moderator"):assign(env.server)
|
||||
check(#env.sent == 0 and #env.events == 0, "assigning to the server is a no-op")
|
||||
end)
|
||||
|
||||
return Env.finish()
|
||||
|
||||
+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
|
||||
|
||||
@@ -1,47 +1,76 @@
|
||||
--- Listing and printing to the players who hold a role
|
||||
local Env = ...
|
||||
local env = Env.new()
|
||||
local check, eq, R = env.check, env.eq, env.R
|
||||
local Roles = env.Roles
|
||||
local Test = Env.Test
|
||||
local test, check, eq = Test.test, Test.check, Test.eq
|
||||
|
||||
local alice = env.add_player("alice", 1)
|
||||
env.add_player("bob", 2)
|
||||
env.add_player("dave", 4, false) -- offline
|
||||
env.initialise{
|
||||
Env.assignment("alice", { 5 }),
|
||||
Env.assignment("bob", { 6 }),
|
||||
Env.assignment("dave", { 5 }),
|
||||
Env.assignment("zed", { 5 }), -- has never joined this map
|
||||
}
|
||||
Roles.set_emit_events(true)
|
||||
|
||||
local mod = R("Moderator")
|
||||
check(eq(env.sorted(mod:get_player_names()), { "alice", "dave", "zed" }),
|
||||
"player names include players not on this map")
|
||||
check(eq(env.sorted(env.names(mod:get_players())), { "alice", "dave" }),
|
||||
"players are limited to this map")
|
||||
check(eq(env.names(mod:get_players(true)), { "alice" }), "players can be filtered by connected state")
|
||||
check(eq(env.names(mod:get_players(false)), { "dave" }), "players can be filtered to offline")
|
||||
|
||||
-- A player holding the role in both the synced and the local list counts once
|
||||
R("Regular"):assign(alice, { silent = true, local_only = true })
|
||||
Roles.receive_assignment_updates{ Env.assignment("alice", { 5, 6 }) }
|
||||
local sd = Roles._script_data()
|
||||
check(sd.local_players.alice ~= nil and sd.synced_players.alice ~= nil, "the role is held in both lists")
|
||||
check(eq(env.sorted(R("Regular"):get_player_names()), { "alice", "bob" }),
|
||||
"a player in both lists is counted once")
|
||||
|
||||
env.reset_log()
|
||||
check(mod:print("hello") == 1, "print returns the number of players reached")
|
||||
check(#env.printed == 1 and env.printed[1].to == "alice", "print reaches only online holders")
|
||||
|
||||
env.reset_log()
|
||||
for _, role in ipairs(Roles.get_higher_roles(R("Regular"))) do
|
||||
role:print("hello")
|
||||
--- alice and dave are moderators with dave offline, zed has never joined
|
||||
local function setup(env)
|
||||
local players = {
|
||||
alice = env.add_player("alice", 1),
|
||||
bob = env.add_player("bob", 2),
|
||||
dave = env.add_player("dave", 4, false),
|
||||
}
|
||||
env.initialise{
|
||||
Env.assignment("alice", { 5 }),
|
||||
Env.assignment("bob", { 6 }),
|
||||
Env.assignment("dave", { 5 }),
|
||||
Env.assignment("zed", { 5 }),
|
||||
}
|
||||
env.Roles.set_emit_events(true)
|
||||
return players
|
||||
end
|
||||
local got = {}
|
||||
for _, entry in ipairs(env.printed) do got[#got + 1] = entry.to end
|
||||
-- alice holds two of the roles, so like the legacy system the message can repeat
|
||||
check(eq(env.sorted(got), { "alice", "alice", "bob" }), "printing to the higher roles reaches their online holders")
|
||||
|
||||
return Env.results_json(env)
|
||||
test("player names include players not on this map", function(env)
|
||||
setup(env)
|
||||
check(eq(Test.sorted(env.R("Moderator"):get_player_names()), { "alice", "dave", "zed" }),
|
||||
"every player given the role is listed")
|
||||
end)
|
||||
|
||||
test("players are limited to this map and can be filtered", function(env)
|
||||
setup(env)
|
||||
local mod = env.R("Moderator")
|
||||
check(eq(Test.sorted(Test.names(mod:get_players())), { "alice", "dave" }), "players are limited to this map")
|
||||
check(eq(Test.names(mod:get_players(true)), { "alice" }), "filtered to connected players")
|
||||
check(eq(Test.names(mod:get_players(false)), { "dave" }), "filtered to offline players")
|
||||
end)
|
||||
|
||||
test("a player holding the role in both lists is counted once", function(env)
|
||||
local players = setup(env)
|
||||
env.R("Regular"):assign(players.alice, { silent = true, local_only = true })
|
||||
env.Roles.receive_assignment_updates{ Env.assignment("alice", { 5, 6 }) }
|
||||
|
||||
local sd = env.Roles._script_data()
|
||||
check(sd.local_players.alice ~= nil and sd.synced_players.alice ~= nil, "the role is held in both lists")
|
||||
check(eq(Test.sorted(env.R("Regular"):get_player_names()), { "alice", "bob" }), "the player is counted once")
|
||||
end)
|
||||
|
||||
test("print reaches online holders", function(env)
|
||||
setup(env)
|
||||
env.reset_log()
|
||||
check(env.R("Moderator"):print("hello") == 1, "print returns the number of players reached")
|
||||
check(#env.printed == 1 and env.printed[1].to == "alice", "only online holders are reached")
|
||||
end)
|
||||
|
||||
test("printing to the higher roles", function(env)
|
||||
local players = setup(env)
|
||||
env.R("Regular"):assign(players.alice, { silent = true, local_only = true })
|
||||
env.reset_log()
|
||||
for _, role in ipairs(env.Roles.get_higher_roles(env.R("Regular"))) do
|
||||
role:print("hello")
|
||||
end
|
||||
|
||||
local got = {}
|
||||
for _, entry in ipairs(env.printed) do got[#got + 1] = entry.to end
|
||||
-- alice holds two of the roles, so like the legacy system the message can repeat
|
||||
check(eq(Test.sorted(got), { "alice", "alice", "bob" }), "the online holders of each role are reached")
|
||||
end)
|
||||
|
||||
test("deep equality helper", function(env)
|
||||
check(Test.deep_eq({ a = { 1, 2 }, b = "x" }, { a = { 1, 2 }, b = "x" }), "equal nested tables")
|
||||
check(not Test.deep_eq({ a = { 1, 2 } }, { a = { 1, 3 } }), "differing nested values")
|
||||
check(not Test.deep_eq({ a = 1 }, { a = 1, b = 2 }), "extra keys on the right")
|
||||
check(not Test.deep_eq({ a = 1, b = 2 }, { a = 1 }), "extra keys on the left")
|
||||
check(Test.deep_eq(env.Roles._script_data().pending, {}), "works against module state")
|
||||
end)
|
||||
|
||||
return Env.finish()
|
||||
|
||||
@@ -1,36 +1,48 @@
|
||||
--- Role lookup, decoding, and comparisons
|
||||
local Env = ...
|
||||
local env = Env.new()
|
||||
local check, eq, names, R = env.check, env.eq, env.names, env.R
|
||||
local Roles = env.Roles
|
||||
local Test = Env.Test
|
||||
local test, check, eq = Test.test, Test.check, Test.eq
|
||||
|
||||
env.initialise{}
|
||||
test("roles are ordered most privileged first", function(env)
|
||||
env.initialise{}
|
||||
check(eq(Test.names(env.Roles.get_ordered_roles()), { "Cluster Admin", "Moderator", "Regular", "Jail", "Player" }),
|
||||
"ordered roles follow their order with deleted roles skipped")
|
||||
check(#env.Roles.get_roles() == 5, "get_roles returns every role")
|
||||
|
||||
check(eq(names(Roles.get_ordered_roles()), { "Cluster Admin", "Moderator", "Regular", "Jail", "Player" }),
|
||||
"ordered roles are most privileged first with deleted roles skipped")
|
||||
check(#Roles.get_roles() == 5, "get_roles returns every role")
|
||||
check(Roles.get_role(6).name == "Regular", "get_role looks up by clusterio id")
|
||||
check(Roles.get_role_by_name("Moderator").id == 5, "get_role_by_name searches the roles")
|
||||
check(Roles.get_role(R("Jail").id) == R("Jail"), "lookups return the same object")
|
||||
check(Roles.get_role(9) == nil and Roles.get_role_by_name("Gone") == nil, "deleted roles are not known")
|
||||
check(Roles.get_default_role() == R("Player"), "the default role comes from is_default")
|
||||
local sorted = env.Roles.sort_roles{ env.R("Player"), env.R("Cluster Admin"), env.R("Regular") }
|
||||
check(eq(Test.names(sorted), { "Cluster Admin", "Regular", "Player" }), "sort_roles orders most privileged first")
|
||||
end)
|
||||
|
||||
check(R("Moderator").short_hand == "Mod" or R("Moderator").short_hand == "Moderator",
|
||||
"short hand falls back to the name")
|
||||
check(R("Cluster Admin").short_hand == "SYS", "short hand decoded")
|
||||
check(R("Moderator").color.g == 170, "color decoded")
|
||||
check(R("Jail").priority == 1 and R("Jail").block_auto_assign, "priority and block auto assign decoded")
|
||||
test("roles are looked up by id", function(env)
|
||||
env.initialise{}
|
||||
check(env.Roles.get_role(6).name == "Regular", "get_role looks up by clusterio id")
|
||||
check(env.Roles.get_role_by_name("Moderator").id == 5, "get_role_by_name searches the roles")
|
||||
check(env.Roles.get_role(env.R("Jail").id) == env.R("Jail"), "lookups return the same object")
|
||||
check(env.Roles.get_role(9) == nil and env.Roles.get_role_by_name("Gone") == nil, "deleted roles are not known")
|
||||
check(env.Roles.get_default_role() == env.R("Player"), "the default role comes from is_default")
|
||||
end)
|
||||
|
||||
check(R("Moderator"):is_higher_than(R("Player")), "roles compare on their order")
|
||||
check(R("Player"):is_lower_than(R("Moderator")), "is_lower_than is the reverse")
|
||||
check(not R("Moderator"):is_higher_than(R("Moderator")), "a role is not higher than itself")
|
||||
test("role records are decoded", function(env)
|
||||
env.initialise{}
|
||||
check(env.R("Cluster Admin").short_hand == "SYS", "short hand decoded")
|
||||
check(env.R("Moderator").short_hand == "Moderator", "short hand falls back to the name")
|
||||
check(env.R("Moderator").color.g == 170, "color decoded")
|
||||
check(env.R("Jail").priority == 1 and env.R("Jail").block_auto_assign, "priority and block auto assign decoded")
|
||||
end)
|
||||
|
||||
check(eq(env.sorted(names(Roles.get_higher_roles(R("Regular")))), { "Cluster Admin", "Moderator", "Regular" }),
|
||||
"higher roles include the role itself and exclude the default role")
|
||||
check(eq(env.sorted(names(Roles.get_lower_roles(R("Regular")))), { "Jail", "Regular" }),
|
||||
"lower roles include the role itself and exclude the default role")
|
||||
test("roles compare on their order", function(env)
|
||||
env.initialise{}
|
||||
check(env.R("Moderator"):is_higher_than(env.R("Player")), "a lower order is more privileged")
|
||||
check(env.R("Player"):is_lower_than(env.R("Moderator")), "is_lower_than is the reverse")
|
||||
check(not env.R("Moderator"):is_higher_than(env.R("Moderator")), "a role is not higher than itself")
|
||||
end)
|
||||
|
||||
local sorted = Roles.sort_roles{ R("Player"), R("Cluster Admin"), R("Regular") }
|
||||
check(eq(names(sorted), { "Cluster Admin", "Regular", "Player" }), "sort_roles orders most privileged first")
|
||||
test("higher and lower roles", function(env)
|
||||
env.initialise{}
|
||||
check(eq(Test.sorted(Test.names(env.Roles.get_higher_roles(env.R("Regular")))), { "Cluster Admin", "Moderator", "Regular" }),
|
||||
"higher roles include the role itself and exclude the default role")
|
||||
check(eq(Test.sorted(Test.names(env.Roles.get_lower_roles(env.R("Regular")))), { "Jail", "Regular" }),
|
||||
"lower roles include the role itself and exclude the default role")
|
||||
end)
|
||||
|
||||
return Env.results_json(env)
|
||||
return Env.finish()
|
||||
|
||||
@@ -1,56 +1,81 @@
|
||||
--- Player role and permission checks
|
||||
local Env = ...
|
||||
local env = Env.new()
|
||||
local check, eq, names, R = env.check, env.eq, env.names, env.R
|
||||
local Roles = env.Roles
|
||||
local Test = Env.Test
|
||||
local test, check, eq = Test.test, Test.check, Test.eq
|
||||
|
||||
local alice = env.add_player("alice", 1) -- moderator
|
||||
local bob = env.add_player("bob", 2) -- regular
|
||||
local carol = env.add_player("carol", 3) -- only the default role
|
||||
env.initialise{
|
||||
Env.assignment("alice", { 5 }),
|
||||
Env.assignment("bob", { 6 }),
|
||||
}
|
||||
--- alice is a moderator, bob is a regular, and carol has only the default role
|
||||
local function setup(env)
|
||||
local players = {
|
||||
alice = env.add_player("alice", 1),
|
||||
bob = env.add_player("bob", 2),
|
||||
carol = env.add_player("carol", 3),
|
||||
}
|
||||
env.initialise{
|
||||
Env.assignment("alice", { 5 }),
|
||||
Env.assignment("bob", { 6 }),
|
||||
}
|
||||
return players
|
||||
end
|
||||
|
||||
check(eq(env.sorted(names(Roles.get_player_roles(alice))), { "Moderator", "Player" }),
|
||||
"player roles include the default role")
|
||||
check(eq(names(Roles.get_player_roles(carol)), { "Player" }), "a player with no roles has the default role")
|
||||
check(eq(names(Roles.get_player_roles(nil)), { "<server>" }), "nil is the server")
|
||||
check(eq(names(Roles.get_player_roles(env.server)), { "<server>" }), "a player with index 0 is the server")
|
||||
check(Roles.get_player_highest_role(alice) == R("Moderator"), "highest role")
|
||||
check(Roles.get_player_highest_role(carol) == R("Player"), "highest role is the default role when none are held")
|
||||
test("player roles include the default role", function(env)
|
||||
local players = setup(env)
|
||||
check(eq(Test.sorted(Test.names(env.Roles.get_player_roles(players.alice))), { "Moderator", "Player" }),
|
||||
"held roles and the default role")
|
||||
check(eq(Test.names(env.Roles.get_player_roles(players.carol)), { "Player" }),
|
||||
"a player with no roles has the default role")
|
||||
end)
|
||||
|
||||
check(Roles.player_has_permission(alice, "exp_scenario.command.kill"), "permission through a held role")
|
||||
check(Roles.player_has_permission(alice, "exp_scenario.gui.readme"), "permission through the default role")
|
||||
check(not Roles.player_has_permission(bob, "exp_scenario.command.jail"), "permission not granted")
|
||||
check(Roles.player_has_permission(nil, "anything.at.all"), "the server has every permission")
|
||||
check(Roles.player_has_permission(env.server, "anything.at.all"), "an index 0 player has every permission")
|
||||
test("the server is nil or a player with index 0", function(env)
|
||||
setup(env)
|
||||
check(eq(Test.names(env.Roles.get_player_roles(nil)), { "<server>" }), "nil is the server")
|
||||
check(eq(Test.names(env.Roles.get_player_roles(env.server)), { "<server>" }), "index 0 is the server")
|
||||
check(env.Roles.player_has_permission(nil, "anything.at.all"), "the server has every permission")
|
||||
check(not env.R("Player"):has_player(nil), "the server does not have the default role")
|
||||
end)
|
||||
|
||||
check(Roles.player_has_any_permission(bob, "exp_scenario.command.jail", "exp_scenario.command.kill"),
|
||||
"has any passes when one permission is granted")
|
||||
check(not Roles.player_has_any_permission(bob, "exp_scenario.command.jail", "exp_scenario.command.assign_role"),
|
||||
"has any fails when none are granted")
|
||||
check(not Roles.player_has_any_permission(bob), "has any with no permissions is false")
|
||||
check(Roles.player_has_all_permission(alice, "exp_scenario.command.jail", "exp_scenario.command.kill"),
|
||||
"has all passes when every permission is granted")
|
||||
check(not Roles.player_has_all_permission(bob, "exp_scenario.command.jail", "exp_scenario.command.kill"),
|
||||
"has all fails when one is missing")
|
||||
check(Roles.player_has_all_permission(bob), "has all with no permissions is true")
|
||||
check(Roles.player_has_any_permission(nil, "anything.at.all"), "the server passes has any")
|
||||
test("the highest role", function(env)
|
||||
local players = setup(env)
|
||||
check(env.Roles.get_player_highest_role(players.alice) == env.R("Moderator"), "highest held role")
|
||||
check(env.Roles.get_player_highest_role(players.carol) == env.R("Player"), "the default role when none are held")
|
||||
end)
|
||||
|
||||
check(R("Moderator"):has_permission("exp_scenario.command.kill"), "role has_permission")
|
||||
check(not R("Regular"):has_permission("exp_scenario.command.jail"), "role has_permission not granted")
|
||||
check(R("Cluster Admin"):has_permission("anything.at.all"), "core.admin grants everything")
|
||||
test("permission checks", function(env)
|
||||
local players = setup(env)
|
||||
check(env.Roles.player_has_permission(players.alice, "exp_scenario.command.kill"), "granted by a held role")
|
||||
check(env.Roles.player_has_permission(players.alice, "exp_scenario.gui.readme"), "granted by the default role")
|
||||
check(not env.Roles.player_has_permission(players.bob, "exp_scenario.command.jail"), "not granted")
|
||||
end)
|
||||
|
||||
check(R("Moderator"):has_player(alice), "has_player for a held role")
|
||||
check(not R("Moderator"):has_player(bob), "has_player for a role not held")
|
||||
check(R("Player"):has_player(carol), "everyone has the default role")
|
||||
check(not R("Player"):has_player(nil), "the server does not have the default role")
|
||||
test("has any and has all", function(env)
|
||||
local players = setup(env)
|
||||
local jail, kill = "exp_scenario.command.jail", "exp_scenario.command.kill"
|
||||
check(env.Roles.player_has_any_permission(players.bob, jail, kill), "any passes when one is granted")
|
||||
check(not env.Roles.player_has_any_permission(players.bob, jail, "exp_scenario.command.assign_role"),
|
||||
"any fails when none are granted")
|
||||
check(not env.Roles.player_has_any_permission(players.bob), "any of none is false")
|
||||
check(env.Roles.player_has_all_permission(players.alice, jail, kill), "all passes when every one is granted")
|
||||
check(not env.Roles.player_has_all_permission(players.bob, jail, kill), "all fails when one is missing")
|
||||
check(env.Roles.player_has_all_permission(players.bob), "all of none is true")
|
||||
check(env.Roles.player_has_any_permission(nil, "anything.at.all"), "the server passes any")
|
||||
end)
|
||||
|
||||
check(Roles.player_outranks(alice, bob), "a higher role outranks a lower one")
|
||||
check(not Roles.player_outranks(bob, alice), "a lower role does not outrank a higher one")
|
||||
check(not Roles.player_outranks(alice, alice), "nobody outranks themselves")
|
||||
check(Roles.player_outranks(nil, alice), "the server outranks everyone")
|
||||
check(not Roles.player_outranks(alice, nil), "nobody outranks the server")
|
||||
test("role permission and player methods", function(env)
|
||||
local players = setup(env)
|
||||
check(env.R("Moderator"):has_permission("exp_scenario.command.kill"), "has_permission granted")
|
||||
check(not env.R("Regular"):has_permission("exp_scenario.command.jail"), "has_permission not granted")
|
||||
check(env.R("Cluster Admin"):has_permission("anything.at.all"), "core.admin grants everything")
|
||||
check(env.R("Moderator"):has_player(players.alice), "has_player for a held role")
|
||||
check(not env.R("Moderator"):has_player(players.bob), "has_player for a role not held")
|
||||
check(env.R("Player"):has_player(players.carol), "everyone has the default role")
|
||||
end)
|
||||
|
||||
return Env.results_json(env)
|
||||
test("outranks", function(env)
|
||||
local players = setup(env)
|
||||
check(env.Roles.player_outranks(players.alice, players.bob), "a higher role outranks a lower one")
|
||||
check(not env.Roles.player_outranks(players.bob, players.alice), "a lower role does not outrank a higher one")
|
||||
check(not env.Roles.player_outranks(players.alice, players.alice), "nobody outranks themselves")
|
||||
check(env.Roles.player_outranks(nil, players.alice), "the server outranks everyone")
|
||||
check(not env.Roles.player_outranks(players.alice, nil), "nobody outranks the server")
|
||||
end)
|
||||
|
||||
return Env.finish()
|
||||
|
||||
+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