diff --git a/src/context-engine/context-engine.test.ts b/src/context-engine/context-engine.test.ts index 261cb97953e7..ff8049132fff 100644 --- a/src/context-engine/context-engine.test.ts +++ b/src/context-engine/context-engine.test.ts @@ -1,6 +1,6 @@ // Context engine tests cover context extraction and prompt context assembly. import type { AgentMessage } from "openclaw/plugin-sdk/agent-core"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { MemoryCitationsMode } from "../config/types.memory.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { @@ -26,7 +26,10 @@ import { resolveContextEngine, resolveContextEngineOwnerPluginId, } from "./registry.js"; -import { resetContextEngineRuntimeQuarantineForTests } from "./registry.test-support.js"; +import { + captureContextEngineRegistryStateForTests, + resetContextEngineRuntimeQuarantineForTests, +} from "./registry.test-support.js"; import type { ContextEngine, ContextEngineInfo, @@ -93,6 +96,16 @@ function makeMockMessage(role: "user" | "assistant" = "user", text = "hello"): A return { role, content: text, timestamp: Date.now() } as AgentMessage; } +let restoreContextEngineRegistry = () => {}; + +beforeAll(() => { + restoreContextEngineRegistry = captureContextEngineRegistryStateForTests(); +}); + +afterAll(() => { + restoreContextEngineRegistry(); +}); + let uniqueEngineIdCounter = 0; function uniqueEngineId(prefix: string): string { uniqueEngineIdCounter += 1; diff --git a/src/context-engine/registry.test-support.ts b/src/context-engine/registry.test-support.ts index be3ae8383274..8feef6218e48 100644 --- a/src/context-engine/registry.test-support.ts +++ b/src/context-engine/registry.test-support.ts @@ -1,18 +1,53 @@ import { resolveGlobalSingleton } from "../shared/global-singleton.js"; -import { clearPersistedContextEngineQuarantineForProcess } from "./quarantine-health.js"; +import { + clearPersistedContextEngineQuarantineForProcess, + recordPersistedContextEngineQuarantine, +} from "./quarantine-health.js"; + +type ContextEngineRuntimeQuarantineForTests = { + engineId: string; + owner?: string; + operation: string; + reason: string; + failedAt: Date; +}; type ContextEngineRegistryStateForTests = { engines: Map; - quarantinedEngines: Map; + quarantinedEngines: Map; }; const CONTEXT_ENGINE_REGISTRY_STATE = Symbol.for("openclaw.contextEngineRegistryState"); -export function resetContextEngineRuntimeQuarantineForTests(): void { - const state = resolveGlobalSingleton( +function getContextEngineRegistryStateForTests(): ContextEngineRegistryStateForTests { + return resolveGlobalSingleton( CONTEXT_ENGINE_REGISTRY_STATE, () => ({ engines: new Map(), quarantinedEngines: new Map() }), ); +} + +export function captureContextEngineRegistryStateForTests(): () => void { + const state = getContextEngineRegistryStateForTests(); + const engines = new Map(state.engines); + const quarantinedEngines = new Map(state.quarantinedEngines); + + return () => { + state.engines.clear(); + for (const [engineId, registration] of engines) { + state.engines.set(engineId, registration); + } + + state.quarantinedEngines.clear(); + clearPersistedContextEngineQuarantineForProcess(undefined, process.pid); + for (const [engineId, quarantine] of quarantinedEngines) { + state.quarantinedEngines.set(engineId, quarantine); + recordPersistedContextEngineQuarantine(quarantine); + } + }; +} + +export function resetContextEngineRuntimeQuarantineForTests(): void { + const state = getContextEngineRegistryStateForTests(); state.quarantinedEngines.clear(); clearPersistedContextEngineQuarantineForProcess(undefined, process.pid); } diff --git a/src/hooks/gmail-watcher.test.ts b/src/hooks/gmail-watcher.test.ts index f378ec0b449d..18f6a2ddbc4d 100644 --- a/src/hooks/gmail-watcher.test.ts +++ b/src/hooks/gmail-watcher.test.ts @@ -1,7 +1,7 @@ // Gmail watcher tests cover watcher events and Gmail hook message flow. import { EventEmitter } from "node:events"; import { expectDefined } from "@openclaw/normalization-core"; -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; const mocks = vi.hoisted(() => ({ hasBinary: vi.fn(() => true), @@ -104,6 +104,11 @@ describe("startGmailWatcher", () => { }); }); + afterEach(async () => { + vi.useRealTimers(); + await stopGmailWatcher(); + }); + it("does not let a stale cancelled startup clear newer watcher config", async () => { vi.useFakeTimers(); try { diff --git a/src/web-search/runtime.test.ts b/src/web-search/runtime.test.ts index c3890c7a6498..0fd9e448e121 100644 --- a/src/web-search/runtime.test.ts +++ b/src/web-search/runtime.test.ts @@ -169,6 +169,7 @@ describe("web search runtime", () => { let runWebSearch: typeof import("./runtime.js").runWebSearch; let activateSecretsRuntimeSnapshot: typeof import("../secrets/runtime.js").activateSecretsRuntimeSnapshot; let clearSecretsRuntimeSnapshot: typeof import("../secrets/runtime.js").clearSecretsRuntimeSnapshot; + let clearRuntimeConfigSnapshot: typeof import("../config/config.js").clearRuntimeConfigSnapshot; let setRuntimeConfigSnapshot: typeof import("../config/config.js").setRuntimeConfigSnapshot; const tempDirs: string[] = []; @@ -176,10 +177,12 @@ describe("web search runtime", () => { ({ hasUsableWebSearchProvider, runWebSearch } = await import("./runtime.js")); ({ activateSecretsRuntimeSnapshot, clearSecretsRuntimeSnapshot } = await import("../secrets/runtime.js")); - ({ setRuntimeConfigSnapshot } = await import("../config/config.js")); + ({ clearRuntimeConfigSnapshot, setRuntimeConfigSnapshot } = + await import("../config/config.js")); }); beforeEach(() => { + clearRuntimeConfigSnapshot(); resolveManifestContractOwnerPluginIdMock.mockReset(); resolvePluginWebSearchProvidersMock.mockReset(); resolveRuntimeWebSearchProvidersMock.mockReset(); @@ -189,6 +192,7 @@ describe("web search runtime", () => { }); afterEach(() => { + clearRuntimeConfigSnapshot(); clearSecretsRuntimeSnapshot(); clearRuntimeAuthProfileStoreSnapshots(); for (const tempDir of tempDirs.splice(0)) {