diff options
Diffstat (limited to 'internal/executor/claude_test.go')
| -rw-r--r-- | internal/executor/claude_test.go | 103 |
1 files changed, 79 insertions, 24 deletions
diff --git a/internal/executor/claude_test.go b/internal/executor/claude_test.go index 9bb873f..02d1b2e 100644 --- a/internal/executor/claude_test.go +++ b/internal/executor/claude_test.go @@ -173,8 +173,11 @@ func TestClaudeRunner_BuildArgs_PreamblePrepended(t *testing.T) { if len(args) < 2 || args[0] != "-p" { t.Fatalf("expected -p as first arg, got: %v", args) } - if !strings.HasPrefix(args[1], planningPreamble) { - t.Errorf("instructions should start with planning preamble") + if !strings.HasPrefix(args[1], "## Runtime Environment") { + t.Errorf("instructions should start with planning preamble, got prefix: %q", args[1][:min(len(args[1]), 20)]) + } + if !strings.Contains(args[1], "$CLAUDOMATOR_PROJECT_DIR") { + t.Errorf("preamble should mention $CLAUDOMATOR_PROJECT_DIR") } if !strings.HasSuffix(args[1], "fix the bug") { t.Errorf("instructions should end with original instructions") @@ -329,7 +332,7 @@ func TestExecOnce_NoGoroutineLeak_OnNaturalExit(t *testing.T) { runtime.Gosched() baseline := runtime.NumGoroutine() - if err := r.execOnce(context.Background(), []string{}, "", e); err != nil { + if err := r.execOnce(context.Background(), []string{}, "", "", e); err != nil { t.Fatalf("execOnce failed: %v", err) } @@ -350,16 +353,24 @@ func TestExecOnce_NoGoroutineLeak_OnNaturalExit(t *testing.T) { func initGitRepo(t *testing.T, dir string) { t.Helper() cmds := [][]string{ - {"git", "-C", dir, "init"}, - {"git", "-C", dir, "config", "user.email", "test@test"}, - {"git", "-C", dir, "config", "user.name", "test"}, - {"git", "-C", dir, "commit", "--allow-empty", "-m", "init"}, + {"git", "-c", "safe.directory=*", "-C", dir, "init", "-b", "main"}, + {"git", "-c", "safe.directory=*", "-C", dir, "config", "user.email", "test@test"}, + {"git", "-c", "safe.directory=*", "-C", dir, "config", "user.name", "test"}, } for _, args := range cmds { if out, err := exec.Command(args[0], args[1:]...).CombinedOutput(); err != nil { t.Fatalf("%v: %v\n%s", args, err, out) } } + if err := os.WriteFile(filepath.Join(dir, "init.txt"), []byte("init"), 0644); err != nil { + t.Fatal(err) + } + if out, err := exec.Command("git", "-c", "safe.directory=*", "-C", dir, "add", ".").CombinedOutput(); err != nil { + t.Fatalf("git add: %v\n%s", err, out) + } + if out, err := exec.Command("git", "-c", "safe.directory=*", "-C", dir, "commit", "-m", "init").CombinedOutput(); err != nil { + t.Fatalf("git commit: %v\n%s", err, out) + } } func TestSandboxCloneSource_PrefersLocalRemote(t *testing.T) { @@ -409,6 +420,13 @@ func TestSetupSandbox_ClonesGitRepo(t *testing.T) { } t.Cleanup(func() { os.RemoveAll(sandbox) }) + // Force sandbox to master if it cloned as main + exec.Command("git", gitSafe("-C", sandbox, "checkout", "master")...).Run() + + // Debug sandbox + logOut, _ := exec.Command("git", "-C", sandbox, "log", "-1").CombinedOutput() + fmt.Printf("DEBUG: sandbox log: %s\n", string(logOut)) + // Verify sandbox is a git repo with at least one commit. out, err := exec.Command("git", "-C", sandbox, "log", "--oneline").Output() if err != nil { @@ -434,31 +452,63 @@ func TestSetupSandbox_InitialisesNonGitDir(t *testing.T) { } } -func TestTeardownSandbox_UncommittedChanges_ReturnsError(t *testing.T) { - src := t.TempDir() - initGitRepo(t, src) - sandbox, err := setupSandbox(src) +func TestTeardownSandbox_AutocommitsChanges(t *testing.T) { + // Create a bare repo as origin so push succeeds. + bare := t.TempDir() + if out, err := exec.Command("git", "init", "--bare", bare).CombinedOutput(); err != nil { + t.Fatalf("git init bare: %v\n%s", err, out) + } + + // Create a sandbox directly. + sandbox := t.TempDir() + initGitRepo(t, sandbox) + if out, err := exec.Command("git", "-c", "safe.directory=*", "-C", sandbox, "remote", "add", "origin", bare).CombinedOutput(); err != nil { + t.Fatalf("git remote add: %v\n%s", err, out) + } + // Initial push to establish origin/main + if out, err := exec.Command("git", "-c", "safe.directory=*", "-C", sandbox, "push", "origin", "main").CombinedOutput(); err != nil { + t.Fatalf("git push initial: %v\n%s", err, out) + } + + // Capture startHEAD + headOut, err := exec.Command("git", "-c", "safe.directory=*", "-C", sandbox, "rev-parse", "HEAD").Output() if err != nil { - t.Fatalf("setupSandbox: %v", err) + t.Fatalf("rev-parse HEAD: %v", err) } - t.Cleanup(func() { os.RemoveAll(sandbox) }) + startHEAD := strings.TrimSpace(string(headOut)) // Leave an uncommitted file in the sandbox. - if err := os.WriteFile(filepath.Join(sandbox, "dirty.txt"), []byte("oops"), 0644); err != nil { + if err := os.WriteFile(filepath.Join(sandbox, "dirty.txt"), []byte("autocommit me"), 0644); err != nil { t.Fatal(err) } - logger := slog.New(slog.NewTextHandler(io.Discard, nil)) - err = teardownSandbox(src, sandbox, logger) - if err == nil { - t.Fatal("expected error for uncommitted changes, got nil") + logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug})) + execRecord := &storage.Execution{} + + err = teardownSandbox("", sandbox, startHEAD, logger, execRecord) + if err != nil { + t.Fatalf("expected autocommit to succeed, got error: %v", err) + } + + // Sandbox should be removed after successful autocommit and push. + if _, statErr := os.Stat(sandbox); !os.IsNotExist(statErr) { + t.Error("sandbox should have been removed after successful autocommit and push") } - if !strings.Contains(err.Error(), "uncommitted changes") { - t.Errorf("expected 'uncommitted changes' in error, got: %v", err) + + // Verify the commit exists in the bare repo. + out, err := exec.Command("git", "-C", bare, "log", "-1", "--pretty=%B").Output() + if err != nil { + t.Fatalf("git log in bare repo: %v", err) } - // Sandbox should be preserved (not removed) on error. - if _, statErr := os.Stat(sandbox); os.IsNotExist(statErr) { - t.Error("sandbox was removed despite error; should be preserved for debugging") + if !strings.Contains(string(out), "chore: autocommit uncommitted changes") { + t.Errorf("expected autocommit message in log, got: %q", string(out)) + } + + // Verify the commit was captured in execRecord. + if len(execRecord.Commits) == 0 { + t.Error("expected at least one commit in execRecord") + } else if !strings.Contains(execRecord.Commits[0].Message, "chore: autocommit uncommitted changes") { + t.Errorf("unexpected commit message: %q", execRecord.Commits[0].Message) } } @@ -471,8 +521,13 @@ func TestTeardownSandbox_CleanSandboxWithNoNewCommits_RemovesSandbox(t *testing. } logger := slog.New(slog.NewTextHandler(io.Discard, nil)) + execRecord := &storage.Execution{} + + headOut, _ := exec.Command("git", "-C", sandbox, "rev-parse", "HEAD").Output() + startHEAD := strings.TrimSpace(string(headOut)) + // Sandbox has no new commits beyond origin; teardown should succeed and remove it. - if err := teardownSandbox(src, sandbox, logger); err != nil { + if err := teardownSandbox(src, sandbox, startHEAD, logger, execRecord); err != nil { t.Fatalf("teardownSandbox: %v", err) } if _, statErr := os.Stat(sandbox); !os.IsNotExist(statErr) { |
