From 8edc671eb4be487e841d9348f8356468d0c2bb23 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 24 May 2026 19:09:57 +0200 Subject: [PATCH] fix(e2e): harden Windows plugin assertions --- CHANGELOG.md | 1 + scripts/e2e/lib/plugins/assertions.mjs | 166 +++++++++++++----------- scripts/e2e/lib/plugins/clawhub.sh | 14 +- scripts/e2e/lib/plugins/marketplace.sh | 12 +- scripts/e2e/lib/plugins/sweep.sh | 104 +++++++-------- test/scripts/plugins-assertions.test.ts | 68 ++++++++++ 6 files changed, 226 insertions(+), 139 deletions(-) create mode 100644 test/scripts/plugins-assertions.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e090dded7b..1e64cc7c12f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Docs: https://docs.openclaw.ai - Channels/iMessage: advance the startup catchup cursor from live-handled rows after a completed catchup pass, including rows received while catchup is still running, so restarts do not replay them. (#85475) Thanks @TurboTheTurtle. - Tests: mount the shared Windows command helper into bare Docker E2E harness containers so published upgrade-survivor config walks can start on Linux. +- Tests: let the generic plugin install E2E assertions use a configurable temp root and Windows home-relative install paths. - Tests: keep kitchen-sink plugin assertion fixtures on a configurable temp root so native Windows runs no longer skip full-surface diagnostic coverage. - Tests: fail Gateway startup benchmarks when a child startup never produces ready probes or process metrics instead of reporting all `n/a` samples as passing. - Config/secrets: allow exec SecretRef ids to include `#` selectors so AWS-style `secret#json_key` ids validate consistently. (#80731) Thanks @TurboTheTurtle. diff --git a/scripts/e2e/lib/plugins/assertions.mjs b/scripts/e2e/lib/plugins/assertions.mjs index 415ed44ea6c..8f7718f2c69 100644 --- a/scripts/e2e/lib/plugins/assertions.mjs +++ b/scripts/e2e/lib/plugins/assertions.mjs @@ -1,8 +1,21 @@ import fs from "node:fs"; +import os from "node:os"; import path from "node:path"; const command = process.argv[2]; +const scratchRoot = process.env.OPENCLAW_PLUGINS_TMP_DIR || os.tmpdir(); const readJson = (file) => JSON.parse(fs.readFileSync(file, "utf8")); +const scratchFile = (name) => path.join(scratchRoot, name); + +function resolveHomePath(value) { + if (value === "~") { + return process.env.HOME; + } + if (value?.startsWith("~/") || value?.startsWith("~\\")) { + return path.join(process.env.HOME, value.slice(2)); + } + return value; +} function getInstallRecords() { const indexPath = path.join(process.env.HOME, ".openclaw", "plugins", "installs.json"); @@ -59,7 +72,7 @@ function rememberPluginInstallPath(params) { `unexpected source path for ${params.pluginId}: ${record.sourcePath}, expected ${params.sourcePath}`, ); } - const installPath = record.installPath?.replace(/^~(?=$|\/)/u, process.env.HOME); + const installPath = resolveHomePath(record.installPath); if (!installPath || !fs.existsSync(installPath)) { throw new Error(`${params.pluginId} install path missing on disk: ${installPath}`); } @@ -128,8 +141,8 @@ function recordFixturePluginTrust() { } function assertDemoPlugin() { - const data = readJson("/tmp/plugins.json"); - const inspect = readJson("/tmp/plugins-inspect.json"); + const data = readJson(scratchFile("plugins.json")); + const inspect = readJson(scratchFile("plugins-inspect.json")); const plugin = (data.plugins || []).find((entry) => entry.id === "demo-plugin"); if (!plugin) { throw new Error("plugin not found"); @@ -183,7 +196,7 @@ function assertUpdateOutput(logFile, expectedSnippet) { } function assertClaudeBundleDisabled() { - const data = readJson("/tmp/plugins-bundle-disabled.json"); + const data = readJson(scratchFile("plugins-bundle-disabled.json")); const plugin = (data.plugins || []).find((entry) => entry.id === "claude-bundle-e2e"); if (!plugin) { throw new Error("Claude bundle plugin not found"); @@ -194,7 +207,7 @@ function assertClaudeBundleDisabled() { } function assertClaudeBundleInspect() { - const inspect = readJson("/tmp/plugins-bundle-inspect.json"); + const inspect = readJson(scratchFile("plugins-bundle-inspect.json")); if (inspect.plugin?.bundleFormat !== "claude") { throw new Error(`expected Claude bundle format, got ${inspect.plugin?.bundleFormat}`); } @@ -206,7 +219,7 @@ function assertClaudeBundleInspect() { } function assertSlashInstall() { - const inspect = readJson("/tmp/plugin-command-install-show.json"); + const inspect = readJson(scratchFile("plugin-command-install-show.json")); if (inspect.plugin?.status !== "loaded") { throw new Error(`expected loaded status after install, got ${inspect.plugin?.status}`); } @@ -226,7 +239,7 @@ function parseClawHubPackageName(rawSpec) { } function assertMarketplaceList() { - const data = readJson("/tmp/marketplace-list.json"); + const data = readJson(scratchFile("marketplace-list.json")); const names = (data.plugins || []).map((entry) => entry.name).toSorted(); if (data.name !== "Fixture Marketplace") { throw new Error(`unexpected marketplace name: ${data.name}`); @@ -237,9 +250,9 @@ function assertMarketplaceList() { } function assertMarketplaceInstalled() { - const data = readJson("/tmp/plugins-marketplace.json"); - const shortcutInspect = readJson("/tmp/plugins-marketplace-shortcut-inspect.json"); - const directInspect = readJson("/tmp/plugins-marketplace-direct-inspect.json"); + const data = readJson(scratchFile("plugins-marketplace.json")); + const shortcutInspect = readJson(scratchFile("plugins-marketplace-shortcut-inspect.json")); + const directInspect = readJson(scratchFile("plugins-marketplace-direct-inspect.json")); const getPlugin = (id) => { const plugin = (data.plugins || []).find((entry) => entry.id === id); if (!plugin) { @@ -292,14 +305,14 @@ function assertMarketplaceRecords() { function assertPluginTgz() { assertSimplePlugin( - "/tmp/plugins2.json", - "/tmp/plugins2-inspect.json", + scratchFile("plugins2.json"), + scratchFile("plugins2-inspect.json"), "demo-plugin-tgz", "demo.tgz", ); rememberPluginInstallPath({ pluginId: "demo-plugin-tgz", - installPathFile: "/tmp/plugins2-install-path.txt", + installPathFile: scratchFile("plugins2-install-path.txt"), source: "archive", }); } @@ -307,23 +320,23 @@ function assertPluginTgz() { function assertPluginTgzRemoved() { assertManagedInstallRemoved({ pluginId: "demo-plugin-tgz", - listFile: "/tmp/plugins2-uninstalled.json", - installPathFile: "/tmp/plugins2-install-path.txt", + listFile: scratchFile("plugins2-uninstalled.json"), + installPathFile: scratchFile("plugins2-install-path.txt"), }); } function assertPluginDir() { const sourceDir = process.argv[3]; assertSimplePlugin( - "/tmp/plugins3.json", - "/tmp/plugins3-inspect.json", + scratchFile("plugins3.json"), + scratchFile("plugins3-inspect.json"), "demo-plugin-dir", "demo.dir", ); rememberPluginInstallPath({ pluginId: "demo-plugin-dir", - installPathFile: "/tmp/plugins3-install-path.txt", - sourcePathFile: "/tmp/plugins3-source-path.txt", + installPathFile: scratchFile("plugins3-install-path.txt"), + sourcePathFile: scratchFile("plugins3-source-path.txt"), source: "path", sourcePath: sourceDir, }); @@ -332,9 +345,9 @@ function assertPluginDir() { function assertPluginDirRemoved() { assertManagedInstallRemoved({ pluginId: "demo-plugin-dir", - listFile: "/tmp/plugins3-uninstalled.json", - installPathFile: "/tmp/plugins3-install-path.txt", - sourcePathFile: "/tmp/plugins3-source-path.txt", + listFile: scratchFile("plugins3-uninstalled.json"), + installPathFile: scratchFile("plugins3-install-path.txt"), + sourcePathFile: scratchFile("plugins3-source-path.txt"), }); } @@ -342,18 +355,18 @@ function assertGitPlugin() { const repoUrl = process.argv[3]; const gitRef = process.argv[4]; assertSimplePlugin( - "/tmp/plugins-git.json", - "/tmp/plugins-git-inspect.json", + scratchFile("plugins-git.json"), + scratchFile("plugins-git-inspect.json"), "demo-plugin-git", "demo.git", ); - const inspect = readJson("/tmp/plugins-git-inspect.json"); + const inspect = readJson(scratchFile("plugins-git-inspect.json")); if (!Array.isArray(inspect.cliCommands) || !inspect.cliCommands.includes("demo-git")) { throw new Error(`expected demo-git cli command, got ${inspect.cliCommands?.join(", ")}`); } - const cliOutput = fs.readFileSync("/tmp/plugins-git-cli.txt", "utf8"); + const cliOutput = fs.readFileSync(scratchFile("plugins-git-cli.txt"), "utf8"); if (!cliOutput.includes("demo-plugin-git:pong")) { throw new Error(`unexpected git plugin cli output: ${cliOutput.trim()}`); } @@ -378,7 +391,7 @@ function assertGitPlugin() { throw new Error(`unexpected git spec: ${record.spec}`); } - const installPath = record.installPath?.replace(/^~(?=$|\/)/u, process.env.HOME); + const installPath = resolveHomePath(record.installPath); if (!installPath || !fs.existsSync(installPath)) { throw new Error(`git install path missing on disk: ${installPath}`); } @@ -392,16 +405,16 @@ function assertGitPlugin() { throw new Error(`missing git plugin installed dependency: ${dependencyPackagePath}`); } assertRealPathInside(installPath, dependencyPackagePath, "git plugin installed dependency"); - fs.writeFileSync("/tmp/plugins-git-install-path.txt", installPath, "utf8"); - fs.writeFileSync("/tmp/plugins-git-install-parent.txt", path.dirname(installPath), "utf8"); + fs.writeFileSync(scratchFile("plugins-git-install-path.txt"), installPath, "utf8"); + fs.writeFileSync(scratchFile("plugins-git-install-parent.txt"), path.dirname(installPath), "utf8"); } function assertGitPluginRemoved() { - const installPath = fs.readFileSync("/tmp/plugins-git-install-path.txt", "utf8").trim(); - const installParent = fs.readFileSync("/tmp/plugins-git-install-parent.txt", "utf8").trim(); + const installPath = fs.readFileSync(scratchFile("plugins-git-install-path.txt"), "utf8").trim(); + const installParent = fs.readFileSync(scratchFile("plugins-git-install-parent.txt"), "utf8").trim(); assertPluginRemoved({ pluginId: "demo-plugin-git", - listFile: "/tmp/plugins-git-uninstalled.json", + listFile: scratchFile("plugins-git-uninstalled.json"), }); if (fs.existsSync(installPath)) { throw new Error(`git managed repo still exists after uninstall: ${installPath}`); @@ -470,8 +483,8 @@ function assertClawHubArtifactMetadata(record, pluginId) { function assertPluginDirDeps() { const sourceDir = process.argv[3]; assertSimplePlugin( - "/tmp/plugins-dir-deps.json", - "/tmp/plugins-dir-deps-inspect.json", + scratchFile("plugins-dir-deps.json"), + scratchFile("plugins-dir-deps-inspect.json"), "demo-plugin-dir-deps", "demo.dir.deps", ); @@ -486,7 +499,7 @@ function assertPluginDirDeps() { if (record.sourcePath !== sourceDir) { throw new Error(`unexpected local dependency plugin source path: ${record.sourcePath}`); } - const installPath = record.installPath?.replace(/^~(?=$|\/)/u, process.env.HOME); + const installPath = resolveHomePath(record.installPath); if (!installPath || !fs.existsSync(installPath)) { throw new Error(`local dependency plugin install path missing on disk: ${installPath}`); } @@ -497,8 +510,8 @@ function assertPluginDirDeps() { assertRealPathInside(installPath, dependencyPackagePath, "local plugin copied dependency"); rememberPluginInstallPath({ pluginId: "demo-plugin-dir-deps", - installPathFile: "/tmp/plugins-dir-deps-install-path.txt", - sourcePathFile: "/tmp/plugins-dir-deps-source-path.txt", + installPathFile: scratchFile("plugins-dir-deps-install-path.txt"), + sourcePathFile: scratchFile("plugins-dir-deps-source-path.txt"), source: "path", sourcePath: sourceDir, }); @@ -507,30 +520,33 @@ function assertPluginDirDeps() { function assertPluginDirDepsRemoved() { assertManagedInstallRemoved({ pluginId: "demo-plugin-dir-deps", - listFile: "/tmp/plugins-dir-deps-uninstalled.json", - installPathFile: "/tmp/plugins-dir-deps-install-path.txt", - sourcePathFile: "/tmp/plugins-dir-deps-source-path.txt", + listFile: scratchFile("plugins-dir-deps-uninstalled.json"), + installPathFile: scratchFile("plugins-dir-deps-install-path.txt"), + sourcePathFile: scratchFile("plugins-dir-deps-source-path.txt"), }); } function assertLocalPathUpdateSkipped() { - assertUpdateOutput("/tmp/plugins-dir-update.log", 'Skipping "demo-plugin-dir" (source: path).'); + assertUpdateOutput( + scratchFile("plugins-dir-update.log"), + 'Skipping "demo-plugin-dir" (source: path).', + ); } function assertNpmPlugin() { assertSimplePlugin( - "/tmp/plugins-npm.json", - "/tmp/plugins-npm-inspect.json", + scratchFile("plugins-npm.json"), + scratchFile("plugins-npm-inspect.json"), "demo-plugin-npm", "demo.npm", ); - const inspect = readJson("/tmp/plugins-npm-inspect.json"); + const inspect = readJson(scratchFile("plugins-npm-inspect.json")); if (!Array.isArray(inspect.cliCommands) || !inspect.cliCommands.includes("demo-npm")) { throw new Error(`expected demo-npm cli command, got ${inspect.cliCommands?.join(", ")}`); } - const cliOutput = fs.readFileSync("/tmp/plugins-npm-cli.txt", "utf8"); + const cliOutput = fs.readFileSync(scratchFile("plugins-npm-cli.txt"), "utf8"); if (!cliOutput.includes("demo-plugin-npm:pong")) { throw new Error(`unexpected npm plugin cli output: ${cliOutput.trim()}`); } @@ -551,7 +567,7 @@ function assertNpmPlugin() { if (record.resolvedVersion !== "0.0.1") { throw new Error(`unexpected npm resolved version: ${record.resolvedVersion}`); } - const installPath = record.installPath?.replace(/^~(?=$|\/)/u, process.env.HOME); + const installPath = resolveHomePath(record.installPath); if (!installPath || !fs.existsSync(installPath)) { throw new Error(`npm install path missing on disk: ${installPath}`); } @@ -562,27 +578,27 @@ function assertNpmPlugin() { throw new Error(`missing npm plugin installed dependency: ${dependencyPackagePath}`); } assertRealPathInside(npmRoot, dependencyPackagePath, "npm plugin installed dependency"); - fs.writeFileSync("/tmp/plugins-npm-install-path.txt", installPath, "utf8"); - fs.writeFileSync("/tmp/plugins-npm-dependency-path.txt", dependencyPackagePath, "utf8"); + fs.writeFileSync(scratchFile("plugins-npm-install-path.txt"), installPath, "utf8"); + fs.writeFileSync(scratchFile("plugins-npm-dependency-path.txt"), dependencyPackagePath, "utf8"); } function assertNpmPluginUpdateUnchanged() { - assertUpdateOutput("/tmp/plugins-npm-update.log", "demo-plugin-npm is up to date (0.0.1)."); + assertUpdateOutput(scratchFile("plugins-npm-update.log"), "demo-plugin-npm is up to date (0.0.1)."); assertNpmPlugin(); } function assertPluginFile() { const sourceDir = process.argv[3]; assertSimplePlugin( - "/tmp/plugins4.json", - "/tmp/plugins4-inspect.json", + scratchFile("plugins4.json"), + scratchFile("plugins4-inspect.json"), "demo-plugin-file", "demo.file", ); rememberPluginInstallPath({ pluginId: "demo-plugin-file", - installPathFile: "/tmp/plugins4-install-path.txt", - sourcePathFile: "/tmp/plugins4-source-path.txt", + installPathFile: scratchFile("plugins4-install-path.txt"), + sourcePathFile: scratchFile("plugins4-source-path.txt"), source: "path", sourcePath: sourceDir, }); @@ -591,20 +607,20 @@ function assertPluginFile() { function assertPluginFileRemoved() { assertManagedInstallRemoved({ pluginId: "demo-plugin-file", - listFile: "/tmp/plugins4-uninstalled.json", - installPathFile: "/tmp/plugins4-install-path.txt", - sourcePathFile: "/tmp/plugins4-source-path.txt", + listFile: scratchFile("plugins4-uninstalled.json"), + installPathFile: scratchFile("plugins4-install-path.txt"), + sourcePathFile: scratchFile("plugins4-source-path.txt"), }); } function assertNpmPluginRemoved() { - const installPath = fs.readFileSync("/tmp/plugins-npm-install-path.txt", "utf8").trim(); + const installPath = fs.readFileSync(scratchFile("plugins-npm-install-path.txt"), "utf8").trim(); const dependencyPackagePath = fs - .readFileSync("/tmp/plugins-npm-dependency-path.txt", "utf8") + .readFileSync(scratchFile("plugins-npm-dependency-path.txt"), "utf8") .trim(); assertPluginRemoved({ pluginId: "demo-plugin-npm", - listFile: "/tmp/plugins-npm-uninstalled.json", + listFile: scratchFile("plugins-npm-uninstalled.json"), }); if (fs.existsSync(installPath)) { throw new Error(`npm managed package still exists after uninstall: ${installPath}`); @@ -618,7 +634,7 @@ function assertNpmPluginRemoved() { function assertInvalidOpenClawExtensionsRejected() { const pluginId = "demo-plugin-invalid-metadata"; - const output = fs.readFileSync("/tmp/plugins-invalid-openclaw-extensions.log", "utf8"); + const output = fs.readFileSync(scratchFile("plugins-invalid-openclaw-extensions.log"), "utf8"); for (const expected of ["openclaw.extensions[1]", "non-empty string"]) { if (!output.includes(expected)) { throw new Error( @@ -627,7 +643,7 @@ function assertInvalidOpenClawExtensionsRejected() { } } - const list = readJson("/tmp/plugins-invalid-openclaw-extensions-list.json"); + const list = readJson(scratchFile("plugins-invalid-openclaw-extensions-list.json")); if ((list.plugins || []).some((entry) => entry.id === pluginId)) { throw new Error(`${pluginId} listed after rejected install`); } @@ -644,8 +660,8 @@ function assertInvalidOpenClawExtensionsRejected() { } function assertMarketplaceUpdated() { - const data = readJson("/tmp/plugins-marketplace-updated.json"); - const inspect = readJson("/tmp/plugins-marketplace-updated-inspect.json"); + const data = readJson(scratchFile("plugins-marketplace-updated.json")); + const inspect = readJson(scratchFile("plugins-marketplace-updated-inspect.json")); const plugin = (data.plugins || []).find((entry) => entry.id === "marketplace-shortcut"); if (!plugin) { throw new Error("updated marketplace plugin not found"); @@ -661,18 +677,18 @@ function assertMarketplaceUpdated() { function assertGitPluginUpdated() { const beforeCommit = process.argv[3]; assertSimplePlugin( - "/tmp/plugins-git-update.json", - "/tmp/plugins-git-update-inspect.json", + scratchFile("plugins-git-update.json"), + scratchFile("plugins-git-update-inspect.json"), "demo-plugin-git-update", "demo.git.update.v2", ); - const inspect = readJson("/tmp/plugins-git-update-inspect.json"); + const inspect = readJson(scratchFile("plugins-git-update-inspect.json")); if (!Array.isArray(inspect.cliCommands) || !inspect.cliCommands.includes("demo-git-update")) { throw new Error(`expected demo-git-update cli command, got ${inspect.cliCommands?.join(", ")}`); } - const cliOutput = fs.readFileSync("/tmp/plugins-git-update-cli.txt", "utf8"); + const cliOutput = fs.readFileSync(scratchFile("plugins-git-update-cli.txt"), "utf8"); if (!cliOutput.includes("demo-plugin-git-update:pong-v2")) { throw new Error(`unexpected updated git plugin cli output: ${cliOutput.trim()}`); } @@ -696,7 +712,7 @@ function assertGitPluginUpdated() { throw new Error(`unexpected git update version: ${record.version}`); } assertUpdateOutput( - "/tmp/plugins-git-update.log", + scratchFile("plugins-git-update.log"), "Updated demo-plugin-git-update: 0.0.1 -> 0.0.2.", ); } @@ -744,8 +760,8 @@ function assertClawHubInstalled() { const pluginId = process.env.CLAWHUB_PLUGIN_ID; const spec = process.env.CLAWHUB_PLUGIN_SPEC; const packageName = parseClawHubPackageName(spec); - const list = readJson("/tmp/plugins-clawhub-installed.json"); - const inspect = readJson("/tmp/plugins-clawhub-inspect.json"); + const list = readJson(scratchFile("plugins-clawhub-installed.json")); + const inspect = readJson(scratchFile("plugins-clawhub-inspect.json")); const plugin = (list.plugins || []).find((entry) => entry.id === pluginId); if (!plugin) { throw new Error(`ClawHub plugin not found after install: ${pluginId}`); @@ -788,7 +804,7 @@ function assertClawHubInstalled() { } assertClawHubArtifactMetadata(record, pluginId); - const installPath = record.installPath.replace(/^~(?=$|\/)/u, process.env.HOME); + const installPath = resolveHomePath(record.installPath); const extensionsRoot = path.join(process.env.HOME, ".openclaw", "extensions"); if (!installPath.startsWith(`${extensionsRoot}${path.sep}`)) { throw new Error(`ClawHub install path is outside managed extensions root: ${installPath}`); @@ -799,13 +815,13 @@ function assertClawHubInstalled() { if (record.artifactKind === "npm-pack") { assertClawHubExternalInstallContract(installPath); } - fs.writeFileSync("/tmp/plugins-clawhub-install-path.txt", installPath, "utf8"); + fs.writeFileSync(scratchFile("plugins-clawhub-install-path.txt"), installPath, "utf8"); } function assertClawHubRemoved() { const pluginId = process.env.CLAWHUB_PLUGIN_ID; - const installPath = fs.readFileSync("/tmp/plugins-clawhub-install-path.txt", "utf8").trim(); - const list = readJson("/tmp/plugins-clawhub-uninstalled.json"); + const installPath = fs.readFileSync(scratchFile("plugins-clawhub-install-path.txt"), "utf8").trim(); + const list = readJson(scratchFile("plugins-clawhub-uninstalled.json")); if ((list.plugins || []).some((entry) => entry.id === pluginId)) { throw new Error(`ClawHub plugin still listed after uninstall: ${pluginId}`); } @@ -840,7 +856,7 @@ function assertClawHubRemoved() { } function assertClawHubUpdated() { - const output = fs.readFileSync("/tmp/plugins-clawhub-update.log", "utf8"); + const output = fs.readFileSync(scratchFile("plugins-clawhub-update.log"), "utf8"); if (!output.includes(`${process.env.CLAWHUB_PLUGIN_ID} already at `)) { throw new Error(`expected ClawHub update to report already-at version:\n${output}`); } diff --git a/scripts/e2e/lib/plugins/clawhub.sh b/scripts/e2e/lib/plugins/clawhub.sh index 2076b65e74f..fec91404fa1 100644 --- a/scripts/e2e/lib/plugins/clawhub.sh +++ b/scripts/e2e/lib/plugins/clawhub.sh @@ -44,26 +44,26 @@ run_plugins_clawhub_scenario() { echo "Ignoring ambient ClawHub URL for fixture-mode plugin E2E; set OPENCLAW_PLUGINS_E2E_LIVE_CLAWHUB=1 for live ClawHub." fi unset OPENCLAW_CLAWHUB_URL CLAWHUB_URL - clawhub_fixture_dir="$(mktemp -d "/tmp/openclaw-clawhub-fixture.XXXXXX")" + clawhub_fixture_dir="$(mktemp -d "$OPENCLAW_PLUGINS_TMP_DIR/openclaw-clawhub-fixture.XXXXXX")" start_clawhub_fixture_server "$clawhub_fixture_dir" fi node scripts/e2e/lib/plugins/assertions.mjs clawhub-preflight run_logged install-clawhub node "$OPENCLAW_ENTRY" plugins install "$CLAWHUB_PLUGIN_SPEC" - node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins-clawhub-installed.json - node "$OPENCLAW_ENTRY" plugins inspect "$CLAWHUB_PLUGIN_ID" --json >/tmp/plugins-clawhub-inspect.json + node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-clawhub-installed.json" + node "$OPENCLAW_ENTRY" plugins inspect "$CLAWHUB_PLUGIN_ID" --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-clawhub-inspect.json" node scripts/e2e/lib/plugins/assertions.mjs clawhub-installed - node "$OPENCLAW_ENTRY" plugins update "$CLAWHUB_PLUGIN_ID" >/tmp/plugins-clawhub-update.log 2>&1 - node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins-clawhub-updated.json - node "$OPENCLAW_ENTRY" plugins inspect "$CLAWHUB_PLUGIN_ID" --json >/tmp/plugins-clawhub-updated-inspect.json + node "$OPENCLAW_ENTRY" plugins update "$CLAWHUB_PLUGIN_ID" >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-clawhub-update.log" 2>&1 + node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-clawhub-updated.json" + node "$OPENCLAW_ENTRY" plugins inspect "$CLAWHUB_PLUGIN_ID" --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-clawhub-updated-inspect.json" node scripts/e2e/lib/plugins/assertions.mjs clawhub-updated run_logged uninstall-clawhub node "$OPENCLAW_ENTRY" plugins uninstall "$CLAWHUB_PLUGIN_SPEC" --force - node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins-clawhub-uninstalled.json + node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-clawhub-uninstalled.json" node scripts/e2e/lib/plugins/assertions.mjs clawhub-removed fi diff --git a/scripts/e2e/lib/plugins/marketplace.sh b/scripts/e2e/lib/plugins/marketplace.sh index e246e87b690..f08c79f7a92 100644 --- a/scripts/e2e/lib/plugins/marketplace.sh +++ b/scripts/e2e/lib/plugins/marketplace.sh @@ -16,15 +16,15 @@ run_plugins_marketplace_scenario() { "Marketplace Direct" node scripts/e2e/lib/fixture.mjs marketplace "$marketplace_root" - node "$OPENCLAW_ENTRY" plugins marketplace list claude-fixtures --json >/tmp/marketplace-list.json + node "$OPENCLAW_ENTRY" plugins marketplace list claude-fixtures --json >"$OPENCLAW_PLUGINS_TMP_DIR/marketplace-list.json" node scripts/e2e/lib/plugins/assertions.mjs marketplace-list run_logged install-marketplace-shortcut node "$OPENCLAW_ENTRY" plugins install marketplace-shortcut@claude-fixtures run_logged install-marketplace-direct node "$OPENCLAW_ENTRY" plugins install marketplace-direct --marketplace claude-fixtures - node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins-marketplace.json - node "$OPENCLAW_ENTRY" plugins inspect marketplace-shortcut --runtime --json >/tmp/plugins-marketplace-shortcut-inspect.json - node "$OPENCLAW_ENTRY" plugins inspect marketplace-direct --runtime --json >/tmp/plugins-marketplace-direct-inspect.json + node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-marketplace.json" + node "$OPENCLAW_ENTRY" plugins inspect marketplace-shortcut --runtime --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-marketplace-shortcut-inspect.json" + node "$OPENCLAW_ENTRY" plugins inspect marketplace-direct --runtime --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-marketplace-direct-inspect.json" node scripts/e2e/lib/plugins/assertions.mjs marketplace-installed @@ -38,8 +38,8 @@ run_plugins_marketplace_scenario() { "Marketplace Shortcut" run_logged update-marketplace-shortcut-dry-run node "$OPENCLAW_ENTRY" plugins update marketplace-shortcut --dry-run run_logged update-marketplace-shortcut node "$OPENCLAW_ENTRY" plugins update marketplace-shortcut - node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins-marketplace-updated.json - node "$OPENCLAW_ENTRY" plugins inspect marketplace-shortcut --runtime --json >/tmp/plugins-marketplace-updated-inspect.json + node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-marketplace-updated.json" + node "$OPENCLAW_ENTRY" plugins inspect marketplace-shortcut --runtime --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-marketplace-updated-inspect.json" node scripts/e2e/lib/plugins/assertions.mjs marketplace-updated } diff --git a/scripts/e2e/lib/plugins/sweep.sh b/scripts/e2e/lib/plugins/sweep.sh index c471d68ac37..724cc63a3aa 100644 --- a/scripts/e2e/lib/plugins/sweep.sh +++ b/scripts/e2e/lib/plugins/sweep.sh @@ -5,6 +5,8 @@ source scripts/lib/openclaw-e2e-instance.sh source scripts/lib/docker-e2e-logs.sh OPENCLAW_ENTRY="$(openclaw_e2e_resolve_entrypoint)" export OPENCLAW_ENTRY +export OPENCLAW_PLUGINS_TMP_DIR="${OPENCLAW_PLUGINS_TMP_DIR:-/tmp}" +mkdir -p "$OPENCLAW_PLUGINS_TMP_DIR" PACKAGE_VERSION="$(node -p 'require("./package.json").version')" OPENCLAW_PACKAGE_ACCEPTANCE_LEGACY_COMPAT="$(node scripts/e2e/lib/package-compat.mjs "$PACKAGE_VERSION")" export OPENCLAW_PACKAGE_ACCEPTANCE_LEGACY_COMPAT @@ -21,105 +23,105 @@ demo_plugin_root="$OPENCLAW_PLUGIN_HOME/$demo_plugin_id" write_demo_fixture_plugin "$demo_plugin_root" record_fixture_plugin_trust "$demo_plugin_id" "$demo_plugin_root" 1 -node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins.json -node "$OPENCLAW_ENTRY" plugins inspect demo-plugin --runtime --json >/tmp/plugins-inspect.json +node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins.json" +node "$OPENCLAW_ENTRY" plugins inspect demo-plugin --runtime --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-inspect.json" node scripts/e2e/lib/plugins/assertions.mjs demo-plugin echo "Testing tgz install flow..." -pack_dir="$(mktemp -d "/tmp/openclaw-plugin-pack.XXXXXX")" -pack_fixture_plugin "$pack_dir" /tmp/demo-plugin-tgz.tgz demo-plugin-tgz 0.0.1 demo.tgz "Demo Plugin TGZ" +pack_dir="$(mktemp -d "$OPENCLAW_PLUGINS_TMP_DIR/openclaw-plugin-pack.XXXXXX")" +pack_fixture_plugin "$pack_dir" "$OPENCLAW_PLUGINS_TMP_DIR/demo-plugin-tgz.tgz" demo-plugin-tgz 0.0.1 demo.tgz "Demo Plugin TGZ" -run_logged install-tgz node "$OPENCLAW_ENTRY" plugins install /tmp/demo-plugin-tgz.tgz -node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins2.json -node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-tgz --runtime --json >/tmp/plugins2-inspect.json +run_logged install-tgz node "$OPENCLAW_ENTRY" plugins install "$OPENCLAW_PLUGINS_TMP_DIR/demo-plugin-tgz.tgz" +node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins2.json" +node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-tgz --runtime --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins2-inspect.json" node scripts/e2e/lib/plugins/assertions.mjs plugin-tgz run_logged uninstall-tgz node "$OPENCLAW_ENTRY" plugins uninstall demo-plugin-tgz --force -node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins2-uninstalled.json +node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins2-uninstalled.json" node scripts/e2e/lib/plugins/assertions.mjs plugin-tgz-removed echo "Testing install from local folder (plugins.load.paths)..." -dir_plugin="$(mktemp -d "/tmp/openclaw-plugin-dir.XXXXXX")" +dir_plugin="$(mktemp -d "$OPENCLAW_PLUGINS_TMP_DIR/openclaw-plugin-dir.XXXXXX")" write_fixture_plugin "$dir_plugin" demo-plugin-dir 0.0.1 demo.dir "Demo Plugin DIR" run_logged install-dir node "$OPENCLAW_ENTRY" plugins install "$dir_plugin" -node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins3.json -node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-dir --runtime --json >/tmp/plugins3-inspect.json +node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins3.json" +node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-dir --runtime --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins3-inspect.json" node scripts/e2e/lib/plugins/assertions.mjs plugin-dir "$dir_plugin" -node "$OPENCLAW_ENTRY" plugins update demo-plugin-dir >/tmp/plugins-dir-update.log 2>&1 +node "$OPENCLAW_ENTRY" plugins update demo-plugin-dir >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-dir-update.log" 2>&1 node scripts/e2e/lib/plugins/assertions.mjs plugin-dir-update-skipped run_logged uninstall-dir node "$OPENCLAW_ENTRY" plugins uninstall demo-plugin-dir --force -node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins3-uninstalled.json +node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins3-uninstalled.json" node scripts/e2e/lib/plugins/assertions.mjs plugin-dir-removed echo "Testing install from local folder with preinstalled dependencies..." -dir_deps_plugin="$(mktemp -d "/tmp/openclaw-plugin-dir-deps.XXXXXX")" +dir_deps_plugin="$(mktemp -d "$OPENCLAW_PLUGINS_TMP_DIR/openclaw-plugin-dir-deps.XXXXXX")" write_fixture_plugin_with_vendored_dependency "$dir_deps_plugin" demo-plugin-dir-deps 0.0.1 demo.dir.deps "Demo Plugin DIR Deps" run_logged install-dir-deps node "$OPENCLAW_ENTRY" plugins install "$dir_deps_plugin" -node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins-dir-deps.json -node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-dir-deps --runtime --json >/tmp/plugins-dir-deps-inspect.json +node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-dir-deps.json" +node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-dir-deps --runtime --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-dir-deps-inspect.json" node scripts/e2e/lib/plugins/assertions.mjs plugin-dir-deps "$dir_deps_plugin" run_logged uninstall-dir-deps node "$OPENCLAW_ENTRY" plugins uninstall demo-plugin-dir-deps --force -node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins-dir-deps-uninstalled.json +node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-dir-deps-uninstalled.json" node scripts/e2e/lib/plugins/assertions.mjs plugin-dir-deps-removed echo "Testing install from npm spec (file:)..." -file_pack_dir="$(mktemp -d "/tmp/openclaw-plugin-filepack.XXXXXX")" +file_pack_dir="$(mktemp -d "$OPENCLAW_PLUGINS_TMP_DIR/openclaw-plugin-filepack.XXXXXX")" write_fixture_plugin "$file_pack_dir/package" demo-plugin-file 0.0.1 demo.file "Demo Plugin FILE" run_logged install-file node "$OPENCLAW_ENTRY" plugins install "file:$file_pack_dir/package" -node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins4.json -node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-file --runtime --json >/tmp/plugins4-inspect.json +node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins4.json" +node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-file --runtime --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins4-inspect.json" node scripts/e2e/lib/plugins/assertions.mjs plugin-file "$file_pack_dir/package" run_logged uninstall-file node "$OPENCLAW_ENTRY" plugins uninstall demo-plugin-file --force -node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins4-uninstalled.json +node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins4-uninstalled.json" node scripts/e2e/lib/plugins/assertions.mjs plugin-file-removed echo "Testing install and update from npm registry..." -npm_pack_dir="$(mktemp -d "/tmp/openclaw-plugin-npm-pack.XXXXXX")" -npm_dep_pack_dir="$(mktemp -d "/tmp/openclaw-plugin-npm-dep-pack.XXXXXX")" -invalid_npm_pack_dir="$(mktemp -d "/tmp/openclaw-plugin-invalid-metadata-pack.XXXXXX")" -npm_registry_dir="$(mktemp -d "/tmp/openclaw-plugin-npm-registry.XXXXXX")" -pack_fixture_plugin_with_cli_registry_dependency "$npm_pack_dir" /tmp/demo-plugin-npm.tgz demo-plugin-npm 0.0.1 demo.npm "Demo Plugin NPM" demo-npm "demo-plugin-npm:pong" -pack_fake_is_number_package "$npm_dep_pack_dir" /tmp/is-number-7.0.0.tgz -pack_fixture_plugin_with_invalid_extension_entry "$invalid_npm_pack_dir" /tmp/demo-plugin-invalid-metadata.tgz demo-plugin-invalid-metadata 0.0.1 demo.invalid.metadata "Demo Plugin Invalid Metadata" -start_npm_fixture_registry "@openclaw/demo-plugin-npm" "0.0.1" /tmp/demo-plugin-npm.tgz "$npm_registry_dir" "is-number" "7.0.0" /tmp/is-number-7.0.0.tgz "@openclaw/demo-plugin-invalid-metadata" "0.0.1" /tmp/demo-plugin-invalid-metadata.tgz +npm_pack_dir="$(mktemp -d "$OPENCLAW_PLUGINS_TMP_DIR/openclaw-plugin-npm-pack.XXXXXX")" +npm_dep_pack_dir="$(mktemp -d "$OPENCLAW_PLUGINS_TMP_DIR/openclaw-plugin-npm-dep-pack.XXXXXX")" +invalid_npm_pack_dir="$(mktemp -d "$OPENCLAW_PLUGINS_TMP_DIR/openclaw-plugin-invalid-metadata-pack.XXXXXX")" +npm_registry_dir="$(mktemp -d "$OPENCLAW_PLUGINS_TMP_DIR/openclaw-plugin-npm-registry.XXXXXX")" +pack_fixture_plugin_with_cli_registry_dependency "$npm_pack_dir" "$OPENCLAW_PLUGINS_TMP_DIR/demo-plugin-npm.tgz" demo-plugin-npm 0.0.1 demo.npm "Demo Plugin NPM" demo-npm "demo-plugin-npm:pong" +pack_fake_is_number_package "$npm_dep_pack_dir" "$OPENCLAW_PLUGINS_TMP_DIR/is-number-7.0.0.tgz" +pack_fixture_plugin_with_invalid_extension_entry "$invalid_npm_pack_dir" "$OPENCLAW_PLUGINS_TMP_DIR/demo-plugin-invalid-metadata.tgz" demo-plugin-invalid-metadata 0.0.1 demo.invalid.metadata "Demo Plugin Invalid Metadata" +start_npm_fixture_registry "@openclaw/demo-plugin-npm" "0.0.1" "$OPENCLAW_PLUGINS_TMP_DIR/demo-plugin-npm.tgz" "$npm_registry_dir" "is-number" "7.0.0" "$OPENCLAW_PLUGINS_TMP_DIR/is-number-7.0.0.tgz" "@openclaw/demo-plugin-invalid-metadata" "0.0.1" "$OPENCLAW_PLUGINS_TMP_DIR/demo-plugin-invalid-metadata.tgz" run_logged install-npm node "$OPENCLAW_ENTRY" plugins install "npm:@openclaw/demo-plugin-npm@0.0.1" -node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins-npm.json -node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-npm --runtime --json >/tmp/plugins-npm-inspect.json -run_logged exec-npm-plugin-cli bash -c 'node "$OPENCLAW_ENTRY" demo-npm ping >/tmp/plugins-npm-cli.txt' +node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-npm.json" +node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-npm --runtime --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-npm-inspect.json" +run_logged exec-npm-plugin-cli bash -c 'node "$OPENCLAW_ENTRY" demo-npm ping >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-npm-cli.txt"' node scripts/e2e/lib/plugins/assertions.mjs plugin-npm -node "$OPENCLAW_ENTRY" plugins update demo-plugin-npm >/tmp/plugins-npm-update.log 2>&1 +node "$OPENCLAW_ENTRY" plugins update demo-plugin-npm >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-npm-update.log" 2>&1 node scripts/e2e/lib/plugins/assertions.mjs plugin-npm-update run_logged uninstall-npm node "$OPENCLAW_ENTRY" plugins uninstall demo-plugin-npm --force -node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins-npm-uninstalled.json +node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-npm-uninstalled.json" node scripts/e2e/lib/plugins/assertions.mjs plugin-npm-removed echo "Testing npm install rejects malformed package metadata..." -if node "$OPENCLAW_ENTRY" plugins install "npm:@openclaw/demo-plugin-invalid-metadata@0.0.1" > /tmp/plugins-invalid-openclaw-extensions.log 2>&1; then - cat /tmp/plugins-invalid-openclaw-extensions.log +if node "$OPENCLAW_ENTRY" plugins install "npm:@openclaw/demo-plugin-invalid-metadata@0.0.1" >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-invalid-openclaw-extensions.log" 2>&1; then + cat "$OPENCLAW_PLUGINS_TMP_DIR/plugins-invalid-openclaw-extensions.log" echo "Expected malformed package metadata install to fail." >&2 exit 1 fi -node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins-invalid-openclaw-extensions-list.json +node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-invalid-openclaw-extensions-list.json" node scripts/e2e/lib/plugins/assertions.mjs invalid-openclaw-extensions echo "Testing install from git repo and plugin CLI execution..." -git_fixture_root="$(mktemp -d "/tmp/openclaw-plugin-git.XXXXXX")" +git_fixture_root="$(mktemp -d "$OPENCLAW_PLUGINS_TMP_DIR/openclaw-plugin-git.XXXXXX")" git_repo="$git_fixture_root/repo" git_repo_url="file://$git_repo" write_fixture_plugin_with_cli "$git_repo" demo-plugin-git 0.0.1 demo.git "Demo Plugin Git" demo-git "demo-plugin-git:pong" @@ -131,18 +133,18 @@ git -C "$git_repo" commit -qm "test fixture" git_ref="$(git -C "$git_repo" rev-parse HEAD)" run_logged install-git node "$OPENCLAW_ENTRY" plugins install "git:$git_repo_url@$git_ref" -node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins-git.json -node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-git --runtime --json >/tmp/plugins-git-inspect.json -run_logged exec-git-plugin-cli bash -c 'node "$OPENCLAW_ENTRY" demo-git ping >/tmp/plugins-git-cli.txt' +node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-git.json" +node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-git --runtime --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-git-inspect.json" +run_logged exec-git-plugin-cli bash -c 'node "$OPENCLAW_ENTRY" demo-git ping >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-git-cli.txt"' node scripts/e2e/lib/plugins/assertions.mjs plugin-git "$git_repo_url" "$git_ref" run_logged uninstall-git node "$OPENCLAW_ENTRY" plugins uninstall demo-plugin-git --force -node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins-git-uninstalled.json +node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-git-uninstalled.json" node scripts/e2e/lib/plugins/assertions.mjs plugin-git-removed echo "Testing git plugin update from moving ref..." -git_update_fixture_root="$(mktemp -d "/tmp/openclaw-plugin-git-update.XXXXXX")" +git_update_fixture_root="$(mktemp -d "$OPENCLAW_PLUGINS_TMP_DIR/openclaw-plugin-git-update.XXXXXX")" git_update_repo="$git_update_fixture_root/repo" git_update_repo_url="file://$git_update_repo" write_fixture_plugin_with_cli "$git_update_repo" demo-plugin-git-update 0.0.1 demo.git.update.v1 "Demo Plugin Git Update" demo-git-update "demo-plugin-git-update:pong-v1" @@ -159,10 +161,10 @@ write_fixture_plugin_with_cli "$git_update_repo" demo-plugin-git-update 0.0.2 de git -C "$git_update_repo" add -A git -C "$git_update_repo" commit -qm "test fixture v2" -node "$OPENCLAW_ENTRY" plugins update demo-plugin-git-update >/tmp/plugins-git-update.log 2>&1 -node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins-git-update.json -node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-git-update --runtime --json >/tmp/plugins-git-update-inspect.json -run_logged exec-updated-git-plugin-cli bash -c 'node "$OPENCLAW_ENTRY" demo-git-update ping >/tmp/plugins-git-update-cli.txt' +node "$OPENCLAW_ENTRY" plugins update demo-plugin-git-update >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-git-update.log" 2>&1 +node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-git-update.json" +node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-git-update --runtime --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-git-update-inspect.json" +run_logged exec-updated-git-plugin-cli bash -c 'node "$OPENCLAW_ENTRY" demo-git-update ping >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-git-update-cli.txt"' node scripts/e2e/lib/plugins/assertions.mjs plugin-git-updated "$git_update_ref_v1" @@ -172,19 +174,19 @@ bundle_root="$OPENCLAW_PLUGIN_HOME/$bundle_plugin_id" write_claude_bundle_fixture "$bundle_root" record_fixture_plugin_trust "$bundle_plugin_id" "$bundle_root" 0 -node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins-bundle-disabled.json +node "$OPENCLAW_ENTRY" plugins list --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-bundle-disabled.json" node scripts/e2e/lib/plugins/assertions.mjs bundle-disabled run_logged enable-claude-bundle node "$OPENCLAW_ENTRY" plugins enable claude-bundle-e2e -node "$OPENCLAW_ENTRY" plugins inspect claude-bundle-e2e --json >/tmp/plugins-bundle-inspect.json +node "$OPENCLAW_ENTRY" plugins inspect claude-bundle-e2e --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugins-bundle-inspect.json" node scripts/e2e/lib/plugins/assertions.mjs bundle-inspect echo "Testing plugin install visible after explicit restart..." -slash_install_dir="$(mktemp -d "/tmp/openclaw-plugin-slash-install.XXXXXX")" +slash_install_dir="$(mktemp -d "$OPENCLAW_PLUGINS_TMP_DIR/openclaw-plugin-slash-install.XXXXXX")" write_fixture_plugin "$slash_install_dir" slash-install-plugin 0.0.1 demo.slash.install "Slash Install Plugin" run_logged install-slash-plugin node "$OPENCLAW_ENTRY" plugins install "$slash_install_dir" -node "$OPENCLAW_ENTRY" plugins inspect slash-install-plugin --runtime --json >/tmp/plugin-command-install-show.json +node "$OPENCLAW_ENTRY" plugins inspect slash-install-plugin --runtime --json >"$OPENCLAW_PLUGINS_TMP_DIR/plugin-command-install-show.json" node scripts/e2e/lib/plugins/assertions.mjs slash-install run_plugins_marketplace_scenario diff --git a/test/scripts/plugins-assertions.test.ts b/test/scripts/plugins-assertions.test.ts new file mode 100644 index 00000000000..ac264a804dd --- /dev/null +++ b/test/scripts/plugins-assertions.test.ts @@ -0,0 +1,68 @@ +import { spawnSync } from "node:child_process"; +import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { describe, expect, it } from "vitest"; + +const ASSERTIONS_SCRIPT = "scripts/e2e/lib/plugins/assertions.mjs"; + +function writeJson(filePath: string, value: unknown) { + mkdirSync(path.dirname(filePath), { recursive: true }); + writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8"); +} + +describe("plugins Docker assertions", () => { + it("keeps sweep artifact paths aligned with the assertion scratch root", () => { + const scripts = [ + "scripts/e2e/lib/plugins/sweep.sh", + "scripts/e2e/lib/plugins/marketplace.sh", + "scripts/e2e/lib/plugins/clawhub.sh", + ]; + + for (const scriptPath of scripts) { + const script = readFileSync(scriptPath, "utf8"); + expect(script).toContain("OPENCLAW_PLUGINS_TMP_DIR"); + expect(script).not.toMatch( + /\/tmp\/(?:plugins|marketplace|demo-plugin|is-number|openclaw-plugin|openclaw-clawhub)/, + ); + } + }); + + it("uses the configured scratch root and resolves Windows home-relative install paths", () => { + const root = mkdtempSync(path.join(tmpdir(), "openclaw-plugins-assertions-")); + const home = path.join(root, "home"); + const scratchRoot = path.join(root, "scratch"); + const installPath = path.join(home, "managed-plugin"); + mkdirSync(installPath, { recursive: true }); + + try { + writeJson(path.join(scratchRoot, "plugins2.json"), { + plugins: [{ id: "demo-plugin-tgz", status: "loaded" }], + }); + writeJson(path.join(scratchRoot, "plugins2-inspect.json"), { + gatewayMethods: ["demo.tgz"], + }); + writeJson(path.join(home, ".openclaw", "plugins", "installs.json"), { + installRecords: { + "demo-plugin-tgz": { + source: "archive", + installPath: String.raw`~\managed-plugin`, + }, + }, + }); + + const result = spawnSync(process.execPath, [ASSERTIONS_SCRIPT, "plugin-tgz"], { + encoding: "utf8", + env: { + ...process.env, + HOME: home, + OPENCLAW_PLUGINS_TMP_DIR: scratchRoot, + }, + }); + + expect(result.status).toBe(0); + } finally { + rmSync(root, { force: true, recursive: true }); + } + }); +});