blob: 053b8afdda52604e911a80e9656367a749da5ec3 [file] [log] [blame]
Earl Lee2e463fb2025-04-17 11:22:22 -07001package loop
2
3import (
4 "context"
5 "fmt"
6 "os"
7 "os/exec"
8 "path/filepath"
9 "strings"
10 "testing"
Earl Lee2e463fb2025-04-17 11:22:22 -070011)
12
13// TestGitCommitTracking tests the git commit tracking functionality
14func TestGitCommitTracking(t *testing.T) {
15 // Create a temporary directory for our test git repo
16 tempDir := t.TempDir() // Automatically cleaned up when the test completes
17
18 // Initialize a git repo in the temp directory
19 cmd := exec.Command("git", "init")
20 cmd.Dir = tempDir
21 if err := cmd.Run(); err != nil {
22 t.Fatalf("Failed to initialize git repo: %v", err)
23 }
24
25 // Configure git user for commits
26 cmd = exec.Command("git", "config", "user.name", "Test User")
27 cmd.Dir = tempDir
28 if err := cmd.Run(); err != nil {
29 t.Fatalf("Failed to configure git user name: %v", err)
30 }
31
32 cmd = exec.Command("git", "config", "user.email", "test@example.com")
33 cmd.Dir = tempDir
34 if err := cmd.Run(); err != nil {
35 t.Fatalf("Failed to configure git user email: %v", err)
36 }
37
38 // Make an initial commit
39 testFile := filepath.Join(tempDir, "test.txt")
40 if err := os.WriteFile(testFile, []byte("initial content\n"), 0o644); err != nil {
41 t.Fatalf("Failed to write file: %v", err)
42 }
43
44 cmd = exec.Command("git", "add", "test.txt")
45 cmd.Dir = tempDir
46 if err := cmd.Run(); err != nil {
47 t.Fatalf("Failed to add file: %v", err)
48 }
49
50 cmd = exec.Command("git", "commit", "-m", "Initial commit")
51 cmd.Dir = tempDir
52 if err := cmd.Run(); err != nil {
53 t.Fatalf("Failed to create initial commit: %v", err)
54 }
55
56 // Get the initial commit hash
57 cmd = exec.Command("git", "rev-parse", "HEAD")
58 cmd.Dir = tempDir
59 initialCommitOutput, err := cmd.Output()
60 if err != nil {
61 t.Fatalf("Failed to get initial commit hash: %v", err)
62 }
63 initialCommit := strings.TrimSpace(string(initialCommitOutput))
64
65 // Create agent with the temp repo
66 agent := &Agent{
67 workingDir: tempDir,
68 repoRoot: tempDir, // Set repoRoot to same as workingDir for this test
Earl Lee2e463fb2025-04-17 11:22:22 -070069 seenCommits: make(map[string]bool),
70 initialCommit: initialCommit,
71 }
72
73 // Make a new commit
74 if err := os.WriteFile(testFile, []byte("updated content\n"), 0o644); err != nil {
75 t.Fatalf("Failed to update file: %v", err)
76 }
77
78 cmd = exec.Command("git", "add", "test.txt")
79 cmd.Dir = tempDir
80 if err := cmd.Run(); err != nil {
81 t.Fatalf("Failed to add updated file: %v", err)
82 }
83
84 cmd = exec.Command("git", "commit", "-m", "Second commit\n\nThis commit has a multi-line message\nwith details about the changes.")
85 cmd.Dir = tempDir
86 if err := cmd.Run(); err != nil {
87 t.Fatalf("Failed to create second commit: %v", err)
88 }
89
90 // Call handleGitCommits and verify we get a commit message
91 ctx := context.Background()
92 _, err = agent.handleGitCommits(ctx)
93 if err != nil {
94 t.Fatalf("handleGitCommits failed: %v", err)
95 }
96
97 // Check if we received a commit message
Philip Zeyligerb7c58752025-05-01 10:10:17 -070098 var commitMsg AgentMessage = agent.history[len(agent.history)-1]
Earl Lee2e463fb2025-04-17 11:22:22 -070099
100 // Verify the commit message
101 if commitMsg.Type != CommitMessageType {
102 t.Errorf("Expected message type %s, got %s", CommitMessageType, commitMsg.Type)
103 }
104
105 if len(commitMsg.Commits) < 1 {
106 t.Fatalf("Expected at least 1 commit, got %d", len(commitMsg.Commits))
107 }
108
109 // Find the second commit
110 var commit *GitCommit
111 found := false
112 for _, c := range commitMsg.Commits {
113 if strings.HasPrefix(c.Subject, "Second commit") {
114 commit = c
115 found = true
116 break
117 }
118 }
119
120 if !found {
121 t.Fatalf("Could not find 'Second commit' in commits")
122 }
123 if !strings.HasPrefix(commit.Subject, "Second commit") {
124 t.Errorf("Expected commit subject 'Second commit', got '%s'", commit.Subject)
125 }
126
127 if !strings.Contains(commit.Body, "multi-line message") {
128 t.Errorf("Expected body to contain 'multi-line message', got '%s'", commit.Body)
129 }
130
131 // Test with many commits
132 if testing.Short() {
133 t.Skip("Skipping multiple commits test in short mode")
134 }
135
136 // Make multiple commits (more than 100)
137 for i := 0; i < 110; i++ {
138 newContent := []byte(fmt.Sprintf("content update %d\n", i))
139 if err := os.WriteFile(testFile, newContent, 0o644); err != nil {
140 t.Fatalf("Failed to update file: %v", err)
141 }
142
143 cmd = exec.Command("git", "add", "test.txt")
144 cmd.Dir = tempDir
145 if err := cmd.Run(); err != nil {
146 t.Fatalf("Failed to add updated file: %v", err)
147 }
148
149 cmd = exec.Command("git", "commit", "-m", fmt.Sprintf("Commit %d", i+3))
150 cmd.Dir = tempDir
151 if err := cmd.Run(); err != nil {
152 t.Fatalf("Failed to create commit %d: %v", i+3, err)
153 }
154 }
155
156 // Reset the outbox channel and seen commits map
Earl Lee2e463fb2025-04-17 11:22:22 -0700157 agent.seenCommits = make(map[string]bool)
158
159 // Call handleGitCommits again - it should still work but only show at most 100 commits
160 _, err = agent.handleGitCommits(ctx)
161 if err != nil {
162 t.Fatalf("handleGitCommits failed: %v", err)
163 }
164
165 // Check if we received a commit message
Philip Zeyligerb7c58752025-05-01 10:10:17 -0700166 commitMsg = agent.history[len(agent.history)-1]
Earl Lee2e463fb2025-04-17 11:22:22 -0700167
168 // Should have at most 100 commits due to the -n 100 limit in git log
169 if len(commitMsg.Commits) > 100 {
170 t.Errorf("Expected at most 100 commits, got %d", len(commitMsg.Commits))
171 }
172
173 if len(commitMsg.Commits) < 50 {
174 t.Errorf("Expected at least 50 commits, but only got %d", len(commitMsg.Commits))
175 }
176
177 t.Logf("Received %d commits out of 112 total", len(commitMsg.Commits))
178}
179
180// TestParseGitLog tests the parseGitLog function
181func TestParseGitLog(t *testing.T) {
182 tests := []struct {
183 name string
184 input string
185 expected []GitCommit
186 }{
187 {
188 name: "Empty input",
189 input: "",
190 expected: []GitCommit{},
191 },
192 {
193 name: "Single commit",
194 input: "abcdef1234567890\x00Initial commit\x00This is the first commit\x00",
195 expected: []GitCommit{
196 {Hash: "abcdef1234567890", Subject: "Initial commit", Body: "This is the first commit"},
197 },
198 },
199 {
200 name: "Multiple commits",
201 input: "abcdef1234567890\x00Initial commit\x00This is the first commit\x00" +
202 "fedcba0987654321\x00Second commit\x00This is the second commit\x00" +
203 "123456abcdef7890\x00Third commit\x00This is the third commit\x00",
204 expected: []GitCommit{
205 {Hash: "abcdef1234567890", Subject: "Initial commit", Body: "This is the first commit"},
206 {Hash: "fedcba0987654321", Subject: "Second commit", Body: "This is the second commit"},
207 {Hash: "123456abcdef7890", Subject: "Third commit", Body: "This is the third commit"},
208 },
209 },
210 {
211 name: "Commit with multi-line body",
212 input: "abcdef1234567890\x00Commit with multi-line body\x00This is a commit\nwith a multi-line\nbody message\x00",
213 expected: []GitCommit{
214 {Hash: "abcdef1234567890", Subject: "Commit with multi-line body", Body: "This is a commit\nwith a multi-line\nbody message"},
215 },
216 },
217 {
218 name: "Commit with empty body",
219 input: "abcdef1234567890\x00Commit with empty body\x00\x00",
220 expected: []GitCommit{
221 {Hash: "abcdef1234567890", Subject: "Commit with empty body", Body: ""},
222 },
223 },
224 {
225 name: "Empty parts removed",
226 input: "\x00abcdef1234567890\x00Initial commit\x00This is the first commit\x00\x00",
227 expected: []GitCommit{
228 {Hash: "abcdef1234567890", Subject: "Initial commit", Body: "This is the first commit"},
229 },
230 },
231 }
232
233 for _, tt := range tests {
234 t.Run(tt.name, func(t *testing.T) {
235 actual := parseGitLog(tt.input)
236
237 if len(actual) != len(tt.expected) {
238 t.Fatalf("Expected %d commits, got %d", len(tt.expected), len(actual))
239 }
240
241 for i, commit := range actual {
242 expected := tt.expected[i]
243 if commit.Hash != expected.Hash || commit.Subject != expected.Subject || commit.Body != expected.Body {
244 t.Errorf("Commit %d doesn't match:\nExpected: %+v\nGot: %+v", i, expected, commit)
245 }
246 }
247 })
248 }
249}