github.com/secure-build/gitlab-runner@v12.5.0+incompatible/commands/builds_helper_test.go (about)

     1  package commands
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"regexp"
     9  	"testing"
    10  
    11  	"github.com/prometheus/client_golang/prometheus"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  	"gitlab.com/gitlab-org/gitlab-runner/session"
    15  
    16  	"gitlab.com/gitlab-org/gitlab-runner/common"
    17  )
    18  
    19  var fakeRunner = &common.RunnerConfig{
    20  	RunnerCredentials: common.RunnerCredentials{
    21  		Token: "a1b2c3d4e5f6",
    22  	},
    23  }
    24  
    25  func TestBuildsHelperCollect(t *testing.T) {
    26  	ch := make(chan prometheus.Metric, 50)
    27  	b := newBuildsHelper()
    28  	b.builds = append(b.builds, &common.Build{
    29  		CurrentState: common.BuildRunStatePending,
    30  		CurrentStage: common.BuildStagePrepare,
    31  		Runner:       fakeRunner,
    32  	})
    33  	b.Collect(ch)
    34  	assert.Len(t, ch, 1)
    35  }
    36  
    37  func TestBuildsHelperAcquireRequestWithLimit(t *testing.T) {
    38  	runner := common.RunnerConfig{
    39  		RequestConcurrency: 2,
    40  	}
    41  
    42  	b := newBuildsHelper()
    43  	result := b.acquireRequest(&runner)
    44  	require.True(t, result)
    45  
    46  	result = b.acquireRequest(&runner)
    47  	require.True(t, result)
    48  
    49  	result = b.acquireRequest(&runner)
    50  	require.False(t, result, "allow only two requests")
    51  
    52  	result = b.releaseRequest(&runner)
    53  	require.True(t, result)
    54  
    55  	result = b.releaseRequest(&runner)
    56  	require.True(t, result)
    57  
    58  	result = b.releaseRequest(&runner)
    59  	require.False(t, result, "release only two requests")
    60  }
    61  
    62  func TestBuildsHelperAcquireRequestWithDefault(t *testing.T) {
    63  	runner := common.RunnerConfig{
    64  		RequestConcurrency: 0,
    65  	}
    66  
    67  	b := newBuildsHelper()
    68  	result := b.acquireRequest(&runner)
    69  	require.True(t, result)
    70  
    71  	result = b.acquireRequest(&runner)
    72  	require.False(t, result, "allow only one request")
    73  
    74  	result = b.releaseRequest(&runner)
    75  	require.True(t, result)
    76  
    77  	result = b.releaseRequest(&runner)
    78  	require.False(t, result, "release only one request")
    79  
    80  	result = b.acquireRequest(&runner)
    81  	require.True(t, result)
    82  
    83  	result = b.releaseRequest(&runner)
    84  	require.True(t, result)
    85  
    86  	result = b.releaseRequest(&runner)
    87  	require.False(t, result, "nothing to release")
    88  }
    89  
    90  func TestBuildsHelperAcquireBuildWithLimit(t *testing.T) {
    91  	runner := common.RunnerConfig{
    92  		Limit: 1,
    93  	}
    94  
    95  	b := newBuildsHelper()
    96  	result := b.acquireBuild(&runner)
    97  	require.True(t, result)
    98  
    99  	result = b.acquireBuild(&runner)
   100  	require.False(t, result, "allow only one build")
   101  
   102  	result = b.releaseBuild(&runner)
   103  	require.True(t, result)
   104  
   105  	result = b.releaseBuild(&runner)
   106  	require.False(t, result, "release only one build")
   107  }
   108  
   109  func TestBuildsHelperAcquireBuildUnlimited(t *testing.T) {
   110  	runner := common.RunnerConfig{
   111  		Limit: 0,
   112  	}
   113  
   114  	b := newBuildsHelper()
   115  	result := b.acquireBuild(&runner)
   116  	require.True(t, result)
   117  
   118  	result = b.acquireBuild(&runner)
   119  	require.True(t, result)
   120  
   121  	result = b.releaseBuild(&runner)
   122  	require.True(t, result)
   123  
   124  	result = b.releaseBuild(&runner)
   125  	require.True(t, result)
   126  }
   127  
   128  func TestBuildsHelperFindSessionByURL(t *testing.T) {
   129  	sess, err := session.NewSession(nil)
   130  	require.NoError(t, err)
   131  	build := common.Build{
   132  		Session: sess,
   133  		Runner: &common.RunnerConfig{
   134  			RunnerCredentials: common.RunnerCredentials{
   135  				Token: "abcd1234",
   136  			},
   137  		},
   138  	}
   139  
   140  	h := newBuildsHelper()
   141  	h.addBuild(&build)
   142  
   143  	foundSession := h.findSessionByURL(sess.Endpoint + "/action")
   144  	assert.Equal(t, sess, foundSession)
   145  
   146  	foundSession = h.findSessionByURL("/session/hash/action")
   147  	assert.Nil(t, foundSession)
   148  }
   149  
   150  type listJobsHandlerVersioningTest struct {
   151  	URL             string
   152  	expectedVersion string
   153  	expectedCode    int
   154  }
   155  
   156  func TestBuildsHelper_ListJobsHandlerVersioning(t *testing.T) {
   157  	baseURL := "/test/url"
   158  
   159  	tests := map[string]listJobsHandlerVersioningTest{
   160  		"no version specified": {
   161  			URL:             baseURL,
   162  			expectedVersion: "1",
   163  			expectedCode:    http.StatusOK,
   164  		},
   165  		"version 1 specified": {
   166  			URL:             baseURL + "?v=1",
   167  			expectedVersion: "1",
   168  			expectedCode:    http.StatusOK,
   169  		},
   170  		"version 2 specified": {
   171  			URL:             baseURL + "?v=2",
   172  			expectedVersion: "2",
   173  			expectedCode:    http.StatusOK,
   174  		},
   175  		"unsupported version specified": {
   176  			URL:          baseURL + "?v=3",
   177  			expectedCode: http.StatusNotFound,
   178  		},
   179  	}
   180  
   181  	b := newBuildsHelper()
   182  	mux := http.NewServeMux()
   183  	mux.HandleFunc(baseURL, b.ListJobsHandler)
   184  
   185  	server := httptest.NewServer(mux)
   186  	defer server.Close()
   187  
   188  	for name, test := range tests {
   189  		t.Run(name, func(t *testing.T) {
   190  			req, err := http.NewRequest(http.MethodGet, server.URL+test.URL, nil)
   191  			require.NoError(t, err)
   192  
   193  			resp, err := http.DefaultClient.Do(req)
   194  			require.NoError(t, err)
   195  			require.NotNil(t, resp)
   196  
   197  			assert.Equal(t, test.expectedCode, resp.StatusCode)
   198  
   199  			if test.expectedVersion != "" {
   200  				require.Contains(t, resp.Header, "X-List-Version")
   201  				assert.Equal(t, test.expectedVersion, resp.Header.Get("X-List-Version"))
   202  			}
   203  		})
   204  	}
   205  }
   206  
   207  type fakeResponseWriter struct {
   208  	output     *bytes.Buffer
   209  	header     http.Header
   210  	statusCode int
   211  }
   212  
   213  func (w *fakeResponseWriter) Header() http.Header            { return w.header }
   214  func (w *fakeResponseWriter) Write(data []byte) (int, error) { return w.output.Write(data) }
   215  func (w *fakeResponseWriter) WriteHeader(statusCode int)     { w.statusCode = statusCode }
   216  
   217  func newFakeResponseWriter() *fakeResponseWriter {
   218  	return &fakeResponseWriter{
   219  		output: &bytes.Buffer{},
   220  		header: http.Header{},
   221  	}
   222  }
   223  
   224  var testBuildCurrentID int
   225  
   226  func getTestBuild() *common.Build {
   227  	testBuildCurrentID++
   228  
   229  	runner := common.RunnerConfig{}
   230  	runner.Token = "a1b2c3d4"
   231  	jobInfo := common.JobInfo{
   232  		ProjectID: 1,
   233  	}
   234  
   235  	build := &common.Build{}
   236  	build.ID = testBuildCurrentID
   237  	build.Runner = &runner
   238  	build.JobInfo = jobInfo
   239  	build.GitInfo = common.GitInfo{
   240  		RepoURL: "https://gitlab.example.com/my-namespace/my-project.git",
   241  	}
   242  
   243  	return build
   244  }
   245  
   246  type listJobsHandlerTest struct {
   247  	build          *common.Build
   248  	version        string
   249  	expectedOutput []string
   250  	expectedRegexp []*regexp.Regexp
   251  	expectedStatus int
   252  }
   253  
   254  func TestBuildsHelper_ListJobsHandler(t *testing.T) {
   255  	build := getTestBuild()
   256  
   257  	tests := map[string]listJobsHandlerTest{
   258  		"no jobs": {
   259  			build:          nil,
   260  			expectedStatus: http.StatusOK,
   261  		},
   262  		"job exists": {
   263  			build: build,
   264  			expectedOutput: []string{
   265  				fmt.Sprintf("id=%d url=https://gitlab.example.com/my-namespace/my-project.git", build.ID),
   266  			},
   267  			expectedStatus: http.StatusOK,
   268  		},
   269  		"job exists v2": {
   270  			build:   build,
   271  			version: "2",
   272  			expectedOutput: []string{
   273  				fmt.Sprintf("url=https://gitlab.example.com/my-namespace/my-project/-/jobs/%d", build.ID),
   274  			},
   275  			expectedRegexp: []*regexp.Regexp{
   276  				regexp.MustCompile("duration=[0-9hms.]+"),
   277  			},
   278  			expectedStatus: http.StatusOK,
   279  		},
   280  	}
   281  
   282  	for name, test := range tests {
   283  		t.Run(name, func(t *testing.T) {
   284  			writer := newFakeResponseWriter()
   285  
   286  			URL := "/"
   287  			if test.version != "" {
   288  				URL = fmt.Sprintf("/?v=%s", test.version)
   289  			}
   290  
   291  			req, err := http.NewRequest(http.MethodGet, URL, nil)
   292  			require.NoError(t, err)
   293  
   294  			b := newBuildsHelper()
   295  			b.addBuild(test.build)
   296  			b.ListJobsHandler(writer, req)
   297  
   298  			if len(test.expectedOutput) == 0 && len(test.expectedRegexp) == 0 {
   299  				assert.Empty(t, writer.output.String())
   300  			} else {
   301  				for _, expectedOutput := range test.expectedOutput {
   302  					assert.Contains(t, writer.output.String(), expectedOutput)
   303  				}
   304  
   305  				for _, expectedRegexp := range test.expectedRegexp {
   306  					assert.Regexp(t, expectedRegexp, writer.output.String())
   307  				}
   308  			}
   309  
   310  			assert.Equal(t, test.expectedStatus, writer.statusCode)
   311  		})
   312  	}
   313  }
   314  
   315  func TestCreateJobURL(t *testing.T) {
   316  	testCases := map[string]string{
   317  		"http://gitlab.example.com/my-namespace/my-project.git":     "http://gitlab.example.com/my-namespace/my-project/-/jobs/1",
   318  		"http://gitlab.example.com/my-namespace/my-project":         "http://gitlab.example.com/my-namespace/my-project/-/jobs/1",
   319  		"http://gitlab.example.com/my-namespace/my.git.project.git": "http://gitlab.example.com/my-namespace/my.git.project/-/jobs/1",
   320  		"http://gitlab.example.com/my-namespace/my.git.project":     "http://gitlab.example.com/my-namespace/my.git.project/-/jobs/1",
   321  	}
   322  
   323  	for URL, expectedURL := range testCases {
   324  		jobURL := CreateJobURL(URL, 1)
   325  		assert.Equal(t, expectedURL, jobURL)
   326  	}
   327  }