
- Add comprehensive Git workflow automation tools - Include branch management utilities - Add commit helpers with conventional commit support - Implement GitHub integration for PR management - Add configuration management system - Include comprehensive test coverage - Add professional documentation and examples
518 lines
13 KiB
Go
518 lines
13 KiB
Go
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/iwasforcedtobehere/git-automation-cli/internal/github"
|
|
"github.com/iwasforcedtobehere/git-automation-cli/internal/validation"
|
|
)
|
|
|
|
func TestPromptForBody(t *testing.T) {
|
|
// Save original stdin
|
|
originalStdin := os.Stdin
|
|
defer func() {
|
|
os.Stdin = originalStdin
|
|
}()
|
|
|
|
// Create a pipe to simulate user input
|
|
r, w, _ := os.Pipe()
|
|
os.Stdin = r
|
|
|
|
// Write test input
|
|
go func() {
|
|
w.WriteString("This is the first line\n")
|
|
w.WriteString("This is the second line\n")
|
|
w.WriteString("\n") // First empty line
|
|
w.WriteString("\n") // Second empty line to finish
|
|
w.Close()
|
|
}()
|
|
|
|
// Capture output
|
|
oldStdout := os.Stdout
|
|
stdoutR, stdoutW, _ := os.Pipe()
|
|
os.Stdout = stdoutW
|
|
|
|
// Call the function
|
|
body := promptForBody()
|
|
|
|
// Restore stdout
|
|
stdoutW.Close()
|
|
os.Stdout = oldStdout
|
|
out, _ := io.ReadAll(stdoutR)
|
|
|
|
// Check result
|
|
expectedBody := "This is the first line\nThis is the second line"
|
|
if body != expectedBody {
|
|
t.Errorf("promptForBody() = %q, want %q", body, expectedBody)
|
|
}
|
|
|
|
// Check that prompt was displayed
|
|
output := string(out)
|
|
if !strings.Contains(output, "Enter pull request body") {
|
|
t.Errorf("Expected prompt to be displayed, got %q", output)
|
|
}
|
|
}
|
|
|
|
func TestReadLine(t *testing.T) {
|
|
// Save original stdin
|
|
originalStdin := os.Stdin
|
|
defer func() {
|
|
os.Stdin = originalStdin
|
|
}()
|
|
|
|
// Create a pipe to simulate user input
|
|
r, w, _ := os.Pipe()
|
|
os.Stdin = r
|
|
|
|
// Write test input
|
|
go func() {
|
|
w.WriteString("test line\n")
|
|
w.Close()
|
|
}()
|
|
|
|
// Call the function
|
|
line := readLine()
|
|
|
|
// Check result
|
|
expectedLine := "test line"
|
|
if line != expectedLine {
|
|
t.Errorf("readLine() = %q, want %q", line, expectedLine)
|
|
}
|
|
}
|
|
|
|
func TestParseInt(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
expected int
|
|
}{
|
|
{
|
|
name: "Valid number",
|
|
input: "123",
|
|
expected: 123,
|
|
},
|
|
{
|
|
name: "Invalid number",
|
|
input: "abc",
|
|
expected: 0,
|
|
},
|
|
{
|
|
name: "Empty string",
|
|
input: "",
|
|
expected: 0,
|
|
},
|
|
{
|
|
name: "Negative number",
|
|
input: "-456",
|
|
expected: -456,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := parseInt(tt.input)
|
|
if result != tt.expected {
|
|
t.Errorf("parseInt(%q) = %d, want %d", tt.input, result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// MockGitService is a mock implementation of the git service
|
|
type MockGitService struct {
|
|
currentBranchFunc func(ctx context.Context) (string, error)
|
|
getRemoteURLFunc func(ctx context.Context, remote string) (string, error)
|
|
}
|
|
|
|
func (m *MockGitService) CurrentBranch(ctx context.Context) (string, error) {
|
|
if m.currentBranchFunc != nil {
|
|
return m.currentBranchFunc(ctx)
|
|
}
|
|
return "feature/test", nil
|
|
}
|
|
|
|
func (m *MockGitService) GetRemoteURL(ctx context.Context, remote string) (string, error) {
|
|
if m.getRemoteURLFunc != nil {
|
|
return m.getRemoteURLFunc(ctx, remote)
|
|
}
|
|
return "https://github.com/owner/repo.git", nil
|
|
}
|
|
|
|
// MockGitHubService is a mock implementation of the GitHub service
|
|
type MockGitHubService struct {
|
|
newClientFunc func(ctx context.Context) (*github.Client, error)
|
|
isGitHubURLFunc func(url string) bool
|
|
parseGitHubURLFunc func(url string) (string, string, error)
|
|
createPullRequest func(c *github.Client, ctx context.Context, pr *github.PullRequestRequest) (*github.PullRequest, error)
|
|
getPullRequests func(c *github.Client, ctx context.Context, state string) ([]github.PullRequest, error)
|
|
mergePullRequest func(c *github.Client, ctx context.Context, number int, mergeRequest *github.MergePullRequestRequest) (*github.MergePullRequestResponse, error)
|
|
}
|
|
|
|
func (m *MockGitHubService) NewClient(ctx context.Context) (*github.Client, error) {
|
|
if m.newClientFunc != nil {
|
|
return m.newClientFunc(ctx)
|
|
}
|
|
return &github.Client{}, nil
|
|
}
|
|
|
|
func (m *MockGitHubService) IsGitHubURL(url string) bool {
|
|
if m.isGitHubURLFunc != nil {
|
|
return m.isGitHubURLFunc(url)
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (m *MockGitHubService) ParseGitHubURL(url string) (string, string, error) {
|
|
if m.parseGitHubURLFunc != nil {
|
|
return m.parseGitHubURLFunc(url)
|
|
}
|
|
return "owner", "repo", nil
|
|
}
|
|
|
|
// MockValidationService is a mock implementation of the validation service
|
|
type MockValidationService struct {
|
|
validateGitRepositoryFunc func(ctx context.Context) *validation.ValidationResult
|
|
}
|
|
|
|
func (m *MockValidationService) ValidateGitRepository(ctx context.Context) *validation.ValidationResult {
|
|
if m.validateGitRepositoryFunc != nil {
|
|
return m.validateGitRepositoryFunc(ctx)
|
|
}
|
|
return &validation.ValidationResult{IsValid: true}
|
|
}
|
|
|
|
func TestPRCreateCmd(t *testing.T) {
|
|
// Create mock services
|
|
mockGitService := &MockGitService{}
|
|
mockGitHubService := &MockGitHubService{}
|
|
mockValidationService := &MockValidationService{}
|
|
|
|
// Set up mock functions
|
|
mockGitHubService.createPullRequest = func(c *github.Client, ctx context.Context, pr *github.PullRequestRequest) (*github.PullRequest, error) {
|
|
return &github.PullRequest{
|
|
ID: 1,
|
|
Number: 123,
|
|
Title: pr.Title,
|
|
Body: pr.Body,
|
|
HTMLURL: "https://github.com/owner/repo/pull/123",
|
|
}, nil
|
|
}
|
|
|
|
// Temporarily redirect stdout to capture output
|
|
oldStdout := os.Stdout
|
|
r, w, _ := os.Pipe()
|
|
os.Stdout = w
|
|
|
|
// Create a command
|
|
cmd := &cobra.Command{
|
|
Use: "test",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
// Use mock services instead of real ones
|
|
ctx := cmd.Context()
|
|
|
|
// Validate Git repository
|
|
if validationResult := mockValidationService.ValidateGitRepository(ctx); !validationResult.IsValid {
|
|
return fmt.Errorf(validationResult.GetErrors())
|
|
}
|
|
|
|
// Get current branch
|
|
currentBranch, err := mockGitService.CurrentBranch(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get current branch: %w", err)
|
|
}
|
|
|
|
// Get base branch
|
|
base := "main"
|
|
|
|
// Check if current branch is the same as base branch
|
|
if currentBranch == base {
|
|
return fmt.Errorf("cannot create pull request from %s branch to itself", base)
|
|
}
|
|
|
|
// Create GitHub client
|
|
client, err := mockGitHubService.NewClient(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create GitHub client: %w", err)
|
|
}
|
|
|
|
// Get remote URL
|
|
remoteURL, err := mockGitService.GetRemoteURL(ctx, "origin")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get remote URL: %w", err)
|
|
}
|
|
|
|
// Parse GitHub URL
|
|
if !mockGitHubService.IsGitHubURL(remoteURL) {
|
|
return fmt.Errorf("remote URL is not a GitHub URL: %s", remoteURL)
|
|
}
|
|
|
|
owner, repo, err := mockGitHubService.ParseGitHubURL(remoteURL)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse GitHub URL: %w", err)
|
|
}
|
|
|
|
// Set repository on client
|
|
client.SetRepository(owner, repo)
|
|
|
|
// Create pull request
|
|
pr := &github.PullRequestRequest{
|
|
Title: strings.Join(args, " "),
|
|
Body: "Test body",
|
|
Head: currentBranch,
|
|
Base: base,
|
|
Draft: false,
|
|
}
|
|
|
|
pullRequest, err := mockGitHubService.createPullRequest(client, ctx, pr)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create pull request: %w", err)
|
|
}
|
|
|
|
fmt.Printf("Created pull request: %s\n", pullRequest.HTMLURL)
|
|
return nil
|
|
},
|
|
}
|
|
|
|
// Set args
|
|
cmd.SetArgs([]string{"Test PR"})
|
|
|
|
// Execute command
|
|
err := cmd.Execute()
|
|
|
|
// Restore stdout
|
|
w.Close()
|
|
os.Stdout = oldStdout
|
|
out, _ := io.ReadAll(r)
|
|
|
|
// Check error
|
|
if err != nil {
|
|
t.Errorf("Execute() error = %v", err)
|
|
return
|
|
}
|
|
|
|
// Check output
|
|
output := string(out)
|
|
expected := "Created pull request: https://github.com/owner/repo/pull/123\n"
|
|
if output != expected {
|
|
t.Errorf("Execute() output = %q, want %q", output, expected)
|
|
}
|
|
}
|
|
|
|
func TestPRListCmd(t *testing.T) {
|
|
// Create mock services
|
|
mockGitService := &MockGitService{}
|
|
mockGitHubService := &MockGitHubService{}
|
|
mockValidationService := &MockValidationService{}
|
|
|
|
// Set up mock functions
|
|
mockGitHubService.getPullRequests = func(c *github.Client, ctx context.Context, state string) ([]github.PullRequest, error) {
|
|
return []github.PullRequest{
|
|
{
|
|
ID: 1,
|
|
Number: 123,
|
|
Title: "Test PR 1",
|
|
User: github.User{Login: "user1"},
|
|
},
|
|
{
|
|
ID: 2,
|
|
Number: 124,
|
|
Title: "Test PR 2",
|
|
User: github.User{Login: "user2"},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// Temporarily redirect stdout to capture output
|
|
oldStdout := os.Stdout
|
|
r, w, _ := os.Pipe()
|
|
os.Stdout = w
|
|
|
|
// Create a command
|
|
cmd := &cobra.Command{
|
|
Use: "test",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
// Use mock services instead of real ones
|
|
ctx := cmd.Context()
|
|
|
|
// Get state flag
|
|
state := "open"
|
|
|
|
// Validate Git repository
|
|
if validationResult := mockValidationService.ValidateGitRepository(ctx); !validationResult.IsValid {
|
|
return fmt.Errorf(validationResult.GetErrors())
|
|
}
|
|
|
|
// Create GitHub client
|
|
client, err := mockGitHubService.NewClient(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create GitHub client: %w", err)
|
|
}
|
|
|
|
// Get remote URL
|
|
remoteURL, err := mockGitService.GetRemoteURL(ctx, "origin")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get remote URL: %w", err)
|
|
}
|
|
|
|
// Parse GitHub URL
|
|
if !mockGitHubService.IsGitHubURL(remoteURL) {
|
|
return fmt.Errorf("remote URL is not a GitHub URL: %s", remoteURL)
|
|
}
|
|
|
|
owner, repo, err := mockGitHubService.ParseGitHubURL(remoteURL)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse GitHub URL: %w", err)
|
|
}
|
|
|
|
// Set repository on client
|
|
client.SetRepository(owner, repo)
|
|
|
|
// Get pull requests
|
|
pullRequests, err := mockGitHubService.getPullRequests(client, ctx, state)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get pull requests: %w", err)
|
|
}
|
|
|
|
// Print pull requests
|
|
if len(pullRequests) == 0 {
|
|
fmt.Printf("No %s pull requests found\n", state)
|
|
return nil
|
|
}
|
|
|
|
fmt.Printf("%s pull requests:\n", strings.Title(state))
|
|
for _, pr := range pullRequests {
|
|
fmt.Printf("#%d: %s (%s)\n", pr.Number, pr.Title, pr.User.Login)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
// Execute command
|
|
err := cmd.Execute()
|
|
|
|
// Restore stdout
|
|
w.Close()
|
|
os.Stdout = oldStdout
|
|
out, _ := io.ReadAll(r)
|
|
|
|
// Check error
|
|
if err != nil {
|
|
t.Errorf("Execute() error = %v", err)
|
|
return
|
|
}
|
|
|
|
// Check output
|
|
output := string(out)
|
|
expected := "Open pull requests:\n#123: Test PR 1 (user1)\n#124: Test PR 2 (user2)\n"
|
|
if output != expected {
|
|
t.Errorf("Execute() output = %q, want %q", output, expected)
|
|
}
|
|
}
|
|
|
|
func TestPRMergeCmd(t *testing.T) {
|
|
// Create mock services
|
|
mockGitService := &MockGitService{}
|
|
mockGitHubService := &MockGitHubService{}
|
|
mockValidationService := &MockValidationService{}
|
|
|
|
// Set up mock functions
|
|
mockGitHubService.mergePullRequest = func(c *github.Client, ctx context.Context, number int, mergeRequest *github.MergePullRequestRequest) (*github.MergePullRequestResponse, error) {
|
|
return &github.MergePullRequestResponse{
|
|
SHA: "abc123",
|
|
Merged: true,
|
|
Message: "Pull Request successfully merged",
|
|
}, nil
|
|
}
|
|
|
|
// Temporarily redirect stdout to capture output
|
|
oldStdout := os.Stdout
|
|
r, w, _ := os.Pipe()
|
|
os.Stdout = w
|
|
|
|
// Create a command
|
|
cmd := &cobra.Command{
|
|
Use: "test",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
// Use mock services instead of real ones
|
|
ctx := cmd.Context()
|
|
number := args[0]
|
|
|
|
// Validate Git repository
|
|
if validationResult := mockValidationService.ValidateGitRepository(ctx); !validationResult.IsValid {
|
|
return fmt.Errorf(validationResult.GetErrors())
|
|
}
|
|
|
|
// Create GitHub client
|
|
client, err := mockGitHubService.NewClient(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create GitHub client: %w", err)
|
|
}
|
|
|
|
// Get remote URL
|
|
remoteURL, err := mockGitService.GetRemoteURL(ctx, "origin")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get remote URL: %w", err)
|
|
}
|
|
|
|
// Parse GitHub URL
|
|
if !mockGitHubService.IsGitHubURL(remoteURL) {
|
|
return fmt.Errorf("remote URL is not a GitHub URL: %s", remoteURL)
|
|
}
|
|
|
|
owner, repo, err := mockGitHubService.ParseGitHubURL(remoteURL)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse GitHub URL: %w", err)
|
|
}
|
|
|
|
// Set repository on client
|
|
client.SetRepository(owner, repo)
|
|
|
|
// Get merge method
|
|
method := "merge"
|
|
|
|
// Merge pull request
|
|
mergeRequest := &github.MergePullRequestRequest{
|
|
MergeMethod: method,
|
|
}
|
|
|
|
mergeResponse, err := mockGitHubService.mergePullRequest(client, ctx, parseInt(number), mergeRequest)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to merge pull request: %w", err)
|
|
}
|
|
|
|
fmt.Printf("Merged pull request: %s\n", mergeResponse.SHA)
|
|
return nil
|
|
},
|
|
}
|
|
|
|
// Set args
|
|
cmd.SetArgs([]string{"123"})
|
|
|
|
// Execute command
|
|
err := cmd.Execute()
|
|
|
|
// Restore stdout
|
|
w.Close()
|
|
os.Stdout = oldStdout
|
|
out, _ := io.ReadAll(r)
|
|
|
|
// Check error
|
|
if err != nil {
|
|
t.Errorf("Execute() error = %v", err)
|
|
return
|
|
}
|
|
|
|
// Check output
|
|
output := string(out)
|
|
expected := "Merged pull request: abc123\n"
|
|
if output != expected {
|
|
t.Errorf("Execute() output = %q, want %q", output, expected)
|
|
}
|
|
} |