mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:10:43 +00:00
test(plugins): cover install lifecycle edges
This commit is contained in:
@@ -299,6 +299,40 @@ describe("installPluginFromNpmSpec", () => {
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("allows duplicate npm installs in update mode", async () => {
|
||||
const stateDir = suiteTempRootTracker.makeTempDir();
|
||||
const npmRoot = path.join(stateDir, "npm");
|
||||
const installRoot = path.join(npmRoot, "node_modules", "@openclaw", "voice-call");
|
||||
fs.mkdirSync(installRoot, { recursive: true });
|
||||
fs.writeFileSync(path.join(installRoot, "old.txt"), "old", "utf-8");
|
||||
mockNpmViewAndInstall({
|
||||
spec: "@openclaw/voice-call@0.0.2",
|
||||
packageName: "@openclaw/voice-call",
|
||||
version: "0.0.2",
|
||||
pluginId: "voice-call",
|
||||
npmRoot,
|
||||
});
|
||||
|
||||
const result = await installPluginFromNpmSpec({
|
||||
spec: "@openclaw/voice-call@0.0.2",
|
||||
npmDir: npmRoot,
|
||||
mode: "update",
|
||||
logger: { info: () => {}, warn: () => {} },
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) {
|
||||
throw new Error(result.error);
|
||||
}
|
||||
expect(result.targetDir).toBe(installRoot);
|
||||
expect(result.npmResolution?.version).toBe("0.0.2");
|
||||
expectNpmInstallIntoRoot({
|
||||
calls: runCommandWithTimeoutMock.mock.calls,
|
||||
npmRoot,
|
||||
spec: "@openclaw/voice-call@0.0.2",
|
||||
});
|
||||
});
|
||||
|
||||
it("aborts when integrity drift callback rejects the fetched artifact", async () => {
|
||||
mockNpmViewMetadataResult(runCommandWithTimeoutMock, {
|
||||
name: "@openclaw/voice-call",
|
||||
|
||||
@@ -1061,6 +1061,37 @@ describe("uninstallPlugin", () => {
|
||||
});
|
||||
await expect(fs.access(installPath)).rejects.toThrow();
|
||||
});
|
||||
|
||||
it("does not delete symlinked git install targets that resolve outside the managed git root", async () => {
|
||||
const stateDir = path.join(tempDir, "state");
|
||||
const extensionsDir = path.join(stateDir, "extensions");
|
||||
const linkParentDir = path.join(stateDir, "git", "git-abc123");
|
||||
const installPath = path.join(linkParentDir, "repo");
|
||||
const outsideDir = path.join(tempDir, "outside");
|
||||
await fs.mkdir(linkParentDir, { recursive: true });
|
||||
await fs.mkdir(outsideDir, { recursive: true });
|
||||
await fs.writeFile(path.join(outsideDir, "index.js"), "// keep me");
|
||||
await fs.symlink(outsideDir, installPath, "dir");
|
||||
|
||||
const result = await uninstallPlugin({
|
||||
config: createPluginConfig({
|
||||
entries: createSinglePluginEntries(),
|
||||
installs: {
|
||||
"my-plugin": createGitInstallRecord("my-plugin", installPath),
|
||||
},
|
||||
}),
|
||||
pluginId: "my-plugin",
|
||||
deleteFiles: true,
|
||||
extensionsDir,
|
||||
});
|
||||
|
||||
expectSuccessfulUninstallActions(result, {
|
||||
directory: false,
|
||||
});
|
||||
await expect(fs.access(outsideDir)).resolves.toBeUndefined();
|
||||
const linkStat = await fs.lstat(installPath);
|
||||
expect(linkStat.isSymbolicLink()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveUninstallDirectoryTarget", () => {
|
||||
|
||||
Reference in New Issue
Block a user