chore: Fix types in tests 27/N.

This commit is contained in:
cpojer
2026-02-17 14:31:55 +09:00
parent 4235435309
commit 97c8f4999e
15 changed files with 129 additions and 51 deletions

View File

@@ -94,6 +94,8 @@ const baseParams = () => ({
textLimit: 4000,
ackReactionScope: "group-mentions",
mediaMaxBytes: 1,
threadHistoryScope: "thread" as const,
threadInheritParent: false,
removeAckAfterReply: false,
});

View File

@@ -35,7 +35,7 @@ describe("usage-format", () => {
},
},
},
} as OpenClawConfig;
} as unknown as OpenClawConfig;
const cost = resolveModelCostConfig({
provider: "test",

View File

@@ -158,6 +158,7 @@ describe("broadcast groups", () => {
from: "+1000",
conversationId: "+1000",
to: "+2000",
accountId: "default",
body: "hello",
timestamp: Date.now(),
chatType: "direct",

View File

@@ -11,6 +11,18 @@ import {
installWebAutoReplyTestHomeHooks();
function createMockListener() {
return {
close: vi.fn(async () => undefined),
onClose: new Promise<import("./inbound.js").WebListenerCloseReason>(() => {}),
signalClose: vi.fn(),
sendMessage: vi.fn(async () => ({ messageId: "msg-1" })),
sendPoll: vi.fn(async () => ({ messageId: "poll-1" })),
sendReaction: vi.fn(async () => undefined),
sendComposingTo: vi.fn(async () => undefined),
};
}
describe("typing controller idle", () => {
installWebAutoReplyUnitTestHooks();
@@ -53,11 +65,12 @@ describe("typing controller idle", () => {
timestamp: Date.now(),
chatType: "direct",
chatId: "direct:+1000",
accountId: "default",
sendComposing,
reply,
sendMedia,
});
return { close: vi.fn().mockResolvedValue(undefined) };
return createMockListener();
},
false,
replyResolver,

View File

@@ -16,6 +16,18 @@ describe("web auto-reply", () => {
installWebAutoReplyUnitTestHooks({ pinDns: true });
type ListenerFactory = NonNullable<Parameters<typeof monitorWebChannel>[1]>;
function createMockListener() {
return {
close: vi.fn(async () => undefined),
onClose: new Promise<import("./inbound.js").WebListenerCloseReason>(() => {}),
signalClose: vi.fn(),
sendMessage: vi.fn(async () => ({ messageId: "msg-1" })),
sendPoll: vi.fn(async () => ({ messageId: "poll-1" })),
sendReaction: vi.fn(async () => undefined),
sendComposingTo: vi.fn(async () => undefined),
};
}
async function setupSingleInboundMessage(params: {
resolverValue: { text: string; mediaUrl: string };
sendMedia: ReturnType<typeof vi.fn>;
@@ -28,7 +40,7 @@ describe("web auto-reply", () => {
let capturedOnMessage: ((msg: WebInboundMessage) => Promise<void>) | undefined;
const listenerFactory: ListenerFactory = async ({ onMessage }) => {
capturedOnMessage = onMessage;
return { close: vi.fn() };
return createMockListener();
};
await monitorWebChannel(false, listenerFactory, false, resolver);
@@ -115,7 +127,7 @@ describe("web auto-reply", () => {
let capturedOnMessage: ((msg: WebInboundMessage) => Promise<void>) | undefined;
const listenerFactory: ListenerFactory = async ({ onMessage }) => {
capturedOnMessage = onMessage;
return { close: vi.fn() };
return createMockListener();
};
const big = await fmt.make(sharedRaw, { width, height });
@@ -173,7 +185,7 @@ describe("web auto-reply", () => {
let capturedOnMessage: ((msg: WebInboundMessage) => Promise<void>) | undefined;
const listenerFactory: ListenerFactory = async ({ onMessage }) => {
capturedOnMessage = onMessage;
return { close: vi.fn() };
return createMockListener();
};
const bigPng = await sharp({

View File

@@ -36,7 +36,9 @@ function createHandlerForTest(opts: { cfg: OpenClawConfig; replyResolver: unknow
groupMemberNames: new Map(),
echoTracker: createEchoTracker({ maxItems: 10 }),
backgroundTasks,
replyResolver: opts.replyResolver,
replyResolver: opts.replyResolver as Parameters<
typeof createWebOnMessageHandler
>[0]["replyResolver"],
replyLogger: makeReplyLogger(),
baseMentionConfig: buildMentionConfig(opts.cfg),
account: {},
@@ -74,7 +76,7 @@ function buildInboundMessage(params: {
timestamp: params.timestamp,
chatType: params.chatType,
chatId: params.chatId,
accountId: params.accountId,
accountId: params.accountId ?? "default",
senderE164: params.senderE164,
senderName: params.senderName,
selfE164: params.selfE164,

View File

@@ -77,7 +77,11 @@ describe("web auto-reply monitor logging", () => {
await capturedOnMessage?.({
body: "hello",
from: "+1",
conversationId: "+1",
to: "+2",
accountId: "default",
chatType: "direct",
chatId: "+1",
id: "msg1",
sendComposing: vi.fn(),
reply: vi.fn(),

View File

@@ -6,6 +6,7 @@ import {
makeSessionStore,
setLoadConfigMock,
} from "./auto-reply.test-harness.js";
import type { WebInboundMessage } from "./inbound.js";
installWebAutoReplyTestHomeHooks();
@@ -19,7 +20,7 @@ function createRuntime() {
function startMonitorWebChannel(params: {
monitorWebChannelFn: (...args: unknown[]) => Promise<unknown>;
listenerFactory: (...args: unknown[]) => Promise<unknown>;
listenerFactory: unknown;
sleep: ReturnType<typeof vi.fn>;
signal?: AbortSignal;
reconnect?: { initialMs: number; maxMs: number; maxAttempts: number; factor: number };
@@ -43,6 +44,32 @@ function startMonitorWebChannel(params: {
return { runtime, controller, run };
}
function makeInboundMessage(params: {
body: string;
from: string;
to: string;
id?: string;
timestamp?: number;
sendComposing: ReturnType<typeof vi.fn>;
reply: ReturnType<typeof vi.fn>;
sendMedia: ReturnType<typeof vi.fn>;
}): WebInboundMessage {
return {
body: params.body,
from: params.from,
to: params.to,
id: params.id,
timestamp: params.timestamp,
conversationId: params.from,
accountId: "default",
chatType: "direct",
chatId: params.from,
sendComposing: params.sendComposing as unknown as WebInboundMessage["sendComposing"],
reply: params.reply as unknown as WebInboundMessage["reply"],
sendMedia: params.sendMedia as unknown as WebInboundMessage["sendMedia"],
};
}
describe("web auto-reply", () => {
installWebAutoReplyUnitTestHooks();
@@ -133,15 +160,17 @@ describe("web auto-reply", () => {
// The watchdog only needs `lastMessageAt` to be set. Don't await full message
// processing here since it can schedule timers and become flaky under load.
void capturedOnMessage?.({
body: "hi",
from: "+1",
to: "+2",
id: "m1",
sendComposing,
reply,
sendMedia,
});
void capturedOnMessage?.(
makeInboundMessage({
body: "hi",
from: "+1",
to: "+2",
id: "m1",
sendComposing,
reply,
sendMedia,
}),
);
await vi.advanceTimersByTimeAsync(31 * 60 * 1000);
await Promise.resolve();
@@ -174,7 +203,7 @@ describe("web auto-reply", () => {
const run = monitorWebChannel(
false,
listenerFactory,
listenerFactory as never,
true,
async () => ({ text: "ok" }),
runtime as never,
@@ -236,30 +265,34 @@ describe("web auto-reply", () => {
session: { store: store.storePath },
}));
await monitorWebChannel(false, listenerFactory, false, resolver);
await monitorWebChannel(false, listenerFactory as never, false, resolver);
expect(capturedOnMessage).toBeDefined();
// Two messages from the same sender with fixed timestamps
await capturedOnMessage?.({
body: "first",
from: "+1",
to: "+2",
id: "m1",
timestamp: 1735689600000, // Jan 1 2025 00:00:00 UTC
sendComposing,
reply,
sendMedia,
});
await capturedOnMessage?.({
body: "second",
from: "+1",
to: "+2",
id: "m2",
timestamp: 1735693200000, // Jan 1 2025 01:00:00 UTC
sendComposing,
reply,
sendMedia,
});
await capturedOnMessage?.(
makeInboundMessage({
body: "first",
from: "+1",
to: "+2",
id: "m1",
timestamp: 1735689600000, // Jan 1 2025 00:00:00 UTC
sendComposing,
reply,
sendMedia,
}),
);
await capturedOnMessage?.(
makeInboundMessage({
body: "second",
from: "+1",
to: "+2",
id: "m2",
timestamp: 1735693200000, // Jan 1 2025 01:00:00 UTC
sendComposing,
reply,
sendMedia,
}),
);
expect(resolver).toHaveBeenCalledTimes(2);
const firstArgs = resolver.mock.calls[0][0];

View File

@@ -4,7 +4,7 @@ import path from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { resolveAgentRoute } from "../../routing/resolve-route.js";
import { buildMentionConfig } from "./mentions.js";
import { applyGroupGating } from "./monitor/group-gating.js";
import { applyGroupGating, type GroupHistoryEntry } from "./monitor/group-gating.js";
import { buildInboundLine, formatReplyContext } from "./monitor/message-line.js";
let sessionDir: string | undefined;
@@ -41,7 +41,7 @@ function runGroupGating(params: {
conversationId?: string;
agentId?: string;
}) {
const groupHistories = new Map<string, unknown[]>();
const groupHistories = new Map<string, GroupHistoryEntry[]>();
const conversationId = params.conversationId ?? "123@g.us";
const agentId = params.agentId ?? "main";
const sessionKey = `agent:${agentId}:whatsapp:group:${conversationId}`;

View File

@@ -112,7 +112,12 @@ describe("web inbound media saves with extension", () => {
it("stores image extension, extracts caption mentions, and keeps document filename", async () => {
const onMessage = vi.fn();
const listener = await monitorWebInbox({ verbose: false, onMessage });
const listener = await monitorWebInbox({
verbose: false,
onMessage,
accountId: "default",
authDir: path.join(HOME, "wa-auth"),
});
const { createWaSocket } = await import("./session.js");
const realSock = await (
createWaSocket as unknown as () => Promise<{
@@ -194,6 +199,8 @@ describe("web inbound media saves with extension", () => {
verbose: false,
onMessage,
mediaMaxMb: 1,
accountId: "default",
authDir: path.join(HOME, "wa-auth"),
});
const { createWaSocket } = await import("./session.js");
const realSock = await (

View File

@@ -108,7 +108,7 @@ describe("createWebSendApi", () => {
});
it("falls back to unknown messageId if Baileys result does not expose key.id", async () => {
sendMessage.mockResolvedValueOnce("ok");
sendMessage.mockResolvedValueOnce({ key: {} });
const res = await api.sendMessage("+1555", "hello");
expect(res.messageId).toBe("unknown");
});

View File

@@ -37,7 +37,9 @@ describe("web login", () => {
});
it("loginWeb waits for connection and closes", async () => {
const sock = await createWaSocket();
const sock = await (
createWaSocket as unknown as () => Promise<{ ws: { close: () => void } }>
)();
const close = vi.spyOn(sock.ws, "close");
const waiter: typeof waitForWaConnection = vi.fn().mockResolvedValue(undefined);
await loginWeb(false, waiter);

View File

@@ -187,7 +187,7 @@ describe("web media loading", () => {
status: 404,
statusText: "Not Found",
url: "https://example.com/missing.jpg",
} as Response);
} as unknown as Response);
await expect(loadWebMedia("https://example.com/missing.jpg", 1024 * 1024)).rejects.toThrow(
/Failed to fetch media from https:\/\/example\.com\/missing\.jpg.*HTTP 404/i,
@@ -225,7 +225,7 @@ describe("web media loading", () => {
arrayBuffer: async () => Buffer.alloc(2048).buffer,
headers: { get: () => "image/png" },
status: 200,
} as Response);
} as unknown as Response);
await expect(loadWebMediaRaw("https://example.com/too-big.png", 1024)).rejects.toThrow(
/exceeds maxBytes 1024/i,
@@ -251,7 +251,7 @@ describe("web media loading", () => {
},
},
status: 200,
} as Response);
} as unknown as Response);
const result = await loadWebMedia("https://example.com/download?id=1", 1024 * 1024);
@@ -274,7 +274,7 @@ describe("web media loading", () => {
gifBytes.buffer.slice(gifBytes.byteOffset, gifBytes.byteOffset + gifBytes.byteLength),
headers: { get: () => "image/gif" },
status: 200,
} as Response);
} as unknown as Response);
const result = await loadWebMedia("https://example.com/animation.gif", 1024 * 1024);

View File

@@ -281,7 +281,9 @@ describe("web monitor inbox", () => {
expect(onMessage).toHaveBeenCalledTimes(2);
resolveFirst?.();
if (typeof resolveFirst === "function") {
resolveFirst();
}
await listener.close();
});

View File

@@ -172,7 +172,7 @@ describe("web session", () => {
inFlight -= 1;
});
useMultiFileAuthStateMock.mockResolvedValueOnce({
state: { creds: {}, keys: {} },
state: { creds: {} as never, keys: {} as never },
saveCreds,
});
@@ -185,7 +185,7 @@ describe("web session", () => {
await new Promise<void>((resolve) => setImmediate(resolve));
expect(inFlight).toBe(1);
if (release) {
if (typeof release === "function") {
release();
}