package scanner import ( "os" "path/filepath" "sort" "testing" "git.membo.co.uk/dtomlinson/gitlocal/internal/git" "git.membo.co.uk/dtomlinson/gitlocal/internal/testutil" ) func TestFindNestedGitRepos(t *testing.T) { tests := []struct { name string setup func(t *testing.T) (rootDir string, expectedPaths []string) expectedCount int }{ { name: "no nested repos", setup: func(t *testing.T) (string, []string) { dir := testutil.CreateTempGitRepo(t) return dir, []string{} }, expectedCount: 0, }, { name: "nested repos structure", setup: func(t *testing.T) (string, []string) { rootDir, nestedDirs := testutil.CreateNestedRepoStructure(t) return rootDir, nestedDirs }, expectedCount: 3, }, { name: "non-git directory", setup: func(t *testing.T) (string, []string) { dir := t.TempDir() // Create subdirectories without .git os.MkdirAll(filepath.Join(dir, "subdir1"), 0755) os.MkdirAll(filepath.Join(dir, "subdir2", "deep"), 0755) return dir, []string{} }, expectedCount: 0, }, { name: "mixed nested repos and regular dirs", setup: func(t *testing.T) (string, []string) { dir := testutil.CreateTempGitRepo(t) // Create one nested git repo nested1 := filepath.Join(dir, "nested1") os.MkdirAll(nested1, 0755) testutil.CreateTempGitRepo(t) // This creates in temp dir, we need to manually init // Let's use a helper to init git in nested1 initGitInDir(t, nested1) // Create regular directories os.MkdirAll(filepath.Join(dir, "regular"), 0755) os.MkdirAll(filepath.Join(dir, "another", "deep"), 0755) return dir, []string{nested1} }, expectedCount: 1, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { rootDir, expectedPaths := tt.setup(t) repos, err := FindNestedGitRepos(rootDir) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(repos) != tt.expectedCount { t.Errorf("expected %d repos, got %d: %v", tt.expectedCount, len(repos), repos) } if tt.expectedCount > 0 { // Sort both slices for comparison sort.Strings(repos) sort.Strings(expectedPaths) for i, expected := range expectedPaths { if i >= len(repos) { t.Errorf("missing expected repo: %s", expected) continue } if repos[i] != expected { t.Errorf("expected repo %s, got %s", expected, repos[i]) } } } }) } } func TestFindNestedGitReposSkipsRootGit(t *testing.T) { rootDir := testutil.CreateTempGitRepo(t) // Create nested repos nested1 := filepath.Join(rootDir, "nested1") os.MkdirAll(nested1, 0755) initGitInDir(t, nested1) repos, err := FindNestedGitRepos(rootDir) if err != nil { t.Fatalf("unexpected error: %v", err) } // Should find nested1 but not the root if len(repos) != 1 { t.Fatalf("expected 1 nested repo, got %d", len(repos)) } if repos[0] != nested1 { t.Errorf("expected %s, got %s", nested1, repos[0]) } } func TestFindNestedGitReposWithGitLocal(t *testing.T) { rootDir := testutil.CreateTempGitRepo(t) // Create nested repo with .gitlocal nested1 := filepath.Join(rootDir, "nested1") os.MkdirAll(nested1, 0755) initGitInDir(t, nested1) // Convert to .gitlocal if err := git.Convert(nested1); err != nil { t.Fatalf("failed to convert: %v", err) } // Create nested repo with .git nested2 := filepath.Join(rootDir, "nested2") os.MkdirAll(nested2, 0755) initGitInDir(t, nested2) repos, err := FindNestedGitRepos(rootDir) if err != nil { t.Fatalf("unexpected error: %v", err) } // Should find both if len(repos) != 2 { t.Fatalf("expected 2 nested repos, got %d", len(repos)) } sort.Strings(repos) expectedPaths := []string{nested1, nested2} sort.Strings(expectedPaths) for i, expected := range expectedPaths { if repos[i] != expected { t.Errorf("expected repo %s, got %s", expected, repos[i]) } } } func TestFindNestedGitReposWithRootGitLocal(t *testing.T) { rootDir := testutil.CreateTempGitRepo(t) // Convert root to .gitlocal if err := git.Convert(rootDir); err != nil { t.Fatalf("failed to convert root: %v", err) } // Create nested repo nested1 := filepath.Join(rootDir, "nested1") os.MkdirAll(nested1, 0755) initGitInDir(t, nested1) repos, err := FindNestedGitRepos(rootDir) if err != nil { t.Fatalf("unexpected error: %v", err) } // Should find nested1 but not the root .gitlocal if len(repos) != 1 { t.Fatalf("expected 1 nested repo, got %d", len(repos)) } if repos[0] != nested1 { t.Errorf("expected %s, got %s", nested1, repos[0]) } } func TestFindNestedGitReposDeepNesting(t *testing.T) { rootDir := testutil.CreateTempGitRepo(t) // Create deeply nested structure deep := filepath.Join(rootDir, "a", "b", "c", "d", "nested") os.MkdirAll(deep, 0755) initGitInDir(t, deep) repos, err := FindNestedGitRepos(rootDir) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(repos) != 1 { t.Fatalf("expected 1 nested repo, got %d", len(repos)) } if repos[0] != deep { t.Errorf("expected %s, got %s", deep, repos[0]) } } // Helper function to initialize git in a directory func initGitInDir(t *testing.T, dir string) { t.Helper() // We can't use testutil.CreateTempGitRepo since it creates a new temp dir // So we manually init git here gitDir := filepath.Join(dir, git.GitDir) if err := os.MkdirAll(gitDir, 0755); err != nil { t.Fatalf("failed to create .git directory: %v", err) } // Create minimal git structure // For the scanner, we just need the .git directory to exist // The scanner doesn't validate the git repo structure }