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) } }