103 lines
3.0 KiB
TypeScript
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"));
|
|
});
|