diff --git a/pkg/cmd/root/pre_command.go b/pkg/cmd/root/pre_command.go index 69117bb32..c47144f34 100644 --- a/pkg/cmd/root/pre_command.go +++ b/pkg/cmd/root/pre_command.go @@ -131,7 +131,9 @@ func checkForUpdateAndMetrics(cVersion string, f *cmdutil.Factory, settings *tok metric.Send(settings) } - tagName, err := github.GetVersionGitHub("azion") + git := github.NewGithub() + + tagName, err := git.GetVersionGitHub("azion") if err != nil { return err } diff --git a/pkg/github/github.go b/pkg/github/github.go index 0033670f8..a07af38cb 100644 --- a/pkg/github/github.go +++ b/pkg/github/github.go @@ -19,14 +19,36 @@ import ( gitignore "github.com/sabhiram/go-gitignore" ) +type Github struct { + GetVersionGitHub func(name string) (string, error) + Clone func(url, path string) error + GetNameRepo func(url string) string + CheckGitignore func(path string) (bool, error) + WriteGitignore func(path string) error +} + type Release struct { TagName string `json:"tag_name"` } -func GetVersionGitHub(name string) (string, error) { - apiURL := fmt.Sprintf("https://api.github.com/repos/aziontech/%s/releases/latest", name) +var ( + ApiURL string +) + +func NewGithub() *Github { + return &Github{ + GetVersionGitHub: getVersionGitHub, + Clone: clone, + GetNameRepo: getNameRepo, + CheckGitignore: checkGitignore, + WriteGitignore: writeGitignore, + } +} + +func getVersionGitHub(name string) (string, error) { + ApiURL = fmt.Sprintf("https://api.github.com/repos/aziontech/%s/releases/latest", name) - response, err := http.Get(apiURL) + response, err := http.Get(ApiURL) if err != nil { logger.Debug("Failed to get latest version of "+name, zap.Error(err)) return "", err @@ -53,8 +75,7 @@ func GetVersionGitHub(name string) (string, error) { return release.TagName, nil } -// Clone clone the repository using git -func Clone(url, path string) error { +func clone(url, path string) error { _, err := git.PlainClone(path, false, &git.CloneOptions{ URL: url, }) @@ -67,24 +88,19 @@ func Clone(url, path string) error { return nil } -// GetNameRepoFunction to get the repository name from the URL -func GetNameRepo(url string) string { - // Remove the initial part of the URL +func getNameRepo(url string) string { parts := strings.Split(url, "/") repoPart := parts[len(parts)-1] - // Remove the .git folder if it exists. repoPart = strings.TrimSuffix(repoPart, ".git") return repoPart } -func CheckGitignore(path string) (bool, error) { +func checkGitignore(path string) (bool, error) { logger.Debug("Checking .gitignore file for existence of Vulcan files") path = filepath.Join(path, ".gitignore") object, err := gitignore.CompileIgnoreFile(path) if err != nil { - // if the error is "no such file or directory" we can return false and nil for error, because the code that called this func will create - // the .gitignore file if errors.Is(err, os.ErrNotExist) { return false, nil } @@ -98,14 +114,12 @@ func CheckGitignore(path string) (bool, error) { return true, nil } -func WriteGitignore(path string) error { +func writeGitignore(path string) error { logger.Debug("Writing .gitignore file") path = filepath.Join(path, ".gitignore") - // Lines to add to .gitignore linesToAdd := []string{"#Paths added by Azion CLI", ".edge/", ".vulcan"} - // Open the file in append mode, create if not exists file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { logger.Error("Error opening file", zap.Error(err)) @@ -123,7 +137,6 @@ func WriteGitignore(path string) error { } } - // Ensure all data is written to the file if err := writer.Flush(); err != nil { logger.Error("Error flushing writer", zap.Error(err)) return err diff --git a/pkg/github/github_test.go b/pkg/github/github_test.go new file mode 100644 index 000000000..38bb9fb73 --- /dev/null +++ b/pkg/github/github_test.go @@ -0,0 +1,264 @@ +package github + +import ( + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" + + "github.com/aziontech/azion-cli/pkg/logger" + "go.uber.org/zap/zapcore" +) + +func TestGetVersionGitHub(t *testing.T) { + logger.New(zapcore.DebugLevel) + tests := []struct { + name string + repoName string + wantTag string + statusCode int + response string + wantErr bool + }{ + { + name: "not found", + repoName: "test-repo", + wantTag: "", + statusCode: http.StatusNotFound, + response: `{"message": "Not Found"}`, + wantErr: false, + }, + { + name: "successful response", + repoName: "azion-cli", + wantTag: "1.30.0", + statusCode: http.StatusOK, + response: `{"tag_name": "1.30.0"}`, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(tt.statusCode) + w.Write([]byte(tt.response)) + })) + defer server.Close() + + oldURL := ApiURL + ApiURL = server.URL + "/repos/aziontech/%s/releases/tag/1.30.0" + defer func() { ApiURL = oldURL }() + + gh := NewGithub() + gh.GetVersionGitHub = getVersionGitHub + + gotTag, err := gh.GetVersionGitHub(tt.repoName) + if (err != nil) != tt.wantErr { + t.Errorf("GetVersionGitHub() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotTag != tt.wantTag { + t.Errorf("GetVersionGitHub() = %v, want %v", gotTag, tt.wantTag) + } + }) + } +} + +func TestClone(t *testing.T) { + logger.New(zapcore.DebugLevel) + tests := []struct { + name string + url string + wantErr bool + setup func() (string, func()) + }{ + { + name: "invalid url", + url: "invalid-url", + wantErr: true, + setup: func() (string, func()) { + return t.TempDir(), func() {} + }, + }, + { + name: "valid url", + url: "https://github.com/aziontech/azion-cli.git", + setup: func() (string, func()) { + // Create a temporary directory + dir := t.TempDir() + // Return the directory path and a cleanup function + return dir, func() { + // Cleanup steps if needed + os.RemoveAll(dir) + } + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + path, cleanup := tt.setup() + defer cleanup() + + gh := NewGithub() + gh.Clone = clone + + if err := gh.Clone(tt.url, path); (err != nil) != tt.wantErr { + t.Errorf("Clone() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestGetNameRepo(t *testing.T) { + logger.New(zapcore.DebugLevel) + tests := []struct { + name string + url string + want string + }{ + { + name: "with .git", + url: "https://github.com/aziontech/azion-cli.git", + want: "azion-cli", + }, + { + name: "without .git", + url: "https://github.com/aziontech/azion-cli", + want: "azion-cli", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gh := NewGithub() + gh.GetNameRepo = getNameRepo + + if got := gh.GetNameRepo(tt.url); got != tt.want { + t.Errorf("GetNameRepo() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCheckGitignore(t *testing.T) { + logger.New(zapcore.DebugLevel) + tests := []struct { + name string + setup func() string + want bool + wantErr bool + cleanup func(string) + }{ + { + name: "exists", + setup: func() string { + path := t.TempDir() + file := filepath.Join(path, ".gitignore") + os.WriteFile(file, []byte(".edge/\n.vulcan\n"), 0644) + return path + }, + want: true, + wantErr: false, + cleanup: func(path string) { + os.RemoveAll(path) + }, + }, + { + name: "does not exist", + setup: func() string { + return t.TempDir() + }, + want: false, + wantErr: false, + cleanup: func(path string) { + os.RemoveAll(path) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + path := tt.setup() + defer tt.cleanup(path) + + gh := NewGithub() + gh.CheckGitignore = checkGitignore + + got, err := gh.CheckGitignore(path) + if (err != nil) != tt.wantErr { + t.Errorf("CheckGitignore() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("CheckGitignore() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestWriteGitignore(t *testing.T) { + logger.New(zapcore.DebugLevel) + tests := []struct { + name string + setup func() string + wantErr bool + wantFile string + cleanup func(string) + }{ + { + name: "success", + setup: func() string { + return t.TempDir() + }, + wantErr: false, + wantFile: "#Paths added by Azion CLI\n.edge/\n.vulcan\n", + cleanup: func(path string) { + os.RemoveAll(path) + }, + }, + { + name: "error opening file", + setup: func() string { + path := t.TempDir() + file := filepath.Join(path, ".gitignore") + os.WriteFile(file, []byte{}, 0444) // Create a read-only file + return path + }, + wantErr: true, + cleanup: func(path string) { + os.Chmod(filepath.Join(path, ".gitignore"), 0644) // Restore permissions before cleanup + os.RemoveAll(path) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + path := tt.setup() + defer tt.cleanup(path) + + gh := NewGithub() + gh.WriteGitignore = writeGitignore + + err := gh.WriteGitignore(path) + if (err != nil) != tt.wantErr { + t.Errorf("WriteGitignore() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr { + got, err := os.ReadFile(filepath.Join(path, ".gitignore")) + if err != nil { + t.Errorf("Error reading .gitignore file: %v", err) + return + } + if string(got) != tt.wantFile { + t.Errorf("WriteGitignore() = %v, want %v", string(got), tt.wantFile) + } + } + }) + } +}