Files

103 lines
3.0 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import vm from "node:vm";
import { workerSource } from "../engine/script-host.ts";
import { entity } from "../engine/schema.ts";
function worker() {
const messages: any[] = [];
const context = vm.createContext({
self: {},
postMessage: (m: any) => messages.push(structuredClone(m)),
structuredClone,
Math,
console,
});
vm.runInContext(workerSource, context);
return {
send: (data: any) => {
context.self.onmessage({ data: structuredClone(data) });
return messages.pop();
},
};
}
test("script field defaults, overrides and per-instance state remain independent", () => {
const w = worker();
const scripts = [
{
id: "counter",
name: "Counter",
fields: { step: { type: "number", default: 2 } },
source:
"({start(api){api.state.count=0},update(api){api.state.count+=api.params.step;api.patch(api.get().id,{components:{data:{count:api.state.count}}})}})",
},
];
const a = entity(
"Default",
{ script: { scriptId: "counter" } },
[0, 0, 0],
"a",
);
const b = entity(
"Override",
{ script: { scriptId: "counter", params: { step: 5 } } },
[0, 0, 0],
"b",
);
w.send({ type: "init", entities: [a, b], scripts });
for (let tick = 1; tick <= 3; tick++) {
const frame = w.send({
type: "tick",
entities: [a, b],
input: {},
dt: 0.02,
});
assert.equal(
frame.commands.find((c: any) => c.id === "a").patch.components.data.count,
tick * 2,
);
assert.equal(
frame.commands.find((c: any) => c.id === "b").patch.components.data.count,
tick * 5,
);
}
});
test("newly spawned instances receive start and update; failing behavior is isolated", () => {
const scripts = [
{
id: "good",
name: "Good",
source:
'({start(api){api.state.ticks=0;api.log("start")},update(api){api.state.ticks++;api.log(api.state.ticks)}})',
fields: {},
},
{
id: "bad",
name: "Bad",
source: '({update(){throw Error("fixture failure")}})',
fields: {},
},
],
w = worker(),
a = entity("A", { script: { scriptId: "good" } }, [0, 0, 0], "a");
w.send({ type: "init", entities: [a], scripts });
const b = entity("B", { script: { scriptId: "good" } }, [0, 0, 0], "b"),
bad = entity("Bad", { script: { scriptId: "bad" } }, [0, 0, 0], "bad");
const m = w.send({ type: "tick", entities: [a, b, bad], input: {}, dt: 0.1 });
assert.equal(
m.commands.filter((c: any) => c.id === "b" && c.message === "start").length,
1,
);
assert.ok(m.commands.some((c: any) => c.id === "a" && c.message === "1"));
assert.ok(m.commands.some((c: any) => c.type === "error"));
const second = w.send({
type: "tick",
entities: [a, b, bad],
input: {},
dt: 0.1,
});
assert.ok(
second.commands.some((c: any) => c.id === "b" && c.message === "2"),
);
assert.ok(!second.commands.some((c: any) => c.type === "error"));
});