From 7194a89469e237dcdfb17f49d4e4b9d7925aa7c2 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 10 May 2026 14:06:07 +0100 Subject: [PATCH] test: clear telegram session meta broad matchers --- .../bot-native-commands.session-meta.test.ts | 215 ++++++++++++------ 1 file changed, 144 insertions(+), 71 deletions(-) diff --git a/extensions/telegram/src/bot-native-commands.session-meta.test.ts b/extensions/telegram/src/bot-native-commands.session-meta.test.ts index 17822076dc5..88190918c2e 100644 --- a/extensions/telegram/src/bot-native-commands.session-meta.test.ts +++ b/extensions/telegram/src/bot-native-commands.session-meta.test.ts @@ -442,15 +442,73 @@ function createConfiguredBindingRoute( }; } +function requireValue(value: T | null | undefined, label: string): T { + if (value == null) { + throw new Error(`expected ${label}`); + } + return value; +} + +function requireRecord(value: unknown, label: string): Record { + if (typeof value !== "object" || value === null || Array.isArray(value)) { + throw new Error(`expected ${label} to be an object`); + } + return value as Record; +} + +function expectRecordFields( + value: unknown, + expected: Record, + label: string, +): Record { + const record = requireRecord(value, label); + for (const [key, expectedValue] of Object.entries(expected)) { + expect(record[key], `${label}.${key}`).toEqual(expectedValue); + } + return record; +} + +function expectSendMessageCall(params: { + sendMessage: ReturnType; + callIndex?: number; + chatId: unknown; + text?: string; + textIncludes?: string; + optionFields?: Record; + requireReplyMarkup?: boolean; + label: string; +}): Record { + const call = requireValue( + params.sendMessage.mock.calls[params.callIndex ?? 0], + `${params.label} sendMessage call`, + ); + expect(call[0]).toBe(params.chatId); + if (params.text !== undefined) { + expect(call[1]).toBe(params.text); + } + if (params.textIncludes !== undefined) { + expect(String(call[1])).toContain(params.textIncludes); + } + const options = params.optionFields + ? expectRecordFields(call[2], params.optionFields, `${params.label} sendMessage options`) + : requireRecord(call[2], `${params.label} sendMessage options`); + if (params.requireReplyMarkup) { + requireRecord(options.reply_markup, `${params.label} reply markup`); + } + return options; +} + function expectUnauthorizedNewCommandBlocked(sendMessage: ReturnType) { expect(replyMocks.dispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled(); expect(persistentBindingMocks.resolveConfiguredBindingRoute).not.toHaveBeenCalled(); expect(persistentBindingMocks.ensureConfiguredBindingRouteReady).not.toHaveBeenCalled(); - expect(sendMessage).toHaveBeenCalledWith( - -1001234567890, - "You are not authorized to use this command.", - expect.objectContaining({ message_thread_id: 42 }), - ); + expectSendMessageCall({ + sendMessage, + chatId: -1001234567890, + text: "You are not authorized to use this command.", + optionFields: { message_thread_id: 42 }, + label: "unauthorized /new", + }); } describe("registerTelegramNativeCommands — session metadata", () => { @@ -546,18 +604,19 @@ describe("registerTelegramNativeCommands — session metadata", () => { const menuCall = commandAuthMocks.resolveCommandArgMenu.mock.calls.find( ([params]) => params.command.key === "think" && params.provider === "anthropic", )?.[0]; - expect(menuCall).toEqual( - expect.objectContaining({ - provider: "anthropic", - model: "claude-opus-4-7", - }), + expectRecordFields( + menuCall, + { provider: "anthropic", model: "claude-opus-4-7" }, + "thinking menu call", ); expect(sessionMocks.loadSessionStore).toHaveBeenCalledWith("/tmp/openclaw-sessions.json"); - expect(sendMessage).toHaveBeenCalledWith( - 100, - expect.stringContaining("Current thinking level: high.\nChoose level for /think."), - expect.objectContaining({ reply_markup: expect.any(Object) }), - ); + expectSendMessageCall({ + sendMessage, + chatId: 100, + textIncludes: "Current thinking level: high.\nChoose level for /think.", + requireReplyMarkup: true, + label: "thinking menu", + }); expect(replyMocks.dispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled(); }); @@ -582,17 +641,18 @@ describe("registerTelegramNativeCommands — session metadata", () => { const menuCall = commandAuthMocks.resolveCommandArgMenu.mock.calls.find( ([params]) => params.command.key === "think" && params.provider === "anthropic", )?.[0]; - expect(menuCall).toEqual( - expect.objectContaining({ - provider: "anthropic", - model: "claude-opus-4-7", - }), - ); - expect(sendMessage).toHaveBeenCalledWith( - 100, - expect.stringContaining("Choose level for /think."), - expect.objectContaining({ reply_markup: expect.any(Object) }), + expectRecordFields( + menuCall, + { provider: "anthropic", model: "claude-opus-4-7" }, + "thread thinking menu call", ); + expectSendMessageCall({ + sendMessage, + chatId: 100, + textIncludes: "Choose level for /think.", + requireReplyMarkup: true, + label: "thread thinking menu", + }); expect(replyMocks.dispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled(); }); @@ -626,17 +686,18 @@ describe("registerTelegramNativeCommands — session metadata", () => { const menuCall = commandAuthMocks.resolveCommandArgMenu.mock.calls.find( ([params]) => params.command.key === "think" && params.provider === "openai", )?.[0]; - expect(menuCall).toEqual( - expect.objectContaining({ - provider: "openai", - model: "gpt-5.5", - }), - ); - expect(sendMessage).toHaveBeenCalledWith( - 100, - expect.stringContaining("Current thinking level: medium.\nChoose level for /think."), - expect.objectContaining({ reply_markup: expect.any(Object) }), + expectRecordFields( + menuCall, + { provider: "openai", model: "gpt-5.5" }, + "default model thinking menu call", ); + expectSendMessageCall({ + sendMessage, + chatId: 100, + textIncludes: "Current thinking level: medium.\nChoose level for /think.", + requireReplyMarkup: true, + label: "default model thinking menu", + }); expect(replyMocks.dispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled(); }); @@ -669,11 +730,13 @@ describe("registerTelegramNativeCommands — session metadata", () => { }); await handler(createTelegramPrivateCommandContext()); - expect(sendMessage).toHaveBeenCalledWith( - 100, - expect.stringContaining("Current thinking level: xhigh.\nChoose level for /think."), - expect.objectContaining({ reply_markup: expect.any(Object) }), - ); + expectSendMessageCall({ + sendMessage, + chatId: 100, + textIncludes: "Current thinking level: xhigh.\nChoose level for /think.", + requireReplyMarkup: true, + label: "target model thinking menu", + }); expect(replyMocks.dispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled(); }); @@ -706,11 +769,13 @@ describe("registerTelegramNativeCommands — session metadata", () => { }); await handler(createTelegramPrivateCommandContext()); - expect(sendMessage).toHaveBeenCalledWith( - 100, - expect.stringContaining("Current thinking level: minimal.\nChoose level for /think."), - expect.objectContaining({ reply_markup: expect.any(Object) }), - ); + expectSendMessageCall({ + sendMessage, + chatId: 100, + textIncludes: "Current thinking level: minimal.\nChoose level for /think.", + requireReplyMarkup: true, + label: "agent thinking menu", + }); expect(replyMocks.dispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled(); }); @@ -743,9 +808,11 @@ describe("registerTelegramNativeCommands — session metadata", () => { await runPromise; expect(replyMocks.dispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalledTimes(1); - expect( + const dispatcherOptions = requireRecord( replyMocks.dispatchReplyWithBufferedBlockDispatcher.mock.calls[0]?.[0].dispatcherOptions, - ).toEqual(expect.objectContaining({ beforeDeliver: expect.any(Function) })); + "dispatcher options", + ); + expect(dispatcherOptions.beforeDeliver).toBeTypeOf("function"); }); it("does not inject approval buttons for native command replies once the monitor owns approvals", async () => { @@ -848,12 +915,10 @@ describe("registerTelegramNativeCommands — session metadata", () => { const deliveredCall = deliveryMocks.deliverReplies.mock.calls[0]?.[0] as | DeliverRepliesParams | undefined; - expect(deliveredCall).toEqual( - expect.objectContaining({ - silent: true, - replies: [expect.objectContaining({ isError: true })], - }), - ); + const deliveryParams = requireValue(deliveredCall, "silent error delivery params"); + expect(deliveryParams.silent).toBe(true); + expect(deliveryParams.replies).toHaveLength(1); + expect(deliveryParams.replies[0]?.isError).toBe(true); }); it("routes Telegram native commands through configured ACP topic bindings", async () => { @@ -1055,12 +1120,14 @@ describe("registerTelegramNativeCommands — session metadata", () => { ] > )[0]?.[0]; - expect(dispatchCall?.ctx).toEqual( - expect.objectContaining({ + expectRecordFields( + dispatchCall?.ctx, + { CommandTargetSessionKey: "agent:main:telegram:group:-1001234567890:topic:42", MessageThreadId: 42, OriginatingTo: "telegram:-1001234567890:topic:42", - }), + }, + "topic dispatch context", ); }, ); @@ -1091,11 +1158,13 @@ describe("registerTelegramNativeCommands — session metadata", () => { await handler(createTelegramTopicCommandContext()); expect(replyMocks.dispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled(); - expect(sendMessage).toHaveBeenCalledWith( - -1001234567890, - "Configured ACP binding is unavailable right now. Please try again.", - expect.objectContaining({ message_thread_id: 42 }), - ); + expectSendMessageCall({ + sendMessage, + chatId: -1001234567890, + text: "Configured ACP binding is unavailable right now. Please try again.", + optionFields: { message_thread_id: 42 }, + label: "unavailable ACP binding", + }); }); it("keeps /new blocked in ACP-bound Telegram topics when sender is unauthorized", async () => { @@ -1180,22 +1249,26 @@ describe("registerTelegramNativeCommands — session metadata", () => { createTelegramTopicCommandContext({ match: "bind --cwd /tmp/work", threadId: 42 }), ); - expect(sessionMocks.resolveAndPersistSessionFile).toHaveBeenCalledWith( - expect.objectContaining({ + expectRecordFields( + sessionMocks.resolveAndPersistSessionFile.mock.calls[0]?.[0], + { sessionId: "sess-topic", sessionKey: "agent:main:telegram:group:-1001234567890:topic:42", storePath: "/tmp/openclaw-sessions/sessions.json", sessionsDir: "/tmp/openclaw-sessions", fallbackSessionFile: path.resolve("/tmp/openclaw-sessions", "sess-topic-topic-42.jsonl"), - }), + }, + "resolved session file params", ); - expect(pluginRuntimeMocks.executePluginCommand).toHaveBeenCalledWith( - expect.objectContaining({ + expectRecordFields( + (pluginRuntimeMocks.executePluginCommand.mock.calls as unknown as Array<[unknown]>)[0]?.[0], + { sessionKey: "agent:main:telegram:group:-1001234567890:topic:42", sessionId: "sess-topic", sessionFile: path.resolve("/tmp/openclaw-sessions", "sess-topic-topic-42.jsonl"), messageThreadId: 42, - }), + }, + "plugin command params", ); }); @@ -1228,10 +1301,10 @@ describe("registerTelegramNativeCommands — session metadata", () => { await handler(createTelegramPrivateCommandContext({ match: "status" })); - expect(deliveryMocks.deliverReplies).toHaveBeenCalledWith( - expect.objectContaining({ - replies: [{ text: "No response generated. Please try again." }], - }), + const deliveryCall = requireValue( + deliveryMocks.deliverReplies.mock.calls[0]?.[0], + "empty response delivery params", ); + expect(deliveryCall.replies).toEqual([{ text: "No response generated. Please try again." }]); }); });