diff --git a/extensions/microsoft/speech-provider.test.ts b/extensions/microsoft/speech-provider.test.ts index ce0989559f8..fbe52717da3 100644 --- a/extensions/microsoft/speech-provider.test.ts +++ b/extensions/microsoft/speech-provider.test.ts @@ -3,6 +3,7 @@ import os from "node:os"; import path from "node:path"; import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime"; import { afterEach, describe, expect, it, vi } from "vitest"; +import { installDebugProxyTestResetHooks } from "../test-support/debug-proxy-env-test-helpers.js"; vi.mock("node-edge-tts", () => ({ EdgeTTS: class { @@ -20,28 +21,7 @@ import * as ttsModule from "./tts.js"; const TEST_CFG = {} as OpenClawConfig; describe("listMicrosoftVoices", () => { - const originalFetch = globalThis.fetch; - const proxyEnvKeys = [ - "OPENCLAW_DEBUG_PROXY_ENABLED", - "OPENCLAW_DEBUG_PROXY_DB_PATH", - "OPENCLAW_DEBUG_PROXY_BLOB_DIR", - "OPENCLAW_DEBUG_PROXY_SESSION_ID", - ] as const; - let priorProxyEnv: Partial> = {}; - - afterEach(() => { - globalThis.fetch = originalFetch; - vi.restoreAllMocks(); - for (const key of proxyEnvKeys) { - const value = priorProxyEnv[key]; - if (value === undefined) { - delete process.env[key]; - } else { - process.env[key] = value; - } - } - priorProxyEnv = {}; - }); + const proxyReset = installDebugProxyTestResetHooks(); it("maps Microsoft voice metadata into speech voice options", async () => { globalThis.fetch = vi.fn().mockResolvedValue( @@ -89,9 +69,7 @@ describe("listMicrosoftVoices", () => { it("records voice discovery exchanges in debug proxy capture mode", async () => { const tempDir = mkdtempSync(path.join(os.tmpdir(), "microsoft-voices-capture-")); - priorProxyEnv = Object.fromEntries( - proxyEnvKeys.map((key) => [key, process.env[key]]), - ) as typeof priorProxyEnv; + proxyReset.captureProxyEnv(); process.env.OPENCLAW_DEBUG_PROXY_ENABLED = "1"; process.env.OPENCLAW_DEBUG_PROXY_DB_PATH = path.join(tempDir, "capture.sqlite"); process.env.OPENCLAW_DEBUG_PROXY_BLOB_DIR = path.join(tempDir, "blobs"); @@ -134,9 +112,7 @@ describe("listMicrosoftVoices", () => { it("does not double-capture voice discovery when the global fetch patch is installed", async () => { const tempDir = mkdtempSync(path.join(os.tmpdir(), "microsoft-voices-global-")); - priorProxyEnv = Object.fromEntries( - proxyEnvKeys.map((key) => [key, process.env[key]]), - ) as typeof priorProxyEnv; + proxyReset.captureProxyEnv(); process.env.OPENCLAW_DEBUG_PROXY_ENABLED = "1"; process.env.OPENCLAW_DEBUG_PROXY_DB_PATH = path.join(tempDir, "capture.sqlite"); process.env.OPENCLAW_DEBUG_PROXY_BLOB_DIR = path.join(tempDir, "blobs"); @@ -175,7 +151,7 @@ describe("listMicrosoftVoices", () => { const kinds = events.map((event) => String(event.kind)).toSorted(); expect(kinds).toEqual(["request", "response"]); } finally { - globalThis.fetch = originalFetch; + globalThis.fetch = proxyReset.originalFetch; finalizeDebugProxyCapture(); } }); diff --git a/extensions/openai/tts.test.ts b/extensions/openai/tts.test.ts index 35153712cd7..2860f57e59b 100644 --- a/extensions/openai/tts.test.ts +++ b/extensions/openai/tts.test.ts @@ -1,7 +1,8 @@ import { mkdtempSync } from "node:fs"; import os from "node:os"; import path from "node:path"; -import { afterEach, describe, expect, it, vi } from "vitest"; +import { describe, expect, it, vi } from "vitest"; +import { installDebugProxyTestResetHooks } from "../test-support/debug-proxy-env-test-helpers.js"; import { isValidOpenAIModel, isValidOpenAIVoice, @@ -12,28 +13,7 @@ import { } from "./tts.js"; describe("openai tts", () => { - const originalFetch = globalThis.fetch; - const proxyEnvKeys = [ - "OPENCLAW_DEBUG_PROXY_ENABLED", - "OPENCLAW_DEBUG_PROXY_DB_PATH", - "OPENCLAW_DEBUG_PROXY_BLOB_DIR", - "OPENCLAW_DEBUG_PROXY_SESSION_ID", - ] as const; - let priorProxyEnv: Partial> = {}; - - afterEach(() => { - globalThis.fetch = originalFetch; - vi.restoreAllMocks(); - for (const key of proxyEnvKeys) { - const value = priorProxyEnv[key]; - if (value === undefined) { - delete process.env[key]; - } else { - process.env[key] = value; - } - } - priorProxyEnv = {}; - }); + const proxyReset = installDebugProxyTestResetHooks(); describe("isValidOpenAIVoice", () => { it("accepts all valid OpenAI voices including newer additions", () => { @@ -205,9 +185,7 @@ describe("openai tts", () => { it("records TTS exchanges in debug proxy capture mode", async () => { const tempDir = mkdtempSync(path.join(os.tmpdir(), "openai-tts-capture-")); - priorProxyEnv = Object.fromEntries( - proxyEnvKeys.map((key) => [key, process.env[key]]), - ) as typeof priorProxyEnv; + proxyReset.captureProxyEnv(); process.env.OPENCLAW_DEBUG_PROXY_ENABLED = "1"; process.env.OPENCLAW_DEBUG_PROXY_DB_PATH = path.join(tempDir, "capture.sqlite"); process.env.OPENCLAW_DEBUG_PROXY_BLOB_DIR = path.join(tempDir, "blobs"); @@ -256,9 +234,7 @@ describe("openai tts", () => { it("does not double-capture TTS exchanges when the global fetch patch is installed", async () => { const tempDir = mkdtempSync(path.join(os.tmpdir(), "openai-tts-patched-capture-")); - priorProxyEnv = Object.fromEntries( - proxyEnvKeys.map((key) => [key, process.env[key]]), - ) as typeof priorProxyEnv; + proxyReset.captureProxyEnv(); process.env.OPENCLAW_DEBUG_PROXY_ENABLED = "1"; process.env.OPENCLAW_DEBUG_PROXY_DB_PATH = path.join(tempDir, "capture.sqlite"); process.env.OPENCLAW_DEBUG_PROXY_BLOB_DIR = path.join(tempDir, "blobs"); diff --git a/extensions/test-support/debug-proxy-env-test-helpers.ts b/extensions/test-support/debug-proxy-env-test-helpers.ts new file mode 100644 index 00000000000..895c558ac5f --- /dev/null +++ b/extensions/test-support/debug-proxy-env-test-helpers.ts @@ -0,0 +1,47 @@ +import { afterEach, vi } from "vitest"; + +export const DEBUG_PROXY_ENV_KEYS = [ + "OPENCLAW_DEBUG_PROXY_ENABLED", + "OPENCLAW_DEBUG_PROXY_DB_PATH", + "OPENCLAW_DEBUG_PROXY_BLOB_DIR", + "OPENCLAW_DEBUG_PROXY_SESSION_ID", +] as const; + +type DebugProxyEnvKey = (typeof DEBUG_PROXY_ENV_KEYS)[number]; +type DebugProxyEnvSnapshot = Partial>; + +function snapshotDebugProxyEnv(): DebugProxyEnvSnapshot { + return Object.fromEntries( + DEBUG_PROXY_ENV_KEYS.map((key) => [key, process.env[key]]), + ) as DebugProxyEnvSnapshot; +} + +function restoreDebugProxyEnv(snapshot: DebugProxyEnvSnapshot): void { + for (const key of DEBUG_PROXY_ENV_KEYS) { + const value = snapshot[key]; + if (value === undefined) { + delete process.env[key]; + } else { + process.env[key] = value; + } + } +} + +export function installDebugProxyTestResetHooks() { + const originalFetch = globalThis.fetch; + let priorProxyEnv: DebugProxyEnvSnapshot = {}; + + afterEach(() => { + globalThis.fetch = originalFetch; + vi.restoreAllMocks(); + restoreDebugProxyEnv(priorProxyEnv); + priorProxyEnv = {}; + }); + + return { + captureProxyEnv() { + priorProxyEnv = snapshotDebugProxyEnv(); + }, + originalFetch, + }; +}