





















@@ -163,6 +163,60 @@ const createCaseDir = async (prefix: string) => {
163163return dir;
164164};
165165166+function requireRecord(value: unknown, label: string): Record<string, unknown> {
167+if (!value || typeof value !== "object" || Array.isArray(value)) {
168+throw new Error(`expected ${label} to be a record`);
169+}
170+return value as Record<string, unknown>;
171+}
172+173+function expectRecordFields(record: Record<string, unknown>, fields: Record<string, unknown>) {
174+for (const [key, value] of Object.entries(fields)) {
175+expect(record[key]).toEqual(value);
176+}
177+}
178+179+function expectWhatsAppSendCall(
180+sendWhatsApp: ReturnType<typeof vi.fn>,
181+index: number,
182+fields: { to: string; text: string },
183+) {
184+const call = sendWhatsApp.mock.calls[index];
185+if (!call) {
186+throw new Error(`expected WhatsApp send call ${index}`);
187+}
188+expect(call[0]).toBe(fields.to);
189+expect(call[1]).toBe(fields.text);
190+requireRecord(call[2], `WhatsApp send call ${index} options`);
191+}
192+193+function expectReplyCall(
194+replySpy: ReturnType<typeof vi.fn>,
195+index: number,
196+bodyFields: Record<string, unknown>,
197+optionsFields?: Record<string, unknown>,
198+cfg?: OpenClawConfig,
199+) {
200+const call = replySpy.mock.calls[index];
201+if (!call) {
202+throw new Error(`expected reply call ${index}`);
203+}
204+const body = requireRecord(call[0], `reply call ${index} body`);
205+for (const [key, value] of Object.entries(bodyFields)) {
206+if (value instanceof RegExp) {
207+expect(String(body[key])).toMatch(value);
208+} else {
209+expect(body[key]).toEqual(value);
210+}
211+}
212+if (optionsFields) {
213+expectRecordFields(requireRecord(call[1], `reply call ${index} options`), optionsFields);
214+}
215+if (cfg) {
216+expect(call[2]).toBe(cfg);
217+}
218+}
219+166220beforeAll(async () => {
167221previousRegistry = getActivePluginRegistry();
168222@@ -709,11 +763,10 @@ describe("runHeartbeatOnce", () => {
709763});
710764711765expect(sendWhatsApp).toHaveBeenCalledTimes(1);
712-expect(sendWhatsApp).toHaveBeenCalledWith(
713-"120363401234567890@g.us",
714-"Final alert",
715-expect.any(Object),
716-);
766+expectWhatsAppSendCall(sendWhatsApp, 0, {
767+to: "120363401234567890@g.us",
768+text: "Final alert",
769+});
717770} finally {
718771replySpy.mockReset();
719772}
@@ -773,22 +826,23 @@ describe("runHeartbeatOnce", () => {
773826deps: createHeartbeatDeps(sendWhatsApp, { getReplyFromConfig: replySpy }),
774827});
775828expect(sendWhatsApp).toHaveBeenCalledTimes(1);
776-expect(sendWhatsApp).toHaveBeenCalledWith(
777-"120363401234567890@g.us",
778-"Final alert",
779-expect.any(Object),
780-);
781-expect(replySpy).toHaveBeenCalledWith(
782-expect.objectContaining({
829+expectWhatsAppSendCall(sendWhatsApp, 0, {
830+to: "120363401234567890@g.us",
831+text: "Final alert",
832+});
833+expectReplyCall(
834+replySpy,
835+0,
836+{
783837Body: expect.stringMatching(/Ops check[\s\S]*Current time: /),
784838SessionKey: sessionKey,
785839From: "120363401234567890@g.us",
786840To: "120363401234567890@g.us",
787841OriginatingChannel: "whatsapp",
788842OriginatingTo: "120363401234567890@g.us",
789843Provider: "heartbeat",
790-}),
791-expect.objectContaining({ isHeartbeat: true, suppressToolErrorWarnings: false }),
844+},
845+{ isHeartbeat: true, suppressToolErrorWarnings: false },
792846cfg,
793847);
794848} finally {
@@ -861,19 +915,20 @@ describe("runHeartbeatOnce", () => {
861915862916expect(result.status).toBe("ran");
863917expect(sendWhatsApp).toHaveBeenCalledTimes(1);
864-expect(sendWhatsApp).toHaveBeenCalledWith(
865-"120363401234567890@g.us",
866-"Final alert",
867-expect.any(Object),
868-);
869-expect(replySpy).toHaveBeenCalledWith(
870-expect.objectContaining({
918+expectWhatsAppSendCall(sendWhatsApp, 0, {
919+to: "120363401234567890@g.us",
920+text: "Final alert",
921+});
922+expectReplyCall(
923+replySpy,
924+0,
925+{
871926SessionKey: sessionKey,
872927From: "120363401234567890@g.us",
873928To: "120363401234567890@g.us",
874929Provider: "heartbeat",
875-}),
876-expect.objectContaining({ isHeartbeat: true, suppressToolErrorWarnings: false }),
930+},
931+{ isHeartbeat: true, suppressToolErrorWarnings: false },
877932cfg,
878933);
879934} finally {
@@ -973,15 +1028,17 @@ describe("runHeartbeatOnce", () => {
9731028});
97410299751030expect(sendWhatsApp, name).toHaveBeenCalledTimes(1);
976-expect(sendWhatsApp, name).toHaveBeenCalledWith(peerId, message, expect.any(Object));
977-expect(replySpy, name).toHaveBeenCalledWith(
978-expect.objectContaining({
1031+expectWhatsAppSendCall(sendWhatsApp, 0, { to: peerId, text: message });
1032+expectReplyCall(
1033+replySpy,
1034+0,
1035+{
9791036SessionKey: overrideSessionKey,
9801037From: peerId,
9811038To: peerId,
9821039Provider: "heartbeat",
983-}),
984-expect.objectContaining({ isHeartbeat: true, suppressToolErrorWarnings: false }),
1040+},
1041+{ isHeartbeat: true, suppressToolErrorWarnings: false },
9851042cfg,
9861043);
9871044} finally {
@@ -1062,21 +1119,13 @@ describe("runHeartbeatOnce", () => {
10621119});
1063112010641121// The heartbeat must use the main session, not the subagent session.
1065-expect(replySpy).toHaveBeenCalledWith(
1066-expect.objectContaining({
1067-SessionKey: mainSessionKey,
1068-}),
1069-expect.anything(),
1070-expect.anything(),
1071-);
1122+expectReplyCall(replySpy, 0, { SessionKey: mainSessionKey });
10721123// Must NOT use the subagent session key.
1073-expect(replySpy).not.toHaveBeenCalledWith(
1074-expect.objectContaining({
1075-SessionKey: subagentKey,
1076-}),
1077-expect.anything(),
1078-expect.anything(),
1079-);
1124+expect(
1125+replySpy.mock.calls.some(
1126+([body]) => requireRecord(body, "reply body").SessionKey === subagentKey,
1127+),
1128+).toBe(false);
10801129} finally {
10811130replySpy.mockReset();
10821131}
@@ -1219,12 +1268,10 @@ describe("runHeartbeatOnce", () => {
1219126812201269expect(sendWhatsApp, name).toHaveBeenCalledTimes(expectedTexts.length);
12211270for (const [index, text] of expectedTexts.entries()) {
1222-expect(sendWhatsApp, name).toHaveBeenNthCalledWith(
1223-index + 1,
1224-"120363401234567890@g.us",
1271+expectWhatsAppSendCall(sendWhatsApp, index, {
1272+to: "120363401234567890@g.us",
12251273 text,
1226-expect.any(Object),
1227-);
1274+});
12281275}
12291276} finally {
12301277replySpy.mockReset();
@@ -1283,11 +1330,10 @@ describe("runHeartbeatOnce", () => {
12831330});
1284133112851332expect(sendWhatsApp).toHaveBeenCalledTimes(1);
1286-expect(sendWhatsApp).toHaveBeenCalledWith(
1287-"120363401234567890@g.us",
1288-"Hello from heartbeat",
1289-expect.any(Object),
1290-);
1333+expectWhatsAppSendCall(sendWhatsApp, 0, {
1334+to: "120363401234567890@g.us",
1335+text: "Hello from heartbeat",
1336+});
12911337} finally {
12921338replySpy.mockReset();
12931339}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。