




















@@ -93,6 +93,33 @@ function installDiscordRuntime(discord: Record<string, unknown>) {
9393} as unknown as PluginRuntime);
9494}
959596+type MockWithCalls = {
97+mock: { calls: unknown[][] };
98+};
99+100+function objectArgAt(
101+mock: MockWithCalls,
102+callIndex: number,
103+argIndex: number,
104+): Record<string, unknown> {
105+const value = mock.mock.calls[callIndex]?.[argIndex];
106+if (value === undefined || value === null || typeof value !== "object" || Array.isArray(value)) {
107+throw new Error(`expected call ${callIndex} argument ${argIndex} to be an object`);
108+}
109+return value as Record<string, unknown>;
110+}
111+112+function argAt(mock: MockWithCalls, callIndex: number, argIndex: number): unknown {
113+return mock.mock.calls[callIndex]?.[argIndex];
114+}
115+116+function recordField(value: unknown, field: string): Record<string, unknown> {
117+if (value === undefined || value === null || typeof value !== "object" || Array.isArray(value)) {
118+throw new Error(`expected ${field} to be an object`);
119+}
120+return value as Record<string, unknown>;
121+}
122+96123afterEach(() => {
97124probeDiscordMock.mockReset();
98125monitorDiscordProviderMock.mockReset();
@@ -243,14 +270,10 @@ describe("discordPlugin outbound", () => {
243270},
244271} as OpenClawConfig;
245272246-expect(resolveAccount(cfg).config).toMatchObject({
247-gatewayReadyTimeoutMs: 90_000,
248-gatewayRuntimeReadyTimeoutMs: 120_000,
249-});
250-expect(resolveAccount(cfg, "work").config).toMatchObject({
251-gatewayReadyTimeoutMs: 60_000,
252-gatewayRuntimeReadyTimeoutMs: 120_000,
253-});
273+expect(resolveAccount(cfg).config.gatewayReadyTimeoutMs).toBe(90_000);
274+expect(resolveAccount(cfg).config.gatewayRuntimeReadyTimeoutMs).toBe(120_000);
275+expect(resolveAccount(cfg, "work").config.gatewayReadyTimeoutMs).toBe(60_000);
276+expect(resolveAccount(cfg, "work").config.gatewayRuntimeReadyTimeoutMs).toBe(120_000);
254277});
255278256279it("forwards full media send context to sendMessageDiscord", async () => {
@@ -272,17 +295,15 @@ describe("discordPlugin outbound", () => {
272295},
273296});
274297275-expect(sendMessageDiscord).toHaveBeenCalledWith(
276-"channel:thread-123",
277-"hi",
278-expect.objectContaining({
279-mediaUrl: "/tmp/image.png",
280-mediaLocalRoots: ["/tmp/agent-root"],
281- mediaReadFile,
282-replyTo: "reply-123",
283-}),
284-);
285-expect(result).toMatchObject({ channel: "discord", messageId: "m1" });
298+expect(argAt(sendMessageDiscord, 0, 0)).toBe("channel:thread-123");
299+expect(argAt(sendMessageDiscord, 0, 1)).toBe("hi");
300+const sendOptions = objectArgAt(sendMessageDiscord, 0, 2);
301+expect(sendOptions.mediaUrl).toBe("/tmp/image.png");
302+expect(sendOptions.mediaLocalRoots).toEqual(["/tmp/agent-root"]);
303+expect(sendOptions.mediaReadFile).toBe(mediaReadFile);
304+expect(sendOptions.replyTo).toBe("reply-123");
305+expect(result.channel).toBe("discord");
306+expect(result.messageId).toBe("m1");
286307});
287308288309it("splits text and video into separate sends for attached outbound delivery", async () => {
@@ -305,23 +326,14 @@ describe("discordPlugin outbound", () => {
305326});
306327307328expect(sendMessageDiscord).toHaveBeenCalledTimes(2);
308-expect(sendMessageDiscord).toHaveBeenNthCalledWith(
309-1,
310-"channel:thread-123",
311-"done - tiny cyber-lobster clip incoming",
312-expect.objectContaining({
313-replyTo: "reply-123",
314-}),
315-);
316-expect(sendMessageDiscord).toHaveBeenNthCalledWith(
317-2,
318-"channel:thread-123",
319-"",
320-expect.objectContaining({
321-mediaUrl: "/tmp/molty.mp4",
322-}),
323-);
324-expect(result).toMatchObject({ channel: "discord", messageId: "video-1" });
329+expect(argAt(sendMessageDiscord, 0, 0)).toBe("channel:thread-123");
330+expect(argAt(sendMessageDiscord, 0, 1)).toBe("done - tiny cyber-lobster clip incoming");
331+expect(objectArgAt(sendMessageDiscord, 0, 2).replyTo).toBe("reply-123");
332+expect(argAt(sendMessageDiscord, 1, 0)).toBe("channel:thread-123");
333+expect(argAt(sendMessageDiscord, 1, 1)).toBe("");
334+expect(objectArgAt(sendMessageDiscord, 1, 2).mediaUrl).toBe("/tmp/molty.mp4");
335+expect(result.channel).toBe("discord");
336+expect(result.messageId).toBe("video-1");
325337});
326338327339it("threads poll sends through the thread target", async () => {
@@ -339,17 +351,15 @@ describe("discordPlugin outbound", () => {
339351threadId: "thread-123",
340352});
341353342-expect(sendPollDiscord).toHaveBeenCalledWith(
343-"channel:thread-123",
344-{
345-question: "Best shell?",
346-options: ["molty", "molter"],
347-},
348-expect.objectContaining({
349-accountId: "work",
350-}),
351-);
352-expect(result).toMatchObject({ channel: "discord", messageId: "poll-1" });
354+expect(argAt(sendPollDiscord, 0, 0)).toBe("channel:thread-123");
355+expect(argAt(sendPollDiscord, 0, 1)).toEqual({
356+question: "Best shell?",
357+options: ["molty", "molter"],
358+});
359+expect(objectArgAt(sendPollDiscord, 0, 2).accountId).toBe("work");
360+const pollResult = result as { channel?: string; messageId?: string };
361+expect(pollResult.channel).toBe("discord");
362+expect(pollResult.messageId).toBe("poll-1");
353363} finally {
354364sendPollSpy.mockRestore();
355365}
@@ -434,14 +444,11 @@ describe("discordPlugin outbound", () => {
434444target: "channel:222",
435445});
436446437-expect(fetchPermissionsSpy).toHaveBeenCalledWith(
438-"222",
439-expect.objectContaining({ token: "discord-token" }),
440-);
441-expect(diagnostics?.details?.permissions).toMatchObject({
442-channelId: "222",
443-missingRequired: ["Connect", "Speak", "ReadMessageHistory"],
444-});
447+expect(fetchPermissionsSpy.mock.calls[0]?.[0]).toBe("222");
448+expect(objectArgAt(fetchPermissionsSpy, 0, 1).token).toBe("discord-token");
449+const permissions = recordField(diagnostics?.details?.permissions, "permissions");
450+expect(permissions.channelId).toBe("222");
451+expect(permissions.missingRequired).toEqual(["Connect", "Speak", "ReadMessageHistory"]);
445452expect(diagnostics?.lines?.map((line) => line.text).join("\n")).toContain(
446453"Missing required: Connect, Speak, ReadMessageHistory",
447454);
@@ -483,12 +490,9 @@ describe("discordPlugin outbound", () => {
483490includeApplication: true,
484491}),
485492);
486-expect(monitorDiscordProviderMock).toHaveBeenCalledWith(
487-expect.objectContaining({
488-token: "discord-token",
489-accountId: "default",
490-}),
491-);
493+const monitorParams = objectArgAt(monitorDiscordProviderMock, 0, 0);
494+expect(monitorParams.token).toBe("discord-token");
495+expect(monitorParams.accountId).toBe("default");
492496expect(sleepWithAbortMock).not.toHaveBeenCalled();
493497expect(runtimeProbeDiscord).not.toHaveBeenCalled();
494498expect(runtimeMonitorDiscordProvider).not.toHaveBeenCalled();
@@ -520,12 +524,9 @@ describe("discordPlugin outbound", () => {
520524521525await discordPlugin.gateway!.startAccount!(ctx);
522526523-expect(monitorDiscordProviderMock).toHaveBeenCalledWith(
524-expect.objectContaining({
525-token: "discord-token",
526-accountId: "default",
527-}),
528-);
527+const monitorParams = objectArgAt(monitorDiscordProviderMock, 0, 0);
528+expect(monitorParams.token).toBe("discord-token");
529+expect(monitorParams.accountId).toBe("default");
529530await vi.waitFor(() =>
530531expect(probeDiscordMock).toHaveBeenCalledWith("discord-token", 2500, {
531532includeApplication: true,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。