Files
openclaw/test/scripts/channel-message-flows.test.ts
2026-06-01 17:37:15 +02:00

246 lines
7.1 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it, vi } from "vitest";
import {
parseChannelMessageFlowArgs,
resolveTelegramFlowThreadSpec,
runTelegramThinkingFinalFlow,
runTelegramWorkingFinalFlow,
} from "../../scripts/dev/channel-message-flows.ts";
import type { OpenClawConfig } from "../../src/config/types.openclaw.js";
describe("channel message flows dev runner", () => {
it("parses the Telegram thinking-final flow from channel/target flags", () => {
const parsed = parseChannelMessageFlowArgs([
"--channel",
"telegram",
"--target",
"123",
"--flow",
"thinking-final",
"--account",
"sut",
"--thread-id",
"42",
"--delay-ms",
"0",
]);
expect(parsed).toEqual({
accountId: "sut",
channel: "telegram",
delayMs: 0,
flow: "thinking-final",
target: "123",
threadId: 42,
});
});
it("parses the Telegram working-final flow from channel/chat flags", () => {
const parsed = parseChannelMessageFlowArgs([
"--channel",
"telegram",
"--chat",
"123",
"--flow",
"working-final",
"--duration-ms",
"12000",
"--delay-ms",
"0",
]);
expect(parsed).toEqual({
channel: "telegram",
delayMs: 0,
durationMs: 12000,
flow: "working-final",
target: "123",
});
});
it("streams thinking updates, clears the preview, then sends the final answer", async () => {
const events: string[] = [];
const stream = {
update: vi.fn((text: string) => {
events.push(`update:${text}`);
}),
flush: vi.fn(async () => {
events.push("flush");
}),
clear: vi.fn(async () => {
events.push("clear");
}),
stop: vi.fn(async () => {}),
messageId: vi.fn(() => 17),
forceNewMessage: vi.fn(),
};
const sendFinal = vi.fn(async () => {
events.push("final");
return { messageId: "99", chatId: "123" };
});
const result = await runTelegramThinkingFinalFlow(
{
cfg: {} as OpenClawConfig,
delayMs: 0,
target: "123",
thinkingUpdates: ["Checking the request.", "Reading the Telegram code.", "Ready."],
},
{
createDraftStream: vi.fn(() => stream),
sendFinal,
sleep: vi.fn(async () => {}),
},
);
expect(stream.update).toHaveBeenCalledTimes(3);
expect(stream.update.mock.calls[0]?.[0]).toContain("Thinking");
expect(stream.update.mock.calls[0]?.[0]).toContain("_Checking the request._");
expect(events.at(-2)).toBe("clear");
expect(events.at(-1)).toBe("final");
expect(sendFinal).toHaveBeenCalledWith({
accountId: undefined,
cfg: {},
target: "123",
text: "Final answer: the Telegram thinking preview cleared and this durable reply landed.",
threadId: undefined,
});
expect(result).toEqual({ finalMessageId: "99", previewUpdates: 3 });
});
it("clears thinking previews when streaming fails before the final answer", async () => {
const stream = {
update: vi.fn(() => {}),
flush: vi.fn(async () => {
throw new Error("flush failed");
}),
clear: vi.fn(async () => {}),
stop: vi.fn(async () => {}),
messageId: vi.fn(() => 17),
forceNewMessage: vi.fn(),
};
const sendFinal = vi.fn(async () => ({ messageId: "99", chatId: "123" }));
await expect(
runTelegramThinkingFinalFlow(
{
cfg: {} as OpenClawConfig,
delayMs: 0,
target: "123",
thinkingUpdates: ["Checking the request."],
},
{
createDraftStream: vi.fn(() => stream),
sendFinal,
sleep: vi.fn(async () => {}),
},
),
).rejects.toThrow("flush failed");
expect(stream.clear).toHaveBeenCalledOnce();
expect(sendFinal).not.toHaveBeenCalled();
});
it("streams working updates through native message drafts before the final answer", async () => {
const draft = {
update: vi.fn(async () => true),
stop: vi.fn(async () => {}),
};
const sendFinal = vi.fn(async () => ({ messageId: "100", chatId: "123" }));
const result = await runTelegramWorkingFinalFlow(
{
cfg: {} as OpenClawConfig,
delayMs: 0,
durationMs: 12_000,
target: "123",
},
{
createNativeToolProgressDraft: vi.fn(() => draft),
sendFinal,
sleep: vi.fn(async () => {}),
},
);
expect(draft.update).toHaveBeenNthCalledWith(1, "Working");
expect(draft.update.mock.calls[2]?.[0]).toContain("🛠️ pgrep -fl Discord || true (agent)");
expect(draft.update.mock.calls[2]?.[0]).toContain(
"🛠️ list files in /Applications/Discord.app -> run true (agent)",
);
expect(draft.update.mock.calls[4]?.[0]).toContain(
"• Discord is installed as a normal '/Applications/Discord.app'",
);
expect(draft.update).toHaveBeenCalledWith(
expect.stringContaining("Working\n\n🛠 pgrep -fl Discord || true (agent)"),
);
expect(draft.stop).toHaveBeenCalledBefore(sendFinal);
expect(sendFinal).toHaveBeenCalledWith({
accountId: undefined,
cfg: {},
target: "123",
text: "Final answer: the Telegram working preview cleared and this durable reply landed.",
threadId: undefined,
});
expect(draft.update).not.toHaveBeenCalledWith(expect.stringContaining("Working for"));
expect(result).toEqual({ finalMessageId: "100", previewUpdates: 6 });
});
it("stops native working drafts when progress updates fail before the final answer", async () => {
const draft = {
update: vi.fn(async () => {
throw new Error("draft update failed");
}),
stop: vi.fn(async () => {}),
};
const sendFinal = vi.fn(async () => ({ messageId: "100", chatId: "123" }));
await expect(
runTelegramWorkingFinalFlow(
{
cfg: {} as OpenClawConfig,
delayMs: 0,
durationMs: 12_000,
target: "123",
},
{
createNativeToolProgressDraft: vi.fn(() => draft),
sendFinal,
sleep: vi.fn(async () => {}),
},
),
).rejects.toThrow("draft update failed");
expect(draft.stop).toHaveBeenCalledOnce();
expect(sendFinal).not.toHaveBeenCalled();
});
it("uses two second progress update cadence by default", async () => {
const draft = {
update: vi.fn(async () => true),
stop: vi.fn(async () => {}),
};
const sleep = vi.fn(async () => {});
const result = await runTelegramWorkingFinalFlow(
{
cfg: {} as OpenClawConfig,
durationMs: 20_000,
target: "123",
},
{
createNativeToolProgressDraft: vi.fn(() => draft),
sendFinal: vi.fn(async () => ({ messageId: "101", chatId: "123" })),
sleep,
},
);
expect(sleep).toHaveBeenCalledTimes(9);
expect(sleep).toHaveBeenCalledWith(2_000);
expect(result.previewUpdates).toBe(7);
});
it("maps flow thread ids to Telegram forum topic specs", () => {
expect(resolveTelegramFlowThreadSpec(42)).toEqual({ id: 42, scope: "forum" });
expect(resolveTelegramFlowThreadSpec()).toBeUndefined();
});
});