refactor: enhance test framework with automated resource tracking and scripted error capture capabilities
This commit is contained in:
+124
-1
@@ -227,6 +227,7 @@ function admin.rpc_admin_clear_global_chat(context, payload)
|
||||
if channelId == "" then
|
||||
error("channel_id is required. Pass the channel ID from the client.")
|
||||
end
|
||||
channelId = utils.resolve_channel_id(channelId)
|
||||
|
||||
local deleted = 0
|
||||
local cursor = ""
|
||||
@@ -319,6 +320,121 @@ function admin.rpc_admin_delete_users(context, payload)
|
||||
return nk.json_encode({ success = true, deleted = deleted, failed = failed })
|
||||
end
|
||||
|
||||
function admin.rpc_admin_get_user_detail(context, payload)
|
||||
utils.require_admin(context)
|
||||
local request = nk.json_decode(payload or "{}")
|
||||
local userId = request.user_id or ""
|
||||
if userId == "" then error("user_id is required") end
|
||||
|
||||
local account = nk.account_get_id(userId)
|
||||
local metadata = {}
|
||||
if account.user.metadata then
|
||||
local s, m = pcall(nk.json_decode, account.user.metadata)
|
||||
if s and m then metadata = m end
|
||||
end
|
||||
|
||||
local friends = {}
|
||||
local okFriends, friendResult = pcall(nk.friends_list, userId, nil, 100, "")
|
||||
if okFriends and friendResult and friendResult.friends then
|
||||
for _, f in pairs(friendResult.friends) do
|
||||
table.insert(friends, {
|
||||
user_id = f.user.id,
|
||||
username = f.user.username,
|
||||
display_name = f.user.display_name,
|
||||
state = f.state
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local purchaseHistory = {}
|
||||
local okReceipts, receiptResult = pcall(nk.storage_list, userId, "receipts", 100, "")
|
||||
if okReceipts and receiptResult and receiptResult.objects then
|
||||
for _, obj in pairs(receiptResult.objects) do
|
||||
table.insert(purchaseHistory, { key = obj.key, value = obj.value, update_time = obj.update_time })
|
||||
end
|
||||
end
|
||||
|
||||
local collections = request.collections or {"profiles", "stats", "inventory", "receipts", "history", "matches", "inbox"}
|
||||
local storage = {}
|
||||
for _, collection in ipairs(collections) do
|
||||
local okStorage, storageResult = pcall(nk.storage_list, userId, collection, 100, "")
|
||||
storage[collection] = {}
|
||||
if okStorage and storageResult and storageResult.objects then
|
||||
for _, obj in pairs(storageResult.objects) do
|
||||
table.insert(storage[collection], { key = obj.key, value = obj.value, version = obj.version, update_time = obj.update_time })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local walletLedger = {}
|
||||
local okLedger, ledgerResult = pcall(nk.wallet_ledger_list, userId, 50)
|
||||
if okLedger and ledgerResult then walletLedger = ledgerResult.items or {} end
|
||||
|
||||
return nk.json_encode({
|
||||
user = {
|
||||
user_id = account.user.id,
|
||||
username = account.user.username or "",
|
||||
display_name = account.user.display_name or "",
|
||||
avatar_url = account.user.avatar_url or "",
|
||||
lang_tag = account.user.lang_tag or "",
|
||||
location = account.user.location or "",
|
||||
timezone = account.user.timezone or "",
|
||||
create_time = account.user.create_time or "",
|
||||
update_time = account.user.update_time or "",
|
||||
metadata = metadata,
|
||||
wallet = account.wallet or {},
|
||||
email = account.email or "",
|
||||
email_verified = account.email_verified or false
|
||||
},
|
||||
friends = friends,
|
||||
purchases = purchaseHistory,
|
||||
wallet_ledger = walletLedger,
|
||||
storage = storage,
|
||||
subscription = metadata.subscription or metadata.subscriptions or {}
|
||||
})
|
||||
end
|
||||
|
||||
function admin.rpc_admin_update_user_identity(context, payload)
|
||||
utils.require_admin(context)
|
||||
local request = nk.json_decode(payload or "{}")
|
||||
local userId = request.user_id or ""
|
||||
if userId == "" then error("user_id is required") end
|
||||
|
||||
local account = nk.account_get_id(userId)
|
||||
local metadata = {}
|
||||
if account.user.metadata then
|
||||
local s, m = pcall(nk.json_decode, account.user.metadata)
|
||||
if s and m then metadata = m end
|
||||
end
|
||||
if request.metadata and type(request.metadata) == "table" then
|
||||
for k, v in pairs(request.metadata) do metadata[k] = v end
|
||||
end
|
||||
|
||||
nk.account_update_id(
|
||||
userId,
|
||||
request.username or account.user.username,
|
||||
request.display_name or account.user.display_name,
|
||||
request.timezone or account.user.timezone,
|
||||
request.location or account.user.location,
|
||||
request.lang_tag or account.user.lang_tag,
|
||||
request.avatar_url or account.user.avatar_url,
|
||||
nk.json_encode(metadata)
|
||||
)
|
||||
return nk.json_encode({ success = true })
|
||||
end
|
||||
|
||||
function admin.rpc_admin_set_user_password(context, payload)
|
||||
utils.require_admin(context)
|
||||
local request = nk.json_decode(payload or "{}")
|
||||
local userId = request.user_id or ""
|
||||
local password = request.password or ""
|
||||
if userId == "" or password == "" then error("user_id and password are required") end
|
||||
local account = nk.account_get_id(userId)
|
||||
if not account.email or account.email == "" then error("User has no email credential") end
|
||||
nk.account_update_id(userId, nil, nil, nil, nil, nil, nil, nil, account.email, password)
|
||||
return nk.json_encode({ success = true })
|
||||
end
|
||||
|
||||
function admin.rpc_admin_get_player_list(context, payload)
|
||||
local request = nk.json_decode(payload)
|
||||
utils.require_admin_or_host(context, request.match_id)
|
||||
@@ -386,6 +502,7 @@ function admin.rpc_admin_purge_old_messages(context, payload)
|
||||
if channelId == "" then
|
||||
error("channel_id is required")
|
||||
end
|
||||
channelId = utils.resolve_channel_id(channelId)
|
||||
if maxAgeDays <= 0 then
|
||||
error("max_age_days must be > 0")
|
||||
end
|
||||
@@ -441,6 +558,7 @@ function admin.rpc_admin_list_channel_messages(context, payload)
|
||||
error("channel_id is required")
|
||||
end
|
||||
|
||||
channelId = utils.resolve_channel_id(channelId)
|
||||
local status, result = pcall(nk.channel_messages_list, channelId, limit, forward, cursor)
|
||||
if not status then
|
||||
error("Failed to list messages: " .. tostring(result))
|
||||
@@ -448,7 +566,7 @@ function admin.rpc_admin_list_channel_messages(context, payload)
|
||||
|
||||
local msgs = {}
|
||||
if result and result.messages then
|
||||
for _, msg in ipairs(result.messages) do
|
||||
for _, msg in pairs(result.messages) do
|
||||
table.insert(msgs, {
|
||||
message_id = msg.message_id,
|
||||
sender_id = msg.sender_id,
|
||||
@@ -463,6 +581,7 @@ function admin.rpc_admin_list_channel_messages(context, payload)
|
||||
|
||||
return nk.json_encode({
|
||||
messages = msgs,
|
||||
channel_id = channelId,
|
||||
next_cursor = result.next_cursor or "",
|
||||
cache_cursor = result.cache_cursor or ""
|
||||
})
|
||||
@@ -478,6 +597,7 @@ function admin.rpc_admin_delete_channel_message(context, payload)
|
||||
error("channel_id and message_id are required")
|
||||
end
|
||||
|
||||
channelId = utils.resolve_channel_id(channelId)
|
||||
local status, err = pcall(nk.channel_message_remove, channelId, messageId)
|
||||
if not status then
|
||||
error("Failed to delete message: " .. tostring(err))
|
||||
@@ -500,6 +620,9 @@ nk.register_rpc(admin.rpc_admin_topup_gold, "admin_topup_gold")
|
||||
nk.register_rpc(admin.rpc_admin_clear_global_chat, "admin_clear_global_chat")
|
||||
nk.register_rpc(admin.rpc_admin_list_users, "admin_list_users")
|
||||
nk.register_rpc(admin.rpc_admin_delete_users, "admin_delete_users")
|
||||
nk.register_rpc(admin.rpc_admin_get_user_detail, "admin_get_user_detail")
|
||||
nk.register_rpc(admin.rpc_admin_update_user_identity, "admin_update_user_identity")
|
||||
nk.register_rpc(admin.rpc_admin_set_user_password, "admin_set_user_password")
|
||||
nk.register_rpc(admin.rpc_admin_get_chat_config, "admin_get_chat_config")
|
||||
nk.register_rpc(admin.rpc_admin_set_chat_config, "admin_set_chat_config")
|
||||
nk.register_rpc(admin.rpc_admin_purge_old_messages, "admin_purge_old_messages")
|
||||
|
||||
@@ -14,7 +14,7 @@ function leaderboard.rpc_get_leaderboard_stats(context, payload)
|
||||
local leaderboardData = {}
|
||||
local ownerRecords = records_or_err.records or {}
|
||||
|
||||
for _, record in ipairs(ownerRecords) do
|
||||
for _, record in pairs(ownerRecords) do
|
||||
local metadata = {}
|
||||
if record.metadata then
|
||||
local s, m = pcall(nk.json_decode, record.metadata)
|
||||
|
||||
@@ -50,4 +50,34 @@ function utils.require_admin_or_host(context, match_id)
|
||||
end
|
||||
end
|
||||
|
||||
-- Channel type constants for nk.channel_id_build (Nakama Lua runtime).
|
||||
-- NOTE: these differ from the Godot client's NakamaSocket.ChannelType enum.
|
||||
utils.CHANNEL_TYPE_ROOM = 1
|
||||
utils.CHANNEL_TYPE_DIRECT = 2
|
||||
utils.CHANNEL_TYPE_GROUP = 3
|
||||
|
||||
-- Resolve a chat channel identifier for admin chat RPCs.
|
||||
--
|
||||
-- The client may send either an already-hashed Nakama channel ID, or a friendly
|
||||
-- room name (e.g. "social_global"). A raw room name is NOT a valid channel ID for
|
||||
-- nk.channel_messages_list, so we build the canonical hashed ID via the
|
||||
-- authoritative nk.channel_id_build API (system user as sender → global room).
|
||||
-- Returns the resolved channel ID, or the original value if it already looks
|
||||
-- hashed / can't be built.
|
||||
function utils.resolve_channel_id(channel_id)
|
||||
if not channel_id or channel_id == "" then
|
||||
return ""
|
||||
end
|
||||
-- A hashed Nakama channel ID contains a '.' separator; a plain room name does
|
||||
-- not. Only treat dot-free values as room names needing resolution.
|
||||
if string.find(channel_id, "%.") then
|
||||
return channel_id
|
||||
end
|
||||
local status, built = pcall(nk.channel_id_build, "", channel_id, utils.CHANNEL_TYPE_ROOM)
|
||||
if status and built and built ~= "" then
|
||||
return built
|
||||
end
|
||||
return channel_id
|
||||
end
|
||||
|
||||
return utils
|
||||
|
||||
Reference in New Issue
Block a user