sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/config/jobs_test.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package config 18 19 import ( 20 "errors" 21 "reflect" 22 "testing" 23 24 pipelinev1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" 25 coreapi "k8s.io/api/core/v1" 26 prowapi "sigs.k8s.io/prow/pkg/apis/prowjobs/v1" 27 ) 28 29 func TestRunIfChangedPresubmits(t *testing.T) { 30 PresubmitsStatic := []Presubmit{ 31 { 32 JobBase: JobBase{ 33 Name: "cross build", 34 }, 35 RegexpChangeMatcher: RegexpChangeMatcher{ 36 RunIfChanged: `(Makefile|\.sh|_(windows|linux|osx|unknown)(_test)?\.go)$`, 37 }, 38 }, 39 } 40 SetPresubmitRegexes(PresubmitsStatic) 41 ps := PresubmitsStatic[0] 42 var testcases = []struct { 43 changes []string 44 expected bool 45 }{ 46 {[]string{"some random file"}, false}, 47 {[]string{"./pkg/util/rlimit/rlimit_linux.go"}, true}, 48 {[]string{"./pkg/util/rlimit/rlimit_unknown_test.go"}, true}, 49 {[]string{"build.sh"}, true}, 50 {[]string{"build.shoo"}, false}, 51 {[]string{"Makefile"}, true}, 52 } 53 for _, tc := range testcases { 54 actual := ps.RunsAgainstChanges(tc.changes) 55 if actual != tc.expected { 56 t.Errorf("wrong RunsAgainstChanges(%#v) result. Got %v, expected %v", tc.changes, actual, tc.expected) 57 } 58 } 59 } 60 61 // TestRunIfChangedPostsubmits is identical to TestRunIfChangedPresubmits. 62 func TestRunIfChangedPostsubmits(t *testing.T) { 63 PostsubmitsStatic := []Postsubmit{ 64 { 65 JobBase: JobBase{ 66 Name: "cross build", 67 }, 68 RegexpChangeMatcher: RegexpChangeMatcher{ 69 RunIfChanged: `(Makefile|\.sh|_(windows|linux|osx|unknown)(_test)?\.go)$`, 70 }, 71 }, 72 } 73 SetPostsubmitRegexes(PostsubmitsStatic) 74 ps := PostsubmitsStatic[0] 75 var testcases = []struct { 76 changes []string 77 expected bool 78 }{ 79 {[]string{"some random file"}, false}, 80 {[]string{"./pkg/util/rlimit/rlimit_linux.go"}, true}, 81 {[]string{"./pkg/util/rlimit/rlimit_unknown_test.go"}, true}, 82 {[]string{"build.sh"}, true}, 83 {[]string{"build.shoo"}, false}, 84 {[]string{"Makefile"}, true}, 85 } 86 for _, tc := range testcases { 87 actual := ps.RunsAgainstChanges(tc.changes) 88 if actual != tc.expected { 89 t.Errorf("wrong RunsAgainstChanges(%#v) result. Got %v, expected %v", tc.changes, actual, tc.expected) 90 } 91 } 92 } 93 94 func TestSkipIfOnlyChangedPresubmits(t *testing.T) { 95 PresubmitsStatic := []Presubmit{ 96 { 97 JobBase: JobBase{ 98 Name: "cross build", 99 }, 100 RegexpChangeMatcher: RegexpChangeMatcher{ 101 // Files satisfying any of: 102 // - in the top-level docs/ directory 103 // - with .md/.adoc extensions 104 // - with basename README or OWNERS 105 SkipIfOnlyChanged: `^docs/|\.(md|adoc)$|/?(README|OWNERS)$`, 106 }, 107 }, 108 } 109 SetPresubmitRegexes(PresubmitsStatic) 110 ps := PresubmitsStatic[0] 111 var testcases = []struct { 112 changes []string 113 expected bool 114 }{ 115 {[]string{"some random file"}, true}, 116 {[]string{"./pkg/util/rlimit/rlimit_linux.go"}, true}, 117 // Skips because in docs/, even though it's a go file. Caveat emptor. 118 {[]string{"docs/cobragen.go"}, false}, 119 // Our regex isn't expecting paths to start with ./ 120 {[]string{"./docs/cobragen.go"}, true}, 121 {[]string{"README", "README.md", "OWNERS", "path/to/something.adoc", "path/to/README"}, false}, 122 // Any non-matching file triggers the job 123 {[]string{"README", "README.md", "OWNERS", "path/to/something.adoc", "path/to/README", "foo"}, true}, 124 } 125 for _, tc := range testcases { 126 actual := ps.RunsAgainstChanges(tc.changes) 127 if actual != tc.expected { 128 t.Errorf("wrong RunsAgainstChanges(%#v) result. Got %v, expected %v", tc.changes, actual, tc.expected) 129 } 130 } 131 } 132 133 // TestSkipIfOnlyChangedPostsubmits is identical to TestSkipIfOnlyChangedPresubmits. 134 func TestSkipIfOnlyChangedPostsubmits(t *testing.T) { 135 PostsubmitsStatic := []Postsubmit{ 136 { 137 JobBase: JobBase{ 138 Name: "cross build", 139 }, 140 RegexpChangeMatcher: RegexpChangeMatcher{ 141 // Files satisfying any of: 142 // - in the top-level docs/ directory 143 // - with .md/.adoc extensions 144 // - with basename README or OWNERS 145 SkipIfOnlyChanged: `^docs/|\.(md|adoc)$|/?(README|OWNERS)$`, 146 }, 147 }, 148 } 149 SetPostsubmitRegexes(PostsubmitsStatic) 150 ps := PostsubmitsStatic[0] 151 var testcases = []struct { 152 changes []string 153 expected bool 154 }{ 155 {[]string{"some random file"}, true}, 156 {[]string{"./pkg/util/rlimit/rlimit_linux.go"}, true}, 157 // Skips because in docs/, even though it's a go file. Caveat emptor. 158 {[]string{"docs/cobragen.go"}, false}, 159 // Our regex isn't expecting paths to start with ./ 160 {[]string{"./docs/cobragen.go"}, true}, 161 {[]string{"README", "README.md", "OWNERS", "path/to/something.adoc", "path/to/README"}, false}, 162 // Any non-matching file triggers the job 163 {[]string{"README", "README.md", "OWNERS", "path/to/something.adoc", "path/to/README", "foo"}, true}, 164 } 165 for _, tc := range testcases { 166 actual := ps.RunsAgainstChanges(tc.changes) 167 if actual != tc.expected { 168 t.Errorf("wrong RunsAgainstChanges(%#v) result. Got %v, expected %v", tc.changes, actual, tc.expected) 169 } 170 } 171 } 172 173 func TestListPresubmit(t *testing.T) { 174 c := &Config{ 175 JobConfig: JobConfig{ 176 PresubmitsStatic: map[string][]Presubmit{ 177 "r1": { 178 { 179 JobBase: JobBase{ 180 Name: "a", 181 }, 182 }, 183 {JobBase: JobBase{Name: "b"}}, 184 }, 185 "r2": { 186 { 187 JobBase: JobBase{ 188 Name: "c", 189 }, 190 }, 191 {JobBase: JobBase{Name: "d"}}, 192 }, 193 }, 194 PostsubmitsStatic: map[string][]Postsubmit{ 195 "r1": {{JobBase: JobBase{Name: "e"}}}, 196 }, 197 Periodics: []Periodic{ 198 {JobBase: JobBase{Name: "f"}}, 199 }, 200 }, 201 } 202 203 var testcases = []struct { 204 name string 205 expected []string 206 repos []string 207 }{ 208 { 209 "all presubmits", 210 []string{"a", "b", "c", "d"}, 211 []string{}, 212 }, 213 { 214 "r2 presubmits", 215 []string{"c", "d"}, 216 []string{"r2"}, 217 }, 218 } 219 220 for _, tc := range testcases { 221 actual := c.AllStaticPresubmits(tc.repos) 222 if len(actual) != len(tc.expected) { 223 t.Fatalf("test %s - Wrong number of jobs. Got %v, expected %v", tc.name, actual, tc.expected) 224 } 225 for _, j1 := range tc.expected { 226 found := false 227 for _, j2 := range actual { 228 if j1 == j2.Name { 229 found = true 230 break 231 } 232 } 233 if !found { 234 t.Errorf("test %s - Did not find job %s in output", tc.name, j1) 235 } 236 } 237 } 238 } 239 240 func TestListPostsubmit(t *testing.T) { 241 c := &Config{ 242 JobConfig: JobConfig{ 243 PresubmitsStatic: map[string][]Presubmit{ 244 "r1": {{JobBase: JobBase{Name: "a"}}}, 245 }, 246 PostsubmitsStatic: map[string][]Postsubmit{ 247 "r1": { 248 { 249 JobBase: JobBase{ 250 Name: "c", 251 }, 252 }, 253 {JobBase: JobBase{Name: "d"}}, 254 }, 255 "r2": {{JobBase: JobBase{Name: "e"}}}, 256 }, 257 Periodics: []Periodic{ 258 {JobBase: JobBase{Name: "f"}}, 259 }, 260 }, 261 } 262 263 var testcases = []struct { 264 name string 265 expected []string 266 repos []string 267 }{ 268 { 269 "all postsubmits", 270 []string{"c", "d", "e"}, 271 []string{}, 272 }, 273 { 274 "r2 presubmits", 275 []string{"e"}, 276 []string{"r2"}, 277 }, 278 } 279 280 for _, tc := range testcases { 281 actual := c.AllStaticPostsubmits(tc.repos) 282 if len(actual) != len(tc.expected) { 283 t.Fatalf("%s - Wrong number of jobs. Got %v, expected %v", tc.name, actual, tc.expected) 284 } 285 for _, j1 := range tc.expected { 286 found := false 287 for _, j2 := range actual { 288 if j1 == j2.Name { 289 found = true 290 break 291 } 292 } 293 if !found { 294 t.Errorf("Did not find job %s in output", j1) 295 } 296 } 297 } 298 } 299 300 func TestListPeriodic(t *testing.T) { 301 c := &Config{ 302 JobConfig: JobConfig{ 303 PresubmitsStatic: map[string][]Presubmit{ 304 "r1": {{JobBase: JobBase{Name: "a"}}}, 305 }, 306 PostsubmitsStatic: map[string][]Postsubmit{ 307 "r1": {{JobBase: JobBase{Name: "b"}}}, 308 }, 309 Periodics: []Periodic{ 310 { 311 JobBase: JobBase{ 312 Name: "c", 313 }, 314 }, 315 {JobBase: JobBase{Name: "d"}}, 316 }, 317 }, 318 } 319 320 expected := []string{"c", "d"} 321 actual := c.AllPeriodics() 322 if len(actual) != len(expected) { 323 t.Fatalf("Wrong number of jobs. Got %v, expected %v", actual, expected) 324 } 325 for _, j1 := range expected { 326 found := false 327 for _, j2 := range actual { 328 if j1 == j2.Name { 329 found = true 330 break 331 } 332 } 333 if !found { 334 t.Errorf("Did not find job %s in output", j1) 335 } 336 } 337 } 338 339 func TestRunAgainstBranch(t *testing.T) { 340 jobs := []Presubmit{ 341 { 342 JobBase: JobBase{ 343 Name: "a", 344 }, 345 Brancher: Brancher{SkipBranches: []string{"s"}}, 346 }, 347 { 348 JobBase: JobBase{ 349 Name: "b", 350 }, 351 Brancher: Brancher{Branches: []string{"r"}}, 352 }, 353 { 354 JobBase: JobBase{ 355 Name: "c", 356 }, 357 Brancher: Brancher{ 358 SkipBranches: []string{"s"}, 359 Branches: []string{"r"}, 360 }, 361 }, 362 { 363 JobBase: JobBase{ 364 Name: "d", 365 }, 366 Brancher: Brancher{ 367 SkipBranches: []string{"s"}, 368 Branches: []string{"s", "r"}, 369 }, 370 }, 371 { 372 JobBase: JobBase{ 373 Name: "default", 374 }, 375 }, 376 } 377 378 if err := SetPresubmitRegexes(jobs); err != nil { 379 t.Fatalf("could not set regexes: %v", err) 380 } 381 382 for _, job := range jobs { 383 if job.Name == "default" { 384 if !job.Brancher.ShouldRun("s") { 385 t.Errorf("Job %s should run branch s", job.Name) 386 } 387 } else if job.Brancher.ShouldRun("s") { 388 t.Errorf("Job %s should not run branch s", job.Name) 389 } 390 391 if !job.Brancher.ShouldRun("r") { 392 t.Errorf("Job %s should run branch r", job.Name) 393 } 394 } 395 } 396 397 func TestMergePreset(t *testing.T) { 398 tcs := []struct { 399 name string 400 jobLabels map[string]string 401 pod *coreapi.PodSpec 402 presets []Preset 403 404 shouldError bool 405 numEnv int 406 numVol int 407 numVolMounts int 408 }{ 409 { 410 name: "one volume", 411 jobLabels: map[string]string{"foo": "bar"}, 412 pod: &coreapi.PodSpec{}, 413 presets: []Preset{ 414 { 415 Labels: map[string]string{"foo": "bar"}, 416 Volumes: []coreapi.Volume{{Name: "baz"}}, 417 }, 418 }, 419 numVol: 1, 420 }, 421 { 422 name: "wrong label", 423 jobLabels: map[string]string{"foo": "nope"}, 424 pod: &coreapi.PodSpec{}, 425 presets: []Preset{ 426 { 427 Labels: map[string]string{"foo": "bar"}, 428 Volumes: []coreapi.Volume{{Name: "baz"}}, 429 }, 430 }, 431 }, 432 { 433 name: "conflicting volume name for podspec", 434 jobLabels: map[string]string{"foo": "bar"}, 435 pod: &coreapi.PodSpec{Volumes: []coreapi.Volume{{Name: "baz"}}}, 436 presets: []Preset{ 437 { 438 Labels: map[string]string{"foo": "bar"}, 439 Volumes: []coreapi.Volume{{Name: "baz"}}, 440 }, 441 }, 442 shouldError: true, 443 }, 444 { 445 name: "non conflicting volume name", 446 jobLabels: map[string]string{"foo": "bar"}, 447 pod: &coreapi.PodSpec{Volumes: []coreapi.Volume{{Name: "baz"}}}, 448 presets: []Preset{ 449 { 450 Labels: map[string]string{"foo": "bar"}, 451 Volumes: []coreapi.Volume{{Name: "qux"}}, 452 }, 453 }, 454 numVol: 2, 455 }, 456 { 457 name: "one env", 458 jobLabels: map[string]string{"foo": "bar"}, 459 pod: &coreapi.PodSpec{Containers: []coreapi.Container{{}}}, 460 presets: []Preset{ 461 { 462 Labels: map[string]string{"foo": "bar"}, 463 Env: []coreapi.EnvVar{{Name: "baz"}}, 464 }, 465 }, 466 numEnv: 1, 467 }, 468 { 469 name: "conflicting env", 470 jobLabels: map[string]string{"foo": "bar"}, 471 pod: &coreapi.PodSpec{Containers: []coreapi.Container{{Env: []coreapi.EnvVar{{Name: "baz"}}}}}, 472 presets: []Preset{ 473 { 474 Labels: map[string]string{"foo": "bar"}, 475 Env: []coreapi.EnvVar{{Name: "baz"}}, 476 }, 477 }, 478 shouldError: true, 479 }, 480 { 481 name: "one vm", 482 jobLabels: map[string]string{"foo": "bar"}, 483 pod: &coreapi.PodSpec{Containers: []coreapi.Container{{}}}, 484 presets: []Preset{ 485 { 486 Labels: map[string]string{"foo": "bar"}, 487 VolumeMounts: []coreapi.VolumeMount{{Name: "baz"}}, 488 }, 489 }, 490 numVolMounts: 1, 491 }, 492 { 493 name: "one of each", 494 jobLabels: map[string]string{"foo": "bar"}, 495 pod: &coreapi.PodSpec{Containers: []coreapi.Container{{}}}, 496 presets: []Preset{ 497 { 498 Labels: map[string]string{"foo": "bar"}, 499 Env: []coreapi.EnvVar{{Name: "baz"}}, 500 VolumeMounts: []coreapi.VolumeMount{{Name: "baz"}}, 501 Volumes: []coreapi.Volume{{Name: "qux"}}, 502 }, 503 }, 504 numEnv: 1, 505 numVol: 1, 506 numVolMounts: 1, 507 }, 508 { 509 name: "two vm", 510 jobLabels: map[string]string{"foo": "bar"}, 511 pod: &coreapi.PodSpec{Containers: []coreapi.Container{{}}}, 512 presets: []Preset{ 513 { 514 Labels: map[string]string{"foo": "bar"}, 515 VolumeMounts: []coreapi.VolumeMount{{Name: "baz"}, {Name: "foo"}}, 516 }, 517 }, 518 numVolMounts: 2, 519 }, 520 { 521 name: "default preset only", 522 jobLabels: map[string]string{"foo": "bar"}, 523 pod: &coreapi.PodSpec{Containers: []coreapi.Container{{}}}, 524 presets: []Preset{ 525 { 526 Env: []coreapi.EnvVar{{Name: "baz"}}, 527 }, 528 }, 529 numEnv: 1, 530 }, 531 { 532 name: "default and matching presets", 533 jobLabels: map[string]string{"foo": "bar"}, 534 pod: &coreapi.PodSpec{Containers: []coreapi.Container{{}}}, 535 presets: []Preset{ 536 { 537 Env: []coreapi.EnvVar{{Name: "baz"}}, 538 }, 539 { 540 Labels: map[string]string{"foo": "bar"}, 541 Volumes: []coreapi.Volume{{Name: "qux"}}, 542 }, 543 }, 544 numEnv: 1, 545 numVol: 1, 546 }, 547 { 548 name: "default and non-matching presets", 549 jobLabels: map[string]string{"foo": "bar"}, 550 pod: &coreapi.PodSpec{Containers: []coreapi.Container{{}}}, 551 presets: []Preset{ 552 { 553 Env: []coreapi.EnvVar{{Name: "baz"}}, 554 }, 555 { 556 Labels: map[string]string{"no": "match"}, 557 Volumes: []coreapi.Volume{{Name: "qux"}}, 558 }, 559 }, 560 numEnv: 1, 561 }, 562 { 563 name: "multiple default presets", 564 jobLabels: map[string]string{"foo": "bar"}, 565 pod: &coreapi.PodSpec{Containers: []coreapi.Container{{}}}, 566 presets: []Preset{ 567 { 568 Env: []coreapi.EnvVar{{Name: "baz"}}, 569 }, 570 { 571 Env: []coreapi.EnvVar{{Name: "foo"}}, 572 }, 573 { 574 Volumes: []coreapi.Volume{{Name: "qux"}}, 575 }, 576 }, 577 numEnv: 2, 578 numVol: 1, 579 }, 580 { 581 name: "default preset conflicts with job", 582 jobLabels: map[string]string{"foo": "bar"}, 583 pod: &coreapi.PodSpec{Volumes: []coreapi.Volume{{Name: "baz"}}}, 584 presets: []Preset{ 585 { 586 Volumes: []coreapi.Volume{{Name: "baz"}}, 587 }, 588 }, 589 shouldError: true, 590 }, 591 } 592 for _, tc := range tcs { 593 t.Run(tc.name, func(t *testing.T) { 594 if err := resolvePresets("foo", tc.jobLabels, tc.pod, tc.presets); err == nil && tc.shouldError { 595 t.Errorf("expected error but got none.") 596 } else if err != nil && !tc.shouldError { 597 t.Errorf("expected no error but got %v.", err) 598 } 599 if tc.shouldError { 600 return 601 } 602 if len(tc.pod.Volumes) != tc.numVol { 603 t.Errorf("wrong number of volumes for podspec. Got %d, expected %d.", len(tc.pod.Volumes), tc.numVol) 604 } 605 for _, c := range tc.pod.Containers { 606 if len(c.VolumeMounts) != tc.numVolMounts { 607 t.Errorf("wrong number of volume mounts for podspec. Got %d, expected %d.", len(c.VolumeMounts), tc.numVolMounts) 608 } 609 if len(c.Env) != tc.numEnv { 610 t.Errorf("wrong number of env vars for podspec. Got %d, expected %d.", len(c.Env), tc.numEnv) 611 } 612 } 613 }) 614 } 615 } 616 617 func TestPresubmitShouldRun(t *testing.T) { 618 var testCases = []struct { 619 name string 620 fileChanges []string 621 fileError error 622 job Presubmit 623 ref string 624 forced bool 625 defaults bool 626 627 expectedRun bool 628 expectedErr bool 629 }{ 630 { 631 name: "job skipped on the branch won't run", 632 job: Presubmit{ 633 Brancher: Brancher{ 634 SkipBranches: []string{"master"}, 635 }, 636 }, 637 ref: "master", 638 expectedRun: false, 639 }, 640 { 641 name: "job enabled on the branch will run", 642 job: Presubmit{ 643 Brancher: Brancher{ 644 Branches: []string{"something"}, 645 }, 646 AlwaysRun: true, 647 }, 648 ref: "something", 649 expectedRun: true, 650 }, 651 { 652 name: "job running only on other branches won't run", 653 job: Presubmit{ 654 Brancher: Brancher{ 655 Branches: []string{"master"}, 656 }, 657 }, 658 ref: "release-1.11", 659 expectedRun: false, 660 }, 661 { 662 name: "job on a branch that's not skipped will run", 663 job: Presubmit{ 664 Brancher: Brancher{ 665 SkipBranches: []string{"master"}, 666 }, 667 AlwaysRun: true, 668 }, 669 ref: "other", 670 expectedRun: true, 671 }, 672 { 673 name: "job with always_run: true should run", 674 job: Presubmit{ 675 AlwaysRun: true, 676 }, 677 ref: "master", 678 expectedRun: true, 679 }, 680 { 681 name: "job with always_run: false and no run_if_changed or skip_if_only_changed should not run", 682 job: Presubmit{ 683 AlwaysRun: false, 684 Trigger: `(?m)^/test (?:.*? )?foo(?: .*?)?$`, 685 RerunCommand: "/test foo", 686 RegexpChangeMatcher: RegexpChangeMatcher{ 687 RunIfChanged: "", 688 SkipIfOnlyChanged: "", 689 }, 690 }, 691 ref: "master", 692 expectedRun: false, 693 }, 694 { 695 name: "job with run_if_changed but file get errors should not run", 696 job: Presubmit{ 697 Trigger: `(?m)^/test (?:.*? )?foo(?: .*?)?$`, 698 RerunCommand: "/test foo", 699 RegexpChangeMatcher: RegexpChangeMatcher{ 700 RunIfChanged: "file", 701 }, 702 }, 703 ref: "master", 704 fileError: errors.New("oops"), 705 expectedRun: false, 706 expectedErr: true, 707 }, 708 { 709 name: "job with skip_if_only_changed but file get errors should not run", 710 job: Presubmit{ 711 Trigger: `(?m)^/test (?:.*? )?foo(?: .*?)?$`, 712 RerunCommand: "/test foo", 713 RegexpChangeMatcher: RegexpChangeMatcher{ 714 SkipIfOnlyChanged: "file", 715 }, 716 }, 717 ref: "master", 718 fileChanges: []string{"something"}, 719 fileError: errors.New("oops"), 720 expectedRun: false, 721 expectedErr: true, 722 }, 723 { 724 name: "job with run_if_changed not matching should not run", 725 job: Presubmit{ 726 Trigger: `(?m)^/test (?:.*? )?foo(?: .*?)?$`, 727 RerunCommand: "/test foo", 728 RegexpChangeMatcher: RegexpChangeMatcher{ 729 RunIfChanged: "^file$", 730 }, 731 }, 732 ref: "master", 733 fileChanges: []string{"something"}, 734 expectedRun: false, 735 }, 736 { 737 name: "job with skip_if_only_changed all matching should not run", 738 job: Presubmit{ 739 Trigger: `(?m)^/test (?:.*? )?foo(?: .*?)?$`, 740 RerunCommand: "/test foo", 741 RegexpChangeMatcher: RegexpChangeMatcher{ 742 SkipIfOnlyChanged: "file$", 743 }, 744 }, 745 ref: "master", 746 fileChanges: []string{"onefile", "two-file", "pkg/controller/three_file"}, 747 expectedRun: false, 748 }, 749 { 750 name: "job with run_if_changed not matching should run when default=true", 751 job: Presubmit{ 752 Trigger: `(?m)^/test (?:.*? )?foo(?: .*?)?$`, 753 RerunCommand: "/test foo", 754 RegexpChangeMatcher: RegexpChangeMatcher{ 755 RunIfChanged: "^file$", 756 }, 757 }, 758 ref: "master", 759 fileChanges: []string{"something"}, 760 defaults: true, 761 expectedRun: true, 762 }, 763 { 764 name: "job with skip_if_only_changed all matching should run when default=true", 765 job: Presubmit{ 766 Trigger: `(?m)^/test (?:.*? )?foo(?: .*?)?$`, 767 RerunCommand: "/test foo", 768 RegexpChangeMatcher: RegexpChangeMatcher{ 769 SkipIfOnlyChanged: "file$", 770 }, 771 }, 772 ref: "master", 773 fileChanges: []string{"onefile", "two-file", "pkg/controller/three_file"}, 774 defaults: true, 775 expectedRun: true, 776 }, 777 { 778 name: "job with run_if_changed matching should run", 779 job: Presubmit{ 780 Trigger: `(?m)^/test (?:.*? )?foo(?: .*?)?$`, 781 RerunCommand: "/test foo", 782 RegexpChangeMatcher: RegexpChangeMatcher{ 783 RunIfChanged: "^file$", 784 }, 785 }, 786 ref: "master", 787 fileChanges: []string{"file"}, 788 expectedRun: true, 789 }, 790 { 791 name: "job with skip_if_all_changed partially matching should run", 792 job: Presubmit{ 793 Trigger: `(?m)^/test (?:.*? )?foo(?: .*?)?$`, 794 RerunCommand: "/test foo", 795 RegexpChangeMatcher: RegexpChangeMatcher{ 796 SkipIfOnlyChanged: "file$", 797 }, 798 }, 799 ref: "master", 800 fileChanges: []string{"onefile", "two-file", "pkg/controller/three_file.go"}, 801 expectedRun: true, 802 }, 803 } 804 805 for _, testCase := range testCases { 806 t.Run(testCase.name, func(t *testing.T) { 807 jobs := []Presubmit{testCase.job} 808 if err := SetPresubmitRegexes(jobs); err != nil { 809 t.Fatalf("%s: failed to set presubmit regexes: %v", testCase.name, err) 810 } 811 jobShouldRun, err := jobs[0].ShouldRun(testCase.ref, func() ([]string, error) { 812 return testCase.fileChanges, testCase.fileError 813 }, testCase.forced, testCase.defaults) 814 if err == nil && testCase.expectedErr { 815 t.Errorf("%s: expected an error and got none", testCase.name) 816 } 817 if err != nil && !testCase.expectedErr { 818 t.Errorf("%s: expected no error but got one: %v", testCase.name, err) 819 } 820 if jobShouldRun != testCase.expectedRun { 821 t.Errorf("%s: did not determine if job should run correctly, expected %v but got %v", testCase.name, testCase.expectedRun, jobShouldRun) 822 } 823 }) 824 } 825 } 826 827 func TestPostsubmitShouldRun(t *testing.T) { 828 true_ := true 829 false_ := false 830 var testCases = []struct { 831 name string 832 fileChanges []string 833 fileError error 834 job Postsubmit 835 ref string 836 expectedRun bool 837 expectedErr bool 838 }{ 839 { 840 name: "job skipped on the branch won't run", 841 job: Postsubmit{ 842 Brancher: Brancher{ 843 SkipBranches: []string{"master"}, 844 }, 845 }, 846 ref: "master", 847 expectedRun: false, 848 }, 849 { 850 name: "job enabled on the branch will run", 851 job: Postsubmit{ 852 Brancher: Brancher{ 853 Branches: []string{"something"}, 854 }, 855 }, 856 ref: "something", 857 expectedRun: true, 858 }, 859 { 860 name: "job running only on other branches won't run", 861 job: Postsubmit{ 862 Brancher: Brancher{ 863 Branches: []string{"master"}, 864 }, 865 }, 866 ref: "release-1.11", 867 expectedRun: false, 868 }, 869 { 870 name: "job on a branch that's not skipped will run", 871 job: Postsubmit{ 872 Brancher: Brancher{ 873 SkipBranches: []string{"master"}, 874 }, 875 }, 876 ref: "other", 877 expectedRun: true, 878 }, 879 { 880 name: "job with no run_if_changed should run", 881 job: Postsubmit{}, 882 ref: "master", 883 expectedRun: true, 884 }, 885 { 886 name: "job with run_if_changed but file get errors should not run", 887 job: Postsubmit{ 888 RegexpChangeMatcher: RegexpChangeMatcher{ 889 RunIfChanged: "file", 890 }, 891 }, 892 ref: "master", 893 fileError: errors.New("oops"), 894 expectedRun: false, 895 expectedErr: true, 896 }, 897 { 898 name: "job with skip_if_only_changed but file get errors should not run", 899 job: Postsubmit{ 900 RegexpChangeMatcher: RegexpChangeMatcher{ 901 SkipIfOnlyChanged: "file", 902 }, 903 }, 904 ref: "master", 905 fileChanges: []string{"something"}, 906 fileError: errors.New("oops"), 907 expectedRun: false, 908 expectedErr: true, 909 }, 910 { 911 name: "job with run_if_changed not matching should not run", 912 job: Postsubmit{ 913 RegexpChangeMatcher: RegexpChangeMatcher{ 914 RunIfChanged: "^file$", 915 }, 916 }, 917 ref: "master", 918 fileChanges: []string{"something"}, 919 expectedRun: false, 920 }, 921 { 922 name: "job with skip_if_only_changed all matching should not run", 923 job: Postsubmit{ 924 RegexpChangeMatcher: RegexpChangeMatcher{ 925 SkipIfOnlyChanged: "file$", 926 }, 927 }, 928 ref: "master", 929 fileChanges: []string{"onefile", "two-file", "pkg/controller/three_file"}, 930 expectedRun: false, 931 }, 932 { 933 name: "job with run_if_changed matching should run", 934 job: Postsubmit{ 935 RegexpChangeMatcher: RegexpChangeMatcher{ 936 RunIfChanged: "^file$", 937 }, 938 }, 939 ref: "master", 940 fileChanges: []string{"file"}, 941 expectedRun: true, 942 }, 943 { 944 name: "job with skip_if_only_changed partially matching should run", 945 job: Postsubmit{ 946 RegexpChangeMatcher: RegexpChangeMatcher{ 947 SkipIfOnlyChanged: "file$", 948 }, 949 }, 950 ref: "master", 951 fileChanges: []string{"onefile", "two-file", "pkg/controller/three_file.go"}, 952 expectedRun: true, 953 }, 954 { 955 name: "job with run_if_changed matching (and `always_run` explicitly set to false) should run", 956 job: Postsubmit{ 957 AlwaysRun: &false_, 958 RegexpChangeMatcher: RegexpChangeMatcher{ 959 RunIfChanged: "^file$", 960 }, 961 }, 962 ref: "master", 963 fileChanges: []string{"file"}, 964 expectedRun: true, 965 }, 966 { 967 name: "job with skip_if_only_changed partially matching (and `always_run` explicitly set to false) should run", 968 job: Postsubmit{ 969 AlwaysRun: &false_, 970 RegexpChangeMatcher: RegexpChangeMatcher{ 971 SkipIfOnlyChanged: "file$", 972 }, 973 }, 974 ref: "master", 975 fileChanges: []string{"onefile", "two-file", "pkg/controller/three_file.go"}, 976 expectedRun: true, 977 }, 978 { 979 name: "job with `always_run` explicitly set to true will run", 980 job: Postsubmit{ 981 AlwaysRun: &true_, 982 }, 983 ref: "master", 984 expectedRun: true, 985 }, 986 { 987 name: "job with `always_run` explicitly set to false will not run", 988 job: Postsubmit{ 989 AlwaysRun: &false_, 990 }, 991 ref: "master", 992 expectedRun: false, 993 }, 994 { 995 name: "job skipped on the branch will not run, even if `always_run` set to true explicitly", 996 job: Postsubmit{ 997 AlwaysRun: &true_, 998 Brancher: Brancher{ 999 SkipBranches: []string{"master"}, 1000 }, 1001 }, 1002 ref: "master", 1003 expectedRun: false, 1004 }, 1005 { 1006 name: "job enabled on the branch (with `always_run` explicitly set to false explicitly) will not run", 1007 job: Postsubmit{ 1008 AlwaysRun: &false_, 1009 Brancher: Brancher{ 1010 Branches: []string{"something"}, 1011 }, 1012 }, 1013 ref: "something", 1014 expectedRun: false, 1015 }, 1016 } 1017 1018 for _, testCase := range testCases { 1019 t.Run(testCase.name, func(t *testing.T) { 1020 jobs := []Postsubmit{testCase.job} 1021 if err := SetPostsubmitRegexes(jobs); err != nil { 1022 t.Fatalf("%s: failed to set presubmit regexes: %v", testCase.name, err) 1023 } 1024 jobShouldRun, err := jobs[0].ShouldRun(testCase.ref, func() ([]string, error) { 1025 return testCase.fileChanges, testCase.fileError 1026 }) 1027 if err == nil && testCase.expectedErr { 1028 t.Errorf("%s: expected an error and got none", testCase.name) 1029 } 1030 if err != nil && !testCase.expectedErr { 1031 t.Errorf("%s: expected no error but got one: %v", testCase.name, err) 1032 } 1033 if jobShouldRun != testCase.expectedRun { 1034 t.Errorf("%s: did not determine if job should run correctly, expected %v but got %v", testCase.name, testCase.expectedRun, jobShouldRun) 1035 } 1036 }) 1037 } 1038 } 1039 1040 func TestUtilityConfigValidation(t *testing.T) { 1041 testCases := []struct { 1042 id string 1043 valid bool 1044 uc UtilityConfig 1045 }{ 1046 { 1047 id: "empty UtilityConfig, no error", 1048 valid: true, 1049 uc: UtilityConfig{}, 1050 }, 1051 { 1052 id: "clone_uri is a not valid, error", 1053 uc: UtilityConfig{CloneURI: "://notvalidURI"}, 1054 }, 1055 { 1056 id: "one of the clone_uri is not valid, error", 1057 uc: UtilityConfig{ 1058 CloneURI: "://notvalidURI", 1059 ExtraRefs: []prowapi.Refs{ 1060 { 1061 Org: "org1", 1062 Repo: "repo1", 1063 BaseSHA: "master", 1064 CloneURI: "https://github.com/kubernetes/test-infra.git", 1065 }, 1066 { 1067 Org: "org2", 1068 Repo: "repo2", 1069 BaseSHA: "master", 1070 CloneURI: "://notvalidURI", 1071 }, 1072 }, 1073 }, 1074 }, 1075 { 1076 id: "ssh_keys specified but clone_uri is empty, no error", 1077 valid: true, 1078 uc: UtilityConfig{ 1079 DecorationConfig: &prowapi.DecorationConfig{ 1080 SSHKeySecrets: []string{"ssh-secret"}, 1081 }, 1082 }, 1083 }, 1084 { 1085 id: "ssh_keys specified but clone_uri is invalid, error", 1086 uc: UtilityConfig{ 1087 CloneURI: "://notvalidURI", 1088 DecorationConfig: &prowapi.DecorationConfig{ 1089 SSHKeySecrets: []string{"ssh-secret"}, 1090 }, 1091 }, 1092 }, 1093 { 1094 id: "ssh_keys specified and clone_uri is valid, no error", 1095 valid: true, 1096 uc: UtilityConfig{ 1097 CloneURI: "git@github.com:kubernetes/test-infra.git", 1098 DecorationConfig: &prowapi.DecorationConfig{ 1099 SSHKeySecrets: []string{"ssh-secret"}, 1100 }, 1101 }, 1102 }, 1103 { 1104 id: "ssh_keys specified and all of clone_uri are valid, no error", 1105 valid: true, 1106 uc: UtilityConfig{ 1107 CloneURI: "git@github.com:kubernetes/test-infra.git", 1108 ExtraRefs: []prowapi.Refs{ 1109 { 1110 Org: "org1", 1111 Repo: "repo1", 1112 BaseSHA: "master", 1113 CloneURI: "github.com:org1/repo1.git", 1114 }, 1115 { 1116 Org: "org2", 1117 Repo: "repo2", 1118 BaseSHA: "master", 1119 CloneURI: "git@github.com:org2/repo2.git", 1120 }, 1121 }, 1122 DecorationConfig: &prowapi.DecorationConfig{ 1123 SSHKeySecrets: []string{"ssh-secret"}, 1124 }, 1125 }, 1126 }, 1127 { 1128 id: "ssh_keys specified and one of the clone_uri is invalid, error", 1129 uc: UtilityConfig{ 1130 CloneURI: "git@github.com:kubernetes/test-infra.git", 1131 ExtraRefs: []prowapi.Refs{ 1132 { 1133 Org: "org1", 1134 Repo: "repo1", 1135 BaseSHA: "master", 1136 CloneURI: "git@github.com:org1/repo1.git", 1137 }, 1138 { 1139 Org: "org2", 1140 Repo: "repo2", 1141 BaseSHA: "master", 1142 CloneURI: "://notvalidURI", 1143 }, 1144 }, 1145 DecorationConfig: &prowapi.DecorationConfig{ 1146 SSHKeySecrets: []string{"ssh-secret"}, 1147 }, 1148 }, 1149 }, 1150 { 1151 id: "clone_uri contains a user and decoration config is nil, no error", 1152 uc: UtilityConfig{ 1153 CloneURI: "git@github.com:kubernetes/test-infra.git", 1154 }, 1155 valid: true, 1156 }, 1157 { 1158 id: "oauth token secret provided and all clone_uri have valid http(s) a scheme, no error", 1159 uc: UtilityConfig{ 1160 DecorationConfig: &prowapi.DecorationConfig{ 1161 OauthTokenSecret: &prowapi.OauthTokenSecret{ 1162 Name: "secret", 1163 Key: "token", 1164 }, 1165 }, 1166 CloneURI: "http://github.com/kubernetes/test-infra.git", 1167 ExtraRefs: []prowapi.Refs{ 1168 { 1169 Org: "org1", 1170 Repo: "repo1", 1171 BaseSHA: "master", 1172 CloneURI: "https://github.com/org1/repo1.git", 1173 }, 1174 { 1175 Org: "org2", 1176 Repo: "repo2", 1177 BaseSHA: "master", 1178 CloneURI: "http://github.com/org1/repo1.git", 1179 }, 1180 }, 1181 }, 1182 valid: true, 1183 }, 1184 { 1185 id: "oauth token secret provided but clone_uri doesn't contain a scheme, error", 1186 uc: UtilityConfig{ 1187 DecorationConfig: &prowapi.DecorationConfig{ 1188 OauthTokenSecret: &prowapi.OauthTokenSecret{ 1189 Name: "secret", 1190 Key: "token", 1191 }, 1192 }, 1193 CloneURI: "github.com/kubernetes/test-infra.git", 1194 }, 1195 }, 1196 { 1197 id: "oauth token secret provided but one of the clone_uri doesn't contain a scheme, error", 1198 uc: UtilityConfig{ 1199 DecorationConfig: &prowapi.DecorationConfig{ 1200 OauthTokenSecret: &prowapi.OauthTokenSecret{ 1201 Name: "secret", 1202 Key: "token", 1203 }, 1204 }, 1205 CloneURI: "https://github.com/kubernetes/test-infra.git", 1206 ExtraRefs: []prowapi.Refs{ 1207 { 1208 Org: "org1", 1209 Repo: "repo1", 1210 BaseSHA: "master", 1211 CloneURI: "github.com/org1/repo1.git", 1212 }, 1213 { 1214 Org: "org2", 1215 Repo: "repo2", 1216 BaseSHA: "master", 1217 CloneURI: "http://github.com/org1/repo1.git", 1218 }, 1219 }, 1220 }, 1221 }, 1222 { 1223 id: "oauth token secret provided but clone_uri doesn't contain a http(s) scheme, error", 1224 uc: UtilityConfig{ 1225 DecorationConfig: &prowapi.DecorationConfig{ 1226 OauthTokenSecret: &prowapi.OauthTokenSecret{ 1227 Name: "secret", 1228 Key: "token", 1229 }, 1230 }, 1231 CloneURI: "ssh://github.com/kubernetes/test-infra.git", 1232 }, 1233 }, 1234 { 1235 id: "oauth token secret provided but one of the clone_uri doesn't contain a http(s) scheme, error", 1236 uc: UtilityConfig{ 1237 DecorationConfig: &prowapi.DecorationConfig{ 1238 OauthTokenSecret: &prowapi.OauthTokenSecret{ 1239 Name: "secret", 1240 Key: "token", 1241 }, 1242 }, 1243 CloneURI: "https://github.com/kubernetes/test-infra.git", 1244 ExtraRefs: []prowapi.Refs{ 1245 { 1246 Org: "org1", 1247 Repo: "repo1", 1248 BaseSHA: "master", 1249 CloneURI: "ssh://git@github.com:org1/repo1.git", 1250 }, 1251 { 1252 Org: "org2", 1253 Repo: "repo2", 1254 BaseSHA: "master", 1255 CloneURI: "http://github.com/org1/repo1.git", 1256 }, 1257 }, 1258 }, 1259 }, 1260 } 1261 1262 for _, tc := range testCases { 1263 t.Run(tc.id, func(t *testing.T) { 1264 if err := tc.uc.Validate(); err != nil && tc.valid { 1265 t.Fatalf("No validation error expected: %v", err) 1266 } 1267 if err := tc.uc.Validate(); err == nil && !tc.valid { 1268 t.Fatalf("Validation error expected: %v", err) 1269 } 1270 }) 1271 } 1272 } 1273 1274 func TestJobBase_HasPipelineRunSpec(t *testing.T) { 1275 type fields struct { 1276 PipelineRunSpec *pipelinev1beta1.PipelineRunSpec 1277 TektonPipelineRunSpec *prowapi.TektonPipelineRunSpec 1278 } 1279 tests := []struct { 1280 name string 1281 fields fields 1282 want bool 1283 }{{ 1284 name: "none set", 1285 want: false, 1286 }, { 1287 name: "PipelineRunSpec set", 1288 fields: fields{ 1289 PipelineRunSpec: &pipelinev1beta1.PipelineRunSpec{}, 1290 }, 1291 want: true, 1292 }, { 1293 name: "TektonPipelineRunSpec set", 1294 fields: fields{ 1295 TektonPipelineRunSpec: &prowapi.TektonPipelineRunSpec{}, 1296 }, 1297 want: false, 1298 }, { 1299 name: "TektonPipelineRunSpec.V1VBeta1 set", 1300 fields: fields{ 1301 TektonPipelineRunSpec: &prowapi.TektonPipelineRunSpec{ 1302 V1Beta1: &pipelinev1beta1.PipelineRunSpec{}, 1303 }, 1304 }, 1305 want: true, 1306 }, { 1307 name: "both set", 1308 fields: fields{ 1309 PipelineRunSpec: &pipelinev1beta1.PipelineRunSpec{}, 1310 TektonPipelineRunSpec: &prowapi.TektonPipelineRunSpec{ 1311 V1Beta1: &pipelinev1beta1.PipelineRunSpec{}, 1312 }, 1313 }, 1314 want: true, 1315 }} 1316 for _, tt := range tests { 1317 t.Run(tt.name, func(t *testing.T) { 1318 jb := JobBase{ 1319 PipelineRunSpec: tt.fields.PipelineRunSpec, 1320 TektonPipelineRunSpec: tt.fields.TektonPipelineRunSpec, 1321 } 1322 if got := jb.HasPipelineRunSpec(); got != tt.want { 1323 t.Errorf("JobBase.HasPipelineRunSpec() = %v, want %v", got, tt.want) 1324 } 1325 }) 1326 } 1327 } 1328 1329 func TestJobBase_GetPipelineRunSpec(t *testing.T) { 1330 type fields struct { 1331 PipelineRunSpec *pipelinev1beta1.PipelineRunSpec 1332 TektonPipelineRunSpec *prowapi.TektonPipelineRunSpec 1333 } 1334 tests := []struct { 1335 name string 1336 fields fields 1337 want *pipelinev1beta1.PipelineRunSpec 1338 wantErr bool 1339 }{ 1340 { 1341 name: "none set", 1342 fields: fields{ 1343 PipelineRunSpec: nil, 1344 TektonPipelineRunSpec: nil, 1345 }, 1346 wantErr: true, 1347 }, 1348 { 1349 name: "only PipelineRunSpec set", 1350 fields: fields{ 1351 PipelineRunSpec: &pipelinev1beta1.PipelineRunSpec{ 1352 ServiceAccountName: "robot", 1353 PipelineSpec: &pipelinev1beta1.PipelineSpec{ 1354 Tasks: []pipelinev1beta1.PipelineTask{{Name: "implicit git resource", TaskRef: &pipelinev1beta1.TaskRef{Name: "abc"}}}, 1355 }, 1356 }, 1357 TektonPipelineRunSpec: nil, 1358 }, 1359 want: &pipelinev1beta1.PipelineRunSpec{ 1360 ServiceAccountName: "robot", 1361 PipelineSpec: &pipelinev1beta1.PipelineSpec{ 1362 Tasks: []pipelinev1beta1.PipelineTask{{Name: "implicit git resource", TaskRef: &pipelinev1beta1.TaskRef{Name: "abc"}}}, 1363 }, 1364 }, 1365 }, 1366 { 1367 name: "only TektonPipelineRunSpec set", 1368 fields: fields{ 1369 PipelineRunSpec: nil, 1370 TektonPipelineRunSpec: &prowapi.TektonPipelineRunSpec{ 1371 V1Beta1: &pipelinev1beta1.PipelineRunSpec{ 1372 ServiceAccountName: "robot", 1373 PipelineSpec: &pipelinev1beta1.PipelineSpec{ 1374 Tasks: []pipelinev1beta1.PipelineTask{{Name: "implicit git resource", TaskRef: &pipelinev1beta1.TaskRef{Name: "abc"}}}, 1375 }, 1376 }, 1377 }, 1378 }, 1379 want: &pipelinev1beta1.PipelineRunSpec{ 1380 ServiceAccountName: "robot", 1381 PipelineSpec: &pipelinev1beta1.PipelineSpec{ 1382 Tasks: []pipelinev1beta1.PipelineTask{{Name: "implicit git resource", TaskRef: &pipelinev1beta1.TaskRef{Name: "abc"}}}, 1383 }, 1384 }, 1385 }, 1386 { 1387 name: "PipelineRunSpec and TektonPipelineRunSpec set", 1388 fields: fields{ 1389 PipelineRunSpec: &pipelinev1beta1.PipelineRunSpec{ 1390 ServiceAccountName: "robot", 1391 PipelineSpec: &pipelinev1beta1.PipelineSpec{ 1392 Tasks: []pipelinev1beta1.PipelineTask{{Name: "implicit git resource", TaskRef: &pipelinev1beta1.TaskRef{Name: "abc"}}}, 1393 }, 1394 }, 1395 TektonPipelineRunSpec: &prowapi.TektonPipelineRunSpec{ 1396 V1Beta1: &pipelinev1beta1.PipelineRunSpec{ 1397 ServiceAccountName: "robot", 1398 PipelineSpec: &pipelinev1beta1.PipelineSpec{ 1399 Tasks: []pipelinev1beta1.PipelineTask{{Name: "implicit git resource", TaskRef: &pipelinev1beta1.TaskRef{Name: "def"}}}, 1400 }, 1401 }, 1402 }, 1403 }, 1404 want: &pipelinev1beta1.PipelineRunSpec{ 1405 ServiceAccountName: "robot", 1406 PipelineSpec: &pipelinev1beta1.PipelineSpec{ 1407 Tasks: []pipelinev1beta1.PipelineTask{{Name: "implicit git resource", TaskRef: &pipelinev1beta1.TaskRef{Name: "def"}}}, 1408 }, 1409 }, 1410 }} 1411 for _, tt := range tests { 1412 t.Run(tt.name, func(t *testing.T) { 1413 jb := JobBase{ 1414 PipelineRunSpec: tt.fields.PipelineRunSpec, 1415 TektonPipelineRunSpec: tt.fields.TektonPipelineRunSpec, 1416 } 1417 got, err := jb.GetPipelineRunSpec() 1418 if (err != nil) != tt.wantErr { 1419 t.Errorf("JobBase.GetPipelineRunSpec() error = %v, wantErr %v", err, tt.wantErr) 1420 return 1421 } 1422 if !reflect.DeepEqual(got, tt.want) { 1423 t.Errorf("JobBase.GetPipelineRunSpec() = %v, want %v", got, tt.want) 1424 } 1425 }) 1426 } 1427 }