@@ -18,6 +18,7 @@ class ErrorCode extends Error {
18
18
const EISDIR = new ErrorCode ( 'EISDIR' )
19
19
const EMFILE = new ErrorCode ( 'EMFILE' )
20
20
const ENOENT = new ErrorCode ( 'ENOENT' )
21
+ const ENOTDIR = new ErrorCode ( 'ENOTDIR' )
21
22
const EPERM = new ErrorCode ( 'EPERM' )
22
23
const EUNKNOWN = new ErrorCode ( 'EUNKNOWN' ) // fake error code for else coverage
23
24
@@ -86,6 +87,23 @@ t.test('can delete a directory', async (t) => {
86
87
t . equal ( await fs . exists ( target ) , false , 'target no longer exists' )
87
88
} )
88
89
90
+ t . test ( 'resolves when rmdir gets ENOENT with force' , async ( t ) => {
91
+ const dir = t . testdir ( )
92
+ // doesn't actually exist
93
+ const target = join ( dir , 'directory' )
94
+
95
+ const lstat = realFs . lstat
96
+ realFs . lstat = ( path , cb ) => {
97
+ realFs . lstat = lstat
98
+ setImmediate ( cb , null , { isDirectory : ( ) => true } )
99
+ }
100
+ t . teardown ( ( ) => {
101
+ realFs . lstat = lstat
102
+ } )
103
+
104
+ await t . resolves ( rm ( target , { recursive : true , force : true } ) )
105
+ } )
106
+
89
107
t . test ( 'rejects with EISDIR when deleting a directory without recursive' , async ( t ) => {
90
108
const dir = t . testdir ( {
91
109
directory : { } ,
@@ -198,6 +216,43 @@ t.test('rejects with unknown error removing top directory', async (t) => {
198
216
} )
199
217
} )
200
218
219
+ t . test ( 'posix' , async ( t ) => {
220
+ let posixRm
221
+ t . before ( ( ) => {
222
+ t . context . platform = Object . getOwnPropertyDescriptor ( process , 'platform' )
223
+ Object . defineProperty ( process , 'platform' , {
224
+ ...t . context . platform ,
225
+ value : 'linux' ,
226
+ } )
227
+ posixRm = t . mock ( '../../lib/rm/polyfill.js' )
228
+ } )
229
+
230
+ t . teardown ( ( ) => {
231
+ Object . defineProperty ( process , 'platform' , t . context . platform )
232
+ } )
233
+
234
+ t . test ( 'EPERM in unlink calls rmdir' , async ( t ) => {
235
+ const dir = t . testdir ( {
236
+ file : 'a file' ,
237
+ } )
238
+ const target = join ( dir , 'file' )
239
+
240
+ const rmdir = realFs . rmdir
241
+ const unlink = realFs . unlink
242
+ // need to mock rmdir too so we can force an ENOTDIR
243
+ realFs . rmdir = ( path , cb ) => setImmediate ( cb , ENOTDIR )
244
+ realFs . unlink = ( path , cb ) => setImmediate ( cb , EPERM )
245
+ t . teardown ( ( ) => {
246
+ realFs . rmdir = rmdir
247
+ realFs . unlink = unlink
248
+ } )
249
+
250
+ await t . rejects ( posixRm ( target , { recursive : true } ) , {
251
+ code : 'EPERM' ,
252
+ } , 'got the EPERM' )
253
+ } )
254
+ } )
255
+
201
256
t . test ( 'windows' , async ( t ) => {
202
257
// t.mock instead of require so we flush the cache first
203
258
let winRm
@@ -454,4 +509,84 @@ t.test('windows', async (t) => {
454
509
t . not ( await fs . exists ( join ( target , 'file' ) ) , 'file no longer exists' )
455
510
t . not ( await fs . exists ( target ) , 'target no longer exists' )
456
511
} )
512
+
513
+ t . test ( 'ENOENT in rmdir resolves when file is really gone with force' , async ( t ) => {
514
+ const dir = t . testdir ( {
515
+ directory : { } ,
516
+ } )
517
+ const target = join ( dir , 'directory' )
518
+
519
+ let rmdirCalled = false
520
+ const lstat = realFs . lstat
521
+ const rmdir = realFs . rmdir
522
+ realFs . rmdir = ( path , cb ) => {
523
+ rmdirCalled = true
524
+ realFs . lstat = ( path , cb ) => setImmediate ( cb , ENOENT )
525
+ setImmediate ( cb , ENOENT )
526
+ }
527
+ t . teardown ( ( ) => {
528
+ realFs . lstat = lstat
529
+ realFs . rmdir = rmdir
530
+ } )
531
+
532
+ await winRm ( target , { recursive : true , force : true } )
533
+ t . ok ( rmdirCalled , 'rmdir() was called' )
534
+ } )
535
+
536
+ t . test ( 'ENOENT in rmdir rejects with ENOENT when file is really gone without force' , async ( t ) => {
537
+ const dir = t . testdir ( {
538
+ directory : { } ,
539
+ } )
540
+ const target = join ( dir , 'directory' )
541
+
542
+ const lstat = realFs . lstat
543
+ const rmdir = realFs . rmdir
544
+ realFs . rmdir = ( path , cb ) => {
545
+ // need to hijack this here so only the lstat after rmdir gets the ENOENT
546
+ realFs . lstat = ( path , cb ) => setImmediate ( cb , ENOENT )
547
+ setImmediate ( cb , ENOENT )
548
+ }
549
+ t . teardown ( ( ) => {
550
+ realFs . lstat = lstat
551
+ realFs . rmdir = rmdir
552
+ } )
553
+
554
+ await t . rejects ( winRm ( target , { recursive : true } ) , {
555
+ code : 'ENOENT' ,
556
+ } , 'got the ENOENT' )
557
+ } )
558
+
559
+ t . test ( 'ENOENT in rmdir rejects with ENOTDIR when target still exists' , async ( t ) => {
560
+ const dir = t . testdir ( {
561
+ directory : { } ,
562
+ } )
563
+ const target = join ( dir , 'directory' )
564
+
565
+ const rmdir = realFs . rmdir
566
+ realFs . rmdir = ( path , cb ) => setImmediate ( cb , ENOENT )
567
+ t . teardown ( ( ) => {
568
+ realFs . rmdir = rmdir
569
+ } )
570
+
571
+ await t . rejects ( winRm ( target , { recursive : true } ) , {
572
+ code : 'ENOTDIR' ,
573
+ } , 'got the ENOTDIR' )
574
+ } )
575
+
576
+ t . test ( 'ENOENT in rmdir rejects with ENOTDIR when target still exists and force is set' , async ( t ) => {
577
+ const dir = t . testdir ( {
578
+ directory : { } ,
579
+ } )
580
+ const target = join ( dir , 'directory' )
581
+
582
+ const rmdir = realFs . rmdir
583
+ realFs . rmdir = ( path , cb ) => setImmediate ( cb , ENOENT )
584
+ t . teardown ( ( ) => {
585
+ realFs . rmdir = rmdir
586
+ } )
587
+
588
+ await t . rejects ( winRm ( target , { recursive : true , force : true } ) , {
589
+ code : 'ENOTDIR' ,
590
+ } , 'got the ENOTDIR' )
591
+ } )
457
592
} )
0 commit comments