github.com/secure-build/gitlab-runner@v12.5.0+incompatible/executors/shell/executor_shell_test.go (about)

     1  package shell_test
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"net/url"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/gorilla/websocket"
    18  	"github.com/hashicorp/go-version"
    19  	"github.com/stretchr/testify/assert"
    20  	"github.com/stretchr/testify/require"
    21  
    22  	"gitlab.com/gitlab-org/gitlab-runner/common"
    23  	"gitlab.com/gitlab-org/gitlab-runner/helpers"
    24  	"gitlab.com/gitlab-org/gitlab-runner/session"
    25  	"gitlab.com/gitlab-org/gitlab-runner/shells/shellstest"
    26  )
    27  
    28  const (
    29  	TestTimeout = 20 * time.Second
    30  )
    31  
    32  func gitInDir(dir string, args ...string) ([]byte, error) {
    33  	cmd := exec.Command("git", args...)
    34  	cmd.Dir = dir
    35  
    36  	return cmd.Output()
    37  }
    38  
    39  func skipOnGitWithMessage(t *testing.T, constraints string, message string) {
    40  	out, err := gitInDir("", "version")
    41  	if err != nil {
    42  		t.Fatal("Can't detect git version", err)
    43  		return
    44  	}
    45  
    46  	gitVersionOut := string(out)
    47  	split := strings.SplitN(gitVersionOut, " ", 3)
    48  	if len(split) < 3 {
    49  		t.Fatal("Can't extract git version from", gitVersionOut)
    50  		return
    51  	}
    52  
    53  	gitVersion, err := version.NewVersion(strings.TrimSpace(split[2]))
    54  	if err != nil {
    55  		t.Fatal("Can't detect git version", err)
    56  		return
    57  	}
    58  
    59  	rules, err := version.NewConstraint(constraints)
    60  	if err != nil {
    61  		t.Fatal("Invalid constraint", err)
    62  		return
    63  	}
    64  
    65  	shouldSkip := rules.Check(gitVersion)
    66  	if shouldSkip {
    67  		t.Skipf("Git %q found, skipping the test; %s", constraints, message)
    68  	}
    69  }
    70  
    71  func skipIfGitDoesNotSupportLFS(t *testing.T) {
    72  	skipOnGitWithMessage(t, "< 1.8.2", "available git version doesn't support LFS")
    73  }
    74  
    75  func skipOnGit(t *testing.T, constraints string) {
    76  	skipOnGitWithMessage(t, constraints, "")
    77  }
    78  
    79  func skipOnGit17x(t *testing.T) {
    80  	skipOnGit(t, "< 1.8")
    81  }
    82  
    83  func runBuildWithOptions(t *testing.T, build *common.Build, config *common.Config, trace *common.Trace) error {
    84  	timeoutTimer := time.AfterFunc(TestTimeout, func() {
    85  		t.Log("Timed out")
    86  		t.FailNow()
    87  	})
    88  	defer timeoutTimer.Stop()
    89  
    90  	return build.Run(config, trace)
    91  }
    92  
    93  func runBuildWithTrace(t *testing.T, build *common.Build, trace *common.Trace) error {
    94  	return runBuildWithOptions(t, build, &common.Config{}, trace)
    95  }
    96  
    97  func runBuild(t *testing.T, build *common.Build) error {
    98  	err := runBuildWithTrace(t, build, &common.Trace{Writer: os.Stdout})
    99  	assert.True(t, build.IsSharedEnv())
   100  	return err
   101  }
   102  
   103  func runBuildReturningOutput(t *testing.T, build *common.Build) (string, error) {
   104  	buf := bytes.NewBuffer(nil)
   105  	err := runBuildWithTrace(t, build, &common.Trace{Writer: buf})
   106  	output := buf.String()
   107  	t.Log(output)
   108  
   109  	return output, err
   110  }
   111  
   112  func newBuild(t *testing.T, getBuildResponse common.JobResponse, shell string) (*common.Build, func()) {
   113  	dir, err := ioutil.TempDir("", "gitlab-runner-shell-executor-test")
   114  	if err != nil {
   115  		t.Fatal(err)
   116  	}
   117  
   118  	t.Log("Build directory:", dir)
   119  
   120  	build := &common.Build{
   121  		JobResponse: getBuildResponse,
   122  		Runner: &common.RunnerConfig{
   123  			RunnerSettings: common.RunnerSettings{
   124  				BuildsDir: dir,
   125  				Executor:  "shell",
   126  				Shell:     shell,
   127  			},
   128  		},
   129  		SystemInterrupt: make(chan os.Signal, 1),
   130  		Session: &session.Session{
   131  			DisconnectCh: make(chan error),
   132  			TimeoutCh:    make(chan error),
   133  		},
   134  	}
   135  
   136  	cleanup := func() {
   137  		os.RemoveAll(dir)
   138  	}
   139  
   140  	return build, cleanup
   141  }
   142  
   143  func TestBuildSuccess(t *testing.T) {
   144  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   145  		successfulBuild, err := common.GetSuccessfulBuild()
   146  		assert.NoError(t, err)
   147  		build, cleanup := newBuild(t, successfulBuild, shell)
   148  		defer cleanup()
   149  
   150  		err = runBuild(t, build)
   151  		assert.NoError(t, err)
   152  	})
   153  }
   154  
   155  func TestBuildAbort(t *testing.T) {
   156  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   157  		longRunningBuild, err := common.GetLongRunningBuild()
   158  		assert.NoError(t, err)
   159  		build, cleanup := newBuild(t, longRunningBuild, shell)
   160  		defer cleanup()
   161  
   162  		abortTimer := time.AfterFunc(time.Second, func() {
   163  			t.Log("Interrupt")
   164  			build.SystemInterrupt <- os.Interrupt
   165  		})
   166  		defer abortTimer.Stop()
   167  
   168  		err = runBuild(t, build)
   169  		assert.EqualError(t, err, "aborted: interrupt")
   170  	})
   171  }
   172  
   173  func TestBuildCancel(t *testing.T) {
   174  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   175  		longRunningBuild, err := common.GetLongRunningBuild()
   176  		assert.NoError(t, err)
   177  		build, cleanup := newBuild(t, longRunningBuild, shell)
   178  		defer cleanup()
   179  
   180  		trace := &common.Trace{Writer: os.Stdout}
   181  
   182  		cancelTimer := time.AfterFunc(time.Second, func() {
   183  			t.Log("Cancel")
   184  			trace.CancelFunc()
   185  		})
   186  		defer cancelTimer.Stop()
   187  
   188  		err = runBuildWithTrace(t, build, trace)
   189  		assert.EqualError(t, err, "canceled")
   190  		assert.IsType(t, err, &common.BuildError{})
   191  	})
   192  }
   193  
   194  func TestBuildWithIndexLock(t *testing.T) {
   195  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   196  		successfulBuild, err := common.GetSuccessfulBuild()
   197  		assert.NoError(t, err)
   198  		build, cleanup := newBuild(t, successfulBuild, shell)
   199  		defer cleanup()
   200  
   201  		err = runBuild(t, build)
   202  		assert.NoError(t, err)
   203  
   204  		build.JobResponse.AllowGitFetch = true
   205  		err = ioutil.WriteFile(build.BuildDir+"/.git/index.lock", []byte{}, os.ModeSticky)
   206  		require.NoError(t, err)
   207  
   208  		err = runBuild(t, build)
   209  		assert.NoError(t, err)
   210  	})
   211  }
   212  
   213  func TestBuildWithShallowLock(t *testing.T) {
   214  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   215  		successfulBuild, err := common.GetSuccessfulBuild()
   216  		assert.NoError(t, err)
   217  		build, cleanup := newBuild(t, successfulBuild, shell)
   218  		defer cleanup()
   219  
   220  		build.Variables = append(build.Variables,
   221  			common.JobVariable{Key: "GIT_DEPTH", Value: "1"},
   222  			common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   223  
   224  		err = runBuild(t, build)
   225  		assert.NoError(t, err)
   226  
   227  		err = ioutil.WriteFile(build.BuildDir+"/.git/shallow.lock", []byte{}, os.ModeSticky)
   228  		require.NoError(t, err)
   229  
   230  		err = runBuild(t, build)
   231  		assert.NoError(t, err)
   232  	})
   233  }
   234  
   235  func TestBuildWithHeadLock(t *testing.T) {
   236  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   237  		successfulBuild, err := common.GetSuccessfulBuild()
   238  		assert.NoError(t, err)
   239  		build, cleanup := newBuild(t, successfulBuild, shell)
   240  		defer cleanup()
   241  
   242  		err = runBuild(t, build)
   243  		assert.NoError(t, err)
   244  
   245  		build.JobResponse.AllowGitFetch = true
   246  		err = ioutil.WriteFile(build.BuildDir+"/.git/HEAD.lock", []byte{}, os.ModeSticky)
   247  		require.NoError(t, err)
   248  
   249  		err = runBuild(t, build)
   250  		assert.NoError(t, err)
   251  	})
   252  }
   253  
   254  func TestBuildWithGitLFSHook(t *testing.T) {
   255  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   256  		successfulBuild, err := common.GetSuccessfulBuild()
   257  		assert.NoError(t, err)
   258  		build, cleanup := newBuild(t, successfulBuild, shell)
   259  		defer cleanup()
   260  
   261  		err = runBuild(t, build)
   262  		assert.NoError(t, err)
   263  
   264  		gitLFSPostCheckoutHook := "#!/bin/sh\necho 'running git lfs hook' >&2\nexit 2\n"
   265  
   266  		err = os.MkdirAll(build.BuildDir+"/.git/hooks/", 0755)
   267  		require.NoError(t, err)
   268  		err = ioutil.WriteFile(build.BuildDir+"/.git/hooks/post-checkout", []byte(gitLFSPostCheckoutHook), 0777)
   269  		require.NoError(t, err)
   270  		build.JobResponse.AllowGitFetch = true
   271  
   272  		err = runBuild(t, build)
   273  		assert.NoError(t, err)
   274  	})
   275  }
   276  
   277  func assertLFSFileDownloaded(t *testing.T, build *common.Build) {
   278  	lfsFilePath := filepath.Join(build.FullProjectDir(), "files", "lfs", "file_1.lfs")
   279  	info, err := os.Stat(lfsFilePath)
   280  	require.NoError(t, err)
   281  	assert.Equal(t, common.FilesLFSFile1LFSsize, info.Size(), "invalid size of %q file", lfsFilePath)
   282  }
   283  
   284  func assertLFSFileNotDownloaded(t *testing.T, build *common.Build) {
   285  	lfsFilePath := filepath.Join(build.FullProjectDir(), "files", "lfs", "file_1.lfs")
   286  	info, err := os.Stat(lfsFilePath)
   287  	require.NoError(t, err)
   288  	assert.True(t, info.Size() < common.FilesLFSFile1LFSsize, "invalid size of %q file - expected to be less then downloaded LFS object", lfsFilePath)
   289  }
   290  
   291  func assertLFSFileNotPresent(t *testing.T, build *common.Build) {
   292  	lfsFilePath := filepath.Join(build.FullProjectDir(), "files", "lfs", "file_1.lfs")
   293  	_, err := os.Stat(lfsFilePath)
   294  	require.IsType(t, &os.PathError{}, err)
   295  	assert.Equal(t, lfsFilePath, err.(*os.PathError).Path)
   296  }
   297  
   298  func TestBuildWithGitStrategyNoneWithoutLFS(t *testing.T) {
   299  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   300  		successfulBuild, err := common.GetSuccessfulBuild()
   301  		assert.NoError(t, err)
   302  		build, cleanup := newBuild(t, successfulBuild, shell)
   303  		defer cleanup()
   304  
   305  		build.Runner.PreCloneScript = "echo pre-clone-script"
   306  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "none"})
   307  
   308  		out, err := runBuildReturningOutput(t, build)
   309  		assert.NoError(t, err)
   310  		assert.NotContains(t, out, "pre-clone-script")
   311  		assert.NotContains(t, out, "Created fresh repository")
   312  		assert.NotContains(t, out, "Fetching changes")
   313  		assert.Contains(t, out, "Skipping Git repository setup")
   314  	})
   315  }
   316  
   317  func TestBuildWithGitStrategyNoneWithLFS(t *testing.T) {
   318  	skipIfGitDoesNotSupportLFS(t)
   319  
   320  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   321  		successfulBuild, err := common.GetRemoteSuccessfulLFSBuild()
   322  		assert.NoError(t, err)
   323  		build, cleanup := newBuild(t, successfulBuild, shell)
   324  		defer cleanup()
   325  
   326  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "none"})
   327  
   328  		out, err := runBuildReturningOutput(t, build)
   329  		assert.NoError(t, err)
   330  		assert.NotContains(t, out, "Created fresh repository")
   331  		assert.NotContains(t, out, "Fetching changes")
   332  		assert.Contains(t, out, "Skipping Git repository setup")
   333  		assertLFSFileNotPresent(t, build)
   334  	})
   335  }
   336  
   337  func TestBuildWithGitStrategyFetchWithoutLFS(t *testing.T) {
   338  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   339  		successfulBuild, err := common.GetSuccessfulBuild()
   340  		assert.NoError(t, err)
   341  		build, cleanup := newBuild(t, successfulBuild, shell)
   342  		defer cleanup()
   343  
   344  		build.Runner.PreCloneScript = "echo pre-clone-script"
   345  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   346  
   347  		out, err := runBuildReturningOutput(t, build)
   348  		assert.NoError(t, err)
   349  		assert.Contains(t, out, "Created fresh repository")
   350  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   351  
   352  		out, err = runBuildReturningOutput(t, build)
   353  		assert.NoError(t, err)
   354  		assert.Contains(t, out, "Fetching changes")
   355  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   356  		assert.Contains(t, out, "pre-clone-script")
   357  	})
   358  }
   359  
   360  func TestBuildWithGitStrategyFetchWithLFS(t *testing.T) {
   361  	skipIfGitDoesNotSupportLFS(t)
   362  
   363  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   364  		successfulBuild, err := common.GetRemoteSuccessfulBuild()
   365  		assert.NoError(t, err)
   366  		build, cleanup := newBuild(t, successfulBuild, shell)
   367  		defer cleanup()
   368  
   369  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   370  
   371  		out, err := runBuildReturningOutput(t, build)
   372  		assert.NoError(t, err)
   373  		assert.Contains(t, out, "Created fresh repository")
   374  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   375  		assertLFSFileNotPresent(t, build)
   376  
   377  		build.GitInfo = common.GetLFSGitInfo(build.GitInfo.RepoURL)
   378  
   379  		out, err = runBuildReturningOutput(t, build)
   380  		assert.NoError(t, err)
   381  		assert.Contains(t, out, "Fetching changes")
   382  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   383  		assertLFSFileDownloaded(t, build)
   384  	})
   385  }
   386  
   387  func TestBuildWithGitStrategyFetchWithUserDisabledLFS(t *testing.T) {
   388  	skipIfGitDoesNotSupportLFS(t)
   389  
   390  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   391  		successfulBuild, err := common.GetRemoteSuccessfulBuild()
   392  		assert.NoError(t, err)
   393  		build, cleanup := newBuild(t, successfulBuild, shell)
   394  		defer cleanup()
   395  
   396  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_LFS_SKIP_SMUDGE", Value: "1", Public: true})
   397  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   398  
   399  		out, err := runBuildReturningOutput(t, build)
   400  		assert.NoError(t, err)
   401  		assert.Contains(t, out, "Created fresh repository")
   402  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   403  		assertLFSFileNotPresent(t, build)
   404  
   405  		build.GitInfo = common.GetLFSGitInfo(build.GitInfo.RepoURL)
   406  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_LFS_SKIP_SMUDGE", Value: "1", Public: true})
   407  
   408  		out, err = runBuildReturningOutput(t, build)
   409  		assert.NoError(t, err)
   410  		assert.Contains(t, out, "Fetching changes")
   411  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   412  		assertLFSFileNotDownloaded(t, build)
   413  	})
   414  }
   415  
   416  func TestBuildWithGitStrategyFetchNoCheckoutWithoutLFS(t *testing.T) {
   417  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   418  		successfulBuild, err := common.GetSuccessfulBuild()
   419  		assert.NoError(t, err)
   420  		build, cleanup := newBuild(t, successfulBuild, shell)
   421  		defer cleanup()
   422  
   423  		build.Runner.PreCloneScript = "echo pre-clone-script"
   424  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   425  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_CHECKOUT", Value: "false"})
   426  
   427  		out, err := runBuildReturningOutput(t, build)
   428  		assert.NoError(t, err)
   429  		assert.Contains(t, out, "Created fresh repository")
   430  		assert.Contains(t, out, "Skipping Git checkout")
   431  
   432  		out, err = runBuildReturningOutput(t, build)
   433  		assert.NoError(t, err)
   434  		assert.Contains(t, out, "Fetching changes")
   435  		assert.Contains(t, out, "Skipping Git checkout")
   436  		assert.Contains(t, out, "pre-clone-script")
   437  	})
   438  }
   439  
   440  func TestBuildWithGitStrategyFetchNoCheckoutWithLFS(t *testing.T) {
   441  	skipIfGitDoesNotSupportLFS(t)
   442  
   443  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   444  		successfulBuild, err := common.GetRemoteSuccessfulLFSBuild()
   445  		assert.NoError(t, err)
   446  		build, cleanup := newBuild(t, successfulBuild, shell)
   447  		defer cleanup()
   448  
   449  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   450  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_CHECKOUT", Value: "false"})
   451  
   452  		out, err := runBuildReturningOutput(t, build)
   453  		assert.NoError(t, err)
   454  		assert.Contains(t, out, "Created fresh repository")
   455  		assert.Contains(t, out, "Skipping Git checkout")
   456  		assertLFSFileNotPresent(t, build)
   457  
   458  		build.GitInfo = common.GetLFSGitInfo(build.GitInfo.RepoURL)
   459  
   460  		out, err = runBuildReturningOutput(t, build)
   461  		assert.NoError(t, err)
   462  		assert.Contains(t, out, "Fetching changes")
   463  		assert.Contains(t, out, "Skipping Git checkout")
   464  		assertLFSFileNotPresent(t, build)
   465  	})
   466  }
   467  
   468  func TestBuildWithGitStrategyCloneWithoutLFS(t *testing.T) {
   469  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   470  		successfulBuild, err := common.GetSuccessfulBuild()
   471  		assert.NoError(t, err)
   472  		build, cleanup := newBuild(t, successfulBuild, shell)
   473  		defer cleanup()
   474  
   475  		build.Runner.PreCloneScript = "echo pre-clone-script"
   476  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "clone"})
   477  
   478  		out, err := runBuildReturningOutput(t, build)
   479  		assert.NoError(t, err)
   480  		assert.Contains(t, out, "Created fresh repository")
   481  
   482  		out, err = runBuildReturningOutput(t, build)
   483  		assert.NoError(t, err)
   484  		assert.Contains(t, out, "Created fresh repository")
   485  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   486  		assert.Contains(t, out, "pre-clone-script")
   487  	})
   488  }
   489  
   490  func TestBuildWithGitStrategyCloneWithLFS(t *testing.T) {
   491  	skipIfGitDoesNotSupportLFS(t)
   492  
   493  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   494  		successfulBuild, err := common.GetRemoteSuccessfulLFSBuild()
   495  		assert.NoError(t, err)
   496  		build, cleanup := newBuild(t, successfulBuild, shell)
   497  		defer cleanup()
   498  
   499  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "clone"})
   500  
   501  		out, err := runBuildReturningOutput(t, build)
   502  		assert.NoError(t, err)
   503  		assert.Contains(t, out, "Created fresh repository")
   504  		assertLFSFileDownloaded(t, build)
   505  	})
   506  }
   507  
   508  func TestBuildWithGitStrategyCloneWithUserDisabledLFS(t *testing.T) {
   509  	skipIfGitDoesNotSupportLFS(t)
   510  
   511  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   512  		successfulBuild, err := common.GetRemoteSuccessfulLFSBuild()
   513  		assert.NoError(t, err)
   514  		build, cleanup := newBuild(t, successfulBuild, shell)
   515  		defer cleanup()
   516  
   517  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "clone"})
   518  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_LFS_SKIP_SMUDGE", Value: "1", Public: true})
   519  
   520  		out, err := runBuildReturningOutput(t, build)
   521  		assert.NoError(t, err)
   522  		assert.Contains(t, out, "Created fresh repository")
   523  		assertLFSFileNotDownloaded(t, build)
   524  	})
   525  }
   526  
   527  func TestBuildWithGitStrategyCloneNoCheckoutWithoutLFS(t *testing.T) {
   528  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   529  		successfulBuild, err := common.GetSuccessfulBuild()
   530  		assert.NoError(t, err)
   531  		build, cleanup := newBuild(t, successfulBuild, shell)
   532  		defer cleanup()
   533  
   534  		build.Runner.PreCloneScript = "echo pre-clone-script"
   535  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "clone"})
   536  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_CHECKOUT", Value: "false"})
   537  
   538  		out, err := runBuildReturningOutput(t, build)
   539  		assert.NoError(t, err)
   540  		assert.Contains(t, out, "Created fresh repository")
   541  
   542  		out, err = runBuildReturningOutput(t, build)
   543  		assert.NoError(t, err)
   544  		assert.Contains(t, out, "Created fresh repository")
   545  		assert.Contains(t, out, "Skipping Git checkout")
   546  		assert.Contains(t, out, "pre-clone-script")
   547  	})
   548  }
   549  
   550  func TestBuildWithGitStrategyCloneNoCheckoutWithLFS(t *testing.T) {
   551  	skipIfGitDoesNotSupportLFS(t)
   552  
   553  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   554  		successfulBuild, err := common.GetRemoteSuccessfulLFSBuild()
   555  		assert.NoError(t, err)
   556  		build, cleanup := newBuild(t, successfulBuild, shell)
   557  		defer cleanup()
   558  
   559  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "clone"})
   560  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_CHECKOUT", Value: "false"})
   561  
   562  		out, err := runBuildReturningOutput(t, build)
   563  		assert.NoError(t, err)
   564  		assert.Contains(t, out, "Created fresh repository")
   565  		assert.Contains(t, out, "Skipping Git checkout")
   566  		assertLFSFileNotPresent(t, build)
   567  	})
   568  }
   569  
   570  func TestBuildWithSubmoduleLFSPullsLFSObject(t *testing.T) {
   571  	skipIfGitDoesNotSupportLFS(t)
   572  
   573  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   574  		successfulBuild, err := common.GetRemoteSuccessfulBuild()
   575  		assert.NoError(t, err)
   576  		build, cleanup := newBuild(t, successfulBuild, shell)
   577  		defer cleanup()
   578  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   579  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "normal"})
   580  		build.GitInfo = common.GetSubmoduleLFSGitInfo(build.GitInfo.RepoURL)
   581  
   582  		out, err := runBuildReturningOutput(t, build)
   583  		assert.NoError(t, err)
   584  		assert.Contains(t, out, "Created fresh repository")
   585  
   586  		f, err := os.Stat(filepath.Join(build.FullProjectDir(), "lfs", "1.lfs"))
   587  		require.NoError(t, err)
   588  		assert.Equal(t, common.FilesLFSFile1LFSsize, f.Size())
   589  	})
   590  }
   591  
   592  func TestBuildWithSubmoduleLFSDisabledSmudging(t *testing.T) {
   593  	skipIfGitDoesNotSupportLFS(t)
   594  
   595  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   596  		successfulBuild, err := common.GetRemoteSuccessfulBuild()
   597  		assert.NoError(t, err)
   598  		build, cleanup := newBuild(t, successfulBuild, shell)
   599  		defer cleanup()
   600  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   601  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "normal"})
   602  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_LFS_SKIP_SMUDGE", Value: "1", Public: true})
   603  		build.GitInfo = common.GetSubmoduleLFSGitInfo(build.GitInfo.RepoURL)
   604  
   605  		out, err := runBuildReturningOutput(t, build)
   606  		assert.NoError(t, err)
   607  		assert.Contains(t, out, "Created fresh repository")
   608  
   609  		f, err := os.Stat(filepath.Join(build.FullProjectDir(), "lfs", "1.lfs"))
   610  		require.NoError(t, err)
   611  		assert.True(t, f.Size() < common.FilesLFSFile1LFSsize)
   612  	})
   613  }
   614  
   615  func TestBuildWithGitSubmoduleStrategyNone(t *testing.T) {
   616  	for _, strategy := range []string{"none", ""} {
   617  		t.Run("strategy "+strategy, func(t *testing.T) {
   618  			shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   619  				successfulBuild, err := common.GetSuccessfulBuild()
   620  				assert.NoError(t, err)
   621  				build, cleanup := newBuild(t, successfulBuild, shell)
   622  				defer cleanup()
   623  
   624  				build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "none"})
   625  
   626  				out, err := runBuildReturningOutput(t, build)
   627  				assert.NoError(t, err)
   628  				assert.Contains(t, out, "Skipping Git submodules setup")
   629  				assert.NotContains(t, out, "Updating/initializing submodules...")
   630  				assert.NotContains(t, out, "Updating/initializing submodules recursively...")
   631  
   632  				_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", ".git"))
   633  				assert.Error(t, err, "Submodule not should have been initialized")
   634  
   635  				_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", "tests", "example", ".git"))
   636  				assert.Error(t, err, "The submodule's submodule should not have been initialized")
   637  			})
   638  		})
   639  	}
   640  }
   641  
   642  func TestBuildWithGitSubmoduleStrategyNormal(t *testing.T) {
   643  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   644  		successfulBuild, err := common.GetSuccessfulBuild()
   645  		assert.NoError(t, err)
   646  		build, cleanup := newBuild(t, successfulBuild, shell)
   647  		defer cleanup()
   648  
   649  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "normal"})
   650  
   651  		out, err := runBuildReturningOutput(t, build)
   652  		assert.NoError(t, err)
   653  		assert.NotContains(t, out, "Skipping Git submodules setup")
   654  		assert.Contains(t, out, "Updating/initializing submodules...")
   655  		assert.NotContains(t, out, "Updating/initializing submodules recursively...")
   656  
   657  		_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", ".git"))
   658  		assert.NoError(t, err, "Submodule should have been initialized")
   659  
   660  		_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", "tests", "example", ".git"))
   661  		assert.Error(t, err, "The submodule's submodule should not have been initialized")
   662  	})
   663  }
   664  
   665  func TestBuildWithGitSubmoduleStrategyRecursive(t *testing.T) {
   666  	skipOnGit17x(t)
   667  
   668  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   669  		successfulBuild, err := common.GetSuccessfulBuild()
   670  		assert.NoError(t, err)
   671  		build, cleanup := newBuild(t, successfulBuild, shell)
   672  		defer cleanup()
   673  
   674  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "recursive"})
   675  
   676  		out, err := runBuildReturningOutput(t, build)
   677  		assert.NoError(t, err)
   678  		assert.NotContains(t, out, "Skipping Git submodules setup")
   679  		assert.NotContains(t, out, "Updating/initializing submodules...")
   680  		assert.Contains(t, out, "Updating/initializing submodules recursively...")
   681  
   682  		_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", ".git"))
   683  		assert.NoError(t, err, "Submodule should have been initialized")
   684  
   685  		_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", "tests", "example", ".git"))
   686  		assert.NoError(t, err, "The submodule's submodule should have been initialized")
   687  	})
   688  }
   689  
   690  func TestBuildWithGitFetchSubmoduleStrategyRecursive(t *testing.T) {
   691  	skipOnGit17x(t)
   692  
   693  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   694  		successfulBuild, err := common.GetSuccessfulBuild()
   695  		assert.NoError(t, err)
   696  		build, cleanup := newBuild(t, successfulBuild, shell)
   697  		defer cleanup()
   698  
   699  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   700  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "recursive"})
   701  
   702  		out, err := runBuildReturningOutput(t, build)
   703  		assert.NoError(t, err)
   704  		assert.NotContains(t, out, "Skipping Git submodules setup")
   705  		assert.NotContains(t, out, "Updating/initializing submodules...")
   706  		assert.Contains(t, out, "Updating/initializing submodules recursively...")
   707  
   708  		_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", ".git"))
   709  		assert.NoError(t, err, "Submodule should have been initialized")
   710  
   711  		_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", "tests", "example", ".git"))
   712  		assert.NoError(t, err, "The submodule's submodule should have been initialized")
   713  
   714  		// Create a file not tracked that should be cleaned in submodule.
   715  		excludedFilePath := filepath.Join(build.BuildDir, "gitlab-grack", "excluded_file")
   716  		err = ioutil.WriteFile(excludedFilePath, []byte{}, os.ModePerm)
   717  		require.NoError(t, err)
   718  
   719  		// Run second build, to run fetch.
   720  		out, err = runBuildReturningOutput(t, build)
   721  		assert.NoError(t, err)
   722  		assert.NotContains(t, out, "Created fresh repository")
   723  		assert.Contains(t, out, "Removing excluded_file")
   724  	})
   725  }
   726  
   727  func TestBuildWithGitSubmoduleStrategyInvalid(t *testing.T) {
   728  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   729  		successfulBuild, err := common.GetSuccessfulBuild()
   730  		assert.NoError(t, err)
   731  		build, cleanup := newBuild(t, successfulBuild, shell)
   732  		defer cleanup()
   733  
   734  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "invalid"})
   735  
   736  		out, err := runBuildReturningOutput(t, build)
   737  		assert.EqualError(t, err, "unknown GIT_SUBMODULE_STRATEGY")
   738  		assert.NotContains(t, out, "Skipping Git submodules setup")
   739  		assert.NotContains(t, out, "Updating/initializing submodules...")
   740  		assert.NotContains(t, out, "Updating/initializing submodules recursively...")
   741  	})
   742  }
   743  
   744  func TestBuildWithGitSubmoduleStrategyRecursiveAndGitStrategyNone(t *testing.T) {
   745  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   746  		successfulBuild, err := common.GetSuccessfulBuild()
   747  		assert.NoError(t, err)
   748  		build, cleanup := newBuild(t, successfulBuild, shell)
   749  		defer cleanup()
   750  
   751  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "none"})
   752  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "recursive"})
   753  
   754  		out, err := runBuildReturningOutput(t, build)
   755  		assert.NoError(t, err)
   756  		assert.NotContains(t, out, "Created fresh repository")
   757  		assert.NotContains(t, out, "Fetching changes")
   758  		assert.Contains(t, out, "Skipping Git repository setup")
   759  		assert.NotContains(t, out, "Updating/initializing submodules...")
   760  		assert.NotContains(t, out, "Updating/initializing submodules recursively...")
   761  		assert.Contains(t, out, "Skipping Git submodules setup")
   762  	})
   763  }
   764  
   765  func TestBuildWithGitSubmoduleModified(t *testing.T) {
   766  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   767  		successfulBuild, err := common.GetSuccessfulBuild()
   768  		assert.NoError(t, err)
   769  		build, cleanup := newBuild(t, successfulBuild, shell)
   770  		defer cleanup()
   771  
   772  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "normal"})
   773  
   774  		out, err := runBuildReturningOutput(t, build)
   775  		assert.NoError(t, err)
   776  		assert.Contains(t, out, "Updating/initializing submodules...")
   777  
   778  		submoduleDir := filepath.Join(build.BuildDir, "gitlab-grack")
   779  		submoduleReadme := filepath.Join(submoduleDir, "README.md")
   780  
   781  		// modify submodule and commit
   782  		modifySubmoduleBeforeCommit := "committed change"
   783  		err = ioutil.WriteFile(submoduleReadme, []byte(modifySubmoduleBeforeCommit), os.ModeSticky)
   784  		require.NoError(t, err)
   785  		_, err = gitInDir(submoduleDir, "add", "README.md")
   786  		assert.NoError(t, err)
   787  		_, err = gitInDir(submoduleDir, "config", "user.name", "test")
   788  		assert.NoError(t, err)
   789  		_, err = gitInDir(submoduleDir, "config", "user.email", "test@example.org")
   790  		assert.NoError(t, err)
   791  		_, err = gitInDir(submoduleDir, "commit", "-m", "modify submodule")
   792  		assert.NoError(t, err)
   793  
   794  		_, err = gitInDir(build.BuildDir, "add", "gitlab-grack")
   795  		assert.NoError(t, err)
   796  		_, err = gitInDir(build.BuildDir, "config", "user.name", "test")
   797  		assert.NoError(t, err)
   798  		_, err = gitInDir(build.BuildDir, "config", "user.email", "test@example.org")
   799  		assert.NoError(t, err)
   800  		_, err = gitInDir(build.BuildDir, "commit", "-m", "modify submodule")
   801  		assert.NoError(t, err)
   802  
   803  		// modify submodule without commit before second build
   804  		modifySubmoduleAfterCommit := "not committed change"
   805  		err = ioutil.WriteFile(submoduleReadme, []byte(modifySubmoduleAfterCommit), os.ModeSticky)
   806  		require.NoError(t, err)
   807  
   808  		build.JobResponse.AllowGitFetch = true
   809  		out, err = runBuildReturningOutput(t, build)
   810  		assert.NoError(t, err)
   811  		assert.NotContains(t, out, "Your local changes to the following files would be overwritten by checkout")
   812  		assert.NotContains(t, out, "Please commit your changes or stash them before you switch branches")
   813  		assert.NotContains(t, out, "Aborting")
   814  		assert.Contains(t, out, "Updating/initializing submodules...")
   815  	})
   816  }
   817  
   818  func TestBuildWithoutDebugTrace(t *testing.T) {
   819  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   820  		successfulBuild, err := common.GetSuccessfulBuild()
   821  		assert.NoError(t, err)
   822  		build, cleanup := newBuild(t, successfulBuild, shell)
   823  		defer cleanup()
   824  
   825  		// The default build shouldn't have debug tracing enabled
   826  		out, err := runBuildReturningOutput(t, build)
   827  		assert.NoError(t, err)
   828  		assert.NotRegexp(t, `[^$] echo Hello World`, out)
   829  	})
   830  }
   831  func TestBuildWithDebugTrace(t *testing.T) {
   832  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   833  		successfulBuild, err := common.GetSuccessfulBuild()
   834  		assert.NoError(t, err)
   835  		build, cleanup := newBuild(t, successfulBuild, shell)
   836  		defer cleanup()
   837  
   838  		build.Variables = append(build.Variables, common.JobVariable{Key: "CI_DEBUG_TRACE", Value: "true"})
   839  
   840  		out, err := runBuildReturningOutput(t, build)
   841  		assert.NoError(t, err)
   842  		assert.Regexp(t, `[^$] echo Hello World`, out)
   843  	})
   844  }
   845  
   846  func TestBuildMultilineCommand(t *testing.T) {
   847  	multilineBuild, err := common.GetMultilineBashBuild()
   848  	assert.NoError(t, err)
   849  	build, cleanup := newBuild(t, multilineBuild, "bash")
   850  	defer cleanup()
   851  
   852  	// The default build shouldn't have debug tracing enabled
   853  	out, err := runBuildReturningOutput(t, build)
   854  	assert.NoError(t, err)
   855  	assert.NotContains(t, out, "bash")
   856  	assert.Contains(t, out, "Hello World")
   857  	assert.Contains(t, out, "collapsed multi-line command")
   858  }
   859  
   860  func TestBuildWithBrokenGitSSLCAInfo(t *testing.T) {
   861  	skipOnGit17x(t)
   862  	skipOnGit(t, ">= 2.10.2")
   863  
   864  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   865  		successfulBuild, err := common.GetRemoteBrokenTLSBuild()
   866  		assert.NoError(t, err)
   867  		build, cleanup := newBuild(t, successfulBuild, shell)
   868  		defer cleanup()
   869  
   870  		build.Runner.URL = "https://gitlab.com"
   871  
   872  		out, err := runBuildReturningOutput(t, build)
   873  		assert.Error(t, err)
   874  		assert.Contains(t, out, "Created fresh repository")
   875  		assert.NotContains(t, out, "Updating/initializing submodules")
   876  	})
   877  }
   878  
   879  func TestBuildWithGoodGitSSLCAInfo(t *testing.T) {
   880  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   881  		successfulBuild, err := common.GetRemoteGitLabComTLSBuild()
   882  		assert.NoError(t, err)
   883  		build, cleanup := newBuild(t, successfulBuild, shell)
   884  		defer cleanup()
   885  
   886  		build.Runner.URL = "https://gitlab.com"
   887  
   888  		out, err := runBuildReturningOutput(t, build)
   889  		assert.NoError(t, err)
   890  		assert.Contains(t, out, "Created fresh repository")
   891  		assert.Contains(t, out, "Updating/initializing submodules")
   892  	})
   893  }
   894  
   895  // TestBuildWithGitSSLAndStrategyFetch describes issue https://gitlab.com/gitlab-org/gitlab-runner/issues/2991
   896  func TestBuildWithGitSSLAndStrategyFetch(t *testing.T) {
   897  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   898  		successfulBuild, err := common.GetRemoteGitLabComTLSBuild()
   899  		assert.NoError(t, err)
   900  		build, cleanup := newBuild(t, successfulBuild, shell)
   901  		defer cleanup()
   902  
   903  		build.Runner.PreCloneScript = "echo pre-clone-script"
   904  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   905  
   906  		out, err := runBuildReturningOutput(t, build)
   907  		assert.NoError(t, err)
   908  		assert.Contains(t, out, "Created fresh repository")
   909  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   910  
   911  		out, err = runBuildReturningOutput(t, build)
   912  		assert.NoError(t, err)
   913  		assert.Contains(t, out, "Fetching changes")
   914  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   915  		assert.Contains(t, out, "pre-clone-script")
   916  	})
   917  }
   918  
   919  func TestBuildWithUntrackedDirFromPreviousBuild(t *testing.T) {
   920  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   921  		successfulBuild, err := common.GetRemoteSuccessfulBuild()
   922  		assert.NoError(t, err)
   923  		build, cleanup := newBuild(t, successfulBuild, shell)
   924  		defer cleanup()
   925  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   926  
   927  		out, err := runBuildReturningOutput(t, build)
   928  		assert.NoError(t, err)
   929  		assert.Contains(t, out, "Created fresh repository")
   930  
   931  		err = os.MkdirAll(fmt.Sprintf("%s/.test", build.FullProjectDir()), 0644)
   932  		require.NoError(t, err)
   933  
   934  		out, err = runBuildReturningOutput(t, build)
   935  		assert.NoError(t, err)
   936  		assert.Contains(t, out, "Removing .test/")
   937  	})
   938  }
   939  
   940  func TestBuildChangesBranchesWhenFetchingRepo(t *testing.T) {
   941  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   942  		successfulBuild, err := common.GetRemoteSuccessfulBuild()
   943  		assert.NoError(t, err)
   944  		build, cleanup := newBuild(t, successfulBuild, shell)
   945  		defer cleanup()
   946  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   947  
   948  		out, err := runBuildReturningOutput(t, build)
   949  		assert.NoError(t, err)
   950  		assert.Contains(t, out, "Created fresh repository")
   951  
   952  		// Another build using the same repo but different branch.
   953  		build.GitInfo = common.GetLFSGitInfo(build.GitInfo.RepoURL)
   954  		out, err = runBuildReturningOutput(t, build)
   955  		assert.NoError(t, err)
   956  		assert.Contains(t, out, "Checking out 2371dd05 as add-lfs-object...")
   957  	})
   958  }
   959  
   960  func TestBuildPowerShellCatchesExceptions(t *testing.T) {
   961  	if helpers.SkipIntegrationTests(t, "powershell") {
   962  		t.Skip()
   963  	}
   964  
   965  	successfulBuild, err := common.GetRemoteSuccessfulBuild()
   966  	assert.NoError(t, err)
   967  	build, cleanup := newBuild(t, successfulBuild, "powershell")
   968  	defer cleanup()
   969  	build.Variables = append(build.Variables, common.JobVariable{Key: "ErrorActionPreference", Value: "Stop"})
   970  	build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   971  
   972  	out, err := runBuildReturningOutput(t, build)
   973  	assert.NoError(t, err)
   974  	assert.Contains(t, out, "Created fresh repository")
   975  
   976  	out, err = runBuildReturningOutput(t, build)
   977  	assert.NoError(t, err)
   978  	assert.NotContains(t, out, "Created fresh repository")
   979  	assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   980  
   981  	build.Variables = append(build.Variables, common.JobVariable{Key: "ErrorActionPreference", Value: "Continue"})
   982  	out, err = runBuildReturningOutput(t, build)
   983  	assert.NoError(t, err)
   984  	assert.NotContains(t, out, "Created fresh repository")
   985  	assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   986  
   987  	build.Variables = append(build.Variables, common.JobVariable{Key: "ErrorActionPreference", Value: "SilentlyContinue"})
   988  	out, err = runBuildReturningOutput(t, build)
   989  	assert.NoError(t, err)
   990  	assert.NotContains(t, out, "Created fresh repository")
   991  	assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   992  }
   993  
   994  func TestInteractiveTerminal(t *testing.T) {
   995  	cases := []struct {
   996  		app                string
   997  		shell              string
   998  		command            string
   999  		expectedStatusCode int
  1000  	}{
  1001  		{
  1002  			app:                "bash",
  1003  			shell:              "bash",
  1004  			command:            "sleep 5",
  1005  			expectedStatusCode: http.StatusSwitchingProtocols,
  1006  		},
  1007  		{
  1008  			app:                "cmd.exe",
  1009  			shell:              "cmd",
  1010  			command:            "timeout 2",
  1011  			expectedStatusCode: http.StatusInternalServerError,
  1012  		},
  1013  		{
  1014  			app:                "powershell.exe",
  1015  			shell:              "powershell",
  1016  			command:            "Start-Sleep -s 2",
  1017  			expectedStatusCode: http.StatusInternalServerError,
  1018  		},
  1019  	}
  1020  
  1021  	for _, c := range cases {
  1022  		t.Run(c.shell, func(t *testing.T) {
  1023  			if helpers.SkipIntegrationTests(t, c.app) {
  1024  				t.Skip()
  1025  			}
  1026  
  1027  			successfulBuild, err := common.GetLocalBuildResponse(c.command)
  1028  			require.NoError(t, err)
  1029  			build, cleanup := newBuild(t, successfulBuild, c.shell)
  1030  			defer cleanup()
  1031  			sess, err := session.NewSession(nil)
  1032  			build.Session = sess
  1033  			require.NoError(t, err)
  1034  
  1035  			buildOut := make(chan string)
  1036  
  1037  			go func() {
  1038  				buf := bytes.NewBuffer(nil)
  1039  				err := runBuildWithOptions(
  1040  					t,
  1041  					build,
  1042  					&common.Config{SessionServer: common.SessionServer{SessionTimeout: 2}},
  1043  					&common.Trace{Writer: buf},
  1044  				)
  1045  				require.NoError(t, err)
  1046  
  1047  				buildOut <- buf.String()
  1048  			}()
  1049  
  1050  			// Wait until the build starts.
  1051  			for build.Session.Mux() == nil {
  1052  				time.Sleep(10 * time.Millisecond)
  1053  			}
  1054  
  1055  			srv := httptest.NewServer(build.Session.Mux())
  1056  			defer srv.Close()
  1057  
  1058  			u := url.URL{
  1059  				Scheme: "ws",
  1060  				Host:   srv.Listener.Addr().String(),
  1061  				Path:   build.Session.Endpoint + "/exec",
  1062  			}
  1063  			headers := http.Header{
  1064  				"Authorization": []string{build.Session.Token},
  1065  			}
  1066  			conn, resp, err := websocket.DefaultDialer.Dial(u.String(), headers)
  1067  			assert.NoError(t, err)
  1068  			assert.Equal(t, c.expectedStatusCode, resp.StatusCode)
  1069  
  1070  			defer func() {
  1071  				if conn != nil {
  1072  					conn.Close()
  1073  				}
  1074  			}()
  1075  
  1076  			if c.expectedStatusCode == http.StatusSwitchingProtocols {
  1077  				_, message, err := conn.ReadMessage()
  1078  				assert.NoError(t, err)
  1079  				assert.NotEmpty(t, string(message))
  1080  
  1081  				out := <-buildOut
  1082  				t.Log(out)
  1083  				assert.Contains(t, out, "Terminal is connected, will time out in 2s...")
  1084  				return
  1085  			}
  1086  
  1087  			out := <-buildOut
  1088  			t.Log(out)
  1089  			assert.NotContains(t, out, "Terminal is connected, will time out in 2s...")
  1090  		})
  1091  	}
  1092  }
  1093  
  1094  func TestBuildWithGitCleanFlags(t *testing.T) {
  1095  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
  1096  		jobResponse, err := common.GetSuccessfulBuild()
  1097  		assert.NoError(t, err)
  1098  
  1099  		build, cleanup := newBuild(t, jobResponse, shell)
  1100  		defer cleanup()
  1101  
  1102  		build.Variables = append(build.Variables,
  1103  			common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"},
  1104  			common.JobVariable{Key: "GIT_CLEAN_FLAGS", Value: "-ffdx cleanup_file"})
  1105  
  1106  		// Run build and save file
  1107  		err = runBuild(t, build)
  1108  		require.NoError(t, err)
  1109  
  1110  		excludedFilePath := filepath.Join(build.BuildDir, "excluded_file")
  1111  		cleanUpFilePath := filepath.Join(build.BuildDir, "cleanup_file")
  1112  
  1113  		err = ioutil.WriteFile(excludedFilePath, []byte{}, os.ModePerm)
  1114  		require.NoError(t, err)
  1115  		err = ioutil.WriteFile(cleanUpFilePath, []byte{}, os.ModePerm)
  1116  		require.NoError(t, err)
  1117  
  1118  		// Re-run build and ensure that file still exists
  1119  		err = runBuild(t, build)
  1120  		require.NoError(t, err)
  1121  
  1122  		_, err = os.Stat(excludedFilePath)
  1123  		assert.NoError(t, err, "excluded_file does exist")
  1124  		_, err = os.Stat(cleanUpFilePath)
  1125  		assert.Error(t, err, "cleanup_file does not exist")
  1126  	})
  1127  }