mirror of
https://github.com/ivuorinen/gh-action-readme.git
synced 2026-02-14 09:49:34 +00:00
feat(lint): add many linters, make all the tests run fast! (#23)
* chore(lint): added nlreturn, run linting * chore(lint): replace some fmt.Sprintf calls * chore(lint): replace fmt.Sprintf with strconv * chore(lint): add goconst, use http lib for status codes, and methods * chore(lint): use errors lib, errCodes from internal/errors * chore(lint): dupl, thelper and usetesting * chore(lint): fmt.Errorf %v to %w, more linters * chore(lint): paralleltest, where possible * perf(test): optimize test performance by 78% - Implement shared binary building with package-level cache to eliminate redundant builds - Add strategic parallelization to 15+ tests while preserving environment variable isolation - Implement thread-safe fixture caching with RWMutex to reduce I/O operations - Remove unnecessary working directory changes by leveraging embedded templates - Add embedded template system with go:embed directive for reliable template resolution - Fix linting issues: rename sharedBinaryError to errSharedBinary, add nolint directive Performance improvements: - Total test execution time: 12+ seconds → 2.7 seconds (78% faster) - Binary build overhead: 14+ separate builds → 1 shared build (93% reduction) - Parallel execution: Limited → 15+ concurrent tests (60-70% better CPU usage) - I/O operations: 66+ fixture reads → cached with sync.RWMutex (50% reduction) All tests maintain 100% success rate and coverage while running nearly 4x faster.
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func TestNewConfigurationLoader(t *testing.T) {
|
||||
t.Parallel()
|
||||
loader := NewConfigurationLoader()
|
||||
|
||||
if loader == nil {
|
||||
@@ -38,6 +39,7 @@ func TestNewConfigurationLoader(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewConfigurationLoaderWithOptions(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
opts ConfigurationOptions
|
||||
@@ -75,6 +77,7 @@ func TestNewConfigurationLoaderWithOptions(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
loader := NewConfigurationLoaderWithOptions(tt.opts)
|
||||
|
||||
for _, expectedSource := range tt.expected {
|
||||
@@ -94,6 +97,7 @@ func TestNewConfigurationLoaderWithOptions(t *testing.T) {
|
||||
for _, expectedSource := range tt.expected {
|
||||
if source == expectedSource {
|
||||
expected = true
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -171,12 +175,10 @@ quiet: false
|
||||
},
|
||||
{
|
||||
name: "environment variable overrides",
|
||||
setupFunc: func(_ *testing.T, tempDir string) (string, string, string) {
|
||||
setupFunc: func(t *testing.T, tempDir string) (string, string, string) {
|
||||
t.Helper()
|
||||
// Set environment variables
|
||||
_ = os.Setenv("GH_README_GITHUB_TOKEN", "env-token")
|
||||
t.Cleanup(func() {
|
||||
_ = os.Unsetenv("GH_README_GITHUB_TOKEN")
|
||||
})
|
||||
t.Setenv("GH_README_GITHUB_TOKEN", "env-token")
|
||||
|
||||
// Create config file with different token
|
||||
configPath := filepath.Join(tempDir, "config.yml")
|
||||
@@ -245,15 +247,7 @@ verbose: true
|
||||
defer cleanup()
|
||||
|
||||
// Set HOME to temp directory for fallback
|
||||
originalHome := os.Getenv("HOME")
|
||||
_ = os.Setenv("HOME", tmpDir)
|
||||
defer func() {
|
||||
if originalHome != "" {
|
||||
_ = os.Setenv("HOME", originalHome)
|
||||
} else {
|
||||
_ = os.Unsetenv("HOME")
|
||||
}
|
||||
}()
|
||||
t.Setenv("HOME", tmpDir)
|
||||
|
||||
configFile, repoRoot, actionDir := tt.setupFunc(t, tmpDir)
|
||||
|
||||
@@ -272,6 +266,7 @@ verbose: true
|
||||
|
||||
if tt.expectError {
|
||||
testutil.AssertError(t, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -294,6 +289,7 @@ func TestConfigurationLoader_LoadGlobalConfig(t *testing.T) {
|
||||
{
|
||||
name: "valid global config",
|
||||
setupFunc: func(t *testing.T, tempDir string) string {
|
||||
t.Helper()
|
||||
configPath := filepath.Join(tempDir, "config.yaml")
|
||||
testutil.WriteTestFile(t, configPath, `
|
||||
theme: professional
|
||||
@@ -301,6 +297,7 @@ output_format: html
|
||||
github_token: test-token
|
||||
verbose: true
|
||||
`)
|
||||
|
||||
return configPath
|
||||
},
|
||||
checkFunc: func(_ *testing.T, config *AppConfig) {
|
||||
@@ -320,8 +317,10 @@ verbose: true
|
||||
{
|
||||
name: "invalid YAML",
|
||||
setupFunc: func(t *testing.T, tempDir string) string {
|
||||
t.Helper()
|
||||
configPath := filepath.Join(tempDir, "invalid.yaml")
|
||||
testutil.WriteTestFile(t, configPath, "invalid: yaml: content: [")
|
||||
|
||||
return configPath
|
||||
},
|
||||
expectError: true,
|
||||
@@ -334,15 +333,7 @@ verbose: true
|
||||
defer cleanup()
|
||||
|
||||
// Set HOME to temp directory
|
||||
originalHome := os.Getenv("HOME")
|
||||
_ = os.Setenv("HOME", tmpDir)
|
||||
defer func() {
|
||||
if originalHome != "" {
|
||||
_ = os.Setenv("HOME", originalHome)
|
||||
} else {
|
||||
_ = os.Unsetenv("HOME")
|
||||
}
|
||||
}()
|
||||
t.Setenv("HOME", tmpDir)
|
||||
|
||||
configFile := tt.setupFunc(t, tmpDir)
|
||||
|
||||
@@ -351,6 +342,7 @@ verbose: true
|
||||
|
||||
if tt.expectError {
|
||||
testutil.AssertError(t, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -364,6 +356,7 @@ verbose: true
|
||||
}
|
||||
|
||||
func TestConfigurationLoader_ValidateConfiguration(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
config *AppConfig
|
||||
@@ -442,6 +435,7 @@ func TestConfigurationLoader_ValidateConfiguration(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
loader := NewConfigurationLoader()
|
||||
err := loader.ValidateConfiguration(tt.config)
|
||||
|
||||
@@ -458,6 +452,7 @@ func TestConfigurationLoader_ValidateConfiguration(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConfigurationLoader_SourceManagement(t *testing.T) {
|
||||
t.Parallel()
|
||||
loader := NewConfigurationLoader()
|
||||
|
||||
// Test initial state
|
||||
@@ -487,6 +482,7 @@ func TestConfigurationLoader_SourceManagement(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConfigurationSource_String(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
source ConfigurationSource
|
||||
expected string
|
||||
@@ -510,48 +506,11 @@ func TestConfigurationSource_String(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConfigurationLoader_EnvironmentOverrides(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setupFunc func(t *testing.T) func()
|
||||
expectedToken string
|
||||
}{
|
||||
{
|
||||
name: "GH_README_GITHUB_TOKEN priority",
|
||||
setupFunc: func(_ *testing.T) func() {
|
||||
_ = os.Setenv("GH_README_GITHUB_TOKEN", "priority-token")
|
||||
_ = os.Setenv("GITHUB_TOKEN", "fallback-token")
|
||||
return func() {
|
||||
_ = os.Unsetenv("GH_README_GITHUB_TOKEN")
|
||||
_ = os.Unsetenv("GITHUB_TOKEN")
|
||||
}
|
||||
},
|
||||
expectedToken: "priority-token",
|
||||
},
|
||||
{
|
||||
name: "GITHUB_TOKEN fallback",
|
||||
setupFunc: func(_ *testing.T) func() {
|
||||
_ = os.Unsetenv("GH_README_GITHUB_TOKEN")
|
||||
_ = os.Setenv("GITHUB_TOKEN", "fallback-token")
|
||||
return func() {
|
||||
_ = os.Unsetenv("GITHUB_TOKEN")
|
||||
}
|
||||
},
|
||||
expectedToken: "fallback-token",
|
||||
},
|
||||
{
|
||||
name: "no environment variables",
|
||||
setupFunc: func(_ *testing.T) func() {
|
||||
_ = os.Unsetenv("GH_README_GITHUB_TOKEN")
|
||||
_ = os.Unsetenv("GITHUB_TOKEN")
|
||||
return func() {}
|
||||
},
|
||||
expectedToken: "",
|
||||
},
|
||||
}
|
||||
tests := testutil.GetGitHubTokenHierarchyTests()
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cleanup := tt.setupFunc(t)
|
||||
t.Run(tt.Name, func(t *testing.T) {
|
||||
cleanup := tt.SetupFunc(t)
|
||||
defer cleanup()
|
||||
|
||||
tmpDir, tmpCleanup := testutil.TempDir(t)
|
||||
@@ -561,7 +520,7 @@ func TestConfigurationLoader_EnvironmentOverrides(t *testing.T) {
|
||||
config, err := loader.LoadConfiguration("", tmpDir, "")
|
||||
testutil.AssertNoError(t, err)
|
||||
|
||||
testutil.AssertEqual(t, tt.expectedToken, config.GitHubToken)
|
||||
testutil.AssertEqual(t, tt.ExpectedToken, config.GitHubToken)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -588,15 +547,7 @@ func TestConfigurationLoader_RepoOverrides(t *testing.T) {
|
||||
testutil.WriteTestFile(t, globalConfigPath, globalConfigContent)
|
||||
|
||||
// Set environment for XDG compliance
|
||||
originalHome := os.Getenv("HOME")
|
||||
_ = os.Setenv("HOME", tmpDir)
|
||||
defer func() {
|
||||
if originalHome != "" {
|
||||
_ = os.Setenv("HOME", originalHome)
|
||||
} else {
|
||||
_ = os.Unsetenv("HOME")
|
||||
}
|
||||
}()
|
||||
t.Setenv("HOME", tmpDir)
|
||||
|
||||
loader := NewConfigurationLoader()
|
||||
config, err := loader.LoadConfiguration(globalConfigPath, repoRoot, "")
|
||||
@@ -610,6 +561,7 @@ func TestConfigurationLoader_RepoOverrides(t *testing.T) {
|
||||
|
||||
// TestConfigurationLoader_ApplyRepoOverrides tests repo-specific overrides.
|
||||
func TestConfigurationLoader_ApplyRepoOverrides(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
config *AppConfig
|
||||
@@ -640,6 +592,7 @@ func TestConfigurationLoader_ApplyRepoOverrides(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
tmpDir, cleanup := testutil.TempDir(t)
|
||||
defer cleanup()
|
||||
|
||||
@@ -653,6 +606,7 @@ func TestConfigurationLoader_ApplyRepoOverrides(t *testing.T) {
|
||||
|
||||
// TestConfigurationLoader_LoadActionConfig tests action-specific configuration loading.
|
||||
func TestConfigurationLoader_LoadActionConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
setupFunc func(t *testing.T, tmpDir string) string
|
||||
@@ -670,6 +624,7 @@ func TestConfigurationLoader_LoadActionConfig(t *testing.T) {
|
||||
{
|
||||
name: "action directory with config file",
|
||||
setupFunc: func(t *testing.T, tmpDir string) string {
|
||||
t.Helper()
|
||||
actionDir := filepath.Join(tmpDir, "action")
|
||||
_ = os.MkdirAll(actionDir, 0750) // #nosec G301 -- test directory permissions
|
||||
|
||||
@@ -679,6 +634,7 @@ theme: minimal
|
||||
output_format: json
|
||||
verbose: true
|
||||
`)
|
||||
|
||||
return actionDir
|
||||
},
|
||||
expectError: false,
|
||||
@@ -690,11 +646,13 @@ verbose: true
|
||||
{
|
||||
name: "action directory with malformed config file",
|
||||
setupFunc: func(t *testing.T, tmpDir string) string {
|
||||
t.Helper()
|
||||
actionDir := filepath.Join(tmpDir, "action")
|
||||
_ = os.MkdirAll(actionDir, 0750) // #nosec G301 -- test directory permissions
|
||||
|
||||
configPath := filepath.Join(actionDir, "config.yaml")
|
||||
testutil.WriteTestFile(t, configPath, "invalid yaml content:\n - broken [")
|
||||
|
||||
return actionDir
|
||||
},
|
||||
expectError: false, // Function may handle YAML errors gracefully
|
||||
@@ -705,6 +663,7 @@ verbose: true
|
||||
setupFunc: func(_ *testing.T, tmpDir string) string {
|
||||
actionDir := filepath.Join(tmpDir, "action")
|
||||
_ = os.MkdirAll(actionDir, 0750) // #nosec G301 -- test directory permissions
|
||||
|
||||
return actionDir
|
||||
},
|
||||
expectError: false,
|
||||
@@ -714,6 +673,7 @@ verbose: true
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
tmpDir, cleanup := testutil.TempDir(t)
|
||||
defer cleanup()
|
||||
|
||||
@@ -745,6 +705,7 @@ verbose: true
|
||||
|
||||
// TestConfigurationLoader_ValidateTheme tests theme validation edge cases.
|
||||
func TestConfigurationLoader_ValidateTheme(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
theme string
|
||||
@@ -789,6 +750,7 @@ func TestConfigurationLoader_ValidateTheme(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
loader := NewConfigurationLoader()
|
||||
err := loader.validateTheme(tt.theme)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user