From 100dd794684af639266e2f859920bb50488251ca Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 31 May 2026 14:09:18 +0100 Subject: [PATCH] test(release): wait for bundled runtime commands --- .../runtime-smoke.mjs | 36 +++++++++++++------ ...led-plugin-install-uninstall-probe.test.ts | 12 +++++++ 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs b/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs index 9d5539b18b77..8055a3157573 100644 --- a/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs +++ b/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs @@ -698,14 +698,7 @@ async function runManifestProbes(plan, options) { } } if (plan.runtimeSlashAliases.length > 0 && plan.activeInThisProbe) { - const commands = await retryRpcCall( - "commands.list", - { scope: "both", includeArgs: true }, - options, - ); - for (const alias of plan.runtimeSlashAliases) { - assertCommandVisible(commands, alias); - } + await retryCommandsListWithAliases(plan.runtimeSlashAliases, options); } else if (plan.runtimeSlashAliases.length > 0) { console.log( `Runtime slash command smoke skipped for ${options.pluginId}: plugin is lazy in this probe`, @@ -741,10 +734,10 @@ function isChannelVisible(payload, channel) { return false; } -function assertCommandVisible(payload, alias) { +export function isCommandVisible(payload, alias) { const expected = alias.replace(/^\//u, "").toLowerCase(); const commands = Array.isArray(payload.commands) ? payload.commands : []; - const found = commands.some((command) => { + return commands.some((command) => { const names = [ command?.name, command?.nativeName, @@ -754,13 +747,34 @@ function assertCommandVisible(payload, alias) { .map((value) => value.replace(/^\//u, "").toLowerCase()); return names.includes(expected); }); - if (!found) { +} + +function assertCommandVisible(payload, alias) { + const expected = alias.replace(/^\//u, "").toLowerCase(); + if (!isCommandVisible(payload, alias)) { throw new Error( `commands.list did not include /${expected}: ${JSON.stringify(payload).slice(0, 2000)}`, ); } } +async function retryCommandsListWithAliases(aliases, options) { + const started = Date.now(); + let commands; + while (Date.now() - started < COMMAND_TIMEOUT_MS) { + commands = await retryRpcCall("commands.list", { scope: "both", includeArgs: true }, options); + const missing = aliases.filter((alias) => !isCommandVisible(commands, alias)); + if (missing.length === 0) { + return commands; + } + await delay(500); + } + for (const alias of aliases) { + assertCommandVisible(commands, alias); + } + return commands; +} + function assertToolVisible(payload, tool) { const groups = Array.isArray(payload.groups) ? payload.groups : []; const found = groups.some((group) => diff --git a/test/scripts/bundled-plugin-install-uninstall-probe.test.ts b/test/scripts/bundled-plugin-install-uninstall-probe.test.ts index 18a503a5a94f..44ae5624cfe5 100644 --- a/test/scripts/bundled-plugin-install-uninstall-probe.test.ts +++ b/test/scripts/bundled-plugin-install-uninstall-probe.test.ts @@ -176,6 +176,18 @@ describe("bundled plugin install/uninstall probe", () => { expect(second).toEqual({ text: "fghij", truncatedChars: 5 }); }); + it("matches runtime slash aliases across command list surfaces", async () => { + const runtimeSmoke = await import(pathToFileURL(runtimeSmokePath).href); + const payload = { + commands: [{ name: "voicecall" }, { nativeName: "phone" }, { textAliases: ["/pair"] }], + }; + + expect(runtimeSmoke.isCommandVisible(payload, "/voicecall")).toBe(true); + expect(runtimeSmoke.isCommandVisible(payload, "/phone")).toBe(true); + expect(runtimeSmoke.isCommandVisible(payload, "/pair")).toBe(true); + expect(runtimeSmoke.isCommandVisible(payload, "/missing")).toBe(false); + }); + it("rejects loose runtime output limit env values instead of parsing prefixes", async () => { const runtimeSmoke = await importRuntimeSmokeWithEnv({ OPENCLAW_BUNDLED_PLUGIN_RUNTIME_OUTPUT_CHARS: "5chars",