

























@@ -442,15 +442,73 @@ function createConfiguredBindingRoute(
442442};
443443}
444444445+function requireValue<T>(value: T | null | undefined, label: string): T {
446+if (value == null) {
447+throw new Error(`expected ${label}`);
448+}
449+return value;
450+}
451+452+function requireRecord(value: unknown, label: string): Record<string, unknown> {
453+if (typeof value !== "object" || value === null || Array.isArray(value)) {
454+throw new Error(`expected ${label} to be an object`);
455+}
456+return value as Record<string, unknown>;
457+}
458+459+function expectRecordFields(
460+value: unknown,
461+expected: Record<string, unknown>,
462+label: string,
463+): Record<string, unknown> {
464+const record = requireRecord(value, label);
465+for (const [key, expectedValue] of Object.entries(expected)) {
466+expect(record[key], `${label}.${key}`).toEqual(expectedValue);
467+}
468+return record;
469+}
470+471+function expectSendMessageCall(params: {
472+sendMessage: ReturnType<typeof vi.fn>;
473+callIndex?: number;
474+chatId: unknown;
475+text?: string;
476+textIncludes?: string;
477+optionFields?: Record<string, unknown>;
478+requireReplyMarkup?: boolean;
479+label: string;
480+}): Record<string, unknown> {
481+const call = requireValue(
482+params.sendMessage.mock.calls[params.callIndex ?? 0],
483+`${params.label} sendMessage call`,
484+);
485+expect(call[0]).toBe(params.chatId);
486+if (params.text !== undefined) {
487+expect(call[1]).toBe(params.text);
488+}
489+if (params.textIncludes !== undefined) {
490+expect(String(call[1])).toContain(params.textIncludes);
491+}
492+const options = params.optionFields
493+ ? expectRecordFields(call[2], params.optionFields, `${params.label} sendMessage options`)
494+ : requireRecord(call[2], `${params.label} sendMessage options`);
495+if (params.requireReplyMarkup) {
496+requireRecord(options.reply_markup, `${params.label} reply markup`);
497+}
498+return options;
499+}
500+445501function expectUnauthorizedNewCommandBlocked(sendMessage: ReturnType<typeof vi.fn>) {
446502expect(replyMocks.dispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled();
447503expect(persistentBindingMocks.resolveConfiguredBindingRoute).not.toHaveBeenCalled();
448504expect(persistentBindingMocks.ensureConfiguredBindingRouteReady).not.toHaveBeenCalled();
449-expect(sendMessage).toHaveBeenCalledWith(
450--1001234567890,
451-"You are not authorized to use this command.",
452-expect.objectContaining({ message_thread_id: 42 }),
453-);
505+expectSendMessageCall({
506+ sendMessage,
507+chatId: -1001234567890,
508+text: "You are not authorized to use this command.",
509+optionFields: { message_thread_id: 42 },
510+label: "unauthorized /new",
511+});
454512}
455513456514describe("registerTelegramNativeCommands — session metadata", () => {
@@ -546,18 +604,19 @@ describe("registerTelegramNativeCommands — session metadata", () => {
546604const menuCall = commandAuthMocks.resolveCommandArgMenu.mock.calls.find(
547605([params]) => params.command.key === "think" && params.provider === "anthropic",
548606)?.[0];
549-expect(menuCall).toEqual(
550-expect.objectContaining({
551-provider: "anthropic",
552-model: "claude-opus-4-7",
553-}),
607+expectRecordFields(
608+menuCall,
609+{ provider: "anthropic", model: "claude-opus-4-7" },
610+"thinking menu call",
554611);
555612expect(sessionMocks.loadSessionStore).toHaveBeenCalledWith("/tmp/openclaw-sessions.json");
556-expect(sendMessage).toHaveBeenCalledWith(
557-100,
558-expect.stringContaining("Current thinking level: high.\nChoose level for /think."),
559-expect.objectContaining({ reply_markup: expect.any(Object) }),
560-);
613+expectSendMessageCall({
614+ sendMessage,
615+chatId: 100,
616+textIncludes: "Current thinking level: high.\nChoose level for /think.",
617+requireReplyMarkup: true,
618+label: "thinking menu",
619+});
561620expect(replyMocks.dispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled();
562621});
563622@@ -582,17 +641,18 @@ describe("registerTelegramNativeCommands — session metadata", () => {
582641const menuCall = commandAuthMocks.resolveCommandArgMenu.mock.calls.find(
583642([params]) => params.command.key === "think" && params.provider === "anthropic",
584643)?.[0];
585-expect(menuCall).toEqual(
586-expect.objectContaining({
587-provider: "anthropic",
588-model: "claude-opus-4-7",
589-}),
590-);
591-expect(sendMessage).toHaveBeenCalledWith(
592-100,
593-expect.stringContaining("Choose level for /think."),
594-expect.objectContaining({ reply_markup: expect.any(Object) }),
644+expectRecordFields(
645+menuCall,
646+{ provider: "anthropic", model: "claude-opus-4-7" },
647+"thread thinking menu call",
595648);
649+expectSendMessageCall({
650+ sendMessage,
651+chatId: 100,
652+textIncludes: "Choose level for /think.",
653+requireReplyMarkup: true,
654+label: "thread thinking menu",
655+});
596656expect(replyMocks.dispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled();
597657});
598658@@ -626,17 +686,18 @@ describe("registerTelegramNativeCommands — session metadata", () => {
626686const menuCall = commandAuthMocks.resolveCommandArgMenu.mock.calls.find(
627687([params]) => params.command.key === "think" && params.provider === "openai",
628688)?.[0];
629-expect(menuCall).toEqual(
630-expect.objectContaining({
631-provider: "openai",
632-model: "gpt-5.5",
633-}),
634-);
635-expect(sendMessage).toHaveBeenCalledWith(
636-100,
637-expect.stringContaining("Current thinking level: medium.\nChoose level for /think."),
638-expect.objectContaining({ reply_markup: expect.any(Object) }),
689+expectRecordFields(
690+menuCall,
691+{ provider: "openai", model: "gpt-5.5" },
692+"default model thinking menu call",
639693);
694+expectSendMessageCall({
695+ sendMessage,
696+chatId: 100,
697+textIncludes: "Current thinking level: medium.\nChoose level for /think.",
698+requireReplyMarkup: true,
699+label: "default model thinking menu",
700+});
640701expect(replyMocks.dispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled();
641702});
642703@@ -669,11 +730,13 @@ describe("registerTelegramNativeCommands — session metadata", () => {
669730});
670731await handler(createTelegramPrivateCommandContext());
671732672-expect(sendMessage).toHaveBeenCalledWith(
673-100,
674-expect.stringContaining("Current thinking level: xhigh.\nChoose level for /think."),
675-expect.objectContaining({ reply_markup: expect.any(Object) }),
676-);
733+expectSendMessageCall({
734+ sendMessage,
735+chatId: 100,
736+textIncludes: "Current thinking level: xhigh.\nChoose level for /think.",
737+requireReplyMarkup: true,
738+label: "target model thinking menu",
739+});
677740expect(replyMocks.dispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled();
678741});
679742@@ -706,11 +769,13 @@ describe("registerTelegramNativeCommands — session metadata", () => {
706769});
707770await handler(createTelegramPrivateCommandContext());
708771709-expect(sendMessage).toHaveBeenCalledWith(
710-100,
711-expect.stringContaining("Current thinking level: minimal.\nChoose level for /think."),
712-expect.objectContaining({ reply_markup: expect.any(Object) }),
713-);
772+expectSendMessageCall({
773+ sendMessage,
774+chatId: 100,
775+textIncludes: "Current thinking level: minimal.\nChoose level for /think.",
776+requireReplyMarkup: true,
777+label: "agent thinking menu",
778+});
714779expect(replyMocks.dispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled();
715780});
716781@@ -743,9 +808,11 @@ describe("registerTelegramNativeCommands — session metadata", () => {
743808await runPromise;
744809745810expect(replyMocks.dispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalledTimes(1);
746-expect(
811+const dispatcherOptions = requireRecord(
747812replyMocks.dispatchReplyWithBufferedBlockDispatcher.mock.calls[0]?.[0].dispatcherOptions,
748-).toEqual(expect.objectContaining({ beforeDeliver: expect.any(Function) }));
813+"dispatcher options",
814+);
815+expect(dispatcherOptions.beforeDeliver).toBeTypeOf("function");
749816});
750817751818it("does not inject approval buttons for native command replies once the monitor owns approvals", async () => {
@@ -848,12 +915,10 @@ describe("registerTelegramNativeCommands — session metadata", () => {
848915const deliveredCall = deliveryMocks.deliverReplies.mock.calls[0]?.[0] as
849916| DeliverRepliesParams
850917| undefined;
851-expect(deliveredCall).toEqual(
852-expect.objectContaining({
853-silent: true,
854-replies: [expect.objectContaining({ isError: true })],
855-}),
856-);
918+const deliveryParams = requireValue(deliveredCall, "silent error delivery params");
919+expect(deliveryParams.silent).toBe(true);
920+expect(deliveryParams.replies).toHaveLength(1);
921+expect(deliveryParams.replies[0]?.isError).toBe(true);
857922});
858923859924it("routes Telegram native commands through configured ACP topic bindings", async () => {
@@ -1055,12 +1120,14 @@ describe("registerTelegramNativeCommands — session metadata", () => {
10551120]
10561121>
10571122)[0]?.[0];
1058-expect(dispatchCall?.ctx).toEqual(
1059-expect.objectContaining({
1123+expectRecordFields(
1124+dispatchCall?.ctx,
1125+{
10601126CommandTargetSessionKey: "agent:main:telegram:group:-1001234567890:topic:42",
10611127MessageThreadId: 42,
10621128OriginatingTo: "telegram:-1001234567890:topic:42",
1063-}),
1129+},
1130+"topic dispatch context",
10641131);
10651132},
10661133);
@@ -1091,11 +1158,13 @@ describe("registerTelegramNativeCommands — session metadata", () => {
10911158await handler(createTelegramTopicCommandContext());
1092115910931160expect(replyMocks.dispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled();
1094-expect(sendMessage).toHaveBeenCalledWith(
1095--1001234567890,
1096-"Configured ACP binding is unavailable right now. Please try again.",
1097-expect.objectContaining({ message_thread_id: 42 }),
1098-);
1161+expectSendMessageCall({
1162+ sendMessage,
1163+chatId: -1001234567890,
1164+text: "Configured ACP binding is unavailable right now. Please try again.",
1165+optionFields: { message_thread_id: 42 },
1166+label: "unavailable ACP binding",
1167+});
10991168});
1100116911011170it("keeps /new blocked in ACP-bound Telegram topics when sender is unauthorized", async () => {
@@ -1180,22 +1249,26 @@ describe("registerTelegramNativeCommands — session metadata", () => {
11801249createTelegramTopicCommandContext({ match: "bind --cwd /tmp/work", threadId: 42 }),
11811250);
118212511183-expect(sessionMocks.resolveAndPersistSessionFile).toHaveBeenCalledWith(
1184-expect.objectContaining({
1252+expectRecordFields(
1253+sessionMocks.resolveAndPersistSessionFile.mock.calls[0]?.[0],
1254+{
11851255sessionId: "sess-topic",
11861256sessionKey: "agent:main:telegram:group:-1001234567890:topic:42",
11871257storePath: "/tmp/openclaw-sessions/sessions.json",
11881258sessionsDir: "/tmp/openclaw-sessions",
11891259fallbackSessionFile: path.resolve("/tmp/openclaw-sessions", "sess-topic-topic-42.jsonl"),
1190-}),
1260+},
1261+"resolved session file params",
11911262);
1192-expect(pluginRuntimeMocks.executePluginCommand).toHaveBeenCalledWith(
1193-expect.objectContaining({
1263+expectRecordFields(
1264+(pluginRuntimeMocks.executePluginCommand.mock.calls as unknown as Array<[unknown]>)[0]?.[0],
1265+{
11941266sessionKey: "agent:main:telegram:group:-1001234567890:topic:42",
11951267sessionId: "sess-topic",
11961268sessionFile: path.resolve("/tmp/openclaw-sessions", "sess-topic-topic-42.jsonl"),
11971269messageThreadId: 42,
1198-}),
1270+},
1271+"plugin command params",
11991272);
12001273});
12011274@@ -1228,10 +1301,10 @@ describe("registerTelegramNativeCommands — session metadata", () => {
1228130112291302await handler(createTelegramPrivateCommandContext({ match: "status" }));
123013031231-expect(deliveryMocks.deliverReplies).toHaveBeenCalledWith(
1232-expect.objectContaining({
1233-replies: [{ text: "No response generated. Please try again." }],
1234-}),
1304+const deliveryCall = requireValue(
1305+deliveryMocks.deliverReplies.mock.calls[0]?.[0],
1306+"empty response delivery params",
12351307);
1308+expect(deliveryCall.replies).toEqual([{ text: "No response generated. Please try again." }]);
12361309});
12371310});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。