mirror of
https://github.com/lukaszraczylo/git-velocity.git
synced 2026-06-05 22:43:56 +00:00
950 lines
20 KiB
Go
950 lines
20 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestLoad(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
configYAML string
|
|
envVars map[string]string
|
|
expectError bool
|
|
validate func(t *testing.T, cfg *Config)
|
|
}{
|
|
{
|
|
name: "valid config with token",
|
|
configYAML: `
|
|
version: "1.0"
|
|
auth:
|
|
github_token: "ghp_test123"
|
|
repositories:
|
|
- owner: "testorg"
|
|
name: "testrepo"
|
|
`,
|
|
expectError: false,
|
|
validate: func(t *testing.T, cfg *Config) {
|
|
assert.Equal(t, "1.0", cfg.Version)
|
|
assert.Equal(t, "ghp_test123", cfg.Auth.GithubToken)
|
|
assert.Len(t, cfg.Repositories, 1)
|
|
assert.Equal(t, "testorg", cfg.Repositories[0].Owner)
|
|
assert.Equal(t, "testrepo", cfg.Repositories[0].Name)
|
|
},
|
|
},
|
|
{
|
|
name: "config with env var substitution",
|
|
configYAML: `
|
|
version: "1.0"
|
|
auth:
|
|
github_token: "${TEST_GITHUB_TOKEN_LOAD}"
|
|
repositories:
|
|
- owner: "testorg"
|
|
name: "testrepo"
|
|
`,
|
|
envVars: map[string]string{
|
|
"TEST_GITHUB_TOKEN_LOAD": "ghp_from_env",
|
|
},
|
|
expectError: false,
|
|
validate: func(t *testing.T, cfg *Config) {
|
|
assert.Equal(t, "ghp_from_env", cfg.Auth.GithubToken)
|
|
},
|
|
},
|
|
{
|
|
name: "config with date range",
|
|
configYAML: `
|
|
version: "1.0"
|
|
auth:
|
|
github_token: "ghp_test123"
|
|
repositories:
|
|
- owner: "testorg"
|
|
name: "testrepo"
|
|
date_range:
|
|
start: "2024-01-01"
|
|
end: "2024-12-31"
|
|
`,
|
|
expectError: false,
|
|
validate: func(t *testing.T, cfg *Config) {
|
|
dateRange, err := cfg.GetParsedDateRange()
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, dateRange.Start)
|
|
assert.NotNil(t, dateRange.End)
|
|
assert.Equal(t, 2024, dateRange.Start.Year())
|
|
assert.Equal(t, time.January, dateRange.Start.Month())
|
|
assert.Equal(t, 1, dateRange.Start.Day())
|
|
},
|
|
},
|
|
{
|
|
name: "config with teams",
|
|
configYAML: `
|
|
version: "1.0"
|
|
auth:
|
|
github_token: "ghp_test123"
|
|
repositories:
|
|
- owner: "testorg"
|
|
name: "testrepo"
|
|
teams:
|
|
- name: "Backend"
|
|
members:
|
|
- "user1"
|
|
- "user2"
|
|
color: "#3b82f6"
|
|
- name: "Frontend"
|
|
members:
|
|
- "user3"
|
|
`,
|
|
expectError: false,
|
|
validate: func(t *testing.T, cfg *Config) {
|
|
assert.Len(t, cfg.Teams, 2)
|
|
assert.Equal(t, "Backend", cfg.Teams[0].Name)
|
|
assert.Contains(t, cfg.Teams[0].Members, "user1")
|
|
assert.Equal(t, "#3b82f6", cfg.Teams[0].Color)
|
|
},
|
|
},
|
|
{
|
|
name: "config with custom scoring",
|
|
configYAML: `
|
|
version: "1.0"
|
|
auth:
|
|
github_token: "ghp_test123"
|
|
repositories:
|
|
- owner: "testorg"
|
|
name: "testrepo"
|
|
scoring:
|
|
enabled: true
|
|
points:
|
|
commit: 20
|
|
pr_merged: 100
|
|
`,
|
|
expectError: false,
|
|
validate: func(t *testing.T, cfg *Config) {
|
|
assert.True(t, cfg.Scoring.Enabled)
|
|
assert.Equal(t, 20, cfg.Scoring.Points.Commit)
|
|
assert.Equal(t, 100, cfg.Scoring.Points.PRMerged)
|
|
},
|
|
},
|
|
{
|
|
name: "config with github app",
|
|
configYAML: `
|
|
version: "1.0"
|
|
auth:
|
|
github_app:
|
|
app_id: 12345
|
|
installation_id: 67890
|
|
private_key: "test-key-content"
|
|
repositories:
|
|
- owner: "testorg"
|
|
name: "testrepo"
|
|
`,
|
|
expectError: false,
|
|
validate: func(t *testing.T, cfg *Config) {
|
|
assert.True(t, cfg.HasGithubApp())
|
|
assert.Equal(t, int64(12345), cfg.Auth.GithubApp.AppID)
|
|
assert.Equal(t, int64(67890), cfg.Auth.GithubApp.InstallationID)
|
|
},
|
|
},
|
|
{
|
|
name: "invalid config - no auth",
|
|
configYAML: `
|
|
version: "1.0"
|
|
repositories:
|
|
- owner: "testorg"
|
|
name: "testrepo"
|
|
`,
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "invalid config - no repositories",
|
|
configYAML: `
|
|
version: "1.0"
|
|
auth:
|
|
github_token: "ghp_test123"
|
|
`,
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "invalid config - invalid date format",
|
|
configYAML: `
|
|
version: "1.0"
|
|
auth:
|
|
github_token: "ghp_test123"
|
|
repositories:
|
|
- owner: "testorg"
|
|
name: "testrepo"
|
|
date_range:
|
|
start: "not-a-date"
|
|
`,
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Set up environment variables (sequential test due to env var usage)
|
|
for k, v := range tt.envVars {
|
|
t.Setenv(k, v)
|
|
}
|
|
|
|
// Create temp config file
|
|
tmpDir := t.TempDir()
|
|
configPath := filepath.Join(tmpDir, "config.yaml")
|
|
err := os.WriteFile(configPath, []byte(tt.configYAML), 0600)
|
|
require.NoError(t, err)
|
|
|
|
// Load config
|
|
cfg, err := Load(configPath)
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, cfg)
|
|
|
|
if tt.validate != nil {
|
|
tt.validate(t, cfg)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExpandEnvVars(t *testing.T) {
|
|
// Note: Tests that use t.Setenv cannot use t.Parallel in subtests
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
envVars map[string]string
|
|
expected string
|
|
}{
|
|
{
|
|
name: "simple substitution",
|
|
input: "token: ${TEST_TOKEN_SIMPLE}",
|
|
envVars: map[string]string{"TEST_TOKEN_SIMPLE": "secret123"},
|
|
expected: "token: secret123",
|
|
},
|
|
{
|
|
name: "multiple substitutions",
|
|
input: "user: ${TEST_USER_MULTI}, pass: ${TEST_PASS_MULTI}",
|
|
envVars: map[string]string{"TEST_USER_MULTI": "admin", "TEST_PASS_MULTI": "123"},
|
|
expected: "user: admin, pass: 123",
|
|
},
|
|
{
|
|
name: "missing env var returns empty",
|
|
input: "token: ${TEST_MISSING_VAR_12345}",
|
|
envVars: map[string]string{},
|
|
expected: "token: ",
|
|
},
|
|
{
|
|
name: "no substitution needed",
|
|
input: "token: plaintext",
|
|
envVars: map[string]string{},
|
|
expected: "token: plaintext",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Sequential test due to env var usage
|
|
for k, v := range tt.envVars {
|
|
t.Setenv(k, v)
|
|
}
|
|
|
|
result := expandEnvVars(tt.input)
|
|
assert.Equal(t, tt.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfig_GetParsedDateRange(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
dateRange DateRangeConfig
|
|
expectError bool
|
|
validate func(t *testing.T, result *ParsedDateRange)
|
|
}{
|
|
{
|
|
name: "valid date range",
|
|
dateRange: DateRangeConfig{
|
|
Start: "2024-01-01",
|
|
End: "2024-12-31",
|
|
},
|
|
expectError: false,
|
|
validate: func(t *testing.T, result *ParsedDateRange) {
|
|
assert.NotNil(t, result.Start)
|
|
assert.NotNil(t, result.End)
|
|
assert.Equal(t, 2024, result.Start.Year())
|
|
assert.Equal(t, time.January, result.Start.Month())
|
|
assert.Equal(t, 2024, result.End.Year())
|
|
assert.Equal(t, time.December, result.End.Month())
|
|
},
|
|
},
|
|
{
|
|
name: "only start date",
|
|
dateRange: DateRangeConfig{
|
|
Start: "2024-06-15",
|
|
},
|
|
expectError: false,
|
|
validate: func(t *testing.T, result *ParsedDateRange) {
|
|
assert.NotNil(t, result.Start)
|
|
assert.NotNil(t, result.End) // Should default to now
|
|
assert.Equal(t, 2024, result.Start.Year())
|
|
assert.Equal(t, time.June, result.Start.Month())
|
|
},
|
|
},
|
|
{
|
|
name: "empty date range defaults to now",
|
|
dateRange: DateRangeConfig{},
|
|
expectError: false,
|
|
validate: func(t *testing.T, result *ParsedDateRange) {
|
|
assert.Nil(t, result.Start)
|
|
assert.NotNil(t, result.End)
|
|
},
|
|
},
|
|
{
|
|
name: "invalid start date",
|
|
dateRange: DateRangeConfig{
|
|
Start: "invalid",
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "invalid end date",
|
|
dateRange: DateRangeConfig{
|
|
Start: "2024-01-01",
|
|
End: "invalid",
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "relative date - 90 days ago",
|
|
dateRange: DateRangeConfig{
|
|
Start: "-90d",
|
|
},
|
|
expectError: false,
|
|
validate: func(t *testing.T, result *ParsedDateRange) {
|
|
assert.NotNil(t, result.Start)
|
|
assert.NotNil(t, result.End)
|
|
// Start should be approximately 90 days ago
|
|
expected := time.Now().AddDate(0, 0, -90)
|
|
assert.Equal(t, expected.Year(), result.Start.Year())
|
|
assert.Equal(t, expected.Month(), result.Start.Month())
|
|
assert.Equal(t, expected.Day(), result.Start.Day())
|
|
},
|
|
},
|
|
{
|
|
name: "relative date - 2 weeks ago",
|
|
dateRange: DateRangeConfig{
|
|
Start: "-2w",
|
|
},
|
|
expectError: false,
|
|
validate: func(t *testing.T, result *ParsedDateRange) {
|
|
assert.NotNil(t, result.Start)
|
|
expected := time.Now().AddDate(0, 0, -14)
|
|
assert.Equal(t, expected.Year(), result.Start.Year())
|
|
assert.Equal(t, expected.Month(), result.Start.Month())
|
|
assert.Equal(t, expected.Day(), result.Start.Day())
|
|
},
|
|
},
|
|
{
|
|
name: "relative date - 3 months ago",
|
|
dateRange: DateRangeConfig{
|
|
Start: "-3m",
|
|
},
|
|
expectError: false,
|
|
validate: func(t *testing.T, result *ParsedDateRange) {
|
|
assert.NotNil(t, result.Start)
|
|
expected := time.Now().AddDate(0, -3, 0)
|
|
assert.Equal(t, expected.Year(), result.Start.Year())
|
|
assert.Equal(t, expected.Month(), result.Start.Month())
|
|
},
|
|
},
|
|
{
|
|
name: "relative date - 1 year ago",
|
|
dateRange: DateRangeConfig{
|
|
Start: "-1y",
|
|
},
|
|
expectError: false,
|
|
validate: func(t *testing.T, result *ParsedDateRange) {
|
|
assert.NotNil(t, result.Start)
|
|
expected := time.Now().AddDate(-1, 0, 0)
|
|
assert.Equal(t, expected.Year(), result.Start.Year())
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := &Config{DateRange: tt.dateRange}
|
|
result, err := cfg.GetParsedDateRange()
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
if tt.validate != nil {
|
|
tt.validate(t, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfig_GetCacheTTL(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
ttl string
|
|
expected time.Duration
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "24 hours",
|
|
ttl: "24h",
|
|
expected: 24 * time.Hour,
|
|
},
|
|
{
|
|
name: "1 hour",
|
|
ttl: "1h",
|
|
expected: 1 * time.Hour,
|
|
},
|
|
{
|
|
name: "30 minutes",
|
|
ttl: "30m",
|
|
expected: 30 * time.Minute,
|
|
},
|
|
{
|
|
name: "empty defaults to 24h",
|
|
ttl: "",
|
|
expected: 24 * time.Hour,
|
|
},
|
|
{
|
|
name: "invalid duration",
|
|
ttl: "invalid",
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := &Config{Cache: CacheConfig{TTL: tt.ttl}}
|
|
result, err := cfg.GetCacheTTL()
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tt.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfig_HasGithubToken(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
token string
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "has token",
|
|
token: "ghp_test123",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "empty token",
|
|
token: "",
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := &Config{Auth: AuthConfig{GithubToken: tt.token}}
|
|
assert.Equal(t, tt.expected, cfg.HasGithubToken())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfig_HasGithubApp(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
appCfg *GithubAppConfig
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "valid github app config",
|
|
appCfg: &GithubAppConfig{
|
|
AppID: 12345,
|
|
InstallationID: 67890,
|
|
PrivateKey: "key-content",
|
|
},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "valid github app config with path",
|
|
appCfg: &GithubAppConfig{
|
|
AppID: 12345,
|
|
InstallationID: 67890,
|
|
PrivateKeyPath: "/path/to/key.pem",
|
|
},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "nil github app config",
|
|
appCfg: nil,
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "missing app id",
|
|
appCfg: &GithubAppConfig{
|
|
InstallationID: 67890,
|
|
PrivateKey: "key-content",
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "missing installation id",
|
|
appCfg: &GithubAppConfig{
|
|
AppID: 12345,
|
|
PrivateKey: "key-content",
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "missing private key",
|
|
appCfg: &GithubAppConfig{
|
|
AppID: 12345,
|
|
InstallationID: 67890,
|
|
},
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := &Config{Auth: AuthConfig{GithubApp: tt.appCfg}}
|
|
assert.Equal(t, tt.expected, cfg.HasGithubApp())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfig_GetTeamForUser(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := &Config{
|
|
Teams: []TeamConfig{
|
|
{
|
|
Name: "Backend",
|
|
Members: []string{"alice", "bob"},
|
|
Color: "#blue",
|
|
},
|
|
{
|
|
Name: "Frontend",
|
|
Members: []string{"charlie", "dave"},
|
|
Color: "#green",
|
|
},
|
|
},
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
username string
|
|
expectedTeam string
|
|
expectNil bool
|
|
}{
|
|
{
|
|
name: "user in first team",
|
|
username: "alice",
|
|
expectedTeam: "Backend",
|
|
},
|
|
{
|
|
name: "user in second team",
|
|
username: "charlie",
|
|
expectedTeam: "Frontend",
|
|
},
|
|
{
|
|
name: "case insensitive match",
|
|
username: "ALICE",
|
|
expectedTeam: "Backend",
|
|
},
|
|
{
|
|
name: "user not in any team",
|
|
username: "unknown",
|
|
expectNil: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
team := cfg.GetTeamForUser(tt.username)
|
|
if tt.expectNil {
|
|
assert.Nil(t, team)
|
|
} else {
|
|
require.NotNil(t, team)
|
|
assert.Equal(t, tt.expectedTeam, team.Name)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfig_IsBot(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Bot patterns are now hardcoded, so we just need IncludeBots: false
|
|
cfg := &Config{
|
|
Options: OptionsConfig{
|
|
IncludeBots: false,
|
|
},
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
username string
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "bot suffix pattern",
|
|
username: "my-app[bot]",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "dependabot prefix pattern",
|
|
username: "dependabot-preview",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "renovate prefix pattern",
|
|
username: "renovate[bot]",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "github-actions prefix pattern",
|
|
username: "github-actions[bot]",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "codecov bot (hardcoded)",
|
|
username: "codecov[bot]",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "snyk bot (hardcoded)",
|
|
username: "snyk-bot",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "regular user",
|
|
username: "alice",
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "user with bot in name",
|
|
username: "robotics-engineer",
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
result := cfg.IsBot(tt.username)
|
|
assert.Equal(t, tt.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfig_IsBot_AdditionalPatterns(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := &Config{
|
|
Options: OptionsConfig{
|
|
IncludeBots: false,
|
|
AdditionalBotPatterns: []string{"my-custom-bot", "ci-*"},
|
|
},
|
|
}
|
|
|
|
// Custom patterns should work
|
|
assert.True(t, cfg.IsBot("my-custom-bot"))
|
|
assert.True(t, cfg.IsBot("ci-runner"))
|
|
assert.True(t, cfg.IsBot("ci-bot"))
|
|
|
|
// Hardcoded patterns should still work
|
|
assert.True(t, cfg.IsBot("dependabot[bot]"))
|
|
assert.True(t, cfg.IsBot("renovate[bot]"))
|
|
|
|
// Regular users should not match
|
|
assert.False(t, cfg.IsBot("alice"))
|
|
}
|
|
|
|
func TestConfig_IsBot_IncludeBots(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := &Config{
|
|
Options: OptionsConfig{
|
|
IncludeBots: true,
|
|
},
|
|
}
|
|
|
|
// When IncludeBots is true, nothing should be considered a bot
|
|
// (even hardcoded patterns are bypassed)
|
|
assert.False(t, cfg.IsBot("my-app[bot]"))
|
|
assert.False(t, cfg.IsBot("dependabot"))
|
|
assert.False(t, cfg.IsBot("renovate[bot]"))
|
|
}
|
|
|
|
func TestMatchPattern(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
s string
|
|
pattern string
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "exact match",
|
|
s: "hello",
|
|
pattern: "hello",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "prefix match",
|
|
s: "hello-world",
|
|
pattern: "hello*",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "suffix match",
|
|
s: "hello-world",
|
|
pattern: "*world",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "contains match",
|
|
s: "hello-world-test",
|
|
pattern: "*world*",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "no match",
|
|
s: "hello",
|
|
pattern: "world",
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "prefix no match",
|
|
s: "hello",
|
|
pattern: "world*",
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
result := matchPattern(tt.s, tt.pattern)
|
|
assert.Equal(t, tt.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfig_GetCustomPeriods(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
customPeriods []CustomPeriod
|
|
expectError bool
|
|
validate func(t *testing.T, periods []ParsedCustomPeriod)
|
|
}{
|
|
{
|
|
name: "valid custom periods",
|
|
customPeriods: []CustomPeriod{
|
|
{Name: "Q1", Start: "2024-01-01", End: "2024-03-31"},
|
|
{Name: "Q2", Start: "2024-04-01", End: "2024-06-30"},
|
|
},
|
|
expectError: false,
|
|
validate: func(t *testing.T, periods []ParsedCustomPeriod) {
|
|
assert.Len(t, periods, 2)
|
|
assert.Equal(t, "Q1", periods[0].Name)
|
|
assert.Equal(t, time.January, periods[0].Start.Month())
|
|
assert.Equal(t, time.March, periods[0].End.Month())
|
|
},
|
|
},
|
|
{
|
|
name: "empty custom periods",
|
|
customPeriods: []CustomPeriod{},
|
|
expectError: false,
|
|
validate: func(t *testing.T, periods []ParsedCustomPeriod) {
|
|
assert.Empty(t, periods)
|
|
},
|
|
},
|
|
{
|
|
name: "invalid start date",
|
|
customPeriods: []CustomPeriod{
|
|
{Name: "Bad", Start: "invalid", End: "2024-03-31"},
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "invalid end date",
|
|
customPeriods: []CustomPeriod{
|
|
{Name: "Bad", Start: "2024-01-01", End: "invalid"},
|
|
},
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := &Config{CustomPeriods: tt.customPeriods}
|
|
periods, err := cfg.GetCustomPeriods()
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
if tt.validate != nil {
|
|
tt.validate(t, periods)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDefaultConfig(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := DefaultConfig()
|
|
|
|
assert.Equal(t, "1.0", cfg.Version)
|
|
assert.Contains(t, cfg.Granularity, "daily")
|
|
assert.Contains(t, cfg.Granularity, "weekly")
|
|
assert.Contains(t, cfg.Granularity, "monthly")
|
|
assert.True(t, cfg.Scoring.Enabled)
|
|
assert.Equal(t, 10, cfg.Scoring.Points.Commit)
|
|
assert.Equal(t, 50, cfg.Scoring.Points.PRMerged)
|
|
assert.NotEmpty(t, cfg.Scoring.GetAchievements())
|
|
assert.Equal(t, "./dist", cfg.Output.Directory)
|
|
assert.True(t, cfg.Cache.Enabled)
|
|
assert.Equal(t, "./.cache", cfg.Cache.Directory)
|
|
assert.Equal(t, "24h", cfg.Cache.TTL)
|
|
assert.Equal(t, 5, cfg.Options.ConcurrentRequests)
|
|
assert.False(t, cfg.Options.IncludeBots)
|
|
}
|
|
|
|
func TestConfig_GetGithubAppPrivateKey(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("returns inline key", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := &Config{
|
|
Auth: AuthConfig{
|
|
GithubApp: &GithubAppConfig{
|
|
PrivateKey: "inline-key-content",
|
|
},
|
|
},
|
|
}
|
|
|
|
key, err := cfg.GetGithubAppPrivateKey()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []byte("inline-key-content"), key)
|
|
})
|
|
|
|
t.Run("returns key from file", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tmpDir := t.TempDir()
|
|
keyPath := filepath.Join(tmpDir, "key.pem")
|
|
err := os.WriteFile(keyPath, []byte("file-key-content"), 0600)
|
|
require.NoError(t, err)
|
|
|
|
cfg := &Config{
|
|
Auth: AuthConfig{
|
|
GithubApp: &GithubAppConfig{
|
|
PrivateKeyPath: keyPath,
|
|
},
|
|
},
|
|
}
|
|
|
|
key, err := cfg.GetGithubAppPrivateKey()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []byte("file-key-content"), key)
|
|
})
|
|
|
|
t.Run("error when no github app configured", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := &Config{}
|
|
|
|
_, err := cfg.GetGithubAppPrivateKey()
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("error when no key configured", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := &Config{
|
|
Auth: AuthConfig{
|
|
GithubApp: &GithubAppConfig{
|
|
AppID: 12345,
|
|
InstallationID: 67890,
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err := cfg.GetGithubAppPrivateKey()
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestLoad_FileNotFound(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
_, err := Load("/nonexistent/path/config.yaml")
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "failed to read config file")
|
|
}
|
|
|
|
func TestLoad_InvalidYAML(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
configPath := filepath.Join(tmpDir, "config.yaml")
|
|
err := os.WriteFile(configPath, []byte("invalid: yaml: content: ["), 0600)
|
|
require.NoError(t, err)
|
|
|
|
_, err = Load(configPath)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "failed to parse config file")
|
|
}
|