























@@ -43,6 +43,37 @@ function installGoogleFetchMock(params?: {
4343return fetchMock;
4444}
454546+function fetchRequest(fetchMock: ReturnType<typeof vi.fn>): {
47+body?: string;
48+headers?: HeadersInit;
49+method?: string;
50+url: string;
51+} {
52+const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit | undefined];
53+expect(typeof url).toBe("string");
54+expect(init).toBeDefined();
55+return {
56+body: typeof init?.body === "string" ? init.body : undefined,
57+headers: init?.headers,
58+method: init?.method,
59+ url,
60+};
61+}
62+63+function postJsonRequestOptions(spy: unknown): {
64+allowPrivateNetwork?: boolean;
65+pinDns?: boolean;
66+ssrfPolicy?: { allowRfc2544BenchmarkRange?: boolean };
67+} {
68+const options = (spy as { mock?: { calls?: Array<[unknown]> } }).mock?.calls?.[0]?.[0];
69+expect(options).toBeDefined();
70+return options as {
71+allowPrivateNetwork?: boolean;
72+pinDns?: boolean;
73+ssrfPolicy?: { allowRfc2544BenchmarkRange?: boolean };
74+};
75+}
76+4677describe("Google image-generation provider", () => {
4778afterEach(() => {
4879vi.restoreAllMocks();
@@ -86,27 +117,26 @@ describe("Google image-generation provider", () => {
86117size: "1536x1024",
87118});
8811989-expect(fetchMock).toHaveBeenCalledWith(
120+const request = fetchRequest(fetchMock);
121+expect(request.url).toBe(
90122"https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-flash-image-preview:generateContent",
91-expect.objectContaining({
92-method: "POST",
93-body: JSON.stringify({
94-contents: [
95-{
96-role: "user",
97-parts: [{ text: "draw a cat" }],
98-},
99-],
100-generationConfig: {
101-responseModalities: ["TEXT", "IMAGE"],
102-imageConfig: {
103-aspectRatio: "3:2",
104-imageSize: "2K",
105-},
106-},
107-}),
108-}),
109123);
124+expect(request.method).toBe("POST");
125+expect(JSON.parse(request.body ?? "")).toEqual({
126+contents: [
127+{
128+role: "user",
129+parts: [{ text: "draw a cat" }],
130+},
131+],
132+generationConfig: {
133+responseModalities: ["TEXT", "IMAGE"],
134+imageConfig: {
135+aspectRatio: "3:2",
136+imageSize: "2K",
137+},
138+},
139+});
110140expect(result).toEqual({
111141images: [
112142{
@@ -155,11 +185,9 @@ describe("Google image-generation provider", () => {
155185ssrfPolicy: { allowRfc2544BenchmarkRange: true },
156186});
157187158-expect(postJsonRequest).toHaveBeenCalledWith(
159-expect.objectContaining({
160-ssrfPolicy: { allowRfc2544BenchmarkRange: true },
161-}),
162-);
188+expect(postJsonRequestOptions(postJsonRequest).ssrfPolicy).toEqual({
189+allowRfc2544BenchmarkRange: true,
190+});
163191});
164192165193it("accepts OAuth JSON auth and inline_data responses", async () => {
@@ -197,14 +225,10 @@ describe("Google image-generation provider", () => {
197225cfg: {},
198226});
199227200-expect(fetchMock).toHaveBeenCalledWith(
201-expect.any(String),
202-expect.objectContaining({
203-headers: expect.any(Headers),
204-}),
205-);
206-const [, init] = fetchMock.mock.calls[0];
207-expect(new Headers(init.headers).get("authorization")).toBe("Bearer oauth-token");
228+const request = fetchRequest(fetchMock);
229+expect(request.url.length).toBeGreaterThan(0);
230+expect(request.headers).toBeInstanceOf(Headers);
231+expect(new Headers(request.headers).get("authorization")).toBe("Bearer oauth-token");
208232expect(result).toEqual({
209233images: [
210234{
@@ -237,34 +261,33 @@ describe("Google image-generation provider", () => {
237261],
238262});
239263240-expect(fetchMock).toHaveBeenCalledWith(
264+const request = fetchRequest(fetchMock);
265+expect(request.url).toBe(
241266"https://generativelanguage.googleapis.com/v1beta/models/gemini-3-pro-image-preview:generateContent",
242-expect.objectContaining({
243-method: "POST",
244-body: JSON.stringify({
245-contents: [
267+);
268+expect(request.method).toBe("POST");
269+expect(JSON.parse(request.body ?? "")).toEqual({
270+contents: [
271+{
272+role: "user",
273+parts: [
246274{
247-role: "user",
248-parts: [
249-{
250-inlineData: {
251-mimeType: "image/png",
252-data: Buffer.from("reference-bytes").toString("base64"),
253-},
254-},
255-{ text: "Change only the sky to a sunset." },
256-],
275+inlineData: {
276+mimeType: "image/png",
277+data: Buffer.from("reference-bytes").toString("base64"),
278+},
257279},
280+{ text: "Change only the sky to a sunset." },
258281],
259- generationConfig: {
260- responseModalities: ["TEXT", "IMAGE"],
261- imageConfig: {
262- imageSize: "4K",
263- },
264-},
265-}),
266-}),
267-);
282+},
283+],
284+generationConfig: {
285+responseModalities: ["TEXT", "IMAGE"],
286+imageConfig: {
287+imageSize: "4K",
288+},
289+},
290+});
268291});
269292270293it("forwards explicit aspect ratio without forcing a default when size is omitted", async () => {
@@ -280,26 +303,25 @@ describe("Google image-generation provider", () => {
280303aspectRatio: "9:16",
281304});
282305283-expect(fetchMock).toHaveBeenCalledWith(
306+const request = fetchRequest(fetchMock);
307+expect(request.url).toBe(
284308"https://generativelanguage.googleapis.com/v1beta/models/gemini-3-pro-image-preview:generateContent",
285-expect.objectContaining({
286-method: "POST",
287-body: JSON.stringify({
288-contents: [
289-{
290-role: "user",
291-parts: [{ text: "portrait photo" }],
292-},
293-],
294-generationConfig: {
295-responseModalities: ["TEXT", "IMAGE"],
296-imageConfig: {
297-aspectRatio: "9:16",
298-},
299-},
300-}),
301-}),
302309);
310+expect(request.method).toBe("POST");
311+expect(JSON.parse(request.body ?? "")).toEqual({
312+contents: [
313+{
314+role: "user",
315+parts: [{ text: "portrait photo" }],
316+},
317+],
318+generationConfig: {
319+responseModalities: ["TEXT", "IMAGE"],
320+imageConfig: {
321+aspectRatio: "9:16",
322+},
323+},
324+});
303325});
304326305327it("disables DNS pinning for Google image generation requests", async () => {
@@ -315,11 +337,7 @@ describe("Google image-generation provider", () => {
315337cfg: {},
316338});
317339318-expect(postJsonRequestSpy).toHaveBeenCalledWith(
319-expect.objectContaining({
320-pinDns: false,
321-}),
322-);
340+expect(postJsonRequestOptions(postJsonRequestSpy).pinDns).toBe(false);
323341});
324342325343it("honors configured private-network opt-in for Google image generation", async () => {
@@ -345,11 +363,7 @@ describe("Google image-generation provider", () => {
345363},
346364});
347365348-expect(postJsonRequestSpy).toHaveBeenCalledWith(
349-expect.objectContaining({
350-allowPrivateNetwork: true,
351-}),
352-);
366+expect(postJsonRequestOptions(postJsonRequestSpy).allowPrivateNetwork).toBe(true);
353367});
354368355369it("normalizes a configured bare Google host to the v1beta API root", async () => {
@@ -373,10 +387,11 @@ describe("Google image-generation provider", () => {
373387},
374388});
375389376-expect(fetchMock).toHaveBeenCalledWith(
390+const request = fetchRequest(fetchMock);
391+expect(request.url).toBe(
377392"https://generativelanguage.googleapis.com/v1beta/models/gemini-3-pro-image-preview:generateContent",
378-expect.any(Object),
379393);
394+expect(typeof request.method).toBe("string");
380395});
381396382397it("strips a configured /openai suffix before calling the native Gemini image API", async () => {
@@ -400,10 +415,11 @@ describe("Google image-generation provider", () => {
400415},
401416});
402417403-expect(fetchMock).toHaveBeenCalledWith(
418+const request = fetchRequest(fetchMock);
419+expect(request.url).toBe(
404420"https://generativelanguage.googleapis.com/v1beta/models/gemini-3-pro-image-preview:generateContent",
405-expect.any(Object),
406421);
422+expect(typeof request.method).toBe("string");
407423});
408424409425it("prefers scoped configured Gemini API keys over environment fallbacks", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。