1package subagents
2
3import (
4 "context"
5 "os"
6 "os/exec"
7 "path/filepath"
8 "strings"
9 "testing"
10)
11
12func TestSpawnWorktreeCapturesBeforeCleanup(t *testing.T) {
13 if _, err := exec.LookPath("git"); err != nil {
14 t.Skip("git is required")
15 }
16 repo := initTestRepo(t)
17 if err := os.WriteFile(filepath.Join(repo, "main.txt"), []byte("host\n"), 0o600); err != nil {
18 t.Fatal(err)
19 }
20 runGit(t, repo, "add", "main.txt")
21 runGit(t, repo, "commit", "-m", "initial")
22
23 state := t.TempDir()
24 f := New(Config{
25 Root: state, RepoRoot: repo,
26 Policy: SubagentPolicy{DefaultTimeout: 0, IdleTimeout: 0},
27 NewRunner: func(a *Agent) Runner {
28 return RunnerFunc(func(context.Context, Sink) error {
29 if err := os.WriteFile(filepath.Join(a.Dir, "main.txt"), []byte("child\n"), 0o600); err != nil {
30 return err
31 }
32 return os.WriteFile(filepath.Join(a.Dir, "new.txt"), []byte("created\n"), 0o600)
33 })
34 },
35 })
36 a, err := f.SpawnReq(context.Background(), SpawnRequest{Task: "isolate", WorkspaceMode: WorkspaceWorktree, WorkspaceBase: "HEAD"})
37 if err != nil {
38 t.Fatal(err)
39 }
40 a.Wait()
41 result, err := f.ReadResult(a.ID)
42 if err != nil {
43 t.Fatal(err)
44 }
45 if result.Status != ResultSucceeded || len(result.ChangedFiles) != 2 || result.ChangedFiles[0] != "main.txt" || result.ChangedFiles[1] != "new.txt" {
46 t.Fatalf("result = %#v", result)
47 }
48 patch, err := os.ReadFile(filepath.Join(state, "agents", a.ID, "patch.diff"))
49 if err != nil {
50 t.Fatal(err)
51 }
52 if !strings.Contains(string(patch), "child") || !strings.Contains(string(patch), "created") {
53 t.Fatalf("patch = %q", patch)
54 }
55 host, err := os.ReadFile(filepath.Join(repo, "main.txt"))
56 if err != nil {
57 t.Fatal(err)
58 }
59 if string(host) != "host\n" {
60 t.Fatalf("host checkout changed: %q", host)
61 }
62 if _, err := os.Stat(a.WorkspacePath); !os.IsNotExist(err) {
63 t.Fatalf("worktree still exists: %v", err)
64 }
65}