From 72f00df95a26bd9327b3e53a8582492d87227721 Mon Sep 17 00:00:00 2001 From: cpojer Date: Tue, 17 Feb 2026 10:12:14 +0900 Subject: [PATCH] chore: Fix more extension test type 1/N. --- extensions/tlon/src/urbit/auth.ssrf.test.ts | 4 ++- extensions/tlon/src/urbit/sse-client.test.ts | 4 ++- extensions/voice-call/src/config.test.ts | 6 +++- .../voice-call/src/manager/events.test.ts | 36 ++++++++++++------- .../whatsapp/src/channel.send-options.test.ts | 7 ++-- .../whatsapp/src/resolve-target.test.ts | 15 ++++++++ extensions/zalo/src/channel.directory.test.ts | 16 +++++++-- extensions/zalo/src/monitor.webhook.test.ts | 7 ++-- test/helpers/mock-incoming-request.ts | 6 +++- 9 files changed, 75 insertions(+), 26 deletions(-) diff --git a/extensions/tlon/src/urbit/auth.ssrf.test.ts b/extensions/tlon/src/urbit/auth.ssrf.test.ts index 89235e922..104492e96 100644 --- a/extensions/tlon/src/urbit/auth.ssrf.test.ts +++ b/extensions/tlon/src/urbit/auth.ssrf.test.ts @@ -1,4 +1,5 @@ import { SsrFBlockedError } from "openclaw/plugin-sdk"; +import type { LookupFn } from "openclaw/plugin-sdk"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { authenticate } from "./auth.js"; @@ -31,10 +32,11 @@ describe("tlon urbit auth ssrf", () => { }), }); vi.stubGlobal("fetch", mockFetch); + const lookupFn = (async () => [{ address: "127.0.0.1", family: 4 }]) as unknown as LookupFn; const cookie = await authenticate("http://127.0.0.1:8080", "code", { ssrfPolicy: { allowPrivateNetwork: true }, - lookupFn: async () => [{ address: "127.0.0.1", family: 4 }], + lookupFn, }); expect(cookie).toContain("urbauth-~zod=123"); expect(mockFetch).toHaveBeenCalled(); diff --git a/extensions/tlon/src/urbit/sse-client.test.ts b/extensions/tlon/src/urbit/sse-client.test.ts index fa0530509..b37c3be05 100644 --- a/extensions/tlon/src/urbit/sse-client.test.ts +++ b/extensions/tlon/src/urbit/sse-client.test.ts @@ -1,3 +1,4 @@ +import type { LookupFn } from "openclaw/plugin-sdk"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { UrbitSSEClient } from "./sse-client.js"; @@ -15,9 +16,10 @@ describe("UrbitSSEClient", () => { it("sends subscriptions added after connect", async () => { mockFetch.mockResolvedValue({ ok: true, status: 200, text: async () => "" }); + const lookupFn = (async () => [{ address: "1.1.1.1", family: 4 }]) as unknown as LookupFn; const client = new UrbitSSEClient("https://example.com", "urbauth-~zod=123", { - lookupFn: async () => [{ address: "1.1.1.1", family: 4 }], + lookupFn, }); (client as { isConnected: boolean }).isConnected = true; diff --git a/extensions/voice-call/src/config.test.ts b/extensions/voice-call/src/config.test.ts index 4b1389b35..081d86b10 100644 --- a/extensions/voice-call/src/config.test.ts +++ b/extensions/voice-call/src/config.test.ts @@ -10,6 +10,7 @@ function createBaseConfig(provider: "telnyx" | "twilio" | "plivo" | "mock"): Voi allowFrom: [], outbound: { defaultMode: "notify", notifyHangupDelaySec: 3 }, maxDurationSeconds: 300, + staleCallReaperSeconds: 600, silenceTimeoutMs: 800, transcriptTimeoutMs: 180000, ringTimeoutMs: 30000, @@ -32,7 +33,10 @@ function createBaseConfig(provider: "telnyx" | "twilio" | "plivo" | "mock"): Voi }, skipSignatureVerification: false, stt: { provider: "openai", model: "whisper-1" }, - tts: { provider: "openai", model: "gpt-4o-mini-tts", voice: "coral" }, + tts: { + provider: "openai", + openai: { model: "gpt-4o-mini-tts", voice: "coral" }, + }, responseModel: "openai/gpt-4o-mini", responseTimeoutMs: 30000, }; diff --git a/extensions/voice-call/src/manager/events.test.ts b/extensions/voice-call/src/manager/events.test.ts index 5e9b7e1d9..8407c9cc6 100644 --- a/extensions/voice-call/src/manager/events.test.ts +++ b/extensions/voice-call/src/manager/events.test.ts @@ -3,6 +3,7 @@ import os from "node:os"; import path from "node:path"; import { describe, expect, it } from "vitest"; import { VoiceCallConfigSchema } from "../config.js"; +import type { VoiceCallProvider } from "../providers/base.js"; import type { HangupCallInput, NormalizedEvent } from "../types.js"; import type { CallManagerContext } from "./context.js"; import { processEvent } from "./events.js"; @@ -29,15 +30,28 @@ function createContext(overrides: Partial = {}): CallManager }; } +function createProvider(overrides: Partial = {}): VoiceCallProvider { + return { + name: "plivo", + verifyWebhook: () => ({ ok: true }), + parseWebhookEvent: () => ({ events: [] }), + initiateCall: async () => ({ providerCallId: "provider-call-id", status: "initiated" }), + hangupCall: async () => {}, + playTts: async () => {}, + startListening: async () => {}, + stopListening: async () => {}, + ...overrides, + }; +} + describe("processEvent (functional)", () => { it("calls provider hangup when rejecting inbound call", () => { const hangupCalls: HangupCallInput[] = []; - const provider = { - name: "plivo" as const, - async hangupCall(input: HangupCallInput): Promise { + const provider = createProvider({ + hangupCall: async (input: HangupCallInput): Promise => { hangupCalls.push(input); }, - }; + }); const ctx = createContext({ config: VoiceCallConfigSchema.parse({ @@ -98,12 +112,11 @@ describe("processEvent (functional)", () => { it("calls hangup only once for duplicate events for same rejected call", () => { const hangupCalls: HangupCallInput[] = []; - const provider = { - name: "plivo" as const, - async hangupCall(input: HangupCallInput): Promise { + const provider = createProvider({ + hangupCall: async (input: HangupCallInput): Promise => { hangupCalls.push(input); }, - }; + }); const ctx = createContext({ config: VoiceCallConfigSchema.parse({ enabled: true, @@ -208,12 +221,11 @@ describe("processEvent (functional)", () => { }); it("when hangup throws, logs and does not throw", () => { - const provider = { - name: "plivo" as const, - async hangupCall(): Promise { + const provider = createProvider({ + hangupCall: async (): Promise => { throw new Error("provider down"); }, - }; + }); const ctx = createContext({ config: VoiceCallConfigSchema.parse({ enabled: true, diff --git a/extensions/whatsapp/src/channel.send-options.test.ts b/extensions/whatsapp/src/channel.send-options.test.ts index f50bdc314..abdc07112 100644 --- a/extensions/whatsapp/src/channel.send-options.test.ts +++ b/extensions/whatsapp/src/channel.send-options.test.ts @@ -1,3 +1,4 @@ +import type { OpenClawConfig } from "openclaw/plugin-sdk"; import { describe, it, expect, vi, beforeEach } from "vitest"; import { whatsappPlugin } from "./channel.js"; @@ -25,7 +26,8 @@ describe("whatsappPlugin.outbound.sendText", () => { }); it("passes linkPreview option to sendMessageWhatsApp", async () => { - await whatsappPlugin.outbound.sendText({ + await whatsappPlugin.outbound!.sendText!({ + cfg: {} as OpenClawConfig, to: "1234567890", text: "http://example.com", // @ts-expect-error - injecting extra param as per runtime behavior @@ -42,7 +44,8 @@ describe("whatsappPlugin.outbound.sendText", () => { }); it("passes linkPreview=undefined when omitted", async () => { - await whatsappPlugin.outbound.sendText({ + await whatsappPlugin.outbound!.sendText!({ + cfg: {} as OpenClawConfig, to: "1234567890", text: "hello", }); diff --git a/extensions/whatsapp/src/resolve-target.test.ts b/extensions/whatsapp/src/resolve-target.test.ts index 4a5930abe..86295a310 100644 --- a/extensions/whatsapp/src/resolve-target.test.ts +++ b/extensions/whatsapp/src/resolve-target.test.ts @@ -101,6 +101,9 @@ describe("whatsapp resolveTarget", () => { }); expect(result.ok).toBe(true); + if (!result.ok) { + throw result.error; + } expect(result.to).toBe("5511999999999@s.whatsapp.net"); }); @@ -112,6 +115,9 @@ describe("whatsapp resolveTarget", () => { }); expect(result.ok).toBe(true); + if (!result.ok) { + throw result.error; + } expect(result.to).toBe("5511999999999@s.whatsapp.net"); }); @@ -123,6 +129,9 @@ describe("whatsapp resolveTarget", () => { }); expect(result.ok).toBe(true); + if (!result.ok) { + throw result.error; + } expect(result.to).toBe("5511999999999@s.whatsapp.net"); }); @@ -134,6 +143,9 @@ describe("whatsapp resolveTarget", () => { }); expect(result.ok).toBe(true); + if (!result.ok) { + throw result.error; + } expect(result.to).toBe("120363123456789@g.us"); }); @@ -145,6 +157,9 @@ describe("whatsapp resolveTarget", () => { }); expect(result.ok).toBe(false); + if (result.ok) { + throw new Error("expected resolution to fail"); + } expect(result.error).toBeDefined(); }); diff --git a/extensions/zalo/src/channel.directory.test.ts b/extensions/zalo/src/channel.directory.test.ts index 91660c6b5..61b446a50 100644 --- a/extensions/zalo/src/channel.directory.test.ts +++ b/extensions/zalo/src/channel.directory.test.ts @@ -1,8 +1,16 @@ -import type { OpenClawConfig } from "openclaw/plugin-sdk"; +import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk"; import { describe, expect, it } from "vitest"; import { zaloPlugin } from "./channel.js"; describe("zalo directory", () => { + const runtimeEnv: RuntimeEnv = { + log: () => {}, + error: () => {}, + exit: (code: number): never => { + throw new Error(`exit ${code}`); + }, + }; + it("lists peers from allowFrom", async () => { const cfg = { channels: { @@ -17,11 +25,12 @@ describe("zalo directory", () => { expect(zaloPlugin.directory?.listGroups).toBeTruthy(); await expect( - zaloPlugin.directory!.listPeers({ + zaloPlugin.directory!.listPeers!({ cfg, accountId: undefined, query: undefined, limit: undefined, + runtime: runtimeEnv, }), ).resolves.toEqual( expect.arrayContaining([ @@ -32,11 +41,12 @@ describe("zalo directory", () => { ); await expect( - zaloPlugin.directory!.listGroups({ + zaloPlugin.directory!.listGroups!({ cfg, accountId: undefined, query: undefined, limit: undefined, + runtime: runtimeEnv, }), ).resolves.toEqual([]); }); diff --git a/extensions/zalo/src/monitor.webhook.test.ts b/extensions/zalo/src/monitor.webhook.test.ts index b55577a55..91e1be8c4 100644 --- a/extensions/zalo/src/monitor.webhook.test.ts +++ b/extensions/zalo/src/monitor.webhook.test.ts @@ -1,14 +1,11 @@ -import { createServer } from "node:http"; +import { createServer, type RequestListener } from "node:http"; import type { AddressInfo } from "node:net"; import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk"; import { describe, expect, it, vi } from "vitest"; import { handleZaloWebhookRequest, registerZaloWebhookTarget } from "./monitor.js"; import type { ResolvedZaloAccount } from "./types.js"; -async function withServer( - handler: Parameters[0], - fn: (baseUrl: string) => Promise, -) { +async function withServer(handler: RequestListener, fn: (baseUrl: string) => Promise) { const server = createServer(handler); await new Promise((resolve) => { server.listen(0, "127.0.0.1", () => resolve()); diff --git a/test/helpers/mock-incoming-request.ts b/test/helpers/mock-incoming-request.ts index aa5476b9c..208389303 100644 --- a/test/helpers/mock-incoming-request.ts +++ b/test/helpers/mock-incoming-request.ts @@ -2,11 +2,15 @@ import { EventEmitter } from "node:events"; import type { IncomingMessage } from "node:http"; export function createMockIncomingRequest(chunks: string[]): IncomingMessage { - const req = new EventEmitter() as IncomingMessage & { destroyed?: boolean; destroy: () => void }; + const req = new EventEmitter() as IncomingMessage & { + destroyed?: boolean; + destroy: (error?: Error) => IncomingMessage; + }; req.destroyed = false; req.headers = {}; req.destroy = () => { req.destroyed = true; + return req; }; void Promise.resolve().then(() => {