gitlab.com/jfprevost/gitlab-runner-notlscheck@v11.11.4+incompatible/common/build_test.go (about) 1 package common 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "net/http" 8 "net/http/httptest" 9 "net/url" 10 "os" 11 "path/filepath" 12 "testing" 13 "time" 14 15 "github.com/gorilla/websocket" 16 "github.com/sirupsen/logrus" 17 "github.com/sirupsen/logrus/hooks/test" 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/mock" 20 "github.com/stretchr/testify/require" 21 22 "gitlab.com/gitlab-org/gitlab-runner/helpers/docker" 23 "gitlab.com/gitlab-org/gitlab-runner/session" 24 "gitlab.com/gitlab-org/gitlab-runner/session/terminal" 25 ) 26 27 func init() { 28 s := MockShell{} 29 s.On("GetName").Return("script-shell") 30 s.On("GenerateScript", mock.Anything, mock.Anything).Return("script", nil) 31 RegisterShell(&s) 32 } 33 34 func TestBuildRun(t *testing.T) { 35 e := MockExecutor{} 36 defer e.AssertExpectations(t) 37 38 p := MockExecutorProvider{} 39 defer p.AssertExpectations(t) 40 41 // Create executor only once 42 p.On("CanCreate").Return(true).Once() 43 p.On("GetDefaultShell").Return("bash").Once() 44 p.On("GetFeatures", mock.Anything).Return(nil).Twice() 45 46 p.On("Create").Return(&e).Once() 47 48 // We run everything once 49 e.On("Prepare", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() 50 e.On("Finish", nil).Return().Once() 51 e.On("Cleanup").Return().Once() 52 53 // Run script successfully 54 e.On("Shell").Return(&ShellScriptInfo{Shell: "script-shell"}) 55 e.On("Run", matchBuildStage(BuildStagePrepare)).Return(nil).Once() 56 e.On("Run", matchBuildStage(BuildStageGetSources)).Return(nil).Once() 57 e.On("Run", matchBuildStage(BuildStageRestoreCache)).Return(nil).Once() 58 e.On("Run", matchBuildStage(BuildStageDownloadArtifacts)).Return(nil).Once() 59 e.On("Run", matchBuildStage(BuildStageUserScript)).Return(nil).Once() 60 e.On("Run", matchBuildStage(BuildStageAfterScript)).Return(nil).Once() 61 e.On("Run", matchBuildStage(BuildStageArchiveCache)).Return(nil).Once() 62 e.On("Run", matchBuildStage(BuildStageUploadOnSuccessArtifacts)).Return(nil).Once() 63 64 RegisterExecutor("build-run-test", &p) 65 66 successfulBuild, err := GetSuccessfulBuild() 67 assert.NoError(t, err) 68 build := &Build{ 69 JobResponse: successfulBuild, 70 Runner: &RunnerConfig{ 71 RunnerSettings: RunnerSettings{ 72 Executor: "build-run-test", 73 }, 74 }, 75 } 76 err = build.Run(&Config{}, &Trace{Writer: os.Stdout}) 77 assert.NoError(t, err) 78 } 79 80 func TestBuildPredefinedVariables(t *testing.T) { 81 e := MockExecutor{} 82 defer e.AssertExpectations(t) 83 84 p := MockExecutorProvider{} 85 defer p.AssertExpectations(t) 86 87 // Create executor only once 88 p.On("CanCreate").Return(true).Once() 89 p.On("GetDefaultShell").Return("bash").Once() 90 p.On("GetFeatures", mock.Anything).Return(nil).Twice() 91 92 p.On("Create").Return(&e).Once() 93 94 // We run everything once 95 e.On("Prepare", mock.Anything). 96 Return(func(options ExecutorPrepareOptions) error { 97 options.Build.StartBuild("/root/dir", "/cache/dir", false, false) 98 return nil 99 }).Once() 100 e.On("Finish", nil).Return().Once() 101 e.On("Cleanup").Return().Once() 102 103 // Run script successfully 104 e.On("Shell").Return(&ShellScriptInfo{Shell: "script-shell"}) 105 e.On("Run", matchBuildStage(BuildStagePrepare)).Return(nil).Once() 106 e.On("Run", matchBuildStage(BuildStageGetSources)).Return(nil).Once() 107 e.On("Run", matchBuildStage(BuildStageRestoreCache)).Return(nil).Once() 108 e.On("Run", matchBuildStage(BuildStageDownloadArtifacts)).Return(nil).Once() 109 e.On("Run", matchBuildStage(BuildStageUserScript)).Return(nil).Once() 110 e.On("Run", matchBuildStage(BuildStageAfterScript)).Return(nil).Once() 111 e.On("Run", matchBuildStage(BuildStageArchiveCache)).Return(nil).Once() 112 e.On("Run", matchBuildStage(BuildStageUploadOnSuccessArtifacts)).Return(nil).Once() 113 114 RegisterExecutor(t.Name(), &p) 115 116 successfulBuild, err := GetSuccessfulBuild() 117 assert.NoError(t, err) 118 build := &Build{ 119 JobResponse: successfulBuild, 120 Runner: &RunnerConfig{ 121 RunnerSettings: RunnerSettings{ 122 Executor: t.Name(), 123 }, 124 }, 125 } 126 err = build.Run(&Config{}, &Trace{Writer: os.Stdout}) 127 assert.NoError(t, err) 128 129 projectDir := build.GetAllVariables().Get("CI_PROJECT_DIR") 130 assert.NotEmpty(t, projectDir, "should have CI_PROJECT_DIR") 131 } 132 133 func TestBuildRunNoModifyConfig(t *testing.T) { 134 e := MockExecutor{} 135 defer e.AssertExpectations(t) 136 137 p := MockExecutorProvider{} 138 defer p.AssertExpectations(t) 139 140 // Create executor only once 141 p.On("CanCreate").Return(true).Once() 142 p.On("GetDefaultShell").Return("bash").Once() 143 p.On("GetFeatures", mock.Anything).Return(nil).Twice() 144 p.On("Create").Return(&e).Once() 145 146 // Attempt to modify the Config object 147 e.On("Prepare", mock.Anything, mock.Anything, mock.Anything). 148 Return(func(options ExecutorPrepareOptions) error { 149 options.Config.Docker.DockerCredentials.Host = "10.0.0.2" 150 return nil 151 }).Once() 152 153 // We run everything else once 154 e.On("Finish", nil).Return().Once() 155 e.On("Cleanup").Return().Once() 156 157 // Run script successfully 158 e.On("Shell").Return(&ShellScriptInfo{Shell: "script-shell"}) 159 e.On("Run", matchBuildStage(BuildStagePrepare)).Return(nil).Once() 160 e.On("Run", matchBuildStage(BuildStageGetSources)).Return(nil).Once() 161 e.On("Run", matchBuildStage(BuildStageRestoreCache)).Return(nil).Once() 162 e.On("Run", matchBuildStage(BuildStageDownloadArtifacts)).Return(nil).Once() 163 e.On("Run", matchBuildStage(BuildStageUserScript)).Return(nil).Once() 164 e.On("Run", matchBuildStage(BuildStageAfterScript)).Return(nil).Once() 165 e.On("Run", matchBuildStage(BuildStageArchiveCache)).Return(nil).Once() 166 e.On("Run", matchBuildStage(BuildStageUploadOnSuccessArtifacts)).Return(nil).Once() 167 168 RegisterExecutor("build-run-nomodify-test", &p) 169 170 successfulBuild, err := GetSuccessfulBuild() 171 assert.NoError(t, err) 172 rc := &RunnerConfig{ 173 RunnerSettings: RunnerSettings{ 174 Executor: "build-run-nomodify-test", 175 Docker: &DockerConfig{ 176 DockerCredentials: docker_helpers.DockerCredentials{ 177 Host: "10.0.0.1", 178 }, 179 }, 180 }, 181 } 182 build, err := NewBuild(successfulBuild, rc, nil, nil) 183 assert.NoError(t, err) 184 185 err = build.Run(&Config{}, &Trace{Writer: os.Stdout}) 186 assert.NoError(t, err) 187 assert.Equal(t, "10.0.0.1", rc.Docker.DockerCredentials.Host) 188 } 189 190 func TestRetryPrepare(t *testing.T) { 191 PreparationRetryInterval = 0 192 193 e := MockExecutor{} 194 defer e.AssertExpectations(t) 195 196 p := MockExecutorProvider{} 197 defer p.AssertExpectations(t) 198 199 // Create executor 200 p.On("CanCreate").Return(true).Once() 201 p.On("GetDefaultShell").Return("bash").Once() 202 p.On("GetFeatures", mock.Anything).Return(nil).Twice() 203 204 p.On("Create").Return(&e).Times(3) 205 206 // Prepare plan 207 e.On("Prepare", mock.Anything, mock.Anything, mock.Anything). 208 Return(errors.New("prepare failed")).Twice() 209 e.On("Prepare", mock.Anything, mock.Anything, mock.Anything). 210 Return(nil).Once() 211 e.On("Cleanup").Return().Times(3) 212 213 // Succeed a build script 214 e.On("Shell").Return(&ShellScriptInfo{Shell: "script-shell"}) 215 e.On("Run", mock.Anything).Return(nil) 216 e.On("Finish", nil).Return().Once() 217 218 RegisterExecutor("build-run-retry-prepare", &p) 219 220 successfulBuild, err := GetSuccessfulBuild() 221 assert.NoError(t, err) 222 build := &Build{ 223 JobResponse: successfulBuild, 224 Runner: &RunnerConfig{ 225 RunnerSettings: RunnerSettings{ 226 Executor: "build-run-retry-prepare", 227 }, 228 }, 229 } 230 err = build.Run(&Config{}, &Trace{Writer: os.Stdout}) 231 assert.NoError(t, err) 232 } 233 234 func TestPrepareFailure(t *testing.T) { 235 PreparationRetryInterval = 0 236 237 e := MockExecutor{} 238 defer e.AssertExpectations(t) 239 240 p := MockExecutorProvider{} 241 defer p.AssertExpectations(t) 242 243 // Create executor 244 p.On("CanCreate").Return(true).Once() 245 p.On("GetDefaultShell").Return("bash").Once() 246 p.On("GetFeatures", mock.Anything).Return(nil).Twice() 247 248 p.On("Create").Return(&e).Times(3) 249 250 // Prepare plan 251 e.On("Prepare", mock.Anything, mock.Anything, mock.Anything). 252 Return(errors.New("prepare failed")).Times(3) 253 e.On("Cleanup").Return().Times(3) 254 255 RegisterExecutor("build-run-prepare-failure", &p) 256 257 successfulBuild, err := GetSuccessfulBuild() 258 assert.NoError(t, err) 259 build := &Build{ 260 JobResponse: successfulBuild, 261 Runner: &RunnerConfig{ 262 RunnerSettings: RunnerSettings{ 263 Executor: "build-run-prepare-failure", 264 }, 265 }, 266 } 267 err = build.Run(&Config{}, &Trace{Writer: os.Stdout}) 268 assert.EqualError(t, err, "prepare failed") 269 } 270 271 func TestPrepareFailureOnBuildError(t *testing.T) { 272 e := MockExecutor{} 273 defer e.AssertExpectations(t) 274 275 p := MockExecutorProvider{} 276 defer p.AssertExpectations(t) 277 278 // Create executor 279 p.On("CanCreate").Return(true).Once() 280 p.On("GetDefaultShell").Return("bash").Once() 281 p.On("GetFeatures", mock.Anything).Return(nil).Twice() 282 283 p.On("Create").Return(&e).Times(1) 284 285 // Prepare plan 286 e.On("Prepare", mock.Anything, mock.Anything, mock.Anything). 287 Return(&BuildError{}).Times(1) 288 e.On("Cleanup").Return().Times(1) 289 290 RegisterExecutor("build-run-prepare-failure-on-build-error", &p) 291 292 successfulBuild, err := GetSuccessfulBuild() 293 assert.NoError(t, err) 294 build := &Build{ 295 JobResponse: successfulBuild, 296 Runner: &RunnerConfig{ 297 RunnerSettings: RunnerSettings{ 298 Executor: "build-run-prepare-failure-on-build-error", 299 }, 300 }, 301 } 302 err = build.Run(&Config{}, &Trace{Writer: os.Stdout}) 303 assert.IsType(t, err, &BuildError{}) 304 } 305 306 func TestJobFailure(t *testing.T) { 307 e := new(MockExecutor) 308 defer e.AssertExpectations(t) 309 310 p := new(MockExecutorProvider) 311 defer p.AssertExpectations(t) 312 313 // Create executor 314 p.On("CanCreate").Return(true).Once() 315 p.On("GetDefaultShell").Return("bash").Once() 316 p.On("GetFeatures", mock.Anything).Return(nil).Twice() 317 318 p.On("Create").Return(e).Times(1) 319 320 // Prepare plan 321 e.On("Prepare", mock.Anything, mock.Anything, mock.Anything). 322 Return(nil).Times(1) 323 e.On("Cleanup").Return().Times(1) 324 325 // Succeed a build script 326 thrownErr := &BuildError{Inner: errors.New("test error")} 327 e.On("Shell").Return(&ShellScriptInfo{Shell: "script-shell"}) 328 e.On("Run", mock.Anything).Return(thrownErr) 329 e.On("Finish", thrownErr).Return().Once() 330 331 RegisterExecutor("build-run-job-failure", p) 332 333 failedBuild, err := GetFailedBuild() 334 assert.NoError(t, err) 335 build := &Build{ 336 JobResponse: failedBuild, 337 Runner: &RunnerConfig{ 338 RunnerSettings: RunnerSettings{ 339 Executor: "build-run-job-failure", 340 }, 341 }, 342 } 343 344 trace := new(MockJobTrace) 345 defer trace.AssertExpectations(t) 346 trace.On("Write", mock.Anything).Return(0, nil) 347 trace.On("IsStdout").Return(true) 348 trace.On("SetCancelFunc", mock.Anything).Once() 349 trace.On("SetMasked", mock.Anything).Once() 350 trace.On("Fail", thrownErr, ScriptFailure).Once() 351 352 err = build.Run(&Config{}, trace) 353 require.IsType(t, &BuildError{}, err) 354 } 355 356 func TestJobFailureOnExecutionTimeout(t *testing.T) { 357 e := new(MockExecutor) 358 defer e.AssertExpectations(t) 359 360 p := new(MockExecutorProvider) 361 defer p.AssertExpectations(t) 362 363 // Create executor 364 p.On("CanCreate").Return(true).Once() 365 p.On("GetDefaultShell").Return("bash").Once() 366 p.On("GetFeatures", mock.Anything).Return(nil).Twice() 367 368 p.On("Create").Return(e).Times(1) 369 370 // Prepare plan 371 e.On("Prepare", mock.Anything, mock.Anything, mock.Anything). 372 Return(nil).Times(1) 373 e.On("Cleanup").Return().Times(1) 374 375 // Succeed a build script 376 e.On("Shell").Return(&ShellScriptInfo{Shell: "script-shell"}) 377 e.On("Run", matchBuildStage(BuildStageUserScript)).Run(func(arguments mock.Arguments) { 378 time.Sleep(2 * time.Second) 379 }).Return(nil) 380 e.On("Run", mock.Anything).Return(nil) 381 e.On("Finish", mock.Anything).Return().Once() 382 383 RegisterExecutor("build-run-job-failure-on-execution-timeout", p) 384 385 successfulBuild, err := GetSuccessfulBuild() 386 assert.NoError(t, err) 387 388 successfulBuild.RunnerInfo.Timeout = 1 389 390 build := &Build{ 391 JobResponse: successfulBuild, 392 Runner: &RunnerConfig{ 393 RunnerSettings: RunnerSettings{ 394 Executor: "build-run-job-failure-on-execution-timeout", 395 }, 396 }, 397 } 398 399 trace := new(MockJobTrace) 400 defer trace.AssertExpectations(t) 401 trace.On("Write", mock.Anything).Return(0, nil) 402 trace.On("IsStdout").Return(true) 403 trace.On("SetCancelFunc", mock.Anything).Once() 404 trace.On("SetMasked", mock.Anything).Once() 405 trace.On("Fail", mock.Anything, JobExecutionTimeout).Run(func(arguments mock.Arguments) { 406 assert.Error(t, arguments.Get(0).(error)) 407 }).Once() 408 409 err = build.Run(&Config{}, trace) 410 require.IsType(t, &BuildError{}, err) 411 } 412 413 func matchBuildStage(buildStage BuildStage) interface{} { 414 return mock.MatchedBy(func(cmd ExecutorCommand) bool { 415 return cmd.Stage == buildStage 416 }) 417 } 418 419 func TestRunFailureRunsAfterScriptAndArtifactsOnFailure(t *testing.T) { 420 e := MockExecutor{} 421 defer e.AssertExpectations(t) 422 423 p := MockExecutorProvider{} 424 defer p.AssertExpectations(t) 425 426 // Create executor 427 p.On("CanCreate").Return(true).Once() 428 p.On("GetDefaultShell").Return("bash").Once() 429 p.On("GetFeatures", mock.Anything).Return(nil).Twice() 430 431 p.On("Create").Return(&e).Once() 432 433 // Prepare plan 434 e.On("Prepare", mock.Anything, mock.Anything, mock.Anything).Return(nil) 435 e.On("Cleanup").Return().Once() 436 437 // Fail a build script 438 e.On("Shell").Return(&ShellScriptInfo{Shell: "script-shell"}) 439 e.On("Run", matchBuildStage(BuildStagePrepare)).Return(nil).Once() 440 e.On("Run", matchBuildStage(BuildStageGetSources)).Return(nil).Once() 441 e.On("Run", matchBuildStage(BuildStageRestoreCache)).Return(nil).Once() 442 e.On("Run", matchBuildStage(BuildStageDownloadArtifacts)).Return(nil).Once() 443 e.On("Run", matchBuildStage(BuildStageUserScript)).Return(errors.New("build fail")).Once() 444 e.On("Run", matchBuildStage(BuildStageAfterScript)).Return(nil).Once() 445 e.On("Run", matchBuildStage(BuildStageUploadOnFailureArtifacts)).Return(nil).Once() 446 e.On("Finish", errors.New("build fail")).Return().Once() 447 448 RegisterExecutor("build-run-run-failure", &p) 449 450 failedBuild, err := GetFailedBuild() 451 assert.NoError(t, err) 452 build := &Build{ 453 JobResponse: failedBuild, 454 Runner: &RunnerConfig{ 455 RunnerSettings: RunnerSettings{ 456 Executor: "build-run-run-failure", 457 }, 458 }, 459 } 460 err = build.Run(&Config{}, &Trace{Writer: os.Stdout}) 461 assert.EqualError(t, err, "build fail") 462 } 463 464 func TestGetSourcesRunFailure(t *testing.T) { 465 e := MockExecutor{} 466 defer e.AssertExpectations(t) 467 468 p := MockExecutorProvider{} 469 defer p.AssertExpectations(t) 470 471 // Create executor 472 p.On("CanCreate").Return(true).Once() 473 p.On("GetDefaultShell").Return("bash").Once() 474 p.On("GetFeatures", mock.Anything).Return(nil).Twice() 475 476 p.On("Create").Return(&e).Once() 477 478 // Prepare plan 479 e.On("Prepare", mock.Anything, mock.Anything, mock.Anything).Return(nil) 480 e.On("Cleanup").Return() 481 482 // Fail a build script 483 e.On("Shell").Return(&ShellScriptInfo{Shell: "script-shell"}) 484 e.On("Run", matchBuildStage(BuildStagePrepare)).Return(nil).Once() 485 e.On("Run", matchBuildStage(BuildStageGetSources)).Return(errors.New("build fail")).Times(3) 486 e.On("Run", matchBuildStage(BuildStageUploadOnFailureArtifacts)).Return(nil).Once() 487 e.On("Finish", errors.New("build fail")).Return().Once() 488 489 RegisterExecutor("build-get-sources-run-failure", &p) 490 491 successfulBuild, err := GetSuccessfulBuild() 492 assert.NoError(t, err) 493 build := &Build{ 494 JobResponse: successfulBuild, 495 Runner: &RunnerConfig{ 496 RunnerSettings: RunnerSettings{ 497 Executor: "build-get-sources-run-failure", 498 }, 499 }, 500 } 501 502 build.Variables = append(build.Variables, JobVariable{Key: "GET_SOURCES_ATTEMPTS", Value: "3"}) 503 err = build.Run(&Config{}, &Trace{Writer: os.Stdout}) 504 assert.EqualError(t, err, "build fail") 505 } 506 507 func TestArtifactDownloadRunFailure(t *testing.T) { 508 e := MockExecutor{} 509 defer e.AssertExpectations(t) 510 511 p := MockExecutorProvider{} 512 defer p.AssertExpectations(t) 513 514 // Create executor 515 p.On("CanCreate").Return(true).Once() 516 p.On("GetDefaultShell").Return("bash").Once() 517 p.On("GetFeatures", mock.Anything).Return(nil).Twice() 518 519 p.On("Create").Return(&e).Once() 520 521 // Prepare plan 522 e.On("Prepare", mock.Anything, mock.Anything, mock.Anything).Return(nil) 523 e.On("Cleanup").Return() 524 525 // Fail a build script 526 e.On("Shell").Return(&ShellScriptInfo{Shell: "script-shell"}) 527 e.On("Run", matchBuildStage(BuildStagePrepare)).Return(nil).Once() 528 e.On("Run", matchBuildStage(BuildStageGetSources)).Return(nil).Once() 529 e.On("Run", matchBuildStage(BuildStageRestoreCache)).Return(nil).Once() 530 e.On("Run", matchBuildStage(BuildStageDownloadArtifacts)).Return(errors.New("build fail")).Times(3) 531 e.On("Run", matchBuildStage(BuildStageUploadOnFailureArtifacts)).Return(nil).Once() 532 e.On("Finish", errors.New("build fail")).Return().Once() 533 534 RegisterExecutor("build-artifacts-run-failure", &p) 535 536 successfulBuild, err := GetSuccessfulBuild() 537 assert.NoError(t, err) 538 build := &Build{ 539 JobResponse: successfulBuild, 540 Runner: &RunnerConfig{ 541 RunnerSettings: RunnerSettings{ 542 Executor: "build-artifacts-run-failure", 543 }, 544 }, 545 } 546 547 build.Variables = append(build.Variables, JobVariable{Key: "ARTIFACT_DOWNLOAD_ATTEMPTS", Value: "3"}) 548 err = build.Run(&Config{}, &Trace{Writer: os.Stdout}) 549 assert.EqualError(t, err, "build fail") 550 } 551 552 func TestArtifactUploadRunFailure(t *testing.T) { 553 e := MockExecutor{} 554 defer e.AssertExpectations(t) 555 556 p := MockExecutorProvider{} 557 defer p.AssertExpectations(t) 558 559 // Create executor 560 p.On("CanCreate").Return(true).Once() 561 p.On("GetDefaultShell").Return("bash").Once() 562 p.On("GetFeatures", mock.Anything).Return(nil).Twice() 563 564 p.On("Create").Return(&e).Once() 565 566 // Prepare plan 567 e.On("Prepare", mock.Anything, mock.Anything, mock.Anything).Return(nil) 568 e.On("Cleanup").Return() 569 570 // Successful build script 571 e.On("Shell").Return(&ShellScriptInfo{Shell: "script-shell"}).Times(8) 572 e.On("Run", matchBuildStage(BuildStagePrepare)).Return(nil).Once() 573 e.On("Run", matchBuildStage(BuildStageGetSources)).Return(nil).Once() 574 e.On("Run", matchBuildStage(BuildStageRestoreCache)).Return(nil).Once() 575 e.On("Run", matchBuildStage(BuildStageDownloadArtifacts)).Return(nil).Once() 576 e.On("Run", matchBuildStage(BuildStageUserScript)).Return(nil).Once() 577 e.On("Run", matchBuildStage(BuildStageAfterScript)).Return(nil).Once() 578 e.On("Run", matchBuildStage(BuildStageArchiveCache)).Return(nil).Once() 579 e.On("Run", matchBuildStage(BuildStageUploadOnSuccessArtifacts)).Return(errors.New("upload fail")).Once() 580 e.On("Finish", errors.New("upload fail")).Return().Once() 581 582 RegisterExecutor("build-upload-artifacts-run-failure", &p) 583 584 successfulBuild, err := GetSuccessfulBuild() 585 successfulBuild.Artifacts = make(Artifacts, 1) 586 successfulBuild.Artifacts[0] = Artifact{ 587 Name: "my-artifact", 588 Untracked: false, 589 Paths: ArtifactPaths{"cached/*"}, 590 When: ArtifactWhenAlways, 591 } 592 assert.NoError(t, err) 593 build := &Build{ 594 JobResponse: successfulBuild, 595 Runner: &RunnerConfig{ 596 RunnerSettings: RunnerSettings{ 597 Executor: "build-upload-artifacts-run-failure", 598 }, 599 }, 600 } 601 602 err = build.Run(&Config{}, &Trace{Writer: os.Stdout}) 603 assert.EqualError(t, err, "upload fail") 604 } 605 606 func TestRestoreCacheRunFailure(t *testing.T) { 607 e := MockExecutor{} 608 defer e.AssertExpectations(t) 609 610 p := MockExecutorProvider{} 611 defer p.AssertExpectations(t) 612 613 // Create executor 614 p.On("CanCreate").Return(true).Once() 615 p.On("GetDefaultShell").Return("bash").Once() 616 p.On("GetFeatures", mock.Anything).Return(nil).Twice() 617 618 p.On("Create").Return(&e).Once() 619 620 // Prepare plan 621 e.On("Prepare", mock.Anything, mock.Anything, mock.Anything).Return(nil) 622 e.On("Cleanup").Return() 623 624 // Fail a build script 625 e.On("Shell").Return(&ShellScriptInfo{Shell: "script-shell"}) 626 e.On("Run", matchBuildStage(BuildStagePrepare)).Return(nil).Once() 627 e.On("Run", matchBuildStage(BuildStageGetSources)).Return(nil).Once() 628 e.On("Run", matchBuildStage(BuildStageRestoreCache)).Return(errors.New("build fail")).Times(3) 629 e.On("Run", matchBuildStage(BuildStageUploadOnFailureArtifacts)).Return(nil).Once() 630 e.On("Finish", errors.New("build fail")).Return().Once() 631 632 RegisterExecutor("build-cache-run-failure", &p) 633 634 successfulBuild, err := GetSuccessfulBuild() 635 assert.NoError(t, err) 636 build := &Build{ 637 JobResponse: successfulBuild, 638 Runner: &RunnerConfig{ 639 RunnerSettings: RunnerSettings{ 640 Executor: "build-cache-run-failure", 641 }, 642 }, 643 } 644 645 build.Variables = append(build.Variables, JobVariable{Key: "RESTORE_CACHE_ATTEMPTS", Value: "3"}) 646 err = build.Run(&Config{}, &Trace{Writer: os.Stdout}) 647 assert.EqualError(t, err, "build fail") 648 } 649 650 func TestRunWrongAttempts(t *testing.T) { 651 e := MockExecutor{} 652 653 p := MockExecutorProvider{} 654 defer p.AssertExpectations(t) 655 656 // Create executor 657 p.On("CanCreate").Return(true).Once() 658 p.On("GetDefaultShell").Return("bash").Once() 659 p.On("GetFeatures", mock.Anything).Return(nil).Twice() 660 661 p.On("Create").Return(&e) 662 663 // Prepare plan 664 e.On("Prepare", mock.Anything, mock.Anything, mock.Anything).Return(nil) 665 e.On("Cleanup").Return() 666 667 // Fail a build script 668 e.On("Shell").Return(&ShellScriptInfo{Shell: "script-shell"}) 669 e.On("Run", mock.Anything).Return(nil).Once() 670 e.On("Run", mock.Anything).Return(errors.New("Number of attempts out of the range [1, 10] for stage: get_sources")) 671 e.On("Finish", errors.New("Number of attempts out of the range [1, 10] for stage: get_sources")).Return() 672 673 RegisterExecutor("build-run-attempt-failure", &p) 674 675 successfulBuild, err := GetSuccessfulBuild() 676 assert.NoError(t, err) 677 build := &Build{ 678 JobResponse: successfulBuild, 679 Runner: &RunnerConfig{ 680 RunnerSettings: RunnerSettings{ 681 Executor: "build-run-attempt-failure", 682 }, 683 }, 684 } 685 686 build.Variables = append(build.Variables, JobVariable{Key: "GET_SOURCES_ATTEMPTS", Value: "0"}) 687 err = build.Run(&Config{}, &Trace{Writer: os.Stdout}) 688 assert.EqualError(t, err, "Number of attempts out of the range [1, 10] for stage: get_sources") 689 } 690 691 func TestRunSuccessOnSecondAttempt(t *testing.T) { 692 e := MockExecutor{} 693 p := MockExecutorProvider{} 694 695 // Create executor only once 696 p.On("CanCreate").Return(true).Once() 697 p.On("GetDefaultShell").Return("bash").Once() 698 p.On("GetFeatures", mock.Anything).Return(nil).Twice() 699 700 p.On("Create").Return(&e).Once() 701 702 // We run everything once 703 e.On("Prepare", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() 704 e.On("Finish", mock.Anything).Return().Twice() 705 e.On("Cleanup").Return().Twice() 706 707 // Run script successfully 708 e.On("Shell").Return(&ShellScriptInfo{Shell: "script-shell"}) 709 710 e.On("Run", mock.Anything).Return(nil) 711 e.On("Run", mock.Anything).Return(errors.New("build fail")).Once() 712 e.On("Run", mock.Anything).Return(nil) 713 714 RegisterExecutor("build-run-success-second-attempt", &p) 715 716 successfulBuild, err := GetSuccessfulBuild() 717 assert.NoError(t, err) 718 build := &Build{ 719 JobResponse: successfulBuild, 720 Runner: &RunnerConfig{ 721 RunnerSettings: RunnerSettings{ 722 Executor: "build-run-success-second-attempt", 723 }, 724 }, 725 } 726 727 build.Variables = append(build.Variables, JobVariable{Key: "GET_SOURCES_ATTEMPTS", Value: "3"}) 728 err = build.Run(&Config{}, &Trace{Writer: os.Stdout}) 729 assert.NoError(t, err) 730 } 731 732 func TestDebugTrace(t *testing.T) { 733 testCases := map[string]struct { 734 debugTraceVariableValue string 735 expectedValue bool 736 debugTraceFeatureDisabled bool 737 expectedLogOutput string 738 }{ 739 "variable not set": { 740 expectedValue: false, 741 }, 742 "variable set to false": { 743 debugTraceVariableValue: "false", 744 expectedValue: false, 745 }, 746 "variable set to true": { 747 debugTraceVariableValue: "true", 748 expectedValue: true, 749 }, 750 "variable set to a non-bool value": { 751 debugTraceVariableValue: "xyz", 752 expectedValue: false, 753 }, 754 "variable set to true and feature disabled from configuration": { 755 debugTraceVariableValue: "true", 756 expectedValue: false, 757 debugTraceFeatureDisabled: true, 758 expectedLogOutput: "CI_DEBUG_TRACE usage is disabled on this Runner", 759 }, 760 } 761 762 for testName, testCase := range testCases { 763 t.Run(testName, func(t *testing.T) { 764 logger, hooks := test.NewNullLogger() 765 766 build := &Build{ 767 logger: NewBuildLogger(nil, logrus.NewEntry(logger)), 768 JobResponse: JobResponse{ 769 Variables: JobVariables{}, 770 }, 771 Runner: &RunnerConfig{ 772 RunnerSettings: RunnerSettings{ 773 DebugTraceDisabled: testCase.debugTraceFeatureDisabled, 774 }, 775 }, 776 } 777 778 if testCase.debugTraceVariableValue != "" { 779 build.Variables = append(build.Variables, JobVariable{Key: "CI_DEBUG_TRACE", Value: testCase.debugTraceVariableValue, Public: true}) 780 } 781 782 isTraceEnabled := build.IsDebugTraceEnabled() 783 assert.Equal(t, testCase.expectedValue, isTraceEnabled) 784 785 if testCase.expectedLogOutput != "" { 786 output, err := hooks.LastEntry().String() 787 require.NoError(t, err) 788 assert.Contains(t, output, testCase.expectedLogOutput) 789 } 790 }) 791 } 792 } 793 794 func TestDefaultEnvVariables(t *testing.T) { 795 buildDir := "/tmp/test-build/dir" 796 build := Build{ 797 BuildDir: buildDir, 798 } 799 800 vars := build.GetAllVariables().StringList() 801 802 assert.Contains(t, vars, "CI_PROJECT_DIR="+filepath.FromSlash(buildDir)) 803 assert.Contains(t, vars, "CI_SERVER=yes") 804 } 805 806 func TestSharedEnvVariables(t *testing.T) { 807 for _, shared := range [...]bool{true, false} { 808 t.Run(fmt.Sprintf("Value:%v", shared), func(t *testing.T) { 809 assert := assert.New(t) 810 build := Build{ 811 ExecutorFeatures: FeaturesInfo{Shared: shared}, 812 } 813 vars := build.GetAllVariables().StringList() 814 815 assert.NotNil(vars) 816 817 present := "CI_SHARED_ENVIRONMENT=true" 818 absent := "CI_DISPOSABLE_ENVIRONMENT=true" 819 if !shared { 820 tmp := present 821 present = absent 822 absent = tmp 823 } 824 825 assert.Contains(vars, present) 826 assert.NotContains(vars, absent) 827 // we never expose false 828 assert.NotContains(vars, "CI_SHARED_ENVIRONMENT=false") 829 assert.NotContains(vars, "CI_DISPOSABLE_ENVIRONMENT=false") 830 }) 831 } 832 } 833 834 func TestGetRemoteURL(t *testing.T) { 835 testCases := []struct { 836 runner RunnerSettings 837 result string 838 }{ 839 { 840 runner: RunnerSettings{ 841 CloneURL: "http://test.local/", 842 }, 843 result: "http://gitlab-ci-token:1234567@test.local/h5bp/html5-boilerplate.git", 844 }, 845 { 846 runner: RunnerSettings{ 847 CloneURL: "https://test.local", 848 }, 849 result: "https://gitlab-ci-token:1234567@test.local/h5bp/html5-boilerplate.git", 850 }, 851 { 852 runner: RunnerSettings{}, 853 result: "http://fallback.url", 854 }, 855 } 856 857 for _, tc := range testCases { 858 build := &Build{ 859 Runner: &RunnerConfig{ 860 RunnerSettings: tc.runner, 861 }, 862 allVariables: JobVariables{ 863 JobVariable{Key: "CI_JOB_TOKEN", Value: "1234567"}, 864 JobVariable{Key: "CI_PROJECT_PATH", Value: "h5bp/html5-boilerplate"}, 865 }, 866 JobResponse: JobResponse{ 867 GitInfo: GitInfo{RepoURL: "http://fallback.url"}, 868 }, 869 } 870 871 assert.Equal(t, tc.result, build.GetRemoteURL()) 872 } 873 } 874 875 type featureFlagOnTestCase struct { 876 value string 877 expectedStatus bool 878 expectedError bool 879 } 880 881 func TestIsFeatureFlagOn(t *testing.T) { 882 hook := test.NewGlobal() 883 884 tests := map[string]featureFlagOnTestCase{ 885 "no value": { 886 value: "", 887 expectedStatus: false, 888 expectedError: false, 889 }, 890 "true": { 891 value: "true", 892 expectedStatus: true, 893 expectedError: false, 894 }, 895 "1": { 896 value: "1", 897 expectedStatus: true, 898 expectedError: false, 899 }, 900 "false": { 901 value: "false", 902 expectedStatus: false, 903 expectedError: false, 904 }, 905 "0": { 906 value: "0", 907 expectedStatus: false, 908 expectedError: false, 909 }, 910 "invalid value": { 911 value: "test", 912 expectedStatus: false, 913 expectedError: true, 914 }, 915 } 916 917 for name, testCase := range tests { 918 t.Run(name, func(t *testing.T) { 919 build := new(Build) 920 build.Variables = JobVariables{ 921 {Key: "FF_TEST_FEATURE", Value: testCase.value}, 922 } 923 924 status := build.IsFeatureFlagOn("FF_TEST_FEATURE") 925 assert.Equal(t, testCase.expectedStatus, status) 926 927 entry := hook.LastEntry() 928 if testCase.expectedError { 929 require.NotNil(t, entry) 930 931 logrusOutput, err := entry.String() 932 require.NoError(t, err) 933 934 assert.Contains(t, logrusOutput, "Error while parsing the value of feature flag") 935 } else { 936 assert.Nil(t, entry) 937 } 938 939 hook.Reset() 940 }) 941 } 942 } 943 944 func TestAllowToOverwriteFeatureFlagWithRunnerVariables(t *testing.T) { 945 tests := map[string]struct { 946 variable string 947 expectedValue bool 948 }{ 949 "it has default value of FF": { 950 variable: "", 951 expectedValue: true, 952 }, 953 "it enables FF": { 954 variable: "FF_K8S_USE_ENTRYPOINT_OVER_COMMAND=true", 955 expectedValue: true, 956 }, 957 "it disable FF": { 958 variable: "FF_K8S_USE_ENTRYPOINT_OVER_COMMAND=false", 959 expectedValue: false, 960 }, 961 } 962 963 for name, test := range tests { 964 t.Run(name, func(t *testing.T) { 965 build := new(Build) 966 build.Runner = &RunnerConfig{ 967 RunnerSettings: RunnerSettings{ 968 Environment: []string{test.variable}, 969 }, 970 } 971 972 result := build.IsFeatureFlagOn("FF_K8S_USE_ENTRYPOINT_OVER_COMMAND") 973 assert.Equal(t, test.expectedValue, result) 974 }) 975 } 976 } 977 978 func TestStartBuild(t *testing.T) { 979 type startBuildArgs struct { 980 rootDir string 981 cacheDir string 982 customBuildDirEnabled bool 983 sharedDir bool 984 } 985 986 tests := map[string]struct { 987 args startBuildArgs 988 jobVariables JobVariables 989 expectedBuildDir string 990 expectedCacheDir string 991 expectedError bool 992 }{ 993 "no job specific build dir with no shared dir": { 994 args: startBuildArgs{ 995 rootDir: "/build", 996 cacheDir: "/cache", 997 customBuildDirEnabled: true, 998 sharedDir: false, 999 }, 1000 jobVariables: JobVariables{}, 1001 expectedBuildDir: "/build/test-namespace/test-repo", 1002 expectedCacheDir: "/cache/test-namespace/test-repo", 1003 expectedError: false, 1004 }, 1005 "no job specified build dir with shared dir": { 1006 args: startBuildArgs{ 1007 rootDir: "/builds", 1008 cacheDir: "/cache", 1009 customBuildDirEnabled: true, 1010 sharedDir: true, 1011 }, 1012 jobVariables: JobVariables{}, 1013 expectedBuildDir: "/builds/1234/0/test-namespace/test-repo", 1014 expectedCacheDir: "/cache/test-namespace/test-repo", 1015 expectedError: false, 1016 }, 1017 "valid GIT_CLONE_PATH was specified": { 1018 args: startBuildArgs{ 1019 rootDir: "/builds", 1020 cacheDir: "/cache", 1021 customBuildDirEnabled: true, 1022 sharedDir: false, 1023 }, 1024 jobVariables: JobVariables{ 1025 {Key: "GIT_CLONE_PATH", Value: "/builds/go/src/gitlab.com/test-namespace/test-repo", Public: true}, 1026 }, 1027 expectedBuildDir: "/builds/go/src/gitlab.com/test-namespace/test-repo", 1028 expectedCacheDir: "/cache/test-namespace/test-repo", 1029 expectedError: false, 1030 }, 1031 "valid GIT_CLONE_PATH using CI_BUILDS_DIR was specified": { 1032 args: startBuildArgs{ 1033 rootDir: "/builds", 1034 cacheDir: "/cache", 1035 customBuildDirEnabled: true, 1036 sharedDir: false, 1037 }, 1038 jobVariables: JobVariables{ 1039 {Key: "GIT_CLONE_PATH", Value: "$CI_BUILDS_DIR/go/src/gitlab.com/test-namespace/test-repo", Public: true}, 1040 }, 1041 expectedBuildDir: "/builds/go/src/gitlab.com/test-namespace/test-repo", 1042 expectedCacheDir: "/cache/test-namespace/test-repo", 1043 expectedError: false, 1044 }, 1045 "custom build disabled": { 1046 args: startBuildArgs{ 1047 rootDir: "/builds", 1048 cacheDir: "/cache", 1049 customBuildDirEnabled: false, 1050 sharedDir: false, 1051 }, 1052 jobVariables: JobVariables{ 1053 {Key: "GIT_CLONE_PATH", Value: "/builds/go/src/gitlab.com/test-namespace/test-repo", Public: true}, 1054 }, 1055 expectedBuildDir: "/builds/test-namespace/test-repo", 1056 expectedCacheDir: "/cache/test-namespace/test-repo", 1057 expectedError: true, 1058 }, 1059 "invalid GIT_CLONE_PATH was specified": { 1060 args: startBuildArgs{ 1061 rootDir: "/builds", 1062 cacheDir: "/cache", 1063 customBuildDirEnabled: true, 1064 sharedDir: false, 1065 }, 1066 jobVariables: JobVariables{ 1067 {Key: "GIT_CLONE_PATH", Value: "/go/src/gitlab.com/test-namespace/test-repo", Public: true}, 1068 }, 1069 expectedError: true, 1070 }, 1071 } 1072 1073 for name, test := range tests { 1074 t.Run(name, func(t *testing.T) { 1075 build := Build{ 1076 JobResponse: JobResponse{ 1077 GitInfo: GitInfo{ 1078 RepoURL: "https://gitlab.com/test-namespace/test-repo.git", 1079 }, 1080 Variables: test.jobVariables, 1081 }, 1082 Runner: &RunnerConfig{ 1083 RunnerCredentials: RunnerCredentials{ 1084 Token: "1234", 1085 }, 1086 }, 1087 } 1088 1089 err := build.StartBuild(test.args.rootDir, test.args.cacheDir, test.args.customBuildDirEnabled, test.args.sharedDir) 1090 if test.expectedError { 1091 assert.Error(t, err) 1092 return 1093 } 1094 1095 assert.NoError(t, err) 1096 assert.Equal(t, test.expectedBuildDir, build.BuildDir) 1097 assert.Equal(t, test.args.rootDir, build.RootDir) 1098 assert.Equal(t, test.expectedCacheDir, build.CacheDir) 1099 }) 1100 } 1101 } 1102 1103 func TestWaitForTerminal(t *testing.T) { 1104 cases := []struct { 1105 name string 1106 cancelFn func(ctxCancel context.CancelFunc, build *Build) 1107 jobTimeout int 1108 waitForTerminalTimeout time.Duration 1109 expectedErr string 1110 }{ 1111 { 1112 name: "Cancel build", 1113 cancelFn: func(ctxCancel context.CancelFunc, build *Build) { 1114 ctxCancel() 1115 }, 1116 jobTimeout: 3600, 1117 waitForTerminalTimeout: time.Hour, 1118 expectedErr: "build cancelled, killing session", 1119 }, 1120 { 1121 name: "Terminal Timeout", 1122 cancelFn: func(ctxCancel context.CancelFunc, build *Build) { 1123 // noop 1124 }, 1125 jobTimeout: 3600, 1126 waitForTerminalTimeout: time.Second, 1127 expectedErr: "Terminal session timed out (maximum time allowed - 1s)", 1128 }, 1129 { 1130 name: "System Interrupt", 1131 cancelFn: func(ctxCancel context.CancelFunc, build *Build) { 1132 build.SystemInterrupt <- os.Interrupt 1133 }, 1134 jobTimeout: 3600, 1135 waitForTerminalTimeout: time.Hour, 1136 expectedErr: "terminal disconnected by system signal: interrupt", 1137 }, 1138 { 1139 name: "Terminal Disconnect", 1140 cancelFn: func(ctxCancel context.CancelFunc, build *Build) { 1141 build.Session.DisconnectCh <- errors.New("user disconnect") 1142 }, 1143 jobTimeout: 3600, 1144 waitForTerminalTimeout: time.Hour, 1145 expectedErr: "terminal disconnected: user disconnect", 1146 }, 1147 } 1148 1149 for _, c := range cases { 1150 t.Run(c.name, func(t *testing.T) { 1151 build := Build{ 1152 Runner: &RunnerConfig{ 1153 RunnerSettings: RunnerSettings{ 1154 Executor: "shell", 1155 }, 1156 }, 1157 JobResponse: JobResponse{ 1158 RunnerInfo: RunnerInfo{ 1159 Timeout: c.jobTimeout, 1160 }, 1161 }, 1162 SystemInterrupt: make(chan os.Signal), 1163 } 1164 1165 trace := Trace{Writer: os.Stdout} 1166 build.logger = NewBuildLogger(&trace, build.Log()) 1167 sess, err := session.NewSession(nil) 1168 require.NoError(t, err) 1169 build.Session = sess 1170 1171 srv := httptest.NewServer(build.Session.Mux()) 1172 defer srv.Close() 1173 1174 mockConn := terminal.MockConn{} 1175 defer mockConn.AssertExpectations(t) 1176 mockConn.On("Close").Maybe().Return(nil) 1177 // On Start upgrade the web socket connection and wait for the 1178 // timeoutCh to exit, to mock real work made on the websocket. 1179 mockConn.On("Start", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { 1180 upgrader := &websocket.Upgrader{} 1181 r := args[1].(*http.Request) 1182 w := args[0].(http.ResponseWriter) 1183 1184 _, _ = upgrader.Upgrade(w, r, nil) 1185 timeoutCh := args[2].(chan error) 1186 1187 <-timeoutCh 1188 }).Once() 1189 1190 mockTerminal := terminal.MockInteractiveTerminal{} 1191 defer mockTerminal.AssertExpectations(t) 1192 mockTerminal.On("Connect").Return(&mockConn, nil) 1193 sess.SetInteractiveTerminal(&mockTerminal) 1194 1195 u := url.URL{ 1196 Scheme: "ws", 1197 Host: srv.Listener.Addr().String(), 1198 Path: build.Session.Endpoint + "/exec", 1199 } 1200 headers := http.Header{ 1201 "Authorization": []string{build.Session.Token}, 1202 } 1203 1204 conn, _, err := websocket.DefaultDialer.Dial(u.String(), headers) 1205 require.NotNil(t, conn) 1206 require.NoError(t, err) 1207 defer conn.Close() 1208 1209 ctx, cancel := context.WithTimeout(context.Background(), build.GetBuildTimeout()) 1210 1211 errCh := make(chan error) 1212 go func() { 1213 errCh <- build.waitForTerminal(ctx, c.waitForTerminalTimeout) 1214 }() 1215 1216 c.cancelFn(cancel, &build) 1217 1218 assert.EqualError(t, <-errCh, c.expectedErr) 1219 }) 1220 } 1221 } 1222 1223 func TestBuild_IsLFSSmudgeDisabled(t *testing.T) { 1224 testCases := map[string]struct { 1225 isVariableUnset bool 1226 variableValue string 1227 expectedResult bool 1228 }{ 1229 "variable not set": { 1230 isVariableUnset: true, 1231 expectedResult: false, 1232 }, 1233 "variable empty": { 1234 variableValue: "", 1235 expectedResult: false, 1236 }, 1237 "variable set to true": { 1238 variableValue: "true", 1239 expectedResult: true, 1240 }, 1241 "variable set to false": { 1242 variableValue: "false", 1243 expectedResult: false, 1244 }, 1245 "variable set to 1": { 1246 variableValue: "1", 1247 expectedResult: true, 1248 }, 1249 "variable set to 0": { 1250 variableValue: "0", 1251 expectedResult: false, 1252 }, 1253 } 1254 1255 for testName, testCase := range testCases { 1256 t.Run(testName, func(t *testing.T) { 1257 b := &Build{ 1258 JobResponse: JobResponse{ 1259 Variables: JobVariables{}, 1260 }, 1261 } 1262 1263 if !testCase.isVariableUnset { 1264 b.Variables = append(b.Variables, JobVariable{Key: "GIT_LFS_SKIP_SMUDGE", Value: testCase.variableValue, Public: true}) 1265 } 1266 1267 assert.Equal(t, testCase.expectedResult, b.IsLFSSmudgeDisabled()) 1268 }) 1269 } 1270 } 1271 1272 func TestGitCleanFlags(t *testing.T) { 1273 tests := map[string]struct { 1274 value string 1275 expectedResult []string 1276 }{ 1277 "empty clean flags": { 1278 value: "", 1279 expectedResult: []string{"-ffdx"}, 1280 }, 1281 "use custom flags": { 1282 value: "custom-flags", 1283 expectedResult: []string{"custom-flags"}, 1284 }, 1285 "use custom flags with multiple arguments": { 1286 value: "-ffdx -e cache/", 1287 expectedResult: []string{"-ffdx", "-e", "cache/"}, 1288 }, 1289 "disabled": { 1290 value: "none", 1291 expectedResult: []string{}, 1292 }, 1293 } 1294 1295 for name, test := range tests { 1296 t.Run(name, func(t *testing.T) { 1297 build := &Build{ 1298 Runner: &RunnerConfig{}, 1299 JobResponse: JobResponse{ 1300 Variables: JobVariables{ 1301 {Key: "GIT_CLEAN_FLAGS", Value: test.value}, 1302 }, 1303 }, 1304 } 1305 1306 result := build.GetGitCleanFlags() 1307 assert.Equal(t, test.expectedResult, result) 1308 }) 1309 } 1310 } 1311 1312 func TestDefaultVariables(t *testing.T) { 1313 tests := map[string]struct { 1314 jobVariables JobVariables 1315 rootDir string 1316 key string 1317 expectedValue string 1318 }{ 1319 "get default CI_SERVER value": { 1320 jobVariables: JobVariables{}, 1321 rootDir: "/builds", 1322 key: "CI_SERVER", 1323 expectedValue: "yes", 1324 }, 1325 "get default CI_PROJECT_DIR value": { 1326 jobVariables: JobVariables{}, 1327 rootDir: "/builds", 1328 key: "CI_PROJECT_DIR", 1329 expectedValue: "/builds/test-namespace/test-repo", 1330 }, 1331 "get overwritten CI_PROJECT_DIR value": { 1332 jobVariables: JobVariables{ 1333 {Key: "GIT_CLONE_PATH", Value: "/builds/go/src/gitlab.com/gitlab-org/gitlab-runner", Public: true}, 1334 }, 1335 rootDir: "/builds", 1336 key: "CI_PROJECT_DIR", 1337 expectedValue: "/builds/go/src/gitlab.com/gitlab-org/gitlab-runner", 1338 }, 1339 } 1340 1341 for name, test := range tests { 1342 t.Run(name, func(t *testing.T) { 1343 build := Build{ 1344 JobResponse: JobResponse{ 1345 GitInfo: GitInfo{ 1346 RepoURL: "https://gitlab.com/test-namespace/test-repo.git", 1347 }, 1348 Variables: test.jobVariables, 1349 }, 1350 Runner: &RunnerConfig{ 1351 RunnerCredentials: RunnerCredentials{ 1352 Token: "1234", 1353 }, 1354 }, 1355 } 1356 1357 err := build.StartBuild(test.rootDir, "/cache", true, false) 1358 assert.NoError(t, err) 1359 1360 variable := build.GetAllVariables().Get(test.key) 1361 assert.Equal(t, test.expectedValue, variable) 1362 }) 1363 } 1364 }