aboutsummaryrefslogblamecommitdiff
path: root/cmd/pr-analyzer/analyzer_test.go
blob: d2c3041836b9843a9807a48b6ce7091df1620b4f (plain) (tree)








































































































































































































































                                                                                                                    
package main

import (
	"testing"
	"time"

	"go.fcuny.net/x/internal/github"
)

func TestAnalyzePRs(t *testing.T) {
	startDate := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
	endDate := time.Date(2024, 1, 31, 0, 0, 0, 0, time.UTC)
	a := NewAnalyzer(startDate, endDate)

	tests := []struct {
		name     string
		prs      []github.PullRequest
		reviews  map[int][]github.Review
		comments map[int][]github.ReviewComment
		want     []ReviewerMetrics
	}{
		{
			name: "single PR with immediate approval",
			prs: []github.PullRequest{
				{
					Number:    1,
					CreatedAt: time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC),
					User: github.User{
						Login: "author1",
					},
				},
			},
			reviews: map[int][]github.Review{
				1: {
					{
						ID:          1,
						User:        github.User{Login: "reviewer1"},
						State:       "APPROVED",
						SubmittedAt: time.Date(2024, 1, 15, 10, 30, 0, 0, time.UTC),
					},
				},
			},
			comments: map[int][]github.ReviewComment{},
			want: []ReviewerMetrics{
				{
					Username:           "reviewer1",
					PRsReviewed:        1,
					AverageReviewSpeed: 0.5, // 30 minutes
					AverageComments:    0,
					ImmediateApprovals: 100.0, // 100% immediate approvals
				},
			},
		},
		{
			name: "PR with comments and changes requested",
			prs: []github.PullRequest{
				{
					Number:    2,
					CreatedAt: time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC),
					User: github.User{
						Login: "author2",
					},
				},
			},
			reviews: map[int][]github.Review{
				2: {
					{
						ID:          2,
						User:        github.User{Login: "reviewer2"},
						State:       "CHANGES_REQUESTED",
						SubmittedAt: time.Date(2024, 1, 15, 11, 0, 0, 0, time.UTC),
					},
				},
			},
			comments: map[int][]github.ReviewComment{
				2: {
					{
						ID:        3,
						ReviewID:  2,
						User:      github.User{Login: "reviewer2"},
						CreatedAt: time.Date(2024, 1, 15, 11, 0, 0, 0, time.UTC),
						Path:      "file1.go",
						Line:      10,
					},
					{
						ID:        4,
						ReviewID:  2,
						User:      github.User{Login: "reviewer2"},
						CreatedAt: time.Date(2024, 1, 15, 11, 0, 0, 0, time.UTC),
						Path:      "file2.go",
						Line:      20,
					},
				},
			},
			want: []ReviewerMetrics{
				{
					Username:           "reviewer2",
					PRsReviewed:        1,
					AverageReviewSpeed: 1.0, // 1 hour
					AverageComments:    2.0,
					ImmediateApprovals: 0.0, // 0% immediate approvals
				},
			},
		},
		{
			name: "multiple PRs with same reviewer",
			prs: []github.PullRequest{
				{
					Number:    3,
					CreatedAt: time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC),
					User: github.User{
						Login: "author3",
					},
				},
				{
					Number:    4,
					CreatedAt: time.Date(2024, 1, 15, 11, 0, 0, 0, time.UTC),
					User: github.User{
						Login: "author4",
					},
				},
			},
			reviews: map[int][]github.Review{
				3: {
					{
						ID:          5,
						User:        github.User{Login: "reviewer3"},
						State:       "APPROVED",
						SubmittedAt: time.Date(2024, 1, 15, 10, 30, 0, 0, time.UTC),
					},
				},
				4: {
					{
						ID:          6,
						User:        github.User{Login: "reviewer3"},
						State:       "APPROVED",
						SubmittedAt: time.Date(2024, 1, 15, 11, 30, 0, 0, time.UTC),
					},
				},
			},
			comments: map[int][]github.ReviewComment{},
			want: []ReviewerMetrics{
				{
					Username:           "reviewer3",
					PRsReviewed:        2,
					AverageReviewSpeed: 0.5, // 30 minutes average
					AverageComments:    0,
					ImmediateApprovals: 100.0, // 100% immediate approvals
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			prData := make([]PRData, len(tt.prs))
			for i, pr := range tt.prs {
				reviews := tt.reviews[pr.Number]
				comments := tt.comments[pr.Number]

				commentsByReview := make(map[int64][]CommentData)
				for _, comment := range comments {
					commentsByReview[comment.ReviewID] = append(
						commentsByReview[comment.ReviewID],
						CommentData{
							Reviewer:  comment.User.Login,
							CreatedAt: comment.CreatedAt,
							Path:      comment.Path,
							Line:      comment.Line,
						},
					)
				}

				reviewData := make([]ReviewData, len(reviews))
				for j, review := range reviews {
					reviewData[j] = ReviewData{
						Reviewer:    review.User.Login,
						State:       review.State,
						SubmittedAt: review.SubmittedAt,
						Comments:    commentsByReview[review.ID],
					}
				}

				prData[i] = PRData{
					Number:    pr.Number,
					CreatedAt: pr.CreatedAt,
					Reviews:   reviewData,
				}
			}

			got, err := a.AnalyzePRs(prData)
			if err != nil {
				t.Errorf("AnalyzePRs() error = %v", err)
				return
			}

			if len(got) != len(tt.want) {
				t.Errorf("AnalyzePRs() returned %d metrics, want %d", len(got), len(tt.want))
				return
			}

			for i, m := range got {
				if m.Username != tt.want[i].Username {
					t.Errorf("Username = %v, want %v", m.Username, tt.want[i].Username)
				}
				if m.PRsReviewed != tt.want[i].PRsReviewed {
					t.Errorf("PRsReviewed = %v, want %v", m.PRsReviewed, tt.want[i].PRsReviewed)
				}
				if m.AverageReviewSpeed != tt.want[i].AverageReviewSpeed {
					t.Errorf(
						"AverageReviewSpeed = %v, want %v",
						m.AverageReviewSpeed,
						tt.want[i].AverageReviewSpeed,
					)
				}
				if m.AverageComments != tt.want[i].AverageComments {
					t.Errorf(
						"AverageComments = %v, want %v",
						m.AverageComments,
						tt.want[i].AverageComments,
					)
				}
				if m.ImmediateApprovals != tt.want[i].ImmediateApprovals {
					t.Errorf(
						"ImmediateApprovals = %v, want %v",
						m.ImmediateApprovals,
						tt.want[i].ImmediateApprovals,
					)
				}
			}
		})
	}
}