diff --git a/src/auto-reply/reply/dispatch-from-config.test.ts b/src/auto-reply/reply/dispatch-from-config.test.ts index c7dfe5d3a7d..3b3214e7b65 100644 --- a/src/auto-reply/reply/dispatch-from-config.test.ts +++ b/src/auto-reply/reply/dispatch-from-config.test.ts @@ -1,5 +1,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../config/config.js"; +import { createInternalHookEventPayload } from "../../test-utils/internal-hook-event-payload.js"; import type { MsgContext } from "../templating.js"; import type { GetReplyOptions, ReplyPayload } from "../types.js"; import type { ReplyDispatcher } from "./reply-dispatcher.js"; @@ -26,16 +27,7 @@ const hookMocks = vi.hoisted(() => ({ }, })); const internalHookMocks = vi.hoisted(() => ({ - createInternalHookEvent: vi.fn( - (type: string, action: string, sessionKey: string, context: Record) => ({ - type, - action, - sessionKey, - context, - timestamp: new Date(), - messages: [], - }), - ), + createInternalHookEvent: vi.fn(), triggerInternalHook: vi.fn(async () => {}), })); @@ -121,7 +113,8 @@ describe("dispatchReplyFromConfig", () => { hookMocks.runner.hasHooks.mockReset(); hookMocks.runner.hasHooks.mockReturnValue(false); hookMocks.runner.runMessageReceived.mockReset(); - internalHookMocks.createInternalHookEvent.mockClear(); + internalHookMocks.createInternalHookEvent.mockReset(); + internalHookMocks.createInternalHookEvent.mockImplementation(createInternalHookEventPayload); internalHookMocks.triggerInternalHook.mockClear(); }); it("does not route when Provider matches OriginatingChannel (even if Surface is missing)", async () => { diff --git a/src/hooks/install.test.ts b/src/hooks/install.test.ts index 1944b83f908..20e26a7fe8f 100644 --- a/src/hooks/install.test.ts +++ b/src/hooks/install.test.ts @@ -3,7 +3,10 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { afterAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { expectSingleNpmInstallIgnoreScriptsCall } from "../test-utils/exec-assertions.js"; +import { + expectSingleNpmInstallIgnoreScriptsCall, + expectSingleNpmPackIgnoreScriptsCall, +} from "../test-utils/exec-assertions.js"; import { isAddressInUseError } from "./gmail-watcher.js"; const fixtureRoot = path.join(os.tmpdir(), `openclaw-hook-install-${randomUUID()}`); @@ -273,18 +276,10 @@ describe("installHooksFromNpmSpec", () => { expect(result.hookPackId).toBe("test-hooks"); expect(fs.existsSync(path.join(result.targetDir, "hooks", "one-hook", "HOOK.md"))).toBe(true); - const packCalls = run.mock.calls.filter( - (c) => Array.isArray(c[0]) && c[0][0] === "npm" && c[0][1] === "pack", - ); - expect(packCalls.length).toBe(1); - const packCall = packCalls[0]; - if (!packCall) { - throw new Error("expected npm pack call"); - } - const [argv, options] = packCall; - expect(argv).toEqual(["npm", "pack", "@openclaw/test-hooks@0.0.1", "--ignore-scripts"]); - const commandOptions = typeof options === "number" ? undefined : options; - expect(commandOptions?.env).toMatchObject({ NPM_CONFIG_IGNORE_SCRIPTS: "true" }); + expectSingleNpmPackIgnoreScriptsCall({ + calls: run.mock.calls, + expectedSpec: "@openclaw/test-hooks@0.0.1", + }); expect(packTmpDir).not.toBe(""); expect(fs.existsSync(packTmpDir)).toBe(false); diff --git a/src/infra/outbound/deliver.test.ts b/src/infra/outbound/deliver.test.ts index cfe6b8a488e..d4d0f5827d3 100644 --- a/src/infra/outbound/deliver.test.ts +++ b/src/infra/outbound/deliver.test.ts @@ -9,6 +9,7 @@ import { setActivePluginRegistry } from "../../plugins/runtime.js"; import { markdownToSignalTextChunks } from "../../signal/format.js"; import { createOutboundTestPlugin, createTestRegistry } from "../../test-utils/channel-plugins.js"; import { createIMessageTestPlugin } from "../../test-utils/imessage-test-plugin.js"; +import { createInternalHookEventPayload } from "../../test-utils/internal-hook-event-payload.js"; const mocks = vi.hoisted(() => ({ appendAssistantMessageToSessionTranscript: vi.fn(async () => ({ ok: true, sessionFile: "x" })), @@ -20,16 +21,7 @@ const hookMocks = vi.hoisted(() => ({ }, })); const internalHookMocks = vi.hoisted(() => ({ - createInternalHookEvent: vi.fn( - (type: string, action: string, sessionKey: string, context: Record) => ({ - type, - action, - sessionKey, - context, - timestamp: new Date(), - messages: [], - }), - ), + createInternalHookEvent: vi.fn(), triggerInternalHook: vi.fn(async () => {}), })); const queueMocks = vi.hoisted(() => ({ @@ -93,7 +85,8 @@ describe("deliverOutboundPayloads", () => { hookMocks.runner.hasHooks.mockReturnValue(false); hookMocks.runner.runMessageSent.mockReset(); hookMocks.runner.runMessageSent.mockResolvedValue(undefined); - internalHookMocks.createInternalHookEvent.mockClear(); + internalHookMocks.createInternalHookEvent.mockReset(); + internalHookMocks.createInternalHookEvent.mockImplementation(createInternalHookEventPayload); internalHookMocks.triggerInternalHook.mockClear(); queueMocks.enqueueDelivery.mockReset(); queueMocks.enqueueDelivery.mockResolvedValue("mock-queue-id"); diff --git a/src/plugins/install.e2e.test.ts b/src/plugins/install.e2e.test.ts index e0b66221e91..cf870b79253 100644 --- a/src/plugins/install.e2e.test.ts +++ b/src/plugins/install.e2e.test.ts @@ -6,7 +6,10 @@ import JSZip from "jszip"; import * as tar from "tar"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import * as skillScanner from "../security/skill-scanner.js"; -import { expectSingleNpmInstallIgnoreScriptsCall } from "../test-utils/exec-assertions.js"; +import { + expectSingleNpmInstallIgnoreScriptsCall, + expectSingleNpmPackIgnoreScriptsCall, +} from "../test-utils/exec-assertions.js"; vi.mock("../process/exec.js", () => ({ runCommandWithTimeout: vi.fn(), @@ -506,18 +509,10 @@ describe("installPluginFromNpmSpec", () => { }); expect(result.ok).toBe(true); - const packCalls = run.mock.calls.filter( - (c) => Array.isArray(c[0]) && c[0][0] === "npm" && c[0][1] === "pack", - ); - expect(packCalls.length).toBe(1); - const packCall = packCalls[0]; - if (!packCall) { - throw new Error("expected npm pack call"); - } - const [argv, options] = packCall; - expect(argv).toEqual(["npm", "pack", "@openclaw/voice-call@0.0.1", "--ignore-scripts"]); - const commandOptions = typeof options === "number" ? undefined : options; - expect(commandOptions?.env).toMatchObject({ NPM_CONFIG_IGNORE_SCRIPTS: "true" }); + expectSingleNpmPackIgnoreScriptsCall({ + calls: run.mock.calls, + expectedSpec: "@openclaw/voice-call@0.0.1", + }); expect(packTmpDir).not.toBe(""); expect(fs.existsSync(packTmpDir)).toBe(false); diff --git a/src/test-utils/exec-assertions.ts b/src/test-utils/exec-assertions.ts index 7bcf17a66cd..f26166bc231 100644 --- a/src/test-utils/exec-assertions.ts +++ b/src/test-utils/exec-assertions.ts @@ -14,3 +14,21 @@ export function expectSingleNpmInstallIgnoreScriptsCall(params: { expect(argv).toEqual(["npm", "install", "--omit=dev", "--silent", "--ignore-scripts"]); expect(opts?.cwd).toBe(params.expectedCwd); } + +export function expectSingleNpmPackIgnoreScriptsCall(params: { + calls: Array<[unknown, unknown]>; + expectedSpec: string; +}) { + const packCalls = params.calls.filter( + (call) => Array.isArray(call[0]) && call[0][0] === "npm" && call[0][1] === "pack", + ); + expect(packCalls.length).toBe(1); + const packCall = packCalls[0]; + if (!packCall) { + throw new Error("expected npm pack call"); + } + const [argv, options] = packCall; + expect(argv).toEqual(["npm", "pack", params.expectedSpec, "--ignore-scripts"]); + const commandOptions = typeof options === "number" ? undefined : options; + expect(commandOptions).toMatchObject({ env: { NPM_CONFIG_IGNORE_SCRIPTS: "true" } }); +} diff --git a/src/test-utils/internal-hook-event-payload.ts b/src/test-utils/internal-hook-event-payload.ts new file mode 100644 index 00000000000..68fe0fbea83 --- /dev/null +++ b/src/test-utils/internal-hook-event-payload.ts @@ -0,0 +1,15 @@ +export function createInternalHookEventPayload( + type: string, + action: string, + sessionKey: string, + context: Record, +) { + return { + type, + action, + sessionKey, + context, + timestamp: new Date(), + messages: [], + }; +}