fix: align plugin install tests with ledger store

This commit is contained in:
Peter Steinberger
2026-04-25 19:44:11 +01:00
parent 4c0e9a4b2e
commit fecf1e9b8f
3 changed files with 32 additions and 72 deletions

View File

@@ -14,6 +14,7 @@ type ListMarketplacePluginsFn =
(typeof import("../plugins/marketplace.js"))["listMarketplacePlugins"];
type ResolveMarketplaceInstallShortcutFn =
(typeof import("../plugins/marketplace.js"))["resolveMarketplaceInstallShortcut"];
type LoadPluginInstallRecordsParams = { config?: OpenClawConfig };
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper preserves mock call and result types.
function invokeMock<TArgs extends unknown[], TResult>(mock: unknown, ...args: TArgs): TResult {
@@ -32,9 +33,9 @@ export const listMarketplacePlugins: Mock<ListMarketplacePluginsFn> = vi.fn();
export const resolveMarketplaceInstallShortcut: Mock<ResolveMarketplaceInstallShortcutFn> = vi.fn();
export const enablePluginInConfig: UnknownMock = vi.fn();
export const recordPluginInstall: UnknownMock = vi.fn();
export const loadPluginInstallRecords: AsyncUnknownMock = vi.fn(async ({ config }) => {
const cfg = config as OpenClawConfig | undefined;
return structuredClone(cfg?.plugins?.installs ?? {});
export const loadPluginInstallRecords: AsyncUnknownMock = vi.fn(async (...args: unknown[]) => {
const params = args[0] as LoadPluginInstallRecordsParams | undefined;
return structuredClone(params?.config?.plugins?.installs ?? {});
});
export const writePersistedPluginInstallLedger: AsyncUnknownMock = vi.fn(async () => undefined);
export const clearPluginManifestRegistryCache: UnknownMock = vi.fn();
@@ -518,9 +519,9 @@ export function resetPluginsCliTestState() {
recordPluginInstall.mockImplementation(
((cfg: OpenClawConfig) => cfg) as (...args: unknown[]) => unknown,
);
loadPluginInstallRecords.mockImplementation(async ({ config }) => {
const cfg = config as OpenClawConfig | undefined;
return structuredClone(cfg?.plugins?.installs ?? {});
loadPluginInstallRecords.mockImplementation(async (...args: unknown[]) => {
const params = args[0] as LoadPluginInstallRecordsParams | undefined;
return structuredClone(params?.config?.plugins?.installs ?? {});
});
writePersistedPluginInstallLedger.mockResolvedValue(undefined);
loadPluginManifestRegistry.mockReturnValue({

View File

@@ -25,6 +25,7 @@ import {
runtimeErrors,
runtimeLogs,
writeConfigFile,
writePersistedPluginInstallLedger,
} from "./plugins-cli-test-helpers.js";
const CLI_STATE_ROOT = "/tmp/openclaw-state";
@@ -53,22 +54,6 @@ function createEmptyPluginConfig(): OpenClawConfig {
} as OpenClawConfig;
}
function createClawHubInstalledConfig(params: {
pluginId: string;
install: Record<string, unknown>;
}): OpenClawConfig {
const enabledCfg = createEnabledPluginConfig(params.pluginId);
return {
...enabledCfg,
plugins: {
...enabledCfg.plugins,
installs: {
[params.pluginId]: params.install,
},
},
} as OpenClawConfig;
}
function createClawHubInstallResult(params: {
pluginId: string;
packageName: string;
@@ -320,19 +305,6 @@ describe("plugins cli install", () => {
},
},
} as OpenClawConfig;
const installedCfg = {
...enabledCfg,
plugins: {
...enabledCfg.plugins,
installs: {
alpha: {
source: "marketplace",
installPath: cliInstallPath("alpha"),
},
},
},
} as OpenClawConfig;
loadConfig.mockReturnValue(cfg);
installPluginFromMarketplace.mockResolvedValue({
ok: true,
@@ -345,20 +317,25 @@ describe("plugins cli install", () => {
marketplacePlugin: "alpha",
});
enablePluginInConfig.mockReturnValue({ config: enabledCfg });
recordPluginInstall.mockReturnValue(installedCfg);
buildPluginDiagnosticsReport.mockReturnValue({
plugins: [{ id: "alpha", kind: "provider" }],
diagnostics: [],
});
applyExclusiveSlotSelection.mockReturnValue({
config: installedCfg,
config: enabledCfg,
warnings: ["slot adjusted"],
});
await runPluginsCommand(["plugins", "install", "alpha", "--marketplace", "local/repo"]);
expect(clearPluginManifestRegistryCache).toHaveBeenCalledTimes(1);
expect(writeConfigFile).toHaveBeenCalledWith(installedCfg);
expect(writePersistedPluginInstallLedger).toHaveBeenCalledWith({
alpha: expect.objectContaining({
source: "marketplace",
installPath: cliInstallPath("alpha"),
}),
});
expect(writeConfigFile).toHaveBeenCalledWith(enabledCfg);
expect(runtimeLogs.some((line) => line.includes("slot adjusted"))).toBe(true);
expect(runtimeLogs.some((line) => line.includes("Installed plugin: alpha"))).toBe(true);
});
@@ -384,18 +361,6 @@ describe("plugins cli install", () => {
},
} as OpenClawConfig;
const enabledCfg = createEnabledPluginConfig("demo");
const installedCfg = createClawHubInstalledConfig({
pluginId: "demo",
install: {
source: "clawhub",
spec: "clawhub:demo@1.2.3",
installPath: cliInstallPath("demo"),
clawhubPackage: "demo",
clawhubFamily: "code-plugin",
clawhubChannel: "official",
},
});
loadConfig.mockReturnValue(cfg);
parseClawHubPluginSpec.mockReturnValue({ name: "demo" });
installPluginFromClawHub.mockResolvedValue(
@@ -407,9 +372,8 @@ describe("plugins cli install", () => {
}),
);
enablePluginInConfig.mockReturnValue({ config: enabledCfg });
recordPluginInstall.mockReturnValue(installedCfg);
applyExclusiveSlotSelection.mockReturnValue({
config: installedCfg,
config: enabledCfg,
warnings: [],
});
@@ -420,18 +384,16 @@ describe("plugins cli install", () => {
spec: "clawhub:demo",
}),
);
expect(recordPluginInstall).toHaveBeenCalledWith(
enabledCfg,
expect.objectContaining({
pluginId: "demo",
expect(writePersistedPluginInstallLedger).toHaveBeenCalledWith({
demo: expect.objectContaining({
source: "clawhub",
spec: "clawhub:demo@1.2.3",
clawhubPackage: "demo",
clawhubFamily: "code-plugin",
clawhubChannel: "official",
}),
);
expect(writeConfigFile).toHaveBeenCalledWith(installedCfg);
});
expect(writeConfigFile).toHaveBeenCalledWith(enabledCfg);
expect(runtimeLogs.some((line) => line.includes("Installed plugin: demo"))).toBe(true);
expect(installPluginFromNpmSpec).not.toHaveBeenCalled();
});
@@ -478,16 +440,6 @@ describe("plugins cli install", () => {
},
} as OpenClawConfig;
const enabledCfg = createEnabledPluginConfig("demo");
const installedCfg = createClawHubInstalledConfig({
pluginId: "demo",
install: {
source: "clawhub",
spec: "clawhub:demo@1.2.3",
installPath: cliInstallPath("demo"),
clawhubPackage: "demo",
},
});
loadConfig.mockReturnValue(cfg);
installPluginFromClawHub.mockResolvedValue(
createClawHubInstallResult({
@@ -498,9 +450,8 @@ describe("plugins cli install", () => {
}),
);
enablePluginInConfig.mockReturnValue({ config: enabledCfg });
recordPluginInstall.mockReturnValue(installedCfg);
applyExclusiveSlotSelection.mockReturnValue({
config: installedCfg,
config: enabledCfg,
warnings: [],
});
@@ -512,7 +463,14 @@ describe("plugins cli install", () => {
}),
);
expect(installPluginFromNpmSpec).not.toHaveBeenCalled();
expect(writeConfigFile).toHaveBeenCalledWith(installedCfg);
expect(writePersistedPluginInstallLedger).toHaveBeenCalledWith({
demo: expect.objectContaining({
source: "clawhub",
spec: "clawhub:demo@1.2.3",
clawhubPackage: "demo",
}),
});
expect(writeConfigFile).toHaveBeenCalledWith(enabledCfg);
});
it("falls back to npm when ClawHub does not have the package", async () => {

View File

@@ -2,6 +2,7 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import type { PluginInstallRecord } from "../config/types.plugins.js";
import {
loadPluginInstallRecords,
loadPluginInstallRecordsSync,
@@ -127,7 +128,7 @@ describe("plugin install ledger store", () => {
source: "npm",
spec: "keep@1.0.0",
},
};
} satisfies Record<string, PluginInstallRecord>;
const withInstall = recordPluginInstallInRecords(records, {
pluginId: "demo",
source: "npm",