@@ -259,14 +259,56 @@ describe('Oracle EPM source files and chunked uploads', () => {
259259 } )
260260 } )
261261
262- it ( 'rejects repository size mismatch before sending a mutation' , async ( ) => {
263- await expect (
264- uploadRepositoryFile (
265- { ...auth , file : { ...file , size : 4 } , fileName : 'data.csv' } ,
262+ it . each ( [ 2 , 4 ] ) (
263+ 'rejects repository declared size %s mismatch before sending a mutation' ,
264+ async ( size ) => {
265+ await expect (
266+ uploadRepositoryFile (
267+ { ...auth , file : { ...file , size } , fileName : 'data.csv' } ,
268+ fileContext
269+ )
270+ ) . rejects . toThrow ( 'declared file size' )
271+ expect ( mockSecureFetch ) . not . toHaveBeenCalled ( )
272+ }
273+ )
274+
275+ it ( 'copies repository source chunks directly into the bounded upload body' , async ( ) => {
276+ storage . downloadFileStream . mockImplementation ( async ( ) =>
277+ Readable . from ( [ Buffer . from ( 'a' ) , Buffer . from ( 'bc' ) ] )
278+ )
279+ await uploadRepositoryFile ( { ...auth , file, fileName : 'data.csv' } , fileContext )
280+ expect ( mockSecureFetch . mock . calls [ 0 ] [ 2 ] . body ) . toEqual ( Buffer . from ( 'abc' ) )
281+ } )
282+
283+ it ( 'returns an immediate legacy extraction reference after a repository snapshot upload' , async ( ) => {
284+ mockSecureFetch . mockImplementation ( async ( ) =>
285+ Response . json ( {
286+ status : - 1 ,
287+ links : [
288+ {
289+ rel : 'Job Status' ,
290+ action : 'GET' ,
291+ href : 'https://epm.example.com/gateway/interop/rest/11.1.2.3.600/applicationsnapshots/Artifact%20Snapshot.zip/contents/status' ,
292+ } ,
293+ ] ,
294+ } )
295+ )
296+ expect (
297+ await uploadRepositoryFile (
298+ {
299+ ...auth ,
300+ file,
301+ fileName : 'Artifact Snapshot.zip' ,
302+ } ,
266303 fileContext
267304 )
268- ) . rejects . toThrow ( 'declared file size' )
269- expect ( mockSecureFetch ) . not . toHaveBeenCalled ( )
305+ ) . toMatchObject ( {
306+ bytesUploaded : 3 ,
307+ completed : false ,
308+ jobKind : 'snapshot_upload' ,
309+ jobId : 'repository:Artifact%20Snapshot.zip' ,
310+ } )
311+ expect ( mockSecureFetch . mock . calls . map ( ( [ , , options ] ) => options . method ) ) . toEqual ( [ 'POST' ] )
270312 } )
271313
272314 it ( 'uses inclusive contiguous ranges, one-based chunk numbers, and empty init/finalize control bodies' , async ( ) => {
@@ -405,6 +447,26 @@ describe('Oracle EPM source files and chunked uploads', () => {
405447 ) . toBe ( true )
406448 } )
407449
450+ it ( 'rejects late cancellation after finalization without deleting the accepted snapshot' , async ( ) => {
451+ const controller = new AbortController ( )
452+ mockSecureFetch . mockImplementation ( async ( url : string ) => {
453+ const response = Response . json ( { status : 0 } )
454+ if ( JSON . parse ( new URL ( url ) . searchParams . get ( 'q' ) ?? '{}' ) . isLast ) {
455+ response . json = async ( ) => {
456+ controller . abort ( new DOMException ( 'Cancelled' , 'AbortError' ) )
457+ return { status : 0 }
458+ }
459+ }
460+ return response
461+ } )
462+ await expect (
463+ uploadSnapshot ( snapshotInput ( ) , { ...fileContext , signal : controller . signal } )
464+ ) . rejects . toMatchObject ( { name : 'AbortError' } )
465+ expect ( mockSecureFetch . mock . calls . some ( ( [ url ] ) => String ( url ) . endsWith ( '/files/delete' ) ) ) . toBe (
466+ false
467+ )
468+ } )
469+
408470 it ( 'cancels an interrupted source and uses a separate bounded signal for owned cleanup' , async ( ) => {
409471 const controller = new AbortController ( )
410472 storage . downloadFileStream . mockImplementation ( async ( ) =>
@@ -534,4 +596,25 @@ describe('Oracle EPM v2 streamed downloads', () => {
534596 true
535597 )
536598 } )
599+
600+ it ( 'removes its completed local output when cancellation overlaps remote cleanup' , async ( ) => {
601+ const controller = new AbortController ( )
602+ setDownload ( )
603+ const provider = mockSecureFetch . getMockImplementation ( ) !
604+ mockSecureFetch . mockImplementation ( async ( ...args ) => {
605+ const response = await provider ( ...args )
606+ if ( args [ 2 ] . method === 'DELETE' ) {
607+ expect ( storage . complete ) . toHaveBeenCalled ( )
608+ controller . abort ( new DOMException ( 'Cancelled' , 'AbortError' ) )
609+ }
610+ return response
611+ } )
612+ await expect (
613+ downloadRepositoryFile ( downloadInput , { ...fileContext , signal : controller . signal } )
614+ ) . rejects . toMatchObject ( { name : 'AbortError' } )
615+ expect ( storage . deleteFile ) . toHaveBeenCalledWith ( {
616+ key : 'execution/result.zip' ,
617+ context : 'execution' ,
618+ } )
619+ } )
537620} )
0 commit comments