refactor(test): reduce dispatch-from-config setup duplication

This commit is contained in:
Peter Steinberger
2026-02-16 18:09:49 +00:00
parent 74c49c943d
commit 5d40d47501

View File

@@ -57,6 +57,9 @@ vi.mock("../../plugins/hook-runner-global.js", () => ({
const { dispatchReplyFromConfig } = await import("./dispatch-from-config.js"); const { dispatchReplyFromConfig } = await import("./dispatch-from-config.js");
const { resetInboundDedupe } = await import("./inbound-dedupe.js"); const { resetInboundDedupe } = await import("./inbound-dedupe.js");
const noAbortResult = { handled: false, aborted: false } as const;
const emptyConfig = {} as OpenClawConfig;
function createDispatcher(): ReplyDispatcher { function createDispatcher(): ReplyDispatcher {
return { return {
sendToolResult: vi.fn(() => true), sendToolResult: vi.fn(() => true),
@@ -68,6 +71,16 @@ function createDispatcher(): ReplyDispatcher {
}; };
} }
function setNoAbort() {
mocks.tryFastAbortFromMessage.mockResolvedValue(noAbortResult);
}
function firstToolResultPayload(dispatcher: ReplyDispatcher): ReplyPayload | undefined {
return (dispatcher.sendToolResult as ReturnType<typeof vi.fn>).mock.calls[0]?.[0] as
| ReplyPayload
| undefined;
}
describe("dispatchReplyFromConfig", () => { describe("dispatchReplyFromConfig", () => {
beforeEach(() => { beforeEach(() => {
resetInboundDedupe(); resetInboundDedupe();
@@ -79,12 +92,9 @@ describe("dispatchReplyFromConfig", () => {
hookMocks.runner.runMessageReceived.mockReset(); hookMocks.runner.runMessageReceived.mockReset();
}); });
it("does not route when Provider matches OriginatingChannel (even if Surface is missing)", async () => { it("does not route when Provider matches OriginatingChannel (even if Surface is missing)", async () => {
mocks.tryFastAbortFromMessage.mockResolvedValue({ setNoAbort();
handled: false,
aborted: false,
});
mocks.routeReply.mockClear(); mocks.routeReply.mockClear();
const cfg = {} as OpenClawConfig; const cfg = emptyConfig;
const dispatcher = createDispatcher(); const dispatcher = createDispatcher();
const ctx = buildTestCtx({ const ctx = buildTestCtx({
Provider: "slack", Provider: "slack",
@@ -105,12 +115,9 @@ describe("dispatchReplyFromConfig", () => {
}); });
it("routes when OriginatingChannel differs from Provider", async () => { it("routes when OriginatingChannel differs from Provider", async () => {
mocks.tryFastAbortFromMessage.mockResolvedValue({ setNoAbort();
handled: false,
aborted: false,
});
mocks.routeReply.mockClear(); mocks.routeReply.mockClear();
const cfg = {} as OpenClawConfig; const cfg = emptyConfig;
const dispatcher = createDispatcher(); const dispatcher = createDispatcher();
const ctx = buildTestCtx({ const ctx = buildTestCtx({
Provider: "slack", Provider: "slack",
@@ -139,12 +146,9 @@ describe("dispatchReplyFromConfig", () => {
}); });
it("routes media-only tool results when summaries are suppressed", async () => { it("routes media-only tool results when summaries are suppressed", async () => {
mocks.tryFastAbortFromMessage.mockResolvedValue({ setNoAbort();
handled: false,
aborted: false,
});
mocks.routeReply.mockClear(); mocks.routeReply.mockClear();
const cfg = {} as OpenClawConfig; const cfg = emptyConfig;
const dispatcher = createDispatcher(); const dispatcher = createDispatcher();
const ctx = buildTestCtx({ const ctx = buildTestCtx({
Provider: "slack", Provider: "slack",
@@ -178,12 +182,9 @@ describe("dispatchReplyFromConfig", () => {
}); });
it("provides onToolResult in DM sessions", async () => { it("provides onToolResult in DM sessions", async () => {
mocks.tryFastAbortFromMessage.mockResolvedValue({ setNoAbort();
handled: false,
aborted: false,
});
mocks.routeReply.mockClear(); mocks.routeReply.mockClear();
const cfg = {} as OpenClawConfig; const cfg = emptyConfig;
const dispatcher = createDispatcher(); const dispatcher = createDispatcher();
const ctx = buildTestCtx({ const ctx = buildTestCtx({
Provider: "telegram", Provider: "telegram",
@@ -205,11 +206,8 @@ describe("dispatchReplyFromConfig", () => {
}); });
it("suppresses group tool summaries but still forwards tool media", async () => { it("suppresses group tool summaries but still forwards tool media", async () => {
mocks.tryFastAbortFromMessage.mockResolvedValue({ setNoAbort();
handled: false, const cfg = emptyConfig;
aborted: false,
});
const cfg = {} as OpenClawConfig;
const dispatcher = createDispatcher(); const dispatcher = createDispatcher();
const ctx = buildTestCtx({ const ctx = buildTestCtx({
Provider: "telegram", Provider: "telegram",
@@ -233,20 +231,15 @@ describe("dispatchReplyFromConfig", () => {
await dispatchReplyFromConfig({ ctx, cfg, dispatcher, replyResolver }); await dispatchReplyFromConfig({ ctx, cfg, dispatcher, replyResolver });
expect(dispatcher.sendToolResult).toHaveBeenCalledTimes(1); expect(dispatcher.sendToolResult).toHaveBeenCalledTimes(1);
const sent = (dispatcher.sendToolResult as ReturnType<typeof vi.fn>).mock.calls[0]?.[0] as const sent = firstToolResultPayload(dispatcher);
| ReplyPayload
| undefined;
expect(sent?.mediaUrls).toEqual(["https://example.com/tts-group.opus"]); expect(sent?.mediaUrls).toEqual(["https://example.com/tts-group.opus"]);
expect(sent?.text).toBeUndefined(); expect(sent?.text).toBeUndefined();
expect(dispatcher.sendFinalReply).toHaveBeenCalledTimes(1); expect(dispatcher.sendFinalReply).toHaveBeenCalledTimes(1);
}); });
it("sends tool results via dispatcher in DM sessions", async () => { it("sends tool results via dispatcher in DM sessions", async () => {
mocks.tryFastAbortFromMessage.mockResolvedValue({ setNoAbort();
handled: false, const cfg = emptyConfig;
aborted: false,
});
const cfg = {} as OpenClawConfig;
const dispatcher = createDispatcher(); const dispatcher = createDispatcher();
const ctx = buildTestCtx({ const ctx = buildTestCtx({
Provider: "telegram", Provider: "telegram",
@@ -271,11 +264,8 @@ describe("dispatchReplyFromConfig", () => {
}); });
it("suppresses native tool summaries but still forwards tool media", async () => { it("suppresses native tool summaries but still forwards tool media", async () => {
mocks.tryFastAbortFromMessage.mockResolvedValue({ setNoAbort();
handled: false, const cfg = emptyConfig;
aborted: false,
});
const cfg = {} as OpenClawConfig;
const dispatcher = createDispatcher(); const dispatcher = createDispatcher();
const ctx = buildTestCtx({ const ctx = buildTestCtx({
Provider: "telegram", Provider: "telegram",
@@ -299,9 +289,7 @@ describe("dispatchReplyFromConfig", () => {
await dispatchReplyFromConfig({ ctx, cfg, dispatcher, replyResolver }); await dispatchReplyFromConfig({ ctx, cfg, dispatcher, replyResolver });
expect(dispatcher.sendToolResult).toHaveBeenCalledTimes(1); expect(dispatcher.sendToolResult).toHaveBeenCalledTimes(1);
const sent = (dispatcher.sendToolResult as ReturnType<typeof vi.fn>).mock.calls[0]?.[0] as const sent = firstToolResultPayload(dispatcher);
| ReplyPayload
| undefined;
expect(sent?.mediaUrl).toBe("https://example.com/tts-native.opus"); expect(sent?.mediaUrl).toBe("https://example.com/tts-native.opus");
expect(sent?.text).toBeUndefined(); expect(sent?.text).toBeUndefined();
expect(dispatcher.sendFinalReply).toHaveBeenCalledTimes(1); expect(dispatcher.sendFinalReply).toHaveBeenCalledTimes(1);
@@ -312,7 +300,7 @@ describe("dispatchReplyFromConfig", () => {
handled: true, handled: true,
aborted: true, aborted: true,
}); });
const cfg = {} as OpenClawConfig; const cfg = emptyConfig;
const dispatcher = createDispatcher(); const dispatcher = createDispatcher();
const ctx = buildTestCtx({ const ctx = buildTestCtx({
Provider: "telegram", Provider: "telegram",
@@ -334,7 +322,7 @@ describe("dispatchReplyFromConfig", () => {
aborted: true, aborted: true,
stoppedSubagents: 2, stoppedSubagents: 2,
}); });
const cfg = {} as OpenClawConfig; const cfg = emptyConfig;
const dispatcher = createDispatcher(); const dispatcher = createDispatcher();
const ctx = buildTestCtx({ const ctx = buildTestCtx({
Provider: "telegram", Provider: "telegram",
@@ -354,11 +342,8 @@ describe("dispatchReplyFromConfig", () => {
}); });
it("deduplicates inbound messages by MessageSid and origin", async () => { it("deduplicates inbound messages by MessageSid and origin", async () => {
mocks.tryFastAbortFromMessage.mockResolvedValue({ setNoAbort();
handled: false, const cfg = emptyConfig;
aborted: false,
});
const cfg = {} as OpenClawConfig;
const ctx = buildTestCtx({ const ctx = buildTestCtx({
Provider: "whatsapp", Provider: "whatsapp",
OriginatingChannel: "whatsapp", OriginatingChannel: "whatsapp",
@@ -384,12 +369,9 @@ describe("dispatchReplyFromConfig", () => {
}); });
it("emits message_received hook with originating channel metadata", async () => { it("emits message_received hook with originating channel metadata", async () => {
mocks.tryFastAbortFromMessage.mockResolvedValue({ setNoAbort();
handled: false,
aborted: false,
});
hookMocks.runner.hasHooks.mockReturnValue(true); hookMocks.runner.hasHooks.mockReturnValue(true);
const cfg = {} as OpenClawConfig; const cfg = emptyConfig;
const dispatcher = createDispatcher(); const dispatcher = createDispatcher();
const ctx = buildTestCtx({ const ctx = buildTestCtx({
Provider: "slack", Provider: "slack",
@@ -435,10 +417,7 @@ describe("dispatchReplyFromConfig", () => {
}); });
it("emits diagnostics when enabled", async () => { it("emits diagnostics when enabled", async () => {
mocks.tryFastAbortFromMessage.mockResolvedValue({ setNoAbort();
handled: false,
aborted: false,
});
const cfg = { diagnostics: { enabled: true } } as OpenClawConfig; const cfg = { diagnostics: { enabled: true } } as OpenClawConfig;
const dispatcher = createDispatcher(); const dispatcher = createDispatcher();
const ctx = buildTestCtx({ const ctx = buildTestCtx({
@@ -468,10 +447,7 @@ describe("dispatchReplyFromConfig", () => {
}); });
it("marks diagnostics skipped for duplicate inbound messages", async () => { it("marks diagnostics skipped for duplicate inbound messages", async () => {
mocks.tryFastAbortFromMessage.mockResolvedValue({ setNoAbort();
handled: false,
aborted: false,
});
const cfg = { diagnostics: { enabled: true } } as OpenClawConfig; const cfg = { diagnostics: { enabled: true } } as OpenClawConfig;
const ctx = buildTestCtx({ const ctx = buildTestCtx({
Provider: "whatsapp", Provider: "whatsapp",