mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-03 00:56:22 +00:00
fix(e2e): harden Windows plugin assertions
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
68
test/scripts/plugins-assertions.test.ts
Normal file
68
test/scripts/plugins-assertions.test.ts
Normal file
@@ -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 });
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user