aboutsummaryrefslogtreecommitdiff
path: root/cmd/pr-analyzer/analyzer_test.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--cmd/pr-analyzer/analyzer_test.go233
1 files changed, 233 insertions, 0 deletions
diff --git a/cmd/pr-analyzer/analyzer_test.go b/cmd/pr-analyzer/analyzer_test.go
new file mode 100644
index 0000000..d2c3041
--- /dev/null
+++ b/cmd/pr-analyzer/analyzer_test.go
@@ -0,0 +1,233 @@
+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,
+ )
+ }
+ }
+ })
+ }
+}