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)) } }