From 4455b83a4767ed596bfd33c5c7f1547df7d792a7 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 3 Apr 2026 12:11:45 +0000 Subject: [PATCH] Rewrite infinite-go tests to use git clone + go build Replace the go-git library tests with tests that shell out to `git clone` and `git pull`, then verify each clone/pull produces a compilable Go package with a unique PullTime. This more closely simulates the real `go get @latest` with GOPROXY=direct flow. - TestGoGet: 3 fresh clones, each builds and has unique PullTime - TestGoGetPull: clone then 3 pulls, each builds and updates PullTime - TestGoGetDiscovery: ?go-get=1 returns correct go-import meta tag https://claude.ai/code/session_01HpvELk7HnbJoXEUjqHP7SU --- cmd/infinite-go/main_test.go | 179 +++++++++++++++++++++++++---------- 1 file changed, 129 insertions(+), 50 deletions(-) diff --git a/cmd/infinite-go/main_test.go b/cmd/infinite-go/main_test.go index e14382d..c129c30 100644 --- a/cmd/infinite-go/main_test.go +++ b/cmd/infinite-go/main_test.go @@ -5,17 +5,17 @@ import ( "net/http" "net/http/httptest" "os" + "os/exec" "path/filepath" "strings" "testing" - "github.com/go-git/go-git/v5" "github.com/imjasonh/infinite-git/internal/repo" "github.com/imjasonh/infinite-git/internal/server" ) -func TestGoCloneAndPull(t *testing.T) { - modulePath := "example.com/infinite-go" +func newGoTestServer(t *testing.T, modulePath string) *httptest.Server { + t.Helper() content := newGoContent(modulePath) serverRepo, err := repo.New(t.TempDir(), content.InitialFiles()) if err != nil { @@ -23,70 +23,149 @@ func TestGoCloneAndPull(t *testing.T) { } srv := server.New(serverRepo, content) ts := httptest.NewServer(goGetMiddleware(modulePath, srv.Handler())) - defer ts.Close() + t.Cleanup(ts.Close) + return ts +} - // Clone the repository - clientDir := t.TempDir() - gitRepo, err := git.PlainClone(clientDir, false, &git.CloneOptions{ - URL: ts.URL, - }) +// TestGoGet clones the infinite-go server repeatedly using git (which is what +// `go get` does under the hood with GOPROXY=direct) and verifies that each +// clone produces a valid, buildable Go package with a unique PullTime. +func TestGoGet(t *testing.T) { + goBin, err := exec.LookPath("go") if err != nil { - t.Fatalf("failed to clone: %v", err) + t.Skip("go binary not found in PATH") + } + gitBin, err := exec.LookPath("git") + if err != nil { + t.Skip("git binary not found in PATH") } - // Verify go.mod exists with correct module path - goMod, err := os.ReadFile(filepath.Join(clientDir, "go.mod")) - if err != nil { - t.Fatalf("failed to read go.mod: %v", err) - } - if !strings.Contains(string(goMod), fmt.Sprintf("module %s", modulePath)) { - t.Errorf("go.mod does not contain expected module path, got: %s", goMod) + modulePath := "example.com/infinite-go" + ts := newGoTestServer(t, modulePath) + + var pullTimes []string + + for i := 0; i < 3; i++ { + cloneDir := t.TempDir() + + // Clone via git — this is exactly what `go get` with GOPROXY=direct does. + cmd := exec.Command(gitBin, "clone", ts.URL, cloneDir) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("git clone #%d failed: %v\noutput: %s", i+1, err, out) + } + + // Verify go.mod has the right module path. + goMod, err := os.ReadFile(filepath.Join(cloneDir, "go.mod")) + if err != nil { + t.Fatalf("clone #%d: failed to read go.mod: %v", i+1, err) + } + if !strings.Contains(string(goMod), fmt.Sprintf("module %s", modulePath)) { + t.Fatalf("clone #%d: go.mod has wrong module path: %s", i+1, goMod) + } + + // Verify the package builds. + cmd = exec.Command(goBin, "build", "./...") + cmd.Dir = cloneDir + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("clone #%d: go build failed: %v\noutput: %s", i+1, err, out) + } + + // Read pulltime.go and extract the PullTime declaration. + data, err := os.ReadFile(filepath.Join(cloneDir, "pulltime.go")) + if err != nil { + t.Fatalf("clone #%d: failed to read pulltime.go: %v", i+1, err) + } + for _, line := range strings.Split(string(data), "\n") { + if strings.Contains(line, "var PullTime") { + pullTimes = append(pullTimes, strings.TrimSpace(line)) + t.Logf("clone #%d: %s", i+1, strings.TrimSpace(line)) + break + } + } } - // Verify pulltime.go exists with PullTime var - goFile, err := os.ReadFile(filepath.Join(clientDir, "pulltime.go")) - if err != nil { - t.Fatalf("failed to read pulltime.go: %v", err) - } - goFileStr := string(goFile) - if !strings.Contains(goFileStr, "package infinitego") { - t.Errorf("pulltime.go does not contain expected package name, got: %s", goFile) - } - if !strings.Contains(goFileStr, "var PullTime = time.Date(") { - t.Errorf("pulltime.go does not contain PullTime var, got: %s", goFile) + if len(pullTimes) != 3 { + t.Fatalf("expected 3 PullTime values, got %d", len(pullTimes)) } - // Pull again and verify PullTime changed - firstGoFile := goFileStr - w, err := gitRepo.Worktree() + // Every clone must produce a unique PullTime. + seen := make(map[string]bool) + for i, pt := range pullTimes { + if seen[pt] { + t.Errorf("clone #%d returned duplicate PullTime: %s", i+1, pt) + } + seen[pt] = true + } +} + +// TestGoGetPull verifies that git pull on an already-cloned repo also +// produces a new PullTime. +func TestGoGetPull(t *testing.T) { + goBin, err := exec.LookPath("go") if err != nil { - t.Fatalf("failed to get worktree: %v", err) + t.Skip("go binary not found in PATH") } - err = w.Pull(&git.PullOptions{RemoteName: "origin"}) - if err != nil && err != git.NoErrAlreadyUpToDate { - t.Fatalf("pull failed: %v", err) - } - goFile, err = os.ReadFile(filepath.Join(clientDir, "pulltime.go")) + gitBin, err := exec.LookPath("git") if err != nil { - t.Fatalf("failed to read pulltime.go after pull: %v", err) + t.Skip("git binary not found in PATH") } - if string(goFile) == firstGoFile { - t.Error("pulltime.go did not change after pull") + + modulePath := "example.com/infinite-go" + ts := newGoTestServer(t, modulePath) + + cloneDir := t.TempDir() + + // Initial clone. + cmd := exec.Command(gitBin, "clone", ts.URL, cloneDir) + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("git clone failed: %v\noutput: %s", err, out) + } + + readPullTime := func() string { + data, err := os.ReadFile(filepath.Join(cloneDir, "pulltime.go")) + if err != nil { + t.Fatalf("failed to read pulltime.go: %v", err) + } + for _, line := range strings.Split(string(data), "\n") { + if strings.Contains(line, "var PullTime") { + return strings.TrimSpace(line) + } + } + t.Fatal("pulltime.go does not contain PullTime declaration") + return "" + } + + prev := readPullTime() + t.Logf("clone: %s", prev) + + for i := 0; i < 3; i++ { + cmd := exec.Command(gitBin, "-C", cloneDir, "pull") + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("git pull #%d failed: %v\noutput: %s", i+1, err, out) + } + + // Verify it still builds. + cmd = exec.Command(goBin, "build", "./...") + cmd.Dir = cloneDir + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("pull #%d: go build failed: %v\noutput: %s", i+1, err, out) + } + + cur := readPullTime() + t.Logf("pull #%d: %s", i+1, cur) + if cur == prev { + t.Errorf("pull #%d did not change PullTime", i+1) + } + prev = cur } } func TestGoGetDiscovery(t *testing.T) { modulePath := "example.com/infinite-go" - content := newGoContent(modulePath) - serverRepo, err := repo.New(t.TempDir(), content.InitialFiles()) - if err != nil { - t.Fatalf("failed to create server repo: %v", err) - } - srv := server.New(serverRepo, content) - ts := httptest.NewServer(goGetMiddleware(modulePath, srv.Handler())) - defer ts.Close() + ts := newGoTestServer(t, modulePath) - // Test ?go-get=1 returns go-import meta tag resp, err := http.Get(ts.URL + "/?go-get=1") if err != nil { t.Fatalf("failed to fetch go-get: %v", err) @@ -99,6 +178,6 @@ func TestGoGetDiscovery(t *testing.T) { expected := fmt.Sprintf(`