sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/crier/reporters/gerrit/reporter_test.go (about) 1 /* 2 Copyright 2018 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 gerrit 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "reflect" 24 "regexp" 25 "strconv" 26 "strings" 27 "testing" 28 "time" 29 30 "github.com/andygrunwald/go-gerrit" 31 "github.com/sirupsen/logrus" 32 "golang.org/x/sync/errgroup" 33 "k8s.io/apimachinery/pkg/api/equality" 34 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 35 "k8s.io/apimachinery/pkg/util/diff" 36 fakectrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" 37 38 v1 "sigs.k8s.io/prow/pkg/apis/prowjobs/v1" 39 "sigs.k8s.io/prow/pkg/crier/reporters/criercommonlib" 40 "sigs.k8s.io/prow/pkg/kube" 41 ) 42 43 var timeNow = time.Date(1234, time.May, 15, 1, 2, 3, 4, time.UTC) 44 45 const ( 46 presubmit = string(v1.PresubmitJob) 47 postsubmit = string(v1.PostsubmitJob) 48 ) 49 50 type fgc struct { 51 reportMessage string 52 reportLabel map[string]string 53 instance string 54 changes map[string][]*gerrit.ChangeInfo 55 count int 56 } 57 58 func (f *fgc) SetReview(instance, id, revision, message string, labels map[string]string) error { 59 if instance != f.instance { 60 return fmt.Errorf("wrong instance: %s", instance) 61 } 62 exist, err := f.ChangeExist(instance, id) 63 if err != nil { 64 return err 65 } 66 if !exist { 67 return errors.New("change not exist: 404") 68 } 69 change, err := f.GetChange(instance, id) 70 if err != nil { 71 return err 72 } 73 74 if _, ok := change.Revisions[revision]; !ok { 75 return errors.New("revision doesn't exist") 76 } 77 78 for label := range labels { 79 if label == "bad-label" { 80 return fmt.Errorf("bad label") 81 } 82 } 83 f.reportMessage = message 84 if len(labels) > 0 { 85 f.reportLabel = labels 86 } 87 f.count++ 88 return nil 89 } 90 91 func (f *fgc) GetChange(instance, id string, addtionalFields ...string) (*gerrit.ChangeInfo, error) { 92 if f.changes == nil { 93 return nil, errors.New("fake client changes is not initialized") 94 } 95 changes, ok := f.changes[instance] 96 if !ok { 97 return nil, fmt.Errorf("instance %s not found", instance) 98 } 99 for _, change := range changes { 100 if change.ID == id { 101 return change, nil 102 } 103 } 104 return nil, nil 105 } 106 107 func (f *fgc) ChangeExist(instance, id string) (bool, error) { 108 if f.changes == nil { 109 return false, errors.New("fake client changes is not initialized") 110 } 111 changes, ok := f.changes[instance] 112 if !ok { 113 return false, fmt.Errorf("instance %s not found", instance) 114 } 115 for _, change := range changes { 116 if change.ID == id { 117 return true, nil 118 } 119 } 120 return false, nil 121 } 122 123 func TestReport(t *testing.T) { 124 changes := map[string][]*gerrit.ChangeInfo{ 125 "gerrit": { 126 {ID: "123-abc", Status: "NEW", Revisions: map[string]gerrit.RevisionInfo{"abc": {}}}, 127 {ID: "merged", Status: "MERGED", Revisions: map[string]gerrit.RevisionInfo{"abc": {}}}, 128 }, 129 } 130 var testcases = []struct { 131 name string 132 pj *v1.ProwJob 133 existingPJs []*v1.ProwJob 134 expectReport bool 135 reportInclude []string 136 reportExclude []string 137 expectLabel map[string]string 138 expectError bool 139 numExpectedReport int 140 }{ 141 { 142 name: "1 job, unfinished, should not report", 143 pj: &v1.ProwJob{ 144 Spec: v1.ProwJobSpec{ 145 Report: true, 146 }, 147 Status: v1.ProwJobStatus{ 148 State: v1.PendingState, 149 }, 150 }, 151 }, 152 { 153 name: "1 job, finished, no labels, should not report", 154 pj: &v1.ProwJob{ 155 Spec: v1.ProwJobSpec{ 156 Report: true, 157 }, 158 Status: v1.ProwJobStatus{ 159 State: v1.SuccessState, 160 }, 161 }, 162 }, 163 { 164 name: "1 job, finished, missing gerrit-id label, should not report", 165 pj: &v1.ProwJob{ 166 Spec: v1.ProwJobSpec{ 167 Report: true, 168 }, 169 ObjectMeta: metav1.ObjectMeta{ 170 Labels: map[string]string{ 171 kube.GerritRevision: "abc", 172 kube.ProwJobTypeLabel: presubmit, 173 kube.GerritReportLabel: "Code-Review", 174 }, 175 Annotations: map[string]string{ 176 kube.GerritInstance: "gerrit", 177 }, 178 }, 179 Status: v1.ProwJobStatus{ 180 State: v1.SuccessState, 181 }, 182 }, 183 }, 184 { 185 name: "1 job, finished, missing gerrit-revision label, should not report", 186 pj: &v1.ProwJob{ 187 Spec: v1.ProwJobSpec{ 188 Report: true, 189 }, 190 ObjectMeta: metav1.ObjectMeta{ 191 Annotations: map[string]string{ 192 kube.GerritID: "123-abc", 193 kube.GerritInstance: "gerrit", 194 kube.GerritReportLabel: "Code-Review", 195 }, 196 }, 197 Status: v1.ProwJobStatus{ 198 State: v1.SuccessState, 199 }, 200 }, 201 }, 202 { 203 name: "1 job, finished, missing gerrit-instance label, should not report", 204 pj: &v1.ProwJob{ 205 Spec: v1.ProwJobSpec{ 206 Report: true, 207 }, 208 ObjectMeta: metav1.ObjectMeta{ 209 Labels: map[string]string{ 210 kube.GerritRevision: "abc", 211 kube.ProwJobTypeLabel: presubmit, 212 kube.GerritReportLabel: "Code-Review", 213 }, 214 Annotations: map[string]string{ 215 kube.GerritID: "123-abc", 216 }, 217 }, 218 Status: v1.ProwJobStatus{ 219 State: v1.SuccessState, 220 }, 221 }, 222 }, 223 { 224 name: "1 job, passed, should report", 225 pj: &v1.ProwJob{ 226 ObjectMeta: metav1.ObjectMeta{ 227 Labels: map[string]string{ 228 kube.GerritRevision: "abc", 229 kube.ProwJobTypeLabel: presubmit, 230 kube.GerritReportLabel: "Code-Review", 231 }, 232 Annotations: map[string]string{ 233 kube.GerritID: "123-abc", 234 kube.GerritInstance: "gerrit", 235 }, 236 Name: "ci-foo", 237 Namespace: "test-pods", 238 }, 239 Status: v1.ProwJobStatus{ 240 State: v1.SuccessState, 241 URL: "guber/foo", 242 }, 243 Spec: v1.ProwJobSpec{ 244 Refs: &v1.Refs{ 245 Repo: "foo", 246 Pulls: []v1.Pull{ 247 { 248 Number: 0, 249 }, 250 }, 251 }, 252 Job: "ci-foo", 253 Report: true, 254 }, 255 }, 256 expectReport: true, 257 reportInclude: []string{"1 out of 1", "ci-foo", "SUCCESS", "guber/foo"}, 258 expectLabel: map[string]string{codeReview: lgtm}, 259 numExpectedReport: 0, 260 }, 261 { 262 name: "1-job-passed-change-missing", 263 pj: &v1.ProwJob{ 264 ObjectMeta: metav1.ObjectMeta{ 265 Labels: map[string]string{ 266 kube.GerritRevision: "abc", 267 kube.ProwJobTypeLabel: presubmit, 268 kube.GerritReportLabel: "Code-Review", 269 }, 270 Annotations: map[string]string{ 271 kube.GerritID: "123-not-exist", 272 kube.GerritInstance: "gerrit", 273 }, 274 Name: "ci-foo", 275 Namespace: "test-pods", 276 }, 277 Status: v1.ProwJobStatus{ 278 State: v1.SuccessState, 279 URL: "guber/foo", 280 }, 281 Spec: v1.ProwJobSpec{ 282 Refs: &v1.Refs{ 283 Repo: "foo", 284 Pulls: []v1.Pull{ 285 { 286 Number: 0, 287 }, 288 }, 289 }, 290 Job: "ci-foo", 291 Report: true, 292 }, 293 }, 294 expectReport: true, 295 numExpectedReport: 0, 296 }, 297 { 298 name: "1-job-passed-revision-missing", 299 pj: &v1.ProwJob{ 300 ObjectMeta: metav1.ObjectMeta{ 301 Labels: map[string]string{ 302 kube.GerritRevision: "not-exist", 303 kube.ProwJobTypeLabel: presubmit, 304 kube.GerritReportLabel: "Code-Review", 305 }, 306 Annotations: map[string]string{ 307 kube.GerritID: "123-abc", 308 kube.GerritInstance: "gerrit", 309 }, 310 Name: "ci-foo", 311 Namespace: "test-pods", 312 }, 313 Status: v1.ProwJobStatus{ 314 State: v1.SuccessState, 315 URL: "guber/foo", 316 }, 317 Spec: v1.ProwJobSpec{ 318 Refs: &v1.Refs{ 319 Repo: "foo", 320 Pulls: []v1.Pull{ 321 { 322 Number: 0, 323 }, 324 }, 325 }, 326 Job: "ci-foo", 327 Report: true, 328 }, 329 }, 330 expectReport: true, 331 numExpectedReport: 0, 332 }, 333 { 334 name: "1 job, passed, skip report set true, should not report", 335 pj: &v1.ProwJob{ 336 ObjectMeta: metav1.ObjectMeta{ 337 Labels: map[string]string{ 338 kube.GerritRevision: "abc", 339 kube.ProwJobTypeLabel: presubmit, 340 kube.GerritReportLabel: "Code-Review", 341 }, 342 Annotations: map[string]string{ 343 kube.GerritID: "123-abc", 344 kube.GerritInstance: "gerrit", 345 }, 346 }, 347 Status: v1.ProwJobStatus{ 348 State: v1.SuccessState, 349 URL: "guber/foo", 350 }, 351 Spec: v1.ProwJobSpec{ 352 Refs: &v1.Refs{ 353 Repo: "foo", 354 Pulls: []v1.Pull{ 355 { 356 Number: 0, 357 }, 358 }, 359 }, 360 Job: "ci-foo", 361 Report: false, 362 }, 363 }, 364 }, 365 { 366 name: "1 job, passed, bad label, should report without label", 367 pj: &v1.ProwJob{ 368 ObjectMeta: metav1.ObjectMeta{ 369 Labels: map[string]string{ 370 kube.GerritRevision: "abc", 371 kube.ProwJobTypeLabel: presubmit, 372 kube.GerritReportLabel: "bad-label", 373 }, 374 Annotations: map[string]string{ 375 kube.GerritID: "123-abc", 376 kube.GerritInstance: "gerrit", 377 }, 378 Name: "ci-foo", 379 Namespace: "test-pods", 380 }, 381 Status: v1.ProwJobStatus{ 382 State: v1.SuccessState, 383 URL: "guber/foo", 384 }, 385 Spec: v1.ProwJobSpec{ 386 Refs: &v1.Refs{ 387 Repo: "foo", 388 Pulls: []v1.Pull{ 389 { 390 Number: 0, 391 }, 392 }, 393 }, 394 Job: "ci-foo", 395 Report: true, 396 }, 397 }, 398 expectReport: true, 399 reportInclude: []string{"1 out of 1", "ci-foo", "SUCCESS", "guber/foo"}, 400 numExpectedReport: 0, 401 }, 402 { 403 name: "1 job, passed, empty label, should report, but not vote", 404 pj: &v1.ProwJob{ 405 ObjectMeta: metav1.ObjectMeta{ 406 Labels: map[string]string{ 407 kube.GerritRevision: "abc", 408 kube.ProwJobTypeLabel: presubmit, 409 kube.GerritReportLabel: "", 410 }, 411 Annotations: map[string]string{ 412 kube.GerritID: "123-abc", 413 kube.GerritInstance: "gerrit", 414 }, 415 Name: "ci-foo", 416 Namespace: "test-pods", 417 }, 418 Status: v1.ProwJobStatus{ 419 State: v1.SuccessState, 420 URL: "guber/foo", 421 }, 422 Spec: v1.ProwJobSpec{ 423 Refs: &v1.Refs{ 424 Repo: "foo", 425 Pulls: []v1.Pull{ 426 { 427 Number: 0, 428 }, 429 }, 430 }, 431 Job: "ci-foo", 432 Report: true, 433 }, 434 }, 435 expectReport: true, 436 reportInclude: []string{"1 out of 1", "ci-foo", "SUCCESS", "guber/foo"}, 437 numExpectedReport: 0, 438 }, 439 { 440 name: "1 job, ABORTED, should not report", 441 pj: &v1.ProwJob{ 442 ObjectMeta: metav1.ObjectMeta{ 443 Labels: map[string]string{ 444 kube.GerritRevision: "abc", 445 kube.ProwJobTypeLabel: presubmit, 446 kube.GerritReportLabel: "Code-Review", 447 }, 448 Annotations: map[string]string{ 449 kube.GerritID: "123-abc", 450 kube.GerritInstance: "gerrit", 451 }, 452 }, 453 Status: v1.ProwJobStatus{ 454 State: v1.AbortedState, 455 URL: "guber/foo", 456 }, 457 Spec: v1.ProwJobSpec{ 458 Refs: &v1.Refs{ 459 Repo: "foo", 460 Pulls: []v1.Pull{ 461 { 462 Number: 0, 463 }, 464 }, 465 }, 466 Job: "ci-foo", 467 Report: true, 468 }, 469 }, 470 expectReport: false, 471 }, 472 { 473 name: "1 job, passed, with customized label, should report to customized label", 474 pj: &v1.ProwJob{ 475 ObjectMeta: metav1.ObjectMeta{ 476 Labels: map[string]string{ 477 kube.GerritRevision: "abc", 478 kube.GerritReportLabel: "foobar-label", 479 kube.ProwJobTypeLabel: presubmit, 480 }, 481 Annotations: map[string]string{ 482 kube.GerritID: "123-abc", 483 kube.GerritInstance: "gerrit", 484 }, 485 Name: "ci-foo", 486 Namespace: "test-pods", 487 }, 488 Status: v1.ProwJobStatus{ 489 State: v1.SuccessState, 490 URL: "guber/foo", 491 }, 492 Spec: v1.ProwJobSpec{ 493 Refs: &v1.Refs{ 494 Repo: "foo", 495 Pulls: []v1.Pull{ 496 { 497 Number: 0, 498 }, 499 }, 500 }, 501 Job: "ci-foo", 502 Report: true, 503 }, 504 }, 505 expectReport: true, 506 reportInclude: []string{"1 out of 1", "ci-foo", "SUCCESS", "guber/foo"}, 507 expectLabel: map[string]string{"foobar-label": lgtm}, 508 numExpectedReport: 0, 509 }, 510 { 511 name: "1 job, failed, should report", 512 pj: &v1.ProwJob{ 513 ObjectMeta: metav1.ObjectMeta{ 514 Labels: map[string]string{ 515 kube.GerritRevision: "abc", 516 kube.ProwJobTypeLabel: presubmit, 517 kube.GerritReportLabel: "Code-Review", 518 }, 519 Annotations: map[string]string{ 520 kube.GerritID: "123-abc", 521 kube.GerritInstance: "gerrit", 522 }, 523 Name: "ci-foo", 524 Namespace: "test-pods", 525 }, 526 Status: v1.ProwJobStatus{ 527 State: v1.FailureState, 528 URL: "guber/foo", 529 }, 530 Spec: v1.ProwJobSpec{ 531 Type: v1.PresubmitJob, 532 Refs: &v1.Refs{ 533 Repo: "foo", 534 Pulls: []v1.Pull{ 535 { 536 Number: 0, 537 }, 538 }, 539 }, 540 Job: "ci-foo", 541 Report: true, 542 }, 543 }, 544 expectReport: true, 545 reportInclude: []string{"0 out of 1", "ci-foo", "FAILURE", "guber/foo"}, 546 expectLabel: map[string]string{codeReview: lbtm}, 547 numExpectedReport: 0, 548 }, 549 { 550 name: "1 job, passed, has slash in repo name, should report and handle slash properly", 551 pj: &v1.ProwJob{ 552 ObjectMeta: metav1.ObjectMeta{ 553 Labels: map[string]string{ 554 kube.GerritRevision: "abc", 555 kube.ProwJobTypeLabel: presubmit, 556 kube.GerritReportLabel: "Code-Review", 557 }, 558 Annotations: map[string]string{ 559 kube.GerritID: "123-abc", 560 kube.GerritInstance: "gerrit", 561 }, 562 Name: "ci-foo", 563 Namespace: "test-pods", 564 }, 565 Status: v1.ProwJobStatus{ 566 State: v1.SuccessState, 567 URL: "guber/foo/bar", 568 }, 569 Spec: v1.ProwJobSpec{ 570 Refs: &v1.Refs{ 571 Repo: "foo/bar", 572 Pulls: []v1.Pull{ 573 { 574 Number: 0, 575 }, 576 }, 577 }, 578 Job: "ci-foo-bar", 579 Report: true, 580 }, 581 }, 582 expectReport: true, 583 reportInclude: []string{"1 out of 1", "ci-foo-bar", "SUCCESS", "guber/foo/bar"}, 584 reportExclude: []string{"foo_bar"}, 585 expectLabel: map[string]string{codeReview: lgtm}, 586 numExpectedReport: 0, 587 }, 588 { 589 name: "1 job, no pulls, should error", 590 pj: &v1.ProwJob{ 591 ObjectMeta: metav1.ObjectMeta{ 592 Labels: map[string]string{ 593 kube.GerritRevision: "abc", 594 kube.ProwJobTypeLabel: presubmit, 595 kube.GerritReportLabel: "Code-Review", 596 }, 597 Annotations: map[string]string{ 598 kube.GerritID: "123-abc", 599 kube.GerritInstance: "gerrit", 600 }, 601 }, 602 Status: v1.ProwJobStatus{ 603 State: v1.SuccessState, 604 URL: "guber/foo", 605 }, 606 Spec: v1.ProwJobSpec{ 607 Type: v1.PresubmitJob, 608 Refs: &v1.Refs{ 609 Repo: "foo", 610 }, 611 Job: "ci-foo", 612 Report: true, 613 }, 614 }, 615 expectReport: true, 616 expectError: true, 617 }, 618 { 619 name: "1 postsubmit job, no pulls, should error", 620 pj: &v1.ProwJob{ 621 ObjectMeta: metav1.ObjectMeta{ 622 Labels: map[string]string{ 623 kube.GerritRevision: "abc", 624 kube.ProwJobTypeLabel: postsubmit, 625 kube.GerritReportLabel: "Code-Review", 626 }, 627 Annotations: map[string]string{ 628 kube.GerritID: "123-abc", 629 kube.GerritInstance: "gerrit", 630 }, 631 }, 632 Status: v1.ProwJobStatus{ 633 State: v1.SuccessState, 634 URL: "guber/foo", 635 }, 636 Spec: v1.ProwJobSpec{ 637 Type: v1.PostsubmitJob, 638 Refs: &v1.Refs{ 639 Repo: "foo", 640 }, 641 Job: "ci-foo", 642 Report: true, 643 }, 644 }, 645 expectReport: true, 646 expectError: true, 647 }, 648 { 649 name: "2 jobs, one passed, other job finished but on different revision, should report", 650 pj: &v1.ProwJob{ 651 ObjectMeta: metav1.ObjectMeta{ 652 Labels: map[string]string{ 653 kube.GerritRevision: "abc", 654 kube.ProwJobTypeLabel: presubmit, 655 kube.GerritReportLabel: "Code-Review", 656 }, 657 Annotations: map[string]string{ 658 kube.GerritID: "123-abc", 659 kube.GerritInstance: "gerrit", 660 }, 661 Name: "ci-foo", 662 Namespace: "test-pods", 663 }, 664 Status: v1.ProwJobStatus{ 665 State: v1.SuccessState, 666 URL: "guber/foo", 667 }, 668 Spec: v1.ProwJobSpec{ 669 Refs: &v1.Refs{ 670 Repo: "foo", 671 Pulls: []v1.Pull{ 672 { 673 Number: 0, 674 }, 675 }, 676 }, 677 Job: "ci-foo", 678 Report: true, 679 }, 680 }, 681 existingPJs: []*v1.ProwJob{ 682 { 683 ObjectMeta: metav1.ObjectMeta{ 684 Labels: map[string]string{ 685 kube.GerritRevision: "def", 686 kube.ProwJobTypeLabel: presubmit, 687 kube.GerritReportLabel: "Code-Review", 688 }, 689 Annotations: map[string]string{ 690 kube.GerritID: "123-def", 691 kube.GerritInstance: "gerrit", 692 }, 693 Name: "ci-foo", 694 Namespace: "test-pods", 695 }, 696 Status: v1.ProwJobStatus{ 697 State: v1.SuccessState, 698 URL: "guber/bar", 699 }, 700 Spec: v1.ProwJobSpec{ 701 Refs: &v1.Refs{ 702 Repo: "bar", 703 Pulls: []v1.Pull{ 704 { 705 Number: 0, 706 }, 707 }, 708 }, 709 Job: "ci-bar", 710 Report: true, 711 }, 712 }, 713 }, 714 expectReport: true, 715 reportInclude: []string{"1 out of 1", "ci-foo", "SUCCESS", "guber/foo"}, 716 reportExclude: []string{"2", "bar"}, 717 expectLabel: map[string]string{codeReview: lgtm}, 718 numExpectedReport: 0, 719 }, 720 { 721 name: "2 jobs, one passed, other job unfinished with same label, should not report", 722 pj: &v1.ProwJob{ 723 ObjectMeta: metav1.ObjectMeta{ 724 Labels: map[string]string{ 725 kube.GerritRevision: "abc", 726 kube.ProwJobTypeLabel: presubmit, 727 kube.GerritReportLabel: "Code-Review", 728 }, 729 Annotations: map[string]string{ 730 kube.GerritID: "123-abc", 731 kube.GerritInstance: "gerrit", 732 }, 733 }, 734 Status: v1.ProwJobStatus{ 735 State: v1.SuccessState, 736 URL: "guber/foo", 737 }, 738 Spec: v1.ProwJobSpec{ 739 Refs: &v1.Refs{ 740 Repo: "foo", 741 Pulls: []v1.Pull{ 742 { 743 Number: 0, 744 }, 745 }, 746 }, 747 Job: "ci-foo", 748 Report: true, 749 }, 750 }, 751 existingPJs: []*v1.ProwJob{ 752 { 753 ObjectMeta: metav1.ObjectMeta{ 754 Labels: map[string]string{ 755 kube.GerritRevision: "abc", 756 kube.ProwJobTypeLabel: presubmit, 757 kube.GerritReportLabel: "Code-Review", 758 }, 759 Annotations: map[string]string{ 760 kube.GerritID: "123-abc", 761 kube.GerritInstance: "gerrit", 762 }, 763 }, 764 Status: v1.ProwJobStatus{ 765 State: v1.PendingState, 766 URL: "guber/bar", 767 }, 768 Spec: v1.ProwJobSpec{ 769 Refs: &v1.Refs{ 770 Repo: "bar", 771 Pulls: []v1.Pull{ 772 { 773 Number: 0, 774 }, 775 }, 776 }, 777 Job: "ci-bar", 778 Report: true, 779 }, 780 }, 781 }, 782 }, 783 { 784 name: "2 jobs, 1 passed, 1 pending, empty labels, should not wait for aggregation, no vote", 785 pj: &v1.ProwJob{ 786 ObjectMeta: metav1.ObjectMeta{ 787 Labels: map[string]string{ 788 kube.GerritRevision: "abc", 789 kube.ProwJobTypeLabel: presubmit, 790 kube.GerritReportLabel: "", 791 }, 792 Annotations: map[string]string{ 793 kube.GerritID: "123-abc", 794 kube.GerritInstance: "gerrit", 795 }, 796 Name: "ci-foo", 797 Namespace: "test-pods", 798 }, 799 Status: v1.ProwJobStatus{ 800 State: v1.SuccessState, 801 URL: "guber/foo", 802 }, 803 Spec: v1.ProwJobSpec{ 804 Refs: &v1.Refs{ 805 Repo: "foo", 806 Pulls: []v1.Pull{ 807 { 808 Number: 0, 809 }, 810 }, 811 }, 812 Job: "ci-foo", 813 Report: true, 814 }, 815 }, 816 existingPJs: []*v1.ProwJob{ 817 { 818 ObjectMeta: metav1.ObjectMeta{ 819 Labels: map[string]string{ 820 kube.GerritRevision: "abc", 821 kube.ProwJobTypeLabel: presubmit, 822 kube.GerritReportLabel: "", 823 }, 824 Annotations: map[string]string{ 825 kube.GerritID: "123-abc", 826 kube.GerritInstance: "gerrit", 827 }, 828 Name: "ci-foo", 829 Namespace: "test-pods", 830 }, 831 Status: v1.ProwJobStatus{ 832 State: v1.PendingState, 833 URL: "guber/bar", 834 }, 835 Spec: v1.ProwJobSpec{ 836 Refs: &v1.Refs{ 837 Repo: "bar", 838 Pulls: []v1.Pull{ 839 { 840 Number: 0, 841 }, 842 }, 843 }, 844 Job: "ci-bar", 845 Report: true, 846 }, 847 }, 848 }, 849 expectReport: true, 850 reportInclude: []string{"1 out of 1", "ci-foo", "SUCCESS", "guber/foo"}, 851 reportExclude: []string{"2", "bar"}, 852 numExpectedReport: 0, 853 }, 854 { 855 name: "non-presubmit failures vote zero", 856 pj: &v1.ProwJob{ 857 ObjectMeta: metav1.ObjectMeta{ 858 Labels: map[string]string{ 859 kube.GerritRevision: "abc", 860 kube.ProwJobTypeLabel: postsubmit, 861 kube.GerritReportLabel: "Code-Review", 862 }, 863 Annotations: map[string]string{ 864 kube.GerritID: "123-abc", 865 kube.GerritInstance: "gerrit", 866 }, 867 Name: "ci-foo", 868 Namespace: "test-pods", 869 }, 870 Status: v1.ProwJobStatus{ 871 State: v1.FailureState, 872 URL: "guber/foo", 873 }, 874 Spec: v1.ProwJobSpec{ 875 Type: v1.PostsubmitJob, 876 Refs: &v1.Refs{ 877 Repo: "foo", 878 Pulls: []v1.Pull{ 879 { 880 Number: 0, 881 }, 882 }, 883 }, 884 Job: "ci-foo", 885 Report: true, 886 }, 887 }, 888 expectReport: true, 889 expectLabel: map[string]string{codeReview: lztm}, 890 numExpectedReport: 0, 891 }, 892 { 893 name: "2 jobs, one passed, other job failed, should report", 894 pj: &v1.ProwJob{ 895 ObjectMeta: metav1.ObjectMeta{ 896 Labels: map[string]string{ 897 kube.GerritRevision: "abc", 898 kube.ProwJobTypeLabel: presubmit, 899 kube.GerritReportLabel: "Code-Review", 900 }, 901 Annotations: map[string]string{ 902 kube.GerritID: "123-abc", 903 kube.GerritInstance: "gerrit", 904 }, 905 Name: "ci-foo", 906 Namespace: "test-pods", 907 }, 908 Status: v1.ProwJobStatus{ 909 State: v1.SuccessState, 910 URL: "guber/foo", 911 }, 912 Spec: v1.ProwJobSpec{ 913 Type: v1.PresubmitJob, 914 Refs: &v1.Refs{ 915 Repo: "foo", 916 Pulls: []v1.Pull{ 917 { 918 Number: 0, 919 }, 920 }, 921 }, 922 Job: "ci-foo", 923 Report: true, 924 }, 925 }, 926 existingPJs: []*v1.ProwJob{ 927 { 928 ObjectMeta: metav1.ObjectMeta{ 929 Labels: map[string]string{ 930 kube.GerritRevision: "abc", 931 kube.ProwJobTypeLabel: presubmit, 932 kube.GerritReportLabel: "Code-Review", 933 }, 934 Annotations: map[string]string{ 935 kube.GerritID: "123-abc", 936 kube.GerritInstance: "gerrit", 937 }, 938 Name: "ci-bar", 939 Namespace: "test-pods", 940 }, 941 Status: v1.ProwJobStatus{ 942 State: v1.FailureState, 943 URL: "guber/bar", 944 }, 945 Spec: v1.ProwJobSpec{ 946 Type: v1.PresubmitJob, 947 Refs: &v1.Refs{ 948 Repo: "bar", 949 Pulls: []v1.Pull{ 950 { 951 Number: 0, 952 }, 953 }, 954 }, 955 Job: "ci-bar", 956 Report: true, 957 }, 958 }, 959 }, 960 expectReport: true, 961 reportInclude: []string{"1 out of 2", "ci-foo", "SUCCESS", "ci-bar", "FAILURE", "guber/foo", "guber/bar"}, 962 reportExclude: []string{"0", "2 out of 2"}, 963 expectLabel: map[string]string{codeReview: lbtm}, 964 numExpectedReport: 0, 965 }, 966 { 967 name: "2 jobs, both passed, should report", 968 pj: &v1.ProwJob{ 969 ObjectMeta: metav1.ObjectMeta{ 970 Labels: map[string]string{ 971 kube.GerritRevision: "abc", 972 kube.ProwJobTypeLabel: presubmit, 973 kube.GerritReportLabel: "Code-Review", 974 }, 975 Annotations: map[string]string{ 976 kube.GerritID: "123-abc", 977 kube.GerritInstance: "gerrit", 978 }, 979 Name: "ci-foo", 980 Namespace: "test-pods", 981 }, 982 Status: v1.ProwJobStatus{ 983 State: v1.SuccessState, 984 URL: "guber/foo", 985 }, 986 Spec: v1.ProwJobSpec{ 987 Refs: &v1.Refs{ 988 Repo: "foo", 989 Pulls: []v1.Pull{ 990 { 991 Number: 0, 992 }, 993 }, 994 }, 995 Job: "ci-foo", 996 Report: true, 997 }, 998 }, 999 existingPJs: []*v1.ProwJob{ 1000 { 1001 ObjectMeta: metav1.ObjectMeta{ 1002 Labels: map[string]string{ 1003 kube.GerritRevision: "abc", 1004 kube.ProwJobTypeLabel: presubmit, 1005 kube.GerritReportLabel: "Code-Review", 1006 }, 1007 Annotations: map[string]string{ 1008 kube.GerritID: "123-abc", 1009 kube.GerritInstance: "gerrit", 1010 }, 1011 Name: "ci-bar", 1012 Namespace: "test-pods", 1013 }, 1014 Status: v1.ProwJobStatus{ 1015 State: v1.SuccessState, 1016 URL: "guber/bar", 1017 }, 1018 Spec: v1.ProwJobSpec{ 1019 Refs: &v1.Refs{ 1020 Repo: "bar", 1021 Pulls: []v1.Pull{ 1022 { 1023 Number: 0, 1024 }, 1025 }, 1026 }, 1027 Job: "ci-bar", 1028 Report: true, 1029 }, 1030 }, 1031 }, 1032 expectReport: true, 1033 reportInclude: []string{"2 out of 2", "ci-foo", "SUCCESS", "ci-bar", "guber/foo", "guber/bar"}, 1034 reportExclude: []string{"1", "0", "FAILURE"}, 1035 expectLabel: map[string]string{codeReview: lgtm}, 1036 numExpectedReport: 0, 1037 }, 1038 { 1039 name: "2 jobs, one passed, one aborted, should report", 1040 pj: &v1.ProwJob{ 1041 ObjectMeta: metav1.ObjectMeta{ 1042 Labels: map[string]string{ 1043 kube.GerritRevision: "abc", 1044 kube.ProwJobTypeLabel: presubmit, 1045 kube.GerritReportLabel: "Code-Review", 1046 }, 1047 Annotations: map[string]string{ 1048 kube.GerritID: "123-abc", 1049 kube.GerritInstance: "gerrit", 1050 }, 1051 Name: "ci-foo", 1052 Namespace: "test-pods", 1053 }, 1054 Status: v1.ProwJobStatus{ 1055 State: v1.SuccessState, 1056 URL: "guber/foo", 1057 }, 1058 Spec: v1.ProwJobSpec{ 1059 Refs: &v1.Refs{ 1060 Repo: "foo", 1061 Pulls: []v1.Pull{ 1062 { 1063 Number: 0, 1064 }, 1065 }, 1066 }, 1067 Job: "ci-foo", 1068 Type: v1.PresubmitJob, 1069 Report: true, 1070 }, 1071 }, 1072 existingPJs: []*v1.ProwJob{ 1073 { 1074 ObjectMeta: metav1.ObjectMeta{ 1075 Labels: map[string]string{ 1076 kube.GerritRevision: "abc", 1077 kube.ProwJobTypeLabel: presubmit, 1078 kube.GerritReportLabel: "Code-Review", 1079 }, 1080 Annotations: map[string]string{ 1081 kube.GerritID: "123-abc", 1082 kube.GerritInstance: "gerrit", 1083 }, 1084 Name: "ci-bar", 1085 Namespace: "test-pods", 1086 }, 1087 Status: v1.ProwJobStatus{ 1088 State: v1.AbortedState, 1089 URL: "guber/bar", 1090 }, 1091 Spec: v1.ProwJobSpec{ 1092 Refs: &v1.Refs{ 1093 Repo: "bar", 1094 Pulls: []v1.Pull{ 1095 { 1096 Number: 0, 1097 }, 1098 }, 1099 }, 1100 Job: "ci-bar", 1101 Type: v1.PresubmitJob, 1102 Report: true, 1103 }, 1104 }, 1105 }, 1106 expectReport: true, 1107 reportInclude: []string{"1 out of 2", "ci-foo", "SUCCESS", "guber/foo"}, 1108 expectLabel: map[string]string{codeReview: lbtm}, 1109 numExpectedReport: 0, 1110 }, 1111 { 1112 name: "postsubmit after presubmit on same revision, should report separately", 1113 pj: &v1.ProwJob{ 1114 ObjectMeta: metav1.ObjectMeta{ 1115 Labels: map[string]string{ 1116 kube.GerritRevision: "abc", 1117 kube.GerritReportLabel: "postsubmit-label", 1118 kube.ProwJobTypeLabel: postsubmit, 1119 }, 1120 Annotations: map[string]string{ 1121 kube.GerritID: "123-abc", 1122 kube.GerritInstance: "gerrit", 1123 }, 1124 Name: "ci-foo", 1125 Namespace: "test-pods", 1126 }, 1127 Status: v1.ProwJobStatus{ 1128 State: v1.SuccessState, 1129 URL: "guber/foo", 1130 }, 1131 Spec: v1.ProwJobSpec{ 1132 Refs: &v1.Refs{ 1133 Repo: "foo", 1134 Pulls: []v1.Pull{ 1135 { 1136 Number: 0, 1137 }, 1138 }, 1139 }, 1140 Job: "ci-foo", 1141 Report: true, 1142 }, 1143 }, 1144 existingPJs: []*v1.ProwJob{ 1145 { 1146 ObjectMeta: metav1.ObjectMeta{ 1147 Labels: map[string]string{ 1148 kube.GerritRevision: "abc", 1149 kube.ProwJobTypeLabel: presubmit, 1150 kube.GerritReportLabel: "Code-Review", 1151 }, 1152 Annotations: map[string]string{ 1153 kube.GerritID: "123-abc", 1154 kube.GerritInstance: "gerrit", 1155 }, 1156 Name: "ci-bar", 1157 Namespace: "test-pods", 1158 }, 1159 Status: v1.ProwJobStatus{ 1160 State: v1.SuccessState, 1161 URL: "guber/bar", 1162 }, 1163 Spec: v1.ProwJobSpec{ 1164 Refs: &v1.Refs{ 1165 Repo: "bar", 1166 Pulls: []v1.Pull{ 1167 { 1168 Number: 0, 1169 }, 1170 }, 1171 }, 1172 Job: "ci-bar", 1173 Report: true, 1174 }, 1175 }, 1176 }, 1177 expectReport: true, 1178 reportInclude: []string{"1 out of 1", "ci-foo", "SUCCESS", "guber/foo"}, 1179 expectLabel: map[string]string{"postsubmit-label": lgtm}, 1180 numExpectedReport: 0, 1181 }, 1182 { 1183 name: "2 jobs, both passed, different label, should report by itself", 1184 pj: &v1.ProwJob{ 1185 ObjectMeta: metav1.ObjectMeta{ 1186 Labels: map[string]string{ 1187 kube.GerritRevision: "abc", 1188 kube.ProwJobTypeLabel: presubmit, 1189 kube.GerritReportLabel: "label-foo", 1190 }, 1191 Annotations: map[string]string{ 1192 kube.GerritID: "123-abc", 1193 kube.GerritInstance: "gerrit", 1194 }, 1195 Name: "ci-foo", 1196 Namespace: "test-pods", 1197 }, 1198 Status: v1.ProwJobStatus{ 1199 State: v1.SuccessState, 1200 URL: "guber/foo", 1201 }, 1202 Spec: v1.ProwJobSpec{ 1203 Refs: &v1.Refs{ 1204 Repo: "foo", 1205 Pulls: []v1.Pull{ 1206 { 1207 Number: 0, 1208 }, 1209 }, 1210 }, 1211 Job: "ci-foo", 1212 Report: true, 1213 }, 1214 }, 1215 existingPJs: []*v1.ProwJob{ 1216 { 1217 ObjectMeta: metav1.ObjectMeta{ 1218 Labels: map[string]string{ 1219 kube.GerritRevision: "abc", 1220 kube.ProwJobTypeLabel: presubmit, 1221 kube.GerritReportLabel: "label-bar", 1222 }, 1223 Annotations: map[string]string{ 1224 kube.GerritID: "123-abc", 1225 kube.GerritInstance: "gerrit", 1226 }, 1227 Name: "ci-foo", 1228 Namespace: "test-pods", 1229 }, 1230 Status: v1.ProwJobStatus{ 1231 State: v1.SuccessState, 1232 URL: "guber/bar", 1233 }, 1234 Spec: v1.ProwJobSpec{ 1235 Refs: &v1.Refs{ 1236 Repo: "bar", 1237 Pulls: []v1.Pull{ 1238 { 1239 Number: 0, 1240 }, 1241 }, 1242 }, 1243 Job: "ci-bar", 1244 Report: true, 1245 }, 1246 }, 1247 }, 1248 expectReport: true, 1249 reportInclude: []string{"1 out of 1", "ci-foo", "SUCCESS", "guber/foo"}, 1250 expectLabel: map[string]string{"label-foo": lgtm}, 1251 numExpectedReport: 0, 1252 }, 1253 { 1254 name: "one job, reported, retriggered, should report by itself", 1255 pj: &v1.ProwJob{ 1256 ObjectMeta: metav1.ObjectMeta{ 1257 Labels: map[string]string{ 1258 kube.GerritRevision: "abc", 1259 kube.ProwJobTypeLabel: presubmit, 1260 kube.GerritReportLabel: "label-foo", 1261 kube.OrgLabel: "org", 1262 kube.RepoLabel: "repo", 1263 kube.PullLabel: "0", 1264 }, 1265 Annotations: map[string]string{ 1266 kube.GerritID: "123-abc", 1267 kube.GerritInstance: "gerrit", 1268 }, 1269 CreationTimestamp: metav1.Time{ 1270 Time: timeNow, 1271 }, 1272 Name: "ci-foo", 1273 Namespace: "test-pods", 1274 }, 1275 Status: v1.ProwJobStatus{ 1276 State: v1.SuccessState, 1277 URL: "guber/foo", 1278 }, 1279 Spec: v1.ProwJobSpec{ 1280 Refs: &v1.Refs{ 1281 Repo: "foo", 1282 Pulls: []v1.Pull{ 1283 { 1284 Number: 0, 1285 }, 1286 }, 1287 }, 1288 Job: "ci-foo", 1289 Report: true, 1290 }, 1291 }, 1292 existingPJs: []*v1.ProwJob{ 1293 { 1294 ObjectMeta: metav1.ObjectMeta{ 1295 Labels: map[string]string{ 1296 kube.GerritRevision: "abc", 1297 kube.ProwJobTypeLabel: presubmit, 1298 kube.GerritReportLabel: "label-foo", 1299 kube.OrgLabel: "org", 1300 kube.RepoLabel: "repo", 1301 kube.PullLabel: "0", 1302 }, 1303 Annotations: map[string]string{ 1304 kube.GerritID: "123-abc", 1305 kube.GerritInstance: "gerrit", 1306 }, 1307 CreationTimestamp: metav1.Time{ 1308 Time: timeNow.Add(-time.Minute), 1309 }, 1310 Name: "ci-foo", 1311 Namespace: "test-pods", 1312 }, 1313 Status: v1.ProwJobStatus{ 1314 PrevReportStates: map[string]v1.ProwJobState{ 1315 "gerrit-reporter": v1.FailureState, 1316 }, 1317 State: v1.FailureState, 1318 URL: "guber/foo", 1319 }, 1320 Spec: v1.ProwJobSpec{ 1321 Refs: &v1.Refs{ 1322 Repo: "foo", 1323 Pulls: []v1.Pull{ 1324 { 1325 Number: 0, 1326 }, 1327 }, 1328 }, 1329 Job: "ci-foo", 1330 Report: true, 1331 }, 1332 }, 1333 }, 1334 expectReport: true, 1335 reportInclude: []string{"1 out of 1", "ci-foo", "SUCCESS", "guber/foo"}, 1336 expectLabel: map[string]string{"label-foo": lgtm}, 1337 numExpectedReport: 0, 1338 }, 1339 { 1340 name: "older job, should not report", 1341 pj: &v1.ProwJob{ 1342 ObjectMeta: metav1.ObjectMeta{ 1343 Labels: map[string]string{ 1344 kube.GerritRevision: "abc", 1345 kube.ProwJobTypeLabel: presubmit, 1346 kube.GerritReportLabel: "label-foo", 1347 kube.OrgLabel: "org", 1348 kube.RepoLabel: "repo", 1349 kube.PullLabel: "0", 1350 }, 1351 Annotations: map[string]string{ 1352 kube.GerritID: "123-abc", 1353 kube.GerritInstance: "gerrit", 1354 }, 1355 CreationTimestamp: metav1.Time{ 1356 Time: timeNow.Add(-2 * time.Minute), 1357 }, 1358 Name: "ci-foo", 1359 Namespace: "test-pods", 1360 }, 1361 Status: v1.ProwJobStatus{ 1362 State: v1.SuccessState, 1363 URL: "guber/foo", 1364 }, 1365 Spec: v1.ProwJobSpec{ 1366 Refs: &v1.Refs{ 1367 Repo: "foo", 1368 Pulls: []v1.Pull{ 1369 { 1370 Number: 0, 1371 }, 1372 }, 1373 }, 1374 Job: "ci-foo", 1375 Report: true, 1376 }, 1377 }, 1378 existingPJs: []*v1.ProwJob{ 1379 { 1380 ObjectMeta: metav1.ObjectMeta{ 1381 Labels: map[string]string{ 1382 kube.GerritRevision: "abc", 1383 kube.ProwJobTypeLabel: presubmit, 1384 kube.GerritReportLabel: "label-foo", 1385 kube.OrgLabel: "org", 1386 kube.RepoLabel: "repo", 1387 kube.PullLabel: "0", 1388 }, 1389 Annotations: map[string]string{ 1390 kube.GerritID: "123-abc", 1391 kube.GerritInstance: "gerrit", 1392 }, 1393 CreationTimestamp: metav1.Time{ 1394 Time: timeNow.Add(-time.Minute), 1395 }, 1396 Name: "ci-foo", 1397 Namespace: "test-pods", 1398 }, 1399 Status: v1.ProwJobStatus{ 1400 PrevReportStates: map[string]v1.ProwJobState{ 1401 "gerrit-reporter": v1.FailureState, 1402 }, 1403 State: v1.FailureState, 1404 URL: "guber/foo", 1405 }, 1406 Spec: v1.ProwJobSpec{ 1407 Refs: &v1.Refs{ 1408 Repo: "foo", 1409 Pulls: []v1.Pull{ 1410 { 1411 Number: 0, 1412 }, 1413 }, 1414 }, 1415 Job: "ci-foo", 1416 Report: true, 1417 }, 1418 }, 1419 }, 1420 }, 1421 { 1422 name: "2 jobs, one SUCCESS one pending, different label, should report by itself", 1423 pj: &v1.ProwJob{ 1424 ObjectMeta: metav1.ObjectMeta{ 1425 Labels: map[string]string{ 1426 kube.GerritRevision: "abc", 1427 kube.ProwJobTypeLabel: presubmit, 1428 kube.GerritReportLabel: "label-foo", 1429 }, 1430 Annotations: map[string]string{ 1431 kube.GerritID: "123-abc", 1432 kube.GerritInstance: "gerrit", 1433 }, 1434 Name: "ci-foo", 1435 Namespace: "test-pods", 1436 }, 1437 Status: v1.ProwJobStatus{ 1438 State: v1.SuccessState, 1439 URL: "guber/foo", 1440 }, 1441 Spec: v1.ProwJobSpec{ 1442 Refs: &v1.Refs{ 1443 Repo: "foo", 1444 Pulls: []v1.Pull{ 1445 { 1446 Number: 0, 1447 }, 1448 }, 1449 }, 1450 Job: "ci-foo", 1451 Report: true, 1452 }, 1453 }, 1454 existingPJs: []*v1.ProwJob{ 1455 { 1456 ObjectMeta: metav1.ObjectMeta{ 1457 Labels: map[string]string{ 1458 kube.GerritRevision: "abc", 1459 kube.ProwJobTypeLabel: presubmit, 1460 kube.GerritReportLabel: "label-bar", 1461 }, 1462 Annotations: map[string]string{ 1463 kube.GerritID: "123-abc", 1464 kube.GerritInstance: "gerrit", 1465 }, 1466 Name: "ci-bar", 1467 Namespace: "test-pods", 1468 }, 1469 Status: v1.ProwJobStatus{ 1470 State: v1.PendingState, 1471 URL: "guber/bar", 1472 }, 1473 Spec: v1.ProwJobSpec{ 1474 Refs: &v1.Refs{ 1475 Repo: "bar", 1476 Pulls: []v1.Pull{ 1477 { 1478 Number: 0, 1479 }, 1480 }, 1481 }, 1482 Job: "ci-bar", 1483 Report: true, 1484 }, 1485 }, 1486 }, 1487 expectReport: true, 1488 reportInclude: []string{"1 out of 1", "ci-foo", "SUCCESS", "guber/foo"}, 1489 expectLabel: map[string]string{"label-foo": lgtm}, 1490 numExpectedReport: 0, 1491 }, 1492 { 1493 name: "2 jobs, both failed, already reported, same label, retrigger one and passed, should report both and not lgtm", 1494 pj: &v1.ProwJob{ 1495 ObjectMeta: metav1.ObjectMeta{ 1496 Labels: map[string]string{ 1497 kube.GerritRevision: "abc", 1498 kube.ProwJobTypeLabel: presubmit, 1499 kube.GerritReportLabel: "same-label", 1500 }, 1501 Annotations: map[string]string{ 1502 kube.GerritID: "123-abc", 1503 kube.GerritInstance: "gerrit", 1504 }, 1505 CreationTimestamp: metav1.Time{ 1506 Time: timeNow, 1507 }, 1508 Name: "ci-foo", 1509 Namespace: "test-pods", 1510 }, 1511 Status: v1.ProwJobStatus{ 1512 State: v1.SuccessState, 1513 URL: "guber/foo", 1514 }, 1515 Spec: v1.ProwJobSpec{ 1516 Type: v1.PresubmitJob, 1517 Refs: &v1.Refs{ 1518 Repo: "foo", 1519 Pulls: []v1.Pull{ 1520 { 1521 Number: 0, 1522 }, 1523 }, 1524 }, 1525 Job: "ci-foo", 1526 Report: true, 1527 }, 1528 }, 1529 existingPJs: []*v1.ProwJob{ 1530 { 1531 ObjectMeta: metav1.ObjectMeta{ 1532 Labels: map[string]string{ 1533 kube.GerritRevision: "abc", 1534 kube.ProwJobTypeLabel: presubmit, 1535 kube.GerritReportLabel: "same-label", 1536 }, 1537 Annotations: map[string]string{ 1538 kube.GerritID: "123-abc", 1539 kube.GerritInstance: "gerrit", 1540 }, 1541 CreationTimestamp: metav1.Time{ 1542 Time: timeNow.Add(-time.Hour), 1543 }, 1544 Name: "ci-bar", 1545 Namespace: "test-pods", 1546 }, 1547 Status: v1.ProwJobStatus{ 1548 State: v1.FailureState, 1549 URL: "guber/bar", 1550 PrevReportStates: map[string]v1.ProwJobState{ 1551 "gerrit-reporter": v1.FailureState, 1552 }, 1553 }, 1554 Spec: v1.ProwJobSpec{ 1555 Refs: &v1.Refs{ 1556 Repo: "bar", 1557 Pulls: []v1.Pull{ 1558 { 1559 Number: 0, 1560 }, 1561 }, 1562 }, 1563 Job: "ci-bar", 1564 Type: v1.PresubmitJob, 1565 Report: true, 1566 }, 1567 }, 1568 { 1569 ObjectMeta: metav1.ObjectMeta{ 1570 Labels: map[string]string{ 1571 kube.GerritRevision: "abc", 1572 kube.ProwJobTypeLabel: presubmit, 1573 kube.GerritReportLabel: "same-label", 1574 }, 1575 Annotations: map[string]string{ 1576 kube.GerritID: "123-abc", 1577 kube.GerritInstance: "gerrit", 1578 }, 1579 CreationTimestamp: metav1.Time{ 1580 Time: timeNow.Add(-time.Hour), 1581 }, 1582 Name: "ci-foo", 1583 Namespace: "test-pods", 1584 }, 1585 Status: v1.ProwJobStatus{ 1586 State: v1.FailureState, 1587 URL: "guber/foo", 1588 PrevReportStates: map[string]v1.ProwJobState{ 1589 "gerrit-reporter": v1.FailureState, 1590 }, 1591 }, 1592 Spec: v1.ProwJobSpec{ 1593 Refs: &v1.Refs{ 1594 Repo: "foo", 1595 Pulls: []v1.Pull{ 1596 { 1597 Number: 0, 1598 }, 1599 }, 1600 }, 1601 Job: "ci-foo", 1602 Type: v1.PresubmitJob, 1603 Report: true, 1604 }, 1605 }, 1606 }, 1607 expectReport: true, 1608 reportInclude: []string{"1 out of 2", "ci-foo", "SUCCESS", "ci-bar", "FAILURE", "guber/foo", "guber/bar", "Comment `/retest`"}, 1609 expectLabel: map[string]string{"same-label": lbtm}, 1610 numExpectedReport: 0, 1611 }, 1612 { 1613 name: "2 jobs, both failed, job from newer patchset pending, should not report", 1614 pj: &v1.ProwJob{ 1615 ObjectMeta: metav1.ObjectMeta{ 1616 Labels: map[string]string{ 1617 kube.GerritRevision: "abc", 1618 kube.ProwJobTypeLabel: presubmit, 1619 kube.GerritReportLabel: "same-label", 1620 kube.GerritPatchset: "5", 1621 kube.OrgLabel: "same-org", 1622 kube.RepoLabel: "same-repo", 1623 kube.PullLabel: "123456", 1624 }, 1625 Annotations: map[string]string{ 1626 kube.GerritID: "123-abc", 1627 kube.GerritInstance: "gerrit", 1628 }, 1629 CreationTimestamp: metav1.Time{ 1630 Time: timeNow, 1631 }, 1632 }, 1633 Status: v1.ProwJobStatus{ 1634 State: v1.SuccessState, 1635 URL: "guber/foo", 1636 }, 1637 Spec: v1.ProwJobSpec{ 1638 Type: v1.PresubmitJob, 1639 Refs: &v1.Refs{ 1640 Repo: "foo", 1641 Pulls: []v1.Pull{ 1642 { 1643 Number: 0, 1644 }, 1645 }, 1646 }, 1647 Job: "ci-foo", 1648 Report: true, 1649 }, 1650 }, 1651 existingPJs: []*v1.ProwJob{ 1652 { 1653 ObjectMeta: metav1.ObjectMeta{ 1654 Labels: map[string]string{ 1655 kube.GerritRevision: "abc", 1656 kube.ProwJobTypeLabel: presubmit, 1657 kube.GerritReportLabel: "same-label", 1658 kube.GerritPatchset: "5", 1659 kube.OrgLabel: "same-org", 1660 kube.RepoLabel: "same-repo", 1661 kube.PullLabel: "123456", 1662 }, 1663 Annotations: map[string]string{ 1664 kube.GerritID: "123-abc", 1665 kube.GerritInstance: "gerrit", 1666 }, 1667 CreationTimestamp: metav1.Time{ 1668 Time: timeNow.Add(-time.Hour), 1669 }, 1670 }, 1671 Status: v1.ProwJobStatus{ 1672 State: v1.FailureState, 1673 URL: "guber/bar", 1674 PrevReportStates: map[string]v1.ProwJobState{ 1675 "gerrit-reporter": v1.FailureState, 1676 }, 1677 }, 1678 Spec: v1.ProwJobSpec{ 1679 Refs: &v1.Refs{ 1680 Repo: "bar", 1681 Pulls: []v1.Pull{ 1682 { 1683 Number: 0, 1684 }, 1685 }, 1686 }, 1687 Job: "ci-bar", 1688 Type: v1.PresubmitJob, 1689 Report: true, 1690 }, 1691 }, 1692 { 1693 ObjectMeta: metav1.ObjectMeta{ 1694 Labels: map[string]string{ 1695 kube.GerritRevision: "abc", 1696 kube.ProwJobTypeLabel: presubmit, 1697 kube.GerritReportLabel: "same-label", 1698 kube.GerritPatchset: "5", 1699 kube.OrgLabel: "same-org", 1700 kube.RepoLabel: "same-repo", 1701 kube.PullLabel: "123456", 1702 }, 1703 Annotations: map[string]string{ 1704 kube.GerritID: "123-abc", 1705 kube.GerritInstance: "gerrit", 1706 }, 1707 CreationTimestamp: metav1.Time{ 1708 Time: timeNow.Add(-time.Hour), 1709 }, 1710 }, 1711 Status: v1.ProwJobStatus{ 1712 State: v1.FailureState, 1713 URL: "guber/foo", 1714 PrevReportStates: map[string]v1.ProwJobState{ 1715 "gerrit-reporter": v1.FailureState, 1716 }, 1717 }, 1718 Spec: v1.ProwJobSpec{ 1719 Refs: &v1.Refs{ 1720 Repo: "foo", 1721 Pulls: []v1.Pull{ 1722 { 1723 Number: 0, 1724 }, 1725 }, 1726 }, 1727 Job: "ci-foo", 1728 Type: v1.PresubmitJob, 1729 Report: true, 1730 }, 1731 }, 1732 { 1733 ObjectMeta: metav1.ObjectMeta{ 1734 Labels: map[string]string{ 1735 kube.GerritRevision: "def", 1736 kube.ProwJobTypeLabel: presubmit, 1737 kube.GerritReportLabel: "same-label", 1738 kube.GerritPatchset: "6", 1739 kube.OrgLabel: "same-org", 1740 kube.RepoLabel: "same-repo", 1741 kube.PullLabel: "123456", 1742 }, 1743 Annotations: map[string]string{ 1744 kube.GerritID: "123-def", 1745 kube.GerritInstance: "gerrit", 1746 }, 1747 CreationTimestamp: metav1.Time{ 1748 Time: timeNow.Add(-time.Hour), 1749 }, 1750 }, 1751 Status: v1.ProwJobStatus{ 1752 State: v1.PendingState, 1753 URL: "guber/foo", 1754 }, 1755 Spec: v1.ProwJobSpec{ 1756 Refs: &v1.Refs{ 1757 Repo: "foo", 1758 Pulls: []v1.Pull{ 1759 { 1760 Number: 0, 1761 }, 1762 }, 1763 }, 1764 Job: "ci-foo", 1765 Type: v1.PresubmitJob, 1766 Report: true, 1767 }, 1768 }, 1769 }, 1770 expectReport: false, 1771 }, 1772 { 1773 name: "2 jobs, both failed, job from newer patchset failed, should not report", 1774 pj: &v1.ProwJob{ 1775 ObjectMeta: metav1.ObjectMeta{ 1776 Labels: map[string]string{ 1777 kube.GerritRevision: "abc", 1778 kube.ProwJobTypeLabel: presubmit, 1779 kube.GerritReportLabel: "same-label", 1780 kube.GerritPatchset: "5", 1781 kube.OrgLabel: "same-org", 1782 kube.RepoLabel: "same-repo", 1783 kube.PullLabel: "123456", 1784 }, 1785 Annotations: map[string]string{ 1786 kube.GerritID: "123-abc", 1787 kube.GerritInstance: "gerrit", 1788 }, 1789 CreationTimestamp: metav1.Time{ 1790 Time: timeNow, 1791 }, 1792 }, 1793 Status: v1.ProwJobStatus{ 1794 State: v1.SuccessState, 1795 URL: "guber/foo", 1796 }, 1797 Spec: v1.ProwJobSpec{ 1798 Type: v1.PresubmitJob, 1799 Refs: &v1.Refs{ 1800 Repo: "foo", 1801 Pulls: []v1.Pull{ 1802 { 1803 Number: 0, 1804 }, 1805 }, 1806 }, 1807 Job: "ci-foo", 1808 Report: true, 1809 }, 1810 }, 1811 existingPJs: []*v1.ProwJob{ 1812 { 1813 ObjectMeta: metav1.ObjectMeta{ 1814 Labels: map[string]string{ 1815 kube.GerritRevision: "abc", 1816 kube.ProwJobTypeLabel: presubmit, 1817 kube.GerritReportLabel: "same-label", 1818 kube.GerritPatchset: "5", 1819 kube.OrgLabel: "same-org", 1820 kube.RepoLabel: "same-repo", 1821 kube.PullLabel: "123456", 1822 }, 1823 Annotations: map[string]string{ 1824 kube.GerritID: "123-abc", 1825 kube.GerritInstance: "gerrit", 1826 }, 1827 CreationTimestamp: metav1.Time{ 1828 Time: timeNow.Add(-time.Hour), 1829 }, 1830 }, 1831 Status: v1.ProwJobStatus{ 1832 State: v1.FailureState, 1833 URL: "guber/bar", 1834 PrevReportStates: map[string]v1.ProwJobState{ 1835 "gerrit-reporter": v1.FailureState, 1836 }, 1837 }, 1838 Spec: v1.ProwJobSpec{ 1839 Refs: &v1.Refs{ 1840 Repo: "bar", 1841 Pulls: []v1.Pull{ 1842 { 1843 Number: 0, 1844 }, 1845 }, 1846 }, 1847 Job: "ci-bar", 1848 Type: v1.PresubmitJob, 1849 Report: true, 1850 }, 1851 }, 1852 { 1853 ObjectMeta: metav1.ObjectMeta{ 1854 Labels: map[string]string{ 1855 kube.GerritRevision: "abc", 1856 kube.ProwJobTypeLabel: presubmit, 1857 kube.GerritReportLabel: "same-label", 1858 kube.GerritPatchset: "5", 1859 kube.OrgLabel: "same-org", 1860 kube.RepoLabel: "same-repo", 1861 kube.PullLabel: "123456", 1862 }, 1863 Annotations: map[string]string{ 1864 kube.GerritID: "123-abc", 1865 kube.GerritInstance: "gerrit", 1866 }, 1867 CreationTimestamp: metav1.Time{ 1868 Time: timeNow.Add(-time.Hour), 1869 }, 1870 }, 1871 Status: v1.ProwJobStatus{ 1872 State: v1.FailureState, 1873 URL: "guber/foo", 1874 PrevReportStates: map[string]v1.ProwJobState{ 1875 "gerrit-reporter": v1.FailureState, 1876 }, 1877 }, 1878 Spec: v1.ProwJobSpec{ 1879 Refs: &v1.Refs{ 1880 Repo: "foo", 1881 Pulls: []v1.Pull{ 1882 { 1883 Number: 0, 1884 }, 1885 }, 1886 }, 1887 Job: "ci-foo", 1888 Type: v1.PresubmitJob, 1889 Report: true, 1890 }, 1891 }, 1892 { 1893 ObjectMeta: metav1.ObjectMeta{ 1894 Labels: map[string]string{ 1895 kube.GerritRevision: "def", 1896 kube.ProwJobTypeLabel: presubmit, 1897 kube.GerritReportLabel: "same-label", 1898 kube.GerritPatchset: "6", 1899 kube.OrgLabel: "same-org", 1900 kube.RepoLabel: "same-repo", 1901 kube.PullLabel: "123456", 1902 }, 1903 Annotations: map[string]string{ 1904 kube.GerritID: "123-def", 1905 kube.GerritInstance: "gerrit", 1906 }, 1907 CreationTimestamp: metav1.Time{ 1908 Time: timeNow.Add(-time.Hour), 1909 }, 1910 }, 1911 Status: v1.ProwJobStatus{ 1912 State: v1.FailureState, 1913 URL: "guber/foo", 1914 }, 1915 Spec: v1.ProwJobSpec{ 1916 Refs: &v1.Refs{ 1917 Repo: "foo", 1918 Pulls: []v1.Pull{ 1919 { 1920 Number: 0, 1921 }, 1922 }, 1923 }, 1924 Job: "ci-foo", 1925 Type: v1.PresubmitJob, 1926 Report: true, 1927 }, 1928 }, 1929 }, 1930 expectReport: false, 1931 }, 1932 { 1933 name: "1 job, failed after merge, should report with non negative vote", 1934 pj: &v1.ProwJob{ 1935 ObjectMeta: metav1.ObjectMeta{ 1936 Labels: map[string]string{ 1937 kube.GerritRevision: "abc", 1938 kube.ProwJobTypeLabel: presubmit, 1939 kube.GerritReportLabel: "Code-Review", 1940 }, 1941 Annotations: map[string]string{ 1942 kube.GerritID: "merged", 1943 kube.GerritInstance: "gerrit", 1944 }, 1945 Name: "ci-foo", 1946 Namespace: "test-pods", 1947 }, 1948 Status: v1.ProwJobStatus{ 1949 State: v1.FailureState, 1950 URL: "guber/foo", 1951 }, 1952 Spec: v1.ProwJobSpec{ 1953 Type: v1.PresubmitJob, 1954 Refs: &v1.Refs{ 1955 Repo: "foo", 1956 Pulls: []v1.Pull{ 1957 { 1958 Number: 0, 1959 }, 1960 }, 1961 }, 1962 Job: "ci-foo", 1963 Report: true, 1964 }, 1965 }, 1966 expectReport: true, 1967 reportInclude: []string{"0 out of 1", "ci-foo", "FAILURE", "guber/foo", "Comment `/retest`"}, 1968 expectLabel: map[string]string{codeReview: lztm}, 1969 numExpectedReport: 0, 1970 }, 1971 { 1972 name: "1 job, passed, should vote +1 even after merge", 1973 pj: &v1.ProwJob{ 1974 ObjectMeta: metav1.ObjectMeta{ 1975 Labels: map[string]string{ 1976 kube.GerritRevision: "abc", 1977 kube.ProwJobTypeLabel: presubmit, 1978 kube.GerritReportLabel: "Code-Review", 1979 }, 1980 Annotations: map[string]string{ 1981 kube.GerritID: "merged", 1982 kube.GerritInstance: "gerrit", 1983 }, 1984 Name: "ci-foo", 1985 Namespace: "test-pods", 1986 }, 1987 Status: v1.ProwJobStatus{ 1988 State: v1.SuccessState, 1989 URL: "guber/foo", 1990 }, 1991 Spec: v1.ProwJobSpec{ 1992 Refs: &v1.Refs{ 1993 Repo: "foo", 1994 Pulls: []v1.Pull{ 1995 { 1996 Number: 0, 1997 }, 1998 }, 1999 }, 2000 Job: "ci-foo", 2001 Report: true, 2002 }, 2003 }, 2004 expectReport: true, 2005 reportInclude: []string{"1 out of 1", "ci-foo", "SUCCESS", "guber/foo"}, 2006 expectLabel: map[string]string{codeReview: lgtm}, 2007 numExpectedReport: 0, 2008 }, 2009 } 2010 2011 for _, tc := range testcases { 2012 t.Run(tc.name, func(t *testing.T) { 2013 fgc := &fgc{instance: "gerrit", changes: changes} 2014 2015 builder := fakectrlruntimeclient.NewClientBuilder().WithRuntimeObjects(tc.pj) 2016 for idx, pj := range tc.existingPJs { 2017 pj.Name = strconv.Itoa(idx) 2018 builder.WithRuntimeObjects(pj) 2019 } 2020 2021 reporter := &Client{ 2022 gc: fgc, 2023 pjclientset: builder.Build(), 2024 prLocks: criercommonlib.NewShardedLock(), 2025 } 2026 2027 shouldReport := reporter.ShouldReport(context.Background(), logrus.NewEntry(logrus.StandardLogger()), tc.pj) 2028 if shouldReport != tc.expectReport { 2029 t.Errorf("shouldReport: %v, expectReport: %v", shouldReport, tc.expectReport) 2030 } 2031 2032 if !shouldReport { 2033 return 2034 } 2035 2036 reportedJobs, _, err := reporter.Report(context.Background(), logrus.NewEntry(logrus.StandardLogger()), tc.pj) 2037 if err != nil { 2038 if !tc.expectError { 2039 t.Errorf("Unexpected error: %v", err) 2040 } 2041 // if this error is expected then no need to verify anything 2042 // later 2043 return 2044 } 2045 2046 for _, include := range tc.reportInclude { 2047 if !strings.Contains(fgc.reportMessage, include) { 2048 t.Errorf("message: got %q, does not contain %s", fgc.reportMessage, include) 2049 } 2050 } 2051 for _, exclude := range tc.reportExclude { 2052 if strings.Contains(fgc.reportMessage, exclude) { 2053 t.Errorf("message: got %q, unexpectedly contains %s", fgc.reportMessage, exclude) 2054 } 2055 } 2056 2057 if !reflect.DeepEqual(tc.expectLabel, fgc.reportLabel) { 2058 t.Errorf("labels: got %v, want %v", fgc.reportLabel, tc.expectLabel) 2059 } 2060 if len(reportedJobs) != tc.numExpectedReport { 2061 t.Errorf("report count: got %d, want %d", len(reportedJobs), tc.numExpectedReport) 2062 } 2063 }) 2064 } 2065 } 2066 2067 func TestMultipleWorks(t *testing.T) { 2068 samplePJ := v1.ProwJob{ 2069 ObjectMeta: metav1.ObjectMeta{ 2070 Labels: map[string]string{ 2071 kube.GerritRevision: "abc", 2072 kube.ProwJobTypeLabel: presubmit, 2073 kube.GerritReportLabel: "same-label", 2074 kube.GerritPatchset: "5", 2075 kube.OrgLabel: "same-org", 2076 kube.RepoLabel: "same-repo", 2077 kube.PullLabel: "123456", 2078 }, 2079 Annotations: map[string]string{ 2080 kube.GerritID: "123-abc", 2081 kube.GerritInstance: "gerrit", 2082 }, 2083 CreationTimestamp: metav1.Time{ 2084 Time: timeNow.Add(-time.Hour), 2085 }, 2086 }, 2087 Status: v1.ProwJobStatus{ 2088 State: v1.FailureState, 2089 URL: "guber/bar", 2090 }, 2091 Spec: v1.ProwJobSpec{ 2092 Refs: &v1.Refs{ 2093 Repo: "bar", 2094 Pulls: []v1.Pull{ 2095 { 2096 Number: 0, 2097 }, 2098 }, 2099 }, 2100 Job: "ci-bar", 2101 Type: v1.PresubmitJob, 2102 Report: true, 2103 }, 2104 } 2105 2106 // Running with 3 different batches to increase the chance of hitting races 2107 for _, count := range []int{10, 20, 30} { 2108 t.Run(fmt.Sprintf("%d-jobs", count), func(t *testing.T) { 2109 expectedCount := 1 2110 expectedComment := []string{" out of " + strconv.Itoa(count), "ci-bar", "FAILURE", "guber/bar", "Comment `/retest`"} 2111 var existingPJs []*v1.ProwJob 2112 for i := 0; i < count; i++ { 2113 pj := samplePJ.DeepCopy() 2114 pj.Spec.Job += strconv.Itoa(i) 2115 if i%2 == 0 { 2116 pj.Status.State = v1.SuccessState 2117 } 2118 existingPJs = append(existingPJs, pj) 2119 } 2120 2121 changes := map[string][]*gerrit.ChangeInfo{ 2122 "gerrit": { 2123 {ID: "123-abc", Status: "NEW", Revisions: map[string]gerrit.RevisionInfo{"abc": {}}}, 2124 }, 2125 } 2126 2127 fgc := &fgc{instance: "gerrit", changes: changes} 2128 2129 builder := fakectrlruntimeclient.NewClientBuilder() 2130 for idx, pj := range existingPJs { 2131 pj.Name = strconv.Itoa(idx) 2132 builder.WithRuntimeObjects(pj) 2133 } 2134 2135 reporter := &Client{ 2136 gc: fgc, 2137 pjclientset: builder.Build(), 2138 prLocks: criercommonlib.NewShardedLock(), 2139 } 2140 2141 g := new(errgroup.Group) 2142 resChan := make(chan []*v1.ProwJob, count) 2143 for _, pj := range existingPJs { 2144 pj := pj.DeepCopy() 2145 g.Go(func() error { 2146 toReportJobs, _, err := reporter.Report(context.Background(), logrus.NewEntry(logrus.StandardLogger()), pj) 2147 if err != nil { 2148 return err 2149 } 2150 resChan <- toReportJobs 2151 return nil 2152 }) 2153 } 2154 2155 if err := g.Wait(); err != nil { 2156 t.Fatalf("Unexpected error: %v", err) 2157 } 2158 if expectedCount != fgc.count { 2159 t.Fatalf("Expect comment count: %d, got: %d", expectedCount, fgc.count) 2160 } 2161 for _, expect := range expectedComment { 2162 if !strings.Contains(fgc.reportMessage, expect) { 2163 t.Fatalf("Expect comment contains %q, got: %q", expect, fgc.reportMessage) 2164 } 2165 } 2166 2167 var reported bool 2168 for i := 0; i < count; i++ { 2169 toReportJobs := <-resChan 2170 if reported && len(toReportJobs) > 0 { 2171 t.Fatalf("These jobs were already reported, should omit reporting again.") 2172 } 2173 if len(toReportJobs) > 0 { 2174 reported = true 2175 } 2176 } 2177 2178 // Ensure that the statues were reported 2179 var pjs v1.ProwJobList 2180 if err := reporter.pjclientset.List(context.Background(), &pjs); err != nil { 2181 t.Fatalf("Failed listing prowjobs: %v", err) 2182 } 2183 if want, got := count, len(pjs.Items); want != got { 2184 t.Fatalf("Number of prowjobs mismatch. Want: %d, got: %d", want, got) 2185 } 2186 for _, pj := range pjs.Items { 2187 if pj.Status.PrevReportStates == nil { 2188 t.Fatalf("PrevReportStates should have been set") 2189 } 2190 if _, ok := pj.Status.PrevReportStates["gerrit-reporter"]; !ok { 2191 t.Fatalf("PrevReportStates should have been set. Got: %v", pj.Status.PrevReportStates) 2192 } 2193 } 2194 }) 2195 } 2196 } 2197 2198 func TestJobReportFormats(t *testing.T) { 2199 tests := []struct { 2200 name string 2201 format string 2202 words []interface{} 2203 formatRegex string 2204 }{ 2205 {"jobReportFormat", jobReportFormat, []interface{}{"a", "b", "c", "d"}, jobReportFormatRegex}, 2206 {"jobReportFormatUrlNotFound", jobReportFormatUrlNotFound, []interface{}{"a", "b", "c"}, jobReportFormatUrlNotFoundRegex}, 2207 {"jobReportFormatWithoutURL", jobReportFormatWithoutURL, []interface{}{"a", "b", "c"}, jobReportFormatWithoutURLRegex}, 2208 } 2209 2210 for _, tc := range tests { 2211 t.Run(tc.name, func(t *testing.T) { 2212 // In GenerateReport(), we use a trailing newline in the 2213 // jobReportFormat* constants, because we use a newline as a 2214 // delimiter. In ParseReport(), we split the overall report on 2215 // newlines first, before applying the jobReportFormat*Regex 2216 // regexes on them. To mimic this behavior, we trim the newline 2217 // before attempting to parse them with tc.formatRegex. 2218 serialized := fmt.Sprintf(tc.format, tc.words...) 2219 serializedWithoutNewline := strings.TrimSuffix(serialized, "\n") 2220 re := regexp.MustCompile(tc.formatRegex) 2221 if !re.MatchString(serializedWithoutNewline) { 2222 t.Fatalf("could not parse serialized job report line %q with regex %q", serializedWithoutNewline, tc.formatRegex) 2223 } 2224 }) 2225 } 2226 2227 // Ensure the legacy job reporting format can be parsed by 2228 // jobReportFormatLegacyRegex. 2229 serializedWithoutNewline := "āļø some-job SUCCESS - https://someURL.com/somewhere" 2230 re := regexp.MustCompile(jobReportFormatLegacyRegex) 2231 if !re.MatchString(serializedWithoutNewline) { 2232 t.Fatalf("could not parse serialized job report line %q with regex %q", serializedWithoutNewline, jobReportFormatLegacyRegex) 2233 } 2234 } 2235 2236 func TestGenerateReport(t *testing.T) { 2237 job := func(name, url string, state v1.ProwJobState, createdByTide bool) *v1.ProwJob { 2238 var out v1.ProwJob 2239 out.Spec.Job = name 2240 out.Status.URL = url 2241 out.Status.State = state 2242 out.Labels = make(map[string]string) 2243 if createdByTide { 2244 out.Labels[kube.CreatedByTideLabel] = "true" 2245 } 2246 return &out 2247 } 2248 2249 tests := []struct { 2250 name string 2251 jobs []*v1.ProwJob 2252 commentSizeLimit int 2253 wantHeader string 2254 wantMessage string 2255 }{ 2256 { 2257 name: "basic", 2258 jobs: []*v1.ProwJob{ 2259 job("this", "url", v1.SuccessState, false), 2260 job("that", "hey", v1.FailureState, false), 2261 job("left", "foo", v1.AbortedState, false), 2262 job("right", "bar", v1.ErrorState, false), 2263 }, 2264 wantHeader: "Prow Status: 1 out of 4 pjs passed! š Comment `/retest` to rerun only failed tests (if any), or `/test all` to rerun all tests.\n", 2265 wantMessage: "ā [that](hey) FAILURE\nš« [right](bar) ERROR\nš« [left](foo) ABORTED\nāļø [this](url) SUCCESS\n", 2266 }, 2267 { 2268 name: "include-tide-jobs", 2269 jobs: []*v1.ProwJob{ 2270 job("this", "url", v1.SuccessState, true), 2271 job("that", "hey", v1.FailureState, false), 2272 job("left", "foo", v1.AbortedState, false), 2273 job("right", "bar", v1.ErrorState, false), 2274 }, 2275 wantHeader: "Prow Status: 1 out of 4 pjs passed! š Comment `/retest` to rerun only failed tests (if any), or `/test all` to rerun all tests. (Not a duplicated report. Some of the jobs below were triggered by Tide)\n", 2276 wantMessage: "ā [that](hey) FAILURE\nš« [right](bar) ERROR\nš« [left](foo) ABORTED\nāļø [this](url) SUCCESS\n", 2277 }, 2278 { 2279 name: "short lines only", 2280 jobs: []*v1.ProwJob{ 2281 job("this", "url", v1.SuccessState, false), 2282 job("that", "hey", v1.FailureState, false), 2283 job("some", "other", v1.SuccessState, false), 2284 }, 2285 // 131 is the length of the Header. 2286 // 154 is the comment size room we give for the Message part. Note 2287 // that it should be 1 char more than what we have in the 2288 // wantMessage part, because we always return comments *under* the 2289 // commentSizeLimit. 2290 commentSizeLimit: 131 + 154, 2291 wantHeader: "Prow Status: 2 out of 3 pjs passed! š Comment `/retest` to rerun only failed tests (if any), or `/test all` to rerun all tests.\n", 2292 wantMessage: "ā that FAILURE\nāļø some SUCCESS\nāļø this SUCCESS\n[NOTE FROM PROW: Skipped displaying URLs for 3/3 jobs due to reaching gerrit comment size limit]", 2293 }, 2294 { 2295 name: "mix of short and long lines", 2296 jobs: []*v1.ProwJob{ 2297 job("this", "url", v1.SuccessState, false), 2298 job("that", "hey", v1.FailureState, false), 2299 job("some", "other", v1.SuccessState, false), 2300 }, 2301 commentSizeLimit: 131 + 161, 2302 wantHeader: "Prow Status: 2 out of 3 pjs passed! š Comment `/retest` to rerun only failed tests (if any), or `/test all` to rerun all tests.\n", 2303 wantMessage: "ā [that](hey) FAILURE\nāļø some SUCCESS\nāļø this SUCCESS\n[NOTE FROM PROW: Skipped displaying URLs for 2/3 jobs due to reaching gerrit comment size limit]", 2304 }, 2305 { 2306 name: "too many jobs", 2307 jobs: []*v1.ProwJob{ 2308 job("this", "url", v1.SuccessState, false), 2309 job("that", "hey", v1.FailureState, false), 2310 job("some", "other", v1.SuccessState, false), 2311 }, 2312 commentSizeLimit: 1, 2313 wantHeader: "Prow Status: 2 out of 3 pjs passed! š Comment `/retest` to rerun only failed tests (if any), or `/test all` to rerun all tests.\n", 2314 wantMessage: "[NOTE FROM PROW: Skipped displaying 3/3 jobs due to reaching gerrit comment size limit (too many jobs)]", 2315 }, 2316 { 2317 name: "too many jobs; only truncate the last job", 2318 jobs: []*v1.ProwJob{ 2319 job("this", "url", v1.SuccessState, false), 2320 job("that", "hey", v1.FailureState, false), 2321 job("some", "other", v1.SuccessState, false), 2322 }, 2323 commentSizeLimit: 130 + 150, 2324 wantHeader: "Prow Status: 2 out of 3 pjs passed! š Comment `/retest` to rerun only failed tests (if any), or `/test all` to rerun all tests.\n", 2325 wantMessage: "ā that FAILURE\nāļø some SUCCESS\n[NOTE FROM PROW: Skipped displaying 1/3 jobs due to reaching gerrit comment size limit (too many jobs)]", 2326 }, 2327 { 2328 // Check cases where the job could legitimately not have its URL 2329 // field set (because the job did not even get scheduled). 2330 name: "missing URLs", 2331 jobs: []*v1.ProwJob{ 2332 job("right", "", v1.ErrorState, false), 2333 }, 2334 commentSizeLimit: 1000, 2335 wantHeader: "Prow Status: 0 out of 1 pjs passed! š Comment `/retest` to rerun only failed tests (if any), or `/test all` to rerun all tests.\n", 2336 wantMessage: "š« right (URL_NOT_FOUND) ERROR\n", 2337 }, 2338 } 2339 2340 for _, tc := range tests { 2341 t.Run(tc.name, func(t *testing.T) { 2342 gotReport := GenerateReport(tc.jobs, tc.commentSizeLimit) 2343 if want, got := tc.wantHeader, gotReport.Header; want != got { 2344 t.Fatalf("Header mismatch. Want:\n%s,\ngot: \n%s", want, got) 2345 } 2346 if want, got := tc.wantMessage, gotReport.Message; want != got { 2347 t.Fatalf("Message mismatch. Want:\n%s\ngot: \n%s", want, got) 2348 } 2349 }) 2350 } 2351 } 2352 2353 func TestParseReport(t *testing.T) { 2354 var testcases = []struct { 2355 name string 2356 comment string 2357 expectedJobs int 2358 expectNil bool 2359 }{ 2360 // These tests all test the legacy format. 2361 { 2362 name: "parse multiple jobs", 2363 comment: "Prow Status: 0 out of 2 passed\nāļø foo-job FAILURE - http://foo-status\nā bar-job FAILURE - http://bar-status", 2364 expectedJobs: 2, 2365 }, 2366 { 2367 name: "parse job without URL", 2368 comment: "Prow Status: 0 out of 2 passed\nāļø foo-job FAILURE\nā bar-job FAILURE", 2369 expectedJobs: 2, 2370 }, 2371 { 2372 name: "parse mixed formats", 2373 comment: "Prow Status: 0 out of 2 passed\nāļø foo-job FAILURE - http://foo-status\nā bar-job FAILURE\n[Skipped displaying URLs for 1/2 jobs due to reaching gerrit comment size limit]", 2374 expectedJobs: 2, 2375 }, 2376 { 2377 name: "parse one job", 2378 comment: "Prow Status: 0 out of 1 passed\nā bar-job FAILURE - http://bar-status", 2379 expectedJobs: 1, 2380 }, 2381 { 2382 name: "parse 0 jobs", 2383 comment: "Prow Status: ", 2384 expectedJobs: 0, 2385 }, 2386 { 2387 name: "do not parse without the header", 2388 comment: "0 out of 1 passed\nā bar-job FAILURE - http://bar-status", 2389 expectNil: true, 2390 }, 2391 { 2392 name: "do not parse empty string", 2393 comment: "", 2394 expectNil: true, 2395 }, 2396 { 2397 name: "parse with extra stuff at the start as long as the header and jobs start on new lines", 2398 comment: `qwerty 2399 Patch Set 1: 2400 Prow Status: 0 out of 2 pjs passed! 2401 ā foo-job FAILURE - https://foo-status 2402 ā bar-job FAILURE - https://bar-status 2403 `, 2404 expectedJobs: 2, 2405 }, 2406 // New Markdown format (link uses Markdown syntax). 2407 { 2408 name: "parse multiple jobs (Markdown)", 2409 comment: "Prow Status: 0 out of 2 passed\nāļø [foo-job](http://foo-status) FAILURE\nā [bar-job](http://bar-status) FAILURE", 2410 expectedJobs: 2, 2411 }, 2412 { 2413 name: "parse mixed formats (Markdown)", 2414 comment: "Prow Status: 0 out of 2 passed\nāļø [foo-job](http://foo-status) FAILURE\nā bar-job FAILURE\n[Skipped displaying URLs for 1/2 jobs due to reaching gerrit comment size limit]", 2415 expectedJobs: 2, 2416 }, 2417 { 2418 name: "parse one job (Markdown)", 2419 comment: "Prow Status: 0 out of 1 passed\nā [bar-job](http://bar-status) FAILURE", 2420 expectedJobs: 1, 2421 }, 2422 { 2423 name: "do not parse without the header (Markdown)", 2424 comment: "0 out of 1 passed\nā [bar-job](http://bar-status) FAILURE", 2425 expectNil: true, 2426 }, 2427 { 2428 name: "parse with extra stuff at the start as long as the header and jobs start on new lines (Markdown)", 2429 comment: `qwerty 2430 Patch Set 1: 2431 Prow Status: 0 out of 2 pjs passed! 2432 ā [foo-job](https://foo-status) FAILURE 2433 ā [bar-job](https://bar-status) FAILURE 2434 `, 2435 expectedJobs: 2, 2436 }, 2437 { 2438 name: "invalid job state (Markdown)", 2439 comment: "Prow Status: 0 out of 1 passed\nā [bar-job](http://bar-status) BANANAS", 2440 expectedJobs: 0, 2441 }, 2442 } 2443 for _, tc := range testcases { 2444 report := ParseReport(tc.comment) 2445 if report == nil { 2446 if !tc.expectNil { 2447 t.Errorf("%s: expected non-nil report but got nil", tc.name) 2448 } 2449 } else { 2450 if tc.expectNil { 2451 t.Errorf("%s: expected nil report but got %v", tc.name, report) 2452 } else if tc.expectedJobs != len(report.Jobs) { 2453 t.Errorf("%s: expected %d jobs in the report but got %d instead", tc.name, tc.expectedJobs, len(report.Jobs)) 2454 } 2455 } 2456 } 2457 2458 } 2459 2460 // TestReportStability ensures a generated report's string parses to the same report 2461 func TestReportStability(t *testing.T) { 2462 job := func(name, url string, state v1.ProwJobState) *v1.ProwJob { 2463 var out v1.ProwJob 2464 out.Spec.Job = name 2465 out.Status.URL = url 2466 out.Status.State = state 2467 return &out 2468 } 2469 expected := GenerateReport([]*v1.ProwJob{ 2470 job("this", "hey", v1.SuccessState), 2471 job("that", "url", v1.FailureState), 2472 }, 0) 2473 actual := ParseReport(expected.String()) 2474 if !equality.Semantic.DeepEqual(&expected, actual) { 2475 t.Errorf(diff.ObjectReflectDiff(&expected, actual)) 2476 } 2477 }