mirror of
https://github.com/openclaw/openclaw.git
synced 2026-09-28 14:12:28 +08:00
Protect the shared cleanup join introduced byec78e8e0d6. Exercise the registered runtime reset hook with a real plugin disposal callback held by a deferred promise. Verify that cleanup waits for disposal, reports complete failure records, and consumes the retirement outcome before returning. Both cases failed against the prior setup because its reset returned early. Validation: both cases pass with the exact ec78 setup bytes on the76c0724proof baseline; the cache and disposal owners match current main. This single-worker invocation took 25.96s wall (22.49s Vitest) on Node24.19. Cold loading of the actual shared setup dominates this boundary proof; the test adds no subprocess harness or production seam. The unchanged test also passed 20 standalone runs during diagnosis. Selected repository checks and fresh independent review with complete setup/owner context are clean.
75 lines
2.5 KiB
TypeScript
75 lines
2.5 KiB
TypeScript
import { expect, it, vi } from "vitest";
|
|
import { createDeferredCore } from "../src/shared/deferred.js";
|
|
|
|
const hooks = vi.hoisted(() => ({ afterEach: [] as Array<() => Promise<void>> }));
|
|
vi.mock("vitest", async (importOriginal) => ({
|
|
...(await importOriginal<typeof import("vitest")>()),
|
|
beforeAll: () => {},
|
|
afterAll: () => {},
|
|
afterEach: (cleanup: () => Promise<void>) => hooks.afterEach.push(cleanup),
|
|
}));
|
|
|
|
it.each([false, true])(
|
|
"joins retired plugin caches in the registered runtime reset (cleanup fails: %s)",
|
|
async (fails) => {
|
|
vi.resetModules();
|
|
hooks.afterEach.length = 0;
|
|
await import("./setup-openclaw-runtime.js");
|
|
const cleanup = hooks.afterEach.at(-1);
|
|
if (!cleanup) {
|
|
throw new Error("Runtime setup did not register its cleanup hook");
|
|
}
|
|
const { getProcessPluginCache, retainPluginCacheInstance, waitForPluginCacheRetirement } =
|
|
await import("../src/plugins/plugin-cache.js");
|
|
const { PluginInstance } = await import("../src/plugins/plugin-instance.js");
|
|
const entered = createDeferredCore();
|
|
const finish = createDeferredCore();
|
|
const failure = new Error("synthetic plugin retirement failure");
|
|
const instance = new PluginInstance("runtime-reset-fixture");
|
|
instance.lifecycle.onDispose(async () => {
|
|
entered.resolve();
|
|
await finish.promise;
|
|
if (fails) {
|
|
throw failure;
|
|
}
|
|
});
|
|
retainPluginCacheInstance(instance, getProcessPluginCache());
|
|
let settled = false;
|
|
const result = cleanup().then(
|
|
() => {
|
|
settled = true;
|
|
return undefined;
|
|
},
|
|
(error: unknown) => {
|
|
settled = true;
|
|
return error;
|
|
},
|
|
);
|
|
try {
|
|
await entered.promise;
|
|
expect(settled).toBe(false);
|
|
finish.resolve();
|
|
if (fails) {
|
|
const error = await result;
|
|
expect(error).toBeInstanceOf(AggregateError);
|
|
if (!(error instanceof AggregateError)) {
|
|
throw new Error("Runtime cleanup did not report the plugin retirement failure");
|
|
}
|
|
expect(error.errors).toEqual([
|
|
{ pluginId: "runtime-reset-fixture", hookId: "instance", error: failure },
|
|
]);
|
|
} else {
|
|
await expect(result).resolves.toBeUndefined();
|
|
}
|
|
await expect(waitForPluginCacheRetirement()).resolves.toEqual({
|
|
cleanupCount: 0,
|
|
failures: [],
|
|
});
|
|
} finally {
|
|
finish.resolve();
|
|
await result;
|
|
await waitForPluginCacheRetirement();
|
|
}
|
|
},
|
|
);
|