test: add lighter extensions vitest setup

This commit is contained in:
Peter Steinberger
2026-04-03 11:33:40 +01:00
parent 52225db134
commit e0c4458a2f
7 changed files with 90 additions and 55 deletions

8
test/setup.extensions.ts Normal file
View File

@@ -0,0 +1,8 @@
import { afterAll } from "vitest";
import { installSharedTestSetup } from "./setup.shared.js";
const testEnv = installSharedTestSetup({ loadProfileEnv: false });
afterAll(() => {
testEnv.cleanup();
});

62
test/setup.shared.ts Normal file
View File

@@ -0,0 +1,62 @@
import { vi } from "vitest";
vi.mock("@mariozechner/pi-ai", async (importOriginal) => {
const original = await importOriginal<typeof import("@mariozechner/pi-ai")>();
return {
...original,
getOAuthApiKey: () => undefined,
getOAuthProviders: () => [],
loginOpenAICodex: vi.fn(),
};
});
vi.mock("@mariozechner/clipboard", () => ({
availableFormats: () => [],
getText: async () => "",
setText: async () => {},
hasText: () => false,
getImageBinary: async () => [],
getImageBase64: async () => "",
setImageBinary: async () => {},
setImageBase64: async () => {},
hasImage: () => false,
getHtml: async () => "",
setHtml: async () => {},
hasHtml: () => false,
getRtf: async () => "",
setRtf: async () => {},
hasRtf: () => false,
clear: async () => {},
watch: () => {},
callThreadsafeFunction: () => {},
}));
// Ensure Vitest environment is properly set.
process.env.VITEST = "true";
// Config validation walks plugin manifests; keep an aggressive cache in tests to avoid
// repeated filesystem discovery across suites/workers.
process.env.OPENCLAW_PLUGIN_MANIFEST_CACHE_MS ??= "60000";
// Vitest vm forks can load transitive lockfile helpers many times per worker.
// Raise listener budget to avoid noisy MaxListeners warnings and warning-stack overhead.
const TEST_PROCESS_MAX_LISTENERS = 128;
if (process.getMaxListeners() > 0 && process.getMaxListeners() < TEST_PROCESS_MAX_LISTENERS) {
process.setMaxListeners(TEST_PROCESS_MAX_LISTENERS);
}
import { installProcessWarningFilter } from "../src/infra/warning-filter.js";
import { withIsolatedTestHome } from "./test-env.js";
type SharedTestSetupOptions = {
loadProfileEnv?: boolean;
};
export function installSharedTestSetup(options?: SharedTestSetupOptions): {
cleanup: () => void;
tempHome: string;
} {
const testEnv = withIsolatedTestHome({
loadProfileEnv: options?.loadProfileEnv,
});
installProcessWarningFilter();
return testEnv;
}

View File

