chore: Fix more extension test type 1/N.

This commit is contained in:
cpojer
2026-02-17 10:12:14 +09:00
parent 0f8d1f175a
commit 72f00df95a
9 changed files with 75 additions and 26 deletions

View File

@@ -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();

View File

@@ -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;

View File

@@ -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,
};

View File

@@ -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<CallManagerContext> = {}): CallManager
};
}
function createProvider(overrides: Partial<VoiceCallProvider> = {}): 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<void> {
const provider = createProvider({
hangupCall: async (input: HangupCallInput): Promise<void> => {
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<void> {
const provider = createProvider({
hangupCall: async (input: HangupCallInput): Promise<void> => {
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<void> {
const provider = createProvider({
hangupCall: async (): Promise<void> => {
throw new Error("provider down");
},
};
});
const ctx = createContext({
config: VoiceCallConfigSchema.parse({
enabled: true,

View File

@@ -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",
});

View File

@@ -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();
});

View File

@@ -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([]);
});

View File

@@ -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<typeof createServer>[0],
fn: (baseUrl: string) => Promise<void>,
) {
async function withServer(handler: RequestListener, fn: (baseUrl: string) => Promise<void>) {
const server = createServer(handler);
await new Promise<void>((resolve) => {
server.listen(0, "127.0.0.1", () => resolve());

View File

@@ -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(() => {