mirror of
https://github.com/ivuorinen/gh-action-readme.git
synced 2026-01-26 11:14:04 +00:00
* feat: rename internal/errors to internal/apperrors * fix(tests): clear env values before using in tests * feat: rename internal/errors to internal/apperrors * chore(deps): update go and all dependencies * chore: remove renovate from pre-commit, formatting * chore: sonarcloud fixes * feat: consolidate constants to appconstants/constants.go * chore: sonarcloud fixes * feat: simplification, deduplication, test utils * chore: sonarcloud fixes * chore: sonarcloud fixes * chore: sonarcloud fixes * chore: sonarcloud fixes * chore: clean up * fix: config discovery, const deduplication * chore: fixes
732 lines
20 KiB
Go
732 lines
20 KiB
Go
package internal
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/ivuorinen/gh-action-readme/appconstants"
|
|
"github.com/ivuorinen/gh-action-readme/testutil"
|
|
)
|
|
|
|
func TestInitConfig(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
name string
|
|
configFile string
|
|
setupFunc func(t *testing.T, tempDir string)
|
|
expectError bool
|
|
expected *AppConfig
|
|
}{
|
|
{
|
|
name: "default config when no file exists",
|
|
configFile: "",
|
|
setupFunc: nil,
|
|
expected: &AppConfig{
|
|
Theme: "default",
|
|
OutputFormat: "md",
|
|
OutputDir: ".",
|
|
Template: "templates/readme.tmpl",
|
|
Schema: "schemas/schema.json",
|
|
Verbose: false,
|
|
Quiet: false,
|
|
GitHubToken: "",
|
|
},
|
|
},
|
|
{
|
|
name: "custom config file",
|
|
configFile: "custom-config.yml",
|
|
setupFunc: func(t *testing.T, tempDir string) {
|
|
t.Helper()
|
|
configPath := filepath.Join(tempDir, "custom-config.yml")
|
|
testutil.WriteTestFile(t, configPath, testutil.MustReadFixture("professional-config.yml"))
|
|
},
|
|
expected: &AppConfig{
|
|
Theme: "professional",
|
|
OutputFormat: "html",
|
|
OutputDir: "docs",
|
|
Template: "custom-template.tmpl",
|
|
Schema: "custom-schema.json",
|
|
Verbose: true,
|
|
Quiet: false,
|
|
GitHubToken: "test-token-from-config",
|
|
},
|
|
},
|
|
{
|
|
name: "invalid config file",
|
|
configFile: "config.yml",
|
|
setupFunc: func(t *testing.T, tempDir string) {
|
|
t.Helper()
|
|
configPath := filepath.Join(tempDir, "config.yml")
|
|
testutil.WriteTestFile(t, configPath, "invalid: yaml: content: [")
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "nonexistent config file",
|
|
configFile: "nonexistent.yml",
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tmpDir, cleanup := testutil.TempDir(t)
|
|
defer cleanup()
|
|
|
|
// Set XDG_CONFIG_HOME to our temp directory
|
|
t.Setenv("XDG_CONFIG_HOME", tmpDir)
|
|
t.Setenv("HOME", tmpDir)
|
|
|
|
if tt.setupFunc != nil {
|
|
tt.setupFunc(t, tmpDir)
|
|
}
|
|
|
|
// Set config file path if specified
|
|
configPath := ""
|
|
if tt.configFile != "" {
|
|
configPath = filepath.Join(tmpDir, tt.configFile)
|
|
}
|
|
|
|
config, err := InitConfig(configPath)
|
|
|
|
if tt.expectError {
|
|
testutil.AssertError(t, err)
|
|
|
|
return
|
|
}
|
|
|
|
testutil.AssertNoError(t, err)
|
|
|
|
// Verify config values
|
|
if tt.expected != nil {
|
|
testutil.AssertEqual(t, tt.expected.Theme, config.Theme)
|
|
testutil.AssertEqual(t, tt.expected.OutputFormat, config.OutputFormat)
|
|
testutil.AssertEqual(t, tt.expected.OutputDir, config.OutputDir)
|
|
testutil.AssertEqual(t, tt.expected.Template, config.Template)
|
|
testutil.AssertEqual(t, tt.expected.Schema, config.Schema)
|
|
testutil.AssertEqual(t, tt.expected.Verbose, config.Verbose)
|
|
testutil.AssertEqual(t, tt.expected.Quiet, config.Quiet)
|
|
testutil.AssertEqual(t, tt.expected.GitHubToken, config.GitHubToken)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLoadConfiguration(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
setupFunc func(t *testing.T, tempDir string) (configFile, repoRoot, currentDir string)
|
|
expectError bool
|
|
checkFunc func(t *testing.T, config *AppConfig)
|
|
}{
|
|
{
|
|
name: "multi-level config hierarchy",
|
|
setupFunc: func(t *testing.T, tempDir string) (string, string, string) {
|
|
t.Helper()
|
|
// Clear environment variables to ensure config file values are used
|
|
t.Setenv(appconstants.EnvGitHubTokenStandard, "")
|
|
t.Setenv(appconstants.EnvGitHubToken, "")
|
|
|
|
// Create global config
|
|
globalConfigDir := filepath.Join(tempDir, ".config", "gh-action-readme")
|
|
_ = os.MkdirAll(globalConfigDir, 0750) // #nosec G301 -- test directory permissions
|
|
globalConfigPath := filepath.Join(globalConfigDir, "config.yaml")
|
|
testutil.WriteTestFile(t, globalConfigPath, `
|
|
theme: default
|
|
output_format: md
|
|
github_token: ghp_test1234567890abcdefghijklmnopqrstuvwxyz
|
|
`)
|
|
|
|
// Create repo root with repo-specific config
|
|
repoRoot := filepath.Join(tempDir, "repo")
|
|
_ = os.MkdirAll(repoRoot, 0750) // #nosec G301 -- test directory permissions
|
|
testutil.WriteTestFile(t, filepath.Join(repoRoot, ".ghreadme.yaml"), `
|
|
theme: github
|
|
output_format: html
|
|
`)
|
|
|
|
// Create current directory with action-specific config
|
|
currentDir := filepath.Join(repoRoot, "action")
|
|
_ = os.MkdirAll(currentDir, 0750) // #nosec G301 -- test directory permissions
|
|
testutil.WriteTestFile(t, filepath.Join(currentDir, "config.yaml"), `
|
|
theme: professional
|
|
output_dir: output
|
|
`)
|
|
|
|
return globalConfigPath, repoRoot, currentDir
|
|
},
|
|
checkFunc: func(t *testing.T, config *AppConfig) {
|
|
t.Helper()
|
|
// Should have action-level overrides
|
|
testutil.AssertEqual(t, "professional", config.Theme)
|
|
testutil.AssertEqual(t, "output", config.OutputDir)
|
|
// Should inherit from repo level
|
|
testutil.AssertEqual(t, "html", config.OutputFormat)
|
|
// Should inherit GitHub token from global config
|
|
testutil.AssertEqual(t, "ghp_test1234567890abcdefghijklmnopqrstuvwxyz", config.GitHubToken)
|
|
},
|
|
},
|
|
{
|
|
name: "environment variable overrides",
|
|
setupFunc: func(t *testing.T, tempDir string) (string, string, string) {
|
|
t.Helper()
|
|
// Set environment variables
|
|
t.Setenv("GH_README_GITHUB_TOKEN", "env-token")
|
|
t.Setenv("GITHUB_TOKEN", "fallback-token")
|
|
|
|
// Create config file
|
|
configPath := filepath.Join(tempDir, "config.yml")
|
|
testutil.WriteTestFile(t, configPath, `
|
|
theme: minimal
|
|
github_token: config-token
|
|
`)
|
|
|
|
return configPath, tempDir, tempDir
|
|
},
|
|
checkFunc: func(t *testing.T, config *AppConfig) {
|
|
t.Helper()
|
|
// Environment variable should override config file
|
|
testutil.AssertEqual(t, "env-token", config.GitHubToken)
|
|
testutil.AssertEqual(t, "minimal", config.Theme)
|
|
},
|
|
},
|
|
{
|
|
name: "XDG compliance",
|
|
setupFunc: func(t *testing.T, tempDir string) (string, string, string) {
|
|
t.Helper()
|
|
// Set XDG environment variables
|
|
xdgConfigHome := filepath.Join(tempDir, "xdg-config")
|
|
t.Setenv("XDG_CONFIG_HOME", xdgConfigHome)
|
|
|
|
// Create XDG-compliant config
|
|
configDir := filepath.Join(xdgConfigHome, "gh-action-readme")
|
|
_ = os.MkdirAll(configDir, 0750) // #nosec G301 -- test directory permissions
|
|
configPath := filepath.Join(configDir, "config.yaml")
|
|
testutil.WriteTestFile(t, configPath, `
|
|
theme: github
|
|
verbose: true
|
|
`)
|
|
|
|
return configPath, tempDir, tempDir
|
|
},
|
|
checkFunc: func(t *testing.T, config *AppConfig) {
|
|
t.Helper()
|
|
testutil.AssertEqual(t, "github", config.Theme)
|
|
testutil.AssertEqual(t, true, config.Verbose)
|
|
},
|
|
},
|
|
{
|
|
name: "hidden config file discovery",
|
|
setupFunc: func(t *testing.T, tempDir string) (string, string, string) {
|
|
t.Helper()
|
|
repoRoot := filepath.Join(tempDir, "repo")
|
|
_ = os.MkdirAll(repoRoot, 0750) // #nosec G301 -- test directory permissions
|
|
|
|
// Create multiple hidden config files
|
|
testutil.WriteTestFile(t, filepath.Join(repoRoot, ".ghreadme.yaml"), `
|
|
theme: minimal
|
|
output_format: json
|
|
`)
|
|
|
|
testutil.WriteTestFile(t, filepath.Join(repoRoot, ".config", "ghreadme.yaml"), `
|
|
theme: professional
|
|
quiet: true
|
|
`)
|
|
|
|
testutil.WriteTestFile(t, filepath.Join(repoRoot, ".github", "ghreadme.yaml"), `
|
|
theme: github
|
|
verbose: true
|
|
`)
|
|
|
|
return "", repoRoot, repoRoot
|
|
},
|
|
checkFunc: func(t *testing.T, config *AppConfig) {
|
|
t.Helper()
|
|
// Should use the first found config (.ghreadme.yaml has priority)
|
|
testutil.AssertEqual(t, "minimal", config.Theme)
|
|
testutil.AssertEqual(t, "json", config.OutputFormat)
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tmpDir, cleanup := testutil.TempDir(t)
|
|
defer cleanup()
|
|
|
|
// Set HOME to temp directory for fallback
|
|
t.Setenv("HOME", tmpDir)
|
|
|
|
configFile, repoRoot, currentDir := tt.setupFunc(t, tmpDir)
|
|
|
|
config, err := LoadConfiguration(configFile, repoRoot, currentDir)
|
|
|
|
if tt.expectError {
|
|
testutil.AssertError(t, err)
|
|
|
|
return
|
|
}
|
|
|
|
testutil.AssertNoError(t, err)
|
|
|
|
if tt.checkFunc != nil {
|
|
tt.checkFunc(t, config)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetConfigPath(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
name string
|
|
setupFunc func(t *testing.T, tempDir string)
|
|
contains string
|
|
}{
|
|
{
|
|
name: "XDG_CONFIG_HOME set",
|
|
setupFunc: func(t *testing.T, tempDir string) {
|
|
t.Helper()
|
|
t.Setenv("XDG_CONFIG_HOME", tempDir)
|
|
t.Setenv("HOME", "")
|
|
},
|
|
contains: "gh-action-readme",
|
|
},
|
|
{
|
|
name: "HOME fallback",
|
|
setupFunc: func(t *testing.T, tempDir string) {
|
|
t.Helper()
|
|
t.Setenv("XDG_CONFIG_HOME", "")
|
|
t.Setenv("HOME", tempDir)
|
|
},
|
|
contains: ".config",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tmpDir, cleanup := testutil.TempDir(t)
|
|
defer cleanup()
|
|
|
|
tt.setupFunc(t, tmpDir)
|
|
|
|
path, err := GetConfigPath()
|
|
testutil.AssertNoError(t, err)
|
|
|
|
if !filepath.IsAbs(path) {
|
|
t.Errorf("expected absolute path, got: %s", path)
|
|
}
|
|
|
|
testutil.AssertStringContains(t, path, tt.contains)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWriteDefaultConfig(t *testing.T) {
|
|
tmpDir, cleanup := testutil.TempDir(t)
|
|
defer cleanup()
|
|
|
|
// Set XDG_CONFIG_HOME to our temp directory
|
|
t.Setenv("XDG_CONFIG_HOME", tmpDir)
|
|
|
|
err := WriteDefaultConfig()
|
|
testutil.AssertNoError(t, err)
|
|
|
|
// Check that config file was created
|
|
configPath, _ := GetConfigPath()
|
|
t.Logf("Expected config path: %s", configPath)
|
|
testutil.AssertFileExists(t, configPath)
|
|
|
|
// Verify config file content
|
|
config, err := InitConfig(configPath)
|
|
testutil.AssertNoError(t, err)
|
|
|
|
// Should have default values
|
|
testutil.AssertEqual(t, "default", config.Theme)
|
|
testutil.AssertEqual(t, "md", config.OutputFormat)
|
|
testutil.AssertEqual(t, ".", config.OutputDir)
|
|
}
|
|
|
|
func TestResolveThemeTemplate(t *testing.T) {
|
|
t.Parallel()
|
|
tests := []struct {
|
|
name string
|
|
theme string
|
|
expectError bool
|
|
shouldExist bool
|
|
expectedPath string
|
|
}{
|
|
{
|
|
name: "default theme",
|
|
theme: "default",
|
|
expectError: false,
|
|
shouldExist: true,
|
|
expectedPath: "templates/readme.tmpl",
|
|
},
|
|
{
|
|
name: "github theme",
|
|
theme: "github",
|
|
expectError: false,
|
|
shouldExist: true,
|
|
expectedPath: "templates/themes/github/readme.tmpl",
|
|
},
|
|
{
|
|
name: "gitlab theme",
|
|
theme: "gitlab",
|
|
expectError: false,
|
|
shouldExist: true,
|
|
expectedPath: "templates/themes/gitlab/readme.tmpl",
|
|
},
|
|
{
|
|
name: "minimal theme",
|
|
theme: "minimal",
|
|
expectError: false,
|
|
shouldExist: true,
|
|
expectedPath: "templates/themes/minimal/readme.tmpl",
|
|
},
|
|
{
|
|
name: "professional theme",
|
|
theme: "professional",
|
|
expectError: false,
|
|
shouldExist: true,
|
|
expectedPath: "templates/themes/professional/readme.tmpl",
|
|
},
|
|
{
|
|
name: "unknown theme",
|
|
theme: "nonexistent",
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "empty theme",
|
|
theme: "",
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
path := resolveThemeTemplate(tt.theme)
|
|
|
|
if tt.expectError {
|
|
if path != "" {
|
|
t.Errorf("expected empty path on error, got: %s", path)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if path == "" {
|
|
t.Error("expected non-empty path")
|
|
}
|
|
|
|
if tt.expectedPath != "" {
|
|
testutil.AssertStringContains(t, path, tt.expectedPath)
|
|
}
|
|
|
|
// Note: We can't check file existence here because template files
|
|
// might not be present in the test environment
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfigTokenHierarchy(t *testing.T) {
|
|
tests := testutil.GetGitHubTokenHierarchyTests()
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.Name, func(t *testing.T) {
|
|
cleanup := tt.SetupFunc(t)
|
|
defer cleanup()
|
|
|
|
tmpDir, tmpCleanup := testutil.TempDir(t)
|
|
defer tmpCleanup()
|
|
|
|
// Use default config
|
|
config, err := LoadConfiguration("", tmpDir, tmpDir)
|
|
testutil.AssertNoError(t, err)
|
|
|
|
testutil.AssertEqual(t, tt.ExpectedToken, config.GitHubToken)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfigMerging(t *testing.T) {
|
|
tmpDir, cleanup := testutil.TempDir(t)
|
|
defer cleanup()
|
|
|
|
// Test config merging by creating config files and seeing the result
|
|
|
|
globalConfigDir := filepath.Join(tmpDir, ".config", "gh-action-readme")
|
|
_ = os.MkdirAll(globalConfigDir, 0750) // #nosec G301 -- test directory permissions
|
|
testutil.WriteTestFile(t, filepath.Join(globalConfigDir, "config.yaml"), `
|
|
theme: default
|
|
output_format: md
|
|
github_token: base-token
|
|
verbose: false
|
|
`)
|
|
|
|
repoRoot := filepath.Join(tmpDir, "repo")
|
|
_ = os.MkdirAll(repoRoot, 0750) // #nosec G301 -- test directory permissions
|
|
testutil.WriteTestFile(t, filepath.Join(repoRoot, ".ghreadme.yaml"), `
|
|
theme: github
|
|
output_format: html
|
|
verbose: true
|
|
`)
|
|
|
|
// Set HOME and XDG_CONFIG_HOME to temp directory
|
|
t.Setenv("HOME", tmpDir)
|
|
t.Setenv("XDG_CONFIG_HOME", filepath.Join(tmpDir, ".config"))
|
|
|
|
// Use the specific config file path instead of relying on XDG discovery
|
|
configPath := filepath.Join(tmpDir, ".config", "gh-action-readme", "config.yaml")
|
|
config, err := LoadConfiguration(configPath, repoRoot, repoRoot)
|
|
testutil.AssertNoError(t, err)
|
|
|
|
// Should have merged values
|
|
testutil.AssertEqual(t, "github", config.Theme) // from repo config
|
|
testutil.AssertEqual(t, "html", config.OutputFormat) // from repo config
|
|
testutil.AssertEqual(t, true, config.Verbose) // from repo config
|
|
testutil.AssertEqual(t, "base-token", config.GitHubToken) // from global config
|
|
testutil.AssertEqual(t, "schemas/schema.json", config.Schema) // default value
|
|
}
|
|
|
|
// TestGetGitHubToken tests GitHub token resolution with different priority levels.
|
|
func TestGetGitHubToken(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
name string
|
|
toolEnvToken string
|
|
stdEnvToken string
|
|
configToken string
|
|
expectedToken string
|
|
}{
|
|
{
|
|
name: "tool-specific env var has highest priority",
|
|
toolEnvToken: "tool-token",
|
|
stdEnvToken: "std-token",
|
|
configToken: "config-token",
|
|
expectedToken: "tool-token",
|
|
},
|
|
{
|
|
name: "standard env var when tool env not set",
|
|
toolEnvToken: "",
|
|
stdEnvToken: "std-token",
|
|
configToken: "config-token",
|
|
expectedToken: "std-token",
|
|
},
|
|
{
|
|
name: "config token when env vars not set",
|
|
toolEnvToken: "",
|
|
stdEnvToken: "",
|
|
configToken: "config-token",
|
|
expectedToken: "config-token",
|
|
},
|
|
{
|
|
name: "empty string when nothing set",
|
|
toolEnvToken: "",
|
|
stdEnvToken: "",
|
|
configToken: "",
|
|
expectedToken: "",
|
|
},
|
|
{
|
|
name: "empty env var does not override config",
|
|
toolEnvToken: "",
|
|
stdEnvToken: "",
|
|
configToken: "config-token",
|
|
expectedToken: "config-token",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Set up environment
|
|
if tt.toolEnvToken != "" {
|
|
t.Setenv(appconstants.EnvGitHubToken, tt.toolEnvToken)
|
|
} else {
|
|
t.Setenv(appconstants.EnvGitHubToken, "")
|
|
}
|
|
if tt.stdEnvToken != "" {
|
|
t.Setenv(appconstants.EnvGitHubTokenStandard, tt.stdEnvToken)
|
|
} else {
|
|
t.Setenv(appconstants.EnvGitHubTokenStandard, "")
|
|
}
|
|
|
|
config := &AppConfig{GitHubToken: tt.configToken}
|
|
result := GetGitHubToken(config)
|
|
|
|
testutil.AssertEqual(t, tt.expectedToken, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestMergeMapFields tests the merging of map fields in configuration.
|
|
func TestMergeMapFields(t *testing.T) {
|
|
t.Parallel()
|
|
tests := []struct {
|
|
name string
|
|
dst *AppConfig
|
|
src *AppConfig
|
|
expected *AppConfig
|
|
}{
|
|
{
|
|
name: "merge permissions into empty dst",
|
|
dst: &AppConfig{},
|
|
src: &AppConfig{
|
|
Permissions: map[string]string{"read": "read", "write": "write"},
|
|
},
|
|
expected: &AppConfig{
|
|
Permissions: map[string]string{"read": "read", "write": "write"},
|
|
},
|
|
},
|
|
{
|
|
name: "merge permissions into existing dst",
|
|
dst: &AppConfig{
|
|
Permissions: map[string]string{"read": "existing"},
|
|
},
|
|
src: &AppConfig{
|
|
Permissions: map[string]string{"read": "new", "write": "write"},
|
|
},
|
|
expected: &AppConfig{
|
|
Permissions: map[string]string{"read": "new", "write": "write"},
|
|
},
|
|
},
|
|
{
|
|
name: "merge variables into empty dst",
|
|
dst: &AppConfig{},
|
|
src: &AppConfig{
|
|
Variables: map[string]string{"VAR1": "value1", "VAR2": "value2"},
|
|
},
|
|
expected: &AppConfig{
|
|
Variables: map[string]string{"VAR1": "value1", "VAR2": "value2"},
|
|
},
|
|
},
|
|
{
|
|
name: "merge variables into existing dst",
|
|
dst: &AppConfig{
|
|
Variables: map[string]string{"VAR1": "existing"},
|
|
},
|
|
src: &AppConfig{
|
|
Variables: map[string]string{"VAR1": "new", "VAR2": "value2"},
|
|
},
|
|
expected: &AppConfig{
|
|
Variables: map[string]string{"VAR1": "new", "VAR2": "value2"},
|
|
},
|
|
},
|
|
{
|
|
name: "merge both permissions and variables",
|
|
dst: &AppConfig{
|
|
Permissions: map[string]string{"read": "existing"},
|
|
},
|
|
src: &AppConfig{
|
|
Permissions: map[string]string{"write": "write"},
|
|
Variables: map[string]string{"VAR1": "value1"},
|
|
},
|
|
expected: &AppConfig{
|
|
Permissions: map[string]string{"read": "existing", "write": "write"},
|
|
Variables: map[string]string{"VAR1": "value1"},
|
|
},
|
|
},
|
|
{
|
|
name: "empty src does not affect dst",
|
|
dst: &AppConfig{
|
|
Permissions: map[string]string{"read": "read"},
|
|
Variables: map[string]string{"VAR1": "value1"},
|
|
},
|
|
src: &AppConfig{},
|
|
expected: &AppConfig{
|
|
Permissions: map[string]string{"read": "read"},
|
|
Variables: map[string]string{"VAR1": "value1"},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
// Deep copy dst to avoid modifying test data
|
|
dst := &AppConfig{}
|
|
if tt.dst.Permissions != nil {
|
|
dst.Permissions = make(map[string]string)
|
|
for k, v := range tt.dst.Permissions {
|
|
dst.Permissions[k] = v
|
|
}
|
|
}
|
|
if tt.dst.Variables != nil {
|
|
dst.Variables = make(map[string]string)
|
|
for k, v := range tt.dst.Variables {
|
|
dst.Variables[k] = v
|
|
}
|
|
}
|
|
|
|
mergeMapFields(dst, tt.src)
|
|
|
|
testutil.AssertEqual(t, tt.expected.Permissions, dst.Permissions)
|
|
testutil.AssertEqual(t, tt.expected.Variables, dst.Variables)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestMergeSliceFields tests the merging of slice fields in configuration.
|
|
func TestMergeSliceFields(t *testing.T) {
|
|
t.Parallel()
|
|
tests := []struct {
|
|
name string
|
|
dst *AppConfig
|
|
src *AppConfig
|
|
expected []string
|
|
}{
|
|
{
|
|
name: "merge runsOn into empty dst",
|
|
dst: &AppConfig{},
|
|
src: &AppConfig{RunsOn: []string{"ubuntu-latest", "windows-latest"}},
|
|
expected: []string{"ubuntu-latest", "windows-latest"},
|
|
},
|
|
{
|
|
name: "merge runsOn replaces existing dst",
|
|
dst: &AppConfig{RunsOn: []string{"macos-latest"}},
|
|
src: &AppConfig{RunsOn: []string{"ubuntu-latest", "windows-latest"}},
|
|
expected: []string{"ubuntu-latest", "windows-latest"},
|
|
},
|
|
{
|
|
name: "empty src does not affect dst",
|
|
dst: &AppConfig{RunsOn: []string{"ubuntu-latest"}},
|
|
src: &AppConfig{},
|
|
expected: []string{"ubuntu-latest"},
|
|
},
|
|
{
|
|
name: "empty src slice does not affect dst",
|
|
dst: &AppConfig{RunsOn: []string{"ubuntu-latest"}},
|
|
src: &AppConfig{RunsOn: []string{}},
|
|
expected: []string{"ubuntu-latest"},
|
|
},
|
|
{
|
|
name: "single item slice",
|
|
dst: &AppConfig{},
|
|
src: &AppConfig{RunsOn: []string{"self-hosted"}},
|
|
expected: []string{"self-hosted"},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
mergeSliceFields(tt.dst, tt.src)
|
|
|
|
// Compare slices manually since they can't be compared directly
|
|
if len(tt.expected) != len(tt.dst.RunsOn) {
|
|
t.Errorf("expected slice length %d, got %d", len(tt.expected), len(tt.dst.RunsOn))
|
|
|
|
return
|
|
}
|
|
for i, expected := range tt.expected {
|
|
if i >= len(tt.dst.RunsOn) || tt.dst.RunsOn[i] != expected {
|
|
t.Errorf("expected %v, got %v", tt.expected, tt.dst.RunsOn)
|
|
|
|
return
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|