mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-29 10:02:04 +00:00
refactor: dedupe channel outbound and monitor tests
This commit is contained in:
@@ -110,6 +110,18 @@ function setupTransientGetFileRetry() {
|
||||
return getFile;
|
||||
}
|
||||
|
||||
function mockPdfFetchAndSave(fileName: string | undefined) {
|
||||
fetchRemoteMedia.mockResolvedValueOnce({
|
||||
buffer: Buffer.from("pdf-data"),
|
||||
contentType: "application/pdf",
|
||||
fileName,
|
||||
});
|
||||
saveMediaBuffer.mockResolvedValueOnce({
|
||||
path: "/tmp/file_42---uuid.pdf",
|
||||
contentType: "application/pdf",
|
||||
});
|
||||
}
|
||||
|
||||
function createFileTooBigError(): Error {
|
||||
return new Error("GrammyError: Call to 'getFile' failed! (400: Bad Request: file is too big)");
|
||||
}
|
||||
@@ -321,15 +333,7 @@ describe("resolveMedia original filename preservation", () => {
|
||||
|
||||
it("falls back to fetched.fileName when telegram file_name is absent", async () => {
|
||||
const getFile = vi.fn().mockResolvedValue({ file_path: "documents/file_42.pdf" });
|
||||
fetchRemoteMedia.mockResolvedValueOnce({
|
||||
buffer: Buffer.from("pdf-data"),
|
||||
contentType: "application/pdf",
|
||||
fileName: "file_42.pdf",
|
||||
});
|
||||
saveMediaBuffer.mockResolvedValueOnce({
|
||||
path: "/tmp/file_42---uuid.pdf",
|
||||
contentType: "application/pdf",
|
||||
});
|
||||
mockPdfFetchAndSave("file_42.pdf");
|
||||
|
||||
const ctx = makeCtx("document", getFile);
|
||||
const result = await resolveMedia(ctx, MAX_MEDIA_BYTES, BOT_TOKEN);
|
||||
@@ -346,15 +350,7 @@ describe("resolveMedia original filename preservation", () => {
|
||||
|
||||
it("falls back to filePath when neither telegram nor fetched fileName is available", async () => {
|
||||
const getFile = vi.fn().mockResolvedValue({ file_path: "documents/file_42.pdf" });
|
||||
fetchRemoteMedia.mockResolvedValueOnce({
|
||||
buffer: Buffer.from("pdf-data"),
|
||||
contentType: "application/pdf",
|
||||
fileName: undefined,
|
||||
});
|
||||
saveMediaBuffer.mockResolvedValueOnce({
|
||||
path: "/tmp/file_42---uuid.pdf",
|
||||
contentType: "application/pdf",
|
||||
});
|
||||
mockPdfFetchAndSave(undefined);
|
||||
|
||||
const ctx = makeCtx("document", getFile);
|
||||
const result = await resolveMedia(ctx, MAX_MEDIA_BYTES, BOT_TOKEN);
|
||||
|
||||
@@ -44,6 +44,14 @@ async function expectInitialForumSend(
|
||||
);
|
||||
}
|
||||
|
||||
function expectDmMessagePreviewViaSendMessage(
|
||||
api: ReturnType<typeof createMockDraftApi>,
|
||||
text = "Hello",
|
||||
): void {
|
||||
expect(api.sendMessage).toHaveBeenCalledWith(123, text, { message_thread_id: 42 });
|
||||
expect(api.editMessageText).not.toHaveBeenCalled();
|
||||
}
|
||||
|
||||
function createForceNewMessageHarness(params: { throttleMs?: number } = {}) {
|
||||
const api = createMockDraftApi();
|
||||
api.sendMessage
|
||||
@@ -135,9 +143,8 @@ describe("createTelegramDraftStream", () => {
|
||||
stream.update("Hello");
|
||||
await stream.flush();
|
||||
|
||||
expect(api.sendMessage).toHaveBeenCalledWith(123, "Hello", { message_thread_id: 42 });
|
||||
expectDmMessagePreviewViaSendMessage(api);
|
||||
expect(api.sendMessageDraft).not.toHaveBeenCalled();
|
||||
expect(api.editMessageText).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("falls back to message transport when sendMessageDraft is unavailable", async () => {
|
||||
@@ -153,8 +160,7 @@ describe("createTelegramDraftStream", () => {
|
||||
stream.update("Hello");
|
||||
await stream.flush();
|
||||
|
||||
expect(api.sendMessage).toHaveBeenCalledWith(123, "Hello", { message_thread_id: 42 });
|
||||
expect(api.editMessageText).not.toHaveBeenCalled();
|
||||
expectDmMessagePreviewViaSendMessage(api);
|
||||
expect(warn).toHaveBeenCalledWith(
|
||||
"telegram stream preview: sendMessageDraft unavailable; falling back to sendMessage/editMessageText",
|
||||
);
|
||||
@@ -392,6 +398,14 @@ describe("draft stream initial message debounce", () => {
|
||||
deleteMessage: vi.fn().mockResolvedValue(true),
|
||||
});
|
||||
|
||||
function createDebouncedStream(api: ReturnType<typeof createMockApi>, minInitialChars = 30) {
|
||||
return createTelegramDraftStream({
|
||||
api: api as unknown as Bot["api"],
|
||||
chatId: 123,
|
||||
minInitialChars,
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
@@ -403,11 +417,7 @@ describe("draft stream initial message debounce", () => {
|
||||
describe("isFinal has highest priority", () => {
|
||||
it("sends immediately on stop() even with 1 character", async () => {
|
||||
const api = createMockApi();
|
||||
const stream = createTelegramDraftStream({
|
||||
api: api as unknown as Bot["api"],
|
||||
chatId: 123,
|
||||
minInitialChars: 30,
|
||||
});
|
||||
const stream = createDebouncedStream(api);
|
||||
|
||||
stream.update("Y");
|
||||
await stream.stop();
|
||||
@@ -418,11 +428,7 @@ describe("draft stream initial message debounce", () => {
|
||||
|
||||
it("sends immediately on stop() with short sentence", async () => {
|
||||
const api = createMockApi();
|
||||
const stream = createTelegramDraftStream({
|
||||
api: api as unknown as Bot["api"],
|
||||
chatId: 123,
|
||||
minInitialChars: 30,
|
||||
});
|
||||
const stream = createDebouncedStream(api);
|
||||
|
||||
stream.update("Ok.");
|
||||
await stream.stop();
|
||||
@@ -435,11 +441,7 @@ describe("draft stream initial message debounce", () => {
|
||||
describe("minInitialChars threshold", () => {
|
||||
it("does not send first message below threshold", async () => {
|
||||
const api = createMockApi();
|
||||
const stream = createTelegramDraftStream({
|
||||
api: api as unknown as Bot["api"],
|
||||
chatId: 123,
|
||||
minInitialChars: 30,
|
||||
});
|
||||
const stream = createDebouncedStream(api);
|
||||
|
||||
stream.update("Processing"); // 10 chars, below 30
|
||||
await stream.flush();
|
||||
@@ -449,11 +451,7 @@ describe("draft stream initial message debounce", () => {
|
||||
|
||||
it("sends first message when reaching threshold", async () => {
|
||||
const api = createMockApi();
|
||||
const stream = createTelegramDraftStream({
|
||||
api: api as unknown as Bot["api"],
|
||||
chatId: 123,
|
||||
minInitialChars: 30,
|
||||
});
|
||||
const stream = createDebouncedStream(api);
|
||||
|
||||
// Exactly 30 chars
|
||||
stream.update("I am processing your request..");
|
||||
@@ -464,11 +462,7 @@ describe("draft stream initial message debounce", () => {
|
||||
|
||||
it("works with longer text above threshold", async () => {
|
||||
const api = createMockApi();
|
||||
const stream = createTelegramDraftStream({
|
||||
api: api as unknown as Bot["api"],
|
||||
chatId: 123,
|
||||
minInitialChars: 30,
|
||||
});
|
||||
const stream = createDebouncedStream(api);
|
||||
|
||||
stream.update("I am processing your request, please wait a moment"); // 50 chars
|
||||
await stream.flush();
|
||||
@@ -480,11 +474,7 @@ describe("draft stream initial message debounce", () => {
|
||||
describe("subsequent updates after first message", () => {
|
||||
it("edits normally after first message is sent", async () => {
|
||||
const api = createMockApi();
|
||||
const stream = createTelegramDraftStream({
|
||||
api: api as unknown as Bot["api"],
|
||||
chatId: 123,
|
||||
minInitialChars: 30,
|
||||
});
|
||||
const stream = createDebouncedStream(api);
|
||||
|
||||
// First message at threshold (30 chars)
|
||||
stream.update("I am processing your request..");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { once } from "node:events";
|
||||
import { request } from "node:http";
|
||||
import { request, type IncomingMessage } from "node:http";
|
||||
import { setTimeout as sleep } from "node:timers/promises";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { startTelegramWebhook } from "./webhook.js";
|
||||
@@ -24,6 +24,22 @@ const TELEGRAM_TOKEN = "tok";
|
||||
const TELEGRAM_SECRET = "secret";
|
||||
const TELEGRAM_WEBHOOK_PATH = "/hook";
|
||||
|
||||
function collectResponseBody(
|
||||
res: IncomingMessage,
|
||||
onDone: (payload: { statusCode: number; body: string }) => void,
|
||||
): void {
|
||||
const chunks: Buffer[] = [];
|
||||
res.on("data", (chunk: Buffer | string) => {
|
||||
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
||||
});
|
||||
res.on("end", () => {
|
||||
onDone({
|
||||
statusCode: res.statusCode ?? 0,
|
||||
body: Buffer.concat(chunks).toString("utf-8"),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
vi.mock("grammy", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("grammy")>();
|
||||
return {
|
||||
@@ -124,16 +140,7 @@ async function postWebhookPayloadWithChunkPlan(params: {
|
||||
},
|
||||
},
|
||||
(res) => {
|
||||
const chunks: Buffer[] = [];
|
||||
res.on("data", (chunk: Buffer | string) => {
|
||||
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
||||
});
|
||||
res.on("end", () => {
|
||||
finishResolve({
|
||||
statusCode: res.statusCode ?? 0,
|
||||
body: Buffer.concat(chunks).toString("utf-8"),
|
||||
});
|
||||
});
|
||||
collectResponseBody(res, finishResolve);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -555,16 +562,8 @@ describe("startTelegramWebhook", () => {
|
||||
},
|
||||
},
|
||||
(res) => {
|
||||
const chunks: Buffer[] = [];
|
||||
res.on("data", (chunk: Buffer | string) => {
|
||||
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
||||
});
|
||||
res.on("end", () => {
|
||||
resolve({
|
||||
kind: "response",
|
||||
statusCode: res.statusCode ?? 0,
|
||||
body: Buffer.concat(chunks).toString("utf-8"),
|
||||
});
|
||||
collectResponseBody(res, (payload) => {
|
||||
resolve({ kind: "response", ...payload });
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user