698 lines
15 KiB
Go
698 lines
15 KiB
Go
package repo
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
)
|
|
|
|
func TestCalculateHealthScore(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
result *HealthResult
|
|
expected int
|
|
}{
|
|
{
|
|
name: "perfect health - active repo",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{
|
|
Available: true,
|
|
DaysAgo: 5,
|
|
},
|
|
Issues: &IssuesInfo{
|
|
Available: true,
|
|
OpenCount: 5,
|
|
},
|
|
PullRequests: &PullRequestsInfo{
|
|
Available: true,
|
|
OpenCount: 2,
|
|
},
|
|
WorkflowStatus: &WorkflowStatusInfo{
|
|
Available: true,
|
|
LastRunConclusion: "success",
|
|
HasRecentRuns: true,
|
|
},
|
|
BranchProtection: &BranchProtectionInfo{
|
|
Available: true,
|
|
ProtectedBranchesCount: 1,
|
|
},
|
|
RepositoryInfo: &RepositoryInfo{
|
|
IsArchived: false,
|
|
},
|
|
},
|
|
expected: 100,
|
|
},
|
|
{
|
|
name: "stale commits - 35 days",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{
|
|
Available: true,
|
|
DaysAgo: 35,
|
|
},
|
|
Issues: &IssuesInfo{
|
|
Available: true,
|
|
OpenCount: 5,
|
|
},
|
|
},
|
|
expected: 85,
|
|
},
|
|
{
|
|
name: "very stale commits - 100 days",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{
|
|
Available: true,
|
|
DaysAgo: 100,
|
|
},
|
|
Issues: &IssuesInfo{
|
|
Available: true,
|
|
OpenCount: 5,
|
|
},
|
|
},
|
|
expected: 70,
|
|
},
|
|
{
|
|
name: "too many open issues",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{
|
|
Available: true,
|
|
DaysAgo: 5,
|
|
},
|
|
Issues: &IssuesInfo{
|
|
Available: true,
|
|
OpenCount: 60,
|
|
},
|
|
},
|
|
expected: 90,
|
|
},
|
|
{
|
|
name: "workflow failure",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{
|
|
Available: true,
|
|
DaysAgo: 5,
|
|
},
|
|
WorkflowStatus: &WorkflowStatusInfo{
|
|
Available: true,
|
|
LastRunConclusion: "failure",
|
|
HasRecentRuns: true,
|
|
},
|
|
},
|
|
expected: 85,
|
|
},
|
|
{
|
|
name: "archived repository",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{
|
|
Available: true,
|
|
DaysAgo: 5,
|
|
},
|
|
RepositoryInfo: &RepositoryInfo{
|
|
IsArchived: true,
|
|
},
|
|
},
|
|
expected: 60,
|
|
},
|
|
{
|
|
name: "empty result - no data",
|
|
result: &HealthResult{},
|
|
expected: 100,
|
|
},
|
|
{
|
|
name: "boundary - minimum score",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{
|
|
Available: true,
|
|
DaysAgo: 1000,
|
|
},
|
|
RepositoryInfo: &RepositoryInfo{
|
|
IsArchived: true,
|
|
},
|
|
WorkflowStatus: &WorkflowStatusInfo{
|
|
Available: true,
|
|
LastRunConclusion: "failure",
|
|
HasRecentRuns: false,
|
|
},
|
|
Issues: &IssuesInfo{
|
|
Available: true,
|
|
OpenCount: 100,
|
|
},
|
|
},
|
|
expected: 0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
score := calculateHealthScore(tt.result)
|
|
if score != tt.expected {
|
|
t.Errorf("calculateHealthScore() = %d, want %d", score, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetHealthStatus(t *testing.T) {
|
|
tests := []struct {
|
|
score int
|
|
expected string
|
|
}{
|
|
{95, "excellent"},
|
|
{90, "excellent"},
|
|
{85, "good"},
|
|
{70, "good"},
|
|
{60, "fair"},
|
|
{50, "fair"},
|
|
{40, "poor"},
|
|
{30, "poor"},
|
|
{20, "critical"},
|
|
{0, "critical"},
|
|
{100, "excellent"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.expected, func(t *testing.T) {
|
|
status := getHealthStatus(tt.score)
|
|
if status != tt.expected {
|
|
t.Errorf("getHealthStatus(%d) = %s, want %s", tt.score, status, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetStringFromMap(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
m map[string]any
|
|
key string
|
|
expected string
|
|
}{
|
|
{
|
|
name: "string value",
|
|
m: map[string]any{"status": "success"},
|
|
key: "status",
|
|
expected: "success",
|
|
},
|
|
{
|
|
name: "missing key",
|
|
m: map[string]any{"other": "value"},
|
|
key: "status",
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "non-string value",
|
|
m: map[string]any{"count": 42},
|
|
key: "count",
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "empty map",
|
|
m: map[string]any{},
|
|
key: "status",
|
|
expected: "",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := getStringFromMap(tt.m, tt.key)
|
|
if result != tt.expected {
|
|
t.Errorf("getStringFromMap() = %q, want %q", result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHealthResultJSONMarshaling(t *testing.T) {
|
|
result := &HealthResult{
|
|
Repository: "owner/repo",
|
|
HealthScore: 85,
|
|
HealthStatus: "good",
|
|
LastCommit: &CommitInfo{
|
|
SHA: "abc123",
|
|
Message: "Initial commit",
|
|
Author: "user",
|
|
Date: "2024-01-15T10:30:00Z",
|
|
DaysAgo: 5,
|
|
Available: true,
|
|
},
|
|
Issues: &IssuesInfo{
|
|
OpenCount: 10,
|
|
TotalCount: 50,
|
|
Available: true,
|
|
},
|
|
PullRequests: &PullRequestsInfo{
|
|
OpenCount: 3,
|
|
TotalCount: 15,
|
|
Available: true,
|
|
},
|
|
WorkflowStatus: &WorkflowStatusInfo{
|
|
LastRunStatus: "completed",
|
|
LastRunConclusion: "success",
|
|
HasRecentRuns: true,
|
|
Available: true,
|
|
},
|
|
BranchProtection: &BranchProtectionInfo{
|
|
ProtectedBranchesCount: 2,
|
|
ProtectedBranches: []string{"main", "develop"},
|
|
Available: true,
|
|
},
|
|
RepositoryInfo: &RepositoryInfo{
|
|
Stars: 100,
|
|
Forks: 20,
|
|
Language: "Go",
|
|
IsPrivate: false,
|
|
IsArchived: false,
|
|
Available: true,
|
|
},
|
|
CheckedAt: "2024-01-20T10:00:00Z",
|
|
PartialResult: false,
|
|
}
|
|
|
|
jsonBytes, err := json.MarshalIndent(result, "", " ")
|
|
if err != nil {
|
|
t.Fatalf("Failed to marshal HealthResult: %v", err)
|
|
}
|
|
|
|
if len(jsonBytes) == 0 {
|
|
t.Error("Expected non-empty JSON output")
|
|
}
|
|
|
|
var unmarshaled HealthResult
|
|
if err := json.Unmarshal(jsonBytes, &unmarshaled); err != nil {
|
|
t.Fatalf("Failed to unmarshal HealthResult: %v", err)
|
|
}
|
|
|
|
if unmarshaled.HealthScore != result.HealthScore {
|
|
t.Errorf("HealthScore mismatch: got %d, want %d", unmarshaled.HealthScore, result.HealthScore)
|
|
}
|
|
|
|
if unmarshaled.HealthStatus != result.HealthStatus {
|
|
t.Errorf("HealthStatus mismatch: got %s, want %s", unmarshaled.HealthStatus, result.HealthStatus)
|
|
}
|
|
|
|
if unmarshaled.LastCommit == nil || unmarshaled.LastCommit.SHA != result.LastCommit.SHA {
|
|
t.Error("LastCommit mismatch")
|
|
}
|
|
}
|
|
|
|
func TestCalculateHealthScore_EdgeCases(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
result *HealthResult
|
|
expected int
|
|
}{
|
|
{
|
|
name: "all nil fields",
|
|
result: &HealthResult{
|
|
LastCommit: nil,
|
|
Issues: nil,
|
|
PullRequests: nil,
|
|
WorkflowStatus: nil,
|
|
BranchProtection: nil,
|
|
RepositoryInfo: nil,
|
|
},
|
|
expected: 100,
|
|
},
|
|
{
|
|
name: "unavailable fields",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{Available: false},
|
|
Issues: &IssuesInfo{Available: false},
|
|
},
|
|
expected: 100,
|
|
},
|
|
{
|
|
name: "stale commits boundary - exactly 30 days",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{Available: true, DaysAgo: 30},
|
|
},
|
|
expected: 100,
|
|
},
|
|
{
|
|
name: "stale commits boundary - exactly 31 days",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{Available: true, DaysAgo: 31},
|
|
},
|
|
expected: 85,
|
|
},
|
|
{
|
|
name: "stale commits boundary - exactly 90 days",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{Available: true, DaysAgo: 90},
|
|
},
|
|
expected: 85,
|
|
},
|
|
{
|
|
name: "stale commits boundary - exactly 91 days",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{Available: true, DaysAgo: 91},
|
|
},
|
|
expected: 70,
|
|
},
|
|
{
|
|
name: "issues boundary - exactly 20",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{Available: true, DaysAgo: 5},
|
|
Issues: &IssuesInfo{Available: true, OpenCount: 20},
|
|
},
|
|
expected: 100,
|
|
},
|
|
{
|
|
name: "issues boundary - exactly 21",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{Available: true, DaysAgo: 5},
|
|
Issues: &IssuesInfo{Available: true, OpenCount: 21},
|
|
},
|
|
expected: 95,
|
|
},
|
|
{
|
|
name: "issues boundary - exactly 50",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{Available: true, DaysAgo: 5},
|
|
Issues: &IssuesInfo{Available: true, OpenCount: 50},
|
|
},
|
|
expected: 95,
|
|
},
|
|
{
|
|
name: "issues boundary - exactly 51",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{Available: true, DaysAgo: 5},
|
|
Issues: &IssuesInfo{Available: true, OpenCount: 51},
|
|
},
|
|
expected: 90,
|
|
},
|
|
{
|
|
name: "PRs boundary - exactly 10",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{Available: true, DaysAgo: 5},
|
|
PullRequests: &PullRequestsInfo{Available: true, OpenCount: 10},
|
|
},
|
|
expected: 100,
|
|
},
|
|
{
|
|
name: "PRs boundary - exactly 11",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{Available: true, DaysAgo: 5},
|
|
PullRequests: &PullRequestsInfo{Available: true, OpenCount: 11},
|
|
},
|
|
expected: 95,
|
|
},
|
|
{
|
|
name: "workflow cancelled",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{Available: true, DaysAgo: 5},
|
|
WorkflowStatus: &WorkflowStatusInfo{
|
|
Available: true,
|
|
LastRunConclusion: "cancelled",
|
|
HasRecentRuns: true,
|
|
},
|
|
},
|
|
expected: 95,
|
|
},
|
|
{
|
|
name: "workflow no recent runs",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{Available: true, DaysAgo: 5},
|
|
WorkflowStatus: &WorkflowStatusInfo{
|
|
Available: true,
|
|
LastRunConclusion: "success",
|
|
HasRecentRuns: false,
|
|
},
|
|
},
|
|
expected: 95,
|
|
},
|
|
{
|
|
name: "archived with negative score",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{Available: true, DaysAgo: 1000},
|
|
RepositoryInfo: &RepositoryInfo{
|
|
IsArchived: true,
|
|
},
|
|
WorkflowStatus: &WorkflowStatusInfo{
|
|
Available: true,
|
|
LastRunConclusion: "failure",
|
|
HasRecentRuns: false,
|
|
},
|
|
},
|
|
expected: 0,
|
|
},
|
|
{
|
|
name: "maximum score cap",
|
|
result: &HealthResult{
|
|
LastCommit: &CommitInfo{Available: true, DaysAgo: 0},
|
|
Issues: &IssuesInfo{Available: true, OpenCount: 0},
|
|
BranchProtection: &BranchProtectionInfo{
|
|
Available: true,
|
|
ProtectedBranchesCount: 10,
|
|
},
|
|
},
|
|
expected: 100,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
score := calculateHealthScore(tt.result)
|
|
if score != tt.expected {
|
|
t.Errorf("calculateHealthScore() = %d, want %d", score, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetHealthStatus_Boundaries(t *testing.T) {
|
|
tests := []struct {
|
|
score int
|
|
expected string
|
|
}{
|
|
{100, "excellent"},
|
|
{91, "excellent"},
|
|
{89, "good"},
|
|
{71, "good"},
|
|
{69, "fair"},
|
|
{51, "fair"},
|
|
{49, "poor"},
|
|
{31, "poor"},
|
|
{29, "critical"},
|
|
{1, "critical"},
|
|
{-10, "critical"},
|
|
{110, "excellent"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(fmt.Sprintf("score_%d", tt.score), func(t *testing.T) {
|
|
status := getHealthStatus(tt.score)
|
|
if status != tt.expected {
|
|
t.Errorf("getHealthStatus(%d) = %s, want %s", tt.score, status, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetStringFromMap_EdgeCases(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
m map[string]any
|
|
key string
|
|
expected string
|
|
}{
|
|
{
|
|
name: "nil map",
|
|
m: nil,
|
|
key: "status",
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "empty map",
|
|
m: map[string]any{},
|
|
key: "status",
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "int value",
|
|
m: map[string]any{"count": int(42)},
|
|
key: "count",
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "float64 value",
|
|
m: map[string]any{"count": float64(42)},
|
|
key: "count",
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "bool value",
|
|
m: map[string]any{"active": true},
|
|
key: "active",
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "nested map value",
|
|
m: map[string]any{"data": map[string]any{"key": "value"}},
|
|
key: "data",
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "slice value",
|
|
m: map[string]any{"items": []string{"a", "b"}},
|
|
key: "items",
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "empty string value",
|
|
m: map[string]any{"name": ""},
|
|
key: "name",
|
|
expected: "",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := getStringFromMap(tt.m, tt.key)
|
|
if result != tt.expected {
|
|
t.Errorf("getStringFromMap() = %q, want %q", result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHealthCheckStructs(t *testing.T) {
|
|
t.Run("CommitInfo", func(t *testing.T) {
|
|
commit := &CommitInfo{
|
|
SHA: "abc123",
|
|
Message: "Initial commit",
|
|
Author: "user@example.com",
|
|
Date: "2024-01-15T10:30:00Z",
|
|
DaysAgo: 5,
|
|
Available: true,
|
|
}
|
|
if commit.SHA != "abc123" {
|
|
t.Error("SHA mismatch")
|
|
}
|
|
if commit.DaysAgo != 5 {
|
|
t.Error("DaysAgo mismatch")
|
|
}
|
|
})
|
|
|
|
t.Run("IssuesInfo", func(t *testing.T) {
|
|
issues := &IssuesInfo{
|
|
OpenCount: 10,
|
|
TotalCount: 50,
|
|
Available: true,
|
|
}
|
|
if issues.OpenCount != 10 {
|
|
t.Error("OpenCount mismatch")
|
|
}
|
|
if !issues.Available {
|
|
t.Error("Available should be true")
|
|
}
|
|
})
|
|
|
|
t.Run("PullRequestsInfo", func(t *testing.T) {
|
|
prs := &PullRequestsInfo{
|
|
OpenCount: 3,
|
|
TotalCount: 15,
|
|
Available: true,
|
|
}
|
|
if prs.OpenCount != 3 {
|
|
t.Error("OpenCount mismatch")
|
|
}
|
|
})
|
|
|
|
t.Run("WorkflowStatusInfo", func(t *testing.T) {
|
|
wf := &WorkflowStatusInfo{
|
|
LastRunStatus: "completed",
|
|
LastRunConclusion: "success",
|
|
HasRecentRuns: true,
|
|
Available: true,
|
|
Error: "",
|
|
}
|
|
if wf.LastRunConclusion != "success" {
|
|
t.Error("LastRunConclusion mismatch")
|
|
}
|
|
})
|
|
|
|
t.Run("BranchProtectionInfo", func(t *testing.T) {
|
|
bp := &BranchProtectionInfo{
|
|
ProtectedBranchesCount: 2,
|
|
ProtectedBranches: []string{"main", "develop"},
|
|
Available: true,
|
|
}
|
|
if bp.ProtectedBranchesCount != 2 {
|
|
t.Error("ProtectedBranchesCount mismatch")
|
|
}
|
|
if len(bp.ProtectedBranches) != 2 {
|
|
t.Error("ProtectedBranches length mismatch")
|
|
}
|
|
})
|
|
|
|
t.Run("RepositoryInfo", func(t *testing.T) {
|
|
repo := &RepositoryInfo{
|
|
Stars: 100,
|
|
Forks: 20,
|
|
Language: "Go",
|
|
IsPrivate: false,
|
|
IsArchived: false,
|
|
Available: true,
|
|
}
|
|
if repo.Stars != 100 {
|
|
t.Error("Stars mismatch")
|
|
}
|
|
if repo.IsArchived {
|
|
t.Error("IsArchived should be false")
|
|
}
|
|
})
|
|
|
|
t.Run("HealthCheckError", func(t *testing.T) {
|
|
err := HealthCheckError{
|
|
Check: "workflow_status",
|
|
Error: "API not available",
|
|
}
|
|
if err.Check != "workflow_status" {
|
|
t.Error("Check mismatch")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestHealthResultErrors(t *testing.T) {
|
|
result := &HealthResult{
|
|
Repository: "owner/repo",
|
|
HealthScore: 75,
|
|
HealthStatus: "good",
|
|
Errors: []HealthCheckError{
|
|
{Check: "workflow_status", Error: "API not available"},
|
|
{Check: "branch_protection", Error: "No permissions"},
|
|
},
|
|
PartialResult: true,
|
|
CheckedAt: "2024-01-20T10:00:00Z",
|
|
}
|
|
|
|
if !result.PartialResult {
|
|
t.Error("PartialResult should be true when errors exist")
|
|
}
|
|
if len(result.Errors) != 2 {
|
|
t.Errorf("Errors count = %d, want 2", len(result.Errors))
|
|
}
|
|
}
|
|
|
|
func TestHealthResultWithNilFields(t *testing.T) {
|
|
result := &HealthResult{
|
|
Repository: "owner/repo",
|
|
HealthScore: 100,
|
|
HealthStatus: "excellent",
|
|
LastCommit: nil,
|
|
Issues: nil,
|
|
PullRequests: nil,
|
|
WorkflowStatus: nil,
|
|
BranchProtection: nil,
|
|
RepositoryInfo: nil,
|
|
Errors: []HealthCheckError{},
|
|
CheckedAt: "2024-01-20T10:00:00Z",
|
|
PartialResult: false,
|
|
}
|
|
|
|
score := calculateHealthScore(result)
|
|
if score != 100 {
|
|
t.Errorf("calculateHealthScore() with nil fields = %d, want 100", score)
|
|
}
|
|
}
|