github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/command/command_test.go (about) 1 //go:build unit 2 // +build unit 3 4 package command 5 6 import ( 7 "bytes" 8 "fmt" 9 "io" 10 "os" 11 "os/exec" 12 "strings" 13 "testing" 14 15 "github.com/SAP/jenkins-library/pkg/log" 16 "github.com/stretchr/testify/assert" 17 ) 18 19 // based on https://golang.org/src/os/exec/exec_test.go 20 func helperCommand(command string, s ...string) (cmd *exec.Cmd) { 21 cs := []string{"-test.run=TestHelperProcess", "--", command} 22 cs = append(cs, s...) 23 cmd = exec.Command(os.Args[0], cs...) 24 cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} 25 return cmd 26 } 27 28 func TestShellRun(t *testing.T) { 29 30 t.Run("test shell", func(t *testing.T) { 31 ExecCommand = helperCommand 32 defer func() { ExecCommand = exec.Command }() 33 o := new(bytes.Buffer) 34 e := new(bytes.Buffer) 35 36 s := Command{stdout: o, stderr: e} 37 s.RunShell("/bin/bash", "myScript") 38 39 t.Run("success case", func(t *testing.T) { 40 t.Run("stdin-stdout", func(t *testing.T) { 41 expectedOut := "Stdout: command /bin/bash - Stdin: myScript\n" 42 if oStr := o.String(); oStr != expectedOut { 43 t.Errorf("expected: %v got: %v", expectedOut, oStr) 44 } 45 }) 46 t.Run("stderr", func(t *testing.T) { 47 expectedErr := "Stderr: command /bin/bash\n" 48 if eStr := e.String(); eStr != expectedErr { 49 t.Errorf("expected: %v got: %v", expectedErr, eStr) 50 } 51 }) 52 }) 53 }) 54 } 55 56 func TestExecutableRun(t *testing.T) { 57 58 t.Run("test executable", func(t *testing.T) { 59 ExecCommand = helperCommand 60 defer func() { ExecCommand = exec.Command }() 61 stdout := new(bytes.Buffer) 62 stderr := new(bytes.Buffer) 63 64 t.Run("success case", func(t *testing.T) { 65 ex := Command{stdout: stdout, stderr: stderr} 66 ex.RunExecutable("echo", []string{"foo bar", "baz"}...) 67 68 assert.Equal(t, 0, ex.GetExitCode()) 69 70 t.Run("stdin", func(t *testing.T) { 71 expectedOut := "foo bar baz\n" 72 if oStr := stdout.String(); oStr != expectedOut { 73 t.Errorf("expected: %v got: %v", expectedOut, oStr) 74 } 75 }) 76 t.Run("stderr", func(t *testing.T) { 77 expectedErr := "Stderr: command echo\n" 78 if eStr := stderr.String(); eStr != expectedErr { 79 t.Errorf("expected: %v got: %v", expectedErr, eStr) 80 } 81 }) 82 }) 83 84 t.Run("success case - log parsing", func(t *testing.T) { 85 log.SetErrorCategory(log.ErrorUndefined) 86 ex := Command{stdout: stdout, stderr: stderr, ErrorCategoryMapping: map[string][]string{"config": {"command echo"}}} 87 ex.RunExecutable("echo", []string{"foo bar", "baz"}...) 88 assert.Equal(t, log.ErrorConfiguration, log.GetErrorCategory()) 89 }) 90 91 t.Run("success case - log parsing long line", func(t *testing.T) { 92 log.SetErrorCategory(log.ErrorUndefined) 93 ex := Command{stdout: stdout, stderr: stderr, ErrorCategoryMapping: map[string][]string{"config": {"aaaa"}}} 94 ex.RunExecutable("long", []string{"foo bar", "baz"}...) 95 assert.Equal(t, log.ErrorUndefined, log.GetErrorCategory()) 96 }) 97 98 log.SetErrorCategory(log.ErrorUndefined) 99 }) 100 } 101 102 func TestEnvironmentVariables(t *testing.T) { 103 104 ExecCommand = helperCommand 105 defer func() { ExecCommand = exec.Command }() 106 107 stdout := new(bytes.Buffer) 108 stderr := new(bytes.Buffer) 109 110 ex := Command{stdout: stdout, stderr: stderr} 111 112 // helperCommand function replaces the full environment with one single entry 113 // (GO_WANT_HELPER_PROCESS), hence there is no need for checking if the DEBUG 114 // environment variable already exists in the set of environment variables for the 115 // current process. 116 ex.SetEnv([]string{"DEBUG=true"}) 117 ex.RunExecutable("env") 118 119 oStr := stdout.String() 120 121 if !strings.Contains(oStr, "DEBUG=true") { 122 t.Errorf("expected Environment variable not found") 123 } 124 } 125 126 func TestPrepareOut(t *testing.T) { 127 128 t.Run("os", func(t *testing.T) { 129 s := Command{} 130 s.prepareOut() 131 132 if s.stdout != os.Stdout { 133 t.Errorf("expected out to be os.Stdout") 134 } 135 136 if s.stderr != os.Stderr { 137 t.Errorf("expected err to be os.Stderr") 138 } 139 }) 140 141 t.Run("custom", func(t *testing.T) { 142 o := bytes.NewBufferString("") 143 e := bytes.NewBufferString("") 144 s := Command{stdout: o, stderr: e} 145 s.prepareOut() 146 147 expectOut := "Test out" 148 expectErr := "Test err" 149 s.stdout.Write([]byte(expectOut)) 150 s.stderr.Write([]byte(expectErr)) 151 152 t.Run("out", func(t *testing.T) { 153 if o.String() != expectOut { 154 t.Errorf("expected: %v got: %v", expectOut, o.String()) 155 } 156 }) 157 t.Run("err", func(t *testing.T) { 158 if e.String() != expectErr { 159 t.Errorf("expected: %v got: %v", expectErr, e.String()) 160 } 161 }) 162 }) 163 } 164 165 func TestParseConsoleErrors(t *testing.T) { 166 cmd := Command{ 167 ErrorCategoryMapping: map[string][]string{ 168 "config": {"configuration error 1", "configuration error 2"}, 169 "build": {"build failed"}, 170 }, 171 } 172 173 tt := []struct { 174 consoleLine string 175 expectedCategory log.ErrorCategory 176 }{ 177 {consoleLine: "this is an error", expectedCategory: log.ErrorUndefined}, 178 {consoleLine: "this is configuration error 2", expectedCategory: log.ErrorConfiguration}, 179 {consoleLine: "the build failed", expectedCategory: log.ErrorBuild}, 180 } 181 182 for _, test := range tt { 183 log.SetErrorCategory(log.ErrorUndefined) 184 cmd.parseConsoleErrors(test.consoleLine) 185 assert.Equal(t, test.expectedCategory, log.GetErrorCategory(), test.consoleLine) 186 } 187 log.SetErrorCategory(log.ErrorUndefined) 188 } 189 190 func TestMatchPattern(t *testing.T) { 191 tt := []struct { 192 text string 193 pattern string 194 expected bool 195 }{ 196 {text: "", pattern: "", expected: true}, 197 {text: "simple test", pattern: "", expected: false}, 198 {text: "simple test", pattern: "no", expected: false}, 199 {text: "simple test", pattern: "simple", expected: true}, 200 {text: "simple test", pattern: "test", expected: true}, 201 {text: "advanced pattern test", pattern: "advanced * test", expected: true}, 202 {text: "advanced pattern failed", pattern: "advanced * test", expected: false}, 203 {text: "advanced pattern with multiple placeholders", pattern: "advanced * with * placeholders", expected: true}, 204 {text: "advanced pattern lacking multiple placeholders", pattern: "advanced * with * placeholders", expected: false}, 205 } 206 207 for _, test := range tt { 208 assert.Equalf(t, test.expected, matchPattern(test.text, test.pattern), test.text) 209 } 210 } 211 212 func TestCmdPipes(t *testing.T) { 213 cmd := helperCommand("echo", "foo bar", "baz") 214 defer func() { ExecCommand = exec.Command }() 215 216 t.Run("success case", func(t *testing.T) { 217 o, e, err := cmdPipes(cmd) 218 t.Run("no error", func(t *testing.T) { 219 if err != nil { 220 t.Errorf("error occurred but no error expected") 221 } 222 }) 223 224 t.Run("out pipe", func(t *testing.T) { 225 if o == nil { 226 t.Errorf("no pipe received") 227 } 228 }) 229 230 t.Run("err pipe", func(t *testing.T) { 231 if e == nil { 232 t.Errorf("no pipe received") 233 } 234 }) 235 }) 236 } 237 238 // based on https://golang.org/src/os/exec/exec_test.go 239 // this is not directly executed 240 func TestHelperProcess(*testing.T) { 241 242 if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { 243 return 244 } 245 defer os.Exit(0) 246 247 args := os.Args 248 for len(args) > 0 { 249 if args[0] == "--" { 250 args = args[1:] 251 break 252 } 253 args = args[1:] 254 } 255 if len(args) == 0 { 256 fmt.Fprintf(os.Stderr, "No command\n") 257 os.Exit(2) 258 } 259 260 cmd, args := args[0], args[1:] 261 switch cmd { 262 case "/bin/bash": 263 o, _ := io.ReadAll(os.Stdin) 264 fmt.Fprintf(os.Stdout, "Stdout: command %v - Stdin: %v\n", cmd, string(o)) 265 fmt.Fprintf(os.Stderr, "Stderr: command %v\n", cmd) 266 case "echo": 267 iargs := []interface{}{} 268 for _, s := range args { 269 iargs = append(iargs, s) 270 } 271 fmt.Println(iargs...) 272 fmt.Fprintf(os.Stderr, "Stderr: command %v\n", cmd) 273 case "env": 274 for _, e := range os.Environ() { 275 fmt.Println(e) 276 } 277 case "long": 278 b := []byte("a") 279 size := 64000 280 b = bytes.Repeat(b, size) 281 282 fmt.Fprint(os.Stderr, b) 283 default: 284 fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd) 285 os.Exit(2) 286 287 } 288 }