perf(test): narrow gateway context reload coverage

This commit is contained in:
Peter Steinberger
2026-07-09 05:55:34 -04:00
parent f650d641e4
commit d27d7ba6d3
3 changed files with 73 additions and 66 deletions
@@ -0,0 +1,61 @@
import { resolveGlobalSingleton } from "../shared/global-singleton.js";
import type { GatewayRequestContext } from "./server-methods/types.js";
const FALLBACK_GATEWAY_CONTEXT_STATE_KEY: unique symbol = Symbol.for(
"openclaw.fallbackGatewayContextState",
);
type FallbackGatewayContextState = {
context: GatewayRequestContext | undefined;
resolveContext: (() => GatewayRequestContext | undefined) | undefined;
};
const getFallbackGatewayContextState = () =>
resolveGlobalSingleton<FallbackGatewayContextState>(FALLBACK_GATEWAY_CONTEXT_STATE_KEY, () => ({
context: undefined,
resolveContext: undefined,
}));
/** Set the process fallback gateway context for channel adapters outside WS requests. */
export function setFallbackGatewayContext(ctx: GatewayRequestContext): () => void {
const fallbackGatewayContextState = getFallbackGatewayContextState();
fallbackGatewayContextState.context = ctx;
fallbackGatewayContextState.resolveContext = undefined;
return () => {
const currentFallbackGatewayContextState = getFallbackGatewayContextState();
if (
currentFallbackGatewayContextState.context === ctx &&
currentFallbackGatewayContextState.resolveContext === undefined
) {
currentFallbackGatewayContextState.context = undefined;
}
};
}
export function setFallbackGatewayContextResolver(
resolveContext: () => GatewayRequestContext | undefined,
): () => void {
const fallbackGatewayContextState = getFallbackGatewayContextState();
fallbackGatewayContextState.context = undefined;
fallbackGatewayContextState.resolveContext = resolveContext;
return () => {
const currentFallbackGatewayContextState = getFallbackGatewayContextState();
if (currentFallbackGatewayContextState.resolveContext === resolveContext) {
currentFallbackGatewayContextState.context = undefined;
currentFallbackGatewayContextState.resolveContext = undefined;
}
};
}
/** Clear the fallback gateway context installed for non-WS dispatch paths. */
export function clearFallbackGatewayContext(): void {
const fallbackGatewayContextState = getFallbackGatewayContextState();
fallbackGatewayContextState.context = undefined;
fallbackGatewayContextState.resolveContext = undefined;
}
export function getFallbackGatewayContext(): GatewayRequestContext | undefined {
const fallbackGatewayContextState = getFallbackGatewayContextState();
const resolved = fallbackGatewayContextState.resolveContext?.();
return resolved ?? fallbackGatewayContextState.context;
}
+5 -3
View File
@@ -321,9 +321,11 @@ async function createSubagentRuntime(
return runtimeModule.createPluginRuntime({ allowGatewaySubagentBinding: true }).subagent;
}
async function reloadServerPluginsModule(): Promise<ServerPluginsModule> {
async function reloadFallbackGatewayContextModule() {
// Existing runtimes retain the old module graph; only the process-global state owner
// must reload to prove a restarted Gateway can replace their fallback context.
vi.resetModules();
return await import("./server-plugins.js");
return await import("./server-plugin-fallback-context.js");
}
function loadGatewayPluginsForTest(
@@ -1611,7 +1613,7 @@ describe("loadGatewayPlugins", () => {
await runtime.run({ sessionKey: "s-1", message: "hello" });
expect(getLastDispatchedContext()).toBe(staleContext);
const reloaded = await reloadServerPluginsModule();
const reloaded = await reloadFallbackGatewayContextModule();
const freshContext = createTestContext("fresh");
reloaded.setFallbackGatewayContext(freshContext);
+7 -63
View File
@@ -29,70 +29,14 @@ import { resolveGlobalSingleton } from "../shared/global-singleton.js";
import { resolveSafeTimeoutDelayMs } from "../utils/timer-delay.js";
import { ADMIN_SCOPE, APPROVALS_SCOPE, WRITE_SCOPE } from "./method-scopes.js";
import { normalizeOperatorScopeList, type OperatorScope } from "./operator-scopes.js";
import type {
GatewayRequestContext,
GatewayRequestHandler,
GatewayRequestOptions,
} from "./server-methods/types.js";
import type { GatewayRequestHandler, GatewayRequestOptions } from "./server-methods/types.js";
import { getFallbackGatewayContext } from "./server-plugin-fallback-context.js";
const FALLBACK_GATEWAY_CONTEXT_STATE_KEY: unique symbol = Symbol.for(
"openclaw.fallbackGatewayContextState",
);
type FallbackGatewayContextState = {
context: GatewayRequestContext | undefined;
resolveContext: (() => GatewayRequestContext | undefined) | undefined;
};
const getFallbackGatewayContextState = () =>
resolveGlobalSingleton<FallbackGatewayContextState>(FALLBACK_GATEWAY_CONTEXT_STATE_KEY, () => ({
context: undefined,
resolveContext: undefined,
}));
/** Set the process fallback gateway context for channel adapters outside WS requests. */
export function setFallbackGatewayContext(ctx: GatewayRequestContext): () => void {
const fallbackGatewayContextState = getFallbackGatewayContextState();
fallbackGatewayContextState.context = ctx;
fallbackGatewayContextState.resolveContext = undefined;
return () => {
const currentFallbackGatewayContextState = getFallbackGatewayContextState();
if (
currentFallbackGatewayContextState.context === ctx &&
currentFallbackGatewayContextState.resolveContext === undefined
) {
currentFallbackGatewayContextState.context = undefined;
}
};
}
export function setFallbackGatewayContextResolver(
resolveContext: () => GatewayRequestContext | undefined,
): () => void {
const fallbackGatewayContextState = getFallbackGatewayContextState();
fallbackGatewayContextState.context = undefined;
fallbackGatewayContextState.resolveContext = resolveContext;
return () => {
const currentFallbackGatewayContextState = getFallbackGatewayContextState();
if (currentFallbackGatewayContextState.resolveContext === resolveContext) {
currentFallbackGatewayContextState.context = undefined;
currentFallbackGatewayContextState.resolveContext = undefined;
}
};
}
/** Clear the fallback gateway context installed for non-WS dispatch paths. */
export function clearFallbackGatewayContext(): void {
const fallbackGatewayContextState = getFallbackGatewayContextState();
fallbackGatewayContextState.context = undefined;
fallbackGatewayContextState.resolveContext = undefined;
}
function getFallbackGatewayContext(): GatewayRequestContext | undefined {
const fallbackGatewayContextState = getFallbackGatewayContextState();
const resolved = fallbackGatewayContextState.resolveContext?.();
return resolved ?? fallbackGatewayContextState.context;
}
export {
clearFallbackGatewayContext,
setFallbackGatewayContext,
setFallbackGatewayContextResolver,
} from "./server-plugin-fallback-context.js";
export function hasInProcessGatewayContext(): boolean {
return Boolean(getPluginRuntimeGatewayRequestScope()?.context ?? getFallbackGatewayContext());