mirror of
https://github.com/ivuorinen/gh-action-readme.git
synced 2026-02-13 05:49:06 +00:00
This commit represents a comprehensive refactoring of the codebase focused on improving code quality, testability, and maintainability. Key improvements: - Implement dependency injection and interface-based architecture - Add comprehensive test framework with fixtures and test suites - Fix all linting issues (errcheck, gosec, staticcheck, goconst, etc.) - Achieve full EditorConfig compliance across all files - Replace hardcoded test data with proper fixture files - Add configuration loader with hierarchical config support - Improve error handling with contextual information - Add progress indicators for better user feedback - Enhance Makefile with help system and improved editorconfig commands - Consolidate constants and remove deprecated code - Strengthen validation logic for GitHub Actions - Add focused consumer interfaces for better separation of concerns Testing improvements: - Add comprehensive integration tests - Implement test executor pattern for better test organization - Create extensive YAML fixture library for testing - Fix all failing tests and improve test coverage - Add validation test fixtures to avoid embedded YAML in Go files Build and tooling: - Update Makefile to show help by default - Fix editorconfig commands to use eclint properly - Add comprehensive help documentation to all make targets - Improve file selection patterns to avoid glob errors This refactoring maintains backward compatibility while significantly improving the internal architecture and developer experience.
281 lines
7.1 KiB
Go
281 lines
7.1 KiB
Go
package helpers
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/ivuorinen/gh-action-readme/internal"
|
|
"github.com/ivuorinen/gh-action-readme/testutil"
|
|
)
|
|
|
|
func TestGetCurrentDir(t *testing.T) {
|
|
t.Run("successfully get current directory", func(t *testing.T) {
|
|
currentDir, err := GetCurrentDir()
|
|
|
|
testutil.AssertNoError(t, err)
|
|
|
|
if currentDir == "" {
|
|
t.Error("expected non-empty current directory")
|
|
}
|
|
|
|
// Verify it's an absolute path
|
|
if !filepath.IsAbs(currentDir) {
|
|
t.Errorf("expected absolute path, got: %s", currentDir)
|
|
}
|
|
|
|
// Verify the directory actually exists
|
|
if _, err := os.Stat(currentDir); os.IsNotExist(err) {
|
|
t.Errorf("current directory does not exist: %s", currentDir)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestSetupGeneratorContext(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
config *internal.AppConfig
|
|
}{
|
|
{
|
|
name: "basic config",
|
|
config: &internal.AppConfig{
|
|
Theme: "default",
|
|
OutputFormat: "md",
|
|
OutputDir: ".",
|
|
Verbose: false,
|
|
Quiet: false,
|
|
},
|
|
},
|
|
{
|
|
name: "verbose config",
|
|
config: &internal.AppConfig{
|
|
Theme: "github",
|
|
OutputFormat: "html",
|
|
OutputDir: "/tmp",
|
|
Verbose: true,
|
|
Quiet: false,
|
|
},
|
|
},
|
|
{
|
|
name: "quiet config",
|
|
config: &internal.AppConfig{
|
|
Theme: "minimal",
|
|
OutputFormat: "json",
|
|
OutputDir: ".",
|
|
Verbose: false,
|
|
Quiet: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
generator, currentDir, err := SetupGeneratorContext(tt.config)
|
|
|
|
// Verify no error occurred
|
|
testutil.AssertNoError(t, err)
|
|
|
|
// Verify generator was created
|
|
if generator == nil {
|
|
t.Error("expected generator to be created")
|
|
return
|
|
}
|
|
|
|
// Verify current directory is returned
|
|
if currentDir == "" {
|
|
t.Error("expected non-empty current directory")
|
|
}
|
|
|
|
if !filepath.IsAbs(currentDir) {
|
|
t.Errorf("expected absolute path, got: %s", currentDir)
|
|
}
|
|
|
|
// Verify generator has the correct config
|
|
if generator.Config != tt.config {
|
|
t.Error("expected generator to have the provided config")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFindGitRepoRoot(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
setupFunc func(t *testing.T, tmpDir string) string
|
|
expectGit bool
|
|
}{
|
|
{
|
|
name: "directory with git repository",
|
|
setupFunc: func(t *testing.T, tmpDir string) string {
|
|
// Create .git directory
|
|
gitDir := filepath.Join(tmpDir, ".git")
|
|
err := os.MkdirAll(gitDir, 0750) // #nosec G301 -- test directory permissions
|
|
testutil.AssertNoError(t, err)
|
|
|
|
// Create subdirectory to test from
|
|
subDir := filepath.Join(tmpDir, "subdir")
|
|
err = os.MkdirAll(subDir, 0750) // #nosec G301 -- test directory permissions
|
|
testutil.AssertNoError(t, err)
|
|
|
|
return subDir
|
|
},
|
|
expectGit: true,
|
|
},
|
|
{
|
|
name: "directory without git repository",
|
|
setupFunc: func(_ *testing.T, tmpDir string) string {
|
|
// Just return the temp directory without .git
|
|
return tmpDir
|
|
},
|
|
expectGit: false,
|
|
},
|
|
{
|
|
name: "nested directory in git repository",
|
|
setupFunc: func(t *testing.T, tmpDir string) string {
|
|
// Create .git directory at root
|
|
gitDir := filepath.Join(tmpDir, ".git")
|
|
err := os.MkdirAll(gitDir, 0750) // #nosec G301 -- test directory permissions
|
|
testutil.AssertNoError(t, err)
|
|
|
|
// Create deeply nested subdirectory
|
|
nestedDir := filepath.Join(tmpDir, "a", "b", "c")
|
|
err = os.MkdirAll(nestedDir, 0750) // #nosec G301 -- test directory permissions
|
|
testutil.AssertNoError(t, err)
|
|
|
|
return nestedDir
|
|
},
|
|
expectGit: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tmpDir, cleanup := testutil.TempDir(t)
|
|
defer cleanup()
|
|
|
|
testDir := tt.setupFunc(t, tmpDir)
|
|
repoRoot := FindGitRepoRoot(testDir)
|
|
|
|
if tt.expectGit {
|
|
if repoRoot == "" {
|
|
t.Error("expected to find git repository root, got empty string")
|
|
}
|
|
if !strings.Contains(repoRoot, tmpDir) {
|
|
t.Errorf("expected repo root to be within %s, got %s", tmpDir, repoRoot)
|
|
}
|
|
} else if repoRoot != "" {
|
|
t.Errorf("expected empty string for non-git directory, got %s", repoRoot)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetGitRepoRootAndInfo(t *testing.T) {
|
|
t.Run("valid git repository with complete info", func(t *testing.T) {
|
|
tmpDir, cleanup := testutil.TempDir(t)
|
|
defer cleanup()
|
|
|
|
testDir := setupCompleteGitRepo(t, tmpDir)
|
|
repoRoot, gitInfo, err := GetGitRepoRootAndInfo(testDir)
|
|
|
|
testutil.AssertNoError(t, err)
|
|
verifyRepoRoot(t, repoRoot, tmpDir)
|
|
if gitInfo == nil {
|
|
t.Error("expected git info to be returned, got nil")
|
|
}
|
|
})
|
|
|
|
t.Run("git repository but info detection fails", func(t *testing.T) {
|
|
tmpDir, cleanup := testutil.TempDir(t)
|
|
defer cleanup()
|
|
|
|
testDir := setupMinimalGitRepo(t, tmpDir)
|
|
repoRoot, gitInfo, err := GetGitRepoRootAndInfo(testDir)
|
|
|
|
testutil.AssertNoError(t, err)
|
|
verifyRepoRoot(t, repoRoot, tmpDir)
|
|
if gitInfo != nil {
|
|
t.Logf("got unexpected git info: %+v", gitInfo)
|
|
}
|
|
})
|
|
|
|
t.Run("directory without git repository", func(t *testing.T) {
|
|
tmpDir, cleanup := testutil.TempDir(t)
|
|
defer cleanup()
|
|
|
|
repoRoot, gitInfo, err := GetGitRepoRootAndInfo(tmpDir)
|
|
|
|
if err == nil {
|
|
t.Error("expected error, got nil")
|
|
}
|
|
if repoRoot != "" {
|
|
t.Errorf("expected empty repo root, got: %s", repoRoot)
|
|
}
|
|
if gitInfo != nil {
|
|
t.Errorf("expected nil git info, got: %+v", gitInfo)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Helper functions to reduce complexity.
|
|
func setupCompleteGitRepo(t *testing.T, tmpDir string) string {
|
|
// Create .git directory
|
|
gitDir := filepath.Join(tmpDir, ".git")
|
|
err := os.MkdirAll(gitDir, 0750) // #nosec G301 -- test directory permissions
|
|
testutil.AssertNoError(t, err)
|
|
|
|
// Create a basic git config to make it look like a real repo
|
|
configContent := `[core]
|
|
repositoryformatversion = 0
|
|
filemode = true
|
|
bare = false
|
|
[remote "origin"]
|
|
url = https://github.com/test/repo.git
|
|
fetch = +refs/heads/*:refs/remotes/origin/*
|
|
[branch "main"]
|
|
remote = origin
|
|
merge = refs/heads/main
|
|
`
|
|
configPath := filepath.Join(gitDir, "config")
|
|
err = os.WriteFile(configPath, []byte(configContent), 0600) // #nosec G306 -- test file permissions
|
|
testutil.AssertNoError(t, err)
|
|
|
|
return tmpDir
|
|
}
|
|
|
|
func setupMinimalGitRepo(t *testing.T, tmpDir string) string {
|
|
// Create .git directory but with minimal content
|
|
gitDir := filepath.Join(tmpDir, ".git")
|
|
err := os.MkdirAll(gitDir, 0750) // #nosec G301 -- test directory permissions
|
|
testutil.AssertNoError(t, err)
|
|
|
|
return tmpDir
|
|
}
|
|
|
|
func verifyRepoRoot(t *testing.T, repoRoot, tmpDir string) {
|
|
if repoRoot != "" && !strings.Contains(repoRoot, tmpDir) {
|
|
t.Errorf("expected repo root to be within %s, got %s", tmpDir, repoRoot)
|
|
}
|
|
}
|
|
|
|
// Test error handling in GetGitRepoRootAndInfo.
|
|
func TestGetGitRepoRootAndInfo_ErrorHandling(t *testing.T) {
|
|
t.Run("nonexistent directory", func(t *testing.T) {
|
|
nonexistentPath := "/this/path/should/not/exist"
|
|
repoRoot, gitInfo, err := GetGitRepoRootAndInfo(nonexistentPath)
|
|
|
|
if err == nil {
|
|
t.Error("expected error for nonexistent directory")
|
|
}
|
|
|
|
if repoRoot != "" {
|
|
t.Errorf("expected empty repo root, got: %s", repoRoot)
|
|
}
|
|
|
|
if gitInfo != nil {
|
|
t.Errorf("expected nil git info, got: %+v", gitInfo)
|
|
}
|
|
})
|
|
}
|