Skip to content

Commit f8db47e

Browse files
committed
fix(e2e): verify bundled plugin source roots
1 parent cd1a90b commit f8db47e

3 files changed

Lines changed: 101 additions & 9 deletions

File tree

scripts/e2e/lib/bundled-plugin-install-uninstall/probe.mjs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ function pathReferencesPackagedBundledRoot(value) {
5656
return bundledRuntimeRootFragments.some((fragment) => normalized.includes(fragment));
5757
}
5858

59+
function pathsEqualForProbe(actual, expected) {
60+
return normalizePathForProbe(actual) === normalizePathForProbe(expected);
61+
}
62+
5963
function resolveOpenClawEntry() {
6064
if (process.env.OPENCLAW_ENTRY) {
6165
return process.env.OPENCLAW_ENTRY;
@@ -173,7 +177,7 @@ async function selectedManifestEntries() {
173177
return selected;
174178
}
175179

176-
function assertInstalled(pluginId, pluginDir, requiresConfig) {
180+
function assertInstalled(pluginId, pluginDir, requiresConfig, selectedPluginRoot = "") {
177181
const stateDir = resolveStateDir();
178182
const configPath = path.join(stateDir, "openclaw.json");
179183
const config = readJson(configPath);
@@ -187,13 +191,22 @@ function assertInstalled(pluginId, pluginDir, requiresConfig) {
187191
`expected bundled install record source=path for ${pluginId}, got ${record.source}`,
188192
);
189193
}
190-
if (
191-
typeof record.sourcePath !== "string" ||
192-
!pathReferencesBundledRuntime(record.sourcePath, pluginDir)
193-
) {
194+
const sourcePath = typeof record.sourcePath === "string" ? record.sourcePath : "";
195+
if (!sourcePath) {
196+
throw new Error(`unexpected bundled source path for ${pluginId}: ${record.sourcePath}`);
197+
}
198+
if (selectedPluginRoot && !pathsEqualForProbe(sourcePath, selectedPluginRoot)) {
199+
throw new Error(
200+
`bundled source path for ${pluginId} did not match selected root: expected ${selectedPluginRoot}, got ${record.sourcePath}`,
201+
);
202+
}
203+
if (!selectedPluginRoot && !pathReferencesBundledRuntime(sourcePath, pluginDir)) {
194204
throw new Error(`unexpected bundled source path for ${pluginId}: ${record.sourcePath}`);
195205
}
196-
if (normalizePathForProbe(record.installPath) !== normalizePathForProbe(record.sourcePath)) {
206+
if (selectedPluginRoot && !fs.existsSync(sourcePath)) {
207+
throw new Error(`bundled source path for ${pluginId} does not exist: ${record.sourcePath}`);
208+
}
209+
if (!pathsEqualForProbe(record.installPath, record.sourcePath)) {
197210
throw new Error(`bundled install path should equal source path for ${pluginId}`);
198211
}
199212
const paths = config.plugins?.load?.paths || [];
@@ -246,13 +259,13 @@ function assertUninstalled(pluginId, pluginDir) {
246259
}
247260
}
248261

249-
const [command, pluginId, pluginDir, requiresConfig] = process.argv.slice(2);
262+
const [command, pluginId, pluginDir, requiresConfig, selectedPluginRoot] = process.argv.slice(2);
250263
if (command === "select") {
251264
for (const entry of await selectedManifestEntries()) {
252265
console.log(`${entry.id}\t${entry.dir}\t${entry.requiresConfig ? "1" : "0"}\t${entry.rootDir}`);
253266
}
254267
} else if (command === "assert-installed") {
255-
assertInstalled(pluginId, pluginDir, requiresConfig === "1");
268+
assertInstalled(pluginId, pluginDir, requiresConfig === "1", selectedPluginRoot);
256269
} else if (command === "assert-uninstalled") {
257270
assertUninstalled(pluginId, pluginDir);
258271
} else {

scripts/e2e/lib/bundled-plugin-install-uninstall/sweep.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ for plugin_entry in "${plugin_entries[@]}"; do
7979
docker_e2e_print_log "$install_log"
8080
fi
8181
install_finished_at="$(now_ms)"
82-
node "$probe" assert-installed "$plugin_id" "$plugin_dir" "$requires_config"
82+
node "$probe" assert-installed "$plugin_id" "$plugin_dir" "$requires_config" "$plugin_root"
8383
installed_asserted_at="$(now_ms)"
8484
if [[ "${OPENCLAW_BUNDLED_PLUGIN_RUNTIME_SMOKE:-1}" != "0" ]]; then
8585
echo "Running bundled plugin runtime smoke: $plugin_id ($plugin_dir)"

test/scripts/bundled-plugin-install-uninstall-probe.test.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,6 +1222,85 @@ describe("bundled plugin install/uninstall probe", () => {
12221222
expect(result.status).toBe(0);
12231223
});
12241224

1225+
it("requires bundled install source paths to match the selected plugin root", () => {
1226+
const root = makePackageRoot();
1227+
const stateDir = path.join(root, "state");
1228+
const selectedRoot = path.join(root, "dist-runtime", "extensions", "nostr");
1229+
const staleRoot = path.join(root, "dist-runtime", "extensions", "nostr-copy");
1230+
fs.mkdirSync(path.join(stateDir, "plugins"), { recursive: true });
1231+
fs.mkdirSync(selectedRoot, { recursive: true });
1232+
fs.mkdirSync(staleRoot, { recursive: true });
1233+
fs.writeFileSync(
1234+
path.join(stateDir, "openclaw.json"),
1235+
JSON.stringify({ plugins: { entries: { nostr: { enabled: true } } } }),
1236+
"utf8",
1237+
);
1238+
fs.writeFileSync(
1239+
path.join(stateDir, "plugins", "installs.json"),
1240+
JSON.stringify({
1241+
installRecords: {
1242+
nostr: {
1243+
source: "path",
1244+
sourcePath: staleRoot,
1245+
installPath: staleRoot,
1246+
},
1247+
},
1248+
}),
1249+
"utf8",
1250+
);
1251+
writePluginsList(root, []);
1252+
1253+
const result = runProbeCommand(
1254+
root,
1255+
["assert-installed", "nostr", "nostr", "0", selectedRoot],
1256+
{
1257+
HOME: undefined,
1258+
OPENCLAW_STATE_DIR: stateDir,
1259+
},
1260+
);
1261+
1262+
expect(result.status).not.toBe(0);
1263+
expect(result.stderr).toContain("did not match selected root");
1264+
});
1265+
1266+
it("requires bundled install source paths to exist", () => {
1267+
const root = makePackageRoot();
1268+
const stateDir = path.join(root, "state");
1269+
const selectedRoot = path.join(root, "dist-runtime", "extensions", "nostr");
1270+
fs.mkdirSync(path.join(stateDir, "plugins"), { recursive: true });
1271+
fs.writeFileSync(
1272+
path.join(stateDir, "openclaw.json"),
1273+
JSON.stringify({ plugins: { entries: { nostr: { enabled: true } } } }),
1274+
"utf8",
1275+
);
1276+
fs.writeFileSync(
1277+
path.join(stateDir, "plugins", "installs.json"),
1278+
JSON.stringify({
1279+
installRecords: {
1280+
nostr: {
1281+
source: "path",
1282+
sourcePath: selectedRoot,
1283+
installPath: selectedRoot,
1284+
},
1285+
},
1286+
}),
1287+
"utf8",
1288+
);
1289+
writePluginsList(root, []);
1290+
1291+
const result = runProbeCommand(
1292+
root,
1293+
["assert-installed", "nostr", "nostr", "0", selectedRoot],
1294+
{
1295+
HOME: undefined,
1296+
OPENCLAW_STATE_DIR: stateDir,
1297+
},
1298+
);
1299+
1300+
expect(result.status).not.toBe(0);
1301+
expect(result.stderr).toContain("does not exist");
1302+
});
1303+
12251304
it("detects native Windows bundled load paths after uninstall", () => {
12261305
const root = makePackageRoot();
12271306
const stateDir = path.join(root, "state");

0 commit comments

Comments
 (0)