@@ -31,6 +31,23 @@ function streamingTextResponse(params: {
3131 return new Response ( stream , { status : params . status , headers : params . headers } ) ;
3232}
3333
34+ function stallingResponse ( params : { status : number ; onCancel : ( ) => void } ) : Response {
35+ const reader = {
36+ read : ( ) => new Promise < ReadableStreamReadResult < Uint8Array > > ( ( ) => { } ) ,
37+ cancel : async ( ) => {
38+ params . onCancel ( ) ;
39+ } ,
40+ releaseLock : ( ) => undefined ,
41+ } as ReadableStreamDefaultReader < Uint8Array > ;
42+
43+ return {
44+ status : params . status ,
45+ ok : params . status >= 200 && params . status < 300 ,
46+ headers : new Headers ( ) ,
47+ body : { getReader : ( ) => reader } ,
48+ } as Response ;
49+ }
50+
3451describe ( "uploadBatchJsonlFile" , ( ) => {
3552 beforeEach ( ( ) => {
3653 vi . clearAllMocks ( ) ;
@@ -108,4 +125,70 @@ describe("uploadBatchJsonlFile", () => {
108125 ) . rejects . toThrow ( "file upload failed: response body too large: 64 bytes (limit: 8 bytes)" ) ;
109126 expect ( canceled ) . toBe ( true ) ;
110127 } ) ;
128+
129+ it ( "passes caller abort signals through non-ok file-upload response snippets" , async ( ) => {
130+ let canceled = false ;
131+ remoteHttpMock . mockImplementationOnce ( async ( params ) => {
132+ return await params . onResponse (
133+ stallingResponse ( {
134+ status : 500 ,
135+ onCancel : ( ) => {
136+ canceled = true ;
137+ } ,
138+ } ) ,
139+ ) ;
140+ } ) ;
141+ const controller = new AbortController ( ) ;
142+ const upload = uploadBatchJsonlFile ( {
143+ client : {
144+ baseUrl : "https://memory.example/v1" ,
145+ headers : { Authorization : "Bearer test" } ,
146+ } ,
147+ requests : [ { input : "one" } ] ,
148+ errorPrefix : "file upload failed" ,
149+ signal : controller . signal ,
150+ } ) ;
151+
152+ await new Promise ( ( resolve ) => {
153+ setTimeout ( resolve , 0 ) ;
154+ } ) ;
155+ controller . abort ( new Error ( "upload aborted" ) ) ;
156+
157+ await expect ( upload ) . rejects . toThrow ( "upload aborted" ) ;
158+ expect ( canceled ) . toBe ( true ) ;
159+ expect ( remoteHttpMock . mock . calls [ 0 ] ?. [ 0 ] . signal ) . toBe ( controller . signal ) ;
160+ } ) ;
161+
162+ it ( "passes caller abort signals through successful file-upload JSON reads" , async ( ) => {
163+ let canceled = false ;
164+ remoteHttpMock . mockImplementationOnce ( async ( params ) => {
165+ return await params . onResponse (
166+ stallingResponse ( {
167+ status : 200 ,
168+ onCancel : ( ) => {
169+ canceled = true ;
170+ } ,
171+ } ) ,
172+ ) ;
173+ } ) ;
174+ const controller = new AbortController ( ) ;
175+ const upload = uploadBatchJsonlFile ( {
176+ client : {
177+ baseUrl : "https://memory.example/v1" ,
178+ headers : { Authorization : "Bearer test" } ,
179+ } ,
180+ requests : [ { input : "one" } ] ,
181+ errorPrefix : "file upload failed" ,
182+ signal : controller . signal ,
183+ } ) ;
184+
185+ await new Promise ( ( resolve ) => {
186+ setTimeout ( resolve , 0 ) ;
187+ } ) ;
188+ controller . abort ( new Error ( "upload json aborted" ) ) ;
189+
190+ await expect ( upload ) . rejects . toThrow ( "upload json aborted" ) ;
191+ expect ( canceled ) . toBe ( true ) ;
192+ expect ( remoteHttpMock . mock . calls [ 0 ] ?. [ 0 ] . signal ) . toBe ( controller . signal ) ;
193+ } ) ;
111194} ) ;
0 commit comments