

























@@ -21,7 +21,7 @@ describe("cleanupEmbeddedAttemptResources", () => {
2121vi.restoreAllMocks();
2222});
232324-it("waits for aborted prompt settlement before flushing, disposing, and releasing the lock", async () => {
24+it("waits for aborted prompt settlement before flushing and releasing the lock", async () => {
2525const order: string[] = [];
2626const settle = createDeferred<void>();
2727@@ -57,7 +57,7 @@ describe("cleanupEmbeddedAttemptResources", () => {
5757settle.resolve();
5858await cleanupPromise;
595960-expect(order).toEqual(["guard", "flush", "dispose", "release"]);
60+expect(order).toEqual(["guard", "flush", "release", "dispose"]);
6161});
62626363it("releases the lock after the aborted settle timeout", async () => {
@@ -93,7 +93,44 @@ describe("cleanupEmbeddedAttemptResources", () => {
9393await vi.advanceTimersByTimeAsync(1);
9494await cleanupPromise;
959596-expect(order).toEqual(["flush", "dispose", "release"]);
96+expect(order).toEqual(["flush", "release", "dispose"]);
97+});
98+99+it("releases the lock before runtime teardown can hang", async () => {
100+const order: string[] = [];
101+let markRuntimeDisposeStarted!: () => void;
102+const runtimeDisposeStarted = new Promise<void>((resolve) => {
103+markRuntimeDisposeStarted = resolve;
104+});
105+106+void cleanupEmbeddedAttemptResources({
107+flushPendingToolResultsAfterIdle: vi.fn(async () => {
108+order.push("flush");
109+}),
110+session: {
111+agent: {},
112+dispose: () => {
113+order.push("dispose");
114+},
115+},
116+sessionManager: {},
117+sessionLock: {
118+release: async () => {
119+order.push("release");
120+},
121+},
122+bundleMcpRuntime: {
123+dispose: async () => {
124+order.push("runtime-dispose-start");
125+markRuntimeDisposeStarted();
126+await new Promise(() => {});
127+},
128+},
129+});
130+131+await runtimeDisposeStarted;
132+133+expect(order).toEqual(["flush", "release", "dispose", "runtime-dispose-start"]);
97134});
9813599136it("does not wait for the settle promise on non-aborted cleanup", async () => {
@@ -116,10 +153,43 @@ describe("cleanupEmbeddedAttemptResources", () => {
116153expect(release).toHaveBeenCalledTimes(1);
117154});
118155156+it("still disposes resources when lock release fails", async () => {
157+const releaseError = new Error("release failed");
158+const dispose = vi.fn();
159+const runtimeDispose = vi.fn(async () => {});
160+161+await expect(
162+cleanupEmbeddedAttemptResources({
163+flushPendingToolResultsAfterIdle: vi.fn(async () => {}),
164+session: {
165+agent: {},
166+ dispose,
167+},
168+sessionManager: {},
169+sessionLock: {
170+release: async () => {
171+throw releaseError;
172+},
173+},
174+bundleMcpRuntime: {
175+dispose: runtimeDispose,
176+},
177+}),
178+).rejects.toBe(releaseError);
179+180+expect(dispose).toHaveBeenCalledTimes(1);
181+expect(runtimeDispose).toHaveBeenCalledTimes(1);
182+});
183+119184it("can skip stale session-manager flushing after session takeover", async () => {
120185const flushPendingToolResultsAfterIdle = vi.fn(async () => {});
121-const dispose = vi.fn();
122-const release = vi.fn(async () => {});
186+const order: string[] = [];
187+const dispose = vi.fn(() => {
188+order.push("dispose");
189+});
190+const release = vi.fn(async () => {
191+order.push("release");
192+});
123193124194await cleanupEmbeddedAttemptResources({
125195 flushPendingToolResultsAfterIdle,
@@ -135,5 +205,6 @@ describe("cleanupEmbeddedAttemptResources", () => {
135205expect(flushPendingToolResultsAfterIdle).not.toHaveBeenCalled();
136206expect(dispose).toHaveBeenCalledTimes(1);
137207expect(release).toHaveBeenCalledTimes(1);
208+expect(order).toEqual(["release", "dispose"]);
138209});
139210});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。