Files
openclaw/test/setup-openclaw-runtime.plugin-cache.test.ts
Peter Steinberger 7435149323 test: cover shared runtime plugin retirement
Protect the shared cleanup join introduced by ec78e8e0d6. 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 the 76c0724
proof 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.
2026-09-24 13:49:45 -07:00

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();
}
},
);