

























@@ -102,6 +102,26 @@ function createConfigWithDiscordAccount(overrides: Record<string, unknown> = {})
102102} as OpenClawConfig;
103103}
104104105+type MockCallReader = { mock: { calls: unknown[][] } };
106+107+function mockMessages(mock: unknown): string[] {
108+return (mock as MockCallReader).mock.calls.map((call) => String(call[0] ?? ""));
109+}
110+111+function expectMockLogContains(mock: unknown, expected: string): void {
112+expect(mockMessages(mock).some((message) => message.includes(expected))).toBe(true);
113+}
114+115+function expectMockLogNotContains(mock: unknown, expected: string): void {
116+expect(mockMessages(mock).every((message) => !message.includes(expected))).toBe(true);
117+}
118+119+function expectMessagesContainAll(messages: string[], expected: string[]): void {
120+for (const entry of expected) {
121+expect(messages.some((message) => message.includes(entry))).toBe(true);
122+}
123+}
124+105125vi.mock("../voice/manager.runtime.js", () => {
106126voiceRuntimeModuleLoadedMock();
107127return {
@@ -163,6 +183,21 @@ describe("monitorDiscordProvider", () => {
163183return reconcileParams.healthProbe;
164184};
165185186+const getMonitorLifecycleParams = (): {
187+gatewayReadyTimeoutMs?: number;
188+gatewayRuntimeReadyTimeoutMs?: number;
189+} => {
190+expect(monitorLifecycleMock).toHaveBeenCalledTimes(1);
191+const firstCall = monitorLifecycleMock.mock.calls.at(0);
192+const params = firstCall?.[0] as
193+| { gatewayReadyTimeoutMs?: number; gatewayRuntimeReadyTimeoutMs?: number }
194+| undefined;
195+if (!params) {
196+throw new Error("expected lifecycle monitor params");
197+}
198+return params;
199+};
200+166201beforeAll(async () => {
167202vi.doMock("openclaw/plugin-sdk/plugin-runtime", async () => {
168203const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/plugin-runtime")>(
@@ -330,8 +365,9 @@ describe("monitorDiscordProvider", () => {
330365expect(
331366emitter.emit("error", new Error("Max reconnect attempts (0) reached after code 1005")),
332367).toBe(true);
333-expect(runtime.error).toHaveBeenCalledWith(
334-expect.stringContaining("suppressed late gateway reconnect-exhausted error after dispose"),
368+expectMockLogContains(
369+runtime.error,
370+"suppressed late gateway reconnect-exhausted error after dispose",
335371);
336372});
337373@@ -350,7 +386,7 @@ describe("monitorDiscordProvider", () => {
350386expect(monitorLifecycleMock).not.toHaveBeenCalled();
351387expect(createdBindingManagers).toHaveLength(1);
352388expect(createdBindingManagers[0]?.stop).toHaveBeenCalledTimes(1);
353-expect(runtime.error).toHaveBeenCalledWith(expect.stringContaining("identity offline"));
389+expectMockLogContains(runtime.error, "identity offline");
354390});
355391356392it("fails closed before lifecycle when Discord bot identity has no usable id", async () => {
@@ -368,7 +404,7 @@ describe("monitorDiscordProvider", () => {
368404expect(monitorLifecycleMock).not.toHaveBeenCalled();
369405expect(createdBindingManagers).toHaveLength(1);
370406expect(createdBindingManagers[0]?.stop).toHaveBeenCalledTimes(1);
371-expect(runtime.error).toHaveBeenCalledWith(expect.stringContaining("no usable id"));
407+expectMockLogContains(runtime.error, "no usable id");
372408});
373409374410it("does not double-stop thread bindings when lifecycle performs cleanup", async () => {
@@ -402,12 +438,9 @@ describe("monitorDiscordProvider", () => {
402438runtime: baseRuntime(),
403439});
404440405-expect(monitorLifecycleMock).toHaveBeenCalledWith(
406-expect.objectContaining({
407-gatewayReadyTimeoutMs: 90_000,
408-gatewayRuntimeReadyTimeoutMs: 120_000,
409-}),
410-);
441+const lifecycleParams = getMonitorLifecycleParams();
442+expect(lifecycleParams.gatewayReadyTimeoutMs).toBe(90_000);
443+expect(lifecycleParams.gatewayRuntimeReadyTimeoutMs).toBe(120_000);
411444});
412445413446it("does not load the Discord voice runtime when voice is disabled", async () => {
@@ -852,9 +885,7 @@ describe("monitorDiscordProvider", () => {
852885853886await vi.waitFor(() => expect(clientDeployCommandsMock).toHaveBeenCalledTimes(1));
854887expect(monitorLifecycleMock).toHaveBeenCalledTimes(1);
855-expect(runtime.error).not.toHaveBeenCalledWith(
856-expect.stringContaining("failed to deploy native commands"),
857-);
888+expectMockLogNotContains(runtime.error, "failed to deploy native commands");
858889expect(
859890vi
860891.mocked(runtime.log)
@@ -908,9 +939,7 @@ describe("monitorDiscordProvider", () => {
908939expect(warningMessages[0]).toContain("retry after 0s");
909940expect(warningMessages[0]).toContain("Message send/receive is unaffected.");
910941expect(warningMessages[0]).not.toContain("body=");
911-expect(runtime.error).not.toHaveBeenCalledWith(
912-expect.stringContaining("native-slash-command-deploy-rest"),
913-);
942+expectMockLogNotContains(runtime.error, "native-slash-command-deploy-rest");
914943});
915944916945it("formats Discord deploy rate limits without raw response bodies", () => {
@@ -985,11 +1014,10 @@ describe("monitorDiscordProvider", () => {
98510149861015await vi.waitFor(() => expect(clientDeployCommandsMock).toHaveBeenCalledTimes(1));
9871016expect(clientDeployCommandsMock).toHaveBeenCalledWith({ mode: "reconcile" });
988-expect(getConstructedClientOptions().requestOptions).toMatchObject({
989-timeout: 15_000,
990-runtimeProfile: "persistent",
991-maxQueueSize: 1000,
992-});
1017+const requestOptions = getConstructedClientOptions().requestOptions;
1018+expect(requestOptions?.timeout).toBe(15_000);
1019+expect(requestOptions?.runtimeProfile).toBe("persistent");
1020+expect(requestOptions?.maxQueueSize).toBe(1000);
9931021expect(getConstructedClientOptions().eventQueue?.listenerTimeout).toBe(120_000);
9941022});
9951023@@ -1017,9 +1045,7 @@ describe("monitorDiscordProvider", () => {
10171045expect(listNativeCommandSpecsForConfigMock).not.toHaveBeenCalled();
10181046expect(getPluginCommandSpecsMock).not.toHaveBeenCalled();
10191047expect(clientDeployCommandsMock).not.toHaveBeenCalled();
1020-expect(runtime.log).not.toHaveBeenCalledWith(
1021-expect.stringContaining("cleared native commands"),
1022-);
1048+expectMockLogNotContains(runtime.log, "cleared native commands");
10231049});
1024105010251051it("derives application id from token before probing Discord over REST", async () => {
@@ -1082,8 +1108,9 @@ describe("monitorDiscordProvider", () => {
10821108 setStatus,
10831109});
108411101085-expect(setStatus.mock.calls).toContainEqual([expect.objectContaining({ connected: true })]);
1086-expect(setStatus.mock.calls).toContainEqual([expect.objectContaining({ connected: false })]);
1111+const statuses = setStatus.mock.calls.map((call) => call[0] as { connected?: boolean });
1112+expect(statuses.some((status) => status.connected === true)).toBe(true);
1113+expect(statuses.some((status) => status.connected === false)).toBe(true);
10871114});
1088111510891116it("logs Discord startup phases and early gateway debug events", async () => {
@@ -1104,27 +1131,21 @@ describe("monitorDiscordProvider", () => {
11041131 runtime,
11051132});
110611331107-await vi.waitFor(() =>
1108-expect(vi.mocked(runtime.log).mock.calls.map((call) => String(call[0]))).toEqual(
1109-expect.arrayContaining([expect.stringContaining("deploy-commands:done")]),
1110-),
1111-);
1134+await vi.waitFor(() => expectMockLogContains(runtime.log, "deploy-commands:done"));
1112113511131136const messages = vi.mocked(runtime.log).mock.calls.map((call) => String(call[0]));
1114-expect(messages).toEqual(
1115-expect.arrayContaining([
1116-expect.stringContaining("fetch-application-id:start"),
1117-expect.stringContaining("fetch-application-id:done"),
1118-expect.stringContaining("deploy-commands:schedule"),
1119-expect.stringContaining("deploy-commands:scheduled"),
1120-expect.stringContaining("deploy-commands:done"),
1121-expect.stringContaining("fetch-bot-identity:start"),
1122-expect.stringContaining("fetch-bot-identity:done"),
1123-]),
1124-);
1125-expect(messages).toEqual(
1126-expect.arrayContaining([expect.stringMatching(/gateway-debug.*Gateway websocket opened/)]),
1127-);
1137+expectMessagesContainAll(messages, [
1138+"fetch-application-id:start",
1139+"fetch-application-id:done",
1140+"deploy-commands:schedule",
1141+"deploy-commands:scheduled",
1142+"deploy-commands:done",
1143+"fetch-bot-identity:start",
1144+"fetch-bot-identity:done",
1145+]);
1146+expect(
1147+messages.some((message) => /gateway-debug.*Gateway websocket opened/.test(message)),
1148+).toBe(true);
11281149});
1129115011301151it("keeps Discord startup chatter quiet by default", async () => {
@@ -1136,8 +1157,6 @@ describe("monitorDiscordProvider", () => {
11361157});
1137115811381159const messages = vi.mocked(runtime.log).mock.calls.map((call) => String(call[0]));
1139-expect(messages).not.toEqual(
1140-expect.arrayContaining([expect.stringContaining("discord startup [")]),
1141-);
1160+expect(messages.every((message) => !message.includes("discord startup ["))).toBe(true);
11421161});
11431162});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。