145 lines
5.3 KiB
JavaScript
145 lines
5.3 KiB
JavaScript
/**
|
|
* rbxl-lua-services.test.js — тесты Humanoid, RemoteEvent, DataStore, HttpService.
|
|
*/
|
|
import { LuaFactory } from 'wasmoon';
|
|
import { registerRobloxApi } from '../src/engine/roblox-shim.js';
|
|
import { RobloxScheduler } from '../src/engine/roblox-scheduler.js';
|
|
import { installRobloxServices } from '../src/engine/roblox-services.js';
|
|
|
|
const SCENE = { primitives: {} };
|
|
|
|
const STORE = new Map();
|
|
|
|
async function run(luaSource, ticks = []) {
|
|
const factory = new LuaFactory();
|
|
const lua = await factory.createEngine();
|
|
const sent = [];
|
|
const send = (cmd, payload) => sent.push({ cmd, payload });
|
|
let playerState = { x: 0, y: 5, z: 0 };
|
|
|
|
registerRobloxApi(lua, { getSceneSnap: () => SCENE, targetPrimitiveId: null, send });
|
|
const sched = new RobloxScheduler(lua);
|
|
sched.install();
|
|
installRobloxServices(lua, {
|
|
send,
|
|
getPlayerState: () => playerState,
|
|
loadSave: (k) => STORE.get(k),
|
|
saveSave: (k, v) => STORE.set(k, v),
|
|
removeSave: (k) => STORE.delete(k),
|
|
});
|
|
|
|
await sched.spawnMain(luaSource);
|
|
for (const dt of ticks) await sched.tick(dt);
|
|
lua.global.close();
|
|
return { logs: sent.filter(s => s.cmd === 'log').map(s => s.payload),
|
|
playerCmds: sent.filter(s => s.cmd === 'playerCmd').map(s => s.payload),
|
|
broadcasts: sent.filter(s => s.cmd === 'broadcast').map(s => s.payload) };
|
|
}
|
|
|
|
const TESTS = [
|
|
{
|
|
name: 'Players.LocalPlayer.Character.Humanoid существует',
|
|
lua: `
|
|
local p = game:GetService("Players").LocalPlayer
|
|
local h = p.Character:WaitForChild("Humanoid")
|
|
print("hp:", h.Health, "ws:", h.WalkSpeed)
|
|
`,
|
|
expect: [{ level: 'info', text: 'hp:\t100\tws:\t16' }],
|
|
},
|
|
{
|
|
name: 'Humanoid.WalkSpeed = 50 → playerCmd setWalkSpeed',
|
|
lua: `
|
|
local h = game:GetService("Players").LocalPlayer.Character.Humanoid
|
|
h.WalkSpeed = 50
|
|
`,
|
|
expectPlayerCmd: { method: 'setWalkSpeed', argsCheck: (a) => a[0] === 50 },
|
|
},
|
|
{
|
|
name: 'Humanoid:TakeDamage уменьшает HP',
|
|
lua: `
|
|
local h = game:GetService("Players").LocalPlayer.Character.Humanoid
|
|
h:TakeDamage(30)
|
|
print("after damage:", h.Health)
|
|
`,
|
|
expect: [{ level: 'info', text: 'after damage:\t70' }],
|
|
},
|
|
{
|
|
name: 'Humanoid.Health = 0 → Died fires',
|
|
lua: `
|
|
local h = game:GetService("Players").LocalPlayer.Character.Humanoid
|
|
h.Died:Connect(function() print("DIED") end)
|
|
h.Health = 0
|
|
`,
|
|
expect: [{ level: 'info', text: 'DIED' }],
|
|
},
|
|
{
|
|
name: 'DataStoreService GetAsync/SetAsync',
|
|
lua: `
|
|
local DSS = game:GetService("DataStoreService")
|
|
local store = DSS:GetDataStore("coins")
|
|
store:SetAsync("player1", 100)
|
|
print("got:", store:GetAsync("player1"))
|
|
`,
|
|
expect: [{ level: 'info', text: 'got:\t100' }],
|
|
},
|
|
{
|
|
name: 'DataStoreService IncrementAsync',
|
|
lua: `
|
|
local store = game:GetService("DataStoreService"):GetDataStore("score")
|
|
store:SetAsync("p1", 50)
|
|
store:IncrementAsync("p1", 25)
|
|
print("final:", store:GetAsync("p1"))
|
|
`,
|
|
expect: [{ level: 'info', text: 'final:\t75' }],
|
|
},
|
|
{
|
|
name: 'HttpService:JSONEncode/Decode',
|
|
lua: `
|
|
local HS = game:GetService("HttpService")
|
|
local s = HS:JSONEncode({a=1, b="two"})
|
|
print("encoded len:", #s)
|
|
local d = HS:JSONDecode('{"x":42}')
|
|
print("decoded x:", d.x)
|
|
`,
|
|
expect: [{ level: 'info', text: 'decoded x:\t42' }],
|
|
},
|
|
{
|
|
name: 'RemoteEvent FireServer + OnServerEvent',
|
|
lua: `
|
|
local re = Instance.new("RemoteEvent", workspace)
|
|
re.Name = "MyEvent"
|
|
re.OnServerEvent:Connect(function(player, msg)
|
|
print("server got:", msg)
|
|
end)
|
|
re:FireServer("hello")
|
|
`,
|
|
expect: [{ level: 'info', text: 'server got:\thello' }],
|
|
},
|
|
];
|
|
|
|
(async () => {
|
|
let passed = 0, failed = 0;
|
|
for (const t of TESTS) {
|
|
try {
|
|
const r = await run(t.lua, t.ticks);
|
|
let ok = true; let reason = '';
|
|
for (const exp of (t.expect || [])) {
|
|
const found = r.logs.find(l => l.level === exp.level && l.text === exp.text);
|
|
if (!found) { ok = false; reason = `missing log: ${exp.text}; got: ${JSON.stringify(r.logs)}`; break; }
|
|
}
|
|
if (t.expectPlayerCmd) {
|
|
const found = r.playerCmds.find(c => c.method === t.expectPlayerCmd.method
|
|
&& (!t.expectPlayerCmd.argsCheck || t.expectPlayerCmd.argsCheck(c.args)));
|
|
if (!found) { ok = false; reason = `missing playerCmd ${t.expectPlayerCmd.method}; got: ${JSON.stringify(r.playerCmds)}`; }
|
|
}
|
|
if (ok) { console.log(`✓ ${t.name}`); passed++; }
|
|
else { console.log(`✗ ${t.name} — ${reason}`); failed++; }
|
|
} catch (e) {
|
|
console.log(`✗ ${t.name} — exception: ${e.message || e}`);
|
|
failed++;
|
|
}
|
|
}
|
|
console.log(`\n${passed} passed, ${failed} failed`);
|
|
process.exit(failed > 0 ? 1 : 0);
|
|
})();
|