From cf6cdc74d0882fd8eb1b6056df5b58c0f0467736 Mon Sep 17 00:00:00 2001 From: cpojer Date: Tue, 17 Feb 2026 12:23:01 +0900 Subject: [PATCH] chore: Fix types in tests 23/N. --- extensions/voice-call/src/webhook.test.ts | 4 +- ...ded-subscribe.handlers.tools.media.test.ts | 2 +- src/line/bot-handlers.test.ts | 10 ++- src/line/webhook-node.test.ts | 14 +-- src/media-understanding/resolve.test.ts | 3 +- src/media/store.redirect.test.ts | 17 +++- .../wired-hooks-after-tool-call.e2e.test.ts | 20 ++++- ...ends-tool-summaries-responseprefix.test.ts | 90 ++++++++----------- src/test-utils/channel-plugins.ts | 10 ++- src/tts/tts.test.ts | 65 +++++++++----- src/web/login-qr.test.ts | 9 +- src/web/login.coverage.test.ts | 25 +++--- src/web/session.test.ts | 13 +-- 13 files changed, 166 insertions(+), 116 deletions(-) diff --git a/extensions/voice-call/src/webhook.test.ts b/extensions/voice-call/src/webhook.test.ts index e925872c16..2b4b611c3f 100644 --- a/extensions/voice-call/src/webhook.test.ts +++ b/extensions/voice-call/src/webhook.test.ts @@ -1,15 +1,15 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { VoiceCallConfigSchema, type VoiceCallConfig } from "./config.js"; import type { CallManager } from "./manager.js"; import type { VoiceCallProvider } from "./providers/base.js"; import type { CallRecord } from "./types.js"; -import { VoiceCallConfigSchema, type VoiceCallConfig } from "./config.js"; import { VoiceCallWebhookServer } from "./webhook.js"; const provider: VoiceCallProvider = { name: "mock", verifyWebhook: () => ({ ok: true }), parseWebhookEvent: () => ({ events: [] }), - initiateCall: async () => ({ providerCallId: "provider-call" }), + initiateCall: async () => ({ providerCallId: "provider-call", status: "initiated" }), hangupCall: async () => {}, playTts: async () => {}, startListening: async () => {}, diff --git a/src/agents/pi-embedded-subscribe.handlers.tools.media.test.ts b/src/agents/pi-embedded-subscribe.handlers.tools.media.test.ts index 0677a525fc..5d0a91b4fa 100644 --- a/src/agents/pi-embedded-subscribe.handlers.tools.media.test.ts +++ b/src/agents/pi-embedded-subscribe.handlers.tools.media.test.ts @@ -1,9 +1,9 @@ import { describe, expect, it, vi } from "vitest"; -import type { EmbeddedPiSubscribeContext } from "./pi-embedded-subscribe.handlers.types.js"; import { handleToolExecutionEnd, handleToolExecutionStart, } from "./pi-embedded-subscribe.handlers.tools.js"; +import type { EmbeddedPiSubscribeContext } from "./pi-embedded-subscribe.handlers.types.js"; // Minimal mock context factory. Only the fields needed for the media emission path. function createMockContext(overrides?: { diff --git a/src/line/bot-handlers.test.ts b/src/line/bot-handlers.test.ts index 369c181b56..32eaab80a6 100644 --- a/src/line/bot-handlers.test.ts +++ b/src/line/bot-handlers.test.ts @@ -65,6 +65,8 @@ const { readAllowFromStoreMock, upsertPairingRequestMock } = vi.hoisted(() => ({ let handleLineWebhookEvents: typeof import("./bot-handlers.js").handleLineWebhookEvents; +const createRuntime = () => ({ log: vi.fn(), error: vi.fn(), exit: vi.fn() }); + vi.mock("../pairing/pairing-store.js", () => ({ readChannelAllowFromStore: readAllowFromStoreMock, upsertChannelPairingRequest: upsertPairingRequestMock, @@ -105,7 +107,7 @@ describe("handleLineWebhookEvents", () => { tokenSource: "config", config: { groupPolicy: "disabled" }, }, - runtime: { error: vi.fn() }, + runtime: createRuntime(), mediaMaxBytes: 1, processMessage, }); @@ -137,7 +139,7 @@ describe("handleLineWebhookEvents", () => { tokenSource: "config", config: { groupPolicy: "allowlist" }, }, - runtime: { error: vi.fn() }, + runtime: createRuntime(), mediaMaxBytes: 1, processMessage, }); @@ -171,7 +173,7 @@ describe("handleLineWebhookEvents", () => { tokenSource: "config", config: { groupPolicy: "allowlist", groupAllowFrom: ["user-3"] }, }, - runtime: { error: vi.fn() }, + runtime: createRuntime(), mediaMaxBytes: 1, processMessage, }); @@ -203,7 +205,7 @@ describe("handleLineWebhookEvents", () => { tokenSource: "config", config: { groupPolicy: "open", groups: { "*": { enabled: false } } }, }, - runtime: { error: vi.fn() }, + runtime: createRuntime(), mediaMaxBytes: 1, processMessage, }); diff --git a/src/line/webhook-node.test.ts b/src/line/webhook-node.test.ts index 3db5746089..27b489ae67 100644 --- a/src/line/webhook-node.test.ts +++ b/src/line/webhook-node.test.ts @@ -8,24 +8,26 @@ const sign = (body: string, secret: string) => function createRes() { const headers: Record = {}; - const res = { + const resObj = { statusCode: 0, headersSent: false, setHeader: (k: string, v: string) => { headers[k.toLowerCase()] = v; }, end: vi.fn((data?: unknown) => { - res.headersSent = true; + resObj.headersSent = true; // Keep payload available for assertions - (res as { body?: unknown }).body = data; + resObj.body = data; }), - } as unknown as ServerResponse & { body?: unknown }; + body: undefined as unknown, + }; + const res = resObj as unknown as ServerResponse & { body?: unknown }; return { res, headers }; } function createPostWebhookTestHarness(rawBody: string, secret = "secret") { const bot = { handleWebhook: vi.fn(async () => {}) }; - const runtime = { error: vi.fn() }; + const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn() }; const handler = createLineNodeWebhookHandler({ channelSecret: secret, bot, @@ -38,7 +40,7 @@ function createPostWebhookTestHarness(rawBody: string, secret = "secret") { describe("createLineNodeWebhookHandler", () => { it("returns 200 for GET", async () => { const bot = { handleWebhook: vi.fn(async () => {}) }; - const runtime = { error: vi.fn() }; + const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn() }; const handler = createLineNodeWebhookHandler({ channelSecret: "secret", bot, diff --git a/src/media-understanding/resolve.test.ts b/src/media-understanding/resolve.test.ts index 9898794b40..3f7b21c52c 100644 --- a/src/media-understanding/resolve.test.ts +++ b/src/media-understanding/resolve.test.ts @@ -1,8 +1,9 @@ import { describe, expect, it } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; import { resolveEntriesWithActiveFallback, resolveModelEntries } from "./resolve.js"; +import type { MediaUnderstandingCapability } from "./types.js"; -const providerRegistry = new Map([ +const providerRegistry = new Map([ ["openai", { capabilities: ["image"] }], ["groq", { capabilities: ["audio"] }], ]); diff --git a/src/media/store.redirect.test.ts b/src/media/store.redirect.test.ts index f82a9f516a..e236c4f903 100644 --- a/src/media/store.redirect.test.ts +++ b/src/media/store.redirect.test.ts @@ -4,6 +4,7 @@ import path from "node:path"; import { PassThrough } from "node:stream"; import JSZip from "jszip"; import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { createPinnedLookup } from "../infra/net/ssrf.js"; import { captureEnv } from "../test-utils/env.js"; import { saveMediaSource, setMediaStoreNetworkDepsForTest } from "./store.js"; @@ -24,8 +25,10 @@ describe("media store redirects", () => { setMediaStoreNetworkDepsForTest({ httpRequest: (...args) => mockRequest(...args), httpsRequest: (...args) => mockRequest(...args), - resolvePinnedHostname: async () => ({ - lookup: async () => [{ address: "93.184.216.34", family: 4 }], + resolvePinnedHostname: async (hostname) => ({ + hostname, + addresses: ["93.184.216.34"], + lookup: createPinnedLookup({ hostname, addresses: ["93.184.216.34"] }), }), }); }); @@ -41,7 +44,10 @@ describe("media store redirects", () => { let call = 0; mockRequest.mockImplementation((_url, _opts, cb) => { call += 1; - const res = new PassThrough(); + const res = Object.assign(new PassThrough(), { + statusCode: 0, + headers: {} as Record, + }); const req = { on: (event: string, handler: (...args: unknown[]) => void) => { if (event === "error") { @@ -83,7 +89,10 @@ describe("media store redirects", () => { it("sniffs xlsx from zip content when headers and url extension are missing", async () => { mockRequest.mockImplementationOnce((_url, _opts, cb) => { - const res = new PassThrough(); + const res = Object.assign(new PassThrough(), { + statusCode: 0, + headers: {} as Record, + }); const req = { on: (event: string, handler: (...args: unknown[]) => void) => { if (event === "error") { diff --git a/src/plugins/wired-hooks-after-tool-call.e2e.test.ts b/src/plugins/wired-hooks-after-tool-call.e2e.test.ts index 47c6a8c262..d0c74e7f4c 100644 --- a/src/plugins/wired-hooks-after-tool-call.e2e.test.ts +++ b/src/plugins/wired-hooks-after-tool-call.e2e.test.ts @@ -104,7 +104,17 @@ describe("after_tool_call hook wiring", () => { expect(hookMocks.runner.runAfterToolCall).toHaveBeenCalledTimes(1); expect(hookMocks.runner.runBeforeToolCall).not.toHaveBeenCalled(); - const [event, context] = hookMocks.runner.runAfterToolCall.mock.calls[0]; + const firstCall = (hookMocks.runner.runAfterToolCall as ReturnType).mock.calls[0]; + expect(firstCall).toBeDefined(); + const event = firstCall?.[0] as + | { toolName?: string; params?: unknown; error?: unknown; durationMs?: unknown } + | undefined; + const context = firstCall?.[1] as { toolName?: string } | undefined; + expect(event).toBeDefined(); + expect(context).toBeDefined(); + if (!event || !context) { + throw new Error("missing hook call payload"); + } expect(event.toolName).toBe("read"); expect(event.params).toEqual({ path: "/tmp/file.txt" }); expect(event.error).toBeUndefined(); @@ -143,7 +153,13 @@ describe("after_tool_call hook wiring", () => { expect(hookMocks.runner.runAfterToolCall).toHaveBeenCalledTimes(1); - const [event] = hookMocks.runner.runAfterToolCall.mock.calls[0]; + const firstCall = (hookMocks.runner.runAfterToolCall as ReturnType).mock.calls[0]; + expect(firstCall).toBeDefined(); + const event = firstCall?.[0] as { error?: unknown } | undefined; + expect(event).toBeDefined(); + if (!event) { + throw new Error("missing hook call payload"); + } expect(event.error).toBeDefined(); }); diff --git a/src/signal/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts b/src/signal/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts index a52c956adf..f63a418634 100644 --- a/src/signal/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts +++ b/src/signal/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts @@ -38,18 +38,26 @@ function createMonitorRuntime() { } function setSignalAutoStartConfig(overrides: Record = {}) { - setSignalToolResultTestConfig({ - ...config, + setSignalToolResultTestConfig(createSignalConfig(overrides)); +} + +function createSignalConfig(overrides: Record = {}): Record { + const base = config as OpenClawConfig; + const channels = (base.channels ?? {}) as Record; + const signal = (channels.signal ?? {}) as Record; + return { + ...base, channels: { - ...config.channels, + ...channels, signal: { + ...signal, autoStart: true, dmPolicy: "open", allowFrom: ["*"], ...overrides, }, }, - }); + }; } function createAutoAbortController() { @@ -193,18 +201,9 @@ describe("monitorSignalProvider tool results", () => { }); it("replies with pairing code when dmPolicy is pairing and no allowFrom is set", async () => { - setSignalToolResultTestConfig({ - ...config, - channels: { - ...config.channels, - signal: { - ...config.channels?.signal, - autoStart: false, - dmPolicy: "pairing", - allowFrom: [], - }, - }, - }); + setSignalToolResultTestConfig( + createSignalConfig({ autoStart: false, dmPolicy: "pairing", allowFrom: [] }), + ); await receiveSignalPayloads({ payloads: [ { @@ -277,19 +276,14 @@ describe("monitorSignalProvider tool results", () => { }); it("enqueues system events for reaction notifications", async () => { - setSignalToolResultTestConfig({ - ...config, - channels: { - ...config.channels, - signal: { - ...config.channels?.signal, - autoStart: false, - dmPolicy: "open", - allowFrom: ["*"], - reactionNotifications: "all", - }, - }, - }); + setSignalToolResultTestConfig( + createSignalConfig({ + autoStart: false, + dmPolicy: "open", + allowFrom: ["*"], + reactionNotifications: "all", + }), + ); await receiveSignalPayloads({ payloads: [ { @@ -312,20 +306,15 @@ describe("monitorSignalProvider tool results", () => { }); it("notifies on own reactions when target includes uuid + phone", async () => { - setSignalToolResultTestConfig({ - ...config, - channels: { - ...config.channels, - signal: { - ...config.channels?.signal, - autoStart: false, - dmPolicy: "open", - allowFrom: ["*"], - account: "+15550002222", - reactionNotifications: "own", - }, - }, - }); + setSignalToolResultTestConfig( + createSignalConfig({ + autoStart: false, + dmPolicy: "open", + allowFrom: ["*"], + account: "+15550002222", + reactionNotifications: "own", + }), + ); await receiveSignalPayloads({ payloads: [ { @@ -376,18 +365,9 @@ describe("monitorSignalProvider tool results", () => { }); it("does not resend pairing code when a request is already pending", async () => { - setSignalToolResultTestConfig({ - ...config, - channels: { - ...config.channels, - signal: { - ...config.channels?.signal, - autoStart: false, - dmPolicy: "pairing", - allowFrom: [], - }, - }, - }); + setSignalToolResultTestConfig( + createSignalConfig({ autoStart: false, dmPolicy: "pairing", allowFrom: [] }), + ); upsertPairingRequestMock .mockResolvedValueOnce({ code: "PAIRCODE", created: true }) .mockResolvedValueOnce({ code: "PAIRCODE", created: false }); diff --git a/src/test-utils/channel-plugins.ts b/src/test-utils/channel-plugins.ts index f0ba062c94..e6246e3214 100644 --- a/src/test-utils/channel-plugins.ts +++ b/src/test-utils/channel-plugins.ts @@ -6,12 +6,18 @@ import type { } from "../channels/plugins/types.js"; import type { PluginRegistry } from "../plugins/registry.js"; -export const createTestRegistry = (channels: PluginRegistry["channels"] = []): PluginRegistry => ({ +type TestChannelRegistration = { + pluginId: string; + plugin: unknown; + source: string; +}; + +export const createTestRegistry = (channels: TestChannelRegistration[] = []): PluginRegistry => ({ plugins: [], tools: [], hooks: [], typedHooks: [], - channels, + channels: channels as unknown as PluginRegistry["channels"], providers: [], gatewayHandlers: {}, httpHandlers: [], diff --git a/src/tts/tts.test.ts b/src/tts/tts.test.ts index a00a00605d..1c5ecfb558 100644 --- a/src/tts/tts.test.ts +++ b/src/tts/tts.test.ts @@ -1,7 +1,8 @@ -import { completeSimple } from "@mariozechner/pi-ai"; +import { completeSimple, type AssistantMessage } from "@mariozechner/pi-ai"; import { describe, expect, it, vi, beforeEach } from "vitest"; import { getApiKeyForModel } from "../agents/model-auth.js"; import { resolveModel } from "../agents/pi-embedded-runner/model.js"; +import type { OpenClawConfig } from "../config/config.js"; import { withEnv } from "../test-utils/env.js"; import * as tts from "./tts.js"; @@ -54,12 +55,36 @@ const { resolveEdgeOutputFormat, } = _test; +const mockAssistantMessage = (content: AssistantMessage["content"]): AssistantMessage => ({ + role: "assistant", + content, + api: "openai-completions", + provider: "openai", + model: "gpt-4o-mini", + usage: { + input: 1, + output: 1, + cacheRead: 0, + cacheWrite: 0, + totalTokens: 2, + cost: { + input: 0, + output: 0, + cacheRead: 0, + cacheWrite: 0, + total: 0, + }, + }, + stopReason: "stop", + timestamp: Date.now(), +}); + describe("tts", () => { beforeEach(() => { vi.clearAllMocks(); - vi.mocked(completeSimple).mockResolvedValue({ - content: [{ type: "text", text: "Summary" }], - }); + vi.mocked(completeSimple).mockResolvedValue( + mockAssistantMessage([{ type: "text", text: "Summary" }]), + ); }); describe("isValidVoiceId", () => { @@ -165,7 +190,7 @@ describe("tts", () => { }); describe("resolveEdgeOutputFormat", () => { - const baseCfg = { + const baseCfg: OpenClawConfig = { agents: { defaults: { model: { primary: "openai/gpt-4o-mini" } } }, messages: { tts: {} }, }; @@ -223,7 +248,7 @@ describe("tts", () => { }); describe("summarizeText", () => { - const baseCfg = { + const baseCfg: OpenClawConfig = { agents: { defaults: { model: { primary: "openai/gpt-4o-mini" } } }, messages: { tts: {} }, }; @@ -231,9 +256,9 @@ describe("tts", () => { it("summarizes text and returns result with metrics", async () => { const mockSummary = "This is a summarized version of the text."; - vi.mocked(completeSimple).mockResolvedValue({ - content: [{ type: "text", text: mockSummary }], - }); + vi.mocked(completeSimple).mockResolvedValue( + mockAssistantMessage([{ type: "text", text: mockSummary }]), + ); const longText = "A".repeat(2000); const result = await summarizeText({ @@ -268,7 +293,7 @@ describe("tts", () => { }); it("uses summaryModel override when configured", async () => { - const cfg = { + const cfg: OpenClawConfig = { agents: { defaults: { model: { primary: "anthropic/claude-opus-4-5" } } }, messages: { tts: { summaryModel: "openai/gpt-4.1-mini" } }, }; @@ -330,9 +355,7 @@ describe("tts", () => { }); it("throws error when no summary is returned", async () => { - vi.mocked(completeSimple).mockResolvedValue({ - content: [], - }); + vi.mocked(completeSimple).mockResolvedValue(mockAssistantMessage([])); await expect( summarizeText({ @@ -346,9 +369,9 @@ describe("tts", () => { }); it("throws error when summary content is empty", async () => { - vi.mocked(completeSimple).mockResolvedValue({ - content: [{ type: "text", text: " " }], - }); + vi.mocked(completeSimple).mockResolvedValue( + mockAssistantMessage([{ type: "text", text: " " }]), + ); await expect( summarizeText({ @@ -363,7 +386,7 @@ describe("tts", () => { }); describe("getTtsProvider", () => { - const baseCfg = { + const baseCfg: OpenClawConfig = { agents: { defaults: { model: { primary: "openai/gpt-4o-mini" } } }, messages: { tts: {} }, }; @@ -415,7 +438,7 @@ describe("tts", () => { }); describe("maybeApplyTtsToPayload", () => { - const baseCfg = { + const baseCfg: OpenClawConfig = { agents: { defaults: { model: { primary: "openai/gpt-4o-mini" } } }, messages: { tts: { @@ -445,11 +468,11 @@ describe("tts", () => { } }; - const taggedCfg = { + const taggedCfg: OpenClawConfig = { ...baseCfg, messages: { - ...baseCfg.messages, - tts: { ...baseCfg.messages.tts, auto: "tagged" }, + ...baseCfg.messages!, + tts: { ...baseCfg.messages!.tts, auto: "tagged" }, }, }; diff --git a/src/web/login-qr.test.ts b/src/web/login-qr.test.ts index b00abaedd0..4021ba73a1 100644 --- a/src/web/login-qr.test.ts +++ b/src/web/login-qr.test.ts @@ -37,6 +37,9 @@ vi.mock("./qr-image.js", () => ({ const { startWebLoginWithQr, waitForWebLogin } = await import("./login-qr.js"); const { createWaSocket, waitForWaConnection, logoutWeb } = await import("./session.js"); +const createWaSocketMock = vi.mocked(createWaSocket); +const waitForWaConnectionMock = vi.mocked(waitForWaConnection); +const logoutWebMock = vi.mocked(logoutWeb); describe("login-qr", () => { beforeEach(() => { @@ -44,7 +47,7 @@ describe("login-qr", () => { }); it("restarts login once on status 515 and completes", async () => { - waitForWaConnection + waitForWaConnectionMock .mockRejectedValueOnce({ output: { statusCode: 515 } }) .mockResolvedValueOnce(undefined); @@ -54,7 +57,7 @@ describe("login-qr", () => { const result = await waitForWebLogin({ timeoutMs: 5000 }); expect(result.connected).toBe(true); - expect(createWaSocket).toHaveBeenCalledTimes(2); - expect(logoutWeb).not.toHaveBeenCalled(); + expect(createWaSocketMock).toHaveBeenCalledTimes(2); + expect(logoutWebMock).not.toHaveBeenCalled(); }); }); diff --git a/src/web/login.coverage.test.ts b/src/web/login.coverage.test.ts index 207c559ffd..4d758e27b4 100644 --- a/src/web/login.coverage.test.ts +++ b/src/web/login.coverage.test.ts @@ -44,6 +44,9 @@ vi.mock("./session.js", () => { }); const { createWaSocket, waitForWaConnection, formatError } = await import("./session.js"); +const createWaSocketMock = vi.mocked(createWaSocket); +const waitForWaConnectionMock = vi.mocked(waitForWaConnection); +const formatErrorMock = vi.mocked(formatError); const { loginWeb } = await import("./login.js"); describe("loginWeb coverage", () => { @@ -57,27 +60,29 @@ describe("loginWeb coverage", () => { }); it("restarts once when WhatsApp requests code 515", async () => { - waitForWaConnection + waitForWaConnectionMock .mockRejectedValueOnce({ output: { statusCode: 515 } }) .mockResolvedValueOnce(undefined); const runtime = { log: vi.fn(), error: vi.fn() } as never; - await loginWeb(false, waitForWaConnection as never, runtime); + await loginWeb(false, waitForWaConnectionMock as never, runtime); - expect(createWaSocket).toHaveBeenCalledTimes(2); - const firstSock = await createWaSocket.mock.results[0].value; + expect(createWaSocketMock).toHaveBeenCalledTimes(2); + const firstSock = await createWaSocketMock.mock.results[0]?.value; expect(firstSock.ws.close).toHaveBeenCalled(); vi.runAllTimers(); - const secondSock = await createWaSocket.mock.results[1].value; + const secondSock = await createWaSocketMock.mock.results[1]?.value; expect(secondSock.ws.close).toHaveBeenCalled(); }); it("clears creds and throws when logged out", async () => { - waitForWaConnection.mockRejectedValueOnce({ + waitForWaConnectionMock.mockRejectedValueOnce({ output: { statusCode: DisconnectReason.loggedOut }, }); - await expect(loginWeb(false, waitForWaConnection as never)).rejects.toThrow(/cache cleared/i); + await expect(loginWeb(false, waitForWaConnectionMock as never)).rejects.toThrow( + /cache cleared/i, + ); expect(rmMock).toHaveBeenCalledWith(authDir, { recursive: true, force: true, @@ -85,10 +90,10 @@ describe("loginWeb coverage", () => { }); it("formats and rethrows generic errors", async () => { - waitForWaConnection.mockRejectedValueOnce(new Error("boom")); - await expect(loginWeb(false, waitForWaConnection as never)).rejects.toThrow( + waitForWaConnectionMock.mockRejectedValueOnce(new Error("boom")); + await expect(loginWeb(false, waitForWaConnectionMock as never)).rejects.toThrow( "formatted:Error: boom", ); - expect(formatError).toHaveBeenCalled(); + expect(formatErrorMock).toHaveBeenCalled(); }); }); diff --git a/src/web/session.test.ts b/src/web/session.test.ts index fc35362fcb..ffc0e2af19 100644 --- a/src/web/session.test.ts +++ b/src/web/session.test.ts @@ -7,6 +7,7 @@ import { baileys, getLastSocket, resetBaileysMocks, resetLoadConfigMock } from " const { createWaSocket, formatError, logWebSelfId, waitForWaConnection } = await import("./session.js"); +const useMultiFileAuthStateMock = vi.mocked(baileys.useMultiFileAuthState); function mockCredsJsonSpies(readContents: string) { const credsSuffix = path.join(".openclaw", "credentials", "whatsapp", "default", "creds.json"); @@ -65,7 +66,7 @@ describe("web session", () => { expect(passedLogger?.level).toBe("silent"); expect(typeof passedLogger?.trace).toBe("function"); const sock = getLastSocket(); - const saveCreds = (await baileys.useMultiFileAuthState.mock.results[0].value).saveCreds; + const saveCreds = (await useMultiFileAuthStateMock.mock.results[0]?.value)?.saveCreds; // trigger creds.update listener sock.ev.emit("creds.update", {}); await new Promise((resolve) => setImmediate(resolve)); @@ -145,7 +146,7 @@ describe("web session", () => { await createWaSocket(false, false); const sock = getLastSocket(); - const saveCreds = (await baileys.useMultiFileAuthState.mock.results[0].value).saveCreds; + const saveCreds = (await useMultiFileAuthStateMock.mock.results[0]?.value)?.saveCreds; sock.ev.emit("creds.update", {}); await new Promise((resolve) => setImmediate(resolve)); @@ -170,7 +171,7 @@ describe("web session", () => { await gate; inFlight -= 1; }); - baileys.useMultiFileAuthState.mockResolvedValueOnce({ + useMultiFileAuthStateMock.mockResolvedValueOnce({ state: { creds: {}, keys: {} }, saveCreds, }); @@ -184,7 +185,9 @@ describe("web session", () => { await new Promise((resolve) => setImmediate(resolve)); expect(inFlight).toBe(1); - release?.(); + if (release) { + release(); + } // let both queued saves complete await new Promise((resolve) => setImmediate(resolve)); @@ -207,7 +210,7 @@ describe("web session", () => { await createWaSocket(false, false); const sock = getLastSocket(); - const saveCreds = (await baileys.useMultiFileAuthState.mock.results[0].value).saveCreds; + const saveCreds = (await useMultiFileAuthStateMock.mock.results[0]?.value)?.saveCreds; sock.ev.emit("creds.update", {}); await new Promise((resolve) => setImmediate(resolve));