mirror of https://github.com/openclaw/openclaw.git
test: stabilize test isolation
This commit is contained in:
parent
ae336d1602
commit
d67efbfbd3
|
|
@ -29,6 +29,13 @@ let callGatewayTool: typeof import("./tools/gateway.js").callGatewayTool;
|
|||
let createExecTool: typeof import("./bash-tools.exec.js").createExecTool;
|
||||
let detectCommandObfuscation: typeof import("../infra/exec-obfuscation-detect.js").detectCommandObfuscation;
|
||||
|
||||
async function loadExecApprovalModules() {
|
||||
vi.resetModules();
|
||||
({ callGatewayTool } = await import("./tools/gateway.js"));
|
||||
({ createExecTool } = await import("./bash-tools.exec.js"));
|
||||
({ detectCommandObfuscation } = await import("../infra/exec-obfuscation-detect.js"));
|
||||
}
|
||||
|
||||
function buildPreparedSystemRunPayload(rawInvokeParams: unknown) {
|
||||
const invoke = (rawInvokeParams ?? {}) as {
|
||||
params?: {
|
||||
|
|
@ -210,10 +217,7 @@ describe("exec approvals", () => {
|
|||
process.env.HOME = tempDir;
|
||||
// Windows uses USERPROFILE for os.homedir()
|
||||
process.env.USERPROFILE = tempDir;
|
||||
vi.resetModules();
|
||||
({ callGatewayTool } = await import("./tools/gateway.js"));
|
||||
({ createExecTool } = await import("./bash-tools.exec.js"));
|
||||
({ detectCommandObfuscation } = await import("../infra/exec-obfuscation-detect.js"));
|
||||
await loadExecApprovalModules();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import os from "node:os";
|
|||
import path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { registerLogTransport, resetLogger, setLoggerOverride } from "../logging/logger.js";
|
||||
import type { AuthProfileStore } from "./auth-profiles.js";
|
||||
import { makeModelFallbackCfg } from "./test-helpers/model-fallback-config-fixture.js";
|
||||
|
||||
|
|
@ -15,24 +14,53 @@ vi.mock("./auth-profiles.js", () => ({
|
|||
resolveAuthProfileOrder: vi.fn(),
|
||||
}));
|
||||
|
||||
import {
|
||||
ensureAuthProfileStore,
|
||||
getSoonestCooldownExpiry,
|
||||
isProfileInCooldown,
|
||||
resolveProfilesUnavailableReason,
|
||||
resolveAuthProfileOrder,
|
||||
} from "./auth-profiles.js";
|
||||
import { _probeThrottleInternals, runWithModelFallback } from "./model-fallback.js";
|
||||
type AuthProfilesModule = typeof import("./auth-profiles.js");
|
||||
type ModelFallbackModule = typeof import("./model-fallback.js");
|
||||
type LoggerModule = typeof import("../logging/logger.js");
|
||||
|
||||
const mockedEnsureAuthProfileStore = vi.mocked(ensureAuthProfileStore);
|
||||
const mockedGetSoonestCooldownExpiry = vi.mocked(getSoonestCooldownExpiry);
|
||||
const mockedIsProfileInCooldown = vi.mocked(isProfileInCooldown);
|
||||
const mockedResolveProfilesUnavailableReason = vi.mocked(resolveProfilesUnavailableReason);
|
||||
const mockedResolveAuthProfileOrder = vi.mocked(resolveAuthProfileOrder);
|
||||
let mockedEnsureAuthProfileStore: ReturnType<
|
||||
typeof vi.mocked<AuthProfilesModule["ensureAuthProfileStore"]>
|
||||
>;
|
||||
let mockedGetSoonestCooldownExpiry: ReturnType<
|
||||
typeof vi.mocked<AuthProfilesModule["getSoonestCooldownExpiry"]>
|
||||
>;
|
||||
let mockedIsProfileInCooldown: ReturnType<
|
||||
typeof vi.mocked<AuthProfilesModule["isProfileInCooldown"]>
|
||||
>;
|
||||
let mockedResolveProfilesUnavailableReason: ReturnType<
|
||||
typeof vi.mocked<AuthProfilesModule["resolveProfilesUnavailableReason"]>
|
||||
>;
|
||||
let mockedResolveAuthProfileOrder: ReturnType<
|
||||
typeof vi.mocked<AuthProfilesModule["resolveAuthProfileOrder"]>
|
||||
>;
|
||||
let runWithModelFallback: ModelFallbackModule["runWithModelFallback"];
|
||||
let _probeThrottleInternals: ModelFallbackModule["_probeThrottleInternals"];
|
||||
let registerLogTransport: LoggerModule["registerLogTransport"];
|
||||
let resetLogger: LoggerModule["resetLogger"];
|
||||
let setLoggerOverride: LoggerModule["setLoggerOverride"];
|
||||
|
||||
const makeCfg = makeModelFallbackCfg;
|
||||
let unregisterLogTransport: (() => void) | undefined;
|
||||
|
||||
async function loadModelFallbackProbeModules() {
|
||||
vi.resetModules();
|
||||
const authProfilesModule = await import("./auth-profiles.js");
|
||||
const loggerModule = await import("../logging/logger.js");
|
||||
const modelFallbackModule = await import("./model-fallback.js");
|
||||
mockedEnsureAuthProfileStore = vi.mocked(authProfilesModule.ensureAuthProfileStore);
|
||||
mockedGetSoonestCooldownExpiry = vi.mocked(authProfilesModule.getSoonestCooldownExpiry);
|
||||
mockedIsProfileInCooldown = vi.mocked(authProfilesModule.isProfileInCooldown);
|
||||
mockedResolveProfilesUnavailableReason = vi.mocked(
|
||||
authProfilesModule.resolveProfilesUnavailableReason,
|
||||
);
|
||||
mockedResolveAuthProfileOrder = vi.mocked(authProfilesModule.resolveAuthProfileOrder);
|
||||
runWithModelFallback = modelFallbackModule.runWithModelFallback;
|
||||
_probeThrottleInternals = modelFallbackModule._probeThrottleInternals;
|
||||
registerLogTransport = loggerModule.registerLogTransport;
|
||||
resetLogger = loggerModule.resetLogger;
|
||||
setLoggerOverride = loggerModule.setLoggerOverride;
|
||||
}
|
||||
|
||||
function expectFallbackUsed(
|
||||
result: { result: unknown; attempts: Array<{ reason?: string }> },
|
||||
run: {
|
||||
|
|
@ -131,7 +159,8 @@ describe("runWithModelFallback – probe logic", () => {
|
|||
run,
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
await loadModelFallbackProbeModules();
|
||||
realDateNow = Date.now;
|
||||
Date.now = vi.fn(() => NOW);
|
||||
|
||||
|
|
@ -159,7 +188,7 @@ describe("runWithModelFallback – probe logic", () => {
|
|||
return [];
|
||||
});
|
||||
// Default: only openai profiles are in cooldown; fallback providers are available
|
||||
mockedIsProfileInCooldown.mockImplementation((_store, profileId: string) => {
|
||||
mockedIsProfileInCooldown.mockImplementation((_store: AuthProfileStore, profileId: string) => {
|
||||
return profileId.startsWith("openai");
|
||||
});
|
||||
mockedResolveProfilesUnavailableReason.mockReturnValue("rate_limit");
|
||||
|
|
@ -355,7 +384,7 @@ describe("runWithModelFallback – probe logic", () => {
|
|||
}
|
||||
return [];
|
||||
});
|
||||
mockedIsProfileInCooldown.mockImplementation((_store, profileId: string) =>
|
||||
mockedIsProfileInCooldown.mockImplementation((_store: AuthProfileStore, profileId: string) =>
|
||||
profileId.startsWith("google"),
|
||||
);
|
||||
mockedGetSoonestCooldownExpiry.mockReturnValue(NOW + 30 * 1000);
|
||||
|
|
|
|||
|
|
@ -11,12 +11,35 @@ import type {
|
|||
} from "../../plugin-sdk/media-understanding.js";
|
||||
import { withFetchPreconnect } from "../../test-utils/fetch-mock.js";
|
||||
import { minimaxUnderstandImage } from "../minimax-vlm.js";
|
||||
import { createOpenClawCodingTools } from "../pi-tools.js";
|
||||
import type { SandboxFsBridge } from "../sandbox/fs-bridge.js";
|
||||
import { createHostSandboxFsBridge } from "../test-helpers/host-sandbox-fs-bridge.js";
|
||||
import { createUnsafeMountedSandbox } from "../test-helpers/unsafe-mounted-sandbox.js";
|
||||
import { makeZeroUsageSnapshot } from "../usage.js";
|
||||
import { __testing, createImageTool, resolveImageModelConfigForTool } from "./image-tool.js";
|
||||
|
||||
type PiToolsModule = typeof import("../pi-tools.js");
|
||||
type CreateOpenClawCodingToolsArgs = Parameters<PiToolsModule["createOpenClawCodingTools"]>[0];
|
||||
type MockOpenClawToolsOptions = {
|
||||
config?: OpenClawConfig;
|
||||
agentDir?: string;
|
||||
workspaceDir?: string;
|
||||
sandboxRoot?: string;
|
||||
sandboxFsBridge?: SandboxFsBridge;
|
||||
fsPolicy?: NonNullable<Parameters<typeof createImageTool>[0]>["fsPolicy"];
|
||||
modelHasVision?: boolean;
|
||||
};
|
||||
|
||||
const piToolsHarness = vi.hoisted(() => ({
|
||||
createStubTool(name: string) {
|
||||
return {
|
||||
name,
|
||||
description: `${name} stub`,
|
||||
parameters: { type: "object", properties: {} },
|
||||
execute: vi.fn(),
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
const imageProviderHarness = vi.hoisted(() => {
|
||||
let providers = new Map<string, MediaUnderstandingProvider>();
|
||||
return {
|
||||
|
|
@ -63,6 +86,50 @@ vi.mock("../../media-understanding/provider-registry.js", async (importOriginal)
|
|||
};
|
||||
});
|
||||
|
||||
vi.mock("../bash-tools.js", () => ({
|
||||
createExecTool: vi.fn(() => piToolsHarness.createStubTool("exec")),
|
||||
createProcessTool: vi.fn(() => piToolsHarness.createStubTool("process")),
|
||||
}));
|
||||
|
||||
vi.mock("../channel-tools.js", () => ({
|
||||
listChannelAgentTools: vi.fn(() => []),
|
||||
}));
|
||||
|
||||
vi.mock("../apply-patch.js", () => ({
|
||||
createApplyPatchTool: vi.fn(() => piToolsHarness.createStubTool("apply_patch")),
|
||||
}));
|
||||
|
||||
vi.mock("../pi-tools.before-tool-call.js", () => ({
|
||||
wrapToolWithBeforeToolCallHook: vi.fn((tool) => tool),
|
||||
}));
|
||||
|
||||
vi.mock("../pi-tools.abort.js", () => ({
|
||||
wrapToolWithAbortSignal: vi.fn((tool) => tool),
|
||||
}));
|
||||
|
||||
vi.mock("../openclaw-tools.js", async () => {
|
||||
const { createImageTool } = await import("./image-tool.js");
|
||||
return {
|
||||
createOpenClawTools: vi.fn((options?: MockOpenClawToolsOptions) => {
|
||||
const imageTool = createImageTool({
|
||||
config: options?.config,
|
||||
agentDir: options?.agentDir,
|
||||
workspaceDir: options?.workspaceDir,
|
||||
sandbox:
|
||||
options?.sandboxRoot && options?.sandboxFsBridge
|
||||
? {
|
||||
root: options.sandboxRoot,
|
||||
bridge: options.sandboxFsBridge,
|
||||
}
|
||||
: undefined,
|
||||
fsPolicy: options?.fsPolicy,
|
||||
modelHasVision: options?.modelHasVision,
|
||||
});
|
||||
return imageTool ? [imageTool] : [];
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
async function writeAuthProfiles(agentDir: string, profiles: unknown) {
|
||||
await fs.mkdir(agentDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
|
|
@ -72,6 +139,12 @@ async function writeAuthProfiles(agentDir: string, profiles: unknown) {
|
|||
);
|
||||
}
|
||||
|
||||
async function createOpenClawCodingToolsWithFreshModules(options?: CreateOpenClawCodingToolsArgs) {
|
||||
vi.resetModules();
|
||||
const { createOpenClawCodingTools } = await import("../pi-tools.js");
|
||||
return createOpenClawCodingTools(options);
|
||||
}
|
||||
|
||||
async function withTempAgentDir<T>(run: (agentDir: string) => Promise<T>): Promise<T> {
|
||||
const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-image-"));
|
||||
try {
|
||||
|
|
@ -732,7 +805,11 @@ describe("image tool implicit imageModel config", () => {
|
|||
await withTempAgentDir(async (agentDir) => {
|
||||
const cfg = createMinimaxImageConfig();
|
||||
|
||||
const tools = createOpenClawCodingTools({ config: cfg, agentDir, workspaceDir });
|
||||
const tools = await createOpenClawCodingToolsWithFreshModules({
|
||||
config: cfg,
|
||||
agentDir,
|
||||
workspaceDir,
|
||||
});
|
||||
const tool = requireImageTool(tools.find((candidate) => candidate.name === "image"));
|
||||
|
||||
await expectImageToolExecOk(tool, imagePath);
|
||||
|
|
@ -776,7 +853,7 @@ describe("image tool implicit imageModel config", () => {
|
|||
tools: { fs: { workspaceOnly: true } },
|
||||
};
|
||||
|
||||
const tools = createOpenClawCodingTools({
|
||||
const tools = await createOpenClawCodingToolsWithFreshModules({
|
||||
config: cfg,
|
||||
agentDir,
|
||||
sandbox,
|
||||
|
|
|
|||
|
|
@ -110,8 +110,15 @@ vi.mock("../logger.js", async (importOriginal) => {
|
|||
};
|
||||
});
|
||||
|
||||
const { GatewayClient } = await import("./client.js");
|
||||
type GatewayClientInstance = InstanceType<typeof GatewayClient>;
|
||||
type GatewayClientModule = typeof import("./client.js");
|
||||
type GatewayClientInstance = InstanceType<GatewayClientModule["GatewayClient"]>;
|
||||
|
||||
let GatewayClient: GatewayClientModule["GatewayClient"];
|
||||
|
||||
async function loadGatewayClientModule() {
|
||||
vi.resetModules();
|
||||
({ GatewayClient } = await import("./client.js"));
|
||||
}
|
||||
|
||||
function getLatestWs(): MockWebSocket {
|
||||
const ws = wsInstances.at(-1);
|
||||
|
|
@ -153,6 +160,10 @@ function expectSecurityConnectError(
|
|||
}
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
await loadGatewayClientModule();
|
||||
});
|
||||
|
||||
describe("GatewayClient security checks", () => {
|
||||
const envSnapshot = captureEnv(["OPENCLAW_ALLOW_INSECURE_PRIVATE_WS"]);
|
||||
|
||||
|
|
|
|||
|
|
@ -25,9 +25,13 @@ vi.mock("../plugins/channel-plugin-ids.js", () => ({
|
|||
resolveGatewayStartupPluginIds,
|
||||
}));
|
||||
|
||||
vi.mock("../channels/plugins/binding-registry.js", () => ({
|
||||
primeConfiguredBindingRegistry,
|
||||
}));
|
||||
vi.mock("../channels/plugins/binding-registry.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../channels/plugins/binding-registry.js")>();
|
||||
return {
|
||||
...actual,
|
||||
primeConfiguredBindingRegistry,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("./server-methods.js", () => ({
|
||||
handleGatewayRequest,
|
||||
|
|
|
|||
Loading…
Reference in New Issue