@@ -8,6 +8,13 @@ import { testing as geminiWebSearchTesting } from "./src/gemini-web-search-provi
88
99let ssrfMock : { mockRestore : ( ) => void } | undefined ;
1010
11+ function jsonResponse ( payload : unknown ) : Response {
12+ return new Response ( JSON . stringify ( payload ) , {
13+ status : 200 ,
14+ headers : { "Content-Type" : "application/json" } ,
15+ } ) ;
16+ }
17+
1118function mockGoogleApiKeyAuth ( ) {
1219 vi . spyOn ( providerAuthRuntime , "resolveApiKeyForProvider" ) . mockResolvedValue ( {
1320 apiKey : "google-test-key" ,
@@ -24,9 +31,8 @@ function installGoogleFetchMock(params?: {
2431 const mimeType = params ?. mimeType ?? "image/png" ;
2532 const data = params ?. data ?? "png-data" ;
2633 const inlineDataKey = params ?. inlineDataKey ?? "inlineData" ;
27- const fetchMock = vi . fn ( ) . mockResolvedValue ( {
28- ok : true ,
29- json : async ( ) => ( {
34+ const fetchMock = vi . fn ( ) . mockResolvedValue (
35+ jsonResponse ( {
3036 candidates : [
3137 {
3238 content : {
@@ -42,7 +48,7 @@ function installGoogleFetchMock(params?: {
4248 } ,
4349 ] ,
4450 } ) ,
45- } ) ;
51+ ) ;
4652 vi . stubGlobal ( "fetch" , fetchMock ) ;
4753 return fetchMock ;
4854}
@@ -100,9 +106,8 @@ describe("Google image-generation provider", () => {
100106 source : "env" ,
101107 mode : "api-key" ,
102108 } ) ;
103- const fetchMock = vi . fn ( ) . mockResolvedValue ( {
104- ok : true ,
105- json : async ( ) => ( {
109+ const fetchMock = vi . fn ( ) . mockResolvedValue (
110+ jsonResponse ( {
106111 candidates : [
107112 {
108113 content : {
@@ -119,7 +124,7 @@ describe("Google image-generation provider", () => {
119124 } ,
120125 ] ,
121126 } ) ,
122- } ) ;
127+ ) ;
123128 vi . stubGlobal ( "fetch" , fetchMock ) ;
124129
125130 const provider = buildGoogleImageGenerationProvider ( ) ;
@@ -208,10 +213,7 @@ describe("Google image-generation provider", () => {
208213 mockGoogleApiKeyAuth ( ) ;
209214 vi . stubGlobal (
210215 "fetch" ,
211- vi . fn ( ) . mockResolvedValue ( {
212- ok : true ,
213- json : async ( ) => ( { candidates : { content : { parts : [ ] } } } ) ,
214- } ) ,
216+ vi . fn ( ) . mockResolvedValue ( jsonResponse ( { candidates : { content : { parts : [ ] } } } ) ) ,
215217 ) ;
216218
217219 const provider = buildGoogleImageGenerationProvider ( ) ;
@@ -229,9 +231,8 @@ describe("Google image-generation provider", () => {
229231 mockGoogleApiKeyAuth ( ) ;
230232 vi . stubGlobal (
231233 "fetch" ,
232- vi . fn ( ) . mockResolvedValue ( {
233- ok : true ,
234- json : async ( ) => ( {
234+ vi . fn ( ) . mockResolvedValue (
235+ jsonResponse ( {
235236 candidates : [
236237 {
237238 content : {
@@ -240,7 +241,7 @@ describe("Google image-generation provider", () => {
240241 } ,
241242 ] ,
242243 } ) ,
243- } ) ,
244+ ) ,
244245 ) ;
245246
246247 const provider = buildGoogleImageGenerationProvider ( ) ;
@@ -260,9 +261,8 @@ describe("Google image-generation provider", () => {
260261 source : "profile" ,
261262 mode : "token" ,
262263 } ) ;
263- const fetchMock = vi . fn ( ) . mockResolvedValue ( {
264- ok : true ,
265- json : async ( ) => ( {
264+ const fetchMock = vi . fn ( ) . mockResolvedValue (
265+ jsonResponse ( {
266266 candidates : [
267267 {
268268 content : {
@@ -278,7 +278,7 @@ describe("Google image-generation provider", () => {
278278 } ,
279279 ] ,
280280 } ) ,
281- } ) ;
281+ ) ;
282282 vi . stubGlobal ( "fetch" , fetchMock ) ;
283283
284284 const provider = buildGoogleImageGenerationProvider ( ) ;
@@ -305,6 +305,74 @@ describe("Google image-generation provider", () => {
305305 } ) ;
306306 } ) ;
307307
308+ it ( "accepts valid multi-image inline JSON responses above the generic provider JSON cap" , async ( ) => {
309+ mockGoogleApiKeyAuth ( ) ;
310+ const imageBytes = Buffer . alloc ( 6 * 1024 * 1024 , 1 ) ;
311+ const imagePayload = imageBytes . toString ( "base64" ) ;
312+ vi . stubGlobal (
313+ "fetch" ,
314+ vi . fn ( ) . mockResolvedValue (
315+ jsonResponse ( {
316+ candidates : [
317+ {
318+ content : {
319+ parts : Array . from ( { length : 3 } , ( ) => ( {
320+ inlineData : {
321+ mimeType : "image/png" ,
322+ data : imagePayload ,
323+ } ,
324+ } ) ) ,
325+ } ,
326+ } ,
327+ ] ,
328+ } ) ,
329+ ) ,
330+ ) ;
331+
332+ const provider = buildGoogleImageGenerationProvider ( ) ;
333+ const result = await provider . generateImage ( {
334+ provider : "google" ,
335+ model : "gemini-3.1-flash-image-preview" ,
336+ prompt : "draw a cat" ,
337+ cfg : { } ,
338+ } ) ;
339+
340+ expect ( result . images ) . toHaveLength ( 3 ) ;
341+ expect ( result . images . map ( ( image ) => image . buffer . byteLength ) ) . toEqual ( [
342+ imageBytes . byteLength ,
343+ imageBytes . byteLength ,
344+ imageBytes . byteLength ,
345+ ] ) ;
346+ } ) ;
347+
348+ it ( "still rejects oversized Google image JSON responses" , async ( ) => {
349+ mockGoogleApiKeyAuth ( ) ;
350+ vi . stubGlobal (
351+ "fetch" ,
352+ vi . fn ( ) . mockResolvedValue (
353+ jsonResponse ( {
354+ candidates : [
355+ {
356+ content : {
357+ parts : [ { text : "x" . repeat ( 35 * 1024 * 1024 ) } ] ,
358+ } ,
359+ } ,
360+ ] ,
361+ } ) ,
362+ ) ,
363+ ) ;
364+
365+ const provider = buildGoogleImageGenerationProvider ( ) ;
366+ await expect (
367+ provider . generateImage ( {
368+ provider : "google" ,
369+ model : "gemini-3.1-flash-image-preview" ,
370+ prompt : "draw a cat" ,
371+ cfg : { } ,
372+ } ) ,
373+ ) . rejects . toThrow ( "google.image-generation: JSON response exceeds" ) ;
374+ } ) ;
375+
308376 it ( "sends reference images and explicit resolution for edit flows" , async ( ) => {
309377 mockGoogleApiKeyAuth ( ) ;
310378 const fetchMock = installGoogleFetchMock ( ) ;
0 commit comments