mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(channels): prevent lifecycle listener buildup (#109108)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
// Discord tests cover message handler.queue plugin behavior.
|
||||
import { getEventListeners } from "node:events";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { DiscordRetryableInboundError } from "./inbound-dedupe.js";
|
||||
@@ -440,6 +441,22 @@ describe("createDiscordMessageHandler queue behavior", () => {
|
||||
expect(setStatus.mock.calls.length).toBe(callsBeforeStop);
|
||||
});
|
||||
|
||||
it("removes lifecycle abort listeners after handler deactivation", () => {
|
||||
const abortController = new AbortController();
|
||||
const initialListenerCount = getEventListeners(abortController.signal, "abort").length;
|
||||
const handler = createDiscordMessageHandler(
|
||||
createDiscordHandlerParams({ abortSignal: abortController.signal }),
|
||||
);
|
||||
|
||||
expect(getEventListeners(abortController.signal, "abort")).toHaveLength(
|
||||
initialListenerCount + 2,
|
||||
);
|
||||
|
||||
handler.deactivate();
|
||||
|
||||
expect(getEventListeners(abortController.signal, "abort")).toHaveLength(initialListenerCount);
|
||||
});
|
||||
|
||||
it("skips queued runs that have not started yet after deactivation", async () => {
|
||||
preflightDiscordMessageMock.mockReset();
|
||||
processDiscordMessageMock.mockReset();
|
||||
|
||||
@@ -82,6 +82,7 @@ export function createDiscordMessageRunQueue(
|
||||
let lifecycleActive = !params.abortSignal?.aborted;
|
||||
|
||||
const cleanupSkippedQueuedMessages = () => {
|
||||
params.abortSignal?.removeEventListener("abort", cleanupSkippedQueuedMessages);
|
||||
// These callbacks represent jobs accepted into the queue but not started.
|
||||
// Running jobs remove their callback before processDiscordMessage owns cleanup.
|
||||
if (!lifecycleActive && skippedCleanup.size === 0) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Run state machine tests cover channel run lifecycle transitions and terminal states.
|
||||
import { getEventListeners } from "node:events";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { createRunStateMachine } from "./run-state-machine.js";
|
||||
|
||||
@@ -38,4 +39,18 @@ describe("createRunStateMachine", () => {
|
||||
machine.onRunEnd();
|
||||
expect(setStatus.mock.calls.length).toBe(callsBeforeAbort);
|
||||
});
|
||||
|
||||
it("removes its abort listener on manual deactivation", () => {
|
||||
const abortController = new AbortController();
|
||||
const initialListenerCount = getEventListeners(abortController.signal, "abort").length;
|
||||
const machine = createRunStateMachine({ abortSignal: abortController.signal });
|
||||
|
||||
expect(getEventListeners(abortController.signal, "abort")).toHaveLength(
|
||||
initialListenerCount + 1,
|
||||
);
|
||||
|
||||
machine.deactivate();
|
||||
|
||||
expect(getEventListeners(abortController.signal, "abort")).toHaveLength(initialListenerCount);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -61,6 +61,7 @@ export function createRunStateMachine(params: RunStateMachineParams) {
|
||||
const deactivate = () => {
|
||||
lifecycleActive = false;
|
||||
clearHeartbeat();
|
||||
params.abortSignal?.removeEventListener("abort", onAbort);
|
||||
};
|
||||
|
||||
const onAbort = () => {
|
||||
|
||||
Reference in New Issue
Block a user