File size: 4,159 Bytes
63522a5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | import { PluginsClient } from "@openhands/typescript-client/clients";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
__resetActiveStoreForTests,
setActiveSelection,
setRegisteredBackends,
} from "#/api/backend-registry/active-store";
import type { Backend } from "#/api/backend-registry/types";
import PluginsManagementService from "#/api/plugins-management-service";
vi.mock("@openhands/typescript-client/clients", () => ({
PluginsClient: vi.fn(),
}));
const listInstalledPlugins = vi.fn();
const installPlugin = vi.fn();
const setPluginEnabled = vi.fn();
const uninstallPlugin = vi.fn();
const refreshPlugin = vi.fn();
const close = vi.fn();
function useBackend(kind: "local" | "cloud"): void {
const backend: Backend = {
id: kind,
name: kind,
host: "http://127.0.0.1:8001",
apiKey: "session-key",
kind,
};
setRegisteredBackends([backend]);
setActiveSelection({ backendId: kind, orgId: null });
}
beforeEach(() => {
vi.clearAllMocks();
__resetActiveStoreForTests();
vi.mocked(PluginsClient).mockImplementation(function MockPluginsClient() {
return {
listInstalledPlugins,
installPlugin,
setPluginEnabled,
uninstallPlugin,
refreshPlugin,
close,
} as unknown as PluginsClient;
} as unknown as typeof PluginsClient);
});
afterEach(() => {
__resetActiveStoreForTests();
});
const installedPlugin = {
name: "demo-plugin",
version: "1.0.0",
description: "A demo plugin",
enabled: true,
source: "github:OpenHands/extensions",
resolved_ref: null,
repo_path: "plugins/demo-plugin",
installed_at: "2026-06-01T00:00:00Z",
install_path: "/home/.openhands/plugins/installed/demo-plugin",
};
describe("PluginsManagementService", () => {
it("lists installed plugins from the local agent-server", async () => {
useBackend("local");
listInstalledPlugins.mockResolvedValue({ plugins: [installedPlugin] });
const result = await PluginsManagementService.listInstalledPlugins();
expect(result).toEqual([installedPlugin]);
expect(listInstalledPlugins).toHaveBeenCalledTimes(1);
});
it("returns an empty installed list on a cloud backend without calling the client", async () => {
useBackend("cloud");
const result = await PluginsManagementService.listInstalledPlugins();
expect(result).toEqual([]);
expect(PluginsClient).not.toHaveBeenCalled();
});
it("returns an empty installed list when the local request fails", async () => {
useBackend("local");
listInstalledPlugins.mockRejectedValue(new Error("unreachable"));
const result = await PluginsManagementService.listInstalledPlugins();
expect(result).toEqual([]);
});
it("forwards source, ref, and repo_path when installing a plugin", async () => {
useBackend("local");
installPlugin.mockResolvedValue(installedPlugin);
await PluginsManagementService.installPlugin({
source: "github:OpenHands/extensions",
ref: "main",
repo_path: "plugins/demo-plugin",
});
expect(installPlugin).toHaveBeenCalledWith({
source: "github:OpenHands/extensions",
ref: "main",
repo_path: "plugins/demo-plugin",
});
});
it("forwards the name and enabled flag when toggling a plugin", async () => {
useBackend("local");
setPluginEnabled.mockResolvedValue({ name: "demo-plugin", enabled: false });
await PluginsManagementService.setPluginEnabled("demo-plugin", false);
expect(setPluginEnabled).toHaveBeenCalledWith("demo-plugin", false);
});
it("forwards the plugin name when uninstalling", async () => {
useBackend("local");
uninstallPlugin.mockResolvedValue({ message: "ok" });
await PluginsManagementService.uninstallPlugin("demo-plugin");
expect(uninstallPlugin).toHaveBeenCalledWith("demo-plugin");
});
it("forwards the plugin name when refreshing", async () => {
useBackend("local");
refreshPlugin.mockResolvedValue({ message: "ok", plugin: installedPlugin });
await PluginsManagementService.refreshPlugin("demo-plugin");
expect(refreshPlugin).toHaveBeenCalledWith("demo-plugin");
});
});
|