@@ -363,6 +363,48 @@ describe('Lib Functions', () => {
363363 { force : true }
364364 ) ;
365365 } ) ;
366+
367+ it ( 'cleans up temp file and re-throws when fs.cp fails after EPERM' , async ( ) => {
368+ const eexistError = new Error ( 'EEXIST' ) as NodeJS . ErrnoException ;
369+ eexistError . code = 'EEXIST' ;
370+ const epermError = new Error ( 'EPERM' ) as NodeJS . ErrnoException ;
371+ epermError . code = 'EPERM' ;
372+ const enospcError = new Error ( 'ENOSPC' ) as NodeJS . ErrnoException ;
373+ enospcError . code = 'ENOSPC' ;
374+
375+ mockFs . writeFile
376+ . mockRejectedValueOnce ( eexistError )
377+ . mockResolvedValueOnce ( undefined ) ;
378+ mockFs . rename . mockRejectedValueOnce ( epermError ) ;
379+ mockFs . cp . mockRejectedValueOnce ( enospcError ) ;
380+ mockFs . unlink . mockResolvedValue ( undefined ) ;
381+
382+ await expect ( writeFileContent ( '/test/file.txt' , 'new content' ) )
383+ . rejects . toThrow ( 'ENOSPC' ) ;
384+
385+ expect ( mockFs . unlink ) . toHaveBeenCalledWith (
386+ expect . stringMatching ( / \/ t e s t \/ f i l e \. t x t \. [ a - f 0 - 9 ] + \. t m p $ / )
387+ ) ;
388+ } ) ;
389+
390+ it ( 'cleans up temp file and re-throws when temp write fails' , async ( ) => {
391+ const eexistError = new Error ( 'EEXIST' ) as NodeJS . ErrnoException ;
392+ eexistError . code = 'EEXIST' ;
393+ const enospcError = new Error ( 'ENOSPC' ) as NodeJS . ErrnoException ;
394+ enospcError . code = 'ENOSPC' ;
395+
396+ mockFs . writeFile
397+ . mockRejectedValueOnce ( eexistError )
398+ . mockRejectedValueOnce ( enospcError ) ;
399+ mockFs . unlink . mockResolvedValue ( undefined ) ;
400+
401+ await expect ( writeFileContent ( '/test/file.txt' , 'new content' ) )
402+ . rejects . toThrow ( 'ENOSPC' ) ;
403+
404+ expect ( mockFs . unlink ) . toHaveBeenCalledWith (
405+ expect . stringMatching ( / \/ t e s t \/ f i l e \. t x t \. [ a - f 0 - 9 ] + \. t m p $ / )
406+ ) ;
407+ } ) ;
366408 } ) ;
367409
368410 } ) ;
@@ -663,6 +705,24 @@ describe('Lib Functions', () => {
663705 ) ;
664706 } ) ;
665707
708+ it ( 'cleans up temp file and re-throws when temp write fails during file edit' , async ( ) => {
709+ const enospcError = new Error ( 'ENOSPC' ) as NodeJS . ErrnoException ;
710+ enospcError . code = 'ENOSPC' ;
711+
712+ mockFs . readFile . mockResolvedValue ( 'line1\nline2\nline3\n' ) ;
713+ mockFs . writeFile . mockRejectedValueOnce ( enospcError ) ;
714+ mockFs . unlink . mockResolvedValue ( undefined ) ;
715+
716+ const edits = [ { oldText : 'line2' , newText : 'modified line2' } ] ;
717+
718+ await expect ( applyFileEdits ( '/test/file.txt' , edits , false ) )
719+ . rejects . toThrow ( 'ENOSPC' ) ;
720+
721+ expect ( mockFs . unlink ) . toHaveBeenCalledWith (
722+ expect . stringMatching ( / \/ t e s t \/ f i l e \. t x t \. [ a - f 0 - 9 ] + \. t m p $ / )
723+ ) ;
724+ } ) ;
725+
666726 it ( 'handles CRLF line endings in file content' , async ( ) => {
667727 mockFs . readFile . mockResolvedValue ( 'line1\r\nline2\r\nline3\r\n' ) ;
668728
0 commit comments