mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-06 23:55:12 +00:00
test: cover no-proxy undici startup
This commit is contained in:
@@ -1139,8 +1139,6 @@ describe("fetchWithSsrFGuard hardening", () => {
|
||||
});
|
||||
|
||||
it("inherits the configured global stream timeout for guarded direct dispatchers", async () => {
|
||||
const { getGlobalDispatcher, setGlobalDispatcher } = await import("undici");
|
||||
const previousDispatcher = getGlobalDispatcher();
|
||||
try {
|
||||
ensureGlobalUndiciStreamTimeouts({ timeoutMs: 1_900_000 });
|
||||
(globalThis as Record<string, unknown>)[TEST_UNDICI_RUNTIME_DEPS_KEY] = {
|
||||
@@ -1168,7 +1166,6 @@ describe("fetchWithSsrFGuard hardening", () => {
|
||||
});
|
||||
await result.release();
|
||||
} finally {
|
||||
setGlobalDispatcher(previousDispatcher);
|
||||
resetGlobalUndiciStreamTimeoutsForTests();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -14,13 +14,13 @@ const ORIGINAL_PROXY_ENV = Object.fromEntries(
|
||||
) as Record<(typeof PROXY_ENV_KEYS)[number], string | undefined>;
|
||||
|
||||
const {
|
||||
ProxyAgent,
|
||||
EnvHttpProxyAgent,
|
||||
MockUndiciFormData,
|
||||
undiciFetch,
|
||||
proxyAgentSpy,
|
||||
envAgentSpy,
|
||||
getLastAgent,
|
||||
loadUndiciRuntimeDeps,
|
||||
} = vi.hoisted(() => {
|
||||
const undiciFetch = vi.fn();
|
||||
const proxyAgentSpy = vi.fn();
|
||||
@@ -53,6 +53,12 @@ const {
|
||||
envAgentSpy(options);
|
||||
}
|
||||
}
|
||||
const loadUndiciRuntimeDeps = vi.fn(() => ({
|
||||
ProxyAgent,
|
||||
EnvHttpProxyAgent,
|
||||
FormData: MockUndiciFormData,
|
||||
fetch: undiciFetch,
|
||||
}));
|
||||
|
||||
return {
|
||||
ProxyAgent,
|
||||
@@ -62,16 +68,14 @@ const {
|
||||
proxyAgentSpy,
|
||||
envAgentSpy,
|
||||
getLastAgent: () => ProxyAgent.lastCreated,
|
||||
loadUndiciRuntimeDeps,
|
||||
};
|
||||
});
|
||||
|
||||
const mockedModuleIds = ["undici"] as const;
|
||||
const mockedModuleIds = ["./undici-runtime.js"] as const;
|
||||
|
||||
vi.mock("undici", () => ({
|
||||
ProxyAgent,
|
||||
EnvHttpProxyAgent,
|
||||
FormData: MockUndiciFormData,
|
||||
fetch: undiciFetch,
|
||||
vi.mock("./undici-runtime.js", () => ({
|
||||
loadUndiciRuntimeDeps,
|
||||
}));
|
||||
|
||||
let getProxyUrlFromFetch: typeof import("./proxy-fetch.js").getProxyUrlFromFetch;
|
||||
@@ -248,6 +252,7 @@ describe("resolveProxyFetchFromEnv", () => {
|
||||
|
||||
it("returns undefined when no proxy env vars are set", () => {
|
||||
expect(resolveProxyFetchFromEnv({})).toBeUndefined();
|
||||
expect(loadUndiciRuntimeDeps).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns proxy fetch using EnvHttpProxyAgent when HTTPS_PROXY is set", async () => {
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const {
|
||||
Agent,
|
||||
EnvHttpProxyAgent,
|
||||
ProxyAgent,
|
||||
getGlobalDispatcher,
|
||||
setGlobalDispatcher,
|
||||
setCurrentDispatcher,
|
||||
getCurrentDispatcher,
|
||||
getDefaultAutoSelectFamily,
|
||||
loadUndiciGlobalDispatcherDeps,
|
||||
} = vi.hoisted(() => {
|
||||
class Agent {
|
||||
constructor(public readonly options?: Record<string, unknown>) {}
|
||||
@@ -34,6 +37,12 @@ const {
|
||||
};
|
||||
const getCurrentDispatcher = () => currentDispatcher;
|
||||
const getDefaultAutoSelectFamily = vi.fn(() => undefined as boolean | undefined);
|
||||
const loadUndiciGlobalDispatcherDeps = vi.fn(() => ({
|
||||
Agent,
|
||||
EnvHttpProxyAgent,
|
||||
getGlobalDispatcher,
|
||||
setGlobalDispatcher,
|
||||
}));
|
||||
|
||||
return {
|
||||
Agent,
|
||||
@@ -44,17 +53,11 @@ const {
|
||||
setCurrentDispatcher,
|
||||
getCurrentDispatcher,
|
||||
getDefaultAutoSelectFamily,
|
||||
loadUndiciGlobalDispatcherDeps,
|
||||
};
|
||||
});
|
||||
|
||||
const mockedModuleIds = ["node:net", "undici", "./proxy-env.js", "../wsl.js"] as const;
|
||||
|
||||
vi.mock("undici", () => ({
|
||||
Agent,
|
||||
EnvHttpProxyAgent,
|
||||
getGlobalDispatcher,
|
||||
setGlobalDispatcher,
|
||||
}));
|
||||
const mockedModuleIds = ["node:net", "./proxy-env.js", "./undici-runtime.js", "../wsl.js"] as const;
|
||||
|
||||
vi.mock("node:net", () => ({
|
||||
getDefaultAutoSelectFamily,
|
||||
@@ -65,6 +68,10 @@ vi.mock("./proxy-env.js", () => ({
|
||||
resolveEnvHttpProxyAgentOptions: vi.fn(() => undefined),
|
||||
}));
|
||||
|
||||
vi.mock("./undici-runtime.js", () => ({
|
||||
loadUndiciGlobalDispatcherDeps,
|
||||
}));
|
||||
|
||||
vi.mock("../wsl.js", () => ({
|
||||
isWSL2Sync: vi.fn(() => false),
|
||||
}));
|
||||
@@ -99,24 +106,53 @@ describe("ensureGlobalUndiciStreamTimeouts", () => {
|
||||
vi.mocked(resolveEnvHttpProxyAgentOptions).mockReturnValue(undefined);
|
||||
});
|
||||
|
||||
it("replaces default Agent dispatcher with extended stream timeouts", () => {
|
||||
it("records timeout bridge without importing undici when no env proxy is configured", () => {
|
||||
getDefaultAutoSelectFamily.mockReturnValue(true);
|
||||
|
||||
ensureGlobalUndiciStreamTimeouts();
|
||||
|
||||
expect(setGlobalDispatcher).toHaveBeenCalledTimes(1);
|
||||
const next = getCurrentDispatcher() as { options?: Record<string, unknown> };
|
||||
expect(next).toBeInstanceOf(Agent);
|
||||
expect(next.options?.bodyTimeout).toBe(DEFAULT_UNDICI_STREAM_TIMEOUT_MS);
|
||||
expect(next.options?.headersTimeout).toBe(DEFAULT_UNDICI_STREAM_TIMEOUT_MS);
|
||||
expect(next.options?.connect).toEqual({
|
||||
autoSelectFamily: true,
|
||||
autoSelectFamilyAttemptTimeout: 300,
|
||||
});
|
||||
expect(loadUndiciGlobalDispatcherDeps).not.toHaveBeenCalled();
|
||||
expect(setGlobalDispatcher).not.toHaveBeenCalled();
|
||||
expect(undiciGlobalDispatcherModule._globalUndiciStreamTimeoutMs).toBe(
|
||||
DEFAULT_UNDICI_STREAM_TIMEOUT_MS,
|
||||
);
|
||||
});
|
||||
|
||||
it("does not initialize the undici global dispatcher in a no-proxy subprocess", () => {
|
||||
const moduleUrl = pathToFileURL(path.resolve("src/infra/net/undici-global-dispatcher.ts")).href;
|
||||
const source = `
|
||||
const dispatcherKey = Symbol.for("undici.globalDispatcher.1");
|
||||
const mod = await import(${JSON.stringify(moduleUrl)});
|
||||
mod.ensureGlobalUndiciStreamTimeouts({ timeoutMs: 1_900_000 });
|
||||
if (globalThis[dispatcherKey] !== undefined) {
|
||||
throw new Error("undici global dispatcher was initialized");
|
||||
}
|
||||
console.log("ok");
|
||||
`;
|
||||
const env = { ...process.env };
|
||||
for (const key of [
|
||||
"HTTP_PROXY",
|
||||
"HTTPS_PROXY",
|
||||
"ALL_PROXY",
|
||||
"http_proxy",
|
||||
"https_proxy",
|
||||
"all_proxy",
|
||||
]) {
|
||||
delete env[key];
|
||||
}
|
||||
|
||||
const output = execFileSync(
|
||||
process.execPath,
|
||||
["--import", "tsx", "--input-type=module", "--eval", source],
|
||||
{ cwd: process.cwd(), encoding: "utf8", env },
|
||||
);
|
||||
|
||||
expect(output.trim()).toBe("ok");
|
||||
});
|
||||
|
||||
it("replaces EnvHttpProxyAgent dispatcher while preserving env-proxy mode", () => {
|
||||
getDefaultAutoSelectFamily.mockReturnValue(false);
|
||||
vi.mocked(hasEnvHttpProxyAgentConfigured).mockReturnValue(true);
|
||||
setCurrentDispatcher(new EnvHttpProxyAgent());
|
||||
|
||||
ensureGlobalUndiciStreamTimeouts();
|
||||
@@ -133,6 +169,7 @@ describe("ensureGlobalUndiciStreamTimeouts", () => {
|
||||
});
|
||||
|
||||
it("preserves explicit env proxy options when replacing EnvHttpProxyAgent dispatcher", () => {
|
||||
vi.mocked(hasEnvHttpProxyAgentConfigured).mockReturnValue(true);
|
||||
vi.mocked(resolveEnvHttpProxyAgentOptions).mockReturnValue({
|
||||
httpProxy: "socks5://proxy.test:1080",
|
||||
httpsProxy: "socks5://proxy.test:1080",
|
||||
@@ -165,6 +202,8 @@ describe("ensureGlobalUndiciStreamTimeouts", () => {
|
||||
|
||||
it("is idempotent for unchanged dispatcher kind and network policy", () => {
|
||||
getDefaultAutoSelectFamily.mockReturnValue(true);
|
||||
vi.mocked(hasEnvHttpProxyAgentConfigured).mockReturnValue(true);
|
||||
setCurrentDispatcher(new EnvHttpProxyAgent());
|
||||
|
||||
ensureGlobalUndiciStreamTimeouts();
|
||||
ensureGlobalUndiciStreamTimeouts();
|
||||
@@ -175,10 +214,11 @@ describe("ensureGlobalUndiciStreamTimeouts", () => {
|
||||
it("does not lower global stream timeouts below the default floor", () => {
|
||||
ensureGlobalUndiciStreamTimeouts({ timeoutMs: 15_000 });
|
||||
|
||||
expect(setGlobalDispatcher).toHaveBeenCalledTimes(1);
|
||||
const next = getCurrentDispatcher() as { options?: Record<string, unknown> };
|
||||
expect(next.options?.bodyTimeout).toBe(DEFAULT_UNDICI_STREAM_TIMEOUT_MS);
|
||||
expect(next.options?.headersTimeout).toBe(DEFAULT_UNDICI_STREAM_TIMEOUT_MS);
|
||||
expect(loadUndiciGlobalDispatcherDeps).not.toHaveBeenCalled();
|
||||
expect(setGlobalDispatcher).not.toHaveBeenCalled();
|
||||
expect(undiciGlobalDispatcherModule._globalUndiciStreamTimeoutMs).toBe(
|
||||
DEFAULT_UNDICI_STREAM_TIMEOUT_MS,
|
||||
);
|
||||
});
|
||||
|
||||
it("honors explicit global stream timeouts above the default floor", () => {
|
||||
@@ -186,13 +226,14 @@ describe("ensureGlobalUndiciStreamTimeouts", () => {
|
||||
|
||||
ensureGlobalUndiciStreamTimeouts({ timeoutMs });
|
||||
|
||||
expect(setGlobalDispatcher).toHaveBeenCalledTimes(1);
|
||||
const next = getCurrentDispatcher() as { options?: Record<string, unknown> };
|
||||
expect(next.options?.bodyTimeout).toBe(timeoutMs);
|
||||
expect(next.options?.headersTimeout).toBe(timeoutMs);
|
||||
expect(loadUndiciGlobalDispatcherDeps).not.toHaveBeenCalled();
|
||||
expect(setGlobalDispatcher).not.toHaveBeenCalled();
|
||||
expect(undiciGlobalDispatcherModule._globalUndiciStreamTimeoutMs).toBe(timeoutMs);
|
||||
});
|
||||
|
||||
it("re-applies when autoSelectFamily decision changes", () => {
|
||||
vi.mocked(hasEnvHttpProxyAgentConfigured).mockReturnValue(true);
|
||||
setCurrentDispatcher(new EnvHttpProxyAgent());
|
||||
getDefaultAutoSelectFamily.mockReturnValue(true);
|
||||
ensureGlobalUndiciStreamTimeouts();
|
||||
|
||||
@@ -210,12 +251,14 @@ describe("ensureGlobalUndiciStreamTimeouts", () => {
|
||||
it("disables autoSelectFamily on WSL2 to avoid IPv6 connectivity issues", () => {
|
||||
getDefaultAutoSelectFamily.mockReturnValue(true);
|
||||
vi.mocked(isWSL2Sync).mockReturnValue(true);
|
||||
vi.mocked(hasEnvHttpProxyAgentConfigured).mockReturnValue(true);
|
||||
setCurrentDispatcher(new EnvHttpProxyAgent());
|
||||
|
||||
ensureGlobalUndiciStreamTimeouts();
|
||||
|
||||
expect(setGlobalDispatcher).toHaveBeenCalledTimes(1);
|
||||
const next = getCurrentDispatcher() as { options?: Record<string, unknown> };
|
||||
expect(next).toBeInstanceOf(Agent);
|
||||
expect(next).toBeInstanceOf(EnvHttpProxyAgent);
|
||||
expect(next.options?.connect).toEqual({
|
||||
autoSelectFamily: false,
|
||||
autoSelectFamilyAttemptTimeout: 300,
|
||||
@@ -313,11 +356,26 @@ describe("forceResetGlobalDispatcher", () => {
|
||||
vi.mocked(resolveEnvHttpProxyAgentOptions).mockReturnValue(undefined);
|
||||
});
|
||||
|
||||
it("replaces an EnvHttpProxyAgent with a direct Agent when proxy env is cleared", () => {
|
||||
it("does not import undici when proxy env is cleared", () => {
|
||||
setCurrentDispatcher(new EnvHttpProxyAgent());
|
||||
|
||||
forceResetGlobalDispatcher();
|
||||
|
||||
expect(loadUndiciGlobalDispatcherDeps).not.toHaveBeenCalled();
|
||||
expect(setGlobalDispatcher).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("restores a direct Agent when clearing a proxy dispatcher installed by OpenClaw", () => {
|
||||
vi.mocked(hasEnvHttpProxyAgentConfigured).mockReturnValue(true);
|
||||
ensureGlobalUndiciEnvProxyDispatcher();
|
||||
expect(getCurrentDispatcher()).toBeInstanceOf(EnvHttpProxyAgent);
|
||||
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(hasEnvHttpProxyAgentConfigured).mockReturnValue(false);
|
||||
|
||||
forceResetGlobalDispatcher();
|
||||
|
||||
expect(loadUndiciGlobalDispatcherDeps).toHaveBeenCalledTimes(1);
|
||||
expect(setGlobalDispatcher).toHaveBeenCalledTimes(1);
|
||||
expect(getCurrentDispatcher()).toBeInstanceOf(Agent);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user