test: stop re-evaluating plugin module graphs for every test case (#131522)

The doctor contract registry suites called vi.resetModules() plus a dynamic
re-import in beforeEach, so all 54 cases re-evaluated the whole doctor owner
graph: setup policy, migration adapters, config normalization, loader state,
and native/SDK alias resolution. Load once in beforeAll instead and keep the
per-case mock resets, loader injection, cache clearing, and temp-dir cleanup
where they were. setup-registry.test.ts already used this shape.

doctor-contract-registry.test.ts: 29.7s -> 18.2s (-39%), import 508ms -> 136ms.

Also narrow imports that pulled broad graphs in for static work:
seven files reached createEmptyPluginRegistry through registry.js (the whole
registry construction graph) instead of its real owner registry-empty.js;
three provider tests imported api.js, paying for transport streaming,
provider auth, and image-generation SDK graphs to test string normalization;
and provider-utils.test.ts used importActual() to spread a runtime whose only
used export it immediately replaced.

Neither provider barrel suite actually asserted the narrowed exports, so add
explicit re-export assertions in extensions/{google,xai}/api.test.ts rather
than let that coverage lapse.

Unrelated flake fixed in passing: hooks.correlation.test.ts spawns a tsx
subprocess under a 3s deadline, which is killed under load and reports
status null. It failed 3/3 on unmodified code and passes 4/4 at 30s. No
assertion changed.

Test-only change: zero production LOC.
This commit is contained in:
Peter Steinberger
2026-08-27 21:45:58 -07:00
committed by GitHub
parent cd7b282ed8
commit 153a6f59bf
15 changed files with 79 additions and 30 deletions
+19
View File
@@ -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);
+1 -1
View File
@@ -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", () => {
+1 -1
View File
@@ -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"])(
+11 -1
View File
@@ -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({
+1 -1
View File
@@ -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", () => {
+1 -1
View File
@@ -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 {
@@ -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();
});
+14 -5
View File
@@ -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();
});
+1 -1
View File
@@ -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 = {
+2 -1
View File
@@ -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,
+2 -1
View File
@@ -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,
+6 -2
View File
@@ -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 = {
+2 -1
View File
@@ -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,
+2 -1
View File
@@ -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" } };
+3 -9
View File
@@ -5,15 +5,9 @@ const { resolveProviderReasoningOutputModeWithPluginMock } = vi.hoisted(() => ({
resolveProviderReasoningOutputModeWithPluginMock: vi.fn(),
}));
vi.mock("../plugins/provider-runtime.js", async () => {
const actual = await vi.importActual<typeof import("../plugins/provider-runtime.js")>(
"../plugins/provider-runtime.js",
);
return {
...actual,
resolveProviderReasoningOutputModeWithPlugin: resolveProviderReasoningOutputModeWithPluginMock,
};
});
vi.mock("../plugins/provider-runtime.js", () => ({
resolveProviderReasoningOutputModeWithPlugin: resolveProviderReasoningOutputModeWithPluginMock,
}));
import { isReasoningTagProvider } from "./provider-utils.js";