0.1.13: one rule for taking an event, and no handler left by a refused subscribe (hq issue 276)
A handler takes an event by returning and asks for it again by throwing; any mesh/event answer but an error is taken. Written down in the README where event handling is, with what follows for a handler: throw only when the work was not done, and be safe to run twice. Both SDKs added the handler before asking the runtime and kept it when the runtime refused, so a module that asks again until it is taken (every Go consumer does) ran its handler once per attempt for every event. A refused subscribe now removes its handler; one the Go runtime did not answer in time is kept, since the runtime may have bound it.
This commit is contained in:
@@ -160,6 +160,40 @@ test("a launched bundle subscribes as it is imported, handles events the runtime
|
||||
}
|
||||
});
|
||||
|
||||
test("a subscribe the runtime refused leaves no handler behind, so one event is handled once (novox/hq issue 276)", async () => {
|
||||
const child = spawn(process.execPath, [fileURLToPath(new URL("./fixtures/stdio-resubscribe.mjs", import.meta.url))],
|
||||
{ stdio: ["pipe", "pipe", "pipe"], env: { ...process.env, MESH_SERVED_MODULE: "plex" } });
|
||||
const seen: any[] = [];
|
||||
let buffered = "";
|
||||
let said = "";
|
||||
child.stderr.on("data", (d) => (said += d.toString()));
|
||||
child.stdout.on("data", (d) => {
|
||||
buffered += d.toString();
|
||||
let at: number;
|
||||
while ((at = buffered.indexOf("\n")) >= 0) {
|
||||
seen.push(JSON.parse(buffered.slice(0, at)));
|
||||
buffered = buffered.slice(at + 1);
|
||||
}
|
||||
});
|
||||
const until = async (match: (m: any) => boolean) => {
|
||||
for (let i = 0; i < 150 && !seen.some(match); i++) await new Promise((r) => setTimeout(r, 20));
|
||||
return seen.find(match);
|
||||
};
|
||||
const send = (m: unknown) => child.stdin.write(JSON.stringify(m) + "\n");
|
||||
try {
|
||||
const first = await until((m) => m.method === "mesh/subscribe");
|
||||
send({ jsonrpc: "2.0", id: first.id, error: { code: -32000, message: "not yet" } });
|
||||
const second = await until((m) => m.method === "mesh/subscribe" && m.id !== first.id);
|
||||
send({ jsonrpc: "2.0", id: second.id, result: {} });
|
||||
send({ jsonrpc: "2.0", id: 20, method: "mesh/event", params: { envelope: { key: "radarr.download.completed", node: "anchor", body: {}, headers: { "x-event-id": "e20" } } } });
|
||||
assert.deepEqual((await until((m) => m.id === 20)).result, {});
|
||||
assert.match(said, /refused: not yet/);
|
||||
assert.equal(said.match(/handled/g)?.length, 1, `one event was handled more than once:\n${said}`);
|
||||
} finally {
|
||||
child.kill();
|
||||
}
|
||||
});
|
||||
|
||||
test("a launched bundle watches its state as it is imported, has the current values before it goes on, and keeps state through the runtime (ADR 0201)", async () => {
|
||||
const child = spawn(process.execPath, [fileURLToPath(new URL("./fixtures/stdio-state.mjs", import.meta.url))],
|
||||
{ stdio: ["pipe", "pipe", "pipe"], env: { ...process.env, MESH_SERVED_MODULE: "keeper" } });
|
||||
|
||||
Reference in New Issue
Block a user