@@ -63,29 +63,44 @@ function streamedVideoResponse(bytes: string): Response {
6363 ) ;
6464}
6565
66+ // Streams a JSON body through a real ReadableStream so create/poll reads exercise
67+ // the byte-bounded reader (readResponseWithLimit) instead of an unbounded res.json().
68+ function streamedJsonResponse ( payload : unknown ) : Response {
69+ return streamedRawResponse ( JSON . stringify ( payload ) ) ;
70+ }
71+
72+ function streamedRawResponse ( text : string ) : Response {
73+ return new Response (
74+ new ReadableStream ( {
75+ start ( controller ) {
76+ controller . enqueue ( new TextEncoder ( ) . encode ( text ) ) ;
77+ controller . close ( ) ;
78+ } ,
79+ } ) ,
80+ { headers : { "content-type" : "application/json" } } ,
81+ ) ;
82+ }
83+
6684describe ( "runway video generation provider" , ( ) => {
6785 it ( "declares explicit mode capabilities" , ( ) => {
6886 expectExplicitVideoGenerationCapabilities ( buildRunwayVideoGenerationProvider ( ) ) ;
6987 } ) ;
7088
7189 it ( "submits a text-to-video task, polls it, and downloads the output" , async ( ) => {
7290 postJsonRequestMock . mockResolvedValue ( {
73- response : {
74- json : async ( ) => ( {
75- id : "task-1" ,
76- } ) ,
77- } ,
91+ response : streamedJsonResponse ( {
92+ id : "task-1" ,
93+ } ) ,
7894 release : vi . fn ( async ( ) => { } ) ,
7995 } ) ;
8096 fetchWithTimeoutMock
81- . mockResolvedValueOnce ( {
82- json : async ( ) => ( {
97+ . mockResolvedValueOnce (
98+ streamedJsonResponse ( {
8399 id : "task-1" ,
84100 status : "SUCCEEDED" ,
85101 output : [ "https://example.com/out.mp4" ] ,
86102 } ) ,
87- headers : new Headers ( ) ,
88- } )
103+ )
89104 . mockResolvedValueOnce ( {
90105 arrayBuffer : async ( ) => Buffer . from ( "mp4-bytes" ) ,
91106 headers : new Headers ( { "content-type" : "video/webm" } ) ,
@@ -130,20 +145,17 @@ describe("runway video generation provider", () => {
130145
131146 it ( "rejects generated video downloads that exceed the configured media cap" , async ( ) => {
132147 postJsonRequestMock . mockResolvedValue ( {
133- response : {
134- json : async ( ) => ( { id : "task-too-large" } ) ,
135- } ,
148+ response : streamedJsonResponse ( { id : "task-too-large" } ) ,
136149 release : vi . fn ( async ( ) => { } ) ,
137150 } ) ;
138151 fetchWithTimeoutMock
139- . mockResolvedValueOnce ( {
140- json : async ( ) => ( {
152+ . mockResolvedValueOnce (
153+ streamedJsonResponse ( {
141154 id : "task-too-large" ,
142155 status : "SUCCEEDED" ,
143156 output : [ "https://example.com/out.mp4" ] ,
144157 } ) ,
145- headers : new Headers ( ) ,
146- } )
158+ )
147159 . mockResolvedValueOnce ( streamedVideoResponse ( "too-large" ) ) ;
148160
149161 const provider = buildRunwayVideoGenerationProvider ( ) ;
@@ -159,20 +171,17 @@ describe("runway video generation provider", () => {
159171
160172 it ( "does not round malformed duration values into create requests" , async ( ) => {
161173 postJsonRequestMock . mockResolvedValue ( {
162- response : {
163- json : async ( ) => ( { id : "task-duration" } ) ,
164- } ,
174+ response : streamedJsonResponse ( { id : "task-duration" } ) ,
165175 release : vi . fn ( async ( ) => { } ) ,
166176 } ) ;
167177 fetchWithTimeoutMock
168- . mockResolvedValueOnce ( {
169- json : async ( ) => ( {
178+ . mockResolvedValueOnce (
179+ streamedJsonResponse ( {
170180 id : "task-duration" ,
171181 status : "SUCCEEDED" ,
172182 output : [ "https://example.com/out.mp4" ] ,
173183 } ) ,
174- headers : new Headers ( ) ,
175- } )
184+ )
176185 . mockResolvedValueOnce ( {
177186 arrayBuffer : async ( ) => Buffer . from ( "mp4-bytes" ) ,
178187 headers : new Headers ( { "content-type" : "video/mp4" } ) ,
@@ -194,20 +203,17 @@ describe("runway video generation provider", () => {
194203
195204 it ( "accepts local image buffers by converting them into data URIs" , async ( ) => {
196205 postJsonRequestMock . mockResolvedValue ( {
197- response : {
198- json : async ( ) => ( { id : "task-2" } ) ,
199- } ,
206+ response : streamedJsonResponse ( { id : "task-2" } ) ,
200207 release : vi . fn ( async ( ) => { } ) ,
201208 } ) ;
202209 fetchWithTimeoutMock
203- . mockResolvedValueOnce ( {
204- json : async ( ) => ( {
210+ . mockResolvedValueOnce (
211+ streamedJsonResponse ( {
205212 id : "task-2" ,
206213 status : "SUCCEEDED" ,
207214 output : [ "https://example.com/out.mp4" ] ,
208215 } ) ,
209- headers : new Headers ( ) ,
210- } )
216+ )
211217 . mockResolvedValueOnce ( {
212218 arrayBuffer : async ( ) => Buffer . from ( "mp4-bytes" ) ,
213219 headers : new Headers ( { "content-type" : "video/mp4" } ) ,
@@ -250,11 +256,7 @@ describe("runway video generation provider", () => {
250256 it ( "reports malformed create JSON with a provider-owned error" , async ( ) => {
251257 const release = vi . fn ( async ( ) => { } ) ;
252258 postJsonRequestMock . mockResolvedValue ( {
253- response : {
254- json : async ( ) => {
255- throw new SyntaxError ( "bad json" ) ;
256- } ,
257- } ,
259+ response : streamedRawResponse ( "{ not json" ) ,
258260 release,
259261 } ) ;
260262
@@ -272,18 +274,15 @@ describe("runway video generation provider", () => {
272274
273275 it ( "rejects status responses missing a task status" , async ( ) => {
274276 postJsonRequestMock . mockResolvedValue ( {
275- response : {
276- json : async ( ) => ( { id : "task-missing-status" } ) ,
277- } ,
277+ response : streamedJsonResponse ( { id : "task-missing-status" } ) ,
278278 release : vi . fn ( async ( ) => { } ) ,
279279 } ) ;
280- fetchWithTimeoutMock . mockResolvedValueOnce ( {
281- json : async ( ) => ( {
280+ fetchWithTimeoutMock . mockResolvedValueOnce (
281+ streamedJsonResponse ( {
282282 id : "task-missing-status" ,
283283 output : [ "https://example.com/out.mp4" ] ,
284284 } ) ,
285- headers : new Headers ( ) ,
286- } ) ;
285+ ) ;
287286
288287 const provider = buildRunwayVideoGenerationProvider ( ) ;
289288 await expect (
@@ -298,19 +297,16 @@ describe("runway video generation provider", () => {
298297
299298 it ( "rejects malformed completed output URLs" , async ( ) => {
300299 postJsonRequestMock . mockResolvedValue ( {
301- response : {
302- json : async ( ) => ( { id : "task-malformed-output" } ) ,
303- } ,
300+ response : streamedJsonResponse ( { id : "task-malformed-output" } ) ,
304301 release : vi . fn ( async ( ) => { } ) ,
305302 } ) ;
306- fetchWithTimeoutMock . mockResolvedValueOnce ( {
307- json : async ( ) => ( {
303+ fetchWithTimeoutMock . mockResolvedValueOnce (
304+ streamedJsonResponse ( {
308305 id : "task-malformed-output" ,
309306 status : "SUCCEEDED" ,
310307 output : "https://example.com/out.mp4" ,
311308 } ) ,
312- headers : new Headers ( ) ,
313- } ) ;
309+ ) ;
314310
315311 const provider = buildRunwayVideoGenerationProvider ( ) ;
316312 await expect (
0 commit comments