























1-import fs from "node:fs";
21import type { Bot } from "grammy";
3-import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
2+import {
3+createPluginStateKeyedStoreForTests,
4+createPluginStateSyncKeyedStoreForTests,
5+resetPluginStateStoreForTests,
6+} from "openclaw/plugin-sdk/plugin-state-test-runtime";
7+import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
48import { resolveAutoTopicLabelConfig as resolveAutoTopicLabelConfigRuntime } from "./auto-topic-label-config.js";
59import type { TelegramBotDeps } from "./bot-deps.js";
610import {
@@ -11,9 +15,11 @@ import { notifyTelegramInboundEventOutboundSuccess } from "./inbound-event-deliv
1115import {
1216buildTelegramConversationContext,
1317createTelegramMessageCache,
14-resolveTelegramMessageCachePath,
18+resolveTelegramMessageCacheScope,
1519} from "./message-cache.js";
1620import { recordOutboundMessageForPromptContext as recordOutboundMessageForPromptContextActual } from "./outbound-message-context.js";
21+import { clearTelegramRuntime, setTelegramRuntime } from "./runtime.js";
22+import type { TelegramRuntime } from "./runtime.types.js";
17231824type DispatchReplyWithBufferedBlockDispatcherArgs = Parameters<
1925TelegramBotDeps["dispatchReplyWithBufferedBlockDispatcher"]
@@ -170,6 +176,24 @@ vi.mock("./sticker-cache.js", () => ({
170176let dispatchTelegramMessage: typeof import("./bot-message-dispatch.js").dispatchTelegramMessage;
171177let resetTelegramReplyFenceForTests: typeof import("./bot-message-dispatch.js").resetTelegramReplyFenceForTests;
172178179+function installTelegramStateRuntimeForTest(): void {
180+setTelegramRuntime({
181+state: {
182+openKeyedStore: ((options) =>
183+createPluginStateKeyedStoreForTests(
184+"telegram",
185+options,
186+)) as TelegramRuntime["state"]["openKeyedStore"],
187+openSyncKeyedStore: ((options) =>
188+createPluginStateSyncKeyedStoreForTests(
189+"telegram",
190+options,
191+)) as TelegramRuntime["state"]["openSyncKeyedStore"],
192+},
193+channel: {},
194+} as TelegramRuntime);
195+}
196+173197const telegramDepsForTest: TelegramBotDeps = {
174198getRuntimeConfig: loadConfig as TelegramBotDeps["getRuntimeConfig"],
175199resolveStorePath: resolveStorePath as TelegramBotDeps["resolveStorePath"],
@@ -210,6 +234,8 @@ describe("dispatchTelegramMessage draft streaming", () => {
210234});
211235212236beforeEach(() => {
237+resetPluginStateStoreForTests({ closeDatabase: false });
238+installTelegramStateRuntimeForTest();
213239resetTelegramReplyFenceForTests();
214240createTelegramDraftStream.mockReset();
215241createNativeTelegramToolProgressDraft.mockReset();
@@ -307,6 +333,11 @@ describe("dispatchTelegramMessage draft streaming", () => {
307333});
308334});
309335336+afterEach(() => {
337+clearTelegramRuntime();
338+resetPluginStateStoreForTests();
339+});
340+310341const createDraftStream = (messageId?: number) => createTestDraftStream({ messageId });
311342const createSequencedDraftStream = (startMessageId = 1001) =>
312343createSequencedTestDraftStream(startMessageId);
@@ -1312,59 +1343,56 @@ describe("dispatchTelegramMessage draft streaming", () => {
1312134313131344it("records streamed final replies into the prompt context cache", async () => {
13141345const storePath = `/tmp/openclaw-telegram-stream-context-${process.pid}-${Date.now()}.json`;
1315-const persistedPath = resolveTelegramMessageCachePath(storePath);
1316-try {
1317-setupDraftStreams({ answerMessageId: 1497 });
1318-dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ dispatcherOptions }) => {
1319-await dispatcherOptions.deliver(
1320-{ text: "Done already: timeoutSeconds is now 7200s." },
1321-{ kind: "final" },
1322-);
1323-return { queuedFinal: true };
1324-});
1346+setupDraftStreams({ answerMessageId: 1497 });
1347+dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ dispatcherOptions }) => {
1348+await dispatcherOptions.deliver(
1349+{ text: "Done already: timeoutSeconds is now 7200s." },
1350+{ kind: "final" },
1351+);
1352+return { queuedFinal: true };
1353+});
132513541326- await dispatchWithContext({
1327- context: createContext(),
1328- cfg: { session: { store: storePath } },
1329- telegramDeps: {
1330- ...telegramDepsForTest,
1331- recordOutboundMessageForPromptContext: recordOutboundMessageForPromptContextActual,
1332- },
1333- });
1355+await dispatchWithContext({
1356+context: createContext(),
1357+cfg: { session: { store: storePath } },
1358+telegramDeps: {
1359+ ...telegramDepsForTest,
1360+recordOutboundMessageForPromptContext: recordOutboundMessageForPromptContextActual,
1361+},
1362+});
133413631335-const cache = createTelegramMessageCache({ persistedPath });
1336-await cache.record({
1337-accountId: "default",
1338-chatId: "123",
1339-threadId: 777,
1340-msg: {
1341-chat: { id: 123, type: "private", first_name: "Keshav" },
1342-message_thread_id: 777,
1343-message_id: 1521,
1344-date: 1_779_425_460,
1345-text: "Did all Amazon crons run fine",
1346-from: { id: 5185575566, is_bot: false, first_name: "Keshav" },
1347-},
1348-});
1364+const cache = createTelegramMessageCache({
1365+scope: resolveTelegramMessageCacheScope(storePath),
1366+});
1367+await cache.record({
1368+accountId: "default",
1369+chatId: "123",
1370+threadId: 777,
1371+msg: {
1372+chat: { id: 123, type: "private", first_name: "Keshav" },
1373+message_thread_id: 777,
1374+message_id: 1521,
1375+date: 1_779_425_460,
1376+text: "Did all Amazon crons run fine",
1377+from: { id: 5185575566, is_bot: false, first_name: "Keshav" },
1378+},
1379+});
134913801350- const context = await buildTelegramConversationContext({
1351- cache,
1352- accountId: "default",
1353- chatId: "123",
1354- threadId: 777,
1355- messageId: "1521",
1356- replyChainNodes: [],
1357- recentLimit: 10,
1358- replyTargetWindowSize: 2,
1359- });
1381+const context = await buildTelegramConversationContext({
1382+ cache,
1383+accountId: "default",
1384+chatId: "123",
1385+threadId: 777,
1386+messageId: "1521",
1387+replyChainNodes: [],
1388+recentLimit: 10,
1389+replyTargetWindowSize: 2,
1390+});
136013911361-expect(context.map((entry) => entry.node.messageId)).toContain("1497");
1362-expect(context.map((entry) => entry.node.body)).toContain(
1363-"Done already: timeoutSeconds is now 7200s.",
1364-);
1365-} finally {
1366-fs.rmSync(persistedPath, { force: true });
1367-}
1392+expect(context.map((entry) => entry.node.messageId)).toContain("1497");
1393+expect(context.map((entry) => entry.node.body)).toContain(
1394+"Done already: timeoutSeconds is now 7200s.",
1395+);
13681396});
1369139713701398it("suppresses text-only tool payloads delivered after the final answer", async () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。