openclaw/test/setup.extensions.ts
Peter Steinberger 313e200f0a
refactor(session-upstream-links): move lifecycle writes to the shared-state worker (#165796)
* refactor(session-upstream-links): move lifecycle writes to the shared-state worker

Await upstream-link upsert and exact deletion in bundled lifecycle callers.
Keep v2026.9.8 synchronous SDK behavior deprecated until the next SDK major,
and validate fork source identity inside worker transactions while retaining
live host authority and accepted cleanup settlement.

Release-note context: plugin authors can await upsertSessionUpstreamLinkAsync
and deleteSessionUpstreamLinkAsync; existing stored links and synchronous
compatibility behavior remain supported.

* fix(sessions): keep link deletion in captured cleanup transaction

Delete upstream links through the existing shared-state cleanup operation so deletion captures its store and revokes ambient reads before yielding. Preserve the replacement store and the existing transaction/commit authority checks. The boundary regression fails in five PR runs and passes in five baseline runs; the repaired owner and lifecycle suites pass.
2026-10-05 14:49:51 -07:00

94 lines
3.6 KiB
TypeScript

// Extension test setup installs extension-specific mocks and cleanup.
import { afterAll, afterEach, beforeEach, expect, vi } from "vitest";
import { installSharedTestSetup } from "./setup.shared.js";
const codexAppServerTestPattern = /\/extensions\/codex\/src\/app-server\/.*\.test\.ts$/;
if (codexAppServerTestPattern.test(expect.getState().testPath?.replaceAll("\\", "/") ?? "")) {
// Prepare declarations before collection without binding worker-cpu ahead of file mocks.
await import("../src/test-utils/prepare-compiled-subprocesses.js");
}
const testEnv = installSharedTestSetup({ loadProfileEnv: false });
let restoreUpstreamLinks: (() => void) | undefined;
beforeEach(async (context) => {
vi.useRealTimers();
const testPath = expect.getState().testPath?.replaceAll("\\", "/");
if (codexAppServerTestPattern.test(testPath ?? "")) {
const { getTrackedWorkerPoolSnapshot } = await vi.importActual<
typeof import("../src/infra/worker-cpu.js")
>("../src/infra/worker-cpu.js");
let stop: (() => Promise<void>) | undefined;
context.codexAttemptRuntime = {
readWorkerPools: getTrackedWorkerPoolSnapshot,
start: async () => {
const [mcp, clocks] = await Promise.all([
vi.importActual<typeof import("../src/agents/agent-bundle-mcp-manager-api.js")>(
"../src/agents/agent-bundle-mcp-manager-api.js",
),
vi.importActual<typeof import("../src/test-utils/gateway-scheduler-clock.js")>(
"../src/test-utils/gateway-scheduler-clock.js",
),
]);
const scheduler = clocks.createTestGatewayScheduler();
stop = async () => {
scheduler.beginClose();
try {
await mcp.disposeAllSessionMcpRuntimes();
} finally {
await scheduler.stop();
}
};
await mcp.setSessionMcpRuntimeScheduler(scheduler);
},
stop: async () => {
await stop?.();
},
};
}
if (
!testPath?.match(
/\/extensions\/codex\/src\/app-server\/upstream-(?:fork-import|session-fork|session-fork-continuation)\.test\.ts$/,
)
) {
return;
}
// Shared initialization calls the core owner; extension fixtures keep their public SDK mocks.
const [owner, facade] = await Promise.all([
vi.importActual<typeof import("../src/sessions/session-upstream-links.js")>(
"../src/sessions/session-upstream-links.js",
),
vi.importMock<typeof import("openclaw/plugin-sdk/session-catalog")>(
"openclaw/plugin-sdk/session-catalog",
),
]);
const upsert = vi
.spyOn(owner, "upsertSessionUpstreamLinkWithCurrentSource")
.mockImplementation((input, options) => facade.upsertSessionUpstreamLinkAsync(input, options));
const remove = vi
.spyOn(owner, "deleteSessionUpstreamLinkAsync")
.mockImplementation(facade.deleteSessionUpstreamLinkAsync);
restoreUpstreamLinks = () => {
upsert.mockRestore();
remove.mockRestore();
};
});
afterEach(() => {
restoreUpstreamLinks?.();
restoreUpstreamLinks = undefined;
});
afterAll(async () => {
const { drainAgentDatabaseResources } = await vi.importActual<
typeof import("../src/state/openclaw-agent-db-resources.js")
>("../src/state/openclaw-agent-db-resources.js");
// File-owned homes must survive until retained Worker leases have been released.
await drainAgentDatabaseResources({}, async () => {
const { drainGlobalSingletonLifecycleState } = await vi.importActual<
typeof import("../src/shared/global-singleton.js")
>("../src/shared/global-singleton.js");
await drainGlobalSingletonLifecycleState();
testEnv.cleanup();
});
});