@@ -55,11 +55,14 @@ pub struct Store {
5555/// Any errors returned by `fs::create_dir_all` are ignored.
5656#[ cfg( feature = "encoding" ) ]
5757pub fn create_repo ( repo_dir : & Path ) -> Result < ( ) > {
58- if !repo_dir. exists ( ) {
59- let _ = fs:: create_dir_all ( repo_dir. join ( "chunks" ) ) ;
60- let _ = fs:: create_dir_all ( repo_dir. join ( "manifests" ) ) ;
61- }
62- Ok ( ( ) )
58+ return if !repo_dir. exists ( ) {
59+ let _ = fs:: create_dir_all ( repo_dir. join ( "chunks" ) ) ?;
60+ let _ = fs:: create_dir_all ( repo_dir. join ( "manifests" ) ) ?;
61+
62+ Ok ( ( ) )
63+ } else {
64+ bail ! ( "Already exists! {}" , repo_dir. display( ) )
65+ } ;
6366}
6467
6568/// Attempts to create the Store and it's associated directories.
@@ -95,7 +98,7 @@ pub fn create_store(store: &Store) -> Result<()> {
9598
9699/// Creates a manifest and it's associated chunks from a dir structure, and saves it into the list of artifacts
97100#[ cfg( feature = "encoding" ) ]
98- pub fn build ( input_dir : & Path , repo_dir : & Path , artifact_name : & String ) -> Result < String > {
101+ pub fn build ( input_dir : & PathBuf , repo_dir : & PathBuf , artifact_name : & String ) -> Result < String > {
99102 use std:: os:: unix:: fs:: PermissionsExt ;
100103 use walkdir:: WalkDir ;
101104
@@ -256,15 +259,38 @@ fn resolve_repo_path(store: &Store, path: &String) -> Result<PathBuf> {
256259}
257260
258261#[ cfg( feature = "decoding" ) ]
259- fn get_temp_file ( potential : Option < u8 > , dir : & Path ) -> String {
262+ fn get_temp_file ( potential : Option < u8 > , dir : & Path ) -> PathBuf {
260263 let potential = potential. unwrap_or_default ( ) ;
261264
262265 let file_name = format ! ( ".tmp_{potential}" ) ;
263266
267+ // Overflow protection, if the potential number is too high, we clear the oldest old temp file and replace it.
268+ if potential > u8:: MAX - 1 {
269+ // Find the most recently modified temp file and remove it to prevent overflow
270+ if let Ok ( entries) = std:: fs:: read_dir ( dir) {
271+ let oldest_temp = entries
272+ . flatten ( )
273+ . filter ( |f| {
274+ f. file_type ( ) . map ( |ft| ft. is_file ( ) ) . unwrap_or ( false )
275+ && f. file_name ( ) . to_string_lossy ( ) . starts_with ( ".tmp_" )
276+ } )
277+ . min_by_key ( |f| f. metadata ( ) . and_then ( |m| m. modified ( ) ) . ok ( ) )
278+ . map ( |f| f. path ( ) )
279+ . unwrap ( ) ;
280+
281+ fs:: remove_file ( & oldest_temp) . unwrap_or_else ( |x| {
282+ panic ! ( "Failed to remove old temp symlink: {x}" ) ;
283+ } ) ;
284+
285+ return oldest_temp;
286+ }
287+ }
288+
289+ // Check if the file already exists, if it does, increment the potential number
264290 if dir. join ( & file_name) . exists ( ) {
265291 get_temp_file ( Some ( potential + 1 ) , dir)
266292 } else {
267- file_name
293+ dir . join ( & file_name)
268294 }
269295}
270296
@@ -319,11 +345,17 @@ mod tests {
319345 #[ cfg( feature = "decoding" ) ]
320346 use crate :: { RepoType , Store , create_store, resolve_repo_path} ;
321347
348+ #[ test]
349+ #[ cfg( all( feature = "encoding" , feature = "decoding" ) ) ]
350+ fn test_create_store ( ) {
351+ let _ = create_test_store ( "basic" ) ;
352+ }
353+
322354 #[ cfg( all( feature = "encoding" , feature = "decoding" ) ) ]
323- fn create_test_store ( ) -> Store {
324- let repo = temp_dir ( ) . join ( "lcas_testing_repo" ) ;
325- let cache = temp_dir ( ) . join ( "lcas_testing_cache" ) ;
326- let store_path = temp_dir ( ) . join ( "lcas_testing_store" ) ;
355+ fn create_test_store ( test_name : & str ) -> Store {
356+ let repo = temp_dir ( ) . join ( format ! ( "lcas_testing_repo_{}" , test_name ) ) ;
357+ let cache = temp_dir ( ) . join ( format ! ( "lcas_testing_cache_{}" , test_name ) ) ;
358+ let store_path = temp_dir ( ) . join ( format ! ( "lcas_testing_store_{}" , test_name ) ) ;
327359
328360 let _ = remove_dir_all ( & repo) ;
329361 let _ = remove_dir_all ( & cache) ;
@@ -343,24 +375,24 @@ mod tests {
343375
344376 #[ test]
345377 #[ cfg( all( feature = "encoding" , feature = "decoding" ) ) ]
346- fn store_to_cache_empty ( ) {
347- let store = create_test_store ( ) ;
378+ fn test_store_to_cache_empty ( ) {
379+ let store = create_test_store ( "store_to_cache_empty" ) ;
348380
349381 assert ! ( resolve_repo_path( & store, & "manifests/undefined" . to_string( ) ) . is_err( ) ) ;
350382 }
351383
352384 #[ test]
353385 #[ cfg( all( feature = "encoding" , feature = "decoding" ) ) ]
354- fn create_store_when_exists_should_fail ( ) {
355- let store = create_test_store ( ) ;
386+ fn test_create_store_when_exists_should_fail ( ) {
387+ let store = create_test_store ( "create_store_when_exists_should_fail" ) ;
356388 // Try to create the store again, should error
357389 let result = create_store ( & store) ;
358390 assert ! ( result. is_err( ) ) ;
359391 }
360392
361393 #[ test]
362394 #[ cfg( feature = "encoding" ) ]
363- fn create_repo_creates_directories ( ) {
395+ fn test_create_repo_creates_directories ( ) {
364396 let repo = temp_dir ( ) . join ( "lcas_testing_repo_dirs" ) ;
365397 let _ = remove_dir_all ( & repo) ;
366398
@@ -370,10 +402,48 @@ mod tests {
370402 assert ! ( repo. join( "manifests" ) . exists( ) ) ;
371403 }
372404
405+ #[ test]
406+ #[ cfg( feature = "encoding" ) ]
407+ fn test_create_repo_on_file ( ) {
408+ let repo = temp_dir ( ) . join ( "lcas_testing_repo_file" ) ;
409+ let _ = remove_dir_all ( & repo) ;
410+
411+ fs:: write ( & repo, "Testing data." ) . unwrap ( ) ;
412+
413+ let err = create_repo ( & repo) . unwrap_err ( ) ;
414+ assert ! ( err. to_string( ) . contains( "Already exists!" ) ) ;
415+ }
416+
417+ #[ test]
418+ #[ cfg( feature = "encoding" ) ]
419+ fn test_create_repo_on_dir ( ) {
420+ let repo = temp_dir ( ) . join ( "lcas_testing_repo_dir" ) ;
421+ let _ = remove_dir_all ( & repo) ;
422+
423+ fs:: create_dir ( & repo) . unwrap ( ) ;
424+
425+ let err = create_repo ( & repo) . unwrap_err ( ) ;
426+ assert ! ( err. to_string( ) . contains( "Already exists!" ) ) ;
427+ }
428+
373429 #[ test]
374430 #[ cfg( feature = "decoding" ) ]
375- fn get_temp_file_returns_unique_names ( ) {
376- let dir = temp_dir ( ) . join ( "lcas_temp_file_test" ) ;
431+ fn test_create_temp_file_overflow_test ( ) {
432+ let dir = temp_dir ( ) . join ( "lcas_temp_file_overflow_test" ) ;
433+ let _ = std:: fs:: remove_dir_all ( & dir) ;
434+ std:: fs:: create_dir_all ( & dir) . unwrap ( ) ;
435+
436+ for _i in 0 ..1000 {
437+ let file_name = super :: get_temp_file ( None , & dir) ;
438+ File :: create_new ( & file_name) . unwrap ( ) ;
439+ assert ! ( file_name. exists( ) ) ;
440+ }
441+ }
442+
443+ #[ test]
444+ #[ cfg( feature = "decoding" ) ]
445+ fn test_get_temp_file_returns_unique_names ( ) {
446+ let dir = temp_dir ( ) . join ( "lcas_temp_file_unique_test" ) ;
377447 let _ = std:: fs:: create_dir_all ( & dir) ;
378448 let file1 = super :: get_temp_file ( None , & dir) ;
379449 std:: fs:: File :: create ( dir. join ( & file1) ) . unwrap ( ) ;
@@ -384,7 +454,7 @@ mod tests {
384454
385455 #[ test]
386456 #[ cfg( feature = "decoding" ) ]
387- fn make_chunk_executable_sets_permissions ( ) {
457+ fn test_make_chunk_executable_sets_permissions ( ) {
388458 let dir = temp_dir ( ) . join ( "lcas_executable_test" ) ;
389459 let _ = fs:: create_dir_all ( dir. join ( "chunks" ) ) ;
390460 let chunk_hash = "testchunk" . to_string ( ) ;
@@ -398,4 +468,40 @@ mod tests {
398468
399469 let _ = remove_dir_all ( & dir) ;
400470 }
471+
472+ #[ test]
473+ #[ cfg( all( feature = "encoding" , feature = "decoding" ) ) ]
474+ fn test_create_and_load_artifact ( ) {
475+ use std:: path:: PathBuf ;
476+
477+ use crate :: { build, install_artifact} ;
478+
479+ let store = create_test_store ( "artifact" ) ;
480+ let input_dir = temp_dir ( ) . join ( "lcas_artifact_test" ) ;
481+
482+ let _ = fs:: remove_dir_all ( & input_dir) ;
483+ fs:: create_dir_all ( & input_dir) . unwrap ( ) ;
484+ fs:: write ( input_dir. join ( "file1.txt" ) , b"Hello, world!" ) . unwrap ( ) ;
485+ fs:: write ( input_dir. join ( "file2.txt" ) , b"Another file." ) . unwrap ( ) ;
486+
487+ // Create a nested directory and files inside it
488+ let nested_dir = input_dir. join ( "nested" ) ;
489+ fs:: create_dir ( & nested_dir) . unwrap ( ) ;
490+ fs:: write ( nested_dir. join ( "nested1.txt" ) , b"Nested file 1." ) . unwrap ( ) ;
491+ fs:: write ( nested_dir. join ( "nested2.txt" ) , b"Nested file 2." ) . unwrap ( ) ;
492+
493+ // Create a deeper nested directory
494+ let deeper_nested_dir = nested_dir. join ( "deeper" ) ;
495+ fs:: create_dir ( & deeper_nested_dir) . unwrap ( ) ;
496+ fs:: write ( deeper_nested_dir. join ( "deepfile.txt" ) , b"Deep nested file." ) . unwrap ( ) ;
497+
498+ build (
499+ & input_dir,
500+ & PathBuf :: from ( & store. repo_path ) ,
501+ & "test_artifact" . to_string ( ) ,
502+ )
503+ . unwrap ( ) ;
504+
505+ install_artifact ( & "test_artifact" . to_string ( ) , & store) . unwrap ( ) ;
506+ }
401507}
0 commit comments