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 }