package gitref import ( "os" "path/filepath" "testing" ) // TestDefaultBranch_RejectsRelativePath — guardRepoPath refuses relative // paths so callers can't sneak `repos/..` past the I/O boundary even if // the upstream allowlist check is bypassed. func TestDefaultBranch_RejectsRelativePath(t *testing.T) { if _, err := DefaultBranch("repos/foo"); err == nil { t.Fatal("expected error for relative path") } } // TestDefaultBranch_RejectsTraversal — explicit guard against ".." // segments in the RAW input. We must check before filepath.Clean: Clean // resolves '..' away from absolute paths (`/allowed/../etc` → `/etc`) and // would silently let a traversal attempt through. func TestDefaultBranch_RejectsTraversal(t *testing.T) { cases := []string{ "/tmp/../etc", "/foo/bar/../../etc", "/a/b/..", } for _, p := range cases { if _, err := DefaultBranch(p); err == nil { t.Errorf("expected error for path %q (contains '..')", p) } } } // TestDefaultBranch_RejectsEmpty — guardRepoPath errors on empty input // rather than letting filepath.Join silently turn it into ".git". func TestDefaultBranch_RejectsEmpty(t *testing.T) { if _, err := DefaultBranch(""); err == nil { t.Fatal("expected error for empty path") } } // TestDefaultBranch_AcceptsAbsolutePathToValidRepo — sanity check that // the guard doesn't reject legitimate absolute paths. We seed a minimal // .git directory with a HEAD on main and assert the probe finds it. func TestDefaultBranch_AcceptsAbsolutePathToValidRepo(t *testing.T) { repoPath := t.TempDir() if !filepath.IsAbs(repoPath) { t.Fatalf("t.TempDir() returned non-absolute path: %q", repoPath) } gitDir := filepath.Join(repoPath, ".git") if err := os.MkdirAll(gitDir, 0o755); err != nil { t.Fatalf("mkdir git: %v", err) } if err := os.WriteFile(filepath.Join(gitDir, "HEAD"), []byte("ref: refs/heads/main\n"), 0o644); err != nil { t.Fatalf("write HEAD: %v", err) } branch, err := DefaultBranch(repoPath) if err != nil { t.Fatalf("unexpected error: %v", err) } // Git branch names are case-sensitive — assert exact equality, not // EqualFold. A function that returns "Main" or "MAIN" should fail. if branch != "main" { t.Fatalf("expected main, got %q", branch) } } func TestDefaultBranch_PrefersMainWhenOriginHeadPointsAtMaster(t *testing.T) { repoPath := t.TempDir() gitDir := filepath.Join(repoPath, ".git") if err := os.MkdirAll(filepath.Join(gitDir, "refs", "remotes", "origin"), 0o755); err != nil { t.Fatalf("mkdir origin refs: %v", err) } if err := os.WriteFile(filepath.Join(gitDir, "HEAD"), []byte("ref: refs/heads/feature/x\n"), 0o644); err != nil { t.Fatalf("write HEAD: %v", err) } if err := os.WriteFile( filepath.Join(gitDir, "refs", "remotes", "origin", "HEAD"), []byte("ref: refs/remotes/origin/master\n"), 0o644, ); err != nil { t.Fatalf("write origin HEAD: %v", err) } for _, branch := range []string{"main", "master"} { refPath := filepath.Join(gitDir, "refs", "remotes", "origin", branch) if err := os.WriteFile(refPath, []byte("0000000\n"), 0o644); err != nil { t.Fatalf("write %s ref: %v", branch, err) } } branch, err := DefaultBranch(repoPath) if err != nil { t.Fatalf("DefaultBranch error: %v", err) } if branch != "main" { t.Fatalf("DefaultBranch = %q, want main", branch) } } func TestDefaultBranch_PreservesNonMainOriginHeadWhenMainExists(t *testing.T) { repoPath := t.TempDir() gitDir := filepath.Join(repoPath, ".git") if err := os.MkdirAll(filepath.Join(gitDir, "refs", "remotes", "origin"), 0o755); err != nil { t.Fatalf("mkdir origin refs: %v", err) } if err := os.WriteFile(filepath.Join(gitDir, "HEAD"), []byte("ref: refs/heads/feature/x\n"), 0o644); err != nil { t.Fatalf("write HEAD: %v", err) } if err := os.WriteFile( filepath.Join(gitDir, "refs", "remotes", "origin", "HEAD"), []byte("ref: refs/remotes/origin/trunk\n"), 0o644, ); err != nil { t.Fatalf("write origin HEAD: %v", err) } for _, branch := range []string{"main", "trunk"} { refPath := filepath.Join(gitDir, "refs", "remotes", "origin", branch) if err := os.WriteFile(refPath, []byte("0000000\n"), 0o644); err != nil { t.Fatalf("write %s ref: %v", branch, err) } } branch, err := DefaultBranch(repoPath) if err != nil { t.Fatalf("DefaultBranch error: %v", err) } if branch != "trunk" { t.Fatalf("DefaultBranch = %q, want trunk", branch) } } func TestDefaultBranch_LocalMainDoesNotOverrideOriginHeadMaster(t *testing.T) { repoPath := t.TempDir() gitDir := filepath.Join(repoPath, ".git") if err := os.MkdirAll(filepath.Join(gitDir, "refs", "remotes", "origin"), 0o755); err != nil { t.Fatalf("mkdir origin refs: %v", err) } if err := os.MkdirAll(filepath.Join(gitDir, "refs", "heads"), 0o755); err != nil { t.Fatalf("mkdir local refs: %v", err) } if err := os.WriteFile(filepath.Join(gitDir, "HEAD"), []byte("ref: refs/heads/feature/x\n"), 0o644); err != nil { t.Fatalf("write HEAD: %v", err) } if err := os.WriteFile( filepath.Join(gitDir, "refs", "remotes", "origin", "HEAD"), []byte("ref: refs/remotes/origin/master\n"), 0o644, ); err != nil { t.Fatalf("write origin HEAD: %v", err) } if err := os.WriteFile(filepath.Join(gitDir, "refs", "remotes", "origin", "master"), []byte("0000000\n"), 0o644); err != nil { t.Fatalf("write origin master ref: %v", err) } if err := os.WriteFile(filepath.Join(gitDir, "refs", "heads", "main"), []byte("0000000\n"), 0o644); err != nil { t.Fatalf("write local main ref: %v", err) } branch, err := DefaultBranch(repoPath) if err != nil { t.Fatalf("DefaultBranch error: %v", err) } if branch != "master" { t.Fatalf("DefaultBranch = %q, want master", branch) } } func TestDefaultBranch_RemoteMasterBeatsLocalMainWithoutOriginHead(t *testing.T) { repoPath := t.TempDir() gitDir := filepath.Join(repoPath, ".git") if err := os.MkdirAll(filepath.Join(gitDir, "refs", "remotes", "origin"), 0o755); err != nil { t.Fatalf("mkdir origin refs: %v", err) } if err := os.MkdirAll(filepath.Join(gitDir, "refs", "heads"), 0o755); err != nil { t.Fatalf("mkdir local refs: %v", err) } if err := os.WriteFile(filepath.Join(gitDir, "HEAD"), []byte("ref: refs/heads/feature/x\n"), 0o644); err != nil { t.Fatalf("write HEAD: %v", err) } if err := os.WriteFile(filepath.Join(gitDir, "refs", "remotes", "origin", "master"), []byte("0000000\n"), 0o644); err != nil { t.Fatalf("write origin master ref: %v", err) } if err := os.WriteFile(filepath.Join(gitDir, "refs", "heads", "main"), []byte("0000000\n"), 0o644); err != nil { t.Fatalf("write local main ref: %v", err) } branch, err := DefaultBranch(repoPath) if err != nil { t.Fatalf("DefaultBranch error: %v", err) } if branch != "master" { t.Fatalf("DefaultBranch = %q, want master", branch) } } func TestDefaultBranch_MainNamespaceDoesNotOverrideOriginHeadMaster(t *testing.T) { repoPath := t.TempDir() gitDir := filepath.Join(repoPath, ".git") if err := os.MkdirAll(filepath.Join(gitDir, "refs", "remotes", "origin", "main"), 0o755); err != nil { t.Fatalf("mkdir origin main namespace: %v", err) } if err := os.WriteFile(filepath.Join(gitDir, "HEAD"), []byte("ref: refs/heads/feature/x\n"), 0o644); err != nil { t.Fatalf("write HEAD: %v", err) } if err := os.WriteFile( filepath.Join(gitDir, "refs", "remotes", "origin", "HEAD"), []byte("ref: refs/remotes/origin/master\n"), 0o644, ); err != nil { t.Fatalf("write origin HEAD: %v", err) } if err := os.WriteFile(filepath.Join(gitDir, "refs", "remotes", "origin", "main", "foo"), []byte("0000000\n"), 0o644); err != nil { t.Fatalf("write origin main/foo ref: %v", err) } if err := os.WriteFile(filepath.Join(gitDir, "refs", "remotes", "origin", "master"), []byte("0000000\n"), 0o644); err != nil { t.Fatalf("write origin master ref: %v", err) } branch, err := DefaultBranch(repoPath) if err != nil { t.Fatalf("DefaultBranch error: %v", err) } if branch != "master" { t.Fatalf("DefaultBranch = %q, want master", branch) } }