import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; import { withTempHome } from "../../test/helpers/temp-home.js"; import { detectPluginInstallPathIssue, formatPluginInstallPathIssue, } from "./plugin-install-path-warnings.js"; async function detectMatrixCustomPathIssue(sourcePath: string | ((pluginPath: string) => string)) { return withTempHome(async (home) => { const pluginPath = path.join(home, "matrix-plugin"); await fs.mkdir(pluginPath, { recursive: true }); const resolvedSourcePath = typeof sourcePath === "function" ? sourcePath(pluginPath) : sourcePath; const issue = await detectPluginInstallPathIssue({ pluginId: "matrix", install: { source: "path", sourcePath: resolvedSourcePath, installPath: pluginPath, }, }); return { issue, pluginPath }; }); } describe("plugin install path warnings", () => { it("ignores non-path installs and blank path candidates", async () => { expect( await detectPluginInstallPathIssue({ pluginId: "matrix", install: null, }), ).toBeNull(); expect( await detectPluginInstallPathIssue({ pluginId: "matrix", install: { source: "npm", sourcePath: " ", installPath: " ", }, }), ).toBeNull(); }); it("detects stale custom plugin install paths", async () => { const issue = await detectPluginInstallPathIssue({ pluginId: "matrix", install: { source: "path", sourcePath: "/tmp/openclaw-matrix-missing", installPath: "/tmp/openclaw-matrix-missing", }, }); expect(issue).toEqual({ kind: "missing-path", pluginId: "matrix", path: "/tmp/openclaw-matrix-missing", }); expect( formatPluginInstallPathIssue({ issue: issue!, pluginLabel: "Matrix", defaultInstallCommand: "openclaw plugins install @openclaw/matrix", repoInstallCommand: "openclaw plugins install ./extensions/matrix", }), ).toEqual([ "Matrix is installed from a custom path that no longer exists: /tmp/openclaw-matrix-missing", 'Reinstall with "openclaw plugins install @openclaw/matrix".', 'If you are running from a repo checkout, you can also use "openclaw plugins install ./extensions/matrix".', ]); }); it("uses the second candidate path when the first one is stale", async () => { const { issue, pluginPath } = await detectMatrixCustomPathIssue("/tmp/openclaw-matrix-missing"); expect(issue).toEqual({ kind: "custom-path", pluginId: "matrix", path: pluginPath, }); }); it("detects active custom plugin install paths", async () => { const { issue, pluginPath } = await detectMatrixCustomPathIssue( (resolvedPluginPath) => resolvedPluginPath, ); expect(issue).toEqual({ kind: "custom-path", pluginId: "matrix", path: pluginPath, }); }); it("applies custom command formatting in warning messages", () => { expect( formatPluginInstallPathIssue({ issue: { kind: "custom-path", pluginId: "matrix", path: "/tmp/matrix-plugin", }, pluginLabel: "Matrix", defaultInstallCommand: "openclaw plugins install @openclaw/matrix", repoInstallCommand: "openclaw plugins install ./extensions/matrix", formatCommand: (command) => `<${command}>`, }), ).toEqual([ "Matrix is installed from a custom path: /tmp/matrix-plugin", "Main updates will not automatically replace that plugin with the repo's default Matrix package.", 'Reinstall with "" when you want to return to the standard Matrix plugin.', 'If you are intentionally running from a repo checkout, reinstall that checkout explicitly with "" after updates.', ]); }); });