agent_comment_integration_test.go
1//go:build integration
2
3package main
4
5import (
6 "os"
7 "os/exec"
8 "strings"
9 "testing"
10
11 "github.com/git-bug/git-bug/entities/bug"
12 "github.com/git-bug/git-bug/entity"
13 "github.com/git-bug/git-bug/repository"
14)
15
16// TestAgentCommentCommand_WithFlag adds a comment as agent using the message flag
17func TestAgentCommentCommand_WithFlag(t *testing.T) {
18 tmpDir := t.TempDir()
19
20 // Initialize a git repo
21 initCmd := exec.Command("git", "init")
22 initCmd.Dir = tmpDir
23 if err := initCmd.Run(); err != nil {
24 t.Fatalf("failed to init git repo: %v", err)
25 }
26
27 // Initialize git-bug with both user and agent identities
28 if err := createIdentity(tmpDir, "Test User", "[email protected]", true); err != nil {
29 t.Fatalf("failed to create user identity: %v", err)
30 }
31 if err := createIdentity(tmpDir, "agent", "", false); err != nil {
32 t.Fatalf("failed to create agent identity: %v", err)
33 }
34
35 // Create a bug first as user
36 title := "Test Bug for Agent Comment"
37 message := "Initial bug description"
38 if err := runNew(tmpDir, title, message, ""); err != nil {
39 t.Fatalf("runNew failed: %v", err)
40 }
41
42 // Get the bug ID
43 repo, err := repository.OpenGoGitRepo(tmpDir, "", nil)
44 if err != nil {
45 t.Fatalf("failed to open repo: %v", err)
46 }
47
48 var bugID string
49 for streamedBug := range bug.ReadAll(repo) {
50 if streamedBug.Err != nil {
51 t.Fatalf("failed to read bug: %v", streamedBug.Err)
52 }
53 b := streamedBug.Entity
54 snap := b.Compile()
55 if snap.Title == title {
56 bugID = b.Id().String()
57 break
58 }
59 }
60 repo.Close()
61
62 if bugID == "" {
63 t.Fatal("could not find created bug")
64 }
65
66 // Add a comment as agent using the flag
67 commentText := "This is an automated agent comment"
68 if err := runAgentComment(tmpDir, bugID, commentText, "", false); err != nil {
69 t.Fatalf("runAgentComment failed: %v", err)
70 }
71
72 // Verify comment was added with agent as author
73 repo, err = repository.OpenGoGitRepo(tmpDir, "", nil)
74 if err != nil {
75 t.Fatalf("failed to open repo: %v", err)
76 }
77 defer repo.Close()
78
79 b, err := bug.Read(repo, entity.Id(bugID))
80 if err != nil {
81 t.Fatalf("failed to read bug: %v", err)
82 }
83
84 snap := b.Compile()
85 if len(snap.Comments) != 2 {
86 t.Errorf("expected 2 comments (original + new), got %d", len(snap.Comments))
87 }
88
89 // Check the second comment (index 1) is our agent comment
90 if snap.Comments[1].Message != commentText {
91 t.Errorf("comment message = %q, want %q", snap.Comments[1].Message, commentText)
92 }
93
94 if snap.Comments[1].Author.Name() != "agent" {
95 t.Errorf("comment author = %q, want %q", snap.Comments[1].Author.Name(), "agent")
96 }
97
98 if snap.Comments[1].Author.Email() != "" {
99 t.Errorf("comment author email = %q, want empty", snap.Comments[1].Author.Email())
100 }
101}
102
103// TestAgentCommentCommand_WithShortID adds a comment as agent using short bug ID
104func TestAgentCommentCommand_WithShortID(t *testing.T) {
105 tmpDir := t.TempDir()
106
107 // Initialize a git repo
108 initCmd := exec.Command("git", "init")
109 initCmd.Dir = tmpDir
110 if err := initCmd.Run(); err != nil {
111 t.Fatalf("failed to init git repo: %v", err)
112 }
113
114 // Initialize identities
115 if err := createIdentity(tmpDir, "Test User", "[email protected]", true); err != nil {
116 t.Fatalf("failed to create user identity: %v", err)
117 }
118 if err := createIdentity(tmpDir, "agent", "", false); err != nil {
119 t.Fatalf("failed to create agent identity: %v", err)
120 }
121
122 // Create a bug
123 title := "Test Bug for Agent Short ID"
124 if err := runNew(tmpDir, title, "", ""); err != nil {
125 t.Fatalf("runNew failed: %v", err)
126 }
127
128 // Get the bug ID and short ID
129 repo, err := repository.OpenGoGitRepo(tmpDir, "", nil)
130 if err != nil {
131 t.Fatalf("failed to open repo: %v", err)
132 }
133
134 var bugID, shortID string
135 for streamedBug := range bug.ReadAll(repo) {
136 if streamedBug.Err != nil {
137 t.Fatalf("failed to read bug: %v", streamedBug.Err)
138 }
139 b := streamedBug.Entity
140 snap := b.Compile()
141 if snap.Title == title {
142 bugID = b.Id().String()
143 if len(bugID) > 7 {
144 shortID = bugID[:7]
145 } else {
146 shortID = bugID
147 }
148 break
149 }
150 }
151 repo.Close()
152
153 if shortID == "" {
154 t.Fatal("could not find created bug")
155 }
156
157 // Add a comment as agent using short ID
158 commentText := "Agent comment via short ID"
159 if err := runAgentComment(tmpDir, shortID, commentText, "", false); err != nil {
160 t.Fatalf("runAgentComment with short ID failed: %v", err)
161 }
162
163 // Verify comment was added
164 repo, err = repository.OpenGoGitRepo(tmpDir, "", nil)
165 if err != nil {
166 t.Fatalf("failed to open repo: %v", err)
167 }
168 defer repo.Close()
169
170 b, err := bug.Read(repo, entity.Id(bugID))
171 if err != nil {
172 t.Fatalf("failed to read bug: %v", err)
173 }
174
175 snap := b.Compile()
176 if len(snap.Comments) != 2 {
177 t.Errorf("expected 2 comments, got %d", len(snap.Comments))
178 }
179
180 if snap.Comments[1].Message != commentText {
181 t.Errorf("comment message = %q, want %q", snap.Comments[1].Message, commentText)
182 }
183
184 if snap.Comments[1].Author.Name() != "agent" {
185 t.Errorf("comment author = %q, want %q", snap.Comments[1].Author.Name(), "agent")
186 }
187}
188
189// TestAgentCommentCommand_MissingAgentIdentity tests error when agent not initialized
190func TestAgentCommentCommand_MissingAgentIdentity(t *testing.T) {
191 tmpDir := t.TempDir()
192
193 // Initialize a git repo
194 initCmd := exec.Command("git", "init")
195 initCmd.Dir = tmpDir
196 if err := initCmd.Run(); err != nil {
197 t.Fatalf("failed to init git repo: %v", err)
198 }
199
200 // Only create user identity, NOT agent
201 if err := createIdentity(tmpDir, "Test User", "[email protected]", true); err != nil {
202 t.Fatalf("failed to create user identity: %v", err)
203 }
204
205 // Create a bug as user
206 title := "Test Bug"
207 if err := runNew(tmpDir, title, "", ""); err != nil {
208 t.Fatalf("runNew failed: %v", err)
209 }
210
211 // Get the bug ID
212 repo, err := repository.OpenGoGitRepo(tmpDir, "", nil)
213 if err != nil {
214 t.Fatalf("failed to open repo: %v", err)
215 }
216
217 var bugID string
218 for streamedBug := range bug.ReadAll(repo) {
219 if streamedBug.Err != nil {
220 t.Fatalf("failed to read bug: %v", streamedBug.Err)
221 }
222 b := streamedBug.Entity
223 snap := b.Compile()
224 if snap.Title == title {
225 bugID = b.Id().String()
226 break
227 }
228 }
229 repo.Close()
230
231 // Try to add a comment as agent (should fail)
232 err = runAgentComment(tmpDir, bugID, "Test comment", "", false)
233 if err == nil {
234 t.Error("expected error when agent identity not found, got nil")
235 }
236
237 if !strings.Contains(err.Error(), "agent identity not found") {
238 t.Errorf("expected 'agent identity not found' error, got: %v", err)
239 }
240}
241
242// TestAgentCommentCommand_EmptyMessageError tests error for empty message
243func TestAgentCommentCommand_EmptyMessageError(t *testing.T) {
244 tmpDir := t.TempDir()
245
246 // Initialize a git repo
247 initCmd := exec.Command("git", "init")
248 initCmd.Dir = tmpDir
249 if err := initCmd.Run(); err != nil {
250 t.Fatalf("failed to init git repo: %v", err)
251 }
252
253 // Initialize identities
254 if err := createIdentity(tmpDir, "Test User", "[email protected]", true); err != nil {
255 t.Fatalf("failed to create user identity: %v", err)
256 }
257 if err := createIdentity(tmpDir, "agent", "", false); err != nil {
258 t.Fatalf("failed to create agent identity: %v", err)
259 }
260
261 // Create a bug
262 title := "Test Bug"
263 if err := runNew(tmpDir, title, "", ""); err != nil {
264 t.Fatalf("runNew failed: %v", err)
265 }
266
267 // Get the bug ID
268 repo, err := repository.OpenGoGitRepo(tmpDir, "", nil)
269 if err != nil {
270 t.Fatalf("failed to open repo: %v", err)
271 }
272
273 var bugID string
274 for streamedBug := range bug.ReadAll(repo) {
275 if streamedBug.Err != nil {
276 t.Fatalf("failed to read bug: %v", streamedBug.Err)
277 }
278 b := streamedBug.Entity
279 snap := b.Compile()
280 if snap.Title == title {
281 bugID = b.Id().String()
282 break
283 }
284 }
285 repo.Close()
286
287 // Try with empty message
288 err = runAgentComment(tmpDir, bugID, " ", "", false)
289 if err == nil {
290 t.Error("expected error for empty message, got nil")
291 }
292
293 if !strings.Contains(err.Error(), "message cannot be empty") {
294 t.Errorf("expected 'message cannot be empty' error, got: %v", err)
295 }
296}
297
298// TestAgentCommentCommand_InvalidBugID tests error for invalid bug ID
299func TestAgentCommentCommand_InvalidBugID(t *testing.T) {
300 tmpDir := t.TempDir()
301
302 // Initialize a git repo
303 initCmd := exec.Command("git", "init")
304 initCmd.Dir = tmpDir
305 if err := initCmd.Run(); err != nil {
306 t.Fatalf("failed to init git repo: %v", err)
307 }
308
309 // Initialize identities
310 if err := createIdentity(tmpDir, "Test User", "[email protected]", true); err != nil {
311 t.Fatalf("failed to create user identity: %v", err)
312 }
313 if err := createIdentity(tmpDir, "agent", "", false); err != nil {
314 t.Fatalf("failed to create agent identity: %v", err)
315 }
316
317 // Try to add a comment to non-existent bug
318 err := runAgentComment(tmpDir, "nonexistent", "Test comment", "", false)
319 if err == nil {
320 t.Error("expected error for invalid bug ID, got nil")
321 }
322
323 if !strings.Contains(err.Error(), "failed to resolve bug ID") {
324 t.Errorf("expected 'failed to resolve bug ID' error, got: %v", err)
325 }
326}
327
328// TestAgentCommentCommand_MultipleComments tests adding multiple agent comments
329func TestAgentCommentCommand_MultipleComments(t *testing.T) {
330 tmpDir := t.TempDir()
331
332 // Initialize a git repo
333 initCmd := exec.Command("git", "init")
334 initCmd.Dir = tmpDir
335 if err := initCmd.Run(); err != nil {
336 t.Fatalf("failed to init git repo: %v", err)
337 }
338
339 // Initialize identities
340 if err := createIdentity(tmpDir, "Test User", "[email protected]", true); err != nil {
341 t.Fatalf("failed to create user identity: %v", err)
342 }
343 if err := createIdentity(tmpDir, "agent", "", false); err != nil {
344 t.Fatalf("failed to create agent identity: %v", err)
345 }
346
347 // Create a bug as user
348 title := "Test Bug for Multiple Agent Comments"
349 if err := runNew(tmpDir, title, "", ""); err != nil {
350 t.Fatalf("runNew failed: %v", err)
351 }
352
353 // Get the bug ID
354 repo, err := repository.OpenGoGitRepo(tmpDir, "", nil)
355 if err != nil {
356 t.Fatalf("failed to open repo: %v", err)
357 }
358
359 var bugID string
360 for streamedBug := range bug.ReadAll(repo) {
361 if streamedBug.Err != nil {
362 t.Fatalf("failed to read bug: %v", streamedBug.Err)
363 }
364 b := streamedBug.Entity
365 snap := b.Compile()
366 if snap.Title == title {
367 bugID = b.Id().String()
368 break
369 }
370 }
371 repo.Close()
372
373 // Add multiple comments as agent
374 comments := []string{
375 "First agent comment",
376 "Second agent comment",
377 "Third agent comment with **markdown**",
378 }
379
380 for _, comment := range comments {
381 if err := runAgentComment(tmpDir, bugID, comment, "", false); err != nil {
382 t.Fatalf("runAgentComment failed: %v", err)
383 }
384 }
385
386 // Verify all comments were added
387 repo, err = repository.OpenGoGitRepo(tmpDir, "", nil)
388 if err != nil {
389 t.Fatalf("failed to open repo: %v", err)
390 }
391 defer repo.Close()
392
393 b, err := bug.Read(repo, entity.Id(bugID))
394 if err != nil {
395 t.Fatalf("failed to read bug: %v", err)
396 }
397
398 snap := b.Compile()
399 // Original + 3 comments = 4 total
400 if len(snap.Comments) != 4 {
401 t.Errorf("expected 4 comments, got %d", len(snap.Comments))
402 }
403
404 // Verify each comment (index 1-3 are our added comments)
405 for i, expected := range comments {
406 if snap.Comments[i+1].Message != expected {
407 t.Errorf("comment %d message = %q, want %q", i+1, snap.Comments[i+1].Message, expected)
408 }
409 if snap.Comments[i+1].Author.Name() != "agent" {
410 t.Errorf("comment %d author = %q, want %q", i+1, snap.Comments[i+1].Author.Name(), "agent")
411 }
412 }
413}
414
415// TestAgentCommentCommand_WithMarkdown tests that markdown is preserved
416func TestAgentCommentCommand_WithMarkdown(t *testing.T) {
417 tmpDir := t.TempDir()
418
419 // Initialize a git repo
420 initCmd := exec.Command("git", "init")
421 initCmd.Dir = tmpDir
422 if err := initCmd.Run(); err != nil {
423 t.Fatalf("failed to init git repo: %v", err)
424 }
425
426 // Initialize identities
427 if err := createIdentity(tmpDir, "Test User", "[email protected]", true); err != nil {
428 t.Fatalf("failed to create user identity: %v", err)
429 }
430 if err := createIdentity(tmpDir, "agent", "", false); err != nil {
431 t.Fatalf("failed to create agent identity: %v", err)
432 }
433
434 // Create a bug
435 title := "Test Bug for Markdown"
436 if err := runNew(tmpDir, title, "", ""); err != nil {
437 t.Fatalf("runNew failed: %v", err)
438 }
439
440 // Get the bug ID
441 repo, err := repository.OpenGoGitRepo(tmpDir, "", nil)
442 if err != nil {
443 t.Fatalf("failed to open repo: %v", err)
444 }
445
446 var bugID string
447 for streamedBug := range bug.ReadAll(repo) {
448 if streamedBug.Err != nil {
449 t.Fatalf("failed to read bug: %v", streamedBug.Err)
450 }
451 b := streamedBug.Entity
452 snap := b.Compile()
453 if snap.Title == title {
454 bugID = b.Id().String()
455 break
456 }
457 }
458 repo.Close()
459
460 // Add a comment with markdown
461 markdownComment := `# Analysis Report
462
463## Summary
464- **Status**: PASSED
465- **Duration**: 5m 32s
466
467## Details
468All tests passed successfully.`
469
470 if err := runAgentComment(tmpDir, bugID, markdownComment, "", false); err != nil {
471 t.Fatalf("runAgentComment failed: %v", err)
472 }
473
474 // Verify comment was added with markdown preserved
475 repo, err = repository.OpenGoGitRepo(tmpDir, "", nil)
476 if err != nil {
477 t.Fatalf("failed to open repo: %v", err)
478 }
479 defer repo.Close()
480
481 b, err := bug.Read(repo, entity.Id(bugID))
482 if err != nil {
483 t.Fatalf("failed to read bug: %v", err)
484 }
485
486 snap := b.Compile()
487 if len(snap.Comments) != 2 {
488 t.Errorf("expected 2 comments, got %d", len(snap.Comments))
489 }
490
491 if snap.Comments[1].Message != markdownComment {
492 t.Errorf("comment message does not match expected markdown:\ngot:\n%s\nwant:\n%s", snap.Comments[1].Message, markdownComment)
493 }
494}
495
496// TestAgentCommentCommand_WithStdin tests adding a comment using --stdin flag
497func TestAgentCommentCommand_WithStdin(t *testing.T) {
498 tmpDir := t.TempDir()
499
500 // Initialize a git repo
501 initCmd := exec.Command("git", "init")
502 initCmd.Dir = tmpDir
503 if err := initCmd.Run(); err != nil {
504 t.Fatalf("failed to init git repo: %v", err)
505 }
506
507 // Initialize identities
508 if err := createIdentity(tmpDir, "Test User", "[email protected]", true); err != nil {
509 t.Fatalf("failed to create user identity: %v", err)
510 }
511 if err := createIdentity(tmpDir, "agent", "", false); err != nil {
512 t.Fatalf("failed to create agent identity: %v", err)
513 }
514
515 // Create a bug
516 title := "Test Bug for Stdin Comment"
517 if err := runNew(tmpDir, title, "", ""); err != nil {
518 t.Fatalf("runNew failed: %v", err)
519 }
520
521 // Get the bug ID
522 repo, err := repository.OpenGoGitRepo(tmpDir, "", nil)
523 if err != nil {
524 t.Fatalf("failed to open repo: %v", err)
525 }
526
527 var bugID string
528 for streamedBug := range bug.ReadAll(repo) {
529 if streamedBug.Err != nil {
530 t.Fatalf("failed to read bug: %v", streamedBug.Err)
531 }
532 b := streamedBug.Entity
533 snap := b.Compile()
534 if snap.Title == title {
535 bugID = b.Id().String()
536 break
537 }
538 }
539 repo.Close()
540
541 // Multi-line content with markdown to test via stdin
542 stdinContent := `# Implementation Plan
543
544## Task 1: Setup
545**Files:**
546- Create: path/to/file.go
547- Test: path/to/file_test.go
548
549**Step 1: Initialize**
550Initialize the configuration:
551
552` + "```go" + `
553func Initialize() error {
554 return nil
555}
556` + "```" + `
557
558## Next Steps
5591. Run tests
5602. Verify results
5613. Document changes`
562
563 // Create a pipe to simulate stdin
564 r, w, err := os.Pipe()
565 if err != nil {
566 t.Fatalf("failed to create pipe: %v", err)
567 }
568
569 // Write content to the pipe in a goroutine
570 go func() {
571 defer w.Close()
572 w.WriteString(stdinContent)
573 }()
574
575 // Save original stdin and replace with our pipe
576 oldStdin := os.Stdin
577 os.Stdin = r
578 defer func() { os.Stdin = oldStdin }()
579
580 // Add a comment as agent using stdin flag
581 // We pass empty message and true for useStdin
582 if err := runAgentComment(tmpDir, bugID, "", "", true); err != nil {
583 t.Fatalf("runAgentComment with stdin failed: %v", err)
584 }
585
586 // Verify comment was added with stdin content
587 repo, err = repository.OpenGoGitRepo(tmpDir, "", nil)
588 if err != nil {
589 t.Fatalf("failed to open repo: %v", err)
590 }
591 defer repo.Close()
592
593 b, err := bug.Read(repo, entity.Id(bugID))
594 if err != nil {
595 t.Fatalf("failed to read bug: %v", err)
596 }
597
598 snap := b.Compile()
599 if len(snap.Comments) != 2 {
600 t.Errorf("expected 2 comments, got %d", len(snap.Comments))
601 }
602
603 // Content should be trimmed but preserve markdown
604 expectedContent := strings.TrimSpace(stdinContent)
605 if snap.Comments[1].Message != expectedContent {
606 t.Errorf("comment message does not match expected stdin content:\ngot:\n%s\nwant:\n%s", snap.Comments[1].Message, expectedContent)
607 }
608
609 // Verify agent is the author
610 if snap.Comments[1].Author.Name() != "agent" {
611 t.Errorf("comment author = %q, want %q", snap.Comments[1].Author.Name(), "agent")
612 }
613}