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  }