mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 17:21:37 +00:00
refactor(test): reduce dispatch-from-config setup duplication
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user