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}