From cc295fb8c9a3e775549838a23fff7dbfb72081bf Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 24 Apr 2026 00:59:44 +0100 Subject: [PATCH] perf: dedupe telegram dispatch tests --- .../telegram/src/bot-message-dispatch.test.ts | 283 +++++++++--------- 1 file changed, 134 insertions(+), 149 deletions(-) diff --git a/extensions/telegram/src/bot-message-dispatch.test.ts b/extensions/telegram/src/bot-message-dispatch.test.ts index 326660185e4..fa96fcdfdfc 100644 --- a/extensions/telegram/src/bot-message-dispatch.test.ts +++ b/extensions/telegram/src/bot-message-dispatch.test.ts @@ -784,27 +784,24 @@ describe("dispatchTelegramMessage draft streaming", () => { ); }); - it.each(["block", "partial"] as const)( - "forces new message when assistant message restarts (%s mode)", - async (streamMode) => { - const draftStream = createDraftStream(999); - createTelegramDraftStream.mockReturnValue(draftStream); - dispatchReplyWithBufferedBlockDispatcher.mockImplementation( - async ({ dispatcherOptions, replyOptions }) => { - await replyOptions?.onPartialReply?.({ text: "First response" }); - await replyOptions?.onAssistantMessageStart?.(); - await replyOptions?.onPartialReply?.({ text: "After tool call" }); - await dispatcherOptions.deliver({ text: "After tool call" }, { kind: "final" }); - return { queuedFinal: true }; - }, - ); - deliverReplies.mockResolvedValue({ delivered: true }); + it("forces new message when assistant message restarts", async () => { + const draftStream = createDraftStream(999); + createTelegramDraftStream.mockReturnValue(draftStream); + dispatchReplyWithBufferedBlockDispatcher.mockImplementation( + async ({ dispatcherOptions, replyOptions }) => { + await replyOptions?.onPartialReply?.({ text: "First response" }); + await replyOptions?.onAssistantMessageStart?.(); + await replyOptions?.onPartialReply?.({ text: "After tool call" }); + await dispatcherOptions.deliver({ text: "After tool call" }, { kind: "final" }); + return { queuedFinal: true }; + }, + ); + deliverReplies.mockResolvedValue({ delivered: true }); - await dispatchWithContext({ context: createContext(), streamMode }); + await dispatchWithContext({ context: createContext(), streamMode: "partial" }); - expect(draftStream.forceNewMessage).toHaveBeenCalledTimes(1); - }, - ); + expect(draftStream.forceNewMessage).toHaveBeenCalledTimes(1); + }); it("materializes boundary preview and keeps it when no matching final arrives", async () => { const answerDraftStream = createDraftStream(999); @@ -1758,51 +1755,48 @@ describe("dispatchTelegramMessage draft streaming", () => { expect(answerDraftStream.clear).toHaveBeenCalledTimes(1); }); - it.each(["partial", "block"] as const)( - "keeps finalized text preview when the next assistant message is media-only (%s mode)", - async (streamMode) => { - let answerMessageId: number | undefined = 1001; - const answerDraftStream = { - update: vi.fn(), - flush: vi.fn().mockResolvedValue(undefined), - messageId: vi.fn().mockImplementation(() => answerMessageId), - clear: vi.fn().mockResolvedValue(undefined), - stop: vi.fn().mockResolvedValue(undefined), - forceNewMessage: vi.fn().mockImplementation(() => { - answerMessageId = undefined; - }), - }; - const reasoningDraftStream = createDraftStream(); - createTelegramDraftStream - .mockImplementationOnce(() => answerDraftStream) - .mockImplementationOnce(() => reasoningDraftStream); - dispatchReplyWithBufferedBlockDispatcher.mockImplementation( - async ({ dispatcherOptions, replyOptions }) => { - await replyOptions?.onPartialReply?.({ text: "First message preview" }); - await dispatcherOptions.deliver({ text: "First message final" }, { kind: "final" }); - await replyOptions?.onAssistantMessageStart?.(); - await dispatcherOptions.deliver({ mediaUrl: "file:///tmp/voice.ogg" }, { kind: "final" }); - return { queuedFinal: true }; - }, - ); - deliverReplies.mockResolvedValue({ delivered: true }); - editMessageTelegram.mockResolvedValue({ ok: true, chatId: "123", messageId: "1001" }); - const bot = createBot(); + it("keeps finalized text preview when the next assistant message is media-only", async () => { + let answerMessageId: number | undefined = 1001; + const answerDraftStream = { + update: vi.fn(), + flush: vi.fn().mockResolvedValue(undefined), + messageId: vi.fn().mockImplementation(() => answerMessageId), + clear: vi.fn().mockResolvedValue(undefined), + stop: vi.fn().mockResolvedValue(undefined), + forceNewMessage: vi.fn().mockImplementation(() => { + answerMessageId = undefined; + }), + }; + const reasoningDraftStream = createDraftStream(); + createTelegramDraftStream + .mockImplementationOnce(() => answerDraftStream) + .mockImplementationOnce(() => reasoningDraftStream); + dispatchReplyWithBufferedBlockDispatcher.mockImplementation( + async ({ dispatcherOptions, replyOptions }) => { + await replyOptions?.onPartialReply?.({ text: "First message preview" }); + await dispatcherOptions.deliver({ text: "First message final" }, { kind: "final" }); + await replyOptions?.onAssistantMessageStart?.(); + await dispatcherOptions.deliver({ mediaUrl: "file:///tmp/voice.ogg" }, { kind: "final" }); + return { queuedFinal: true }; + }, + ); + deliverReplies.mockResolvedValue({ delivered: true }); + editMessageTelegram.mockResolvedValue({ ok: true, chatId: "123", messageId: "1001" }); + const bot = createBot(); - await dispatchWithContext({ context: createContext(), streamMode, bot }); + await dispatchWithContext({ context: createContext(), streamMode: "partial", bot }); - expect(editMessageTelegram).toHaveBeenCalledWith( - 123, - 1001, - "First message final", - expect.any(Object), - ); - const deleteMessageCalls = ( - bot.api as unknown as { deleteMessage: { mock: { calls: unknown[][] } } } - ).deleteMessage.mock.calls; - expect(deleteMessageCalls).not.toContainEqual([123, 1001]); - }, - ); + expect(editMessageTelegram).toHaveBeenCalledWith( + 123, + 1001, + "First message final", + expect.any(Object), + ); + const deleteMessageCalls = ( + bot.api as unknown as { deleteMessage: { mock: { calls: unknown[][] } } } + ).deleteMessage.mock.calls; + expect(deleteMessageCalls).not.toContainEqual([123, 1001]); + }); it("maps finals correctly when archived preview id arrives during final flush", async () => { let answerMessageId: number | undefined; @@ -1873,32 +1867,29 @@ describe("dispatchTelegramMessage draft streaming", () => { expect(deliverReplies).not.toHaveBeenCalled(); }); - it.each(["block", "partial"] as const)( - "splits reasoning lane only when a later reasoning block starts (%s mode)", - async (streamMode) => { - const { reasoningDraftStream } = setupDraftStreams({ - answerMessageId: 999, - reasoningMessageId: 111, - }); - dispatchReplyWithBufferedBlockDispatcher.mockImplementation( - async ({ dispatcherOptions, replyOptions }) => { - await replyOptions?.onReasoningStream?.({ text: "Reasoning:\n_first block_" }); - await replyOptions?.onReasoningEnd?.(); - expect(reasoningDraftStream.forceNewMessage).not.toHaveBeenCalled(); - await replyOptions?.onPartialReply?.({ text: "checking files..." }); - await replyOptions?.onReasoningStream?.({ text: "Reasoning:\n_second block_" }); - await dispatcherOptions.deliver({ text: "Done" }, { kind: "final" }); - return { queuedFinal: true }; - }, - ); - deliverReplies.mockResolvedValue({ delivered: true }); - editMessageTelegram.mockResolvedValue({ ok: true, chatId: "123", messageId: "999" }); + it("splits reasoning lane only when a later reasoning block starts", async () => { + const { reasoningDraftStream } = setupDraftStreams({ + answerMessageId: 999, + reasoningMessageId: 111, + }); + dispatchReplyWithBufferedBlockDispatcher.mockImplementation( + async ({ dispatcherOptions, replyOptions }) => { + await replyOptions?.onReasoningStream?.({ text: "Reasoning:\n_first block_" }); + await replyOptions?.onReasoningEnd?.(); + expect(reasoningDraftStream.forceNewMessage).not.toHaveBeenCalled(); + await replyOptions?.onPartialReply?.({ text: "checking files..." }); + await replyOptions?.onReasoningStream?.({ text: "Reasoning:\n_second block_" }); + await dispatcherOptions.deliver({ text: "Done" }, { kind: "final" }); + return { queuedFinal: true }; + }, + ); + deliverReplies.mockResolvedValue({ delivered: true }); + editMessageTelegram.mockResolvedValue({ ok: true, chatId: "123", messageId: "999" }); - await dispatchWithContext({ context: createReasoningStreamContext(), streamMode }); + await dispatchWithContext({ context: createReasoningStreamContext(), streamMode: "partial" }); - expect(reasoningDraftStream.forceNewMessage).toHaveBeenCalledTimes(1); - }, - ); + expect(reasoningDraftStream.forceNewMessage).toHaveBeenCalledTimes(1); + }); it("queues reasoning-end split decisions behind queued reasoning deltas", async () => { const { reasoningDraftStream } = setupDraftStreams({ @@ -1970,29 +1961,26 @@ describe("dispatchTelegramMessage draft streaming", () => { expect(deleteMessageCalls).toContainEqual([123, 4444]); }); - it.each(["block", "partial"] as const)( - "does not split reasoning lane on reasoning end without a later reasoning block (%s mode)", - async (streamMode) => { - const { reasoningDraftStream } = setupDraftStreams({ - answerMessageId: 999, - reasoningMessageId: 111, - }); - dispatchReplyWithBufferedBlockDispatcher.mockImplementation( - async ({ dispatcherOptions, replyOptions }) => { - await replyOptions?.onReasoningStream?.({ text: "Reasoning:\n_first block_" }); - await replyOptions?.onReasoningEnd?.(); - await replyOptions?.onPartialReply?.({ text: "Here's the answer" }); - await dispatcherOptions.deliver({ text: "Here's the answer" }, { kind: "final" }); - return { queuedFinal: true }; - }, - ); - deliverReplies.mockResolvedValue({ delivered: true }); + it("does not split reasoning lane on reasoning end without a later reasoning block", async () => { + const { reasoningDraftStream } = setupDraftStreams({ + answerMessageId: 999, + reasoningMessageId: 111, + }); + dispatchReplyWithBufferedBlockDispatcher.mockImplementation( + async ({ dispatcherOptions, replyOptions }) => { + await replyOptions?.onReasoningStream?.({ text: "Reasoning:\n_first block_" }); + await replyOptions?.onReasoningEnd?.(); + await replyOptions?.onPartialReply?.({ text: "Here's the answer" }); + await dispatcherOptions.deliver({ text: "Here's the answer" }, { kind: "final" }); + return { queuedFinal: true }; + }, + ); + deliverReplies.mockResolvedValue({ delivered: true }); - await dispatchWithContext({ context: createReasoningStreamContext(), streamMode }); + await dispatchWithContext({ context: createReasoningStreamContext(), streamMode: "partial" }); - expect(reasoningDraftStream.forceNewMessage).not.toHaveBeenCalled(); - }, - ); + expect(reasoningDraftStream.forceNewMessage).not.toHaveBeenCalled(); + }); it("suppresses reasoning-only final payloads when reasoning level is off", async () => { setupDraftStreams({ answerMessageId: 999 }); @@ -2175,50 +2163,47 @@ describe("dispatchTelegramMessage draft streaming", () => { ); }); - it.each(["partial", "block"] as const)( - "does not duplicate reasoning final after reasoning end (%s mode)", - async (streamMode) => { - let reasoningMessageId: number | undefined = 111; - const reasoningDraftStream = { - update: vi.fn(), - flush: vi.fn().mockResolvedValue(undefined), - messageId: vi.fn().mockImplementation(() => reasoningMessageId), - clear: vi.fn().mockResolvedValue(undefined), - stop: vi.fn().mockResolvedValue(undefined), - forceNewMessage: vi.fn().mockImplementation(() => { - reasoningMessageId = undefined; - }), - }; - const answerDraftStream = createDraftStream(999); - createTelegramDraftStream - .mockImplementationOnce(() => answerDraftStream) - .mockImplementationOnce(() => reasoningDraftStream); - dispatchReplyWithBufferedBlockDispatcher.mockImplementation( - async ({ dispatcherOptions, replyOptions }) => { - await replyOptions?.onReasoningStream?.({ text: "Reasoning:\n_step one_" }); - await replyOptions?.onReasoningEnd?.(); - await dispatcherOptions.deliver( - { text: "Reasoning:\n_step one expanded_" }, - { kind: "final" }, - ); - return { queuedFinal: true }; - }, - ); - deliverReplies.mockResolvedValue({ delivered: true }); - editMessageTelegram.mockResolvedValue({ ok: true, chatId: "123", messageId: "111" }); + it("does not duplicate reasoning final after reasoning end", async () => { + let reasoningMessageId: number | undefined = 111; + const reasoningDraftStream = { + update: vi.fn(), + flush: vi.fn().mockResolvedValue(undefined), + messageId: vi.fn().mockImplementation(() => reasoningMessageId), + clear: vi.fn().mockResolvedValue(undefined), + stop: vi.fn().mockResolvedValue(undefined), + forceNewMessage: vi.fn().mockImplementation(() => { + reasoningMessageId = undefined; + }), + }; + const answerDraftStream = createDraftStream(999); + createTelegramDraftStream + .mockImplementationOnce(() => answerDraftStream) + .mockImplementationOnce(() => reasoningDraftStream); + dispatchReplyWithBufferedBlockDispatcher.mockImplementation( + async ({ dispatcherOptions, replyOptions }) => { + await replyOptions?.onReasoningStream?.({ text: "Reasoning:\n_step one_" }); + await replyOptions?.onReasoningEnd?.(); + await dispatcherOptions.deliver( + { text: "Reasoning:\n_step one expanded_" }, + { kind: "final" }, + ); + return { queuedFinal: true }; + }, + ); + deliverReplies.mockResolvedValue({ delivered: true }); + editMessageTelegram.mockResolvedValue({ ok: true, chatId: "123", messageId: "111" }); - await dispatchWithContext({ context: createReasoningStreamContext(), streamMode }); + await dispatchWithContext({ context: createReasoningStreamContext(), streamMode: "partial" }); - expect(reasoningDraftStream.forceNewMessage).not.toHaveBeenCalled(); - expect(editMessageTelegram).toHaveBeenCalledWith( - 123, - 111, - "Reasoning:\n_step one expanded_", - expect.any(Object), - ); - expect(deliverReplies).not.toHaveBeenCalled(); - }, - ); + expect(reasoningDraftStream.forceNewMessage).not.toHaveBeenCalled(); + expect(editMessageTelegram).toHaveBeenCalledWith( + 123, + 111, + "Reasoning:\n_step one expanded_", + expect.any(Object), + ); + expect(deliverReplies).not.toHaveBeenCalled(); + }); it("updates reasoning preview for reasoning block payloads instead of sending duplicates", async () => { setupDraftStreams({ answerMessageId: 999, reasoningMessageId: 111 });