


























@@ -0,0 +1,191 @@
1+import { beforeEach, describe, expect, it, vi } from "vitest";
2+3+const applyPluginAutoEnable = vi.hoisted(() =>
4+vi.fn((params: { config: unknown }) => ({
5+config: params.config,
6+changes: [],
7+autoEnabledReasons: {},
8+})),
9+);
10+const initSubagentRegistry = vi.hoisted(() => vi.fn());
11+const loadGatewayStartupPlugins = vi.hoisted(() =>
12+vi.fn((_params: unknown) => ({
13+pluginRegistry: { diagnostics: [], gatewayHandlers: {}, plugins: [] },
14+gatewayMethods: ["ping"],
15+})),
16+);
17+const repairBundledRuntimeDepsInstallRootAsync = vi.hoisted(() =>
18+vi.fn(async (_params: unknown) => ({})),
19+);
20+const resolveBundledRuntimeDependencyPackageInstallRoot = vi.hoisted(() =>
21+vi.fn((_packageRoot: string, _params: unknown) => "/runtime"),
22+);
23+const resolveConfiguredDeferredChannelPluginIds = vi.hoisted(() => vi.fn((_params: unknown) => []));
24+const resolveGatewayStartupPluginIds = vi.hoisted(() => vi.fn((_params: unknown) => ["telegram"]));
25+const resolveOpenClawPackageRootSync = vi.hoisted(() => vi.fn((_params: unknown) => "/package"));
26+const runChannelPluginStartupMaintenance = vi.hoisted(() =>
27+vi.fn(async (_params: unknown) => undefined),
28+);
29+const runStartupSessionMigration = vi.hoisted(() => vi.fn(async (_params: unknown) => undefined));
30+const scanBundledPluginRuntimeDeps = vi.hoisted(() =>
31+vi.fn((_params: unknown) => ({
32+deps: [{ name: "grammy", version: "1.37.0", pluginIds: ["telegram"] }],
33+missing: [{ name: "grammy", version: "1.37.0", pluginIds: ["telegram"] }],
34+conflicts: [],
35+})),
36+);
37+38+vi.mock("../agents/agent-scope.js", () => ({
39+resolveAgentWorkspaceDir: () => "/workspace",
40+resolveDefaultAgentId: () => "default",
41+}));
42+43+vi.mock("../agents/subagent-registry.js", () => ({
44+initSubagentRegistry: () => initSubagentRegistry(),
45+}));
46+47+vi.mock("../channels/plugins/lifecycle-startup.js", () => ({
48+runChannelPluginStartupMaintenance: (params: unknown) =>
49+runChannelPluginStartupMaintenance(params),
50+}));
51+52+vi.mock("../config/plugin-auto-enable.js", () => ({
53+applyPluginAutoEnable: (params: { config: unknown }) => applyPluginAutoEnable(params),
54+}));
55+56+vi.mock("../infra/openclaw-root.js", () => ({
57+resolveOpenClawPackageRootSync: (params: unknown) => resolveOpenClawPackageRootSync(params),
58+}));
59+60+vi.mock("../plugins/bundled-runtime-deps.js", () => ({
61+repairBundledRuntimeDepsInstallRootAsync: (params: unknown) =>
62+repairBundledRuntimeDepsInstallRootAsync(params),
63+resolveBundledRuntimeDependencyPackageInstallRoot: (packageRoot: string, params: unknown) =>
64+resolveBundledRuntimeDependencyPackageInstallRoot(packageRoot, params),
65+scanBundledPluginRuntimeDeps: (params: unknown) => scanBundledPluginRuntimeDeps(params),
66+}));
67+68+vi.mock("../plugins/channel-plugin-ids.js", () => ({
69+resolveConfiguredDeferredChannelPluginIds: (params: unknown) =>
70+resolveConfiguredDeferredChannelPluginIds(params),
71+resolveGatewayStartupPluginIds: (params: unknown) => resolveGatewayStartupPluginIds(params),
72+}));
73+74+vi.mock("../plugins/registry.js", () => ({
75+createEmptyPluginRegistry: () => ({ diagnostics: [], gatewayHandlers: {}, plugins: [] }),
76+}));
77+78+vi.mock("../plugins/runtime.js", () => ({
79+getActivePluginRegistry: () => undefined,
80+setActivePluginRegistry: vi.fn(),
81+}));
82+83+vi.mock("./server-methods-list.js", () => ({
84+listGatewayMethods: () => ["ping"],
85+}));
86+87+vi.mock("./server-methods.js", () => ({
88+coreGatewayHandlers: {},
89+}));
90+91+vi.mock("./server-plugin-bootstrap.js", () => ({
92+loadGatewayStartupPlugins: (params: unknown) => loadGatewayStartupPlugins(params),
93+}));
94+95+vi.mock("./server-startup-session-migration.js", () => ({
96+runStartupSessionMigration: (params: unknown) => runStartupSessionMigration(params),
97+}));
98+99+function createLog() {
100+return {
101+info: vi.fn(),
102+warn: vi.fn(),
103+error: vi.fn(),
104+debug: vi.fn(),
105+};
106+}
107+108+describe("prepareGatewayPluginBootstrap runtime-deps staging", () => {
109+beforeEach(() => {
110+applyPluginAutoEnable.mockClear();
111+initSubagentRegistry.mockClear();
112+loadGatewayStartupPlugins.mockClear();
113+repairBundledRuntimeDepsInstallRootAsync.mockReset().mockResolvedValue({});
114+resolveBundledRuntimeDependencyPackageInstallRoot.mockClear();
115+resolveConfiguredDeferredChannelPluginIds.mockClear();
116+resolveGatewayStartupPluginIds.mockClear().mockReturnValue(["telegram"]);
117+resolveOpenClawPackageRootSync.mockClear().mockReturnValue("/package");
118+runChannelPluginStartupMaintenance.mockClear();
119+runStartupSessionMigration.mockClear();
120+scanBundledPluginRuntimeDeps.mockClear().mockReturnValue({
121+deps: [{ name: "grammy", version: "1.37.0", pluginIds: ["telegram"] }],
122+missing: [{ name: "grammy", version: "1.37.0", pluginIds: ["telegram"] }],
123+conflicts: [],
124+});
125+});
126+127+it("falls back to per-plugin runtime-deps installs after failed pre-start staging", async () => {
128+const installError = new Error("offline registry");
129+repairBundledRuntimeDepsInstallRootAsync.mockRejectedValueOnce(installError);
130+const log = createLog();
131+const { prepareGatewayPluginBootstrap } = await import("./server-startup-plugins.js");
132+133+await expect(
134+prepareGatewayPluginBootstrap({
135+cfgAtStart: {},
136+startupRuntimeConfig: {},
137+minimalTestGateway: false,
138+ log,
139+}),
140+).resolves.toMatchObject({
141+baseGatewayMethods: ["ping"],
142+startupPluginIds: ["telegram"],
143+});
144+145+expect(loadGatewayStartupPlugins).toHaveBeenCalledOnce();
146+expect(scanBundledPluginRuntimeDeps).toHaveBeenCalledWith(
147+expect.objectContaining({
148+selectedPluginIds: ["telegram"],
149+}),
150+);
151+expect(log.warn).toHaveBeenCalledWith(
152+expect.stringContaining(
153+"gateway startup will continue with per-plugin runtime-deps installs",
154+),
155+);
156+expect(loadGatewayStartupPlugins.mock.calls[0]?.[0]).not.toHaveProperty(
157+"bundledRuntimeDepsInstaller",
158+);
159+});
160+161+it("falls back to per-plugin runtime-deps installs after failed pre-start scan", async () => {
162+scanBundledPluginRuntimeDeps.mockImplementationOnce(() => {
163+throw new Error("unsupported runtime dependency spec");
164+});
165+const log = createLog();
166+const { prepareGatewayPluginBootstrap } = await import("./server-startup-plugins.js");
167+168+await expect(
169+prepareGatewayPluginBootstrap({
170+cfgAtStart: {},
171+startupRuntimeConfig: {},
172+minimalTestGateway: false,
173+ log,
174+}),
175+).resolves.toMatchObject({
176+baseGatewayMethods: ["ping"],
177+startupPluginIds: ["telegram"],
178+});
179+180+expect(repairBundledRuntimeDepsInstallRootAsync).not.toHaveBeenCalled();
181+expect(loadGatewayStartupPlugins).toHaveBeenCalledOnce();
182+expect(log.warn).toHaveBeenCalledWith(
183+expect.stringContaining(
184+"failed to scan bundled runtime deps before gateway startup; gateway startup will continue with per-plugin runtime-deps installs",
185+),
186+);
187+expect(loadGatewayStartupPlugins.mock.calls[0]?.[0]).not.toHaveProperty(
188+"bundledRuntimeDepsInstaller",
189+);
190+});
191+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。