test: clear telegram session meta broad matchers

This commit is contained in:
Peter Steinberger
2026-05-10 14:06:07 +01:00
parent 829134fba1
commit 7194a89469

View File

@@ -442,15 +442,73 @@ function createConfiguredBindingRoute(
};
}
function requireValue<T>(value: T | null | undefined, label: string): T {
if (value == null) {
throw new Error(`expected ${label}`);
}
return value;
}
function requireRecord(value: unknown, label: string): Record<string, unknown> {
if (typeof value !== "object" || value === null || Array.isArray(value)) {
throw new Error(`expected ${label} to be an object`);
}
return value as Record<string, unknown>;
}
function expectRecordFields(
value: unknown,
expected: Record<string, unknown>,
label: string,
): Record<string, unknown> {
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<typeof vi.fn>;
callIndex?: number;
chatId: unknown;
text?: string;
textIncludes?: string;
optionFields?: Record<string, unknown>;
requireReplyMarkup?: boolean;
label: string;
}): Record<string, unknown> {
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<typeof vi.fn>) {
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." }]);
});
});