mirror of
https://github.com/ivuorinen/gh-action-readme.git
synced 2026-01-26 11:14:04 +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.
319 lines
7.8 KiB
Go
319 lines
7.8 KiB
Go
package git
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/ivuorinen/gh-action-readme/testutil"
|
|
)
|
|
|
|
func TestFindRepositoryRoot(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
setupFunc func(t *testing.T, tmpDir string) string
|
|
expectError bool
|
|
expectEmpty bool
|
|
}{
|
|
{
|
|
name: "git repository with .git directory",
|
|
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
|
|
if err != nil {
|
|
t.Fatalf("failed to create .git directory: %v", err)
|
|
}
|
|
|
|
// Create subdirectory to test from
|
|
subDir := filepath.Join(tmpDir, "subdir", "nested")
|
|
err = os.MkdirAll(subDir, 0750) // #nosec G301 -- test directory permissions
|
|
if err != nil {
|
|
t.Fatalf("failed to create subdirectory: %v", err)
|
|
}
|
|
|
|
return subDir
|
|
},
|
|
expectError: false,
|
|
expectEmpty: false,
|
|
},
|
|
{
|
|
name: "git repository with .git file",
|
|
setupFunc: func(t *testing.T, tmpDir string) string {
|
|
// Create .git file (for git worktrees)
|
|
gitFile := filepath.Join(tmpDir, ".git")
|
|
testutil.WriteTestFile(t, gitFile, "gitdir: /path/to/git/dir")
|
|
|
|
return tmpDir
|
|
},
|
|
expectError: false,
|
|
expectEmpty: false,
|
|
},
|
|
{
|
|
name: "no git repository",
|
|
setupFunc: func(t *testing.T, tmpDir string) string {
|
|
// Create subdirectory without .git
|
|
subDir := filepath.Join(tmpDir, "subdir")
|
|
err := os.MkdirAll(subDir, 0750) // #nosec G301 -- test directory permissions
|
|
if err != nil {
|
|
t.Fatalf("failed to create subdirectory: %v", err)
|
|
}
|
|
return subDir
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "nonexistent directory",
|
|
setupFunc: func(_ *testing.T, tmpDir string) string {
|
|
return filepath.Join(tmpDir, "nonexistent")
|
|
},
|
|
expectError: 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, err := FindRepositoryRoot(testDir)
|
|
|
|
if tt.expectError {
|
|
testutil.AssertError(t, err)
|
|
return
|
|
}
|
|
|
|
testutil.AssertNoError(t, err)
|
|
|
|
if tt.expectEmpty {
|
|
if repoRoot != "" {
|
|
t.Errorf("expected empty repository root, got: %s", repoRoot)
|
|
}
|
|
} else {
|
|
if repoRoot == "" {
|
|
t.Error("expected non-empty repository root")
|
|
}
|
|
|
|
// Verify the returned path contains a .git directory or file
|
|
gitPath := filepath.Join(repoRoot, ".git")
|
|
if _, err := os.Stat(gitPath); os.IsNotExist(err) {
|
|
t.Errorf("repository root does not contain .git: %s", repoRoot)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDetectGitRepository(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
setupFunc func(t *testing.T, tmpDir string) string
|
|
checkFunc func(t *testing.T, info *RepoInfo)
|
|
}{
|
|
{
|
|
name: "GitHub 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
|
|
if err != nil {
|
|
t.Fatalf("failed to create .git directory: %v", err)
|
|
}
|
|
|
|
// Create config file with GitHub remote
|
|
configContent := `[core]
|
|
repositoryformatversion = 0
|
|
filemode = true
|
|
bare = false
|
|
logallrefupdates = true
|
|
[remote "origin"]
|
|
url = https://github.com/owner/repo.git
|
|
fetch = +refs/heads/*:refs/remotes/origin/*
|
|
[branch "main"]
|
|
remote = origin
|
|
merge = refs/heads/main
|
|
`
|
|
configPath := filepath.Join(gitDir, "config")
|
|
testutil.WriteTestFile(t, configPath, configContent)
|
|
|
|
return tmpDir
|
|
},
|
|
checkFunc: func(t *testing.T, info *RepoInfo) {
|
|
testutil.AssertEqual(t, "owner", info.Organization)
|
|
testutil.AssertEqual(t, "repo", info.Repository)
|
|
testutil.AssertEqual(t, "https://github.com/owner/repo.git", info.RemoteURL)
|
|
},
|
|
},
|
|
{
|
|
name: "SSH remote URL",
|
|
setupFunc: func(t *testing.T, tmpDir string) string {
|
|
gitDir := filepath.Join(tmpDir, ".git")
|
|
err := os.MkdirAll(gitDir, 0750) // #nosec G301 -- test directory permissions
|
|
if err != nil {
|
|
t.Fatalf("failed to create .git directory: %v", err)
|
|
}
|
|
|
|
configContent := `[remote "origin"]
|
|
url = git@github.com:owner/repo.git
|
|
fetch = +refs/heads/*:refs/remotes/origin/*
|
|
`
|
|
configPath := filepath.Join(gitDir, "config")
|
|
testutil.WriteTestFile(t, configPath, configContent)
|
|
|
|
return tmpDir
|
|
},
|
|
checkFunc: func(t *testing.T, info *RepoInfo) {
|
|
testutil.AssertEqual(t, "owner", info.Organization)
|
|
testutil.AssertEqual(t, "repo", info.Repository)
|
|
testutil.AssertEqual(t, "git@github.com:owner/repo.git", info.RemoteURL)
|
|
},
|
|
},
|
|
{
|
|
name: "no git repository",
|
|
setupFunc: func(_ *testing.T, tmpDir string) string {
|
|
return tmpDir
|
|
},
|
|
checkFunc: func(t *testing.T, info *RepoInfo) {
|
|
testutil.AssertEqual(t, false, info.IsGitRepo)
|
|
testutil.AssertEqual(t, "", info.Organization)
|
|
testutil.AssertEqual(t, "", info.Repository)
|
|
},
|
|
},
|
|
{
|
|
name: "git repository without origin remote",
|
|
setupFunc: func(t *testing.T, tmpDir string) string {
|
|
gitDir := filepath.Join(tmpDir, ".git")
|
|
err := os.MkdirAll(gitDir, 0750) // #nosec G301 -- test directory permissions
|
|
if err != nil {
|
|
t.Fatalf("failed to create .git directory: %v", err)
|
|
}
|
|
|
|
configContent := `[core]
|
|
repositoryformatversion = 0
|
|
filemode = true
|
|
bare = false
|
|
`
|
|
configPath := filepath.Join(gitDir, "config")
|
|
testutil.WriteTestFile(t, configPath, configContent)
|
|
|
|
return tmpDir
|
|
},
|
|
checkFunc: func(t *testing.T, info *RepoInfo) {
|
|
testutil.AssertEqual(t, true, info.IsGitRepo)
|
|
testutil.AssertEqual(t, "", info.Organization)
|
|
testutil.AssertEqual(t, "", info.Repository)
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tmpDir, cleanup := testutil.TempDir(t)
|
|
defer cleanup()
|
|
|
|
testDir := tt.setupFunc(t, tmpDir)
|
|
|
|
repoInfo, _ := DetectRepository(testDir)
|
|
|
|
if repoInfo == nil {
|
|
repoInfo = &RepoInfo{}
|
|
}
|
|
tt.checkFunc(t, repoInfo)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseGitHubURL(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
remoteURL string
|
|
expectedOrg string
|
|
expectedRepo string
|
|
}{
|
|
{
|
|
name: "HTTPS GitHub URL",
|
|
remoteURL: "https://github.com/owner/repo.git",
|
|
expectedOrg: "owner",
|
|
expectedRepo: "repo",
|
|
},
|
|
{
|
|
name: "SSH GitHub URL",
|
|
remoteURL: "git@github.com:owner/repo.git",
|
|
expectedOrg: "owner",
|
|
expectedRepo: "repo",
|
|
},
|
|
{
|
|
name: "GitHub URL without .git suffix",
|
|
remoteURL: "https://github.com/owner/repo",
|
|
expectedOrg: "owner",
|
|
expectedRepo: "repo",
|
|
},
|
|
{
|
|
name: "Invalid URL",
|
|
remoteURL: "not-a-valid-url",
|
|
expectedOrg: "",
|
|
expectedRepo: "",
|
|
},
|
|
{
|
|
name: "Empty URL",
|
|
remoteURL: "",
|
|
expectedOrg: "",
|
|
expectedRepo: "",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
org, repo := parseGitHubURL(tt.remoteURL)
|
|
|
|
testutil.AssertEqual(t, tt.expectedOrg, org)
|
|
testutil.AssertEqual(t, tt.expectedRepo, repo)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRepoInfo_GetRepositoryName(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
repoInfo RepoInfo
|
|
expected string
|
|
}{
|
|
{
|
|
name: "empty repo info",
|
|
repoInfo: RepoInfo{},
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "only organization set",
|
|
repoInfo: RepoInfo{
|
|
Organization: "owner",
|
|
},
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "only repository set",
|
|
repoInfo: RepoInfo{
|
|
Repository: "repo",
|
|
},
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "both organization and repository set",
|
|
repoInfo: RepoInfo{
|
|
Organization: "owner",
|
|
Repository: "repo",
|
|
},
|
|
expected: "owner/repo",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := tt.repoInfo.GetRepositoryName()
|
|
testutil.AssertEqual(t, tt.expected, result)
|
|
})
|
|
}
|
|
}
|