mirror of
https://github.com/lukaszraczylo/git-velocity.git
synced 2026-06-09 23:04:00 +00:00
716 lines
19 KiB
Go
716 lines
19 KiB
Go
package scoring
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/lukaszraczylo/git-velocity/internal/config"
|
|
"github.com/lukaszraczylo/git-velocity/internal/domain/models"
|
|
)
|
|
|
|
func TestNewCalculator(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := &config.Config{}
|
|
calc := NewCalculator(cfg)
|
|
|
|
assert.NotNil(t, calc)
|
|
assert.Equal(t, cfg, calc.config)
|
|
}
|
|
|
|
func TestCalculator_ScoringDisabled(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Scoring.Enabled = false
|
|
calc := NewCalculator(cfg)
|
|
|
|
metrics := &models.GlobalMetrics{
|
|
Repositories: []models.RepositoryMetrics{
|
|
{
|
|
Contributors: []models.ContributorMetrics{
|
|
{Login: "user1", CommitCount: 100},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
result := calc.Calculate(metrics)
|
|
|
|
// Should return unchanged metrics when scoring is disabled
|
|
assert.Equal(t, 0, result.Repositories[0].Contributors[0].Score.Total)
|
|
assert.Empty(t, result.Leaderboard)
|
|
}
|
|
|
|
func TestCalculator_BasicScoring(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Scoring.Enabled = true
|
|
cfg.Scoring.Points = config.PointsConfig{
|
|
Commit: 10,
|
|
PROpened: 25,
|
|
PRMerged: 50,
|
|
PRReviewed: 30,
|
|
ReviewComment: 5,
|
|
LinesAdded: 0.1,
|
|
LinesDeleted: 0.05,
|
|
}
|
|
calc := NewCalculator(cfg)
|
|
|
|
metrics := &models.GlobalMetrics{
|
|
Repositories: []models.RepositoryMetrics{
|
|
{
|
|
FullName: "owner/repo",
|
|
Contributors: []models.ContributorMetrics{
|
|
{
|
|
Login: "user1",
|
|
Name: "User One",
|
|
CommitCount: 10,
|
|
LinesAdded: 1000,
|
|
LinesDeleted: 500,
|
|
PRsOpened: 5,
|
|
PRsMerged: 3,
|
|
ReviewsGiven: 8,
|
|
ReviewComments: 20,
|
|
RepositoriesContributed: []string{"owner/repo"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
result := calc.Calculate(metrics)
|
|
|
|
require.Len(t, result.Leaderboard, 1)
|
|
entry := result.Leaderboard[0]
|
|
assert.Equal(t, "user1", entry.Login)
|
|
assert.Equal(t, 1, entry.Rank)
|
|
|
|
// Verify score breakdown:
|
|
// Commits: 10 * 10 = 100
|
|
// Lines: 1000 * 0.1 + 500 * 0.05 = 100 + 25 = 125
|
|
// PRs: 5 * 25 + 3 * 50 = 125 + 150 = 275
|
|
// Reviews: 8 * 30 + 20 * 5 = 240 + 100 = 340
|
|
// Total: 100 + 125 + 275 + 340 = 840
|
|
assert.Equal(t, 840, entry.Score)
|
|
}
|
|
|
|
func TestCalculator_FastReviewBonus(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
avgReviewTime float64
|
|
expectedBonus int
|
|
expectedPoints config.PointsConfig
|
|
}{
|
|
{
|
|
name: "1 hour review gets 1h bonus",
|
|
avgReviewTime: 0.5,
|
|
expectedBonus: 50,
|
|
expectedPoints: config.PointsConfig{
|
|
FastReview1h: 50,
|
|
FastReview4h: 30,
|
|
FastReview24h: 10,
|
|
},
|
|
},
|
|
{
|
|
name: "3 hour review gets 4h bonus",
|
|
avgReviewTime: 3.0,
|
|
expectedBonus: 30,
|
|
expectedPoints: config.PointsConfig{
|
|
FastReview1h: 50,
|
|
FastReview4h: 30,
|
|
FastReview24h: 10,
|
|
},
|
|
},
|
|
{
|
|
name: "12 hour review gets 24h bonus",
|
|
avgReviewTime: 12.0,
|
|
expectedBonus: 10,
|
|
expectedPoints: config.PointsConfig{
|
|
FastReview1h: 50,
|
|
FastReview4h: 30,
|
|
FastReview24h: 10,
|
|
},
|
|
},
|
|
{
|
|
name: "48 hour review gets no bonus",
|
|
avgReviewTime: 48.0,
|
|
expectedBonus: 0,
|
|
expectedPoints: config.PointsConfig{
|
|
FastReview1h: 50,
|
|
FastReview4h: 30,
|
|
FastReview24h: 10,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Scoring.Enabled = true
|
|
cfg.Scoring.Points = tt.expectedPoints
|
|
calc := NewCalculator(cfg)
|
|
|
|
metrics := &models.GlobalMetrics{
|
|
Repositories: []models.RepositoryMetrics{
|
|
{
|
|
FullName: "owner/repo",
|
|
Contributors: []models.ContributorMetrics{
|
|
{
|
|
Login: "user1",
|
|
ReviewsGiven: 5,
|
|
AvgReviewTime: tt.avgReviewTime,
|
|
RepositoriesContributed: []string{"owner/repo"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
result := calc.Calculate(metrics)
|
|
|
|
require.Len(t, result.Leaderboard, 1)
|
|
// Get the contributor from the repository to check breakdown
|
|
contributor := result.Repositories[0].Contributors[0]
|
|
assert.Equal(t, tt.expectedBonus, contributor.Score.Breakdown.ResponseBonus)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCalculator_MultipleContributorsRanking(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Scoring.Enabled = true
|
|
cfg.Scoring.Points = config.PointsConfig{
|
|
Commit: 10,
|
|
}
|
|
calc := NewCalculator(cfg)
|
|
|
|
metrics := &models.GlobalMetrics{
|
|
Repositories: []models.RepositoryMetrics{
|
|
{
|
|
FullName: "owner/repo",
|
|
Contributors: []models.ContributorMetrics{
|
|
{
|
|
Login: "user1",
|
|
CommitCount: 100,
|
|
RepositoriesContributed: []string{"owner/repo"},
|
|
},
|
|
{
|
|
Login: "user2",
|
|
CommitCount: 50,
|
|
RepositoriesContributed: []string{"owner/repo"},
|
|
},
|
|
{
|
|
Login: "user3",
|
|
CommitCount: 200,
|
|
RepositoriesContributed: []string{"owner/repo"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
result := calc.Calculate(metrics)
|
|
|
|
require.Len(t, result.Leaderboard, 3)
|
|
|
|
// Should be sorted by score (highest first)
|
|
assert.Equal(t, "user3", result.Leaderboard[0].Login)
|
|
assert.Equal(t, 1, result.Leaderboard[0].Rank)
|
|
assert.Equal(t, 2000, result.Leaderboard[0].Score)
|
|
|
|
assert.Equal(t, "user1", result.Leaderboard[1].Login)
|
|
assert.Equal(t, 2, result.Leaderboard[1].Rank)
|
|
assert.Equal(t, 1000, result.Leaderboard[1].Score)
|
|
|
|
assert.Equal(t, "user2", result.Leaderboard[2].Login)
|
|
assert.Equal(t, 3, result.Leaderboard[2].Rank)
|
|
assert.Equal(t, 500, result.Leaderboard[2].Score)
|
|
}
|
|
|
|
func TestCalculator_PercentileRank(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Scoring.Enabled = true
|
|
cfg.Scoring.Points = config.PointsConfig{Commit: 10}
|
|
calc := NewCalculator(cfg)
|
|
|
|
metrics := &models.GlobalMetrics{
|
|
Repositories: []models.RepositoryMetrics{
|
|
{
|
|
FullName: "owner/repo",
|
|
Contributors: []models.ContributorMetrics{
|
|
{Login: "user1", CommitCount: 100, RepositoriesContributed: []string{"owner/repo"}},
|
|
{Login: "user2", CommitCount: 80, RepositoriesContributed: []string{"owner/repo"}},
|
|
{Login: "user3", CommitCount: 60, RepositoriesContributed: []string{"owner/repo"}},
|
|
{Login: "user4", CommitCount: 40, RepositoriesContributed: []string{"owner/repo"}},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
result := calc.Calculate(metrics)
|
|
|
|
require.Len(t, result.Leaderboard, 4)
|
|
|
|
// Leaderboard should be sorted by score (highest first)
|
|
// user1: 100 commits * 10 = 1000, rank 1
|
|
// user2: 80 commits * 10 = 800, rank 2
|
|
// user3: 60 commits * 10 = 600, rank 3
|
|
// user4: 40 commits * 10 = 400, rank 4
|
|
assert.Equal(t, "user1", result.Leaderboard[0].Login)
|
|
assert.Equal(t, 1, result.Leaderboard[0].Rank)
|
|
assert.Equal(t, 1000, result.Leaderboard[0].Score)
|
|
|
|
assert.Equal(t, "user2", result.Leaderboard[1].Login)
|
|
assert.Equal(t, 2, result.Leaderboard[1].Rank)
|
|
assert.Equal(t, 800, result.Leaderboard[1].Score)
|
|
|
|
assert.Equal(t, "user3", result.Leaderboard[2].Login)
|
|
assert.Equal(t, 3, result.Leaderboard[2].Rank)
|
|
assert.Equal(t, 600, result.Leaderboard[2].Score)
|
|
|
|
assert.Equal(t, "user4", result.Leaderboard[3].Login)
|
|
assert.Equal(t, 4, result.Leaderboard[3].Rank)
|
|
assert.Equal(t, 400, result.Leaderboard[3].Score)
|
|
}
|
|
|
|
func TestCalculator_Achievements(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Scoring.Enabled = true
|
|
cfg.Scoring.Achievements = []config.AchievementConfig{
|
|
{
|
|
ID: "commit-10",
|
|
Name: "10 Commits",
|
|
Condition: config.AchievementCondition{
|
|
Type: "commit_count",
|
|
Threshold: 10,
|
|
},
|
|
},
|
|
{
|
|
ID: "pr-master",
|
|
Name: "PR Master",
|
|
Condition: config.AchievementCondition{
|
|
Type: "pr_opened_count",
|
|
Threshold: 5,
|
|
},
|
|
},
|
|
{
|
|
ID: "reviewer",
|
|
Name: "Reviewer",
|
|
Condition: config.AchievementCondition{
|
|
Type: "review_count",
|
|
Threshold: 10,
|
|
},
|
|
},
|
|
{
|
|
ID: "speed-demon",
|
|
Name: "Speed Demon",
|
|
Condition: config.AchievementCondition{
|
|
Type: "avg_review_time_hours",
|
|
Threshold: 1.0,
|
|
},
|
|
},
|
|
}
|
|
calc := NewCalculator(cfg)
|
|
|
|
metrics := &models.GlobalMetrics{
|
|
Repositories: []models.RepositoryMetrics{
|
|
{
|
|
FullName: "owner/repo",
|
|
Contributors: []models.ContributorMetrics{
|
|
{
|
|
Login: "user1",
|
|
CommitCount: 15,
|
|
PRsOpened: 6,
|
|
ReviewsGiven: 5,
|
|
AvgReviewTime: 0.5,
|
|
RepositoriesContributed: []string{"owner/repo"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
result := calc.Calculate(metrics)
|
|
|
|
contributor := result.Repositories[0].Contributors[0]
|
|
// Should have commit-10, pr-master, and speed-demon
|
|
// Should NOT have reviewer (only 5 reviews, need 10)
|
|
assert.Contains(t, contributor.Achievements, "commit-10")
|
|
assert.Contains(t, contributor.Achievements, "pr-master")
|
|
assert.Contains(t, contributor.Achievements, "speed-demon")
|
|
assert.NotContains(t, contributor.Achievements, "reviewer")
|
|
}
|
|
|
|
func TestCalculator_AllAchievementTypes(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Scoring.Enabled = true
|
|
cfg.Scoring.Achievements = []config.AchievementConfig{
|
|
{ID: "commits", Condition: config.AchievementCondition{Type: "commit_count", Threshold: 10}},
|
|
{ID: "prs-opened", Condition: config.AchievementCondition{Type: "pr_opened_count", Threshold: 5}},
|
|
{ID: "prs-merged", Condition: config.AchievementCondition{Type: "pr_merged_count", Threshold: 3}},
|
|
{ID: "reviews", Condition: config.AchievementCondition{Type: "review_count", Threshold: 8}},
|
|
{ID: "comments", Condition: config.AchievementCondition{Type: "comment_count", Threshold: 20}},
|
|
{ID: "lines-added", Condition: config.AchievementCondition{Type: "lines_added", Threshold: 1000}},
|
|
{ID: "lines-deleted", Condition: config.AchievementCondition{Type: "lines_deleted", Threshold: 500}},
|
|
{ID: "fast-review", Condition: config.AchievementCondition{Type: "avg_review_time_hours", Threshold: 2}},
|
|
{ID: "multi-repo", Condition: config.AchievementCondition{Type: "repo_count", Threshold: 2}},
|
|
{ID: "team-player", Condition: config.AchievementCondition{Type: "unique_reviewees", Threshold: 5}},
|
|
}
|
|
calc := NewCalculator(cfg)
|
|
|
|
metrics := &models.GlobalMetrics{
|
|
Repositories: []models.RepositoryMetrics{
|
|
{
|
|
FullName: "owner/repo1",
|
|
Contributors: []models.ContributorMetrics{
|
|
{
|
|
Login: "user1",
|
|
CommitCount: 15,
|
|
PRsOpened: 6,
|
|
PRsMerged: 4,
|
|
ReviewsGiven: 10,
|
|
ReviewComments: 25,
|
|
LinesAdded: 1500,
|
|
LinesDeleted: 600,
|
|
AvgReviewTime: 1.5,
|
|
UniqueReviewees: 7,
|
|
RepositoriesContributed: []string{"owner/repo1", "owner/repo2"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
result := calc.Calculate(metrics)
|
|
|
|
contributor := result.Repositories[0].Contributors[0]
|
|
// Should have all achievements
|
|
assert.Len(t, contributor.Achievements, 10)
|
|
assert.Contains(t, contributor.Achievements, "commits")
|
|
assert.Contains(t, contributor.Achievements, "prs-opened")
|
|
assert.Contains(t, contributor.Achievements, "prs-merged")
|
|
assert.Contains(t, contributor.Achievements, "reviews")
|
|
assert.Contains(t, contributor.Achievements, "comments")
|
|
assert.Contains(t, contributor.Achievements, "lines-added")
|
|
assert.Contains(t, contributor.Achievements, "lines-deleted")
|
|
assert.Contains(t, contributor.Achievements, "fast-review")
|
|
assert.Contains(t, contributor.Achievements, "multi-repo")
|
|
assert.Contains(t, contributor.Achievements, "team-player")
|
|
}
|
|
|
|
func TestCalculator_TopAchievers(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Scoring.Enabled = true
|
|
cfg.Scoring.Points = config.PointsConfig{
|
|
Commit: 10,
|
|
PROpened: 25,
|
|
PRReviewed: 30,
|
|
}
|
|
calc := NewCalculator(cfg)
|
|
|
|
metrics := &models.GlobalMetrics{
|
|
Repositories: []models.RepositoryMetrics{
|
|
{
|
|
FullName: "owner/repo",
|
|
Contributors: []models.ContributorMetrics{
|
|
{
|
|
Login: "committer",
|
|
CommitCount: 100,
|
|
PRsOpened: 5,
|
|
ReviewsGiven: 2,
|
|
RepositoriesContributed: []string{"owner/repo"},
|
|
},
|
|
{
|
|
Login: "pr-author",
|
|
CommitCount: 10,
|
|
PRsOpened: 50,
|
|
ReviewsGiven: 3,
|
|
RepositoriesContributed: []string{"owner/repo"},
|
|
},
|
|
{
|
|
Login: "reviewer",
|
|
CommitCount: 5,
|
|
PRsOpened: 2,
|
|
ReviewsGiven: 100,
|
|
RepositoriesContributed: []string{"owner/repo"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
result := calc.Calculate(metrics)
|
|
|
|
assert.Equal(t, "committer", result.TopAchievers["commits"])
|
|
assert.Equal(t, "pr-author", result.TopAchievers["pull_requests"])
|
|
assert.Equal(t, "reviewer", result.TopAchievers["reviews"])
|
|
// Overall top achiever has highest score
|
|
assert.NotEmpty(t, result.TopAchievers["overall"])
|
|
}
|
|
|
|
func TestCalculator_TeamScoring(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Scoring.Enabled = true
|
|
cfg.Scoring.Points = config.PointsConfig{Commit: 10}
|
|
cfg.Teams = []config.TeamConfig{
|
|
{
|
|
Name: "Backend Team",
|
|
Members: []string{"user1", "user2"},
|
|
Color: "#ff0000",
|
|
},
|
|
}
|
|
calc := NewCalculator(cfg)
|
|
|
|
metrics := &models.GlobalMetrics{
|
|
Repositories: []models.RepositoryMetrics{
|
|
{
|
|
FullName: "owner/repo",
|
|
Contributors: []models.ContributorMetrics{
|
|
{Login: "user1", CommitCount: 50, RepositoriesContributed: []string{"owner/repo"}},
|
|
{Login: "user2", CommitCount: 30, RepositoriesContributed: []string{"owner/repo"}},
|
|
},
|
|
},
|
|
},
|
|
Teams: []models.TeamMetrics{
|
|
{
|
|
Name: "Backend Team",
|
|
Members: []string{"user1", "user2"},
|
|
MemberMetrics: []models.ContributorMetrics{
|
|
{Login: "user1"},
|
|
{Login: "user2"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
result := calc.Calculate(metrics)
|
|
|
|
require.Len(t, result.Teams, 1)
|
|
team := result.Teams[0]
|
|
// Total: 500 + 300 = 800
|
|
assert.Equal(t, 800, team.TotalScore)
|
|
// Avg: 800 / 2 = 400
|
|
assert.Equal(t, 400.0, team.AvgScore)
|
|
|
|
// Check individual member scores
|
|
assert.Equal(t, 500, team.MemberMetrics[0].Score.Total)
|
|
assert.Equal(t, 300, team.MemberMetrics[1].Score.Total)
|
|
}
|
|
|
|
func TestCalculator_TeamInLeaderboard(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Scoring.Enabled = true
|
|
cfg.Scoring.Points = config.PointsConfig{Commit: 10}
|
|
cfg.Teams = []config.TeamConfig{
|
|
{
|
|
Name: "Backend Team",
|
|
Members: []string{"user1"},
|
|
},
|
|
}
|
|
calc := NewCalculator(cfg)
|
|
|
|
metrics := &models.GlobalMetrics{
|
|
Repositories: []models.RepositoryMetrics{
|
|
{
|
|
FullName: "owner/repo",
|
|
Contributors: []models.ContributorMetrics{
|
|
{Login: "user1", CommitCount: 50, RepositoriesContributed: []string{"owner/repo"}},
|
|
{Login: "user2", CommitCount: 30, RepositoriesContributed: []string{"owner/repo"}},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
result := calc.Calculate(metrics)
|
|
|
|
// user1 should have team name in leaderboard
|
|
assert.Equal(t, "Backend Team", result.Leaderboard[0].Team)
|
|
// user2 should not have a team
|
|
assert.Empty(t, result.Leaderboard[1].Team)
|
|
}
|
|
|
|
func TestCalculator_DetermineTopCategory(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Scoring.Enabled = true
|
|
calc := NewCalculator(cfg)
|
|
|
|
tests := []struct {
|
|
name string
|
|
contributor models.ContributorMetrics
|
|
expectedCategory string
|
|
}{
|
|
{
|
|
name: "Top committer",
|
|
contributor: models.ContributorMetrics{
|
|
CommitCount: 100,
|
|
PRsOpened: 10,
|
|
ReviewsGiven: 5,
|
|
ReviewComments: 20,
|
|
},
|
|
expectedCategory: "Commits",
|
|
},
|
|
{
|
|
name: "Top PR author",
|
|
contributor: models.ContributorMetrics{
|
|
CommitCount: 10,
|
|
PRsOpened: 100,
|
|
ReviewsGiven: 5,
|
|
ReviewComments: 20,
|
|
},
|
|
expectedCategory: "PRs",
|
|
},
|
|
{
|
|
name: "Top reviewer",
|
|
contributor: models.ContributorMetrics{
|
|
CommitCount: 10,
|
|
PRsOpened: 5,
|
|
ReviewsGiven: 100,
|
|
ReviewComments: 20,
|
|
},
|
|
expectedCategory: "Reviews",
|
|
},
|
|
{
|
|
name: "Top commenter",
|
|
contributor: models.ContributorMetrics{
|
|
CommitCount: 10,
|
|
PRsOpened: 5,
|
|
ReviewsGiven: 20,
|
|
ReviewComments: 100,
|
|
},
|
|
expectedCategory: "Comments",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
result := calc.determineTopCategory(&tt.contributor)
|
|
assert.Equal(t, tt.expectedCategory, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCalculator_MultipleRepositories(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Scoring.Enabled = true
|
|
cfg.Scoring.Points = config.PointsConfig{Commit: 10}
|
|
calc := NewCalculator(cfg)
|
|
|
|
metrics := &models.GlobalMetrics{
|
|
Repositories: []models.RepositoryMetrics{
|
|
{
|
|
FullName: "owner/repo1",
|
|
Contributors: []models.ContributorMetrics{
|
|
{Login: "user1", CommitCount: 50, RepositoriesContributed: []string{"owner/repo1"}},
|
|
},
|
|
},
|
|
{
|
|
FullName: "owner/repo2",
|
|
Contributors: []models.ContributorMetrics{
|
|
{Login: "user1", CommitCount: 30, RepositoriesContributed: []string{"owner/repo2"}},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
result := calc.Calculate(metrics)
|
|
|
|
// Should aggregate commits from both repos
|
|
require.Len(t, result.Leaderboard, 1)
|
|
// 50 + 30 = 80 commits * 10 = 800
|
|
assert.Equal(t, 800, result.Leaderboard[0].Score)
|
|
|
|
// Per-repo scores should reflect repo-specific metrics, not global
|
|
// Repo1 has 50 commits * 10 = 500
|
|
contributor := result.Repositories[0].Contributors[0]
|
|
assert.Equal(t, 500, contributor.Score.Total)
|
|
}
|
|
|
|
func TestCalculator_EmptyMetrics(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Scoring.Enabled = true
|
|
calc := NewCalculator(cfg)
|
|
|
|
metrics := &models.GlobalMetrics{
|
|
Repositories: []models.RepositoryMetrics{},
|
|
}
|
|
|
|
result := calc.Calculate(metrics)
|
|
|
|
assert.Empty(t, result.Leaderboard)
|
|
assert.Empty(t, result.TopAchievers)
|
|
}
|
|
|
|
func TestCalculator_NoReviewsNoBonus(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Scoring.Enabled = true
|
|
cfg.Scoring.Points = config.PointsConfig{
|
|
FastReview1h: 50,
|
|
}
|
|
calc := NewCalculator(cfg)
|
|
|
|
metrics := &models.GlobalMetrics{
|
|
Repositories: []models.RepositoryMetrics{
|
|
{
|
|
FullName: "owner/repo",
|
|
Contributors: []models.ContributorMetrics{
|
|
{
|
|
Login: "user1",
|
|
ReviewsGiven: 0,
|
|
AvgReviewTime: 0.5, // Fast but no reviews
|
|
RepositoriesContributed: []string{"owner/repo"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
result := calc.Calculate(metrics)
|
|
|
|
contributor := result.Repositories[0].Contributors[0]
|
|
// Should not get bonus if no reviews given
|
|
assert.Equal(t, 0, contributor.Score.Breakdown.ResponseBonus)
|
|
}
|
|
|
|
func TestContains(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
slice := []string{"a", "b", "c"}
|
|
|
|
assert.True(t, contains(slice, "a"))
|
|
assert.True(t, contains(slice, "b"))
|
|
assert.True(t, contains(slice, "c"))
|
|
assert.False(t, contains(slice, "d"))
|
|
assert.False(t, contains([]string{}, "a"))
|
|
}
|