@@ -470,3 +470,165 @@ async fn test_init_command_integration_flow() {
470470 // Note: Config file may not be created if repositories don't have remote URLs
471471 // This is expected behavior in test scenarios
472472}
473+
474+ #[ tokio:: test]
475+ #[ serial]
476+ async fn test_init_command_discovers_repos_two_levels_deep ( ) {
477+ let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
478+
479+ // Create a repository 2 levels deep: ./cloned_repos/level_1/repo
480+ let repo_dir = temp_dir
481+ . path ( )
482+ . join ( "cloned_repos" )
483+ . join ( "level_1" )
484+ . join ( "test-repo" ) ;
485+ fs:: create_dir_all ( & repo_dir) . unwrap ( ) ;
486+ fs:: create_dir_all ( repo_dir. join ( ".git" ) ) . unwrap ( ) ;
487+ create_git_repo ( & repo_dir) . unwrap ( ) ;
488+
489+ // Add a remote URL so the repo will be included in the config
490+ std:: process:: Command :: new ( "git" )
491+ . args ( [
492+ "remote" ,
493+ "add" ,
494+ "origin" ,
495+ "git@github.com:test/test-repo.git" ,
496+ ] )
497+ . current_dir ( & repo_dir)
498+ . output ( )
499+ . unwrap ( ) ;
500+
501+ let output_path = temp_dir. path ( ) . join ( "two-levels-config.yaml" ) ;
502+ let command = InitCommand {
503+ output : output_path. to_string_lossy ( ) . to_string ( ) ,
504+ overwrite : false ,
505+ supplement : false ,
506+ } ;
507+
508+ let context = CommandContext {
509+ config : Config :: new ( ) ,
510+ tag : vec ! [ ] ,
511+ exclude_tag : vec ! [ ] ,
512+ repos : None ,
513+ parallel : false ,
514+ } ;
515+
516+ let original_dir = std:: env:: current_dir ( ) . unwrap ( ) ;
517+ std:: env:: set_current_dir ( temp_dir. path ( ) ) . unwrap ( ) ;
518+
519+ let result = command. execute ( & context) . await ;
520+
521+ std:: env:: set_current_dir ( original_dir) . unwrap ( ) ;
522+
523+ // Should succeed
524+ assert ! ( result. is_ok( ) ) ;
525+
526+ // Config file should exist with the discovered repo
527+ assert ! ( output_path. exists( ) ) ;
528+
529+ // Load and verify the config contains the discovered repo
530+ let config = Config :: load ( & output_path. to_string_lossy ( ) ) . unwrap ( ) ;
531+ assert_eq ! ( config. repositories. len( ) , 1 ) ;
532+ assert_eq ! ( config. repositories[ 0 ] . name, "test-repo" ) ;
533+ assert_eq ! (
534+ config. repositories[ 0 ] . url,
535+ "git@github.com:test/test-repo.git"
536+ ) ;
537+ assert_eq ! (
538+ config. repositories[ 0 ] . path. as_ref( ) . unwrap( ) ,
539+ "cloned_repos/level_1/test-repo"
540+ ) ;
541+ }
542+
543+ #[ tokio:: test]
544+ #[ serial]
545+ async fn test_init_command_depth_boundary ( ) {
546+ let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
547+
548+ // Create repos at different depths to test the boundary
549+ // Level 1: ./repo1 - should be discovered
550+ let repo1_dir = temp_dir. path ( ) . join ( "repo1" ) ;
551+ fs:: create_dir_all ( & repo1_dir) . unwrap ( ) ;
552+ create_git_repo ( & repo1_dir) . unwrap ( ) ;
553+ std:: process:: Command :: new ( "git" )
554+ . args ( [ "remote" , "add" , "origin" , "git@github.com:test/repo1.git" ] )
555+ . current_dir ( & repo1_dir)
556+ . output ( )
557+ . unwrap ( ) ;
558+
559+ // Level 2: ./dir1/repo2 - should be discovered
560+ let repo2_dir = temp_dir. path ( ) . join ( "dir1" ) . join ( "repo2" ) ;
561+ fs:: create_dir_all ( & repo2_dir) . unwrap ( ) ;
562+ create_git_repo ( & repo2_dir) . unwrap ( ) ;
563+ std:: process:: Command :: new ( "git" )
564+ . args ( [ "remote" , "add" , "origin" , "git@github.com:test/repo2.git" ] )
565+ . current_dir ( & repo2_dir)
566+ . output ( )
567+ . unwrap ( ) ;
568+
569+ // Level 3: ./dir1/dir2/repo3 - should be discovered (2 levels deep)
570+ let repo3_dir = temp_dir. path ( ) . join ( "dir1" ) . join ( "dir2" ) . join ( "repo3" ) ;
571+ fs:: create_dir_all ( & repo3_dir) . unwrap ( ) ;
572+ create_git_repo ( & repo3_dir) . unwrap ( ) ;
573+ std:: process:: Command :: new ( "git" )
574+ . args ( [ "remote" , "add" , "origin" , "git@github.com:test/repo3.git" ] )
575+ . current_dir ( & repo3_dir)
576+ . output ( )
577+ . unwrap ( ) ;
578+
579+ // Level 4: ./dir1/dir2/dir3/repo4 - should NOT be discovered (3 levels deep, too deep)
580+ let repo4_dir = temp_dir
581+ . path ( )
582+ . join ( "dir1" )
583+ . join ( "dir2" )
584+ . join ( "dir3" )
585+ . join ( "repo4" ) ;
586+ fs:: create_dir_all ( & repo4_dir) . unwrap ( ) ;
587+ create_git_repo ( & repo4_dir) . unwrap ( ) ;
588+ std:: process:: Command :: new ( "git" )
589+ . args ( [ "remote" , "add" , "origin" , "git@github.com:test/repo4.git" ] )
590+ . current_dir ( & repo4_dir)
591+ . output ( )
592+ . unwrap ( ) ;
593+
594+ let output_path = temp_dir. path ( ) . join ( "depth-boundary-config.yaml" ) ;
595+ let command = InitCommand {
596+ output : output_path. to_string_lossy ( ) . to_string ( ) ,
597+ overwrite : false ,
598+ supplement : false ,
599+ } ;
600+
601+ let context = CommandContext {
602+ config : Config :: new ( ) ,
603+ tag : vec ! [ ] ,
604+ exclude_tag : vec ! [ ] ,
605+ repos : None ,
606+ parallel : false ,
607+ } ;
608+
609+ let original_dir = std:: env:: current_dir ( ) . unwrap ( ) ;
610+ std:: env:: set_current_dir ( temp_dir. path ( ) ) . unwrap ( ) ;
611+
612+ let result = command. execute ( & context) . await ;
613+
614+ std:: env:: set_current_dir ( original_dir) . unwrap ( ) ;
615+
616+ // Should succeed
617+ assert ! ( result. is_ok( ) ) ;
618+ assert ! ( output_path. exists( ) ) ;
619+
620+ // Load and verify only repos 1, 2, and 3 are discovered (not repo4 which is too deep)
621+ let config = Config :: load ( & output_path. to_string_lossy ( ) ) . unwrap ( ) ;
622+ assert_eq ! ( config. repositories. len( ) , 3 ) ;
623+
624+ // Verify the discovered repos
625+ let repo_names: Vec < & str > = config
626+ . repositories
627+ . iter ( )
628+ . map ( |r| r. name . as_str ( ) )
629+ . collect ( ) ;
630+ assert ! ( repo_names. contains( & "repo1" ) ) ;
631+ assert ! ( repo_names. contains( & "repo2" ) ) ;
632+ assert ! ( repo_names. contains( & "repo3" ) ) ;
633+ assert ! ( !repo_names. contains( & "repo4" ) ) ; // Should not be discovered
634+ }
0 commit comments