forked from chroe/gitlink-cli
149 lines
4.6 KiB
Go
149 lines
4.6 KiB
Go
package rules
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestContributorRanking(t *testing.T) {
|
|
upstream := map[string]interface{}{
|
|
"commits": map[string]interface{}{
|
|
"data": []interface{}{
|
|
map[string]interface{}{"created_at": "2025-06-01T00:00:00Z", "author": "dev1"},
|
|
map[string]interface{}{"created_at": "2025-06-05T00:00:00Z", "author": "dev1"},
|
|
map[string]interface{}{"created_at": "2025-06-10T00:00:00Z", "author": "dev2"},
|
|
},
|
|
},
|
|
"open-issues": map[string]interface{}{"data": []interface{}{}},
|
|
"closed-issues": map[string]interface{}{"data": []interface{}{}},
|
|
"merged-prs": map[string]interface{}{"data": []interface{}{}},
|
|
"members": map[string]interface{}{
|
|
"data": []interface{}{
|
|
map[string]interface{}{"login": "dev1", "name": "Dev One"},
|
|
map[string]interface{}{"login": "dev2", "name": "Dev Two"},
|
|
},
|
|
},
|
|
}
|
|
|
|
resp, err := ContributorRankingRule(upstream, "contributor-ranking")
|
|
if err != nil {
|
|
t.Fatalf("ContributorRankingRule failed: %v", err)
|
|
}
|
|
analysis := resp.Analysis.(map[string]interface{})
|
|
rankings := analysis["rankings"].([]map[string]interface{})
|
|
if len(rankings) != 2 {
|
|
t.Fatalf("expected 2 rankings, got %d", len(rankings))
|
|
}
|
|
|
|
// dev1 should be ranked #1 (2 commits vs 1).
|
|
first := rankings[0]
|
|
if first["login"] != "dev1" {
|
|
t.Errorf("expected dev1 as #1, got %v", first["login"])
|
|
}
|
|
if first["rank"] != 1 {
|
|
t.Errorf("expected rank 1, got %v", first["rank"])
|
|
}
|
|
if first["commits"] != 2 {
|
|
t.Errorf("expected 2 commits, got %v", first["commits"])
|
|
}
|
|
}
|
|
|
|
func TestChurnRiskDetection(t *testing.T) {
|
|
// 35 days ago — should trigger churn risk.
|
|
oldDate := "2025-01-01T00:00:00Z"
|
|
|
|
upstream := map[string]interface{}{
|
|
"commits": map[string]interface{}{
|
|
"data": []interface{}{
|
|
map[string]interface{}{"created_at": oldDate, "author": "dev1"},
|
|
},
|
|
},
|
|
"open-issues": map[string]interface{}{"data": []interface{}{}},
|
|
"closed-issues": map[string]interface{}{"data": []interface{}{}},
|
|
"merged-prs": map[string]interface{}{"data": []interface{}{}},
|
|
"members": map[string]interface{}{
|
|
"data": []interface{}{
|
|
map[string]interface{}{"login": "dev1", "name": "Dev One"},
|
|
},
|
|
},
|
|
}
|
|
|
|
resp, err := ContributorRankingRule(upstream, "contributor-ranking")
|
|
if err != nil {
|
|
t.Fatalf("ContributorRankingRule failed: %v", err)
|
|
}
|
|
analysis := resp.Analysis.(map[string]interface{})
|
|
rankings := analysis["rankings"].([]map[string]interface{})
|
|
if len(rankings) > 0 {
|
|
tags := rankings[0]["tags"].([]string)
|
|
for _, tag := range tags {
|
|
if tag == "churn-risk" {
|
|
return // success
|
|
}
|
|
}
|
|
t.Errorf("expected churn-risk tag for old activity, got tags: %v", tags)
|
|
}
|
|
}
|
|
|
|
func TestContributorOutputFormat(t *testing.T) {
|
|
resp, err := ContributorRankingRule(map[string]interface{}{}, "contributor-ranking")
|
|
if err != nil {
|
|
t.Fatalf("ContributorRankingRule failed: %v", err)
|
|
}
|
|
if resp.Analysis == nil {
|
|
t.Fatal("expected non-nil Analysis")
|
|
}
|
|
if resp.Actions != nil {
|
|
t.Fatal("expected nil Actions (read-only report)")
|
|
}
|
|
}
|
|
|
|
func TestNewStarDetection(t *testing.T) {
|
|
// Use very recent dates so the commits appear in the last 30 days.
|
|
now := time.Now()
|
|
d1 := now.Add(-2 * 24 * time.Hour).Format(time.RFC3339)
|
|
d2 := now.Add(-3 * 24 * time.Hour).Format(time.RFC3339)
|
|
d3 := now.Add(-4 * 24 * time.Hour).Format(time.RFC3339)
|
|
|
|
upstream := map[string]interface{}{
|
|
"commits": map[string]interface{}{
|
|
"data": []interface{}{
|
|
map[string]interface{}{"created_at": d1, "author": "dev1"},
|
|
map[string]interface{}{"created_at": d2, "author": "dev1"},
|
|
map[string]interface{}{"created_at": d3, "author": "dev1"},
|
|
},
|
|
},
|
|
"open-issues": map[string]interface{}{"data": []interface{}{}},
|
|
"closed-issues": map[string]interface{}{"data": []interface{}{}},
|
|
"merged-prs": map[string]interface{}{"data": []interface{}{}},
|
|
"members": map[string]interface{}{
|
|
"data": []interface{}{
|
|
map[string]interface{}{"login": "dev1", "name": "Dev One"},
|
|
},
|
|
},
|
|
}
|
|
|
|
resp, err := ContributorRankingRule(upstream, "contributor-ranking")
|
|
if err != nil {
|
|
t.Fatalf("ContributorRankingRule failed: %v", err)
|
|
}
|
|
analysis := resp.Analysis.(map[string]interface{})
|
|
// Should have new-star or at least ranking.
|
|
rankings := analysis["rankings"].([]map[string]interface{})
|
|
if len(rankings) == 0 {
|
|
t.Fatal("expected at least 1 ranking entry")
|
|
}
|
|
tags, _ := rankings[0]["tags"].([]string)
|
|
t.Logf("tags for dev1: %v", tags)
|
|
// With only recent commits (no previous period), trend should be 100%, triggering new-star.
|
|
found := false
|
|
for _, tag := range tags {
|
|
if tag == "new-star" {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("expected new-star tag, got: %v", tags)
|
|
}
|
|
}
|