test(harness): fake-pi-runtime mock for gate CI tests

This commit is contained in:
2026-06-27 19:48:04 +02:00
parent aecc86f328
commit f850c391d8
2 changed files with 58 additions and 0 deletions
@@ -0,0 +1,34 @@
// fake_pi_runtime.js — minimal mock of the Pi extension runtime for gate tests.
// Models Pi's NATIVE extension UI API (ctx.ui.select/confirm/input). There is NO
// ctx.sendRaw in Pi; the gate awaits ctx.ui.* and the runtime routes the response.
function createFakePi() {
const handlers = new Map();
const tools = new Map();
const ctx = {
hasUI: true,
cwd: "/tmp/fake-pi-session",
notifications: [],
uiCalls: [],
uiQueue: [],
ui: {
notify: async (message, level = "info") => ctx.notifications.push({ message, level }),
input: async (title, placeholder, opts) => { ctx.uiCalls.push({ method: "input", title, placeholder, opts }); return ctx.uiQueue.shift(); },
select: async (title, options, opts) => { ctx.uiCalls.push({ method: "select", title, options, opts }); return ctx.uiQueue.shift(); },
confirm: async (title, message, opts) => { ctx.uiCalls.push({ method: "confirm", title, message, opts }); return ctx.uiQueue.shift(); },
},
};
const pi = {
on: (event, handler) => {
if (!handlers.has(event)) handlers.set(event, []);
handlers.get(event).push(handler);
},
registerTool: (def, fn) => tools.set(def?.name ?? def, { def, fn }),
emit: async (event, payload) => {
let result;
for (const h of handlers.get(event) ?? []) result = await h(payload, ctx);
return result;
},
};
return { pi, ctx, tools, emit: pi.emit, enqueueUi: (v) => ctx.uiQueue.push(v) };
}
module.exports = { createFakePi };
@@ -0,0 +1,24 @@
const test = require("node:test");
const assert = require("node:assert");
const { createFakePi } = require("./fake_pi_runtime.js");
test("pi.on + emit invoca il handler e ne ritorna il valore", async () => {
const { pi } = createFakePi();
pi.on("tool_call", (e) => (e.toolName === "x" ? { block: true } : undefined));
assert.deepEqual(await pi.emit("tool_call", { toolName: "x" }), { block: true });
assert.equal(await pi.emit("tool_call", { toolName: "y" }), undefined);
});
test("ctx.ui.input registra la chiamata e ritorna il valore in coda (API nativa)", async () => {
const { ctx, enqueueUi } = createFakePi();
enqueueUi('{"id":"u1","choices":["a"]}');
const v = await ctx.ui.input("title-json", "");
assert.equal(v, '{"id":"u1","choices":["a"]}');
assert.equal(ctx.uiCalls[0].method, "input");
assert.equal(ctx.uiCalls[0].title, "title-json");
});
test("ctx.ui.input senza valore in coda ritorna undefined (modella cancel)", async () => {
const { ctx } = createFakePi();
assert.equal(await ctx.ui.input("t", ""), undefined);
});