





















@@ -3,11 +3,39 @@ import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
33import { createProviderUsageFetch } from "../test-utils/provider-usage-fetch.js";
4455const resolveProviderUsageSnapshotWithPluginMock = vi.fn();
6+const { EnvHttpProxyAgent, envAgentSpy, loadUndiciRuntimeDeps, undiciFetch } = vi.hoisted(() => {
7+const envAgentSpyLocal = vi.fn();
8+const undiciFetchLocal = vi.fn();
9+class EnvHttpProxyAgentLocal {
10+static lastCreated: EnvHttpProxyAgentLocal | undefined;
11+12+constructor(public readonly options?: Record<string, unknown>) {
13+EnvHttpProxyAgentLocal.lastCreated = this;
14+envAgentSpyLocal(options);
15+}
16+}
17+const loadUndiciRuntimeDepsLocal = vi.fn(() => ({
18+EnvHttpProxyAgent: EnvHttpProxyAgentLocal,
19+FormData: globalThis.FormData,
20+fetch: undiciFetchLocal,
21+}));
22+23+return {
24+EnvHttpProxyAgent: EnvHttpProxyAgentLocal,
25+envAgentSpy: envAgentSpyLocal,
26+loadUndiciRuntimeDeps: loadUndiciRuntimeDepsLocal,
27+undiciFetch: undiciFetchLocal,
28+};
29+});
630731vi.mock("../config/config.js", () => ({
832getRuntimeConfig: () => ({}),
933}));
103435+vi.mock("./net/undici-runtime.js", () => ({
36+ loadUndiciRuntimeDeps,
37+}));
38+1139vi.mock("../plugins/provider-runtime.js", async () => {
1240const actual = await vi.importActual<typeof import("../plugins/provider-runtime.js")>(
1341"../plugins/provider-runtime.js",
@@ -30,6 +58,7 @@ function requireFirstPluginUsageCall(): {
3058token?: unknown;
3159authProfileId?: unknown;
3260timeoutMs?: unknown;
61+fetchFn?: unknown;
3362};
3463} {
3564const [call] = resolveProviderUsageSnapshotWithPluginMock.mock.calls;
@@ -47,16 +76,36 @@ function requireFirstPluginUsageCall(): {
4776token?: unknown;
4877authProfileId?: unknown;
4978timeoutMs?: unknown;
79+fetchFn?: unknown;
5080};
5181};
5282}
538384+function requireFetchFn(value: unknown): typeof fetch {
85+if (typeof value !== "function") {
86+throw new Error("expected provider usage context fetch");
87+}
88+return value as typeof fetch;
89+}
90+91+function requireUndiciFetchInit(): Record<string, unknown> {
92+const init = undiciFetch.mock.calls[0]?.[1];
93+if (!init || typeof init !== "object" || Array.isArray(init)) {
94+throw new Error("expected undici fetch init");
95+}
96+return init as Record<string, unknown>;
97+}
98+5499describe("provider-usage.load plugin boundary", () => {
55100beforeAll(async () => {
56101({ loadProviderUsageSummary } = await import("./provider-usage.load.js"));
57102});
5810359104beforeEach(() => {
105+envAgentSpy.mockClear();
106+loadUndiciRuntimeDeps.mockClear();
107+undiciFetch.mockReset();
108+EnvHttpProxyAgent.lastCreated = undefined;
60109resolveProviderUsageSnapshotWithPluginMock.mockReset();
61110resolveProviderUsageSnapshotWithPluginMock.mockResolvedValue(null);
62111});
@@ -76,6 +125,7 @@ describe("provider-usage.load plugin boundary", () => {
76125now: usageNow,
77126auth: [{ provider: "github-copilot", token: "copilot-token" }],
78127fetch: mockFetch as unknown as typeof fetch,
128+env: {},
79129}),
80130).resolves.toEqual({
81131updatedAt: usageNow,
@@ -115,6 +165,7 @@ describe("provider-usage.load plugin boundary", () => {
115165hookProvider: "codex",
116166},
117167],
168+env: {},
118169}),
119170).resolves.toEqual({
120171updatedAt: usageNow,
@@ -133,4 +184,88 @@ describe("provider-usage.load plugin boundary", () => {
133184expect(pluginCall.context?.token).toBe("codex-app-server");
134185expect(pluginCall.context?.authProfileId).toBe("openai:work");
135186});
187+188+it("passes an env proxy fetch into plugin usage context when no explicit fetch is supplied", async () => {
189+undiciFetch.mockResolvedValueOnce(new Response("{}", { status: 200 }));
190+resolveProviderUsageSnapshotWithPluginMock.mockImplementationOnce(async (params: unknown) => {
191+if (!params || typeof params !== "object" || Array.isArray(params)) {
192+throw new Error("expected plugin params");
193+}
194+const context = (params as { context?: { fetchFn?: unknown } }).context;
195+await requireFetchFn(context?.fetchFn)("https://chatgpt.com/backend-api/wham/usage");
196+return {
197+provider: "openai",
198+displayName: "Codex",
199+windows: [{ label: "5h", usedPercent: 7 }],
200+};
201+});
202+203+await expect(
204+loadProviderUsageSummary({
205+now: usageNow,
206+auth: [{ provider: "openai", token: "codex-token", accountId: "acc-1" }],
207+env: {
208+HTTP_PROXY: "",
209+HTTPS_PROXY: "http://proxy.test:8080",
210+},
211+}),
212+).resolves.toEqual({
213+updatedAt: usageNow,
214+providers: [
215+{
216+provider: "openai",
217+displayName: "Codex",
218+windows: [{ label: "5h", usedPercent: 7 }],
219+},
220+],
221+});
222+223+expect(envAgentSpy).toHaveBeenCalledWith({ httpsProxy: "http://proxy.test:8080" });
224+expect(undiciFetch).toHaveBeenCalledOnce();
225+const [input] = undiciFetch.mock.calls[0] ?? [];
226+expect(input).toBe("https://chatgpt.com/backend-api/wham/usage");
227+expect(requireUndiciFetchInit().dispatcher).toBe(EnvHttpProxyAgent.lastCreated);
228+});
229+230+it("keeps an explicit fetch ahead of proxy env for plugin usage context", async () => {
231+const explicitFetch = vi.fn(async () => new Response("{}", { status: 200 }));
232+resolveProviderUsageSnapshotWithPluginMock.mockImplementationOnce(async (params: unknown) => {
233+if (!params || typeof params !== "object" || Array.isArray(params)) {
234+throw new Error("expected plugin params");
235+}
236+const context = (params as { context?: { fetchFn?: unknown } }).context;
237+await requireFetchFn(context?.fetchFn)("https://chatgpt.com/backend-api/wham/usage");
238+return {
239+provider: "openai",
240+displayName: "Codex",
241+windows: [{ label: "5h", usedPercent: 9 }],
242+};
243+});
244+245+await expect(
246+loadProviderUsageSummary({
247+now: usageNow,
248+auth: [{ provider: "openai", token: "codex-token", accountId: "acc-1" }],
249+env: {
250+HTTP_PROXY: "",
251+HTTPS_PROXY: "http://proxy.test:8080",
252+},
253+fetch: explicitFetch as unknown as typeof fetch,
254+}),
255+).resolves.toEqual({
256+updatedAt: usageNow,
257+providers: [
258+{
259+provider: "openai",
260+displayName: "Codex",
261+windows: [{ label: "5h", usedPercent: 9 }],
262+},
263+],
264+});
265+266+expect(explicitFetch).toHaveBeenCalledOnce();
267+expect(loadUndiciRuntimeDeps).not.toHaveBeenCalled();
268+expect(envAgentSpy).not.toHaveBeenCalled();
269+expect(undiciFetch).not.toHaveBeenCalled();
270+});
136271});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。