






















@@ -65,6 +65,35 @@ function buildPluginsParams(commandBodyNormalized: string, workspaceDir: string)
6565});
6666}
676768+function mockCall(mock: unknown, index = 0): Array<unknown> {
69+const calls = (mock as { mock?: { calls?: Array<Array<unknown>> } }).mock?.calls ?? [];
70+const call = calls.at(index);
71+expect(call, `mock call ${index + 1}`).toBeDefined();
72+return call as Array<unknown>;
73+}
74+75+function mockFirstObjectArg(mock: unknown): Record<string, unknown> {
76+const [arg] = mockCall(mock);
77+expect(arg).toBeTypeOf("object");
78+expect(arg).not.toBeNull();
79+return arg as Record<string, unknown>;
80+}
81+82+function expectObjectFields(value: unknown, expected: Record<string, unknown>): void {
83+expect(value).toBeTypeOf("object");
84+expect(value).not.toBeNull();
85+const record = value as Record<string, unknown>;
86+for (const [key, expectedValue] of Object.entries(expected)) {
87+expect(record[key], key).toEqual(expectedValue);
88+}
89+}
90+91+function expectPersistedInstall(pluginId: string, expectedInstall: Record<string, unknown>): void {
92+const persisted = mockFirstObjectArg(persistPluginInstallMock);
93+expect(persisted.pluginId).toBe(pluginId);
94+expectObjectFields(persisted.install, expectedInstall);
95+}
96+6897describe("handleCommands /plugins install", () => {
6998afterEach(async () => {
7099installPluginFromNpmSpecMock.mockReset();
@@ -96,22 +125,13 @@ describe("handleCommands /plugins install", () => {
96125throw new Error("expected plugin install result");
97126}
98127expect(result.reply?.text).toContain('Installed plugin "path-install-plugin"');
99-expect(installPluginFromPathMock).toHaveBeenCalledWith(
100-expect.objectContaining({
101-path: pluginDir,
102-}),
103-);
104-expect(persistPluginInstallMock).toHaveBeenCalledWith(
105-expect.objectContaining({
106-pluginId: "path-install-plugin",
107-install: expect.objectContaining({
108-source: "path",
109-sourcePath: pluginDir,
110-installPath: "/tmp/path-install-plugin",
111-version: "0.0.1",
112-}),
113-}),
114-);
128+expect(mockFirstObjectArg(installPluginFromPathMock).path).toBe(pluginDir);
129+expectPersistedInstall("path-install-plugin", {
130+source: "path",
131+sourcePath: pluginDir,
132+installPath: "/tmp/path-install-plugin",
133+version: "0.0.1",
134+});
115135});
116136});
117137@@ -147,25 +167,18 @@ describe("handleCommands /plugins install", () => {
147167throw new Error("expected plugin install result");
148168}
149169expect(result.reply?.text).toContain('Installed plugin "clawhub-demo"');
150-expect(installPluginFromClawHubMock).toHaveBeenCalledWith(
151-expect.objectContaining({
152-spec: "clawhub:@openclaw/clawhub-demo@1.2.3",
153-}),
154-);
155-expect(persistPluginInstallMock).toHaveBeenCalledWith(
156-expect.objectContaining({
157-pluginId: "clawhub-demo",
158-install: expect.objectContaining({
159-source: "clawhub",
160-spec: "clawhub:@openclaw/clawhub-demo@1.2.3",
161-installPath: "/tmp/clawhub-demo",
162-version: "1.2.3",
163-integrity: "sha512-demo",
164-clawhubPackage: "@openclaw/clawhub-demo",
165-clawhubChannel: "official",
166-}),
167-}),
170+expect(mockFirstObjectArg(installPluginFromClawHubMock).spec).toBe(
171+"clawhub:@openclaw/clawhub-demo@1.2.3",
168172);
173+expectPersistedInstall("clawhub-demo", {
174+source: "clawhub",
175+spec: "clawhub:@openclaw/clawhub-demo@1.2.3",
176+installPath: "/tmp/clawhub-demo",
177+version: "1.2.3",
178+integrity: "sha512-demo",
179+clawhubPackage: "@openclaw/clawhub-demo",
180+clawhubChannel: "official",
181+});
169182});
170183});
171184@@ -225,25 +238,18 @@ describe("handleCommands /plugins install", () => {
225238throw new Error("expected plugin install result");
226239}
227240expect(result.reply?.text).toContain('Installed plugin "git-demo"');
228-expect(installPluginFromGitSpecMock).toHaveBeenCalledWith(
229-expect.objectContaining({
230-spec: "git:github.com/acme/git-demo@v1.2.3",
231-}),
232-);
233-expect(persistPluginInstallMock).toHaveBeenCalledWith(
234-expect.objectContaining({
235-pluginId: "git-demo",
236-install: expect.objectContaining({
237-source: "git",
238-spec: "git:github.com/acme/git-demo@v1.2.3",
239-installPath: "/tmp/git-demo",
240-version: "1.2.3",
241-gitUrl: "https://github.com/acme/git-demo.git",
242-gitRef: "v1.2.3",
243-gitCommit: "abc123",
244-}),
245-}),
241+expect(mockFirstObjectArg(installPluginFromGitSpecMock).spec).toBe(
242+"git:github.com/acme/git-demo@v1.2.3",
246243);
244+expectPersistedInstall("git-demo", {
245+source: "git",
246+spec: "git:github.com/acme/git-demo@v1.2.3",
247+installPath: "/tmp/git-demo",
248+version: "1.2.3",
249+gitUrl: "https://github.com/acme/git-demo.git",
250+gitRef: "v1.2.3",
251+gitCommit: "abc123",
252+});
247253});
248254});
249255@@ -279,10 +285,8 @@ describe("handleCommands /plugins install", () => {
279285throw new Error("expected plugin install result");
280286}
281287expect(result.reply?.text).toContain('Installed plugin "alias-demo"');
282-expect(installPluginFromClawHubMock).toHaveBeenCalledWith(
283-expect.objectContaining({
284-spec: "clawhub:@openclaw/alias-demo@1.0.0",
285-}),
288+expect(mockFirstObjectArg(installPluginFromClawHubMock).spec).toBe(
289+"clawhub:@openclaw/alias-demo@1.0.0",
286290);
287291});
288292});
@@ -315,31 +319,21 @@ describe("handleCommands /plugins install", () => {
315319throw new Error("expected plugin install result");
316320}
317321expect(result.reply?.text).toContain('Installed plugin "wecom-openclaw-plugin"');
318-expect(installPluginFromNpmSpecMock).toHaveBeenCalledWith(
319-expect.objectContaining({
320-spec: "@wecom/wecom-openclaw-plugin@latest",
321-expectedPluginId: "wecom-openclaw-plugin",
322-trustedSourceLinkedOfficialInstall: true,
323-}),
324-);
325-expect(installPluginFromNpmSpecMock).toHaveBeenCalledWith(
326-expect.not.objectContaining({
327-expectedIntegrity: expect.any(String),
328-}),
329-);
330-expect(persistPluginInstallMock).toHaveBeenCalledWith(
331-expect.objectContaining({
332-pluginId: "wecom-openclaw-plugin",
333-install: expect.objectContaining({
334-source: "npm",
335-spec: "@wecom/wecom-openclaw-plugin@latest",
336-installPath: "/tmp/wecom-openclaw-plugin",
337-version: "2026.4.23",
338-resolvedName: "@wecom/wecom-openclaw-plugin",
339-resolvedVersion: "2026.4.23",
340-}),
341-}),
342-);
322+const npmInstallArgs = mockFirstObjectArg(installPluginFromNpmSpecMock);
323+expectObjectFields(npmInstallArgs, {
324+spec: "@wecom/wecom-openclaw-plugin@latest",
325+expectedPluginId: "wecom-openclaw-plugin",
326+trustedSourceLinkedOfficialInstall: true,
327+});
328+expect(npmInstallArgs.expectedIntegrity).toBeUndefined();
329+expectPersistedInstall("wecom-openclaw-plugin", {
330+source: "npm",
331+spec: "@wecom/wecom-openclaw-plugin@latest",
332+installPath: "/tmp/wecom-openclaw-plugin",
333+version: "2026.4.23",
334+resolvedName: "@wecom/wecom-openclaw-plugin",
335+resolvedVersion: "2026.4.23",
336+});
343337});
344338});
345339});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。