diff --git a/extensions/google/api.test.ts b/extensions/google/api.test.ts index 01592a27cbd5..7f42e08a276e 100644 --- a/extensions/google/api.test.ts +++ b/extensions/google/api.test.ts @@ -1,11 +1,15 @@ // Google tests cover api plugin behavior. import { describe, expect, it } from "vitest"; import { + applyGoogleGeminiModelDefault, + GOOGLE_GEMINI_DEFAULT_MODEL, isGoogleGenerativeAiApi, isGoogleVertexBaseUrl, isGoogleVertexHostname, + normalizeAntigravityModelId, normalizeGoogleApiBaseUrl, normalizeGoogleGenerativeAiBaseUrl, + normalizeGoogleModelId, normalizeGoogleProviderConfig, parseGeminiAuth, resolveGoogleGenerativeAiHttpRequestConfig, @@ -13,8 +17,23 @@ import { resolveGoogleGenerativeAiTransport, shouldNormalizeGoogleGenerativeAiProviderConfig, } from "./api.js"; +import { + normalizeAntigravityModelId as normalizeAntigravityModelIdDirect, + normalizeGoogleModelId as normalizeGoogleModelIdDirect, +} from "./model-id.js"; +import { + applyGoogleGeminiModelDefault as applyGoogleGeminiModelDefaultDirect, + GOOGLE_GEMINI_DEFAULT_MODEL as GOOGLE_GEMINI_DEFAULT_MODEL_DIRECT, +} from "./onboard.js"; describe("google generative ai helpers", () => { + it("re-exports model normalization and default helpers", () => { + expect(normalizeAntigravityModelId).toBe(normalizeAntigravityModelIdDirect); + expect(normalizeGoogleModelId).toBe(normalizeGoogleModelIdDirect); + expect(applyGoogleGeminiModelDefault).toBe(applyGoogleGeminiModelDefaultDirect); + expect(GOOGLE_GEMINI_DEFAULT_MODEL).toBe(GOOGLE_GEMINI_DEFAULT_MODEL_DIRECT); + }); + it("detects the Google Generative AI transport id", () => { expect(isGoogleGenerativeAiApi("google-generative-ai")).toBe(true); expect(isGoogleGenerativeAiApi("google-gemini-cli")).toBe(false); diff --git a/extensions/google/default-model.test.ts b/extensions/google/default-model.test.ts index b873152506f8..8d045ca7e5a9 100644 --- a/extensions/google/default-model.test.ts +++ b/extensions/google/default-model.test.ts @@ -1,7 +1,7 @@ // Google tests cover default model plugin behavior. import type { OpenClawConfig } from "openclaw/plugin-sdk/provider-onboard"; import { describe, expect, it } from "vitest"; -import { applyGoogleGeminiModelDefault, GOOGLE_GEMINI_DEFAULT_MODEL } from "./api.js"; +import { applyGoogleGeminiModelDefault, GOOGLE_GEMINI_DEFAULT_MODEL } from "./onboard.js"; describe("google default model", () => { it("sets defaults when model is unset", () => { diff --git a/extensions/google/model-id.test.ts b/extensions/google/model-id.test.ts index 52361d029ce0..800132eff8e7 100644 --- a/extensions/google/model-id.test.ts +++ b/extensions/google/model-id.test.ts @@ -1,6 +1,6 @@ // Google tests cover model id plugin behavior. import { describe, expect, it } from "vitest"; -import { normalizeAntigravityModelId, normalizeGoogleModelId } from "./api.js"; +import { normalizeAntigravityModelId, normalizeGoogleModelId } from "./model-id.js"; describe("google model id helpers", () => { it.each(["gemini-3-pro", "gemini-3.1-pro", "gemini-3-1-pro"])( diff --git a/extensions/xai/api.test.ts b/extensions/xai/api.test.ts index 9ba4bc25ccae..edd10adb3c34 100644 --- a/extensions/xai/api.test.ts +++ b/extensions/xai/api.test.ts @@ -1,8 +1,18 @@ // Xai tests cover api plugin behavior. import { describe, expect, it } from "vitest"; -import { resolveXaiForwardCompatModel, resolveXaiTransport, XAI_BASE_URL } from "./api.js"; +import { + normalizeXaiModelId, + resolveXaiForwardCompatModel, + resolveXaiTransport, + XAI_BASE_URL, +} from "./api.js"; +import { normalizeXaiModelId as normalizeXaiModelIdDirect } from "./model-id.js"; describe("xai api helpers", () => { + it("re-exports the model normalizer", () => { + expect(normalizeXaiModelId).toBe(normalizeXaiModelIdDirect); + }); + it("uses shared endpoint classification for native xAI transports", () => { expect( resolveXaiTransport({ diff --git a/extensions/xai/model-id.test.ts b/extensions/xai/model-id.test.ts index a6eb07ab38ec..a80edbe44ca0 100644 --- a/extensions/xai/model-id.test.ts +++ b/extensions/xai/model-id.test.ts @@ -1,6 +1,6 @@ // Xai tests cover model id plugin behavior. import { describe, expect, it } from "vitest"; -import { normalizeXaiModelId } from "./api.js"; +import { normalizeXaiModelId } from "./model-id.js"; describe("normalizeXaiModelId", () => { it("normalizes family-specific aliases but preserves the global alias", () => { diff --git a/src/plugin-sdk/webhook-targets.test.ts b/src/plugin-sdk/webhook-targets.test.ts index 0a0c3babfe33..a1bcb8406d49 100644 --- a/src/plugin-sdk/webhook-targets.test.ts +++ b/src/plugin-sdk/webhook-targets.test.ts @@ -4,7 +4,7 @@ import { EventEmitter } from "node:events"; import type { IncomingMessage, ServerResponse } from "node:http"; import { afterEach, describe, expect, it, vi } from "vitest"; -import { createEmptyPluginRegistry } from "../plugins/registry.js"; +import { createEmptyPluginRegistry } from "../plugins/registry-empty.js"; import { setActivePluginRegistry } from "../plugins/runtime.js"; import { createWebhookInFlightLimiter } from "./webhook-request-guards.js"; import { diff --git a/src/plugins/doctor-contract-registry.state-migrations.test.ts b/src/plugins/doctor-contract-registry.state-migrations.test.ts index 7c808e2e7d2f..c02592da42bf 100644 --- a/src/plugins/doctor-contract-registry.state-migrations.test.ts +++ b/src/plugins/doctor-contract-registry.state-migrations.test.ts @@ -1,7 +1,7 @@ // Covers plugin doctor state-migration registry behavior. import fs from "node:fs"; import path from "node:path"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { cleanupTrackedTempDirs, makeTrackedTempDir } from "./test-helpers/fs-fixtures.js"; import { getRegistryJitiMocks, @@ -39,9 +39,7 @@ afterEach(() => { }); describe("doctor-contract-registry state migrations", () => { - beforeEach(async () => { - resetRegistryJitiMocks(); - doctorContractWarnMock.mockReset(); + beforeAll(async () => { vi.resetModules(); ({ listPluginDoctorLegacyConfigRules, listPluginDoctorStateMigrationEntries } = await import("./doctor-contract-registry.js")); @@ -49,6 +47,17 @@ describe("doctor-contract-registry state migrations", () => { clearPluginDoctorContractRegistryCache, setPluginDoctorContractRegistryModuleLoaderFactoryForTest, } = await import("./doctor-contract-registry.test-fixtures.js")); + }); + + beforeEach(() => { + resetRegistryJitiMocks(); + doctorContractWarnMock.mockReset(); + // Loaded once in beforeAll; afterEach guards the same binding optionally because it + // can fire when that import never completed. Fail loudly here instead of silently + // running a case against the real module loader. + if (!setPluginDoctorContractRegistryModuleLoaderFactoryForTest) { + throw new Error("doctor contract registry test fixtures were not loaded"); + } setPluginDoctorContractRegistryModuleLoaderFactoryForTest(mocks.createJiti); clearPluginDoctorContractRegistryCache(); }); diff --git a/src/plugins/doctor-contract-registry.test.ts b/src/plugins/doctor-contract-registry.test.ts index 253ece8fd3c3..328f11a6b24d 100644 --- a/src/plugins/doctor-contract-registry.test.ts +++ b/src/plugins/doctor-contract-registry.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs"; import path from "node:path"; import { pathToFileURL } from "node:url"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { withMockedPlatform } from "../test-utils/vitest-spies.js"; import { resolvePluginDoctorContractArtifactPath } from "./doctor-contract-artifact.js"; import { cleanupTrackedTempDirs, makeTrackedTempDir } from "./test-helpers/fs-fixtures.js"; @@ -54,10 +54,7 @@ afterEach(() => { }); describe("doctor-contract-registry module loader", () => { - beforeEach(async () => { - resetRegistryJitiMocks(); - mocks.loadPluginManifestRegistry.mockReturnValue({ plugins: [], diagnostics: [] }); - doctorContractWarnMock.mockReset(); + beforeAll(async () => { vi.resetModules(); ({ applyPluginDoctorCompatibilityMigrations, @@ -71,6 +68,18 @@ describe("doctor-contract-registry module loader", () => { clearPluginDoctorContractRegistryCache, setPluginDoctorContractRegistryModuleLoaderFactoryForTest, } = await import("./doctor-contract-registry.test-fixtures.js")); + }); + + beforeEach(() => { + resetRegistryJitiMocks(); + mocks.loadPluginManifestRegistry.mockReturnValue({ plugins: [], diagnostics: [] }); + doctorContractWarnMock.mockReset(); + // Loaded once in beforeAll; afterEach guards the same binding optionally because it + // can fire when that import never completed. Fail loudly here instead of silently + // running a case against the real module loader. + if (!setPluginDoctorContractRegistryModuleLoaderFactoryForTest) { + throw new Error("doctor contract registry test fixtures were not loaded"); + } setPluginDoctorContractRegistryModuleLoaderFactoryForTest(mocks.createJiti); clearPluginDoctorContractRegistryCache(); }); diff --git a/src/plugins/hook-resolve-exec-env.test.ts b/src/plugins/hook-resolve-exec-env.test.ts index 19e7c03b0b09..9c15bdb7a011 100644 --- a/src/plugins/hook-resolve-exec-env.test.ts +++ b/src/plugins/hook-resolve-exec-env.test.ts @@ -2,7 +2,7 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import { createHookRunner } from "./hooks.js"; import { addStaticTestHooks, addTestHook } from "./hooks.test-fixtures.js"; -import { createEmptyPluginRegistry } from "./registry.js"; +import { createEmptyPluginRegistry } from "./registry-empty.js"; import type { PluginHookResolveExecEnvContext } from "./types.js"; const ctx: PluginHookResolveExecEnvContext = { diff --git a/src/plugins/hooks.before-install.test.ts b/src/plugins/hooks.before-install.test.ts index 311cc81eeb83..6f102a256d2a 100644 --- a/src/plugins/hooks.before-install.test.ts +++ b/src/plugins/hooks.before-install.test.ts @@ -2,7 +2,8 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { createHookRunner } from "./hooks.js"; import { addTestHook } from "./hooks.test-fixtures.js"; -import { createEmptyPluginRegistry, type PluginRegistry } from "./registry.js"; +import { createEmptyPluginRegistry } from "./registry-empty.js"; +import type { PluginRegistry } from "./registry.js"; import type { PluginHookBeforeInstallContext, PluginHookBeforeInstallEvent, diff --git a/src/plugins/hooks.before-tool-call.test.ts b/src/plugins/hooks.before-tool-call.test.ts index 28fe5a061369..3e4112ac1012 100644 --- a/src/plugins/hooks.before-tool-call.test.ts +++ b/src/plugins/hooks.before-tool-call.test.ts @@ -6,7 +6,8 @@ import { configureRuntimeActionDecisionSink } from "../audit/runtime-action-deci import { createHookRunner } from "./hooks.js"; import { addStaticTestHooks } from "./hooks.test-fixtures.js"; import { addTestHook } from "./hooks.test-helpers.js"; -import { createEmptyPluginRegistry, type PluginRegistry } from "./registry.js"; +import { createEmptyPluginRegistry } from "./registry-empty.js"; +import type { PluginRegistry } from "./registry.js"; import type { PluginHookBeforeToolCallEvent, PluginHookBeforeToolCallResult, diff --git a/src/plugins/hooks.correlation.test.ts b/src/plugins/hooks.correlation.test.ts index 858e09b6be79..75c5d70f9cbf 100644 --- a/src/plugins/hooks.correlation.test.ts +++ b/src/plugins/hooks.correlation.test.ts @@ -4,7 +4,8 @@ import { MAX_TIMER_TIMEOUT_MS } from "@openclaw/normalization-core/number-coerci import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { createHookRunner } from "./hooks.js"; import { addTestHook, TEST_PLUGIN_AGENT_CTX } from "./hooks.test-fixtures.js"; -import { createEmptyPluginRegistry, type PluginRegistry } from "./registry.js"; +import { createEmptyPluginRegistry } from "./registry-empty.js"; +import type { PluginRegistry } from "./registry.js"; import type { PluginHookRegistration } from "./types.js"; describe("hook correlation fields", () => { @@ -116,7 +117,10 @@ describe("hook correlation fields", () => { { cwd: process.cwd(), encoding: "utf8", - timeout: 3_000, + // Boots tsx in a child process, so the deadline must cover compile time on a + // loaded machine. A tight bound kills the probe (status null) and fails the + // assertions below for a reason unrelated to the hook timeout under test. + timeout: 30_000, }, ); oneShotAgentEndProbe = { diff --git a/src/plugins/hooks.phase-hooks.test.ts b/src/plugins/hooks.phase-hooks.test.ts index 8d133838f299..bd55c26fe6a6 100644 --- a/src/plugins/hooks.phase-hooks.test.ts +++ b/src/plugins/hooks.phase-hooks.test.ts @@ -4,7 +4,8 @@ import { applyEmbeddedAttemptToolsAllow } from "../agents/embedded-agent-runner/ import { readToolAllowlistIntersection } from "../agents/tool-policy.js"; import { createHookRunner } from "./hooks.js"; import { addStaticTestHooks } from "./hooks.test-fixtures.js"; -import { createEmptyPluginRegistry, type PluginRegistry } from "./registry.js"; +import { createEmptyPluginRegistry } from "./registry-empty.js"; +import type { PluginRegistry } from "./registry.js"; import type { PluginHookBeforeModelResolveResult, PluginHookBeforePromptBuildResult, diff --git a/src/plugins/hooks.security.test.ts b/src/plugins/hooks.security.test.ts index 4f0a45a3297e..94cb9110a2fc 100644 --- a/src/plugins/hooks.security.test.ts +++ b/src/plugins/hooks.security.test.ts @@ -2,7 +2,8 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { createHookRunner } from "./hooks.js"; import { addStaticTestHooks } from "./hooks.test-fixtures.js"; -import { createEmptyPluginRegistry, type PluginRegistry } from "./registry.js"; +import { createEmptyPluginRegistry } from "./registry-empty.js"; +import type { PluginRegistry } from "./registry.js"; import type { PluginHookBeforeToolCallResult, PluginHookMessageSendingResult } from "./types.js"; const toolEvent = { toolName: "bash", params: { command: "echo hello" } }; diff --git a/src/utils/provider-utils.test.ts b/src/utils/provider-utils.test.ts index 12bb7356bb59..ef437c22f80d 100644 --- a/src/utils/provider-utils.test.ts +++ b/src/utils/provider-utils.test.ts @@ -5,15 +5,9 @@ const { resolveProviderReasoningOutputModeWithPluginMock } = vi.hoisted(() => ({ resolveProviderReasoningOutputModeWithPluginMock: vi.fn(), })); -vi.mock("../plugins/provider-runtime.js", async () => { - const actual = await vi.importActual( - "../plugins/provider-runtime.js", - ); - return { - ...actual, - resolveProviderReasoningOutputModeWithPlugin: resolveProviderReasoningOutputModeWithPluginMock, - }; -}); +vi.mock("../plugins/provider-runtime.js", () => ({ + resolveProviderReasoningOutputModeWithPlugin: resolveProviderReasoningOutputModeWithPluginMock, +})); import { isReasoningTagProvider } from "./provider-utils.js";