



















@@ -106,10 +106,29 @@ function createChannelPlugin(
106106};
107107}
108108109-function expectLastDelivery(
110-matcher: Partial<Parameters<(typeof mocks.deliverOutboundPayloads.mock.calls)[number][0]>[0]>,
111-) {
112-expect(mocks.deliverOutboundPayloads).toHaveBeenLastCalledWith(expect.objectContaining(matcher));
109+function lastDelivery() {
110+const call = mocks.deliverOutboundPayloads.mock.calls.at(-1);
111+expect(call).toBeDefined();
112+const delivery = call?.[0];
113+expect(typeof delivery).toBe("object");
114+expect(delivery).not.toBeNull();
115+return delivery as Record<string, unknown>;
116+}
117+118+function expectLastDeliveryFields(fields: Record<string, unknown>) {
119+const delivery = lastDelivery();
120+for (const [key, expected] of Object.entries(fields)) {
121+expect(delivery[key]).toEqual(expected);
122+}
123+}
124+125+function lastDeliveryPayload(index = 0): Record<string, unknown> {
126+const payloads = lastDelivery().payloads;
127+expect(Array.isArray(payloads)).toBe(true);
128+const payload = (payloads as unknown[])[index];
129+expect(typeof payload).toBe("object");
130+expect(payload).not.toBeNull();
131+return payload as Record<string, unknown>;
113132}
114133115134async function expectSlackNoDelivery(
@@ -225,15 +244,11 @@ describe("routeReply", () => {
225244cfg: {} as never,
226245});
227246expect(res.ok).toBe(true);
228-expectLastDelivery({
247+expectLastDeliveryFields({
229248channel: "slack",
230249to: "channel:C123",
231-payloads: [
232-expect.objectContaining({
233-text: `${SILENT_REPLY_TOKEN} -- (why am I here?)`,
234-}),
235-],
236250});
251+expect(lastDeliveryPayload().text).toBe(`${SILENT_REPLY_TOKEN} -- (why am I here?)`);
237252});
238253239254it("passes policySessionKey through to outbound delivery targets", async () => {
@@ -263,15 +278,11 @@ describe("routeReply", () => {
263278});
264279265280expect(res.ok).toBe(true);
266-expectLastDelivery({
267-payloads: [expect.objectContaining({ text: "native command response" })],
268-session: expect.objectContaining({
269-key: "agent:main:main",
270-policyKey: "agent:main:direct:U123",
271-}),
272-});
273-const session = mocks.deliverOutboundPayloads.mock.calls.at(-1)?.[0]?.session;
274-expect(session).not.toEqual(expect.objectContaining({ conversationType: "group" }));
281+expect(lastDeliveryPayload().text).toBe("native command response");
282+const session = lastDelivery().session as Record<string, unknown>;
283+expect(session.key).toBe("agent:main:main");
284+expect(session.policyKey).toBe("agent:main:direct:U123");
285+expect(session.conversationType).toBeUndefined();
275286});
276287277288it("uses explicit policy conversation type to preserve routed direct silent replies", async () => {
@@ -300,14 +311,11 @@ describe("routeReply", () => {
300311});
301312302313expect(res.ok).toBe(true);
303-expectLastDelivery({
304-payloads: [expect.objectContaining({ text: SILENT_REPLY_TOKEN })],
305-session: expect.objectContaining({
306-key: "agent:main:main",
307-policyKey: "agent:main:main",
308-conversationType: "direct",
309-}),
310-});
314+expect(lastDeliveryPayload().text).toBe(SILENT_REPLY_TOKEN);
315+const session = lastDelivery().session as Record<string, unknown>;
316+expect(session.key).toBe("agent:main:main");
317+expect(session.policyKey).toBe("agent:main:main");
318+expect(session.conversationType).toBe("direct");
311319});
312320313321it("applies responsePrefix when routing", async () => {
@@ -320,9 +328,7 @@ describe("routeReply", () => {
320328to: "channel:C123",
321329 cfg,
322330});
323-expectLastDelivery({
324-payloads: [expect.objectContaining({ text: "[openclaw] hi" })],
325-});
331+expect(lastDeliveryPayload().text).toBe("[openclaw] hi");
326332});
327333328334it("routes directive-only Slack replies when interactive replies are enabled", async () => {
@@ -339,13 +345,7 @@ describe("routeReply", () => {
339345to: "channel:C123",
340346 cfg,
341347});
342-expectLastDelivery({
343-payloads: [
344-expect.objectContaining({
345-text: "[[slack_select: Choose one | Alpha:alpha]]",
346-}),
347-],
348-});
348+expect(lastDeliveryPayload().text).toBe("[[slack_select: Choose one | Alpha:alpha]]");
349349});
350350351351it("does not bypass the empty-reply guard for invalid Slack blocks", async () => {
@@ -378,9 +378,7 @@ describe("routeReply", () => {
378378sessionKey: "agent:rich:main",
379379 cfg,
380380});
381-expectLastDelivery({
382-payloads: [expect.objectContaining({ text: "hi" })],
383-});
381+expect(lastDeliveryPayload().text).toBe("hi");
384382});
385383386384it("uses threadId for Slack when replyToId is missing", async () => {
@@ -391,7 +389,7 @@ describe("routeReply", () => {
391389threadId: "456.789",
392390cfg: {} as never,
393391});
394-expectLastDelivery({
392+expectLastDeliveryFields({
395393channel: "slack",
396394replyToId: "456.789",
397395threadId: null,
@@ -406,7 +404,7 @@ describe("routeReply", () => {
406404threadId: 42,
407405cfg: {} as never,
408406});
409-expectLastDelivery({
407+expectLastDeliveryFields({
410408channel: "telegram",
411409to: "telegram:123",
412410threadId: 42,
@@ -420,10 +418,10 @@ describe("routeReply", () => {
420418to: "channel:C123",
421419cfg: {} as never,
422420});
423-expectLastDelivery({
421+expectLastDeliveryFields({
424422channel: "slack",
425-payloads: [expect.objectContaining({ text: "BTW\nQuestion: what is 17 * 19?\n\n323" })],
426423});
424+expect(lastDeliveryPayload().text).toBe("BTW\nQuestion: what is 17 * 19?\n\n323");
427425});
428426429427it("formats BTW replies prominently on routed discord sends", async () => {
@@ -433,10 +431,10 @@ describe("routeReply", () => {
433431to: "channel:123456",
434432cfg: {} as never,
435433});
436-expectLastDelivery({
434+expectLastDeliveryFields({
437435channel: "discord",
438-payloads: [expect.objectContaining({ text: "BTW\nQuestion: what is 17 * 19?\n\n323" })],
439436});
437+expect(lastDeliveryPayload().text).toBe("BTW\nQuestion: what is 17 * 19?\n\n323");
440438});
441439442440it("passes replyToId to Telegram sends", async () => {
@@ -446,7 +444,7 @@ describe("routeReply", () => {
446444to: "telegram:123",
447445cfg: {} as never,
448446});
449-expectLastDelivery({
447+expectLastDeliveryFields({
450448channel: "telegram",
451449to: "telegram:123",
452450replyToId: "123",
@@ -461,17 +459,13 @@ describe("routeReply", () => {
461459cfg: {} as never,
462460});
463461expect(mocks.deliverOutboundPayloads).toHaveBeenCalledTimes(1);
464-expectLastDelivery({
462+expectLastDeliveryFields({
465463channel: "slack",
466464to: "channel:C123",
467-payloads: [
468-expect.objectContaining({
469-text: "voice caption",
470-mediaUrl: "file:///tmp/clip.mp3",
471-audioAsVoice: true,
472-}),
473-],
474465});
466+expect(lastDeliveryPayload().text).toBe("voice caption");
467+expect(lastDeliveryPayload().mediaUrl).toBe("file:///tmp/clip.mp3");
468+expect(lastDeliveryPayload().audioAsVoice).toBe(true);
475469});
476470477471it("uses replyToId as threadTs for Slack", async () => {
@@ -481,7 +475,7 @@ describe("routeReply", () => {
481475to: "channel:C123",
482476cfg: {} as never,
483477});
484-expectLastDelivery({
478+expectLastDeliveryFields({
485479channel: "slack",
486480replyToId: "1710000000.0001",
487481threadId: null,
@@ -496,7 +490,7 @@ describe("routeReply", () => {
496490threadId: "1710000000.9999",
497491cfg: {} as never,
498492});
499-expectLastDelivery({
493+expectLastDeliveryFields({
500494channel: "slack",
501495replyToId: "1710000000.9999",
502496threadId: null,
@@ -511,7 +505,7 @@ describe("routeReply", () => {
511505threadId: "1710000000.9999",
512506cfg: {} as never,
513507});
514-expectLastDelivery({
508+expectLastDeliveryFields({
515509channel: "slack",
516510replyToId: "1710000000.9999",
517511threadId: null,
@@ -534,7 +528,7 @@ describe("routeReply", () => {
534528},
535529} as unknown as OpenClawConfig,
536530});
537-expectLastDelivery({
531+expectLastDeliveryFields({
538532channel: "mattermost",
539533to: "channel:CHAN1",
540534replyToId: "post-root",
@@ -549,15 +543,11 @@ describe("routeReply", () => {
549543to: "channel:C123",
550544cfg: {} as never,
551545});
552-expectLastDelivery({
546+expectLastDeliveryFields({
553547channel: "slack",
554-payloads: [
555-expect.objectContaining({
556-text: "caption",
557-mediaUrls: ["a", "b"],
558-}),
559-],
560548});
549+expect(lastDeliveryPayload().text).toBe("caption");
550+expect(lastDeliveryPayload().mediaUrls).toEqual(["a", "b"]);
561551});
562552563553it("routes WhatsApp with the account id intact", async () => {
@@ -568,7 +558,7 @@ describe("routeReply", () => {
568558accountId: "acc-1",
569559cfg: {} as never,
570560});
571-expectLastDelivery({
561+expectLastDeliveryFields({
572562channel: "whatsapp",
573563to: "+15551234567",
574564accountId: "acc-1",
@@ -589,12 +579,12 @@ describe("routeReply", () => {
589579to: "conversation:19:abc@thread.tacv2",
590580 cfg,
591581});
592-expectLastDelivery({
582+expectLastDeliveryFields({
593583channel: "msteams",
594584to: "conversation:19:abc@thread.tacv2",
595585 cfg,
596-payloads: [expect.objectContaining({ text: "hi" })],
597586});
587+expect(lastDeliveryPayload().text).toBe("hi");
598588});
599589600590it("passes mirror data when sessionKey is set", async () => {
@@ -607,14 +597,11 @@ describe("routeReply", () => {
607597groupId: "channel:C123",
608598cfg: {} as never,
609599});
610-expectLastDelivery({
611-mirror: expect.objectContaining({
612-sessionKey: "agent:main:main",
613-text: "hi",
614-isGroup: true,
615-groupId: "channel:C123",
616-}),
617-});
600+const mirror = lastDelivery().mirror as Record<string, unknown>;
601+expect(mirror.sessionKey).toBe("agent:main:main");
602+expect(mirror.text).toBe("hi");
603+expect(mirror.isGroup).toBe(true);
604+expect(mirror.groupId).toBe("channel:C123");
618605});
619606620607it("skips mirror data when mirror is false", async () => {
@@ -626,7 +613,7 @@ describe("routeReply", () => {
626613mirror: false,
627614cfg: {} as never,
628615});
629-expectLastDelivery({
616+expectLastDeliveryFields({
630617mirror: undefined,
631618});
632619});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。