mirror of
https://github.com/ivuorinen/gh-action-readme.git
synced 2026-01-26 11:14:04 +00:00
* 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.
261 lines
6.3 KiB
Go
261 lines
6.3 KiB
Go
package wizard
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/ivuorinen/gh-action-readme/internal"
|
|
)
|
|
|
|
func TestProjectDetector_analyzeProjectFiles(t *testing.T) {
|
|
t.Parallel()
|
|
// Create temporary directory for testing
|
|
tempDir := t.TempDir()
|
|
|
|
// Create test files (go.mod should be processed last to be the final language)
|
|
testFiles := map[string]string{
|
|
"Dockerfile": "FROM alpine",
|
|
"action.yml": "name: Test Action",
|
|
"next.config.js": "module.exports = {}",
|
|
"package.json": `{"name": "test", "version": "1.0.0"}`,
|
|
"go.mod": "module test", // This should be detected last
|
|
}
|
|
|
|
for filename, content := range testFiles {
|
|
filePath := filepath.Join(tempDir, filename)
|
|
if err := os.WriteFile(filePath, []byte(content), 0600); err != nil { // #nosec G306 -- test file permissions
|
|
t.Fatalf("Failed to create test file %s: %v", filename, err)
|
|
}
|
|
}
|
|
|
|
// Create detector with temp directory
|
|
output := internal.NewColoredOutput(true)
|
|
detector := &ProjectDetector{
|
|
output: output,
|
|
currentDir: tempDir,
|
|
}
|
|
|
|
characteristics := detector.analyzeProjectFiles()
|
|
|
|
// Test that a language is detected (either Go or JavaScript/TypeScript is valid)
|
|
language := characteristics["language"]
|
|
if language != "Go" && language != "JavaScript/TypeScript" {
|
|
t.Errorf("Expected language 'Go' or 'JavaScript/TypeScript', got '%s'", language)
|
|
}
|
|
|
|
// Test that appropriate type is detected
|
|
projectType := characteristics["type"]
|
|
validTypes := []string{"Go Module", "Node.js Project"}
|
|
typeValid := false
|
|
for _, validType := range validTypes {
|
|
if projectType == validType {
|
|
typeValid = true
|
|
|
|
break
|
|
}
|
|
}
|
|
if !typeValid {
|
|
t.Errorf("Expected type to be one of %v, got '%s'", validTypes, projectType)
|
|
}
|
|
|
|
if characteristics["framework"] != "Next.js" {
|
|
t.Errorf("Expected framework 'Next.js', got '%s'", characteristics["framework"])
|
|
}
|
|
}
|
|
|
|
func TestProjectDetector_detectVersionFromPackageJSON(t *testing.T) {
|
|
t.Parallel()
|
|
tempDir := t.TempDir()
|
|
|
|
// Create package.json with version
|
|
packageJSON := `{
|
|
"name": "test-package",
|
|
"version": "2.1.0",
|
|
"description": "Test package"
|
|
}`
|
|
|
|
packagePath := filepath.Join(tempDir, "package.json")
|
|
if err := os.WriteFile(packagePath, []byte(packageJSON), 0600); err != nil { // #nosec G306 -- test file permissions
|
|
t.Fatalf("Failed to create package.json: %v", err)
|
|
}
|
|
|
|
output := internal.NewColoredOutput(true)
|
|
detector := &ProjectDetector{
|
|
output: output,
|
|
currentDir: tempDir,
|
|
}
|
|
|
|
version := detector.detectVersionFromPackageJSON()
|
|
if version != "2.1.0" {
|
|
t.Errorf("Expected version '2.1.0', got '%s'", version)
|
|
}
|
|
}
|
|
|
|
func TestProjectDetector_detectVersionFromFiles(t *testing.T) {
|
|
t.Parallel()
|
|
tempDir := t.TempDir()
|
|
|
|
// Create VERSION file
|
|
versionContent := "3.2.1\n"
|
|
versionPath := filepath.Join(tempDir, "VERSION")
|
|
if err := os.WriteFile(versionPath, []byte(versionContent), 0600); err != nil { // #nosec G306 -- test file permissions
|
|
t.Fatalf("Failed to create VERSION file: %v", err)
|
|
}
|
|
|
|
output := internal.NewColoredOutput(true)
|
|
detector := &ProjectDetector{
|
|
output: output,
|
|
currentDir: tempDir,
|
|
}
|
|
|
|
version := detector.detectVersionFromFiles()
|
|
if version != "3.2.1" {
|
|
t.Errorf("Expected version '3.2.1', got '%s'", version)
|
|
}
|
|
}
|
|
|
|
func TestProjectDetector_findActionFiles(t *testing.T) {
|
|
t.Parallel()
|
|
tempDir := t.TempDir()
|
|
|
|
// Create action files
|
|
actionYML := filepath.Join(tempDir, "action.yml")
|
|
if err := os.WriteFile(
|
|
actionYML,
|
|
[]byte("name: Test Action"),
|
|
0600, // #nosec G306 -- test file permissions
|
|
); err != nil {
|
|
t.Fatalf("Failed to create action.yml: %v", err)
|
|
}
|
|
|
|
// Create subdirectory with another action file
|
|
subDir := filepath.Join(tempDir, "subaction")
|
|
if err := os.MkdirAll(subDir, 0750); err != nil { // #nosec G301 -- test directory permissions
|
|
t.Fatalf("Failed to create subdirectory: %v", err)
|
|
}
|
|
|
|
subActionYAML := filepath.Join(subDir, "action.yaml")
|
|
if err := os.WriteFile(
|
|
subActionYAML,
|
|
[]byte("name: Sub Action"),
|
|
0600, // #nosec G306 -- test file permissions
|
|
); err != nil {
|
|
t.Fatalf("Failed to create sub action.yaml: %v", err)
|
|
}
|
|
|
|
output := internal.NewColoredOutput(true)
|
|
detector := &ProjectDetector{
|
|
output: output,
|
|
currentDir: tempDir,
|
|
}
|
|
|
|
// Test non-recursive
|
|
files, err := detector.findActionFiles(tempDir, false)
|
|
if err != nil {
|
|
t.Fatalf("findActionFiles() error = %v", err)
|
|
}
|
|
|
|
if len(files) != 1 {
|
|
t.Errorf("Expected 1 action file, got %d", len(files))
|
|
}
|
|
|
|
// Test recursive
|
|
files, err = detector.findActionFiles(tempDir, true)
|
|
if err != nil {
|
|
t.Fatalf("findActionFiles() error = %v", err)
|
|
}
|
|
|
|
if len(files) != 2 {
|
|
t.Errorf("Expected 2 action files, got %d", len(files))
|
|
}
|
|
}
|
|
|
|
func TestProjectDetector_isActionFile(t *testing.T) {
|
|
t.Parallel()
|
|
output := internal.NewColoredOutput(true)
|
|
detector := &ProjectDetector{
|
|
output: output,
|
|
}
|
|
|
|
tests := []struct {
|
|
filename string
|
|
expected bool
|
|
}{
|
|
{"action.yml", true},
|
|
{"action.yaml", true},
|
|
{"Action.yml", false},
|
|
{"action.yml.bak", false},
|
|
{"other.yml", false},
|
|
{"readme.md", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.filename, func(t *testing.T) {
|
|
t.Parallel()
|
|
result := detector.isActionFile(tt.filename)
|
|
if result != tt.expected {
|
|
t.Errorf("isActionFile(%s) = %v, want %v", tt.filename, result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestProjectDetector_suggestConfiguration(t *testing.T) {
|
|
t.Parallel()
|
|
output := internal.NewColoredOutput(true)
|
|
detector := &ProjectDetector{
|
|
output: output,
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
settings *DetectedSettings
|
|
expected string
|
|
}{
|
|
{
|
|
name: "composite action",
|
|
settings: &DetectedSettings{
|
|
HasCompositeAction: true,
|
|
},
|
|
expected: "professional",
|
|
},
|
|
{
|
|
name: "with dockerfile",
|
|
settings: &DetectedSettings{
|
|
HasDockerfile: true,
|
|
},
|
|
expected: "github",
|
|
},
|
|
{
|
|
name: "go project",
|
|
settings: &DetectedSettings{
|
|
Language: "Go",
|
|
},
|
|
expected: "minimal",
|
|
},
|
|
{
|
|
name: "with framework",
|
|
settings: &DetectedSettings{
|
|
Framework: "Next.js",
|
|
},
|
|
expected: "github",
|
|
},
|
|
{
|
|
name: "default case",
|
|
settings: &DetectedSettings{},
|
|
expected: "default",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
detector.suggestConfiguration(tt.settings)
|
|
if tt.settings.SuggestedTheme != tt.expected {
|
|
t.Errorf("Expected theme %s, got %s", tt.expected, tt.settings.SuggestedTheme)
|
|
}
|
|
})
|
|
}
|
|
}
|