1
0
Fork 0
mirror of https://github.com/imjasonh/infinite-git synced 2026-07-10 01:48:35 +00:00
infinite-git/main_test.go
Jason Hall 8919cd5826
Implement pure Go Git HTTP server without CLI dependencies
Major rewrite to eliminate git CLI dependency:
- Created object package for Git object format handling (blob, tree, commit)
- Implemented packfile generation from scratch
- Rewrote repo initialization to create Git repos without CLI
- Implemented git-upload-pack protocol in pure Go
- Updated all server handlers to use new implementation
- Added Git packet tracing to test.sh for debugging
- Fixed tests to work with new implementation

The server now works entirely with Go code, no git commands needed.
Clone operations work correctly, but pull operations still have protocol
negotiation issues that need further debugging.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-23 22:28:50 -04:00

276 lines
6.5 KiB
Go

package main
import (
"fmt"
"net/http/httptest"
"os"
"path/filepath"
"sync"
"testing"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/imjasonh/infinite-git/internal/repo"
"github.com/imjasonh/infinite-git/internal/server"
)
func TestCloneAndPull(t *testing.T) {
// Create temporary directories
serverRepoDir := t.TempDir()
clientRepoDir := t.TempDir()
// Initialize server repository
serverRepo, err := repo.New(serverRepoDir)
if err != nil {
t.Fatalf("failed to create server repo: %v", err)
}
srv := server.New(serverRepo)
// Start test server
ts := httptest.NewServer(srv.Handler())
defer ts.Close()
// Clone the repository
gitRepo, err := git.PlainClone(clientRepoDir, false, &git.CloneOptions{
URL: ts.URL,
})
if err != nil {
t.Fatalf("failed to clone: %v", err)
}
// Get initial commit
ref, err := gitRepo.Head()
if err != nil {
t.Fatalf("failed to get HEAD: %v", err)
}
initialCommit := ref.Hash()
t.Logf("Initial commit: %s", initialCommit)
// Count initial commits
initialCount := countCommits(t, gitRepo)
t.Logf("Initial commit count: %d", initialCount)
// Perform multiple pulls
commits := []plumbing.Hash{initialCommit}
for i := 0; i < 3; i++ {
// Pull from origin
w, err := gitRepo.Worktree()
if err != nil {
t.Fatalf("failed to get worktree: %v", err)
}
err = w.Pull(&git.PullOptions{
RemoteName: "origin",
})
if err != nil && err != git.NoErrAlreadyUpToDate {
t.Fatalf("pull %d failed: %v", i+1, err)
}
// Get new HEAD
ref, err := gitRepo.Head()
if err != nil {
t.Fatalf("failed to get HEAD after pull %d: %v", i+1, err)
}
newCommit := ref.Hash()
// Verify we got a new commit
if newCommit == commits[len(commits)-1] {
t.Errorf("pull %d did not generate a new commit", i+1)
}
commits = append(commits, newCommit)
t.Logf("Pull %d got commit: %s", i+1, newCommit)
// Verify commit count increased
newCount := countCommits(t, gitRepo)
if newCount != initialCount+i+1 {
t.Errorf("expected %d commits after pull %d, got %d", initialCount+i+1, i+1, newCount)
}
// Verify new files exist
expectedFile := filepath.Join(clientRepoDir, fmt.Sprintf("pull_%d.txt", i+1))
if _, err := os.Stat(expectedFile); os.IsNotExist(err) {
t.Errorf("expected file %s does not exist", expectedFile)
}
}
// Verify all commits are unique
uniqueCommits := make(map[plumbing.Hash]bool)
for _, commit := range commits {
if uniqueCommits[commit] {
t.Errorf("found duplicate commit: %s", commit)
}
uniqueCommits[commit] = true
}
}
func TestConcurrentPulls(t *testing.T) {
// Create temporary directories
serverRepoDir := t.TempDir()
// Initialize server repository
serverRepo, err := repo.New(serverRepoDir)
if err != nil {
t.Fatalf("failed to create server repo: %v", err)
}
srv := server.New(serverRepo)
// Start test server
ts := httptest.NewServer(srv.Handler())
defer ts.Close()
// Clone initial repository
baseClientDir := t.TempDir()
baseRepo, err := git.PlainClone(baseClientDir, false, &git.CloneOptions{
URL: ts.URL,
})
if err != nil {
t.Fatalf("failed to clone: %v", err)
}
initialCount := countCommits(t, baseRepo)
t.Logf("Initial commit count: %d", initialCount)
// Perform concurrent pulls
const numPulls = 5
var wg sync.WaitGroup
commits := make([]plumbing.Hash, numPulls)
errors := make([]error, numPulls)
for i := 0; i < numPulls; i++ {
wg.Add(1)
go func(idx int) {
defer wg.Done()
// Clone a fresh repo for this goroutine
clientDir := filepath.Join(t.TempDir(), fmt.Sprintf("client_%d", idx))
gitRepo, err := git.PlainClone(clientDir, false, &git.CloneOptions{
URL: ts.URL,
})
if err != nil {
errors[idx] = fmt.Errorf("clone failed: %w", err)
return
}
// Get the commit we received
ref, err := gitRepo.Head()
if err != nil {
errors[idx] = fmt.Errorf("get HEAD failed: %w", err)
return
}
commits[idx] = ref.Hash()
t.Logf("Concurrent pull %d got commit: %s", idx, commits[idx])
}(i)
}
wg.Wait()
// Check for errors
for i, err := range errors {
if err != nil {
t.Errorf("goroutine %d failed: %v", i, err)
}
}
// Verify all commits are unique
uniqueCommits := make(map[plumbing.Hash]bool)
for i, commit := range commits {
if uniqueCommits[commit] {
t.Errorf("concurrent pull %d got duplicate commit: %s", i, commit)
}
uniqueCommits[commit] = true
}
// All pulls should have generated unique commits
if len(uniqueCommits) != numPulls {
t.Errorf("expected %d unique commits, got %d", numPulls, len(uniqueCommits))
}
}
func TestPushRejection(t *testing.T) {
// Create temporary directories
serverRepoDir := t.TempDir()
clientRepoDir := t.TempDir()
// Initialize server repository
serverRepo, err := repo.New(serverRepoDir)
if err != nil {
t.Fatalf("failed to create server repo: %v", err)
}
srv := server.New(serverRepo)
// Start test server
ts := httptest.NewServer(srv.Handler())
defer ts.Close()
// Clone the repository
gitRepo, err := git.PlainClone(clientRepoDir, false, &git.CloneOptions{
URL: ts.URL,
})
if err != nil {
t.Fatalf("failed to clone: %v", err)
}
// Create a new file and commit
testFile := filepath.Join(clientRepoDir, "test-push.txt")
if err := os.WriteFile(testFile, []byte("test push content"), 0644); err != nil {
t.Fatalf("failed to write test file: %v", err)
}
w, err := gitRepo.Worktree()
if err != nil {
t.Fatalf("failed to get worktree: %v", err)
}
if _, err := w.Add("test-push.txt"); err != nil {
t.Fatalf("failed to add file: %v", err)
}
if _, err := w.Commit("Test push commit", &git.CommitOptions{
Author: &object.Signature{
Name: "Test User",
Email: "test@example.com",
},
}); err != nil {
t.Fatalf("failed to commit: %v", err)
}
// Try to push - should fail
err = gitRepo.Push(&git.PushOptions{
RemoteName: "origin",
Auth: &http.BasicAuth{
Username: "test",
Password: "test",
},
})
if err == nil {
t.Fatal("push should have been rejected but succeeded")
}
// Verify the error indicates push was forbidden
t.Logf("Push rejected with error: %v", err)
}
// Helper function to count commits
func countCommits(t *testing.T, repo *git.Repository) int {
iter, err := repo.Log(&git.LogOptions{})
if err != nil {
t.Fatalf("failed to get log: %v", err)
}
defer iter.Close()
count := 0
err = iter.ForEach(func(c *object.Commit) error {
count++
return nil
})
if err != nil {
t.Fatalf("failed to iterate commits: %v", err)
}
return count
}