@@ -564,3 +564,195 @@ function createMountedVfs() {
564564 myVfs . unmount ( ) ;
565565 } ) ) ;
566566}
567+
568+ // ==================== Symlink ops ====================
569+
570+ // Test fs.symlinkSync / fs.readlinkSync on VFS
571+ {
572+ const { myVfs, mountPoint } = createMountedVfs ( ) ;
573+ fs . symlinkSync ( 'hello.txt' , path . join ( mountPoint , 'src/link.txt' ) ) ;
574+ const target = fs . readlinkSync ( path . join ( mountPoint , 'src/link.txt' ) ) ;
575+ assert . strictEqual ( target , 'hello.txt' ) ;
576+ myVfs . unmount ( ) ;
577+ }
578+
579+ // Test fs.symlink / fs.readlink callbacks on VFS
580+ {
581+ const { myVfs, mountPoint } = createMountedVfs ( ) ;
582+ fs . symlink ( 'hello.txt' , path . join ( mountPoint , 'src/link-cb.txt' ) , common . mustCall ( ( err ) => {
583+ assert . strictEqual ( err , null ) ;
584+ fs . readlink ( path . join ( mountPoint , 'src/link-cb.txt' ) , common . mustCall ( ( err2 , target ) => {
585+ assert . strictEqual ( err2 , null ) ;
586+ assert . strictEqual ( target , 'hello.txt' ) ;
587+ myVfs . unmount ( ) ;
588+ } ) ) ;
589+ } ) ) ;
590+ }
591+
592+ // Test fs.promises.symlink / fs.promises.readlink on VFS
593+ {
594+ const { myVfs, mountPoint } = createMountedVfs ( ) ;
595+ fs . promises . symlink ( 'hello.txt' , path . join ( mountPoint , 'src/link-promise.txt' ) )
596+ . then ( ( ) => fs . promises . readlink ( path . join ( mountPoint , 'src/link-promise.txt' ) ) )
597+ . then ( common . mustCall ( ( target ) => {
598+ assert . strictEqual ( target , 'hello.txt' ) ;
599+ myVfs . unmount ( ) ;
600+ } ) ) ;
601+ }
602+
603+ // ==================== Additional callback write ops ====================
604+
605+ // Test fs.appendFile callback on VFS
606+ {
607+ const { myVfs, mountPoint } = createMountedVfs ( ) ;
608+ fs . appendFile ( path . join ( mountPoint , 'src/hello.txt' ) , ' cb-appended' , common . mustCall ( ( err ) => {
609+ assert . strictEqual ( err , null ) ;
610+ const content = fs . readFileSync ( path . join ( mountPoint , 'src/hello.txt' ) , 'utf8' ) ;
611+ assert . strictEqual ( content , 'hello world cb-appended' ) ;
612+ myVfs . unmount ( ) ;
613+ } ) ) ;
614+ }
615+
616+ // Test fs.rmdir callback on VFS
617+ {
618+ const { myVfs, mountPoint } = createMountedVfs ( ) ;
619+ fs . mkdirSync ( path . join ( mountPoint , 'src/cb-rmdir-test' ) ) ;
620+ fs . rmdir ( path . join ( mountPoint , 'src/cb-rmdir-test' ) , common . mustCall ( ( err ) => {
621+ assert . strictEqual ( err , null ) ;
622+ assert . strictEqual ( fs . existsSync ( path . join ( mountPoint , 'src/cb-rmdir-test' ) ) , false ) ;
623+ myVfs . unmount ( ) ;
624+ } ) ) ;
625+ }
626+
627+ // Test fs.rm callback on VFS
628+ {
629+ const { myVfs, mountPoint } = createMountedVfs ( ) ;
630+ fs . rm ( path . join ( mountPoint , 'src/hello.txt' ) , common . mustCall ( ( err ) => {
631+ assert . strictEqual ( err , null ) ;
632+ assert . strictEqual ( fs . existsSync ( path . join ( mountPoint , 'src/hello.txt' ) ) , false ) ;
633+ myVfs . unmount ( ) ;
634+ } ) ) ;
635+ }
636+
637+ // ==================== FD callback write ops ====================
638+
639+ // Test fs.write callback on VFS
640+ {
641+ const { myVfs, mountPoint } = createMountedVfs ( ) ;
642+ fs . open ( path . join ( mountPoint , 'src/fd-write-cb.txt' ) , 'w' , common . mustCall ( ( err , fd ) => {
643+ assert . strictEqual ( err , null ) ;
644+ const data = Buffer . from ( 'fd write callback' ) ;
645+ fs . write ( fd , data , 0 , data . length , 0 , common . mustCall ( ( err2 , bytesWritten ) => {
646+ assert . strictEqual ( err2 , null ) ;
647+ assert . strictEqual ( bytesWritten , data . length ) ;
648+ fs . close ( fd , common . mustCall ( ( err3 ) => {
649+ assert . strictEqual ( err3 , null ) ;
650+ const content = fs . readFileSync ( path . join ( mountPoint , 'src/fd-write-cb.txt' ) , 'utf8' ) ;
651+ assert . strictEqual ( content , 'fd write callback' ) ;
652+ myVfs . unmount ( ) ;
653+ } ) ) ;
654+ } ) ) ;
655+ } ) ) ;
656+ }
657+
658+ // ==================== writeSync with string argument ====================
659+
660+ // Test fs.writeSync with string (not Buffer)
661+ {
662+ const { myVfs, mountPoint } = createMountedVfs ( ) ;
663+ const fd = fs . openSync ( path . join ( mountPoint , 'src/string-write.txt' ) , 'w' ) ;
664+ const bytesWritten = fs . writeSync ( fd , 'string data' , 0 , 'utf8' ) ;
665+ assert . ok ( bytesWritten > 0 ) ;
666+ fs . closeSync ( fd ) ;
667+ const content = fs . readFileSync ( path . join ( mountPoint , 'src/string-write.txt' ) , 'utf8' ) ;
668+ assert . strictEqual ( content , 'string data' ) ;
669+ myVfs . unmount ( ) ;
670+ }
671+
672+ // ==================== ReadStream with start/end options ====================
673+
674+ // Test fs.createReadStream with start/end on VFS
675+ {
676+ const { myVfs, mountPoint } = createMountedVfs ( ) ;
677+ const chunks = [ ] ;
678+ const stream = fs . createReadStream ( path . join ( mountPoint , 'src/hello.txt' ) , {
679+ start : 0 ,
680+ end : 4 ,
681+ } ) ;
682+ assert . strictEqual ( stream . path , path . join ( mountPoint , 'src/hello.txt' ) ) ;
683+ stream . on ( 'data' , ( chunk ) => chunks . push ( chunk ) ) ;
684+ stream . on ( 'end' , common . mustCall ( ( ) => {
685+ assert . strictEqual ( Buffer . concat ( chunks ) . toString ( ) , 'hello' ) ;
686+ myVfs . unmount ( ) ;
687+ } ) ) ;
688+ }
689+
690+ // ==================== Stream open event ====================
691+
692+ // Test ReadStream emits 'open' with VFS fd
693+ {
694+ const { myVfs, mountPoint } = createMountedVfs ( ) ;
695+ const stream = fs . createReadStream ( path . join ( mountPoint , 'src/hello.txt' ) ) ;
696+ stream . on ( 'open' , common . mustCall ( ( fd ) => {
697+ assert . ok ( fd >= 10000 ) ;
698+ } ) ) ;
699+ stream . on ( 'end' , common . mustCall ( ( ) => {
700+ myVfs . unmount ( ) ;
701+ } ) ) ;
702+ stream . resume ( ) ; // Consume the stream
703+ }
704+
705+ // Test WriteStream path getter and 'open' event
706+ {
707+ const { myVfs, mountPoint } = createMountedVfs ( ) ;
708+ const filePath = path . join ( mountPoint , 'src/ws-open.txt' ) ;
709+ const stream = fs . createWriteStream ( filePath ) ;
710+ assert . strictEqual ( stream . path , filePath ) ;
711+ stream . on ( 'open' , common . mustCall ( ( fd ) => {
712+ assert . ok ( fd >= 10000 ) ;
713+ } ) ) ;
714+ stream . end ( 'done' , common . mustCall ( ( ) => {
715+ myVfs . unmount ( ) ;
716+ } ) ) ;
717+ }
718+
719+ // ==================== VFS class properties ====================
720+
721+ // Test VFS instance property getters
722+ {
723+ const myVfs = vfs . create ( ) ;
724+ assert . ok ( myVfs . provider !== null ) ;
725+ assert . strictEqual ( myVfs . mountPoint , null ) ;
726+ assert . strictEqual ( myVfs . mounted , false ) ;
727+ assert . strictEqual ( myVfs . readonly , false ) ;
728+ assert . strictEqual ( myVfs . overlay , false ) ;
729+
730+ const mountPoint = baseMountPoint + '-' + ( mountCounter ++ ) ;
731+ myVfs . mount ( mountPoint ) ;
732+ assert . strictEqual ( myVfs . mountPoint , mountPoint ) ;
733+ assert . strictEqual ( myVfs . mounted , true ) ;
734+ myVfs . unmount ( ) ;
735+ assert . strictEqual ( myVfs . mountPoint , null ) ;
736+ assert . strictEqual ( myVfs . mounted , false ) ;
737+ }
738+
739+ // ==================== rmSync with force option ====================
740+
741+ // Test fs.rmSync with force: true on nonexistent file
742+ {
743+ const { myVfs, mountPoint } = createMountedVfs ( ) ;
744+ // Should not throw with force: true
745+ fs . rmSync ( path . join ( mountPoint , 'nonexistent' ) , { force : true } ) ;
746+ myVfs . unmount ( ) ;
747+ }
748+
749+ // ==================== promises.rm recursive ====================
750+
751+ // Test fs.promises.rm with recursive directory
752+ {
753+ const { myVfs, mountPoint } = createMountedVfs ( ) ;
754+ fs . promises . rm ( path . join ( mountPoint , 'src' ) , { recursive : true } ) . then ( common . mustCall ( ( ) => {
755+ assert . strictEqual ( fs . existsSync ( path . join ( mountPoint , 'src' ) ) , false ) ;
756+ myVfs . unmount ( ) ;
757+ } ) ) ;
758+ }
0 commit comments