Files
PR-Helper/services/git_test.go
T
2026-06-24 18:28:14 +08:00

422 lines
9.7 KiB
Go

package services
import (
"os"
"path/filepath"
"testing"
"time"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/object"
)
// initTestRepo creates a temp git repo with two commits on main branch.
// Returns the repo path and the two commit hashes (first, second).
func initTestRepo(t *testing.T) (string, string, string) {
t.Helper()
dir := t.TempDir()
repo, err := git.PlainInit(dir, false)
if err != nil {
t.Fatalf("PlainInit: %v", err)
}
w, err := repo.Worktree()
if err != nil {
t.Fatalf("Worktree: %v", err)
}
// Commit 1: create file
filePath := filepath.Join(dir, "hello.txt")
if err := os.WriteFile(filePath, []byte("hello world\n"), 0o644); err != nil {
t.Fatalf("WriteFile: %v", err)
}
w.Add("hello.txt")
hash1, err := w.Commit("initial commit", &git.CommitOptions{
Author: &object.Signature{
Name: "Test",
Email: "test@example.com",
When: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
},
})
if err != nil {
t.Fatalf("Commit 1: %v", err)
}
// Commit 2: modify file
if err := os.WriteFile(filePath, []byte("hello world\nmodified\n"), 0o644); err != nil {
t.Fatalf("WriteFile: %v", err)
}
w.Add("hello.txt")
hash2, err := w.Commit("second commit", &git.CommitOptions{
Author: &object.Signature{
Name: "Test",
Email: "test@example.com",
When: time.Date(2024, 1, 2, 0, 0, 0, 0, time.UTC),
},
})
if err != nil {
t.Fatalf("Commit 2: %v", err)
}
return dir, hash1.String(), hash2.String()
}
func TestOpenRepo(t *testing.T) {
dir, _, _ := initTestRepo(t)
repo, err := OpenRepo(dir)
if err != nil {
t.Fatalf("OpenRepo() error: %v", err)
}
if repo == nil {
t.Fatal("OpenRepo() returned nil repo")
}
}
func TestOpenRepo_InvalidPath(t *testing.T) {
_, err := OpenRepo("/nonexistent/path/12345")
if err == nil {
t.Error("OpenRepo() expected error for invalid path")
}
}
func TestGetRefs(t *testing.T) {
dir, _, _ := initTestRepo(t)
repo, err := OpenRepo(dir)
if err != nil {
t.Fatalf("OpenRepo: %v", err)
}
refs, err := GetRefs(repo)
if err != nil {
t.Fatalf("GetRefs() error: %v", err)
}
// Should have at least one branch (main or master depending on git config)
found := false
for _, r := range refs {
if (r.Name == "main" || r.Name == "master") && !r.IsTag {
found = true
if r.Hash == "" {
t.Error("branch ref has empty hash")
}
if !r.IsHead {
t.Error("default branch should be HEAD")
}
}
}
if !found {
t.Errorf("expected default branch in refs, got %v", refs)
}
}
func TestGetGraph(t *testing.T) {
dir, hash1, hash2 := initTestRepo(t)
repo, err := OpenRepo(dir)
if err != nil {
t.Fatalf("OpenRepo: %v", err)
}
graph, err := GetGraph(repo, 100)
if err != nil {
t.Fatalf("GetGraph() error: %v", err)
}
if len(graph.Commits) != 2 {
t.Fatalf("commits count = %d, want 2", len(graph.Commits))
}
// The first commit in the graph should be the latest (hash2)
if graph.Commits[0].Hash != hash2 {
t.Errorf("commits[0].Hash = %q, want %q", graph.Commits[0].Hash, hash2)
}
if graph.Commits[0].Message != "second commit" {
t.Errorf("commits[0].Message = %q, want %q", graph.Commits[0].Message, "second commit")
}
// Second should be hash1
if graph.Commits[1].Hash != hash1 {
t.Errorf("commits[1].Hash = %q, want %q", graph.Commits[1].Hash, hash1)
}
// Should have one edge (hash2 -> hash1)
if len(graph.Edges) != 1 {
t.Fatalf("edges count = %d, want 1", len(graph.Edges))
}
if graph.Edges[0].Source != hash2 || graph.Edges[0].Target != hash1 {
t.Errorf("edge = {%s -> %s}, want {%s -> %s}",
graph.Edges[0].Source, graph.Edges[0].Target, hash2, hash1)
}
// Should have at least one ref
if len(graph.Refs) < 1 {
t.Error("expected at least 1 ref in graph")
}
}
func TestGetGraph_MaxCommits(t *testing.T) {
dir, _, _ := initTestRepo(t)
repo, err := OpenRepo(dir)
if err != nil {
t.Fatalf("OpenRepo: %v", err)
}
graph, err := GetGraph(repo, 1) // limit to 1 commit
if err != nil {
t.Fatalf("GetGraph() error: %v", err)
}
if len(graph.Commits) != 1 {
t.Errorf("commits count = %d, want 1", len(graph.Commits))
}
}
func TestGetDiff(t *testing.T) {
dir, hash1, hash2 := initTestRepo(t)
repo, err := OpenRepo(dir)
if err != nil {
t.Fatalf("OpenRepo: %v", err)
}
diff, err := GetDiff(repo, hash1, hash2)
if err != nil {
t.Fatalf("GetDiff() error: %v", err)
}
if diff == "" {
t.Error("expected non-empty diff")
}
// The diff should contain the added line
if !contains(diff, "modified") {
t.Errorf("diff should contain 'modified', got: %s", diff)
}
}
func TestGetDiffFiles(t *testing.T) {
dir, hash1, hash2 := initTestRepo(t)
repo, err := OpenRepo(dir)
if err != nil {
t.Fatalf("OpenRepo: %v", err)
}
files, err := GetDiffFiles(repo, hash1, hash2)
if err != nil {
t.Fatalf("GetDiffFiles() error: %v", err)
}
if len(files) != 1 {
t.Fatalf("files count = %d, want 1", len(files))
}
if files[0].Filename != "hello.txt" {
t.Errorf("filename = %q, want %q", files[0].Filename, "hello.txt")
}
if files[0].Patch == "" {
t.Error("expected non-empty patch")
}
}
func TestGetCommitLog(t *testing.T) {
dir, hash1, hash2 := initTestRepo(t)
repo, err := OpenRepo(dir)
if err != nil {
t.Fatalf("OpenRepo: %v", err)
}
commits, err := GetCommitLog(repo, "HEAD", 100)
if err != nil {
t.Fatalf("GetCommitLog() error: %v", err)
}
if len(commits) != 2 {
t.Fatalf("commits count = %d, want 2", len(commits))
}
// First commit should be the latest
if commits[0].Hash != hash2 {
t.Errorf("commits[0].Hash = %q, want %q", commits[0].Hash, hash2)
}
if commits[0].Author != "Test" {
t.Errorf("commits[0].Author = %q, want %q", commits[0].Author, "Test")
}
if commits[0].Email != "test@example.com" {
t.Errorf("commits[0].Email = %q, want %q", commits[0].Email, "test@example.com")
}
if commits[1].Hash != hash1 {
t.Errorf("commits[1].Hash = %q, want %q", commits[1].Hash, hash1)
}
}
func TestGetCommitLog_Limit(t *testing.T) {
dir, _, _ := initTestRepo(t)
repo, err := OpenRepo(dir)
if err != nil {
t.Fatalf("OpenRepo: %v", err)
}
commits, err := GetCommitLog(repo, "HEAD", 1)
if err != nil {
t.Fatalf("GetCommitLog() error: %v", err)
}
if len(commits) != 1 {
t.Errorf("commits count = %d, want 1", len(commits))
}
}
func TestGetDefaultBranch(t *testing.T) {
dir, _, _ := initTestRepo(t)
branch, err := GetDefaultBranch(dir)
if err != nil {
t.Fatalf("GetDefaultBranch() error: %v", err)
}
if branch != "main" && branch != "master" {
t.Errorf("GetDefaultBranch() = %q, want %q or %q", branch, "main", "master")
}
}
func TestGetDefaultBranch_InvalidPath(t *testing.T) {
_, err := GetDefaultBranch("/nonexistent/path/12345")
if err == nil {
t.Error("expected error for invalid path")
}
}
func TestGetRepoBranches(t *testing.T) {
dir, _, _ := initTestRepo(t)
branches, err := GetRepoBranches(dir)
if err != nil {
t.Fatalf("GetRepoBranches() error: %v", err)
}
found := false
for _, b := range branches {
if b == "main" || b == "master" {
found = true
}
}
if !found {
t.Errorf("expected default branch in branches, got %v", branches)
}
}
func TestGetRepoTags(t *testing.T) {
dir, _, _ := initTestRepo(t)
tags, err := GetRepoTags(dir)
if err != nil {
t.Fatalf("GetRepoTags() error: %v", err)
}
// No tags were created, should be empty
if len(tags) != 0 {
t.Errorf("expected 0 tags, got %v", tags)
}
}
func TestDirSize(t *testing.T) {
tmpDir := t.TempDir()
// Create files with known sizes
os.WriteFile(filepath.Join(tmpDir, "a.txt"), make([]byte, 100), 0o644)
os.WriteFile(filepath.Join(tmpDir, "b.txt"), make([]byte, 200), 0o644)
size := dirSize(tmpDir)
if size < 300 {
t.Errorf("dirSize() = %d, want >= 300", size)
}
}
func TestDirSize_Empty(t *testing.T) {
tmpDir := t.TempDir()
size := dirSize(tmpDir)
if size != 0 {
t.Errorf("dirSize() = %d, want 0", size)
}
}
func TestDirSize_Nonexistent(t *testing.T) {
size := dirSize("/nonexistent/path/12345")
if size != 0 {
t.Errorf("dirSize() = %d, want 0", size)
}
}
func TestDirSize_NestedFiles(t *testing.T) {
tmpDir := t.TempDir()
subDir := filepath.Join(tmpDir, "sub")
os.MkdirAll(subDir, 0o755)
os.WriteFile(filepath.Join(tmpDir, "root.txt"), make([]byte, 50), 0o644)
os.WriteFile(filepath.Join(subDir, "nested.txt"), make([]byte, 75), 0o644)
size := dirSize(tmpDir)
if size < 125 {
t.Errorf("dirSize() = %d, want >= 125", size)
}
}
func TestCompareCommits(t *testing.T) {
dir, hash1, hash2 := initTestRepo(t)
diff, err := CompareCommits(dir, hash1, hash2)
if err != nil {
t.Fatalf("CompareCommits() error: %v", err)
}
if diff == "" {
t.Error("expected non-empty diff")
}
}
func TestCompareCommitsFiles(t *testing.T) {
dir, hash1, hash2 := initTestRepo(t)
files, err := CompareCommitsFiles(dir, hash1, hash2)
if err != nil {
t.Fatalf("CompareCommitsFiles() error: %v", err)
}
if len(files) != 1 {
t.Fatalf("files count = %d, want 1", len(files))
}
if files[0].Filename != "hello.txt" {
t.Errorf("filename = %q, want %q", files[0].Filename, "hello.txt")
}
}
func TestGetBranchCommits(t *testing.T) {
dir, _, _ := initTestRepo(t)
// Use the actual default branch name (main or master depending on git config)
branch, err := GetDefaultBranch(dir)
if err != nil {
t.Fatalf("GetDefaultBranch: %v", err)
}
commits, err := GetBranchCommits(dir, branch, 100)
if err != nil {
t.Fatalf("GetBranchCommits() error: %v", err)
}
if len(commits) != 2 {
t.Errorf("commits count = %d, want 2", len(commits))
}
}
// contains is a simple helper to check substring presence.
func contains(s, substr string) bool {
return len(s) >= len(substr) && searchString(s, substr)
}
func searchString(s, substr string) bool {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
return false
}