mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:40:44 +00:00
fix(telegram): avoid leaking thread binding persist cleanup
This commit is contained in:
@@ -5,6 +5,20 @@ import { getSessionBindingService } from "openclaw/plugin-sdk/conversation-runti
|
||||
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { importFreshModule } from "../../../test/helpers/import-fresh.js";
|
||||
|
||||
const writeJsonFileAtomicallyMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/json-store", async () => {
|
||||
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/json-store")>(
|
||||
"openclaw/plugin-sdk/json-store",
|
||||
);
|
||||
writeJsonFileAtomicallyMock.mockImplementation(actual.writeJsonFileAtomically);
|
||||
return {
|
||||
...actual,
|
||||
writeJsonFileAtomically: writeJsonFileAtomicallyMock,
|
||||
};
|
||||
});
|
||||
|
||||
import {
|
||||
__testing,
|
||||
createTelegramThreadBindingManager,
|
||||
@@ -16,6 +30,7 @@ describe("telegram thread bindings", () => {
|
||||
let stateDirOverride: string | undefined;
|
||||
|
||||
beforeEach(async () => {
|
||||
writeJsonFileAtomicallyMock.mockClear();
|
||||
await __testing.resetTelegramThreadBindingsForTests();
|
||||
});
|
||||
|
||||
@@ -313,4 +328,43 @@ describe("telegram thread bindings", () => {
|
||||
};
|
||||
expect(persisted.bindings?.[0]?.idleTimeoutMs).toBe(90_000);
|
||||
});
|
||||
|
||||
it("does not leak unhandled rejections when a persist write fails", async () => {
|
||||
stateDirOverride = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-telegram-bindings-"));
|
||||
process.env.OPENCLAW_STATE_DIR = stateDirOverride;
|
||||
const unhandled: unknown[] = [];
|
||||
const onUnhandledRejection = (reason: unknown) => {
|
||||
unhandled.push(reason);
|
||||
};
|
||||
process.on("unhandledRejection", onUnhandledRejection);
|
||||
|
||||
try {
|
||||
const manager = createTelegramThreadBindingManager({
|
||||
accountId: "persist-failure",
|
||||
persist: true,
|
||||
enableSweeper: false,
|
||||
});
|
||||
|
||||
await getSessionBindingService().bind({
|
||||
targetSessionKey: "agent:main:subagent:child-persist-failure",
|
||||
targetKind: "subagent",
|
||||
conversation: {
|
||||
channel: "telegram",
|
||||
accountId: "persist-failure",
|
||||
conversationId: "-100200300:topic:100",
|
||||
},
|
||||
});
|
||||
|
||||
writeJsonFileAtomicallyMock.mockImplementationOnce(async () => {
|
||||
throw new Error("persist boom");
|
||||
});
|
||||
manager.touchConversation("-100200300:topic:100");
|
||||
|
||||
await __testing.resetTelegramThreadBindingsForTests();
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
expect(unhandled).toEqual([]);
|
||||
} finally {
|
||||
process.off("unhandledRejection", onUnhandledRejection);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -345,11 +345,12 @@ function enqueuePersistBindings(params: {
|
||||
await persistBindingsToDisk(params);
|
||||
});
|
||||
getThreadBindingsState().persistQueueByAccountId.set(params.accountId, next);
|
||||
void next.finally(() => {
|
||||
const cleanup = () => {
|
||||
if (getThreadBindingsState().persistQueueByAccountId.get(params.accountId) === next) {
|
||||
getThreadBindingsState().persistQueueByAccountId.delete(params.accountId);
|
||||
}
|
||||
});
|
||||
};
|
||||
next.then(cleanup, cleanup);
|
||||
return next;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user