@@ -460,6 +460,146 @@ describe("cachedInputFileSystem CacheBackend", () => {
460460 } ) ;
461461 } ) ;
462462
463+ it ( "should not clear the entire cache when purging falsy keys 0 or ''" , ( done ) => {
464+ // number 0 (a valid fd) and "" are valid cache keys per the signature;
465+ // passing them must not be confused with the no-arg "clear all" form.
466+ fs . stat ( "/test/path" , ( err , r1 ) => {
467+ r1 . cached = true ;
468+ fs . purge ( 0 ) ;
469+ fs . stat ( "/test/path" , ( err , r2 ) => {
470+ expect ( r2 . cached ) . toBe ( true ) ;
471+ fs . purge ( "" ) ;
472+ fs . stat ( "/test/path" , ( err , r3 ) => {
473+ // Empty string is a prefix of every key, so prefix-purge still clears
474+ // everything — but only because of the prefix semantics, not because
475+ // "" was misclassified as "no argument".
476+ expect ( r3 . cached ) . toBeUndefined ( ) ;
477+ done ( ) ;
478+ } ) ;
479+ } ) ;
480+ } ) ;
481+ } ) ;
482+
483+ it ( "should not crash when options is null" , ( done ) => {
484+ fs . stat ( "/test/path" , ( err , r1 ) => {
485+ r1 . cached = true ;
486+ expect ( ( ) => fs . purge ( "/test/path" , null ) ) . not . toThrow ( ) ;
487+ fs . stat ( "/test/path" , ( err , r2 ) => {
488+ // null is treated as "no options" — falls back to prefix purge
489+ expect ( r2 . cached ) . toBeUndefined ( ) ;
490+ done ( ) ;
491+ } ) ;
492+ } ) ;
493+ } ) ;
494+
495+ it ( "should purge exact entries only when exact: true" , ( done ) => {
496+ fs . stat ( "/test/path" , ( err , r1 ) => {
497+ expect ( r1 . path ) . toBe ( "/test/path" ) ;
498+ r1 . cached = true ;
499+ fs . stat ( "/test/path/sub" , ( err , r2 ) => {
500+ expect ( r2 . path ) . toBe ( "/test/path/sub" ) ;
501+ r2 . cached = true ;
502+ // Prefix purge with exact: true should NOT remove the child entry
503+ fs . purge ( "/test/path" , { exact : true } ) ;
504+ fs . stat ( "/test/path" , ( err , r3 ) => {
505+ // /test/path was the exact match, should be re-fetched (cached flag gone)
506+ expect ( r3 . cached ) . toBeUndefined ( ) ;
507+ fs . stat ( "/test/path/sub" , ( err , r4 ) => {
508+ // /test/path/sub should still be cached
509+ expect ( r4 . cached ) . toBe ( true ) ;
510+ done ( ) ;
511+ } ) ;
512+ } ) ;
513+ } ) ;
514+ } ) ;
515+ } ) ;
516+
517+ it ( "should purge an array exactly when exact: true" , ( done ) => {
518+ fs . stat ( "/test/path" , ( err , r1 ) => {
519+ r1 . cached = true ;
520+ fs . stat ( "/test/path/sub" , ( err , r2 ) => {
521+ r2 . cached = true ;
522+ fs . stat ( "/other" , ( err , r3 ) => {
523+ r3 . cached = true ;
524+ fs . purge ( [ "/test/path" , "/other" ] , { exact : true } ) ;
525+ fs . stat ( "/test/path" , ( err , r4 ) => {
526+ expect ( r4 . cached ) . toBeUndefined ( ) ;
527+ fs . stat ( "/test/path/sub" , ( err , r5 ) => {
528+ expect ( r5 . cached ) . toBe ( true ) ;
529+ fs . stat ( "/other" , ( err , r6 ) => {
530+ expect ( r6 . cached ) . toBeUndefined ( ) ;
531+ done ( ) ;
532+ } ) ;
533+ } ) ;
534+ } ) ;
535+ } ) ;
536+ } ) ;
537+ } ) ;
538+ } ) ;
539+
540+ it ( "should still prefix-purge when exact is not set or false" , ( done ) => {
541+ fs . stat ( "/test/path" , ( err , r1 ) => {
542+ r1 . cached = true ;
543+ fs . stat ( "/test/path/sub" , ( err , r2 ) => {
544+ r2 . cached = true ;
545+ fs . purge ( "/test/path" ) ;
546+ fs . stat ( "/test/path" , ( err , r3 ) => {
547+ expect ( r3 . cached ) . toBeUndefined ( ) ;
548+ fs . stat ( "/test/path/sub" , ( err , r4 ) => {
549+ expect ( r4 . cached ) . toBeUndefined ( ) ;
550+ done ( ) ;
551+ } ) ;
552+ } ) ;
553+ } ) ;
554+ } ) ;
555+ } ) ;
556+
557+ it ( "should purge readdir of the exact directory when exact: true" , ( done ) => {
558+ fs . readdir ( "/test/path" , ( err , r1 ) => {
559+ expect ( r1 [ 0 ] ) . toBe ( "0" ) ;
560+ fs . readdir ( "/test/path/sub" , ( err , r2 ) => {
561+ expect ( r2 [ 0 ] ) . toBe ( "1" ) ;
562+ // Without exact, purging the dir path strips to dirname and prefix-purges
563+ // readdir for that parent — meaning readdir("/test/path") would NOT be evicted
564+ // by purge("/test/path/sub") in legacy mode (parent is "/test/path/", child of
565+ // which is "/test/path/sub", but readdir key is "/test/path"). With exact: true
566+ // the caller is naming the directory itself, so it should evict directly.
567+ fs . purge ( "/test/path" , { exact : true } ) ;
568+ fs . readdir ( "/test/path" , ( err , r3 ) => {
569+ expect ( r3 [ 0 ] ) . toBe ( "2" ) ;
570+ // sibling readdir cache should still be intact
571+ fs . readdir ( "/test/path/sub" , ( err , r4 ) => {
572+ expect ( r4 [ 0 ] ) . toBe ( "1" ) ;
573+ done ( ) ;
574+ } ) ;
575+ } ) ;
576+ } ) ;
577+ } ) ;
578+ } ) ;
579+
580+ it ( "should accept Buffer with exact: true" , ( done ) => {
581+ fs . stat ( "/test/path" , ( err , r1 ) => {
582+ r1 . cached = true ;
583+ fs . stat ( "/test/path/sub" , ( err , r2 ) => {
584+ r2 . cached = true ;
585+ fs . purge ( Buffer . from ( "/test/path" ) , { exact : true } ) ;
586+ fs . stat ( "/test/path" , ( err , r3 ) => {
587+ expect ( r3 . cached ) . toBeUndefined ( ) ;
588+ fs . stat ( "/test/path/sub" , ( err , r4 ) => {
589+ expect ( r4 . cached ) . toBe ( true ) ;
590+ fs . purge ( [ Buffer . from ( "/test/path/sub" ) ] , {
591+ exact : true ,
592+ } ) ;
593+ fs . stat ( "/test/path/sub" , ( err , r5 ) => {
594+ expect ( r5 . cached ) . toBeUndefined ( ) ;
595+ done ( ) ;
596+ } ) ;
597+ } ) ;
598+ } ) ;
599+ } ) ;
600+ } ) ;
601+ } ) ;
602+
463603 it ( "should not stack overflow when resolving in an async loop" , ( done ) => {
464604 let i = 10000 ;
465605 const next = ( ) => {
0 commit comments