extends SceneTree
## Gate: AdminFacade's per-property / per-method configuration is SETTABLE.
##
## From 2026-05-18 until today it was not. The facade composed meta keys like
## `admin_role:<prop>:write` and `admin_call_scope:<method>`, and
## Object::set_meta REJECTS any name failing is_valid_ascii_identifier()
## (core/object/object.cpp:1036) -- a colon is not one. `git log -L` dates that
## guard to 2022-03-23, so it held for all of Godot 4: nothing could ever set
## them, every lookup fell back to its default, and the whole configuration
## surface was decorative. Zero writers existed in this repo's history.
##
## Tests the RESOLVERS directly rather than driving a full facade scan: the
## change is two pure helpers plus one dictionary read, and _scan() needs a
## current_scene that a bare SceneTree script does not have. An earlier version
## of this gate DID drive the scan, indexed nothing, and reported three passes
## that were really "unknown path" -- a broken instrument reading as success.
var ok := true
func _p(c: bool, m: String) -> void:
print(("ok " if c else "FAIL: ") + m)
if not c: ok = false
func _initialize() -> void:
var facade: Object = load("res://autoloads/admin_facade.gd").new()
var target := Node.new()
target.set_meta("admin_roles", {
"secret": {"read": "admin", "write": "admin"},
"volume": {"read": "peer", "write": "peer"},
"peek": {"call": "peer"},
"everything": "mod", # shorthand: one role for every verb
})
target.set_meta("admin_call_scopes", {"peek": "local"})
target.set_meta("admin_exposed_props", {"volume": true})
# 1. the metas are SETTABLE at all -- the entire defect
_p(target.has_meta("admin_roles"), "admin_roles is settable")
_p(target.has_meta("admin_call_scopes"), "admin_call_scopes is settable")
target.set_meta("admin_role:secret:write", "admin")
_p(not target.has_meta("admin_role:secret:write"),
"the OLD colon form is still rejected by the engine")
# 2. per-property roles resolve, and fall back where unset
_p(facade._role_for(target, "volume", "write", "admin") == "peer",
"volume write role -> peer (overrides the admin default)")
_p(facade._role_for(target, "secret", "read", "peer") == "admin",
"secret read role -> admin (overrides the peer default)")
_p(facade._role_for(target, "unlisted", "write", "admin") == "admin",
"unlisted prop falls back to the documented default")
_p(facade._role_for(target, "everything", "call", "admin") == "mod",
"string shorthand applies to every verb")
# 3. per-method call scope
var scopes: Dictionary = target.get_meta("admin_call_scopes", {})
_p(String(scopes.get("peek", "")) == "local", "peek scope -> local")
_p(String(scopes.get("bump", "authority")) == "authority",
"unlisted method falls back to authority")
# 4. explicit opt-in is no longer a trap. admin_exposed_only is settable (no
# colon) but gated on `admin_exposed:<prop>` which was not -- so enabling
# it exposed ZERO properties, silently.
_p(facade._prop_opted_in(target, "volume"), "opted-in prop reads as explicit")
_p(not facade._prop_opted_in(target, "secret"), "non-opted prop does not")
target.set_meta("admin_exposed_props", ["volume"])
_p(facade._prop_opted_in(target, "volume"), "array form works too")
# 5. a skipped call must not read as success (it could clobber the real
# result: every peer dispatches and writes the same admin:result:<seq>,
# LWW-by-ts).
var skipped := {"ok": false, "skipped": true, "error": "not authority"}
_p(not bool(skipped.get("ok", false)) and bool(skipped.get("skipped", false)),
"skipped shape is ok:false + skipped:true")
target.free()
print("GATE admin_facade_meta: %s" % ("PASS" if ok else "FAIL"))
quit(0 if ok else 1)