Files
openclaw/src/cli/plugins-cli-test-helpers.ts
2026-04-27 12:35:58 +01:00

689 lines
27 KiB
TypeScript

import { Command } from "commander";
import type { Mock } from "vitest";
import { vi } from "vitest";
import { getRuntimeConfig } from "../config/config.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import type { PluginInstallRecord } from "../config/types.plugins.js";
import { createEmptyUninstallActions } from "../plugins/uninstall.js";
import { createCliRuntimeCapture } from "./test-runtime-capture.js";
type UnknownMock = Mock<(...args: unknown[]) => unknown>;
type AsyncUnknownMock = Mock<(...args: unknown[]) => Promise<unknown>>;
type LoadConfigFn = (typeof import("../config/config.js"))["loadConfig"];
type ParseClawHubPluginSpecFn = (typeof import("../infra/clawhub.js"))["parseClawHubPluginSpec"];
type InstallPluginFromMarketplaceFn =
(typeof import("../plugins/marketplace.js"))["installPluginFromMarketplace"];
type ListMarketplacePluginsFn =
(typeof import("../plugins/marketplace.js"))["listMarketplacePlugins"];
type ResolveMarketplaceInstallShortcutFn =
(typeof import("../plugins/marketplace.js"))["resolveMarketplaceInstallShortcut"];
type PluginInstallRecordMap = Record<string, PluginInstallRecord>;
let mockInstalledPluginIndexInstallRecords: PluginInstallRecordMap = {};
function clonePluginInstallRecords(records: PluginInstallRecordMap): PluginInstallRecordMap {
return structuredClone(records);
}
// 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 {
return (mock as (...args: TArgs) => TResult)(...args);
}
export const loadConfig: Mock<LoadConfigFn> = vi.fn<LoadConfigFn>(() => ({}) as OpenClawConfig);
export const readConfigFileSnapshot: AsyncUnknownMock = vi.fn();
export const writeConfigFile: AsyncUnknownMock = vi.fn(async () => undefined);
export const replaceConfigFile: AsyncUnknownMock = vi.fn(
async (params: { nextConfig: OpenClawConfig }) => await writeConfigFile(params.nextConfig),
) as AsyncUnknownMock;
export const resolveStateDir: Mock<() => string> = vi.fn(() => "/tmp/openclaw-state");
export const installPluginFromMarketplace: Mock<InstallPluginFromMarketplaceFn> = vi.fn();
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 loadInstalledPluginIndexInstallRecords: AsyncUnknownMock = vi.fn(async () =>
clonePluginInstallRecords(mockInstalledPluginIndexInstallRecords),
);
export const writePersistedInstalledPluginIndexInstallRecords: AsyncUnknownMock = vi.fn(
async (records: unknown) => {
mockInstalledPluginIndexInstallRecords = clonePluginInstallRecords(
(records ?? {}) as PluginInstallRecordMap,
);
},
);
export const clearPluginManifestRegistryCache: UnknownMock = vi.fn();
export const loadPluginManifestRegistry: UnknownMock = vi.fn();
export const buildPluginSnapshotReport: UnknownMock = vi.fn();
export const buildPluginRegistrySnapshotReport: UnknownMock = vi.fn();
export const buildPluginInspectReport: UnknownMock = vi.fn();
export const buildAllPluginInspectReports: UnknownMock = vi.fn();
export const buildPluginDiagnosticsReport: UnknownMock = vi.fn();
export const buildPluginCompatibilityNotices: UnknownMock = vi.fn();
export const inspectPluginRegistry: AsyncUnknownMock = vi.fn();
export const refreshPluginRegistry: AsyncUnknownMock = vi.fn();
export const applyExclusiveSlotSelection: UnknownMock = vi.fn();
export const planPluginUninstall: UnknownMock = vi.fn();
export const applyPluginUninstallDirectoryRemoval: AsyncUnknownMock = vi.fn();
export const uninstallPlugin: AsyncUnknownMock = vi.fn();
export const updateNpmInstalledPlugins: AsyncUnknownMock = vi.fn();
export const updateNpmInstalledHookPacks: AsyncUnknownMock = vi.fn();
export const promptYesNo: AsyncUnknownMock = vi.fn();
export const installPluginFromNpmSpec: AsyncUnknownMock = vi.fn();
export const installPluginFromPath: AsyncUnknownMock = vi.fn();
export const installPluginFromClawHub: AsyncUnknownMock = vi.fn();
export const parseClawHubPluginSpec: Mock<ParseClawHubPluginSpecFn> = vi.fn();
export const installHooksFromNpmSpec: AsyncUnknownMock = vi.fn();
export const installHooksFromPath: AsyncUnknownMock = vi.fn();
export const recordHookInstall: UnknownMock = vi.fn();
const { defaultRuntime, runtimeLogs, runtimeErrors, resetRuntimeCapture } =
createCliRuntimeCapture();
export { runtimeErrors, runtimeLogs };
export function setInstalledPluginIndexInstallRecords(records: PluginInstallRecordMap): void {
mockInstalledPluginIndexInstallRecords = clonePluginInstallRecords(records);
}
function restoreRuntimeCaptureMocks() {
defaultRuntime.log.mockReset();
defaultRuntime.log.mockImplementation((...args: unknown[]) => {
runtimeLogs.push(args.map((value) => String(value)).join(" "));
});
defaultRuntime.error.mockReset();
defaultRuntime.error.mockImplementation((...args: unknown[]) => {
runtimeErrors.push(args.map((value) => String(value)).join(" "));
});
defaultRuntime.writeStdout.mockReset();
defaultRuntime.writeStdout.mockImplementation((value: string) => {
defaultRuntime.log(value.endsWith("\n") ? value.slice(0, -1) : value);
});
defaultRuntime.writeJson.mockReset();
defaultRuntime.writeJson.mockImplementation((value: unknown, space = 2) => {
defaultRuntime.log(JSON.stringify(value, null, space > 0 ? space : undefined));
});
defaultRuntime.exit.mockReset();
defaultRuntime.exit.mockImplementation((code: number) => {
throw new Error(`__exit__:${code}`);
});
}
vi.mock("../runtime.js", () => ({
defaultRuntime,
}));
vi.mock("../config/config.js", () => ({
getRuntimeConfig: () => loadConfig(),
loadConfig: () => loadConfig(),
readConfigFileSnapshot: ((
...args: Parameters<(typeof import("../config/config.js"))["readConfigFileSnapshot"]>
) =>
invokeMock<
Parameters<(typeof import("../config/config.js"))["readConfigFileSnapshot"]>,
ReturnType<(typeof import("../config/config.js"))["readConfigFileSnapshot"]>
>(
readConfigFileSnapshot,
...args,
)) as (typeof import("../config/config.js"))["readConfigFileSnapshot"],
writeConfigFile: ((config: OpenClawConfig) =>
invokeMock<
[OpenClawConfig],
ReturnType<(typeof import("../config/config.js"))["writeConfigFile"]>
>(writeConfigFile, config)) as (typeof import("../config/config.js"))["writeConfigFile"],
replaceConfigFile: ((
params: Parameters<(typeof import("../config/config.js"))["replaceConfigFile"]>[0],
) =>
invokeMock<
[Parameters<(typeof import("../config/config.js"))["replaceConfigFile"]>[0]],
ReturnType<(typeof import("../config/config.js"))["replaceConfigFile"]>
>(replaceConfigFile, params)) as (typeof import("../config/config.js"))["replaceConfigFile"],
}));
vi.mock("../config/paths.js", () => ({
resolveStateDir: () => resolveStateDir(),
}));
vi.mock("../plugins/marketplace.js", () => ({
installPluginFromMarketplace: ((...args: Parameters<InstallPluginFromMarketplaceFn>) =>
installPluginFromMarketplace(...args)) as InstallPluginFromMarketplaceFn,
listMarketplacePlugins: ((...args: Parameters<ListMarketplacePluginsFn>) =>
listMarketplacePlugins(...args)) as ListMarketplacePluginsFn,
resolveMarketplaceInstallShortcut: ((...args: Parameters<ResolveMarketplaceInstallShortcutFn>) =>
resolveMarketplaceInstallShortcut(...args)) as ResolveMarketplaceInstallShortcutFn,
}));
vi.mock("../plugins/enable.js", () => ({
enablePluginInConfig: ((cfg: OpenClawConfig, pluginId: string) =>
invokeMock<[OpenClawConfig, string], unknown>(
enablePluginInConfig,
cfg,
pluginId,
)) as (typeof import("../plugins/enable.js"))["enablePluginInConfig"],
}));
vi.mock("../plugins/installs.js", () => ({
recordPluginInstall: ((
...args: Parameters<(typeof import("../plugins/installs.js"))["recordPluginInstall"]>
) =>
invokeMock<
Parameters<(typeof import("../plugins/installs.js"))["recordPluginInstall"]>,
ReturnType<(typeof import("../plugins/installs.js"))["recordPluginInstall"]>
>(
recordPluginInstall,
...args,
)) as (typeof import("../plugins/installs.js"))["recordPluginInstall"],
}));
vi.mock("../plugins/installed-plugin-index-records.js", async (importOriginal) => {
const actual =
await importOriginal<typeof import("../plugins/installed-plugin-index-records.js")>();
return {
...actual,
loadInstalledPluginIndexInstallRecords: ((...args: unknown[]) =>
invokeMock<unknown[], unknown>(loadInstalledPluginIndexInstallRecords, ...args)) as (
...args: unknown[]
) => unknown,
writePersistedInstalledPluginIndexInstallRecords: ((...args: unknown[]) =>
invokeMock<unknown[], unknown>(
writePersistedInstalledPluginIndexInstallRecords,
...args,
)) as (...args: unknown[]) => unknown,
recordPluginInstallInRecords: (
records: Record<string, unknown>,
update: { pluginId: string; installedAt?: string } & Record<string, unknown>,
) => {
const { pluginId, ...record } = update;
return {
...records,
[pluginId]: {
...(records[pluginId] as Record<string, unknown> | undefined),
...record,
installedAt: update.installedAt ?? "2026-04-25T00:00:00.000Z",
},
};
},
};
});
vi.mock("../plugins/manifest-registry.js", () => ({
clearPluginManifestRegistryCache: () => clearPluginManifestRegistryCache(),
loadPluginManifestRegistry: ((...args: unknown[]) =>
invokeMock<unknown[], unknown>(loadPluginManifestRegistry, ...args)) as (
...args: unknown[]
) => unknown,
}));
vi.mock("../plugins/status.js", () => ({
buildPluginSnapshotReport: ((
...args: Parameters<(typeof import("../plugins/status.js"))["buildPluginSnapshotReport"]>
) =>
invokeMock<
Parameters<(typeof import("../plugins/status.js"))["buildPluginSnapshotReport"]>,
ReturnType<(typeof import("../plugins/status.js"))["buildPluginSnapshotReport"]>
>(
buildPluginSnapshotReport,
...args,
)) as (typeof import("../plugins/status.js"))["buildPluginSnapshotReport"],
buildPluginRegistrySnapshotReport: ((
...args: Parameters<
(typeof import("../plugins/status.js"))["buildPluginRegistrySnapshotReport"]
>
) =>
invokeMock<
Parameters<(typeof import("../plugins/status.js"))["buildPluginRegistrySnapshotReport"]>,
ReturnType<(typeof import("../plugins/status.js"))["buildPluginRegistrySnapshotReport"]>
>(
buildPluginRegistrySnapshotReport,
...args,
)) as (typeof import("../plugins/status.js"))["buildPluginRegistrySnapshotReport"],
buildPluginInspectReport: ((
...args: Parameters<(typeof import("../plugins/status.js"))["buildPluginInspectReport"]>
) =>
invokeMock<
Parameters<(typeof import("../plugins/status.js"))["buildPluginInspectReport"]>,
ReturnType<(typeof import("../plugins/status.js"))["buildPluginInspectReport"]>
>(
buildPluginInspectReport,
...args,
)) as (typeof import("../plugins/status.js"))["buildPluginInspectReport"],
buildAllPluginInspectReports: ((
...args: Parameters<(typeof import("../plugins/status.js"))["buildAllPluginInspectReports"]>
) =>
invokeMock<
Parameters<(typeof import("../plugins/status.js"))["buildAllPluginInspectReports"]>,
ReturnType<(typeof import("../plugins/status.js"))["buildAllPluginInspectReports"]>
>(
buildAllPluginInspectReports,
...args,
)) as (typeof import("../plugins/status.js"))["buildAllPluginInspectReports"],
buildPluginDiagnosticsReport: ((
...args: Parameters<(typeof import("../plugins/status.js"))["buildPluginDiagnosticsReport"]>
) =>
invokeMock<
Parameters<(typeof import("../plugins/status.js"))["buildPluginDiagnosticsReport"]>,
ReturnType<(typeof import("../plugins/status.js"))["buildPluginDiagnosticsReport"]>
>(
buildPluginDiagnosticsReport,
...args,
)) as (typeof import("../plugins/status.js"))["buildPluginDiagnosticsReport"],
buildPluginCompatibilityNotices: ((
...args: Parameters<(typeof import("../plugins/status.js"))["buildPluginCompatibilityNotices"]>
) =>
invokeMock<
Parameters<(typeof import("../plugins/status.js"))["buildPluginCompatibilityNotices"]>,
ReturnType<(typeof import("../plugins/status.js"))["buildPluginCompatibilityNotices"]>
>(
buildPluginCompatibilityNotices,
...args,
)) as (typeof import("../plugins/status.js"))["buildPluginCompatibilityNotices"],
formatPluginCompatibilityNotice: (entry: { message: string }) => entry.message,
}));
vi.mock("../plugins/plugin-registry.js", () => ({
loadPluginManifestRegistryForPluginRegistry: () => ({ diagnostics: [], plugins: [] }),
inspectPluginRegistry: ((
...args: Parameters<(typeof import("../plugins/plugin-registry.js"))["inspectPluginRegistry"]>
) =>
invokeMock<
Parameters<(typeof import("../plugins/plugin-registry.js"))["inspectPluginRegistry"]>,
ReturnType<(typeof import("../plugins/plugin-registry.js"))["inspectPluginRegistry"]>
>(
inspectPluginRegistry,
...args,
)) as (typeof import("../plugins/plugin-registry.js"))["inspectPluginRegistry"],
refreshPluginRegistry: ((
...args: Parameters<(typeof import("../plugins/plugin-registry.js"))["refreshPluginRegistry"]>
) =>
invokeMock<
Parameters<(typeof import("../plugins/plugin-registry.js"))["refreshPluginRegistry"]>,
ReturnType<(typeof import("../plugins/plugin-registry.js"))["refreshPluginRegistry"]>
>(
refreshPluginRegistry,
...args,
)) as (typeof import("../plugins/plugin-registry.js"))["refreshPluginRegistry"],
}));
vi.mock("../plugins/slots.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../plugins/slots.js")>();
return {
...actual,
applyExclusiveSlotSelection: ((
params: Parameters<(typeof import("../plugins/slots.js"))["applyExclusiveSlotSelection"]>[0],
) =>
invokeMock<
[Parameters<(typeof import("../plugins/slots.js"))["applyExclusiveSlotSelection"]>[0]],
ReturnType<(typeof import("../plugins/slots.js"))["applyExclusiveSlotSelection"]>
>(
applyExclusiveSlotSelection,
params,
)) as (typeof import("../plugins/slots.js"))["applyExclusiveSlotSelection"],
};
});
vi.mock("../plugins/uninstall.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../plugins/uninstall.js")>();
return {
...actual,
planPluginUninstall: ((
...args: Parameters<(typeof import("../plugins/uninstall.js"))["planPluginUninstall"]>
) =>
invokeMock<
Parameters<(typeof import("../plugins/uninstall.js"))["planPluginUninstall"]>,
ReturnType<(typeof import("../plugins/uninstall.js"))["planPluginUninstall"]>
>(
planPluginUninstall,
...args,
)) as (typeof import("../plugins/uninstall.js"))["planPluginUninstall"],
applyPluginUninstallDirectoryRemoval: ((
...args: Parameters<
(typeof import("../plugins/uninstall.js"))["applyPluginUninstallDirectoryRemoval"]
>
) =>
invokeMock<
Parameters<
(typeof import("../plugins/uninstall.js"))["applyPluginUninstallDirectoryRemoval"]
>,
ReturnType<
(typeof import("../plugins/uninstall.js"))["applyPluginUninstallDirectoryRemoval"]
>
>(
applyPluginUninstallDirectoryRemoval,
...args,
)) as (typeof import("../plugins/uninstall.js"))["applyPluginUninstallDirectoryRemoval"],
uninstallPlugin: ((
...args: Parameters<(typeof import("../plugins/uninstall.js"))["uninstallPlugin"]>
) =>
invokeMock<
Parameters<(typeof import("../plugins/uninstall.js"))["uninstallPlugin"]>,
ReturnType<(typeof import("../plugins/uninstall.js"))["uninstallPlugin"]>
>(uninstallPlugin, ...args)) as (typeof import("../plugins/uninstall.js"))["uninstallPlugin"],
resolveUninstallDirectoryTarget: ({
installRecord,
}: {
installRecord?: { installPath?: string; sourcePath?: string };
}) => installRecord?.installPath ?? installRecord?.sourcePath ?? null,
};
});
vi.mock("../plugins/update.js", () => ({
updateNpmInstalledPlugins: ((
...args: Parameters<(typeof import("../plugins/update.js"))["updateNpmInstalledPlugins"]>
) =>
invokeMock<
Parameters<(typeof import("../plugins/update.js"))["updateNpmInstalledPlugins"]>,
ReturnType<(typeof import("../plugins/update.js"))["updateNpmInstalledPlugins"]>
>(
updateNpmInstalledPlugins,
...args,
)) as (typeof import("../plugins/update.js"))["updateNpmInstalledPlugins"],
}));
vi.mock("../hooks/update.js", () => ({
updateNpmInstalledHookPacks: ((
...args: Parameters<(typeof import("../hooks/update.js"))["updateNpmInstalledHookPacks"]>
) =>
invokeMock<
Parameters<(typeof import("../hooks/update.js"))["updateNpmInstalledHookPacks"]>,
ReturnType<(typeof import("../hooks/update.js"))["updateNpmInstalledHookPacks"]>
>(
updateNpmInstalledHookPacks,
...args,
)) as (typeof import("../hooks/update.js"))["updateNpmInstalledHookPacks"],
}));
vi.mock("./prompt.js", () => ({
promptYesNo: ((...args: Parameters<(typeof import("./prompt.js"))["promptYesNo"]>) =>
invokeMock<
Parameters<(typeof import("./prompt.js"))["promptYesNo"]>,
ReturnType<(typeof import("./prompt.js"))["promptYesNo"]>
>(promptYesNo, ...args)) as (typeof import("./prompt.js"))["promptYesNo"],
}));
vi.mock("../plugins/install.js", () => ({
PLUGIN_INSTALL_ERROR_CODE: {
NPM_PACKAGE_NOT_FOUND: "npm_package_not_found",
SECURITY_SCAN_BLOCKED: "security_scan_blocked",
SECURITY_SCAN_FAILED: "security_scan_failed",
},
installPluginFromNpmSpec: ((
...args: Parameters<(typeof import("../plugins/install.js"))["installPluginFromNpmSpec"]>
) =>
invokeMock<
Parameters<(typeof import("../plugins/install.js"))["installPluginFromNpmSpec"]>,
ReturnType<(typeof import("../plugins/install.js"))["installPluginFromNpmSpec"]>
>(
installPluginFromNpmSpec,
...args,
)) as (typeof import("../plugins/install.js"))["installPluginFromNpmSpec"],
installPluginFromPath: ((
...args: Parameters<(typeof import("../plugins/install.js"))["installPluginFromPath"]>
) =>
invokeMock<
Parameters<(typeof import("../plugins/install.js"))["installPluginFromPath"]>,
ReturnType<(typeof import("../plugins/install.js"))["installPluginFromPath"]>
>(
installPluginFromPath,
...args,
)) as (typeof import("../plugins/install.js"))["installPluginFromPath"],
}));
vi.mock("../hooks/install.js", () => ({
installHooksFromNpmSpec: ((
...args: Parameters<(typeof import("../hooks/install.js"))["installHooksFromNpmSpec"]>
) =>
invokeMock<
Parameters<(typeof import("../hooks/install.js"))["installHooksFromNpmSpec"]>,
ReturnType<(typeof import("../hooks/install.js"))["installHooksFromNpmSpec"]>
>(
installHooksFromNpmSpec,
...args,
)) as (typeof import("../hooks/install.js"))["installHooksFromNpmSpec"],
installHooksFromPath: ((
...args: Parameters<(typeof import("../hooks/install.js"))["installHooksFromPath"]>
) =>
invokeMock<
Parameters<(typeof import("../hooks/install.js"))["installHooksFromPath"]>,
ReturnType<(typeof import("../hooks/install.js"))["installHooksFromPath"]>
>(
installHooksFromPath,
...args,
)) as (typeof import("../hooks/install.js"))["installHooksFromPath"],
resolveHookInstallDir: (hookId: string) => `/tmp/hooks/${hookId}`,
}));
vi.mock("../hooks/installs.js", () => ({
recordHookInstall: ((
...args: Parameters<(typeof import("../hooks/installs.js"))["recordHookInstall"]>
) =>
invokeMock<
Parameters<(typeof import("../hooks/installs.js"))["recordHookInstall"]>,
ReturnType<(typeof import("../hooks/installs.js"))["recordHookInstall"]>
>(recordHookInstall, ...args)) as (typeof import("../hooks/installs.js"))["recordHookInstall"],
}));
vi.mock("../plugins/clawhub.js", () => ({
CLAWHUB_INSTALL_ERROR_CODE: {
PACKAGE_NOT_FOUND: "package_not_found",
VERSION_NOT_FOUND: "version_not_found",
},
installPluginFromClawHub: ((
...args: Parameters<(typeof import("../plugins/clawhub.js"))["installPluginFromClawHub"]>
) =>
invokeMock<
Parameters<(typeof import("../plugins/clawhub.js"))["installPluginFromClawHub"]>,
ReturnType<(typeof import("../plugins/clawhub.js"))["installPluginFromClawHub"]>
>(
installPluginFromClawHub,
...args,
)) as (typeof import("../plugins/clawhub.js"))["installPluginFromClawHub"],
}));
vi.mock("../infra/clawhub.js", () => ({
parseClawHubPluginSpec: ((
...args: Parameters<(typeof import("../infra/clawhub.js"))["parseClawHubPluginSpec"]>
) =>
invokeMock<
Parameters<(typeof import("../infra/clawhub.js"))["parseClawHubPluginSpec"]>,
ReturnType<(typeof import("../infra/clawhub.js"))["parseClawHubPluginSpec"]>
>(
parseClawHubPluginSpec,
...args,
)) as (typeof import("../infra/clawhub.js"))["parseClawHubPluginSpec"],
}));
const { registerPluginsCli } = await import("./plugins-cli.js");
export { registerPluginsCli };
export async function runPluginsCommand(argv: string[]) {
const program = new Command();
program.exitOverride();
vi.resetModules();
const { registerPluginsCli: registerPluginsCliFresh } = await import("./plugins-cli.js");
registerPluginsCliFresh(program);
return await program.parseAsync(argv, { from: "user" });
}
export function resetPluginsCliTestState() {
resetRuntimeCapture();
restoreRuntimeCaptureMocks();
loadConfig.mockReset();
readConfigFileSnapshot.mockReset();
writeConfigFile.mockReset();
replaceConfigFile.mockReset();
resolveStateDir.mockReset();
installPluginFromMarketplace.mockReset();
listMarketplacePlugins.mockReset();
resolveMarketplaceInstallShortcut.mockReset();
enablePluginInConfig.mockReset();
recordPluginInstall.mockReset();
mockInstalledPluginIndexInstallRecords = {};
loadInstalledPluginIndexInstallRecords.mockReset();
writePersistedInstalledPluginIndexInstallRecords.mockReset();
clearPluginManifestRegistryCache.mockReset();
loadPluginManifestRegistry.mockReset();
buildPluginSnapshotReport.mockReset();
buildPluginRegistrySnapshotReport.mockReset();
buildPluginInspectReport.mockReset();
buildPluginDiagnosticsReport.mockReset();
buildPluginCompatibilityNotices.mockReset();
inspectPluginRegistry.mockReset();
refreshPluginRegistry.mockReset();
applyExclusiveSlotSelection.mockReset();
planPluginUninstall.mockReset();
applyPluginUninstallDirectoryRemoval.mockReset();
uninstallPlugin.mockReset();
updateNpmInstalledPlugins.mockReset();
updateNpmInstalledHookPacks.mockReset();
promptYesNo.mockReset();
installPluginFromNpmSpec.mockReset();
installPluginFromPath.mockReset();
installPluginFromClawHub.mockReset();
parseClawHubPluginSpec.mockReset();
installHooksFromNpmSpec.mockReset();
installHooksFromPath.mockReset();
recordHookInstall.mockReset();
loadConfig.mockReturnValue({} as OpenClawConfig);
readConfigFileSnapshot.mockImplementation(async () => {
const config = getRuntimeConfig();
return {
path: "/tmp/openclaw-config.json5",
exists: true,
raw: "{}",
parsed: config,
resolved: config,
sourceConfig: config,
runtimeConfig: config,
valid: true,
config,
hash: "mock",
issues: [],
warnings: [],
legacyIssues: [],
};
});
writeConfigFile.mockResolvedValue(undefined);
replaceConfigFile.mockImplementation(
(async (params: { nextConfig: OpenClawConfig }) =>
await writeConfigFile(params.nextConfig)) as (...args: unknown[]) => Promise<unknown>,
);
resolveStateDir.mockReturnValue("/tmp/openclaw-state");
resolveMarketplaceInstallShortcut.mockResolvedValue(null);
installPluginFromMarketplace.mockResolvedValue({
ok: false,
error: "marketplace install failed",
});
enablePluginInConfig.mockImplementation(((cfg: OpenClawConfig) => ({ config: cfg })) as (
...args: unknown[]
) => unknown);
recordPluginInstall.mockImplementation(
((cfg: OpenClawConfig) => cfg) as (...args: unknown[]) => unknown,
);
loadInstalledPluginIndexInstallRecords.mockImplementation(async () =>
clonePluginInstallRecords(mockInstalledPluginIndexInstallRecords),
);
writePersistedInstalledPluginIndexInstallRecords.mockImplementation(async (records: unknown) => {
mockInstalledPluginIndexInstallRecords = clonePluginInstallRecords(
(records ?? {}) as PluginInstallRecordMap,
);
});
loadPluginManifestRegistry.mockReturnValue({
plugins: [],
diagnostics: [],
});
const defaultPluginReport = {
plugins: [],
diagnostics: [],
};
buildPluginSnapshotReport.mockReturnValue(defaultPluginReport);
buildPluginRegistrySnapshotReport.mockReturnValue({
...defaultPluginReport,
registrySource: "derived",
registryDiagnostics: [],
});
buildPluginDiagnosticsReport.mockReturnValue(defaultPluginReport);
buildPluginCompatibilityNotices.mockReturnValue([]);
const defaultRegistryIndex = {
version: 1,
hostContractVersion: "2026.4.25",
compatRegistryVersion: "compat-v1",
migrationVersion: 1,
policyHash: "policy-v1",
generatedAtMs: 1777118400000,
plugins: [],
diagnostics: [],
};
inspectPluginRegistry.mockResolvedValue({
state: "fresh",
refreshReasons: [],
persisted: defaultRegistryIndex,
current: defaultRegistryIndex,
});
refreshPluginRegistry.mockResolvedValue(defaultRegistryIndex);
applyExclusiveSlotSelection.mockImplementation((({ config }: { config: OpenClawConfig }) => ({
config,
warnings: [],
})) as (...args: unknown[]) => unknown);
planPluginUninstall.mockImplementation((({
config,
pluginId,
}: {
config: OpenClawConfig;
pluginId: string;
}) => ({
ok: true,
config,
pluginId,
actions: createEmptyUninstallActions(),
directoryRemoval: null,
})) as (...args: unknown[]) => unknown);
applyPluginUninstallDirectoryRemoval.mockResolvedValue({
directoryRemoved: false,
warnings: [],
});
uninstallPlugin.mockResolvedValue({
ok: true,
config: {} as OpenClawConfig,
warnings: [],
actions: createEmptyUninstallActions(),
});
updateNpmInstalledPlugins.mockResolvedValue({
outcomes: [],
changed: false,
config: {} as OpenClawConfig,
});
updateNpmInstalledHookPacks.mockResolvedValue({
outcomes: [],
changed: false,
config: {} as OpenClawConfig,
});
promptYesNo.mockResolvedValue(true);
installPluginFromPath.mockResolvedValue({ ok: false, error: "path install disabled in test" });
installPluginFromNpmSpec.mockResolvedValue({
ok: false,
error: "npm install disabled in test",
});
installPluginFromClawHub.mockResolvedValue({
ok: false,
error: "clawhub install disabled in test",
});
parseClawHubPluginSpec.mockReturnValue(null);
installHooksFromPath.mockResolvedValue({
ok: false,
error: "hook path install disabled in test",
});
installHooksFromNpmSpec.mockResolvedValue({
ok: false,
error: "hook npm install disabled in test",
});
recordHookInstall.mockImplementation(
((cfg: OpenClawConfig) => cfg) as (...args: unknown[]) => unknown,
);
}