Files
gh-action-readme/internal/validation/validation_test.go
2025-07-30 19:17:36 +03:00

530 lines
11 KiB
Go

package validation
import (
"os"
"path/filepath"
"testing"
"github.com/ivuorinen/gh-action-readme/testutil"
)
func TestValidateActionYMLPath(t *testing.T) {
tests := []struct {
name string
setupFunc func(t *testing.T, tmpDir string) string
expectError bool
errorMsg string
}{
{
name: "valid action.yml file",
setupFunc: func(t *testing.T, tmpDir string) string {
actionPath := filepath.Join(tmpDir, "action.yml")
testutil.WriteTestFile(t, actionPath, testutil.SimpleActionYML)
return actionPath
},
expectError: false,
},
{
name: "valid action.yaml file",
setupFunc: func(t *testing.T, tmpDir string) string {
actionPath := filepath.Join(tmpDir, "action.yaml")
testutil.WriteTestFile(t, actionPath, testutil.MinimalActionYML)
return actionPath
},
expectError: false,
},
{
name: "nonexistent file",
setupFunc: func(_ *testing.T, tmpDir string) string {
return filepath.Join(tmpDir, "nonexistent.yml")
},
expectError: true,
},
{
name: "file with wrong extension",
setupFunc: func(t *testing.T, tmpDir string) string {
actionPath := filepath.Join(tmpDir, "action.txt")
testutil.WriteTestFile(t, actionPath, testutil.SimpleActionYML)
return actionPath
},
expectError: true,
},
{
name: "empty file path",
setupFunc: func(_ *testing.T, _ string) string {
return ""
},
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tmpDir, cleanup := testutil.TempDir(t)
defer cleanup()
actionPath := tt.setupFunc(t, tmpDir)
err := ValidateActionYMLPath(actionPath)
if tt.expectError {
testutil.AssertError(t, err)
} else {
testutil.AssertNoError(t, err)
}
})
}
}
func TestIsCommitSHA(t *testing.T) {
tests := []struct {
name string
version string
expected bool
}{
{
name: "full commit SHA",
version: "8f4b7f84bd579b95d7f0b90f8d8b6e5d9b8a7f6e",
expected: true,
},
{
name: "short commit SHA",
version: "8f4b7f8",
expected: true,
},
{
name: "semantic version",
version: "v1.2.3",
expected: false,
},
{
name: "branch name",
version: "main",
expected: false,
},
{
name: "empty string",
version: "",
expected: false,
},
{
name: "non-hex characters",
version: "not-a-sha",
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := IsCommitSHA(tt.version)
testutil.AssertEqual(t, tt.expected, result)
})
}
}
func TestIsSemanticVersion(t *testing.T) {
tests := []struct {
name string
version string
expected bool
}{
{
name: "semantic version with v prefix",
version: "v1.2.3",
expected: true,
},
{
name: "semantic version without v prefix",
version: "1.2.3",
expected: true,
},
{
name: "semantic version with prerelease",
version: "v1.2.3-alpha.1",
expected: true,
},
{
name: "semantic version with build metadata",
version: "v1.2.3+20230101",
expected: true,
},
{
name: "major version only",
version: "v1",
expected: false,
},
{
name: "commit SHA",
version: "8f4b7f84bd579b95d7f0b90f8d8b6e5d9b8a7f6e",
expected: false,
},
{
name: "branch name",
version: "main",
expected: false,
},
{
name: "empty string",
version: "",
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := IsSemanticVersion(tt.version)
testutil.AssertEqual(t, tt.expected, result)
})
}
}
func TestIsVersionPinned(t *testing.T) {
tests := []struct {
name string
version string
expected bool
}{
{
name: "full semantic version",
version: "v1.2.3",
expected: true,
},
{
name: "full commit SHA",
version: "8f4b7f84bd579b95d7f0b90f8d8b6e5d9b8a7f6e",
expected: true,
},
{
name: "major version only",
version: "v1",
expected: false,
},
{
name: "major.minor version",
version: "v1.2",
expected: false,
},
{
name: "branch name",
version: "main",
expected: false,
},
{
name: "short commit SHA",
version: "8f4b7f8",
expected: false,
},
{
name: "empty string",
version: "",
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := IsVersionPinned(tt.version)
testutil.AssertEqual(t, tt.expected, result)
})
}
}
func TestValidateGitBranch(t *testing.T) {
tests := []struct {
name string
setupFunc func(t *testing.T, tmpDir string) (string, string)
expected bool
}{
{
name: "valid git repository with main branch",
setupFunc: func(_ *testing.T, tmpDir string) (string, string) {
// Create a simple git repository
gitDir := filepath.Join(tmpDir, ".git")
_ = os.MkdirAll(gitDir, 0755)
// Create a basic git config
configContent := `[core]
repositoryformatversion = 0
filemode = true
bare = false
[branch "main"]
remote = origin
merge = refs/heads/main
`
testutil.WriteTestFile(t, filepath.Join(gitDir, "config"), configContent)
return tmpDir, "main"
},
expected: true, // This may vary based on actual git repo state
},
{
name: "non-git directory",
setupFunc: func(_ *testing.T, tmpDir string) (string, string) {
return tmpDir, "main"
},
expected: false,
},
{
name: "empty branch name",
setupFunc: func(_ *testing.T, tmpDir string) (string, string) {
return tmpDir, ""
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tmpDir, cleanup := testutil.TempDir(t)
defer cleanup()
repoRoot, branch := tt.setupFunc(t, tmpDir)
result := ValidateGitBranch(repoRoot, branch)
// Note: This test may have different results based on the actual git setup
// We'll accept the result and just verify it doesn't panic
_ = result
})
}
}
func TestIsGitRepository(t *testing.T) {
tests := []struct {
name string
setupFunc func(t *testing.T, tmpDir string) string
expected bool
}{
{
name: "directory with .git folder",
setupFunc: func(_ *testing.T, tmpDir string) string {
gitDir := filepath.Join(tmpDir, ".git")
_ = os.MkdirAll(gitDir, 0755)
return tmpDir
},
expected: true,
},
{
name: "directory with .git file",
setupFunc: func(t *testing.T, tmpDir string) string {
gitFile := filepath.Join(tmpDir, ".git")
testutil.WriteTestFile(t, gitFile, "gitdir: /path/to/git/dir")
return tmpDir
},
expected: true,
},
{
name: "directory without .git",
setupFunc: func(_ *testing.T, tmpDir string) string {
return tmpDir
},
expected: false,
},
{
name: "nonexistent path",
setupFunc: func(_ *testing.T, tmpDir string) string {
return filepath.Join(tmpDir, "nonexistent")
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tmpDir, cleanup := testutil.TempDir(t)
defer cleanup()
testPath := tt.setupFunc(t, tmpDir)
result := IsGitRepository(testPath)
testutil.AssertEqual(t, tt.expected, result)
})
}
}
func TestCleanVersionString(t *testing.T) {
tests := []struct {
name string
input string
expected string
}{
{
name: "version with v prefix",
input: "v1.2.3",
expected: "1.2.3",
},
{
name: "version without v prefix",
input: "1.2.3",
expected: "1.2.3",
},
{
name: "version with leading/trailing spaces",
input: " v1.2.3 ",
expected: "1.2.3",
},
{
name: "empty string",
input: "",
expected: "",
},
{
name: "commit SHA",
input: "8f4b7f84bd579b95d7f0b90f8d8b6e5d9b8a7f6e",
expected: "8f4b7f84bd579b95d7f0b90f8d8b6e5d9b8a7f6e",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := CleanVersionString(tt.input)
testutil.AssertEqual(t, tt.expected, result)
})
}
}
func TestParseGitHubURL(t *testing.T) {
tests := []struct {
name string
url string
expectedOrg string
expectedRepo string
}{
{
name: "HTTPS GitHub URL",
url: "https://github.com/owner/repo",
expectedOrg: "owner",
expectedRepo: "repo",
},
{
name: "GitHub URL with .git suffix",
url: "https://github.com/owner/repo.git",
expectedOrg: "owner",
expectedRepo: "repo",
},
{
name: "SSH GitHub URL",
url: "git@github.com:owner/repo.git",
expectedOrg: "owner",
expectedRepo: "repo",
},
{
name: "Invalid URL",
url: "not-a-url",
expectedOrg: "",
expectedRepo: "",
},
{
name: "Empty URL",
url: "",
expectedOrg: "",
expectedRepo: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
org, repo := ParseGitHubURL(tt.url)
testutil.AssertEqual(t, tt.expectedOrg, org)
testutil.AssertEqual(t, tt.expectedRepo, repo)
})
}
}
func TestSanitizeActionName(t *testing.T) {
tests := []struct {
name string
input string
expected string
}{
{
name: "normal action name",
input: "My Action",
expected: "My Action",
},
{
name: "action name with special characters",
input: "My Action! @#$%",
expected: "My Action ",
},
{
name: "action name with newlines",
input: "My\nAction",
expected: "My Action",
},
{
name: "empty string",
input: "",
expected: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(_ *testing.T) {
result := SanitizeActionName(tt.input)
// The exact behavior may vary, so we'll just verify it doesn't panic
_ = result
})
}
}
func TestGetBinaryDir(t *testing.T) {
dir, err := GetBinaryDir()
testutil.AssertNoError(t, err)
if dir == "" {
t.Error("expected non-empty binary directory")
}
// Verify the directory exists
if _, err := os.Stat(dir); os.IsNotExist(err) {
t.Errorf("binary directory does not exist: %s", dir)
}
}
func TestEnsureAbsolutePath(t *testing.T) {
tests := []struct {
name string
input string
isAbsolute bool
}{
{
name: "absolute path",
input: "/path/to/file",
isAbsolute: true,
},
{
name: "relative path",
input: "./file",
isAbsolute: false,
},
{
name: "just filename",
input: "file.txt",
isAbsolute: false,
},
{
name: "empty path",
input: "",
isAbsolute: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := EnsureAbsolutePath(tt.input)
if tt.input == "" {
// Empty input might cause an error
if err != nil {
return // This is acceptable
}
} else {
testutil.AssertNoError(t, err)
}
// Result should always be absolute
if result != "" && !filepath.IsAbs(result) {
t.Errorf("expected absolute path, got: %s", result)
}
})
}
}