mirror of
https://github.com/lukaszraczylo/git-velocity.git
synced 2026-07-01 05:29:24 +00:00
Initial commit.
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
package site
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
json "github.com/goccy/go-json"
|
||||
"github.com/lukaszraczylo/git-velocity/internal/config"
|
||||
"github.com/lukaszraczylo/git-velocity/internal/domain/models"
|
||||
)
|
||||
|
||||
//go:embed dist/*
|
||||
var spaFS embed.FS
|
||||
|
||||
// Generator handles static site generation
|
||||
type Generator struct {
|
||||
outputDir string
|
||||
config *config.Config
|
||||
}
|
||||
|
||||
// NewGenerator creates a new site generator
|
||||
func NewGenerator(outputDir string, cfg *config.Config) (*Generator, error) {
|
||||
return &Generator{
|
||||
outputDir: outputDir,
|
||||
config: cfg,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Generate creates the static site from metrics
|
||||
func (g *Generator) Generate(metrics *models.GlobalMetrics) error {
|
||||
// Create output directory
|
||||
if err := os.MkdirAll(g.outputDir, 0750); err != nil {
|
||||
return fmt.Errorf("failed to create output directory: %w", err)
|
||||
}
|
||||
|
||||
// Generate data files
|
||||
if err := g.generateDataFiles(metrics); err != nil {
|
||||
return fmt.Errorf("failed to generate data files: %w", err)
|
||||
}
|
||||
|
||||
// Copy Vue SPA files
|
||||
if err := g.copySPAFiles(); err != nil {
|
||||
return fmt.Errorf("failed to copy SPA files: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Generator) generateDataFiles(metrics *models.GlobalMetrics) error {
|
||||
dataDir := filepath.Join(g.outputDir, "data")
|
||||
|
||||
// Clean old data directory to ensure fresh state
|
||||
if err := os.RemoveAll(dataDir); err != nil {
|
||||
return fmt.Errorf("failed to clean data directory: %w", err)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(dataDir, 0750); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Prepare global data with timestamp
|
||||
globalData := struct {
|
||||
*models.GlobalMetrics
|
||||
GeneratedAt time.Time `json:"generated_at"`
|
||||
}{
|
||||
GlobalMetrics: metrics,
|
||||
GeneratedAt: time.Now(),
|
||||
}
|
||||
|
||||
// Global metrics
|
||||
if err := writeJSON(filepath.Join(dataDir, "global.json"), globalData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Leaderboard
|
||||
if err := writeJSON(filepath.Join(dataDir, "leaderboard.json"), metrics.Leaderboard); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Per-repository data
|
||||
for _, repo := range metrics.Repositories {
|
||||
repoDir := filepath.Join(dataDir, "repos", repo.Owner, repo.Name)
|
||||
if err := os.MkdirAll(repoDir, 0750); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeJSON(filepath.Join(repoDir, "metrics.json"), repo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Per-team data
|
||||
if len(metrics.Teams) > 0 {
|
||||
teamDir := filepath.Join(dataDir, "teams")
|
||||
if err := os.MkdirAll(teamDir, 0750); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, team := range metrics.Teams {
|
||||
if err := writeJSON(filepath.Join(teamDir, slugify(team.Name)+".json"), team); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Per-contributor data
|
||||
contributorsSeen := make(map[string]bool)
|
||||
contributorDir := filepath.Join(dataDir, "contributors")
|
||||
if err := os.MkdirAll(contributorDir, 0750); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, repo := range metrics.Repositories {
|
||||
for _, contributor := range repo.Contributors {
|
||||
if contributorsSeen[contributor.Login] {
|
||||
continue
|
||||
}
|
||||
contributorsSeen[contributor.Login] = true
|
||||
|
||||
if err := writeJSON(filepath.Join(contributorDir, contributor.Login+".json"), contributor); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Generator) copySPAFiles() error {
|
||||
return fs.WalkDir(spaFS, "dist", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Skip the root dist directory itself
|
||||
if path == "dist" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Calculate the relative path from "dist/"
|
||||
relPath := strings.TrimPrefix(path, "dist/")
|
||||
destPath := filepath.Join(g.outputDir, relPath)
|
||||
|
||||
if d.IsDir() {
|
||||
return os.MkdirAll(destPath, 0750)
|
||||
}
|
||||
|
||||
// Read file from embedded FS
|
||||
content, err := spaFS.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read embedded file %s: %w", path, err)
|
||||
}
|
||||
|
||||
// Write to destination
|
||||
return os.WriteFile(destPath, content, 0600)
|
||||
})
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
func writeJSON(path string, data interface{}) error {
|
||||
cleanPath := filepath.Clean(path)
|
||||
file, err := os.OpenFile(cleanPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) // #nosec G304 -- path is constructed internally
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
encoder := json.NewEncoder(file)
|
||||
encoder.SetIndent("", " ")
|
||||
return encoder.Encode(data)
|
||||
}
|
||||
|
||||
func slugify(s string) string {
|
||||
s = strings.ToLower(s)
|
||||
s = strings.ReplaceAll(s, " ", "-")
|
||||
s = strings.ReplaceAll(s, "_", "-")
|
||||
return s
|
||||
}
|
||||
@@ -0,0 +1,502 @@
|
||||
package site
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
json "github.com/goccy/go-json"
|
||||
"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 TestNewGenerator(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
gen, err := NewGenerator("/tmp/output", cfg)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, gen)
|
||||
assert.Equal(t, "/tmp/output", gen.outputDir)
|
||||
assert.Equal(t, cfg, gen.config)
|
||||
}
|
||||
|
||||
func TestGenerator_GenerateCreatesOutputDir(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
outputDir := filepath.Join(tempDir, "new-output")
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
gen, err := NewGenerator(outputDir, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
metrics := &models.GlobalMetrics{
|
||||
Period: models.Period{
|
||||
Start: time.Now().Add(-24 * time.Hour),
|
||||
End: time.Now(),
|
||||
},
|
||||
}
|
||||
|
||||
err = gen.Generate(metrics)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify output directory was created
|
||||
info, err := os.Stat(outputDir)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, info.IsDir())
|
||||
}
|
||||
|
||||
func TestGenerator_GenerateCreatesDataDir(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
gen, err := NewGenerator(tempDir, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
metrics := &models.GlobalMetrics{}
|
||||
|
||||
err = gen.Generate(metrics)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify data directory was created
|
||||
dataDir := filepath.Join(tempDir, "data")
|
||||
info, err := os.Stat(dataDir)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, info.IsDir())
|
||||
}
|
||||
|
||||
func TestGenerator_GenerateGlobalJSON(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
gen, err := NewGenerator(tempDir, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
metrics := &models.GlobalMetrics{
|
||||
TotalContributors: 5,
|
||||
TotalCommits: 100,
|
||||
TotalPRs: 50,
|
||||
TotalReviews: 75,
|
||||
TotalLinesAdded: 10000,
|
||||
TotalLinesDeleted: 5000,
|
||||
}
|
||||
|
||||
err = gen.Generate(metrics)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Read and verify global.json
|
||||
globalPath := filepath.Join(tempDir, "data", "global.json")
|
||||
data, err := os.ReadFile(globalPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
var result struct {
|
||||
TotalContributors int `json:"total_contributors"`
|
||||
TotalCommits int `json:"total_commits"`
|
||||
TotalPRs int `json:"total_prs"`
|
||||
TotalReviews int `json:"total_reviews"`
|
||||
TotalLinesAdded int `json:"total_lines_added"`
|
||||
TotalLinesDeleted int `json:"total_lines_deleted"`
|
||||
GeneratedAt time.Time `json:"GeneratedAt"`
|
||||
}
|
||||
err = json.Unmarshal(data, &result)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 5, result.TotalContributors)
|
||||
assert.Equal(t, 100, result.TotalCommits)
|
||||
assert.Equal(t, 50, result.TotalPRs)
|
||||
assert.Equal(t, 75, result.TotalReviews)
|
||||
assert.Equal(t, 10000, result.TotalLinesAdded)
|
||||
assert.Equal(t, 5000, result.TotalLinesDeleted)
|
||||
assert.False(t, result.GeneratedAt.IsZero())
|
||||
}
|
||||
|
||||
func TestGenerator_GenerateLeaderboardJSON(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
gen, err := NewGenerator(tempDir, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
metrics := &models.GlobalMetrics{
|
||||
Leaderboard: []models.LeaderboardEntry{
|
||||
{Rank: 1, Login: "user1", Score: 1000},
|
||||
{Rank: 2, Login: "user2", Score: 800},
|
||||
{Rank: 3, Login: "user3", Score: 600},
|
||||
},
|
||||
}
|
||||
|
||||
err = gen.Generate(metrics)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Read and verify leaderboard.json
|
||||
leaderboardPath := filepath.Join(tempDir, "data", "leaderboard.json")
|
||||
data, err := os.ReadFile(leaderboardPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
var result []models.LeaderboardEntry
|
||||
err = json.Unmarshal(data, &result)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, result, 3)
|
||||
assert.Equal(t, "user1", result[0].Login)
|
||||
assert.Equal(t, 1000, result[0].Score)
|
||||
assert.Equal(t, "user2", result[1].Login)
|
||||
assert.Equal(t, 800, result[1].Score)
|
||||
}
|
||||
|
||||
func TestGenerator_GenerateRepositoryJSON(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
gen, err := NewGenerator(tempDir, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
metrics := &models.GlobalMetrics{
|
||||
Repositories: []models.RepositoryMetrics{
|
||||
{
|
||||
Owner: "myorg",
|
||||
Name: "myrepo",
|
||||
TotalCommits: 42,
|
||||
TotalPRs: 10,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = gen.Generate(metrics)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Read and verify repository metrics
|
||||
repoPath := filepath.Join(tempDir, "data", "repos", "myorg", "myrepo", "metrics.json")
|
||||
data, err := os.ReadFile(repoPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
var result models.RepositoryMetrics
|
||||
err = json.Unmarshal(data, &result)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "myorg", result.Owner)
|
||||
assert.Equal(t, "myrepo", result.Name)
|
||||
assert.Equal(t, 42, result.TotalCommits)
|
||||
assert.Equal(t, 10, result.TotalPRs)
|
||||
}
|
||||
|
||||
func TestGenerator_GenerateMultipleRepositories(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
gen, err := NewGenerator(tempDir, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
metrics := &models.GlobalMetrics{
|
||||
Repositories: []models.RepositoryMetrics{
|
||||
{Owner: "org1", Name: "repo1", TotalCommits: 100},
|
||||
{Owner: "org1", Name: "repo2", TotalCommits: 200},
|
||||
{Owner: "org2", Name: "repo3", TotalCommits: 300},
|
||||
},
|
||||
}
|
||||
|
||||
err = gen.Generate(metrics)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify all repository files exist
|
||||
_, err = os.Stat(filepath.Join(tempDir, "data", "repos", "org1", "repo1", "metrics.json"))
|
||||
assert.NoError(t, err)
|
||||
_, err = os.Stat(filepath.Join(tempDir, "data", "repos", "org1", "repo2", "metrics.json"))
|
||||
assert.NoError(t, err)
|
||||
_, err = os.Stat(filepath.Join(tempDir, "data", "repos", "org2", "repo3", "metrics.json"))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGenerator_GenerateTeamJSON(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
gen, err := NewGenerator(tempDir, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
metrics := &models.GlobalMetrics{
|
||||
Teams: []models.TeamMetrics{
|
||||
{
|
||||
Name: "Backend Team",
|
||||
Color: "#ff0000",
|
||||
Members: []string{"user1", "user2"},
|
||||
TotalScore: 1500,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = gen.Generate(metrics)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Read and verify team JSON (slugified name)
|
||||
teamPath := filepath.Join(tempDir, "data", "teams", "backend-team.json")
|
||||
data, err := os.ReadFile(teamPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
var result models.TeamMetrics
|
||||
err = json.Unmarshal(data, &result)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "Backend Team", result.Name)
|
||||
assert.Equal(t, "#ff0000", result.Color)
|
||||
assert.Equal(t, 1500, result.TotalScore)
|
||||
assert.Len(t, result.Members, 2)
|
||||
}
|
||||
|
||||
func TestGenerator_GenerateContributorJSON(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
gen, err := NewGenerator(tempDir, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
metrics := &models.GlobalMetrics{
|
||||
Repositories: []models.RepositoryMetrics{
|
||||
{
|
||||
Contributors: []models.ContributorMetrics{
|
||||
{
|
||||
Login: "john-doe",
|
||||
Name: "John Doe",
|
||||
CommitCount: 50,
|
||||
PRsOpened: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = gen.Generate(metrics)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Read and verify contributor JSON
|
||||
contributorPath := filepath.Join(tempDir, "data", "contributors", "john-doe.json")
|
||||
data, err := os.ReadFile(contributorPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
var result models.ContributorMetrics
|
||||
err = json.Unmarshal(data, &result)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "john-doe", result.Login)
|
||||
assert.Equal(t, "John Doe", result.Name)
|
||||
assert.Equal(t, 50, result.CommitCount)
|
||||
assert.Equal(t, 10, result.PRsOpened)
|
||||
}
|
||||
|
||||
func TestGenerator_ContributorDeduplication(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
gen, err := NewGenerator(tempDir, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Same contributor in multiple repos
|
||||
metrics := &models.GlobalMetrics{
|
||||
Repositories: []models.RepositoryMetrics{
|
||||
{
|
||||
Contributors: []models.ContributorMetrics{
|
||||
{Login: "user1", CommitCount: 50},
|
||||
},
|
||||
},
|
||||
{
|
||||
Contributors: []models.ContributorMetrics{
|
||||
{Login: "user1", CommitCount: 75}, // Same user, different count
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = gen.Generate(metrics)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Should only have one contributor file (first one seen)
|
||||
contributorPath := filepath.Join(tempDir, "data", "contributors", "user1.json")
|
||||
data, err := os.ReadFile(contributorPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
var result models.ContributorMetrics
|
||||
err = json.Unmarshal(data, &result)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Should be the first one (50 commits)
|
||||
assert.Equal(t, 50, result.CommitCount)
|
||||
}
|
||||
|
||||
func TestGenerator_NoTeamsDoesNotCreateTeamDir(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
gen, err := NewGenerator(tempDir, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
metrics := &models.GlobalMetrics{
|
||||
Teams: []models.TeamMetrics{}, // Empty teams
|
||||
}
|
||||
|
||||
err = gen.Generate(metrics)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Team directory should not exist
|
||||
teamDir := filepath.Join(tempDir, "data", "teams")
|
||||
_, err = os.Stat(teamDir)
|
||||
assert.True(t, os.IsNotExist(err))
|
||||
}
|
||||
|
||||
func TestSlugify(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"Backend Team", "backend-team"},
|
||||
{"Frontend_Team", "frontend-team"},
|
||||
{"UPPER CASE", "upper-case"},
|
||||
{"already-slug", "already-slug"},
|
||||
{"Multiple Spaces", "multiple---spaces"},
|
||||
{"Mixed_And Spaced", "mixed-and-spaced"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.input, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
result := slugify(tt.input)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteJSON(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
testData := map[string]interface{}{
|
||||
"key": "value",
|
||||
"number": 42,
|
||||
"nested": map[string]string{
|
||||
"inner": "data",
|
||||
},
|
||||
}
|
||||
|
||||
path := filepath.Join(tempDir, "test.json")
|
||||
err := writeJSON(path, testData)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify file was created and is valid JSON
|
||||
data, err := os.ReadFile(path)
|
||||
require.NoError(t, err)
|
||||
|
||||
var result map[string]interface{}
|
||||
err = json.Unmarshal(data, &result)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "value", result["key"])
|
||||
assert.Equal(t, float64(42), result["number"]) // JSON numbers are float64
|
||||
}
|
||||
|
||||
func TestWriteJSON_Indented(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
testData := map[string]string{"key": "value"}
|
||||
path := filepath.Join(tempDir, "test.json")
|
||||
|
||||
err := writeJSON(path, testData)
|
||||
require.NoError(t, err)
|
||||
|
||||
data, err := os.ReadFile(path)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Should be formatted with indentation
|
||||
assert.Contains(t, string(data), "\n")
|
||||
assert.Contains(t, string(data), " ") // 2-space indent
|
||||
}
|
||||
|
||||
func TestWriteJSON_ErrorOnInvalidPath(t *testing.T) {
|
||||
// Try to write to a path that doesn't exist
|
||||
path := "/nonexistent/directory/test.json"
|
||||
err := writeJSON(path, "data")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestGenerator_GenerateWithFullMetrics(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
gen, err := NewGenerator(tempDir, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create comprehensive metrics
|
||||
metrics := &models.GlobalMetrics{
|
||||
Period: models.Period{
|
||||
Start: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
End: time.Date(2024, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||
Granularity: "monthly",
|
||||
Label: "2024",
|
||||
},
|
||||
TotalContributors: 10,
|
||||
TotalCommits: 500,
|
||||
TotalPRs: 100,
|
||||
TotalReviews: 200,
|
||||
TotalLinesAdded: 50000,
|
||||
TotalLinesDeleted: 25000,
|
||||
Repositories: []models.RepositoryMetrics{
|
||||
{
|
||||
Owner: "org",
|
||||
Name: "repo1",
|
||||
TotalCommits: 300,
|
||||
TotalPRs: 60,
|
||||
ActiveContributors: 5,
|
||||
Contributors: []models.ContributorMetrics{
|
||||
{Login: "alice", Name: "Alice", CommitCount: 100},
|
||||
{Login: "bob", Name: "Bob", CommitCount: 200},
|
||||
},
|
||||
},
|
||||
{
|
||||
Owner: "org",
|
||||
Name: "repo2",
|
||||
TotalCommits: 200,
|
||||
TotalPRs: 40,
|
||||
ActiveContributors: 5,
|
||||
Contributors: []models.ContributorMetrics{
|
||||
{Login: "alice", Name: "Alice", CommitCount: 50},
|
||||
{Login: "charlie", Name: "Charlie", CommitCount: 150},
|
||||
},
|
||||
},
|
||||
},
|
||||
Teams: []models.TeamMetrics{
|
||||
{
|
||||
Name: "Core Team",
|
||||
Members: []string{"alice", "bob"},
|
||||
TotalScore: 5000,
|
||||
},
|
||||
},
|
||||
Leaderboard: []models.LeaderboardEntry{
|
||||
{Rank: 1, Login: "alice", Score: 3000},
|
||||
{Rank: 2, Login: "bob", Score: 2000},
|
||||
{Rank: 3, Login: "charlie", Score: 1500},
|
||||
},
|
||||
}
|
||||
|
||||
err = gen.Generate(metrics)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify all expected files exist
|
||||
expectedPaths := []string{
|
||||
filepath.Join(tempDir, "data", "global.json"),
|
||||
filepath.Join(tempDir, "data", "leaderboard.json"),
|
||||
filepath.Join(tempDir, "data", "repos", "org", "repo1", "metrics.json"),
|
||||
filepath.Join(tempDir, "data", "repos", "org", "repo2", "metrics.json"),
|
||||
filepath.Join(tempDir, "data", "teams", "core-team.json"),
|
||||
filepath.Join(tempDir, "data", "contributors", "alice.json"),
|
||||
filepath.Join(tempDir, "data", "contributors", "bob.json"),
|
||||
filepath.Join(tempDir, "data", "contributors", "charlie.json"),
|
||||
}
|
||||
|
||||
for _, path := range expectedPaths {
|
||||
_, err := os.Stat(path)
|
||||
assert.NoError(t, err, "Expected file to exist: %s", path)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user