//go:build integration package main import ( "os" "os/exec" "strings" "testing" "github.com/git-bug/git-bug/entities/bug" "github.com/git-bug/git-bug/entity" "github.com/git-bug/git-bug/repository" ) // TestAgentCommentCommand_WithFlag adds a comment as agent using the message flag func TestAgentCommentCommand_WithFlag(t *testing.T) { tmpDir := t.TempDir() // Initialize a git repo initCmd := exec.Command("git", "init") initCmd.Dir = tmpDir if err := initCmd.Run(); err != nil { t.Fatalf("failed to init git repo: %v", err) } // Initialize git-bug with both user and agent identities if err := createIdentity(tmpDir, "Test User", "test@example.com", true); err != nil { t.Fatalf("failed to create user identity: %v", err) } if err := createIdentity(tmpDir, "agent", "", false); err != nil { t.Fatalf("failed to create agent identity: %v", err) } // Create a bug first as user title := "Test Bug for Agent Comment" message := "Initial bug description" if err := runNew(tmpDir, title, message, "", "", ""); err != nil { t.Fatalf("runNew failed: %v", err) } // Get the bug ID repo, err := repository.OpenGoGitRepo(tmpDir, "", nil) if err != nil { t.Fatalf("failed to open repo: %v", err) } var bugID string for streamedBug := range bug.ReadAll(repo) { if streamedBug.Err != nil { t.Fatalf("failed to read bug: %v", streamedBug.Err) } b := streamedBug.Entity snap := b.Compile() if snap.Title == title { bugID = b.Id().String() break } } repo.Close() if bugID == "" { t.Fatal("could not find created bug") } // Add a comment as agent using the flag commentText := "This is an automated agent comment" if err := runAgentComment(tmpDir, bugID, commentText, "", false); err != nil { t.Fatalf("runAgentComment failed: %v", err) } // Verify comment was added with agent as author repo, err = repository.OpenGoGitRepo(tmpDir, "", nil) if err != nil { t.Fatalf("failed to open repo: %v", err) } defer repo.Close() b, err := bug.Read(repo, entity.Id(bugID)) if err != nil { t.Fatalf("failed to read bug: %v", err) } snap := b.Compile() if len(snap.Comments) != 2 { t.Errorf("expected 2 comments (original + new), got %d", len(snap.Comments)) } // Check the second comment (index 1) is our agent comment if snap.Comments[1].Message != commentText { t.Errorf("comment message = %q, want %q", snap.Comments[1].Message, commentText) } if snap.Comments[1].Author.Name() != "agent" { t.Errorf("comment author = %q, want %q", snap.Comments[1].Author.Name(), "agent") } if snap.Comments[1].Author.Email() != "" { t.Errorf("comment author email = %q, want empty", snap.Comments[1].Author.Email()) } } // TestAgentCommentCommand_WithShortID adds a comment as agent using short bug ID func TestAgentCommentCommand_WithShortID(t *testing.T) { tmpDir := t.TempDir() // Initialize a git repo initCmd := exec.Command("git", "init") initCmd.Dir = tmpDir if err := initCmd.Run(); err != nil { t.Fatalf("failed to init git repo: %v", err) } // Initialize identities if err := createIdentity(tmpDir, "Test User", "test@example.com", true); err != nil { t.Fatalf("failed to create user identity: %v", err) } if err := createIdentity(tmpDir, "agent", "", false); err != nil { t.Fatalf("failed to create agent identity: %v", err) } // Create a bug title := "Test Bug for Agent Short ID" if err := runNew(tmpDir, title, "", "", "", ""); err != nil { t.Fatalf("runNew failed: %v", err) } // Get the bug ID and short ID repo, err := repository.OpenGoGitRepo(tmpDir, "", nil) if err != nil { t.Fatalf("failed to open repo: %v", err) } var bugID, shortID string for streamedBug := range bug.ReadAll(repo) { if streamedBug.Err != nil { t.Fatalf("failed to read bug: %v", streamedBug.Err) } b := streamedBug.Entity snap := b.Compile() if snap.Title == title { bugID = b.Id().String() if len(bugID) > 7 { shortID = bugID[:7] } else { shortID = bugID } break } } repo.Close() if shortID == "" { t.Fatal("could not find created bug") } // Add a comment as agent using short ID commentText := "Agent comment via short ID" if err := runAgentComment(tmpDir, shortID, commentText, "", false); err != nil { t.Fatalf("runAgentComment with short ID failed: %v", err) } // Verify comment was added repo, err = repository.OpenGoGitRepo(tmpDir, "", nil) if err != nil { t.Fatalf("failed to open repo: %v", err) } defer repo.Close() b, err := bug.Read(repo, entity.Id(bugID)) if err != nil { t.Fatalf("failed to read bug: %v", err) } snap := b.Compile() if len(snap.Comments) != 2 { t.Errorf("expected 2 comments, got %d", len(snap.Comments)) } if snap.Comments[1].Message != commentText { t.Errorf("comment message = %q, want %q", snap.Comments[1].Message, commentText) } if snap.Comments[1].Author.Name() != "agent" { t.Errorf("comment author = %q, want %q", snap.Comments[1].Author.Name(), "agent") } } // TestAgentCommentCommand_MissingAgentIdentity tests error when agent not initialized func TestAgentCommentCommand_MissingAgentIdentity(t *testing.T) { tmpDir := t.TempDir() // Initialize a git repo initCmd := exec.Command("git", "init") initCmd.Dir = tmpDir if err := initCmd.Run(); err != nil { t.Fatalf("failed to init git repo: %v", err) } // Only create user identity, NOT agent if err := createIdentity(tmpDir, "Test User", "test@example.com", true); err != nil { t.Fatalf("failed to create user identity: %v", err) } // Create a bug as user title := "Test Bug" if err := runNew(tmpDir, title, "", "", "", ""); err != nil { t.Fatalf("runNew failed: %v", err) } // Get the bug ID repo, err := repository.OpenGoGitRepo(tmpDir, "", nil) if err != nil { t.Fatalf("failed to open repo: %v", err) } var bugID string for streamedBug := range bug.ReadAll(repo) { if streamedBug.Err != nil { t.Fatalf("failed to read bug: %v", streamedBug.Err) } b := streamedBug.Entity snap := b.Compile() if snap.Title == title { bugID = b.Id().String() break } } repo.Close() // Try to add a comment as agent (should fail) err = runAgentComment(tmpDir, bugID, "Test comment", "", false) if err == nil { t.Error("expected error when agent identity not found, got nil") } if !strings.Contains(err.Error(), "agent identity not found") { t.Errorf("expected 'agent identity not found' error, got: %v", err) } } // TestAgentCommentCommand_EmptyMessageError tests error for empty message func TestAgentCommentCommand_EmptyMessageError(t *testing.T) { tmpDir := t.TempDir() // Initialize a git repo initCmd := exec.Command("git", "init") initCmd.Dir = tmpDir if err := initCmd.Run(); err != nil { t.Fatalf("failed to init git repo: %v", err) } // Initialize identities if err := createIdentity(tmpDir, "Test User", "test@example.com", true); err != nil { t.Fatalf("failed to create user identity: %v", err) } if err := createIdentity(tmpDir, "agent", "", false); err != nil { t.Fatalf("failed to create agent identity: %v", err) } // Create a bug title := "Test Bug" if err := runNew(tmpDir, title, "", "", "", ""); err != nil { t.Fatalf("runNew failed: %v", err) } // Get the bug ID repo, err := repository.OpenGoGitRepo(tmpDir, "", nil) if err != nil { t.Fatalf("failed to open repo: %v", err) } var bugID string for streamedBug := range bug.ReadAll(repo) { if streamedBug.Err != nil { t.Fatalf("failed to read bug: %v", streamedBug.Err) } b := streamedBug.Entity snap := b.Compile() if snap.Title == title { bugID = b.Id().String() break } } repo.Close() // Try with empty message err = runAgentComment(tmpDir, bugID, " ", "", false) if err == nil { t.Error("expected error for empty message, got nil") } if !strings.Contains(err.Error(), "message cannot be empty") { t.Errorf("expected 'message cannot be empty' error, got: %v", err) } } // TestAgentCommentCommand_InvalidBugID tests error for invalid bug ID func TestAgentCommentCommand_InvalidBugID(t *testing.T) { tmpDir := t.TempDir() // Initialize a git repo initCmd := exec.Command("git", "init") initCmd.Dir = tmpDir if err := initCmd.Run(); err != nil { t.Fatalf("failed to init git repo: %v", err) } // Initialize identities if err := createIdentity(tmpDir, "Test User", "test@example.com", true); err != nil { t.Fatalf("failed to create user identity: %v", err) } if err := createIdentity(tmpDir, "agent", "", false); err != nil { t.Fatalf("failed to create agent identity: %v", err) } // Try to add a comment to non-existent bug err := runAgentComment(tmpDir, "nonexistent", "Test comment", "", false) if err == nil { t.Error("expected error for invalid bug ID, got nil") } if !strings.Contains(err.Error(), "failed to resolve bug ID") { t.Errorf("expected 'failed to resolve bug ID' error, got: %v", err) } } // TestAgentCommentCommand_MultipleComments tests adding multiple agent comments func TestAgentCommentCommand_MultipleComments(t *testing.T) { tmpDir := t.TempDir() // Initialize a git repo initCmd := exec.Command("git", "init") initCmd.Dir = tmpDir if err := initCmd.Run(); err != nil { t.Fatalf("failed to init git repo: %v", err) } // Initialize identities if err := createIdentity(tmpDir, "Test User", "test@example.com", true); err != nil { t.Fatalf("failed to create user identity: %v", err) } if err := createIdentity(tmpDir, "agent", "", false); err != nil { t.Fatalf("failed to create agent identity: %v", err) } // Create a bug as user title := "Test Bug for Multiple Agent Comments" if err := runNew(tmpDir, title, "", "", "", ""); err != nil { t.Fatalf("runNew failed: %v", err) } // Get the bug ID repo, err := repository.OpenGoGitRepo(tmpDir, "", nil) if err != nil { t.Fatalf("failed to open repo: %v", err) } var bugID string for streamedBug := range bug.ReadAll(repo) { if streamedBug.Err != nil { t.Fatalf("failed to read bug: %v", streamedBug.Err) } b := streamedBug.Entity snap := b.Compile() if snap.Title == title { bugID = b.Id().String() break } } repo.Close() // Add multiple comments as agent comments := []string{ "First agent comment", "Second agent comment", "Third agent comment with **markdown**", } for _, comment := range comments { if err := runAgentComment(tmpDir, bugID, comment, "", false); err != nil { t.Fatalf("runAgentComment failed: %v", err) } } // Verify all comments were added repo, err = repository.OpenGoGitRepo(tmpDir, "", nil) if err != nil { t.Fatalf("failed to open repo: %v", err) } defer repo.Close() b, err := bug.Read(repo, entity.Id(bugID)) if err != nil { t.Fatalf("failed to read bug: %v", err) } snap := b.Compile() // Original + 3 comments = 4 total if len(snap.Comments) != 4 { t.Errorf("expected 4 comments, got %d", len(snap.Comments)) } // Verify each comment (index 1-3 are our added comments) for i, expected := range comments { if snap.Comments[i+1].Message != expected { t.Errorf("comment %d message = %q, want %q", i+1, snap.Comments[i+1].Message, expected) } if snap.Comments[i+1].Author.Name() != "agent" { t.Errorf("comment %d author = %q, want %q", i+1, snap.Comments[i+1].Author.Name(), "agent") } } } // TestAgentCommentCommand_WithMarkdown tests that markdown is preserved func TestAgentCommentCommand_WithMarkdown(t *testing.T) { tmpDir := t.TempDir() // Initialize a git repo initCmd := exec.Command("git", "init") initCmd.Dir = tmpDir if err := initCmd.Run(); err != nil { t.Fatalf("failed to init git repo: %v", err) } // Initialize identities if err := createIdentity(tmpDir, "Test User", "test@example.com", true); err != nil { t.Fatalf("failed to create user identity: %v", err) } if err := createIdentity(tmpDir, "agent", "", false); err != nil { t.Fatalf("failed to create agent identity: %v", err) } // Create a bug title := "Test Bug for Markdown" if err := runNew(tmpDir, title, "", "", "", ""); err != nil { t.Fatalf("runNew failed: %v", err) } // Get the bug ID repo, err := repository.OpenGoGitRepo(tmpDir, "", nil) if err != nil { t.Fatalf("failed to open repo: %v", err) } var bugID string for streamedBug := range bug.ReadAll(repo) { if streamedBug.Err != nil { t.Fatalf("failed to read bug: %v", streamedBug.Err) } b := streamedBug.Entity snap := b.Compile() if snap.Title == title { bugID = b.Id().String() break } } repo.Close() // Add a comment with markdown markdownComment := `# Analysis Report ## Summary - **Status**: PASSED - **Duration**: 5m 32s ## Details All tests passed successfully.` if err := runAgentComment(tmpDir, bugID, markdownComment, "", false); err != nil { t.Fatalf("runAgentComment failed: %v", err) } // Verify comment was added with markdown preserved repo, err = repository.OpenGoGitRepo(tmpDir, "", nil) if err != nil { t.Fatalf("failed to open repo: %v", err) } defer repo.Close() b, err := bug.Read(repo, entity.Id(bugID)) if err != nil { t.Fatalf("failed to read bug: %v", err) } snap := b.Compile() if len(snap.Comments) != 2 { t.Errorf("expected 2 comments, got %d", len(snap.Comments)) } if snap.Comments[1].Message != markdownComment { t.Errorf("comment message does not match expected markdown:\ngot:\n%s\nwant:\n%s", snap.Comments[1].Message, markdownComment) } } // TestAgentCommentCommand_WithStdin tests adding a comment using --stdin flag func TestAgentCommentCommand_WithStdin(t *testing.T) { tmpDir := t.TempDir() // Initialize a git repo initCmd := exec.Command("git", "init") initCmd.Dir = tmpDir if err := initCmd.Run(); err != nil { t.Fatalf("failed to init git repo: %v", err) } // Initialize identities if err := createIdentity(tmpDir, "Test User", "test@example.com", true); err != nil { t.Fatalf("failed to create user identity: %v", err) } if err := createIdentity(tmpDir, "agent", "", false); err != nil { t.Fatalf("failed to create agent identity: %v", err) } // Create a bug title := "Test Bug for Stdin Comment" if err := runNew(tmpDir, title, "", "", "", ""); err != nil { t.Fatalf("runNew failed: %v", err) } // Get the bug ID repo, err := repository.OpenGoGitRepo(tmpDir, "", nil) if err != nil { t.Fatalf("failed to open repo: %v", err) } var bugID string for streamedBug := range bug.ReadAll(repo) { if streamedBug.Err != nil { t.Fatalf("failed to read bug: %v", streamedBug.Err) } b := streamedBug.Entity snap := b.Compile() if snap.Title == title { bugID = b.Id().String() break } } repo.Close() // Multi-line content with markdown to test via stdin stdinContent := `# Implementation Plan ## Task 1: Setup **Files:** - Create: path/to/file.go - Test: path/to/file_test.go **Step 1: Initialize** Initialize the configuration: ` + "```go" + ` func Initialize() error { return nil } ` + "```" + ` ## Next Steps 1. Run tests 2. Verify results 3. Document changes` // Create a pipe to simulate stdin r, w, err := os.Pipe() if err != nil { t.Fatalf("failed to create pipe: %v", err) } // Write content to the pipe in a goroutine go func() { defer w.Close() w.WriteString(stdinContent) }() // Save original stdin and replace with our pipe oldStdin := os.Stdin os.Stdin = r defer func() { os.Stdin = oldStdin }() // Add a comment as agent using stdin flag // We pass empty message and true for useStdin if err := runAgentComment(tmpDir, bugID, "", "", true); err != nil { t.Fatalf("runAgentComment with stdin failed: %v", err) } // Verify comment was added with stdin content repo, err = repository.OpenGoGitRepo(tmpDir, "", nil) if err != nil { t.Fatalf("failed to open repo: %v", err) } defer repo.Close() b, err := bug.Read(repo, entity.Id(bugID)) if err != nil { t.Fatalf("failed to read bug: %v", err) } snap := b.Compile() if len(snap.Comments) != 2 { t.Errorf("expected 2 comments, got %d", len(snap.Comments)) } // Content should be trimmed but preserve markdown expectedContent := strings.TrimSpace(stdinContent) if snap.Comments[1].Message != expectedContent { t.Errorf("comment message does not match expected stdin content:\ngot:\n%s\nwant:\n%s", snap.Comments[1].Message, expectedContent) } // Verify agent is the author if snap.Comments[1].Author.Name() != "agent" { t.Errorf("comment author = %q, want %q", snap.Comments[1].Author.Name(), "agent") } }