@@ -1,48 +1,4 @@
import { afterAll, afterEach, beforeAll, vi } from "vitest";
vi.mock("@mariozechner/pi-ai", async (importOriginal) => {
const original = await importOriginal<typeof import("@mariozechner/pi-ai")>();
return {
...original,
getOAuthApiKey: () => undefined,
getOAuthProviders: () => [],
loginOpenAICodex: vi.fn(),
};
});
vi.mock("@mariozechner/clipboard", () => ({
availableFormats: () => [],
getText: async () => "",
setText: async () => {},
hasText: () => false,
getImageBinary: async () => [],
getImageBase64: async () => "",
setImageBinary: async () => {},
setImageBase64: async () => {},
hasImage: () => false,
getHtml: async () => "",
setHtml: async () => {},
hasHtml: () => false,
getRtf: async () => "",
setRtf: async () => {},
hasRtf: () => false,
clear: async () => {},
watch: () => {},
callThreadsafeFunction: () => {},
}));
// Ensure Vitest environment is properly set
process.env.VITEST = "true";
// Config validation walks plugin manifests; keep an aggressive cache in tests to avoid
// repeated filesystem discovery across suites/workers.
process.env.OPENCLAW_PLUGIN_MANIFEST_CACHE_MS ??= "60000";
// Vitest vm forks can load transitive lockfile helpers many times per worker.
// Raise listener budget to avoid noisy MaxListeners warnings and warning-stack overhead.
const TEST_PROCESS_MAX_LISTENERS = 128;
if (process.getMaxListeners() > 0 && process.getMaxListeners() < TEST_PROCESS_MAX_LISTENERS) {
process.setMaxListeners(TEST_PROCESS_MAX_LISTENERS);
}
import { afterAll, afterEach, beforeAll } from "vitest";
import { resetContextWindowCacheForTest } from "../src/agents/context.js";
import { resetModelsJsonReadyCacheForTest } from "../src/agents/models-config.js";
import {
@@ -57,16 +13,12 @@ import type {
} from "../src/channels/plugins/types.js";
import type { OpenClawConfig } from "../src/config/config.js";
import type { OutboundSendDeps } from "../src/infra/outbound/deliver.js";
import { installProcessWarningFilter } from "../src/infra/warning-filter.js";
import type { PluginRegistry } from "../src/plugins/registry.js";
import { createTestRegistry } from "../src/test-utils/channel-plugins.js";
import { cleanupSessionStateForTest } from "../src/test-utils/session-state-cleanup.js";
import { withIsolatedTestHome } from "./test-env.js";
import { installSharedTestSetup } from "./setup.shared.js";
// Set HOME/state isolation before importing any runtime OpenClaw modules.
const testEnv = withIsolatedTestHome();
installProcessWarningFilter();
const testEnv = installSharedTestSetup({ loadProfileEnv: true });
const REGISTRY_STATE = Symbol.for("openclaw.pluginRegistryState");

View File

@@ -343,7 +343,10 @@ function stageLiveTestState(params: {
restoreClaudeConfigFromBackupIfNeeded(params.tempHome);
}
export function installTestEnv(): { cleanup: () => void; tempHome: string } {
export function installTestEnv(options?: { loadProfileEnv?: boolean }): {
cleanup: () => void;
tempHome: string;
} {
const live =
process.env.LIVE === "1" ||
process.env.OPENCLAW_LIVE_TEST === "1" ||
@@ -352,7 +355,9 @@ export function installTestEnv(): { cleanup: () => void; tempHome: string } {
const realHome = process.env.HOME ?? os.homedir();
const liveEnvSnapshot = { ...process.env };
loadProfileEnv(realHome);
if (options?.loadProfileEnv ?? true) {
loadProfileEnv(realHome);
}
if (live && allowRealHome) {
return { cleanup: () => {}, tempHome: realHome };
@@ -368,6 +373,9 @@ export function installTestEnv(): { cleanup: () => void; tempHome: string } {
return testEnv;
}
export function withIsolatedTestHome(): { cleanup: () => void; tempHome: string } {
return installTestEnv();
export function withIsolatedTestHome(options?: { loadProfileEnv?: boolean }): {
cleanup: () => void;
tempHome: string;
} {
return installTestEnv(options);
}

View File

@@ -53,6 +53,8 @@ export default defineConfig({
"package.json",
"pnpm-lock.yaml",
"test/setup.ts",
"test/setup.shared.ts",
"test/setup.extensions.ts",
"scripts/test-parallel.mjs",
"scripts/test-planner/catalog.mjs",
"scripts/test-planner/executor.mjs",

View File

@@ -16,6 +16,7 @@ export function createExtensionsVitestConfig(
dir: "extensions",
env,
passWithNoTests: true,
setupFiles: ["test/setup.extensions.ts"],
// Most channel implementations stay on the channel surface, but a few
// transport-only suites live better in the general extensions lane.
exclude: extensionExcludedChannelTestGlobs,

View File

@@ -45,6 +45,7 @@ export function createScopedVitestConfig(
exclude?: string[];
pool?: "threads" | "forks";
passWithNoTests?: boolean;
setupFiles?: string[];
},
) {
const base = baseConfig as unknown as Record<string, unknown>;
@@ -79,6 +80,7 @@ export function createScopedVitestConfig(
...(options?.passWithNoTests !== undefined
? { passWithNoTests: options.passWithNoTests }
: {}),
...(options?.setupFiles ? { setupFiles: options.setupFiles } : {}),
},
});
}