mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-11-12 07:54:52 +09:00
- Feature.register returns the config as a second value, so files write `local feature, config = Feature.register(...)`. - The lib is ExpScenario.Features and the instance class with its fields sits on Feature._prototype, which is the __index, the same shape as exp_commands. - update_config only looks at values whose override was added, removed, or replaced. _apply_field sets one value and returns it, so the comparison happens while applying and the second loop only raises. Overrides dropped from the previous set count as candidates because the value reverts to its default. - The `@setting name` tags are gone from the config comments. - Protection is guarded as a whole instead of checking config.enabled in one handler. The handlers also forget mined entities, so get_entities and is_entity_protected now drop invalid references, which also covers entities destroyed without an event. The list getters are gone, the selection overlay builds its lists from Protection.config. - Chat and damage popups are one feature in control/popups.lua. - ignore_admins and command_admin_only are removed, roles cover them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
167 lines
8.4 KiB
Lua
167 lines
8.4 KiB
Lua
local Suite = ... --- @type Suite<ExpScenario.TestEnv>
|
|
|
|
--- The values of on_config_updated events, ignoring the event name and tick
|
|
local function updates(env)
|
|
local rtn = {}
|
|
for _, event in ipairs(env.events) do
|
|
rtn[#rtn + 1] = { event.feature_name, event.path, event.old_value, event.new_value }
|
|
end
|
|
return rtn
|
|
end
|
|
|
|
Suite.test("register() enables features unless the config says otherwise", function(env)
|
|
Suite.eq(env.Feature.register("on", { value = 1 }).config.enabled, true, "enabled by default")
|
|
Suite.eq(env.Feature.register("off", { enabled = false }):is_enabled(), false, "the config can disable it")
|
|
Suite.eq(env.Feature.register("empty").config, { enabled = true }, "config is optional")
|
|
Suite.throws(function() env.Feature.register("on", {}) end, "Feature already registered: on", "names are unique")
|
|
end)
|
|
|
|
Suite.test("get() finds registered features", function(env)
|
|
local feature = env.Feature.register("test")
|
|
Suite.eq(env.Feature.get("test") == feature, true, "by name")
|
|
Suite.throws(function() env.Feature.get("unknown") end, "Unknown feature: unknown", "unknown names error")
|
|
end)
|
|
|
|
Suite.test("update_config() changes the config in place", function(env)
|
|
local feature = env.Feature.register("test", { flag = true, count = 5, text = "a" })
|
|
local config = feature.config
|
|
feature:update_config(false, { flag = false, count = 7 })
|
|
Suite.eq(config, { enabled = false, flag = false, count = 7, text = "a" }, "given values replace the defaults")
|
|
|
|
feature:update_config(true, { text = "b" })
|
|
Suite.eq(config, { enabled = true, flag = true, count = 5, text = "b" }, "values left out go back to their default")
|
|
Suite.eq(env.storage, { test = { enabled = true, text = "b" } }, "the overrides are kept in storage")
|
|
end)
|
|
|
|
Suite.test("update_config() raises on_config_updated for each changed value", function(env)
|
|
local feature = env.Feature.register("test", { flag = true, names = env.Feature.set{ "a" }, section = { count = 1 } })
|
|
feature:update_config(false, { names = { "a" }, ["section.count"] = 2 })
|
|
Suite.eq(#env.events, 2, "unchanged values raise nothing")
|
|
Suite.eq(env.Feature.events.on_config_updated, assert(env.events[1]).name, "the event is on_config_updated")
|
|
|
|
local seen = updates(env)
|
|
table.sort(seen, function(a, b) return a[2] < b[2] end)
|
|
Suite.eq(seen, {
|
|
{ "test", "enabled", true, false },
|
|
{ "test", "section.count", 1, 2 },
|
|
}, "with the path, old value, and new value")
|
|
end)
|
|
|
|
Suite.test("update_config() ignores and logs values which can not be overridden", function(env)
|
|
local function check() return true end
|
|
local feature = env.Feature.register("test", { flag = true, check = check, icon = nil })
|
|
feature:update_config(true, { flag = "yes", check = false, icon = "x" })
|
|
Suite.eq(feature.config, { enabled = true, flag = true, check = check }, "wrong types, functions, and unknown keys are skipped")
|
|
Suite.eq(#env.logged, 3, "each one is logged")
|
|
Suite.eq(env.storage.test, { enabled = true, flag = "yes", check = false }, "unknown keys are not stored")
|
|
end)
|
|
|
|
Suite.test("receive_update() updates known features", function(env)
|
|
local feature = env.Feature.register("test", { flag = true })
|
|
env.Feature.receive_update{
|
|
{ name = "test", enabled = true, values = { flag = false } },
|
|
{ name = "unknown", enabled = false, values = {} },
|
|
}
|
|
Suite.eq(feature.config.flag, false, "the known feature changed")
|
|
Suite.eq(env.storage.unknown, nil, "unknown features are not stored")
|
|
end)
|
|
|
|
Suite.test("on_load applies the overrides from storage without raising events", function(env)
|
|
local feature = env.Feature.register("test", { flag = true })
|
|
env.on_load{ test = { enabled = false, flag = false } }
|
|
Suite.eq(feature.config, { enabled = false, flag = false }, "the saved overrides apply")
|
|
Suite.empty(env.events, "nothing is raised")
|
|
|
|
feature:update_config(true, {})
|
|
Suite.eq(env.storage, { test = { enabled = true } }, "later updates go to the loaded storage table")
|
|
end)
|
|
|
|
Suite.test("guard() skips handlers while the feature is disabled", function(env)
|
|
local feature = env.Feature.register("test")
|
|
local calls = {}
|
|
local lib = feature:guard{
|
|
events = { on_event = function(event) calls[#calls + 1] = event end },
|
|
on_nth_tick = { [60] = function(event) calls[#calls + 1] = event end },
|
|
on_init = function() end,
|
|
}
|
|
|
|
lib.events.on_event("a")
|
|
lib.on_nth_tick[60]("b")
|
|
feature:update_config(false, {})
|
|
lib.events.on_event("c")
|
|
lib.on_nth_tick[60]("d")
|
|
Suite.eq(calls, { "a", "b" }, "only called while enabled")
|
|
end)
|
|
|
|
Suite.test("register() addresses nested values by path", function(env)
|
|
local offset = { 1, 2 }
|
|
local feature = env.Feature.register("test", { section = { flag = true, count = 1 }, offset = offset })
|
|
feature:update_config(true, { ["section.flag"] = false, offset = { "x" } })
|
|
Suite.eq(feature.config.section, { flag = false, count = 1 }, "the nested value changes in place")
|
|
Suite.eq(feature.config.offset, offset, "tables which are not sections can not be overridden")
|
|
|
|
feature:update_config(true, {})
|
|
Suite.eq(feature.config.section.flag, true, "and goes back to its default")
|
|
end)
|
|
|
|
Suite.test("list() declares a list of strings which can be overridden", function(env)
|
|
local feature = env.Feature.register("test", { names = env.Feature.list{ "a", "b" }, empty = env.Feature.list{}, plain = { "x" } })
|
|
Suite.eq(feature.config.names, { "a", "b" }, "the default is the list")
|
|
feature:update_config(true, { names = { "c" }, empty = { "d" }, plain = { "y" } })
|
|
Suite.eq(feature.config.names, { "c" }, "a list is replaced")
|
|
Suite.eq(feature.config.empty, { "d" }, "an empty list can be overridden")
|
|
Suite.eq(feature.config.plain, { "x" }, "a plain table can not be overridden")
|
|
feature:update_config(true, { names = { 1 } })
|
|
Suite.eq(feature.config.names, { "a", "b" }, "a list with other types is invalid")
|
|
end)
|
|
|
|
Suite.test("set() declares a set of strings which is overridden with a list", function(env)
|
|
local feature = env.Feature.register("test", { names = env.Feature.set{ "a" }, empty = env.Feature.set{} })
|
|
Suite.eq(feature.config.names, { a = true }, "the default is a set")
|
|
Suite.eq(feature.config.empty, {}, "and can be empty")
|
|
feature:update_config(true, { names = { "b", "c" } })
|
|
Suite.eq(feature.config.names, { b = true, c = true }, "the list is converted to a set")
|
|
env.on_load{ test = { enabled = true, names = { "d" } } }
|
|
Suite.eq(feature.config.names, { d = true }, "also when the overrides are loaded")
|
|
end)
|
|
|
|
Suite.test("optional() declares a value without a default", function(env)
|
|
local feature = env.Feature.register("test", { role_id = env.Feature.optional("number") })
|
|
Suite.eq(feature.config.role_id, nil, "nil until overridden")
|
|
feature:update_config(true, { role_id = 3 })
|
|
Suite.eq(feature.config.role_id, 3, "overridden with the given type")
|
|
feature:update_config(true, {})
|
|
Suite.eq(feature.config.role_id, nil, "and back to nil")
|
|
end)
|
|
|
|
|
|
Suite.test("register() returns the config as well", function(env)
|
|
local feature, config = env.Feature.register("test", { flag = true })
|
|
Suite.eq(config == feature.config, true, "the same table")
|
|
end)
|
|
|
|
Suite.test("update_config() raises when an override is removed", function(env)
|
|
local feature = env.Feature.register("test", { flag = true, count = 1 })
|
|
feature:update_config(true, { flag = false })
|
|
env.events = {}
|
|
feature:update_config(true, {})
|
|
Suite.eq(updates(env), { { "test", "flag", false, true } }, "the value goes back to its default")
|
|
end)
|
|
|
|
Suite.test("update_config() skips stored overrides for settings which no longer exist", function(env)
|
|
local feature = env.Feature.register("test", { flag = true })
|
|
env.on_load{ test = { enabled = true, gone = 1 } }
|
|
feature:update_config(true, { flag = false })
|
|
Suite.eq(updates(env), { { "test", "flag", true, false } }, "only the known value is compared")
|
|
Suite.eq(env.storage.test, { enabled = true, flag = false }, "and the stale key is not kept")
|
|
end)
|
|
|
|
Suite.test("config files register their feature", function(env)
|
|
local feature = env.load_config("spawn_area")
|
|
feature:update_config(true, { ["turrets.enabled"] = false })
|
|
Suite.eq(feature.config.turrets.enabled, false, "spawn_area can be changed")
|
|
Suite.eq(feature.config.turrets.ammo_type, "uranium-rounds-magazine", "other values keep their default")
|
|
end)
|
|
|
|
return Suite.run()
|