1
1
import { createNodeFsMountHandler , loadNodeRuntime } from '..' ;
2
- import { PHP } from '@php-wasm/universal' ;
2
+ import { ErrnoError , PHP } from '@php-wasm/universal' ;
3
3
import { RecommendedPHPVersion } from '@wp-playground/common' ;
4
4
import path , { dirname } from 'path' ;
5
5
import fs from 'fs' ;
@@ -18,132 +18,257 @@ describe('Mounting', () => {
18
18
php . exit ( ) ;
19
19
} ) ;
20
20
21
- it ( 'Should mount a file with exact content match' , async ( ) => {
22
- const testFilePath = path . join (
23
- __dirname ,
24
- 'test-data' ,
25
- 'long-post-body.txt'
26
- ) ;
21
+ describe ( 'File operations' , ( ) => {
22
+ it ( 'Should mount a file with exact content match' , async ( ) => {
23
+ const testFilePath = path . join (
24
+ __dirname ,
25
+ 'test-data' ,
26
+ 'long-post-body.txt'
27
+ ) ;
27
28
28
- await php . mount (
29
- '/single-file.txt' ,
30
- createNodeFsMountHandler ( testFilePath )
31
- ) ;
29
+ await php . mount (
30
+ '/single-file.txt' ,
31
+ createNodeFsMountHandler ( testFilePath )
32
+ ) ;
32
33
33
- const vfsContent = await php . readFileAsText ( '/single-file.txt' ) ;
34
- const localContent = fs . readFileSync ( testFilePath , 'utf8' ) ;
35
- expect ( vfsContent ) . toEqual ( localContent ) ;
36
- } ) ;
34
+ const vfsContent = await php . readFileAsText ( '/single-file.txt' ) ;
35
+ const localContent = fs . readFileSync ( testFilePath , 'utf8' ) ;
36
+ expect ( vfsContent ) . toEqual ( localContent ) ;
37
+ } ) ;
37
38
38
- it ( 'Should mount nested directories with recursive structure matching' , async ( ) => {
39
- const testDataPath = path . join ( __dirname , 'test-data' ) ;
40
- await php . mount ( '/nested-test' , createNodeFsMountHandler ( testDataPath ) ) ;
41
-
42
- // Recursively compare directory structure
43
- const compareDirectories = ( vfsPath : string , localPath : string ) => {
44
- if ( ! fs . existsSync ( localPath ) ) return ;
45
-
46
- const localFiles = fs . readdirSync ( localPath ) ;
47
- const vfsFiles = php . listFiles ( vfsPath ) ;
48
- expect ( vfsFiles . sort ( ) ) . toEqual ( localFiles . sort ( ) ) ;
49
-
50
- localFiles . forEach ( ( file ) => {
51
- const localFilePath = path . join ( localPath , file ) ;
52
- const vfsFilePath = `${ vfsPath } /${ file } ` ;
53
- const localStats = fs . statSync ( localFilePath ) ;
54
-
55
- expect ( php . isFile ( vfsFilePath ) ) . toBe ( localStats . isFile ( ) ) ;
56
- expect ( php . isDir ( vfsFilePath ) ) . toBe ( localStats . isDirectory ( ) ) ;
57
-
58
- if ( localStats . isDirectory ( ) ) {
59
- compareDirectories ( vfsFilePath , localFilePath ) ;
60
- }
61
- } ) ;
62
- } ;
63
-
64
- compareDirectories ( '/nested-test' , testDataPath ) ;
65
-
66
- // Test specific nested file content
67
- const nestedFilePath =
68
- '/nested-test/nested-symlinked-folder/nested-document.txt' ;
69
- const localNestedPath = path . join (
70
- testDataPath ,
71
- 'nested-symlinked-folder' ,
72
- 'nested-document.txt'
73
- ) ;
39
+ it ( 'Should throw an error when mounting to an existing file' , async ( ) => {
40
+ const testFilePath = path . join (
41
+ __dirname ,
42
+ 'test-data' ,
43
+ 'long-post-body.txt'
44
+ ) ;
74
45
75
- if ( fs . existsSync ( localNestedPath ) ) {
76
- const vfsContent = await php . readFileAsText ( nestedFilePath ) ;
77
- const localContent = fs . readFileSync ( localNestedPath , 'utf8' ) ;
78
- expect ( vfsContent ) . toEqual ( localContent ) ;
79
- }
80
- } ) ;
46
+ await php . mount (
47
+ '/single-file.txt' ,
48
+ createNodeFsMountHandler ( testFilePath )
49
+ ) ;
81
50
82
- it ( 'Should unmount a file and remove created node from VFS' , async ( ) => {
83
- const testFilePath = path . join (
84
- __dirname ,
85
- 'test-data' ,
86
- 'long-post-body.txt'
87
- ) ;
51
+ try {
52
+ await php . mount (
53
+ '/single-file.txt' ,
54
+ createNodeFsMountHandler ( testFilePath )
55
+ ) ;
56
+ } catch ( e : any ) {
57
+ e = e as ErrnoError ;
58
+ expect ( e . name ) . toBe ( 'ErrnoError' ) ;
59
+ expect ( e . errno ) . toBe ( 10 ) ;
60
+ }
61
+ } ) ;
88
62
89
- const unmount = await php . mount (
90
- '/single-file.txt' ,
91
- createNodeFsMountHandler ( testFilePath )
92
- ) ;
63
+ it ( 'Should be editable' , async ( ) => {
64
+ const testFilePath = path . join (
65
+ __dirname ,
66
+ 'test-data' ,
67
+ 'long-post-body.txt'
68
+ ) ;
69
+ await php . mount (
70
+ '/single-file.txt' ,
71
+ createNodeFsMountHandler ( testFilePath )
72
+ ) ;
93
73
94
- expect ( php . isFile ( '/single-file.txt' ) ) . toBe ( true ) ;
74
+ const originalContent = await php . readFileAsText (
75
+ '/single-file.txt'
76
+ ) ;
77
+ await php . writeFile ( '/single-file.txt' , 'new content' ) ;
95
78
96
- await unmount ( ) ;
97
- expect ( php . isFile ( '/single-file.txt' ) ) . toBe ( false ) ;
98
- } ) ;
79
+ expect ( await php . readFileAsText ( '/single-file.txt' ) ) . toBe (
80
+ 'new content'
81
+ ) ;
99
82
100
- it ( 'Should unmount a directory and remove created node from VFS' , async ( ) => {
101
- const testDataPath = path . join ( __dirname , 'test-data' ) ;
102
- const unmount = await php . mount (
103
- '/nested-test' ,
104
- createNodeFsMountHandler ( testDataPath )
105
- ) ;
83
+ await php . writeFile ( '/single-file.txt' , originalContent ) ;
84
+ expect ( await php . readFileAsText ( '/single-file.txt' ) ) . toBe (
85
+ originalContent
86
+ ) ;
87
+ } ) ;
106
88
107
- expect ( php . isDir ( '/nested-test' ) ) . toBe ( true ) ;
89
+ it ( 'Should not be deletable' , async ( ) => {
90
+ const testFilePath = path . join (
91
+ __dirname ,
92
+ 'test-data' ,
93
+ 'long-post-body.txt'
94
+ ) ;
95
+ await php . mount (
96
+ '/single-file.txt' ,
97
+ createNodeFsMountHandler ( testFilePath )
98
+ ) ;
108
99
109
- await unmount ( ) ;
110
- expect ( php . isDir ( '/nested-test' ) ) . toBe ( false ) ;
111
- } ) ;
100
+ try {
101
+ await php . unlink ( '/single-file.txt' ) ;
102
+ } catch ( e : any ) {
103
+ e = e as Error ;
104
+ expect ( e . message ) . toContain (
105
+ 'Could not unlink "/single-file.txt": Device or resource busy.'
106
+ ) ;
107
+ }
108
+ } ) ;
112
109
113
- it ( 'Should unmount a file, but not remove the parent directory from VFS if it was created manually' , async ( ) => {
114
- const testFilePath = path . join (
115
- __dirname ,
116
- 'test-data' ,
117
- 'long-post-body.txt'
118
- ) ;
110
+ it ( 'Should not be movable' , async ( ) => {
111
+ const testFilePath = path . join (
112
+ __dirname ,
113
+ 'test-data' ,
114
+ 'long-post-body.txt'
115
+ ) ;
116
+ await php . mount (
117
+ '/single-file.txt' ,
118
+ createNodeFsMountHandler ( testFilePath )
119
+ ) ;
119
120
120
- const mountPoint = '/sub-dir/single-file.txt' ;
121
+ try {
122
+ await php . mv ( '/single-file.txt' , '/single-file-moved.txt' ) ;
123
+ } catch ( e : any ) {
124
+ e = e as Error ;
125
+ expect ( e . message ) . toContain (
126
+ 'Could not move /single-file.txt to /single-file-moved.txt: Device or resource busy.'
127
+ ) ;
128
+ }
129
+ } ) ;
121
130
122
- await php . mkdir ( dirname ( mountPoint ) ) ;
131
+ it ( 'Should unmount a file and remove created node from VFS' , async ( ) => {
132
+ const testFilePath = path . join (
133
+ __dirname ,
134
+ 'test-data' ,
135
+ 'long-post-body.txt'
136
+ ) ;
123
137
124
- const unmount = await php . mount (
125
- mountPoint ,
126
- createNodeFsMountHandler ( testFilePath )
127
- ) ;
138
+ const unmount = await php . mount (
139
+ '/single-file.txt' ,
140
+ createNodeFsMountHandler ( testFilePath )
141
+ ) ;
142
+
143
+ expect ( php . isFile ( '/single-file.txt' ) ) . toBe ( true ) ;
144
+
145
+ await unmount ( ) ;
146
+ expect ( php . isFile ( '/single-file.txt' ) ) . toBe ( false ) ;
147
+ } ) ;
148
+
149
+ it ( 'Should remount after unmounting' , async ( ) => {
150
+ const testFilePath = path . join (
151
+ __dirname ,
152
+ 'test-data' ,
153
+ 'long-post-body.txt'
154
+ ) ;
155
+
156
+ const unmount = await php . mount (
157
+ '/single-file.txt' ,
158
+ createNodeFsMountHandler ( testFilePath )
159
+ ) ;
160
+
161
+ await unmount ( ) ;
162
+ await php . mount (
163
+ '/single-file.txt' ,
164
+ createNodeFsMountHandler ( testFilePath )
165
+ ) ;
128
166
129
- expect ( php . isFile ( mountPoint ) ) . toBe ( true ) ;
167
+ expect ( php . isFile ( '/single-file.txt' ) ) . toBe ( true ) ;
168
+ expect ( await php . readFileAsText ( '/single-file.txt' ) ) . toBe (
169
+ fs . readFileSync ( testFilePath , 'utf8' )
170
+ ) ;
171
+ } ) ;
130
172
131
- await unmount ( ) ;
132
- expect ( php . isDir ( dirname ( mountPoint ) ) ) . toBe ( true ) ;
173
+ it ( 'Should unmount a file, but not remove the parent directory from VFS if it was created manually' , async ( ) => {
174
+ const testFilePath = path . join (
175
+ __dirname ,
176
+ 'test-data' ,
177
+ 'long-post-body.txt'
178
+ ) ;
179
+
180
+ const mountPoint = '/sub-dir/single-file.txt' ;
181
+
182
+ await php . mkdir ( dirname ( mountPoint ) ) ;
183
+
184
+ const unmount = await php . mount (
185
+ mountPoint ,
186
+ createNodeFsMountHandler ( testFilePath )
187
+ ) ;
188
+
189
+ expect ( php . isFile ( mountPoint ) ) . toBe ( true ) ;
190
+
191
+ await unmount ( ) ;
192
+ expect ( php . isDir ( dirname ( mountPoint ) ) ) . toBe ( true ) ;
193
+ } ) ;
133
194
} ) ;
134
195
135
- it ( 'Should unmount a directory, but not remove the parent directory from VFS if it was created manually' , async ( ) => {
136
- const testDataPath = path . join ( __dirname , 'test-data' ) ;
196
+ describe ( 'Directory operations' , ( ) => {
197
+ it ( 'Should mount nested directories with recursive structure matching' , async ( ) => {
198
+ const testDataPath = path . join ( __dirname , 'test-data' ) ;
199
+ await php . mount (
200
+ '/nested-test' ,
201
+ createNodeFsMountHandler ( testDataPath )
202
+ ) ;
137
203
138
- await php . mkdir ( '/nested-test' ) ;
139
- const unmount = await php . mount (
140
- '/nested-test' ,
141
- createNodeFsMountHandler ( testDataPath )
142
- ) ;
204
+ // Recursively compare directory structure
205
+ const compareDirectories = ( vfsPath : string , localPath : string ) => {
206
+ if ( ! fs . existsSync ( localPath ) ) return ;
207
+
208
+ const localFiles = fs . readdirSync ( localPath ) ;
209
+ const vfsFiles = php . listFiles ( vfsPath ) ;
210
+ expect ( vfsFiles . sort ( ) ) . toEqual ( localFiles . sort ( ) ) ;
211
+
212
+ localFiles . forEach ( ( file ) => {
213
+ const localFilePath = path . join ( localPath , file ) ;
214
+ const vfsFilePath = `${ vfsPath } /${ file } ` ;
215
+ const localStats = fs . statSync ( localFilePath ) ;
216
+
217
+ expect ( php . isFile ( vfsFilePath ) ) . toBe ( localStats . isFile ( ) ) ;
218
+ expect ( php . isDir ( vfsFilePath ) ) . toBe (
219
+ localStats . isDirectory ( )
220
+ ) ;
221
+
222
+ if ( localStats . isDirectory ( ) ) {
223
+ compareDirectories ( vfsFilePath , localFilePath ) ;
224
+ }
225
+ } ) ;
226
+ } ;
227
+
228
+ compareDirectories ( '/nested-test' , testDataPath ) ;
229
+
230
+ // Test specific nested file content
231
+ const nestedFilePath =
232
+ '/nested-test/nested-symlinked-folder/nested-document.txt' ;
233
+ const localNestedPath = path . join (
234
+ testDataPath ,
235
+ 'nested-symlinked-folder' ,
236
+ 'nested-document.txt'
237
+ ) ;
238
+
239
+ if ( fs . existsSync ( localNestedPath ) ) {
240
+ const vfsContent = await php . readFileAsText ( nestedFilePath ) ;
241
+ const localContent = fs . readFileSync ( localNestedPath , 'utf8' ) ;
242
+ expect ( vfsContent ) . toEqual ( localContent ) ;
243
+ }
244
+ } ) ;
245
+
246
+ it ( 'Should unmount a directory and remove created node from VFS' , async ( ) => {
247
+ const testDataPath = path . join ( __dirname , 'test-data' ) ;
248
+ const unmount = await php . mount (
249
+ '/nested-test' ,
250
+ createNodeFsMountHandler ( testDataPath )
251
+ ) ;
252
+
253
+ expect ( php . isDir ( '/nested-test' ) ) . toBe ( true ) ;
254
+
255
+ await unmount ( ) ;
256
+ expect ( php . isDir ( '/nested-test' ) ) . toBe ( false ) ;
257
+ } ) ;
258
+
259
+ it ( 'Should unmount a directory, but not remove the parent directory from VFS if it was created manually' , async ( ) => {
260
+ const testDataPath = path . join ( __dirname , 'test-data' ) ;
261
+
262
+ await php . mkdir ( '/nested-test' ) ;
263
+ const unmount = await php . mount (
264
+ '/nested-test' ,
265
+ createNodeFsMountHandler ( testDataPath )
266
+ ) ;
143
267
144
- expect ( php . isDir ( '/nested-test' ) ) . toBe ( true ) ;
268
+ expect ( php . isDir ( '/nested-test' ) ) . toBe ( true ) ;
145
269
146
- await unmount ( ) ;
147
- expect ( php . isDir ( '/nested-test' ) ) . toBe ( true ) ;
270
+ await unmount ( ) ;
271
+ expect ( php . isDir ( '/nested-test' ) ) . toBe ( true ) ;
272
+ } ) ;
148
273
} ) ;
149
274
} ) ;
0 commit comments