blob: a1ca128c7d752fc2aa927db596e191685be8829b [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
Philip Zeyliger49edc922025-05-14 09:45:45 -070056 // Note: The initial commit will be tagged as sketch-base later
Earl Lee2e463fb2025-04-17 11:22:22 -070057
58 // Create agent with the temp repo
59 agent := &Agent{
Philip Zeyliger49edc922025-05-14 09:45:45 -070060 workingDir: tempDir,
61 repoRoot: tempDir, // Set repoRoot to same as workingDir for this test
62 seenCommits: make(map[string]bool),
63 subscribers: []chan *AgentMessage{},
64 config: AgentConfig{
65 SessionID: "test-session",
66 InDocker: false,
67 },
68 history: []AgentMessage{},
69 }
70
71 // Create sketch-base-test-session tag at current HEAD to serve as the base commit
72 cmd = exec.Command("git", "tag", "-f", "sketch-base-test-session", "HEAD")
73 cmd.Dir = tempDir
74 if err := cmd.Run(); err != nil {
75 t.Fatalf("Failed to create sketch-base tag: %v", err)
Earl Lee2e463fb2025-04-17 11:22:22 -070076 }
77
78 // Make a new commit
79 if err := os.WriteFile(testFile, []byte("updated content\n"), 0o644); err != nil {
80 t.Fatalf("Failed to update file: %v", err)
81 }
82
83 cmd = exec.Command("git", "add", "test.txt")
84 cmd.Dir = tempDir
85 if err := cmd.Run(); err != nil {
86 t.Fatalf("Failed to add updated file: %v", err)
87 }
88
89 cmd = exec.Command("git", "commit", "-m", "Second commit\n\nThis commit has a multi-line message\nwith details about the changes.")
90 cmd.Dir = tempDir
91 if err := cmd.Run(); err != nil {
92 t.Fatalf("Failed to create second commit: %v", err)
93 }
94
95 // Call handleGitCommits and verify we get a commit message
96 ctx := context.Background()
Philip Zeyliger49edc922025-05-14 09:45:45 -070097 _, gitErr := agent.handleGitCommits(ctx)
98 if gitErr != nil {
99 t.Fatalf("handleGitCommits failed: %v", gitErr)
Earl Lee2e463fb2025-04-17 11:22:22 -0700100 }
101
102 // Check if we received a commit message
Philip Zeyliger9373c072025-05-01 10:27:01 -0700103 agent.mu.Lock()
104 if len(agent.history) == 0 {
105 agent.mu.Unlock()
106 t.Fatal("No commit message was added to history")
107 }
108 commitMsg := agent.history[len(agent.history)-1]
109 agent.mu.Unlock()
Earl Lee2e463fb2025-04-17 11:22:22 -0700110
111 // Verify the commit message
112 if commitMsg.Type != CommitMessageType {
113 t.Errorf("Expected message type %s, got %s", CommitMessageType, commitMsg.Type)
114 }
115
116 if len(commitMsg.Commits) < 1 {
117 t.Fatalf("Expected at least 1 commit, got %d", len(commitMsg.Commits))
118 }
119
120 // Find the second commit
121 var commit *GitCommit
122 found := false
123 for _, c := range commitMsg.Commits {
124 if strings.HasPrefix(c.Subject, "Second commit") {
125 commit = c
126 found = true
127 break
128 }
129 }
130
131 if !found {
132 t.Fatalf("Could not find 'Second commit' in commits")
133 }
134 if !strings.HasPrefix(commit.Subject, "Second commit") {
135 t.Errorf("Expected commit subject 'Second commit', got '%s'", commit.Subject)
136 }
137
138 if !strings.Contains(commit.Body, "multi-line message") {
139 t.Errorf("Expected body to contain 'multi-line message', got '%s'", commit.Body)
140 }
141
142 // Test with many commits
143 if testing.Short() {
144 t.Skip("Skipping multiple commits test in short mode")
145 }
146
Philip Zeyliger9373c072025-05-01 10:27:01 -0700147 // Skip the multiple commits test in short mode
148 if testing.Short() {
149 t.Log("Skipping multiple commits test in short mode")
150 return
151 }
152
153 // Make multiple commits - reduce from 110 to 20 for faster tests
154 // 20 is enough to verify the functionality without the time penalty
155 for i := range 20 {
156 newContent := fmt.Appendf(nil, "content update %d\n", i)
Earl Lee2e463fb2025-04-17 11:22:22 -0700157 if err := os.WriteFile(testFile, newContent, 0o644); err != nil {
158 t.Fatalf("Failed to update file: %v", err)
159 }
160
161 cmd = exec.Command("git", "add", "test.txt")
162 cmd.Dir = tempDir
163 if err := cmd.Run(); err != nil {
164 t.Fatalf("Failed to add updated file: %v", err)
165 }
166
167 cmd = exec.Command("git", "commit", "-m", fmt.Sprintf("Commit %d", i+3))
168 cmd.Dir = tempDir
169 if err := cmd.Run(); err != nil {
170 t.Fatalf("Failed to create commit %d: %v", i+3, err)
171 }
172 }
173
Philip Zeyliger9373c072025-05-01 10:27:01 -0700174 // Reset the seen commits map
Earl Lee2e463fb2025-04-17 11:22:22 -0700175 agent.seenCommits = make(map[string]bool)
176
Philip Zeyliger9373c072025-05-01 10:27:01 -0700177 // Call handleGitCommits again - it should show up to 20 commits (or whatever git defaults to)
Philip Zeyliger49edc922025-05-14 09:45:45 -0700178 _, handleErr := agent.handleGitCommits(ctx)
179 if handleErr != nil {
180 t.Fatalf("handleGitCommits failed: %v", handleErr)
Earl Lee2e463fb2025-04-17 11:22:22 -0700181 }
182
183 // Check if we received a commit message
Philip Zeyliger9373c072025-05-01 10:27:01 -0700184 agent.mu.Lock()
Philip Zeyligerb7c58752025-05-01 10:10:17 -0700185 commitMsg = agent.history[len(agent.history)-1]
Philip Zeyliger9373c072025-05-01 10:27:01 -0700186 agent.mu.Unlock()
Earl Lee2e463fb2025-04-17 11:22:22 -0700187
Philip Zeyliger9373c072025-05-01 10:27:01 -0700188 // We should have our commits
189 if len(commitMsg.Commits) < 5 {
190 t.Errorf("Expected at least 5 commits, but only got %d", len(commitMsg.Commits))
Earl Lee2e463fb2025-04-17 11:22:22 -0700191 }
192
Philip Zeyliger9373c072025-05-01 10:27:01 -0700193 t.Logf("Received %d commits total", len(commitMsg.Commits))
Earl Lee2e463fb2025-04-17 11:22:22 -0700194}
195
196// TestParseGitLog tests the parseGitLog function
197func TestParseGitLog(t *testing.T) {
198 tests := []struct {
199 name string
200 input string
201 expected []GitCommit
202 }{
203 {
204 name: "Empty input",
205 input: "",
206 expected: []GitCommit{},
207 },
208 {
209 name: "Single commit",
210 input: "abcdef1234567890\x00Initial commit\x00This is the first commit\x00",
211 expected: []GitCommit{
212 {Hash: "abcdef1234567890", Subject: "Initial commit", Body: "This is the first commit"},
213 },
214 },
215 {
216 name: "Multiple commits",
217 input: "abcdef1234567890\x00Initial commit\x00This is the first commit\x00" +
218 "fedcba0987654321\x00Second commit\x00This is the second commit\x00" +
219 "123456abcdef7890\x00Third commit\x00This is the third commit\x00",
220 expected: []GitCommit{
221 {Hash: "abcdef1234567890", Subject: "Initial commit", Body: "This is the first commit"},
222 {Hash: "fedcba0987654321", Subject: "Second commit", Body: "This is the second commit"},
223 {Hash: "123456abcdef7890", Subject: "Third commit", Body: "This is the third commit"},
224 },
225 },
226 {
227 name: "Commit with multi-line body",
228 input: "abcdef1234567890\x00Commit with multi-line body\x00This is a commit\nwith a multi-line\nbody message\x00",
229 expected: []GitCommit{
230 {Hash: "abcdef1234567890", Subject: "Commit with multi-line body", Body: "This is a commit\nwith a multi-line\nbody message"},
231 },
232 },
233 {
234 name: "Commit with empty body",
235 input: "abcdef1234567890\x00Commit with empty body\x00\x00",
236 expected: []GitCommit{
237 {Hash: "abcdef1234567890", Subject: "Commit with empty body", Body: ""},
238 },
239 },
240 {
241 name: "Empty parts removed",
242 input: "\x00abcdef1234567890\x00Initial commit\x00This is the first commit\x00\x00",
243 expected: []GitCommit{
244 {Hash: "abcdef1234567890", Subject: "Initial commit", Body: "This is the first commit"},
245 },
246 },
247 }
248
249 for _, tt := range tests {
250 t.Run(tt.name, func(t *testing.T) {
251 actual := parseGitLog(tt.input)
252
253 if len(actual) != len(tt.expected) {
254 t.Fatalf("Expected %d commits, got %d", len(tt.expected), len(actual))
255 }
256
257 for i, commit := range actual {
258 expected := tt.expected[i]
259 if commit.Hash != expected.Hash || commit.Subject != expected.Subject || commit.Body != expected.Body {
260 t.Errorf("Commit %d doesn't match:\nExpected: %+v\nGot: %+v", i, expected, commit)
261 }
262 }
263 })
264 }
265}