@@ -33,12 +33,14 @@ class MemoryFile {
3333 * @param {Object } param
3434 * @param {string } param.full_path
3535 * @param {boolean } param.is_dir
36- * @param {Buffer } param.content - The content of the file, `null` if the file is a directory.
36+ * @param {Buffer|null } param.content - The content of the file, `null` if the file is a directory.
37+ * @param {string|null } [param.parent_uid] - UID of parent directory; null for root.
3738 */
3839 constructor ( {
3940 full_path,
4041 is_dir,
4142 content,
43+ parent_uid = null ,
4244 } ) {
4345 this . uuid = uuidv4 ( ) ;
4446
@@ -49,8 +51,8 @@ class MemoryFile {
4951
5052 this . content = content ;
5153
52- // TODO (xiaochen): return consistent parent_uid
53- this . parent_uid = uuidv4 ( ) ;
54+ // parent_uid should reflect the actual parent's uid; null for root
55+ this . parent_uid = parent_uid ;
5456
5557 // TODO (xiaochen): return sensible values for "user_id", currently
5658 // it must be 2 (admin) to pass the test.
@@ -89,6 +91,7 @@ class MemoryFSProvider {
8991 full_path : '/' ,
9092 is_dir : true ,
9193 content : null ,
94+ parent_uid : null ,
9295 } ) ;
9396 this . entriesByPath . set ( '/' , root . uuid ) ;
9497 this . entriesByUUID . set ( root . uuid , root ) ;
@@ -132,40 +135,62 @@ class MemoryFSProvider {
132135 }
133136
134137 /**
135- * Check the integrity of the whole memory filesystem and the input . Throws error if any violation is found.
138+ * Check the integrity of the whole memory filesystem. Throws error if any violation is found.
136139 *
137- * @param {MemoryFile|FSNodeContext } entry - The entry to check.
138140 * @returns {Promise<void> }
139141 */
140- _integrity_check ( entry ) {
142+ _integrity_check ( ) {
141143 if ( config . env !== 'dev' ) {
142144 // only check in debug mode since it's expensive
143145 return ;
144146 }
145147
146- if ( entry ) {
147- // check all directories along the path are valid
148- const path_to_check = 'entry' in entry ? entry . entry ?. path : entry . path ;
149- const inner_path = this . _inner_path ( path_to_check ) ;
150- const path_components = inner_path . split ( '/' ) ;
151- for ( let i = 2 ; i < path_components . length ; i ++ ) {
152- const path_component = path_components . slice ( 0 , i ) . join ( '/' ) ;
153- const entry_uuid = this . entriesByPath . get ( path_component ) ;
154- if ( ! entry_uuid ) {
155- throw new Error ( `Directory ${ path_component } does not exist` ) ;
156- }
157- }
158- }
159-
160148 // check the 2 maps are consistent
161149 if ( this . entriesByPath . size !== this . entriesByUUID . size ) {
162150 throw new Error ( 'Path map and UUID map have different sizes' ) ;
163151 }
152+
164153 for ( const [ inner_path , uuid ] of this . entriesByPath ) {
165154 const entry = this . entriesByUUID . get ( uuid ) ;
166- if ( ! entry || this . _inner_path ( entry . path ) !== inner_path ) {
155+
156+ // entry should exist
157+ if ( ! entry ) {
158+ throw new Error ( `Entry ${ uuid } does not exist` ) ;
159+ }
160+
161+ // path should match
162+ if ( this . _inner_path ( entry . path ) !== inner_path ) {
167163 throw new Error ( `Path ${ inner_path } does not match entry ${ uuid } ` ) ;
168164 }
165+
166+ // uuid should match
167+ if ( entry . uuid !== uuid ) {
168+ throw new Error ( `UUID ${ uuid } does not match entry ${ entry . uuid } ` ) ;
169+ }
170+
171+ // parent should exist
172+ if ( entry . parent_uid ) {
173+ const parent_entry = this . entriesByUUID . get ( entry . parent_uid ) ;
174+ if ( ! parent_entry ) {
175+ throw new Error ( `Parent ${ entry . parent_uid } does not exist` ) ;
176+ }
177+ }
178+
179+ // parent's path should be a prefix of the entry's path
180+ if ( entry . parent_uid ) {
181+ const parent_entry = this . entriesByUUID . get ( entry . parent_uid ) ;
182+ if ( ! entry . path . startsWith ( parent_entry . path ) ) {
183+ throw new Error ( `Parent ${ entry . parent_uid } path ${ parent_entry . path } is not a prefix of entry ${ entry . path } ` ) ;
184+ }
185+ }
186+
187+ // parent should be a directory
188+ if ( entry . parent_uid ) {
189+ const parent_entry = this . entriesByUUID . get ( entry . parent_uid ) ;
190+ if ( ! parent_entry . is_dir ) {
191+ throw new Error ( `Parent ${ entry . parent_uid } is not a directory` ) ;
192+ }
193+ }
169194 }
170195 }
171196
@@ -219,7 +244,7 @@ class MemoryFSProvider {
219244 * @param {string } param.uid
220245 * @returns {Promise<Object|null> } - The result of the stat operation, or `null` if the node doesn't exist.
221246 */
222- async _stat_by_uid ( { uid } ) {
247+ async stat_by_uid ( { uid } ) {
223248 const entry = this . entriesByUUID . get ( uid ) ;
224249 if ( ! entry ) {
225250 return null ;
@@ -271,6 +296,7 @@ class MemoryFSProvider {
271296 full_path : full_path ,
272297 is_dir : true ,
273298 content : null ,
299+ parent_uid : parent . uid ,
274300 } ) ;
275301 this . entriesByPath . set ( inner_path , entry . uuid ) ;
276302 this . entriesByUUID . set ( entry . uuid , entry ) ;
@@ -279,7 +305,7 @@ class MemoryFSProvider {
279305 const fs = context . get ( 'services' ) . get ( 'filesystem' ) ;
280306 const node = await fs . node ( entry . path ) ;
281307
282- this . _integrity_check ( node ) ;
308+ this . _integrity_check ( ) ;
283309
284310 return node ;
285311 }
@@ -330,7 +356,7 @@ class MemoryFSProvider {
330356 this . entriesByUUID . delete ( node . uid ) ;
331357 }
332358
333- this . _integrity_check ( node ) ;
359+ this . _integrity_check ( ) ;
334360 }
335361
336362 /**
@@ -359,15 +385,14 @@ class MemoryFSProvider {
359385 * @returns {Promise<MemoryFile> }
360386 */
361387 async move ( { context, node, new_parent, new_name, metadata } ) {
362- this . _integrity_check ( null ) ;
363-
364388 // create the new entry
365389 const new_full_path = _path . join ( new_parent . path , new_name ) ;
366390 const new_inner_path = this . _inner_path ( new_full_path ) ;
367391 const entry = new MemoryFile ( {
368392 full_path : new_full_path ,
369393 is_dir : node . entry . is_dir ,
370394 content : node . entry . content ,
395+ parent_uid : new_parent . uid ,
371396 } ) ;
372397 entry . uuid = node . entry . uuid ;
373398 this . entriesByPath . set ( new_inner_path , entry . uuid ) ;
@@ -378,7 +403,7 @@ class MemoryFSProvider {
378403 this . entriesByPath . delete ( inner_path ) ;
379404 // should not delete the entry by uuid because it's the same
380405
381- this . _integrity_check ( entry ) ;
406+ this . _integrity_check ( ) ;
382407
383408 return entry ;
384409 }
@@ -456,6 +481,7 @@ class MemoryFSProvider {
456481 full_path : full_path ,
457482 is_dir : false ,
458483 content : file . stream . read ( ) ,
484+ parent_uid : parent . uid ,
459485 } ) ;
460486 this . entriesByPath . set ( inner_path , entry . uuid ) ;
461487 this . entriesByUUID . set ( entry . uuid , entry ) ;
@@ -495,13 +521,12 @@ class MemoryFSProvider {
495521 this . entriesByUUID . set ( node . uid , original_entry ) ;
496522 }
497523
498- this . _integrity_check ( node ) ;
499-
500- // return node;
501524 const fs = context . get ( 'services' ) . get ( 'filesystem' ) ;
502525 node = await fs . node ( original_entry . path ) ;
503526 await node . fetchEntry ( ) ;
504527
528+ this . _integrity_check ( ) ;
529+
505530 return node ;
506531 }
507532}
0 commit comments