mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
247 lines
7.2 KiB
TypeScript
247 lines
7.2 KiB
TypeScript
// Channel Message Flows tests cover channel message flows script behavior.
|
|
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();
|
|
});
|
|
});
|