574bc91c2e459aa8afd9f4c77698b3417ce47883 / internal/git/browse_test.go · 5603 bytes · raw
package git
import (
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
func runGit(t *testing.T, dir string, args ...string) {
t.Helper()
cmd := exec.Command("git", args...)
cmd.Dir = dir
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("git %v in %s: %v: %s", args, dir, err, out)
}
}
// seedBare gives a bare repo one commit on main with a few files.
func seedBare(t *testing.T, barePath string) {
t.Helper()
work := filepath.Join(t.TempDir(), "work")
runGit(t, "", "clone", "-q", barePath, work)
files := map[string]string{
"README.md": "# demo\n\n**hi** there\n",
"LICENSE": "MIT License\n\nPermission is hereby granted, free of charge...\n",
"hello.c": "int main(void) { return 0; }\n",
"sub/note.txt": "note\n",
}
for rel, content := range files {
full := filepath.Join(work, rel)
if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil {
t.Fatalf("mkdir: %v", err)
}
if err := os.WriteFile(full, []byte(content), 0o644); err != nil {
t.Fatalf("write %s: %v", rel, err)
}
}
runGit(t, work, "add", ".")
runGit(t, work, "-c", "user.email=t@t", "-c", "user.name=t", "commit", "-qm", "seed")
runGit(t, work, "push", "-q", "origin", "main")
}
func TestRefExists(t *testing.T) {
path := filepath.Join(t.TempDir(), "demo.git")
if err := InitBare(path, "main"); err != nil {
t.Fatalf("InitBare: %v", err)
}
exists, err := RefExists(path, "main")
if err != nil {
t.Fatalf("RefExists: %v", err)
}
if exists {
t.Error("fresh bare repo resolves main, want empty")
}
seedBare(t, path)
if exists, err := RefExists(path, "main"); err != nil || !exists {
t.Fatalf("RefExists after seed = (%v, %v), want (true, nil)", exists, err)
}
if exists, err := RefExists(path, "nosuchbranch"); err != nil || exists {
t.Errorf("RefExists nosuchbranch = (%v, %v), want (false, nil)", exists, err)
}
}
func TestLsTree(t *testing.T) {
path := filepath.Join(t.TempDir(), "demo.git")
if err := InitBare(path, "main"); err != nil {
t.Fatalf("InitBare: %v", err)
}
seedBare(t, path)
entries, err := LsTree(path, "main", "")
if err != nil {
t.Fatalf("LsTree root: %v", err)
}
types := map[string]string{}
for _, e := range entries {
types[e.Path] = e.Type
}
if len(entries) != 4 {
t.Errorf("root entries = %d, want 4: %+v", len(entries), entries)
}
if types["README.md"] != "blob" || types["LICENSE"] != "blob" || types["hello.c"] != "blob" {
t.Errorf("root blobs wrong: %+v", types)
}
if types["sub"] != "tree" {
t.Errorf("sub type = %q, want tree", types["sub"])
}
sub, err := LsTree(path, "main", "sub")
if err != nil {
t.Fatalf("LsTree sub: %v", err)
}
if len(sub) != 1 || sub[0].Path != "sub/note.txt" || sub[0].Type != "blob" {
t.Errorf("sub entries = %+v, want one blob at sub/note.txt", sub)
}
}
func TestShowFile(t *testing.T) {
path := filepath.Join(t.TempDir(), "demo.git")
if err := InitBare(path, "main"); err != nil {
t.Fatalf("InitBare: %v", err)
}
seedBare(t, path)
content, err := ShowFile(path, "main", "README.md", 1<<20)
if err != nil {
t.Fatalf("ShowFile: %v", err)
}
if !strings.Contains(string(content), "**hi** there") {
t.Errorf("content = %q", content)
}
if _, err := ShowFile(path, "main", "nope.txt", 1<<20); err == nil {
t.Error("ShowFile missing file = nil error, want error")
} else if !strings.Contains(err.Error(), "not found") && !strings.Contains(err.Error(), "does not exist") {
t.Logf("missing-file error text: %v", err)
}
}
// An oversized object must never be buffered in full: ShowFile stops at
// limit+1 bytes and kills the subprocess.
func TestShowFileBounded(t *testing.T) {
path := filepath.Join(t.TempDir(), "demo.git")
if err := InitBare(path, "main"); err != nil {
t.Fatalf("InitBare: %v", err)
}
seedBare(t, path)
content, err := ShowFile(path, "main", "README.md", 4)
if err != nil {
t.Fatalf("ShowFile bounded: %v", err)
}
if len(content) != 5 {
t.Errorf("bounded content = %d bytes (%q), want limit+1 = 5", len(content), content)
}
}
// A hostile parent environment (GIT_DIR et al.) must not redirect browse
// commands to a different repository — gitCommand pins a minimal env.
func TestBrowseIgnoresInheritedGitEnv(t *testing.T) {
path := filepath.Join(t.TempDir(), "demo.git")
if err := InitBare(path, "main"); err != nil {
t.Fatalf("InitBare: %v", err)
}
seedBare(t, path)
decoy := filepath.Join(t.TempDir(), "decoy.git")
if err := InitBare(decoy, "main"); err != nil {
t.Fatalf("InitBare decoy: %v", err)
}
work := filepath.Join(t.TempDir(), "decoy-work")
runGit(t, "", "init", "-q", "-b", "main", work)
if err := os.WriteFile(filepath.Join(work, "README.md"), []byte("DECOY\n"), 0o644); err != nil {
t.Fatalf("write decoy readme: %v", err)
}
runGit(t, work, "add", ".")
runGit(t, work, "-c", "user.email=t@t", "-c", "user.name=t", "commit", "-qm", "decoy")
runGit(t, work, "push", "-q", decoy, "main")
t.Setenv("GIT_DIR", decoy)
t.Setenv("GIT_WORK_TREE", work)
content, err := ShowFile(path, "main", "README.md", 1<<20)
if err != nil {
t.Fatalf("ShowFile with hostile GIT_DIR: %v", err)
}
if strings.Contains(string(content), "DECOY") {
t.Errorf("ShowFile served the decoy repo: %q", content)
}
if !strings.Contains(string(content), "**hi** there") {
t.Errorf("ShowFile content = %q, want the seeded README", content)
}
entries, err := LsTree(path, "main", "")
if err != nil {
t.Fatalf("LsTree with hostile GIT_DIR: %v", err)
}
if len(entries) != 4 {
t.Errorf("LsTree returned %d entries, want 4 from demo (decoy has 1)", len(entries))
}
}