diff --git a/cli/cmd/report/report.go b/cli/cmd/report/report.go index 043461c..c79555d 100644 --- a/cli/cmd/report/report.go +++ b/cli/cmd/report/report.go @@ -16,24 +16,39 @@ var ReportCmd = &cobra.Command{ Short: "Opens numerous report and feedback page.", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - if err := openURL(numerousReportURL); err != nil { + if err := openURL(numerousReportURL, runtime.GOOS, OSCommandExecutor{}); err != nil { fmt.Println("Error:", err) } }, } -func openURL(url string) error { - cmd, args, err := setCmdByOS(runtime.GOOS, isWSL) +type CommandExecutor interface { + Output(command string, args ...string) ([]byte, error) + Start(command string, args ...string) error +} + +type OSCommandExecutor struct{} + +func (osExec OSCommandExecutor) Output(command string, args ...string) ([]byte, error) { + return exec.Command(command, args...).Output() +} + +func (osExec OSCommandExecutor) Start(command string, args ...string) error { + return exec.Command(command, args...).Start() +} + +func openURL(url string, os string, exec CommandExecutor) error { + cmd, args, err := setCmdByOS(os, exec, isWSL) if err != nil { return err } fmt.Println("Opening the report page in your default browser.") args = append(args, url) - return exec.Command(cmd, args...).Start() + return exec.Start(cmd, args...) } -func setCmdByOS(os string, wsl func() (*bool, error)) (string, []string, error) { +func setCmdByOS(os string, exec CommandExecutor, wsl func(exec CommandExecutor) (*bool, error)) (string, []string, error) { switch os { case "windows": return "cmd", []string{"/c", "start"}, nil @@ -42,7 +57,7 @@ func setCmdByOS(os string, wsl func() (*bool, error)) (string, []string, error) case "freebsd", "openbsd", "netbsd": return "xdg-open", nil, nil case "linux": - wsl, err := wsl() + wsl, err := wsl(exec) if err != nil { return "", nil, errors.New("unexpected linux subsystem check error") } @@ -56,9 +71,9 @@ func setCmdByOS(os string, wsl func() (*bool, error)) (string, []string, error) } } -func isWSL() (*bool, error) { +func isWSL(exec CommandExecutor) (*bool, error) { var wsl bool - out, err := exec.Command("sh", "-c", "grep -i Windows /proc/version").Output() + out, err := exec.Output("sh", "-c", "grep -i Windows /proc/version") if err != nil && err.Error() != "exit status 1" { fmt.Println("Error:", err) return nil, err diff --git a/cli/cmd/report/report_test.go b/cli/cmd/report/report_test.go index 5ffb3c6..eb40eb4 100644 --- a/cli/cmd/report/report_test.go +++ b/cli/cmd/report/report_test.go @@ -1,11 +1,53 @@ package report import ( + "errors" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) +func TestOpenURL(t *testing.T) { + testURL := "numerous.test.com" + + testCases := []struct { + goos string + expectedCommand string + expectedArgs []string + output []byte + errorOutput string + errorStart string + }{ + {"windows", "cmd", []string{"/c", "start"}, nil, "", ""}, + {"darwin", "open", nil, nil, "", ""}, + {"freebsd", "xdg-open", nil, nil, "", ""}, + {"linux", "xdg-open", nil, []byte(nil), "exit status 1", ""}, + {"linux", "sensible-browser", nil, []byte("it will return a line that contains the windows word"), "", ""}, + {"unknown", "", nil, nil, "", "it wasn't possible to identify your OS"}, + } + + for _, tc := range testCases { + t.Run(tc.goos, func(t *testing.T) { + exec := new(FakeCommandExecutor) + exec.On("Output", "sh", "-c", "grep -i Windows /proc/version").Return(tc.output, tc.errorOutput) + mockArgs := aggregateArgs(tc.expectedCommand, &testURL, tc.expectedArgs...) + exec.On("Start", mockArgs...).Return(tc.errorStart) + err := openURL(testURL, tc.goos, exec) + if tc.goos == "linux" { + exec.AssertNumberOfCalls(t, "Output", 1) + } + + if tc.errorStart == "" { + exec.AssertNumberOfCalls(t, "Start", 1) + } else { + assert.Error(t, err) + assert.Equal(t, tc.errorStart, err.Error()) + } + }) + } +} + func TestSetCmdByOS(t *testing.T) { tests := []struct { goos string @@ -24,11 +66,10 @@ func TestSetCmdByOS(t *testing.T) { for _, test := range tests { t.Run(test.goos, func(t *testing.T) { - isWSLfake := func() (*bool, error) { + isWSLfake := func(exec CommandExecutor) (*bool, error) { return &test.wsl, nil } - - cmd, args, err := setCmdByOS(test.goos, isWSLfake) + cmd, args, err := setCmdByOS(test.goos, nil, isWSLfake) if test.errExpected { assert.Error(t, err) } else { @@ -40,4 +81,79 @@ func TestSetCmdByOS(t *testing.T) { } } -// TODO: test isWSL() and openURL(...) +func TestIsWSL(t *testing.T) { + tests := []struct { + name string + output []byte + errorOut string + resultExpected bool + errExpected bool + }{ + {"linux distro with grep exit status 1", []byte(nil), "exit status 1", false, false}, + {"wsl environment", []byte("it will return a line that contains the windows word"), "", true, false}, + {"linux distro without grep exit status 0", []byte(nil), "", false, false}, + {"grep any other error", []byte(nil), "any other error", false, true}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + exec := new(FakeCommandExecutor) + exec.On("Output", "sh", "-c", "grep -i Windows /proc/version").Return(test.output, test.errorOut) + result, err := isWSL(exec) + if !test.errExpected { + assert.Equal(t, test.resultExpected, *result) + assert.NoError(t, err) + } else { + assert.Nil(t, result) + assert.Error(t, err) + assert.Equal(t, test.errorOut, err.Error()) + } + }) + } +} + +type FakeCommandExecutor struct { + mock.Mock +} + +func (exec *FakeCommandExecutor) Output(command string, args ...string) ([]byte, error) { + var err error + allArgs := aggregateArgs(command, nil, args...) + mockArgs := exec.Called(allArgs...) + result := mockArgs.Get(0).([]byte) + errString := mockArgs.Get(1).(string) + if errString != "" { + err = errors.New(errString) + } else { + err = nil + } + + return result, err +} + +func (exec *FakeCommandExecutor) Start(command string, args ...string) error { + var err error + allArgs := aggregateArgs(command, nil, args...) + mockArgs := exec.Called(allArgs...) + errString := mockArgs.Get(0).(string) + if errString != "" { + err = errors.New(errString) + } else { + err = nil + } + + return err +} + +func aggregateArgs(functionMocked string, url *string, args ...string) []interface{} { + var argsPack []interface{} + argsPack = append(argsPack, functionMocked) + for _, a := range args { + argsPack = append(argsPack, a) + } + if url != nil { + argsPack = append(argsPack, *url) + } + + return argsPack +}