diff options
| author | 2025-11-16 00:00:00 +0000 | |
|---|---|---|
| committer | 2025-11-16 00:00:00 +0000 | |
| commit | bad0f9715556a470d0de2a22c7040181e3a033ba (patch) | |
| tree | 21463072ce5bc85682a887ce0cae26d833941af3 /hybrid_test.go | |
| parent | EntryRecursive should return ErrNotFound instead of nil, nil (diff) | |
| signature | ||
Use actual git for tests and enhance Head
Diffstat (limited to 'hybrid_test.go')
| -rw-r--r-- | hybrid_test.go | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/hybrid_test.go b/hybrid_test.go new file mode 100644 index 00000000..083605d8 --- /dev/null +++ b/hybrid_test.go @@ -0,0 +1,271 @@ +package furgit + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "testing" +) + +func TestTreeNestedDeep(t *testing.T) { + repoPath, cleanup := setupTestRepo(t) + defer cleanup() + + workDir, cleanupWork := setupWorkDir(t) + defer cleanupWork() + + depth := 50 + currentDir := workDir + for i := 0; i < depth; i++ { + currentDir = filepath.Join(currentDir, fmt.Sprintf("level%d", i)) + err := os.MkdirAll(currentDir, 0o755) + if err != nil { + t.Fatalf("failed to create directory %s: %v", currentDir, err) + } + } + err := os.WriteFile(filepath.Join(currentDir, "deep.txt"), []byte("deep content"), 0o644) + if err != nil { + t.Fatalf("failed to create deep.txt: %v", err) + } + + gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".") + treeHash := gitCmd(t, repoPath, "--work-tree="+workDir, "write-tree") + + repo, err := OpenRepository(repoPath) + if err != nil { + t.Fatalf("OpenRepository failed: %v", err) + } + defer func() { + _ = repo.Close() + }() + + hash, _ := repo.ParseHash(treeHash) + obj, _ := repo.ReadObject(hash) + tree := obj.(*StoredTree) + + path := make([][]byte, depth+1) + for i := 0; i < depth; i++ { + path[i] = []byte(fmt.Sprintf("level%d", i)) + } + path[depth] = []byte("deep.txt") + + entry, err := tree.EntryRecursive(repo, path) + if err != nil { + t.Fatalf("EntryRecursive failed for deep path: %v", err) + } + + blobObj, _ := repo.ReadObject(entry.ID) + blob := blobObj.(*StoredBlob) + + if !bytes.Equal(blob.Data, []byte("deep content")) { + t.Errorf("deep file content: got %q, want %q", blob.Data, "deep content") + } +} + +func TestTreeMixedModes(t *testing.T) { + repoPath, cleanup := setupTestRepo(t) + defer cleanup() + + workDir, cleanupWork := setupWorkDir(t) + defer cleanupWork() + + err := os.WriteFile(filepath.Join(workDir, "normal.txt"), []byte("normal"), 0o644) + if err != nil { + t.Fatalf("failed to create normal.txt: %v", err) + } + err = os.WriteFile(filepath.Join(workDir, "executable.sh"), []byte("#!/bin/sh\necho test"), 0o755) + if err != nil { + t.Fatalf("failed to create executable.sh: %v", err) + } + err = os.Symlink("normal.txt", filepath.Join(workDir, "link.txt")) + if err != nil { + t.Fatalf("failed to create symlink: %v", err) + } + + gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".") + treeHash := gitCmd(t, repoPath, "--work-tree="+workDir, "write-tree") + + repo, err := OpenRepository(repoPath) + if err != nil { + t.Fatalf("OpenRepository failed: %v", err) + } + defer func() { + _ = repo.Close() + }() + + hash, _ := repo.ParseHash(treeHash) + obj, _ := repo.ReadObject(hash) + tree := obj.(*StoredTree) + + modes := make(map[string]FileMode) + for _, entry := range tree.Entries { + modes[string(entry.Name)] = entry.Mode + } + + if modes["normal.txt"] != 0o100644 { + t.Errorf("normal.txt mode: got %o, want %o", modes["normal.txt"], 0o100644) + } + if modes["executable.sh"] != 0o100755 { + t.Errorf("executable.sh mode: got %o, want %o", modes["executable.sh"], 0o100755) + } + if modes["link.txt"] != 0o120000 { + t.Errorf("link.txt mode: got %o, want %o", modes["link.txt"], 0o120000) + } +} + +func TestCommitChain(t *testing.T) { + repoPath, cleanup := setupTestRepo(t) + defer cleanup() + + workDir, cleanupWork := setupWorkDir(t) + defer cleanupWork() + + numCommits := 100 + var commits []string + + for i := 0; i < numCommits; i++ { + filename := filepath.Join(workDir, fmt.Sprintf("file%d.txt", i)) + err := os.WriteFile(filename, []byte(fmt.Sprintf("content %d", i)), 0o644) + if err != nil { + t.Fatalf("failed to create %s: %v", filename, err) + } + gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".") + gitCmd(t, repoPath, "--work-tree="+workDir, "commit", "-m", fmt.Sprintf("Commit %d", i)) + commitHash := gitCmd(t, repoPath, "rev-parse", "HEAD") + commits = append(commits, commitHash) + } + + repo, err := OpenRepository(repoPath) + if err != nil { + t.Fatalf("OpenRepository failed: %v", err) + } + defer func() { + _ = repo.Close() + }() + + hash, _ := repo.ParseHash(commits[len(commits)-1]) + for i := numCommits - 1; i >= 0; i-- { + obj, err := repo.ReadObject(hash) + if err != nil { + t.Fatalf("failed to read commit %d: %v", i, err) + } + + commit, ok := obj.(*StoredCommit) + if !ok { + t.Fatalf("expected *StoredCommit at %d, got %T", i, obj) + } + + expectedMsg := fmt.Sprintf("Commit %d\n", i) + if !bytes.Equal(commit.Message, []byte(expectedMsg)) { + t.Errorf("commit %d message: got %q, want %q", i, commit.Message, expectedMsg) + } + + if i > 0 { + if len(commit.Parents) != 1 { + t.Fatalf("commit %d should have 1 parent, got %d", i, len(commit.Parents)) + } + hash = commit.Parents[0] + } else { + if len(commit.Parents) != 0 { + t.Errorf("first commit should have 0 parents, got %d", len(commit.Parents)) + } + } + } +} + +func TestMultipleTags(t *testing.T) { + repoPath, cleanup := setupTestRepo(t) + defer cleanup() + + workDir, cleanupWork := setupWorkDir(t) + defer cleanupWork() + + err := os.WriteFile(filepath.Join(workDir, "file.txt"), []byte("content"), 0o644) + if err != nil { + t.Fatalf("failed to create file.txt: %v", err) + } + gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".") + gitCmd(t, repoPath, "--work-tree="+workDir, "commit", "-m", "Tagged commit") + commitHash := gitCmd(t, repoPath, "rev-parse", "HEAD") + + tags := []string{"v1.0.0", "v1.0.1", "v1.1.0", "v2.0.0"} + for _, tagName := range tags { + gitCmd(t, repoPath, "tag", "-a", "-m", fmt.Sprintf("Release %s", tagName), tagName, commitHash) + } + + repo, err := OpenRepository(repoPath) + if err != nil { + t.Fatalf("OpenRepository failed: %v", err) + } + defer func() { + _ = repo.Close() + }() + + for _, tagName := range tags { + tagHash := gitCmd(t, repoPath, "rev-parse", tagName) + hash, _ := repo.ParseHash(tagHash) + obj, err := repo.ReadObject(hash) + if err != nil { + t.Errorf("failed to read tag %s: %v", tagName, err) + continue + } + + tag, ok := obj.(*StoredTag) + if !ok { + t.Errorf("tag %s: expected *StoredTag, got %T", tagName, obj) + continue + } + + if !bytes.Equal(tag.Name, []byte(tagName)) { + t.Errorf("tag name: got %q, want %q", tag.Name, tagName) + } + } +} + +func TestPackfileAfterMultipleRepacks(t *testing.T) { + if testing.Short() { + t.Skip("skipping multiple repack test in short mode") + } + + repoPath, cleanup := setupTestRepo(t) + defer cleanup() + + gitCmd(t, repoPath, "config", "gc.auto", "0") + + workDir, cleanupWork := setupWorkDir(t) + defer cleanupWork() + + for i := 0; i < 5; i++ { + err := os.WriteFile(filepath.Join(workDir, fmt.Sprintf("file%d.txt", i)), []byte(fmt.Sprintf("content %d", i)), 0o644) + if err != nil { + t.Fatalf("failed to create file%d.txt: %v", i, err) + } + gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".") + gitCmd(t, repoPath, "--work-tree="+workDir, "commit", "-m", fmt.Sprintf("Commit %d", i)) + gitCmd(t, repoPath, "repack", "-d") + } + + gitCmd(t, repoPath, "repack", "-a", "-d") + + repo, err := OpenRepository(repoPath) + if err != nil { + t.Fatalf("OpenRepository failed: %v", err) + } + defer func() { + _ = repo.Close() + }() + + headHash := gitCmd(t, repoPath, "rev-parse", "HEAD") + hash, _ := repo.ParseHash(headHash) + + obj, err := repo.ReadObject(hash) + if err != nil { + t.Fatalf("failed to read HEAD from final packfile: %v", err) + } + + commit := obj.(*StoredCommit) + if !bytes.Contains(commit.Message, []byte("Commit 4")) { + t.Errorf("HEAD commit message incorrect: got %q", commit.Message) + } +} |
