From 1bef457cb6f0fceb5403025962d84972a763b485 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 19 Apr 2026 01:17:14 +0100 Subject: [PATCH] test: speed up agent hotspot tests --- .../bash-tools.exec.approval-id.test.ts | 10 ++++ src/agents/model-auth-markers.ts | 18 ++++-- src/agents/provider-attribution.ts | 55 +++++++++++++------ src/agents/subagent-control.test.ts | 45 ++++++++------- src/agents/subagent-control.ts | 6 +- .../subagent-registry.steer-restart.test.ts | 47 ++++------------ 6 files changed, 104 insertions(+), 77 deletions(-) diff --git a/src/agents/bash-tools.exec.approval-id.test.ts b/src/agents/bash-tools.exec.approval-id.test.ts index 18ebb33c1bf..4c04cf38435 100644 --- a/src/agents/bash-tools.exec.approval-id.test.ts +++ b/src/agents/bash-tools.exec.approval-id.test.ts @@ -25,6 +25,16 @@ vi.mock("../infra/outbound/message.js", () => ({ sendMessage: vi.fn(async () => ({ ok: true })), })); +vi.mock("../infra/shell-env.js", async () => { + const mod = + await vi.importActual("../infra/shell-env.js"); + return { + ...mod, + getShellPathFromLoginShell: vi.fn(() => null), + resolveShellEnvFallbackTimeoutMs: vi.fn(() => 0), + }; +}); + function buildPreparedSystemRunPayload(rawInvokeParams: unknown) { const invoke = (rawInvokeParams ?? {}) as { params?: { diff --git a/src/agents/model-auth-markers.ts b/src/agents/model-auth-markers.ts index d057c4db80e..dbd782de1e9 100644 --- a/src/agents/model-auth-markers.ts +++ b/src/agents/model-auth-markers.ts @@ -19,6 +19,8 @@ const CORE_NON_SECRET_API_KEY_MARKERS = [ CUSTOM_LOCAL_AUTH_MARKER, NON_ENV_SECRETREF_MARKER, ] as const; +let knownEnvApiKeyMarkersCache: Set | undefined; +let knownNonSecretApiKeyMarkersCache: string[] | undefined; // Legacy marker names kept for backward compatibility with existing models.json files. const LEGACY_ENV_API_KEY_MARKERS = [ @@ -33,18 +35,24 @@ const LEGACY_ENV_API_KEY_MARKERS = [ ]; function listKnownEnvApiKeyMarkers(): Set { - return new Set([ + knownEnvApiKeyMarkersCache ??= new Set([ ...listKnownProviderEnvApiKeyNames(), ...LEGACY_ENV_API_KEY_MARKERS, ...AWS_SDK_ENV_MARKERS, ]); + return knownEnvApiKeyMarkersCache; } export function listKnownNonSecretApiKeyMarkers(): string[] { - const bundledMarkers = loadPluginManifestRegistry({ cache: true }).plugins.flatMap((plugin) => - plugin.origin === "bundled" ? (plugin.nonSecretAuthMarkers ?? []) : [], - ); - return [...new Set([...CORE_NON_SECRET_API_KEY_MARKERS, ...bundledMarkers])]; + knownNonSecretApiKeyMarkersCache ??= [ + ...new Set([ + ...CORE_NON_SECRET_API_KEY_MARKERS, + ...loadPluginManifestRegistry({ cache: true }).plugins.flatMap((plugin) => + plugin.origin === "bundled" ? (plugin.nonSecretAuthMarkers ?? []) : [], + ), + ]), + ]; + return [...knownNonSecretApiKeyMarkersCache]; } export function isAwsSdkAuthMarker(value: string): boolean { diff --git a/src/agents/provider-attribution.ts b/src/agents/provider-attribution.ts index 24d51f28716..0e9caae688d 100644 --- a/src/agents/provider-attribution.ts +++ b/src/agents/provider-attribution.ts @@ -133,6 +133,12 @@ const OPENAI_RESPONSES_APIS = new Set([ const OPENAI_RESPONSES_PROVIDERS = new Set(["openai", "azure-openai", "azure-openai-responses"]); const MOONSHOT_COMPAT_PROVIDERS = new Set(["moonshot", "kimi"]); const MANIFEST_PROVIDER_ENDPOINT_CLASSES = new Set(["xai-native"]); +type ManifestProviderEndpointCacheEntry = { + endpointClass: ProviderEndpointClass; + hosts: readonly string[]; + normalizedBaseUrls: readonly string[]; +}; +let manifestProviderEndpointCache: ManifestProviderEndpointCacheEntry[] | null = null; function formatOpenClawUserAgent(version: string): string { return `${OPENCLAW_ATTRIBUTION_ORIGINATOR}/${version}`; @@ -192,27 +198,42 @@ function isManifestProviderEndpointClass(value: string): value is ProviderEndpoi return MANIFEST_PROVIDER_ENDPOINT_CLASSES.has(value as ProviderEndpointClass); } +function loadManifestProviderEndpointCache(): ManifestProviderEndpointCacheEntry[] { + if (!manifestProviderEndpointCache) { + const registry = loadPluginManifestRegistry({ cache: true }); + const entries: ManifestProviderEndpointCacheEntry[] = []; + for (const plugin of registry.plugins) { + for (const endpoint of plugin.providerEndpoints ?? []) { + if (!isManifestProviderEndpointClass(endpoint.endpointClass)) { + continue; + } + entries.push({ + endpointClass: endpoint.endpointClass, + hosts: (endpoint.hosts ?? []).map((host) => host.toLowerCase()), + normalizedBaseUrls: (endpoint.baseUrls ?? []) + .map((baseUrl) => normalizeComparableBaseUrl(baseUrl)) + .filter((baseUrl): baseUrl is string => baseUrl !== undefined), + }); + } + } + manifestProviderEndpointCache = entries; + } + return manifestProviderEndpointCache; +} + function resolveManifestProviderEndpoint(params: { host: string; normalizedBaseUrl?: string; }): ProviderEndpointResolution | undefined { - const registry = loadPluginManifestRegistry({ cache: true }); - for (const plugin of registry.plugins) { - for (const endpoint of plugin.providerEndpoints ?? []) { - if (!isManifestProviderEndpointClass(endpoint.endpointClass)) { - continue; - } - if (endpoint.hosts?.some((host) => host.toLowerCase() === params.host)) { - return { endpointClass: endpoint.endpointClass, hostname: params.host }; - } - if ( - params.normalizedBaseUrl && - endpoint.baseUrls?.some( - (baseUrl) => normalizeComparableBaseUrl(baseUrl) === params.normalizedBaseUrl, - ) - ) { - return { endpointClass: endpoint.endpointClass, hostname: params.host }; - } + for (const endpoint of loadManifestProviderEndpointCache()) { + if (endpoint.hosts.includes(params.host)) { + return { endpointClass: endpoint.endpointClass, hostname: params.host }; + } + if ( + params.normalizedBaseUrl && + endpoint.normalizedBaseUrls.includes(params.normalizedBaseUrl) + ) { + return { endpointClass: endpoint.endpointClass, hostname: params.host }; } } return undefined; diff --git a/src/agents/subagent-control.test.ts b/src/agents/subagent-control.test.ts index 2b65b600c4d..42667f47f7a 100644 --- a/src/agents/subagent-control.test.ts +++ b/src/agents/subagent-control.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import * as sessionStore from "../config/sessions/store.js"; +import type { SessionEntry } from "../config/sessions/types.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import type { CallGatewayOptions } from "../gateway/call.js"; import { @@ -112,6 +112,15 @@ function setSubagentControlDepsForTest( __testing.setDepsForTest({ abortEmbeddedPiRun: () => false, clearSessionQueues: () => ({ followupCleared: 0, laneCleared: 0, keys: [] }), + updateSessionStore: async ( + storePath: string, + mutator: (store: Record) => Promise | T, + ) => { + const store = JSON.parse(fs.readFileSync(storePath, "utf-8")) as Record; + const result = await mutator(store); + fs.writeFileSync(storePath, JSON.stringify(store, null, 2), "utf-8"); + return result; + }, ...overrides, }); } @@ -623,26 +632,24 @@ describe("killSubagentRunAdmin", () => { startedAt: Date.now() - 4_000, }); - const updateSessionStoreSpy = vi - .spyOn(sessionStore, "updateSessionStore") - .mockRejectedValueOnce(new Error("session store unavailable")); + setSubagentControlDepsForTest({ + updateSessionStore: async () => { + throw new Error("session store unavailable"); + }, + }); - try { - const result = await killSubagentRunAdmin({ - cfg: cfgWithSessionStore(storePath), - sessionKey: childSessionKey, - }); + const result = await killSubagentRunAdmin({ + cfg: cfgWithSessionStore(storePath), + sessionKey: childSessionKey, + }); - expect(result).toMatchObject({ - found: true, - killed: true, - runId: "run-worker-store-fail", - sessionKey: childSessionKey, - }); - expect(getSubagentRunByChildSessionKey(childSessionKey)?.endedAt).toBeTypeOf("number"); - } finally { - updateSessionStoreSpy.mockRestore(); - } + expect(result).toMatchObject({ + found: true, + killed: true, + runId: "run-worker-store-fail", + sessionKey: childSessionKey, + }); + expect(getSubagentRunByChildSessionKey(childSessionKey)?.endedAt).toBeTypeOf("number"); }); }); diff --git a/src/agents/subagent-control.ts b/src/agents/subagent-control.ts index 50f4e14214f..c3daf4b6e32 100644 --- a/src/agents/subagent-control.ts +++ b/src/agents/subagent-control.ts @@ -57,15 +57,18 @@ const SUBAGENT_REPLY_HISTORY_LIMIT = 50; const steerRateLimit = new Map(); type GatewayCaller = typeof callGateway; +type UpdateSessionStore = typeof updateSessionStore; type AbortEmbeddedPiRun = (sessionId: string) => boolean; type ClearSessionQueues = (keys: Array) => ClearSessionQueueResult; const defaultSubagentControlDeps = { callGateway, + updateSessionStore, }; let subagentControlDeps: { callGateway: GatewayCaller; + updateSessionStore: UpdateSessionStore; abortEmbeddedPiRun?: AbortEmbeddedPiRun; clearSessionQueues?: ClearSessionQueues; } = defaultSubagentControlDeps; @@ -191,7 +194,7 @@ async function killSubagentRun(params: { } if (resolved.entry) { try { - await updateSessionStore(resolved.storePath, (store) => { + await subagentControlDeps.updateSessionStore(resolved.storePath, (store) => { const current = store[childSessionKey]; if (!current) { return; @@ -744,6 +747,7 @@ export const __testing = { setDepsForTest( overrides?: Partial<{ callGateway: GatewayCaller; + updateSessionStore: UpdateSessionStore; abortEmbeddedPiRun: AbortEmbeddedPiRun; clearSessionQueues: ClearSessionQueues; }>, diff --git a/src/agents/subagent-registry.steer-restart.test.ts b/src/agents/subagent-registry.steer-restart.test.ts index be0ee7a0afe..d8e212e43ba 100644 --- a/src/agents/subagent-registry.steer-restart.test.ts +++ b/src/agents/subagent-registry.steer-restart.test.ts @@ -123,29 +123,6 @@ describe("subagent registry steer restarts", () => { await vi.waitFor(assertion, { interval: 1, timeout: 1_000 }); }; - const withPendingAgentWait = async (run: () => Promise): Promise => { - const callGateway = vi.mocked((await import("../gateway/call.js")).callGateway); - const originalCallGateway = callGateway.getMockImplementation(); - callGateway.mockImplementation(async (request: unknown) => { - const typed = request as { method?: string }; - if (typed.method === "agent.wait") { - return new Promise(() => undefined); - } - if (originalCallGateway) { - return originalCallGateway(request as Parameters[0]); - } - return {}; - }); - - try { - return await run(); - } finally { - if (originalCallGateway) { - callGateway.mockImplementation(originalCallGateway); - } - } - }; - const createDeferredAnnounceResolver = (): ((value: boolean) => void) => { let resolveAnnounce!: (value: boolean) => void; announceSpy.mockImplementationOnce( @@ -254,7 +231,7 @@ describe("subagent registry steer restarts", () => { }); it("suppresses announce for interrupted runs and only announces the replacement run", async () => { - await withPendingAgentWait(async () => { + { registerRun({ runId: "run-old", childSessionKey: "agent:main:subagent:steer", @@ -303,11 +280,11 @@ describe("subagent registry steer restarts", () => { const announce = (announceSpy.mock.calls[0]?.[0] ?? {}) as { childRunId?: string }; expect(announce.childRunId).toBe("run-new"); - }); + } }); it("defers subagent_ended hook for completion-mode runs until announce delivery resolves", async () => { - await withPendingAgentWait(async () => { + { const resolveAnnounce = createDeferredAnnounceResolver(); registerCompletionModeRun( "run-completion-delayed", @@ -337,11 +314,11 @@ describe("subagent registry steer restarts", () => { requesterSessionKey: MAIN_REQUESTER_SESSION_KEY, }), ); - }); + } }); it("does not emit subagent_ended on completion for persistent session-mode runs", async () => { - await withPendingAgentWait(async () => { + { const resolveAnnounce = createDeferredAnnounceResolver(); registerCompletionModeRun( "run-persistent-session", @@ -363,11 +340,11 @@ describe("subagent registry steer restarts", () => { expect(run?.runId).toBe("run-persistent-session"); expect(run?.cleanupCompletedAt).toBeTypeOf("number"); expect(run?.endedHookEmittedAt).toBeUndefined(); - }); + } }); it("clears announce retry state when replacing after steer restart", async () => { - await withPendingAgentWait(async () => { + { registerRun({ runId: "run-retry-reset-old", childSessionKey: "agent:main:subagent:retry-reset", @@ -388,11 +365,11 @@ describe("subagent registry steer restarts", () => { }); expect(run.announceRetryCount).toBeUndefined(); expect(run.lastAnnounceRetryAt).toBeUndefined(); - }); + } }); it("clears terminal lifecycle state when replacing after steer restart", async () => { - await withPendingAgentWait(async () => { + { registerRun({ runId: "run-terminal-state-old", childSessionKey: "agent:main:subagent:terminal-state", @@ -434,7 +411,7 @@ describe("subagent registry steer restarts", () => { reason: "subagent-status", }), ); - }); + } }); it("clears frozen completion fields when replacing after steer restart", () => { @@ -707,7 +684,7 @@ describe("subagent registry steer restarts", () => { }); it("retries completion-mode announce delivery with backoff and then gives up after retry limit", async () => { - await withPendingAgentWait(async () => { + { vi.useFakeTimers(); try { announceSpy.mockResolvedValue(false); @@ -742,7 +719,7 @@ describe("subagent registry steer restarts", () => { } finally { vi.useRealTimers(); } - }); + } }); it("keeps completion cleanup pending while descendants are still active", async () => {