Files
openclaw/src/infra/provider-usage.load.plugin.test.ts
Andy Ye 0a9b1526ac fix(provider-usage): honor proxy env for usage fetch (#93943)
* fix(provider-usage): honor proxy env for usage fetch

* refactor(mcp): remove unused Claude permission type
2026-06-22 16:56:07 +00:00

272 lines
8.6 KiB
TypeScript

// Tests provider usage loading from plugin-provided sources.
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { createProviderUsageFetch } from "../test-utils/provider-usage-fetch.js";
const resolveProviderUsageSnapshotWithPluginMock = vi.fn();
const { EnvHttpProxyAgent, envAgentSpy, loadUndiciRuntimeDeps, undiciFetch } = vi.hoisted(() => {
const envAgentSpyLocal = vi.fn();
const undiciFetchLocal = vi.fn();
class EnvHttpProxyAgentLocal {
static lastCreated: EnvHttpProxyAgentLocal | undefined;
constructor(public readonly options?: Record<string, unknown>) {
EnvHttpProxyAgentLocal.lastCreated = this;
envAgentSpyLocal(options);
}
}
const loadUndiciRuntimeDepsLocal = vi.fn(() => ({
EnvHttpProxyAgent: EnvHttpProxyAgentLocal,
FormData: globalThis.FormData,
fetch: undiciFetchLocal,
}));
return {
EnvHttpProxyAgent: EnvHttpProxyAgentLocal,
envAgentSpy: envAgentSpyLocal,
loadUndiciRuntimeDeps: loadUndiciRuntimeDepsLocal,
undiciFetch: undiciFetchLocal,
};
});
vi.mock("../config/config.js", () => ({
getRuntimeConfig: () => ({}),
}));
vi.mock("./net/undici-runtime.js", () => ({
loadUndiciRuntimeDeps,
}));
vi.mock("../plugins/provider-runtime.js", async () => {
const actual = await vi.importActual<typeof import("../plugins/provider-runtime.js")>(
"../plugins/provider-runtime.js",
);
return {
...actual,
resolveProviderUsageSnapshotWithPlugin: (...args: unknown[]) =>
resolveProviderUsageSnapshotWithPluginMock(...args),
};
});
let loadProviderUsageSummary: typeof import("./provider-usage.load.js").loadProviderUsageSummary;
const usageNow = Date.UTC(2026, 0, 7, 0, 0, 0);
function requireFirstPluginUsageCall(): {
provider?: unknown;
context?: {
provider?: unknown;
token?: unknown;
authProfileId?: unknown;
timeoutMs?: unknown;
fetchFn?: unknown;
};
} {
const [call] = resolveProviderUsageSnapshotWithPluginMock.mock.calls;
if (!call) {
throw new Error("expected provider usage plugin call");
}
const [pluginCall] = call;
if (!pluginCall || typeof pluginCall !== "object" || Array.isArray(pluginCall)) {
throw new Error("expected provider usage plugin call");
}
return pluginCall as {
provider?: unknown;
context?: {
provider?: unknown;
token?: unknown;
authProfileId?: unknown;
timeoutMs?: unknown;
fetchFn?: unknown;
};
};
}
function requireFetchFn(value: unknown): typeof fetch {
if (typeof value !== "function") {
throw new Error("expected provider usage context fetch");
}
return value as typeof fetch;
}
function requireUndiciFetchInit(): Record<string, unknown> {
const init = undiciFetch.mock.calls[0]?.[1];
if (!init || typeof init !== "object" || Array.isArray(init)) {
throw new Error("expected undici fetch init");
}
return init as Record<string, unknown>;
}
describe("provider-usage.load plugin boundary", () => {
beforeAll(async () => {
({ loadProviderUsageSummary } = await import("./provider-usage.load.js"));
});
beforeEach(() => {
envAgentSpy.mockClear();
loadUndiciRuntimeDeps.mockClear();
undiciFetch.mockReset();
EnvHttpProxyAgent.lastCreated = undefined;
resolveProviderUsageSnapshotWithPluginMock.mockReset();
resolveProviderUsageSnapshotWithPluginMock.mockResolvedValue(null);
});
it("prefers plugin-owned usage snapshots", async () => {
resolveProviderUsageSnapshotWithPluginMock.mockResolvedValueOnce({
provider: "github-copilot",
displayName: "Copilot",
windows: [{ label: "Plugin", usedPercent: 11 }],
});
const mockFetch = createProviderUsageFetch(async () => {
throw new Error("legacy fetch should not run");
});
await expect(
loadProviderUsageSummary({
now: usageNow,
auth: [{ provider: "github-copilot", token: "copilot-token" }],
fetch: mockFetch as unknown as typeof fetch,
env: {},
}),
).resolves.toEqual({
updatedAt: usageNow,
providers: [
{
provider: "github-copilot",
displayName: "Copilot",
windows: [{ label: "Plugin", usedPercent: 11 }],
},
],
});
expect(mockFetch).not.toHaveBeenCalled();
expect(resolveProviderUsageSnapshotWithPluginMock).toHaveBeenCalledOnce();
const pluginCall = requireFirstPluginUsageCall();
expect(pluginCall.provider).toBe("github-copilot");
expect(pluginCall.context?.provider).toBe("github-copilot");
expect(pluginCall.context?.token).toBe("copilot-token");
expect(pluginCall.context?.timeoutMs).toBe(5_000);
});
it("routes synthetic Codex usage through the Codex hook while preserving OpenAI context", async () => {
resolveProviderUsageSnapshotWithPluginMock.mockResolvedValueOnce({
provider: "openai",
displayName: "OpenAI",
windows: [{ label: "5h", usedPercent: 9 }],
});
await expect(
loadProviderUsageSummary({
now: usageNow,
auth: [
{
provider: "openai",
token: "codex-app-server",
authProfileId: "openai:work",
hookProvider: "codex",
},
],
env: {},
}),
).resolves.toEqual({
updatedAt: usageNow,
providers: [
{
provider: "openai",
displayName: "OpenAI",
windows: [{ label: "5h", usedPercent: 9 }],
},
],
});
const pluginCall = requireFirstPluginUsageCall();
expect(pluginCall.provider).toBe("codex");
expect(pluginCall.context?.provider).toBe("openai");
expect(pluginCall.context?.token).toBe("codex-app-server");
expect(pluginCall.context?.authProfileId).toBe("openai:work");
});
it("passes an env proxy fetch into plugin usage context when no explicit fetch is supplied", async () => {
undiciFetch.mockResolvedValueOnce(new Response("{}", { status: 200 }));
resolveProviderUsageSnapshotWithPluginMock.mockImplementationOnce(async (params: unknown) => {
if (!params || typeof params !== "object" || Array.isArray(params)) {
throw new Error("expected plugin params");
}
const context = (params as { context?: { fetchFn?: unknown } }).context;
await requireFetchFn(context?.fetchFn)("https://chatgpt.com/backend-api/wham/usage");
return {
provider: "openai",
displayName: "Codex",
windows: [{ label: "5h", usedPercent: 7 }],
};
});
await expect(
loadProviderUsageSummary({
now: usageNow,
auth: [{ provider: "openai", token: "codex-token", accountId: "acc-1" }],
env: {
HTTP_PROXY: "",
HTTPS_PROXY: "http://proxy.test:8080",
},
}),
).resolves.toEqual({
updatedAt: usageNow,
providers: [
{
provider: "openai",
displayName: "Codex",
windows: [{ label: "5h", usedPercent: 7 }],
},
],
});
expect(envAgentSpy).toHaveBeenCalledWith({ httpsProxy: "http://proxy.test:8080" });
expect(undiciFetch).toHaveBeenCalledOnce();
const [input] = undiciFetch.mock.calls[0] ?? [];
expect(input).toBe("https://chatgpt.com/backend-api/wham/usage");
expect(requireUndiciFetchInit().dispatcher).toBe(EnvHttpProxyAgent.lastCreated);
});
it("keeps an explicit fetch ahead of proxy env for plugin usage context", async () => {
const explicitFetch = vi.fn(async () => new Response("{}", { status: 200 }));
resolveProviderUsageSnapshotWithPluginMock.mockImplementationOnce(async (params: unknown) => {
if (!params || typeof params !== "object" || Array.isArray(params)) {
throw new Error("expected plugin params");
}
const context = (params as { context?: { fetchFn?: unknown } }).context;
await requireFetchFn(context?.fetchFn)("https://chatgpt.com/backend-api/wham/usage");
return {
provider: "openai",
displayName: "Codex",
windows: [{ label: "5h", usedPercent: 9 }],
};
});
await expect(
loadProviderUsageSummary({
now: usageNow,
auth: [{ provider: "openai", token: "codex-token", accountId: "acc-1" }],
env: {
HTTP_PROXY: "",
HTTPS_PROXY: "http://proxy.test:8080",
},
fetch: explicitFetch as unknown as typeof fetch,
}),
).resolves.toEqual({
updatedAt: usageNow,
providers: [
{
provider: "openai",
displayName: "Codex",
windows: [{ label: "5h", usedPercent: 9 }],
},
],
});
expect(explicitFetch).toHaveBeenCalledOnce();
expect(loadUndiciRuntimeDeps).not.toHaveBeenCalled();
expect(envAgentSpy).not.toHaveBeenCalled();
expect(undiciFetch).not.toHaveBeenCalled();
});
});