github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/plugins/projectmanager/projectmanager_test.go (about) 1 /* 2 Copyright 2019 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 projectmanager 18 19 import ( 20 "fmt" 21 "testing" 22 23 "github.com/sirupsen/logrus" 24 "sigs.k8s.io/prow/pkg/config" 25 "sigs.k8s.io/prow/pkg/github" 26 "sigs.k8s.io/prow/pkg/github/fakegithub" 27 "sigs.k8s.io/prow/pkg/plugins" 28 ) 29 30 func TestHandlePR(t *testing.T) { 31 columnID := new(int) 32 *columnID = 1 33 columnIDMap := map[string]map[int]string{ 34 "testProject": { 35 00001: "testColumn", 36 00002: "testColumn2", 37 }, 38 "testProject2": { 39 00003: "testColumn", 40 00004: "testColumn2", 41 }, 42 } 43 ie := github.IssueEvent{ 44 Action: github.IssueActionOpened, 45 Issue: github.Issue{ 46 ID: 2, 47 State: "open", 48 Labels: []github.Label{{Name: "label1"}, {Name: "label2"}}, 49 }, 50 Repo: github.Repo{ 51 Name: "someRepo", 52 Owner: github.User{ 53 Login: "otherOrg", 54 }, 55 }, 56 } 57 ie2 := ie 58 ie2.Repo.Owner.Login = "otherOrg2" 59 // pe belongs to otherOrg/somerepo and has labels 'label1' and 'label2' 60 // this pe should land in testproject under column testColumn2 61 pe := github.IssueEvent{ 62 Action: github.IssueActionOpened, 63 Issue: github.Issue{ 64 ID: 2, 65 State: "open", 66 Labels: []github.Label{{Name: "label1"}, {Name: "label2"}}, 67 PullRequest: &struct{}{}, 68 }, 69 Repo: github.Repo{ 70 Name: "someRepo", 71 Owner: github.User{ 72 Login: "otherOrg", 73 }, 74 }, 75 } 76 pe2 := pe 77 pe2.Repo.Owner.Login = "otherOrg2" 78 // all issues/PRs will be automatically be populated by these labels depending on which org they belong 79 labels := []string{"otherOrg/someRepo#1:label1", "otherOrg/someRepo#1:label2", "otherOrg/someRepo#2:label1", "otherOrg2/someRepo#1:label1"} 80 cases := []struct { 81 name string 82 gc *fakegithub.FakeClient 83 projectManager plugins.ProjectManager 84 pe github.IssueEvent 85 ie github.IssueEvent 86 expectedColumnCards map[int][]github.ProjectCard 87 expectedError error 88 }{ 89 { 90 name: "add Issue/PR to project column with no columnID", 91 gc: &fakegithub.FakeClient{ 92 IssueLabelsAdded: labels, 93 IssueLabelsRemoved: []string{}, 94 RepoProjects: map[string][]github.Project{ 95 "testOrg/testRepo": { 96 { 97 Name: "testProject", 98 ID: 1, 99 }, 100 }, 101 }, 102 OrgProjects: map[string][]github.Project{}, 103 ProjectColumnsMap: map[string][]github.ProjectColumn{ 104 "testProject": { 105 { 106 Name: "testColumn2", 107 ID: 2, 108 }, 109 { 110 Name: "testColumn", 111 ID: 1, 112 }, 113 }, 114 }, 115 ColumnCardsMap: map[int][]github.ProjectCard{}, 116 ColumnIDMap: columnIDMap, 117 }, 118 projectManager: plugins.ProjectManager{ 119 OrgRepos: map[string]plugins.ManagedOrgRepo{ 120 "testOrg/testRepo": { 121 Projects: map[string]plugins.ManagedProject{ 122 "testProject": { 123 Columns: []plugins.ManagedColumn{ 124 { 125 Name: "testColumn2", 126 State: "open", 127 Org: "otherOrg", 128 Labels: []string{"label1", "label2"}, 129 }, 130 }, 131 }, 132 }, 133 }, 134 }, 135 }, 136 pe: pe, 137 ie: ie, 138 expectedColumnCards: map[int][]github.ProjectCard{ 139 2: { 140 { 141 ContentID: 2, 142 }, 143 }, 144 }, 145 }, 146 { 147 name: "add Issue/PR to project column with only columnID", 148 gc: &fakegithub.FakeClient{ 149 IssueLabelsAdded: labels, 150 IssueLabelsRemoved: []string{}, 151 // Note that repoProjects and ProjectColumns are empty so the columnID cannot be looked up using repo, project and column names 152 // This means if the project_manager plugin is not using the columnID specified in the config this test will fail 153 RepoProjects: map[string][]github.Project{ 154 "testOrg/testRepo": { 155 { 156 Name: "testProject", 157 ID: 1, 158 }, 159 }, 160 }, 161 OrgProjects: map[string][]github.Project{}, 162 ProjectColumnsMap: map[string][]github.ProjectColumn{ 163 "testProject": { 164 { 165 Name: "testColumn2", 166 ID: 2, 167 }, 168 { 169 Name: "testColumn", 170 ID: 1, 171 }, 172 }, 173 }, 174 ColumnCardsMap: map[int][]github.ProjectCard{}, 175 ColumnIDMap: columnIDMap, 176 }, 177 projectManager: plugins.ProjectManager{ 178 OrgRepos: map[string]plugins.ManagedOrgRepo{ 179 "testOrg/testRepo": { 180 Projects: map[string]plugins.ManagedProject{ 181 "testProject": { 182 Columns: []plugins.ManagedColumn{ 183 { 184 ID: columnID, 185 State: "open", 186 Org: "otherOrg", 187 Labels: []string{"label1", "label2"}, 188 }, 189 }, 190 }, 191 }, 192 }, 193 }, 194 }, 195 pe: pe, 196 ie: ie, 197 expectedColumnCards: map[int][]github.ProjectCard{ 198 1: { 199 { 200 ContentID: 2, 201 }, 202 }, 203 }, 204 }, 205 { 206 name: "don't add Issue/PR with incorrect column name", 207 gc: &fakegithub.FakeClient{ 208 IssueLabelsAdded: labels, 209 IssueLabelsRemoved: []string{}, 210 RepoProjects: map[string][]github.Project{ 211 "testOrg/testRepo": { 212 { 213 Name: "testProject", 214 ID: 1, 215 }, 216 }, 217 }, 218 OrgProjects: map[string][]github.Project{}, 219 ProjectColumnsMap: map[string][]github.ProjectColumn{ 220 "testProject": { 221 { 222 Name: "testColumn2", 223 ID: 2, 224 }, 225 }, 226 "testProject2": { 227 { 228 Name: "testColumn2", 229 ID: 4, 230 }, 231 { 232 Name: "testColumn", 233 ID: 3, 234 }, 235 }, 236 }, 237 ColumnCardsMap: map[int][]github.ProjectCard{}, 238 }, 239 projectManager: plugins.ProjectManager{ 240 OrgRepos: map[string]plugins.ManagedOrgRepo{ 241 "testOrg/testRepo": { 242 Projects: map[string]plugins.ManagedProject{ 243 "testProject": { 244 Columns: []plugins.ManagedColumn{ 245 { 246 Name: "testColumn", 247 State: "open", 248 Org: "otherOrg", 249 Labels: []string{"label1", "label2"}, 250 }, 251 }, 252 }, 253 }, 254 }, 255 }, 256 }, 257 pe: pe, 258 ie: ie, 259 expectedColumnCards: map[int][]github.ProjectCard{}, 260 }, 261 { 262 name: "don't add Issue/PR if all the labels do not match", 263 gc: &fakegithub.FakeClient{ 264 IssueLabelsAdded: labels, 265 IssueLabelsRemoved: []string{}, 266 RepoProjects: map[string][]github.Project{ 267 "testOrg/testRepo": { 268 { 269 Name: "testProject", 270 ID: 1, 271 }, 272 }, 273 }, 274 OrgProjects: map[string][]github.Project{}, 275 ProjectColumnsMap: map[string][]github.ProjectColumn{ 276 "testProject": { 277 { 278 Name: "testColumn2", 279 ID: 2, 280 }, 281 }, 282 "testProject2": { 283 { 284 Name: "testColumn2", 285 ID: 4, 286 }, 287 { 288 Name: "testColumn", 289 ID: 3, 290 }, 291 }, 292 }, 293 ColumnCardsMap: map[int][]github.ProjectCard{}, 294 }, 295 projectManager: plugins.ProjectManager{ 296 OrgRepos: map[string]plugins.ManagedOrgRepo{ 297 "testOrg/testRepo": { 298 Projects: map[string]plugins.ManagedProject{ 299 "testProject": { 300 Columns: []plugins.ManagedColumn{ 301 { 302 Name: "testColumn", 303 State: "open", 304 Org: "otherOrg", 305 Labels: []string{"label1", "label2", "label3"}, 306 }, 307 }, 308 }, 309 }, 310 }, 311 }, 312 }, 313 pe: pe, 314 ie: ie, 315 expectedColumnCards: map[int][]github.ProjectCard{}, 316 }, 317 { 318 name: "add Issue/PR using column name in multiple repos", 319 gc: &fakegithub.FakeClient{ 320 IssueLabelsAdded: labels, 321 IssueLabelsRemoved: []string{}, 322 RepoProjects: map[string][]github.Project{ 323 "testOrg/testRepo": { 324 { 325 Name: "testProject", 326 ID: 1, 327 }, 328 }, 329 "testOrg/testRepo2": { 330 { 331 Name: "testProject2", 332 ID: 2, 333 }, 334 }, 335 }, 336 OrgProjects: map[string][]github.Project{}, 337 ProjectColumnsMap: map[string][]github.ProjectColumn{ 338 "testProject": { 339 { 340 Name: "testColumn2", 341 ID: 2, 342 }, 343 { 344 Name: "testColumn", 345 ID: 1, 346 }, 347 }, 348 "testProject2": { 349 { 350 Name: "testColumn2", 351 ID: 4, 352 }, 353 { 354 Name: "testColumn", 355 ID: 3, 356 }, 357 }, 358 }, 359 ColumnCardsMap: map[int][]github.ProjectCard{}, 360 ColumnIDMap: columnIDMap, 361 }, 362 projectManager: plugins.ProjectManager{ 363 OrgRepos: map[string]plugins.ManagedOrgRepo{ 364 "testOrg/testRepo": { 365 Projects: map[string]plugins.ManagedProject{ 366 "testProject": { 367 Columns: []plugins.ManagedColumn{ 368 { 369 Name: "testColumn", 370 State: "open", 371 Org: "otherOrg2", 372 Labels: []string{"label1"}, 373 }, 374 }, 375 }, 376 }, 377 }, 378 "testOrg/testRepo2": { 379 Projects: map[string]plugins.ManagedProject{ 380 "testProject2": { 381 Columns: []plugins.ManagedColumn{ 382 { 383 Name: "testColumn2", 384 State: "open", 385 Org: "otherOrg2", 386 Labels: []string{"label1"}, 387 }, 388 }, 389 }, 390 }, 391 }, 392 }, 393 }, 394 pe: pe2, 395 ie: ie2, 396 expectedColumnCards: map[int][]github.ProjectCard{ 397 4: { 398 { 399 ContentID: 2, 400 }, 401 }, 402 1: { 403 { 404 ContentID: 2, 405 }, 406 }, 407 }, 408 }, 409 { 410 name: "add Issue/PR using column name in multirepo to multiple projects", 411 gc: &fakegithub.FakeClient{ 412 IssueLabelsAdded: labels, 413 IssueLabelsRemoved: []string{}, 414 RepoProjects: map[string][]github.Project{ 415 "testOrg/testRepo": { 416 { 417 Name: "testProject", 418 ID: 1, 419 }, 420 }, 421 "testOrg/testRepo2": { 422 { 423 Name: "testProject2", 424 ID: 2, 425 }, 426 }, 427 }, 428 OrgProjects: map[string][]github.Project{}, 429 ProjectColumnsMap: map[string][]github.ProjectColumn{ 430 "testProject": { 431 { 432 Name: "testColumn2", 433 ID: 2, 434 }, 435 { 436 Name: "testColumn", 437 ID: 1, 438 }, 439 }, 440 "testProject2": { 441 { 442 Name: "testColumn2", 443 ID: 4, 444 }, 445 { 446 Name: "testColumn", 447 ID: 3, 448 }, 449 }, 450 }, 451 ColumnCardsMap: map[int][]github.ProjectCard{}, 452 ColumnIDMap: columnIDMap, 453 }, 454 projectManager: plugins.ProjectManager{ 455 OrgRepos: map[string]plugins.ManagedOrgRepo{ 456 "testOrg/testRepo": { 457 Projects: map[string]plugins.ManagedProject{ 458 "testProject": { 459 Columns: []plugins.ManagedColumn{ 460 { 461 Name: "testColumn", 462 State: "open", 463 Org: "otherOrg", 464 Labels: []string{"label1"}, 465 }, 466 }, 467 }, 468 }, 469 }, 470 "testOrg/testRepo2": { 471 Projects: map[string]plugins.ManagedProject{ 472 "testProject2": { 473 Columns: []plugins.ManagedColumn{ 474 { 475 Name: "testColumn2", 476 State: "open", 477 Org: "otherOrg", 478 Labels: []string{"label1", "label2"}, 479 }, 480 }, 481 }, 482 }, 483 }, 484 }, 485 }, 486 pe: pe, 487 ie: ie, 488 expectedColumnCards: map[int][]github.ProjectCard{ 489 4: { 490 { 491 ContentID: 2, 492 }, 493 }, 494 1: { 495 { 496 ContentID: 2, 497 }, 498 }, 499 }, 500 }, 501 { 502 name: "add Issue/PR to multiple columns in a project, should realize conflict", 503 gc: &fakegithub.FakeClient{ 504 IssueLabelsAdded: labels, 505 IssueLabelsRemoved: []string{}, 506 RepoProjects: map[string][]github.Project{ 507 "testOrg/testRepo": { 508 { 509 Name: "testProject", 510 ID: 1, 511 }, 512 }, 513 }, 514 OrgProjects: map[string][]github.Project{}, 515 ProjectColumnsMap: map[string][]github.ProjectColumn{ 516 "testProject": { 517 { 518 Name: "testColumn2", 519 ID: 2, 520 }, 521 { 522 Name: "testColumn", 523 ID: 1, 524 }, 525 }, 526 }, 527 ColumnCardsMap: map[int][]github.ProjectCard{1: { 528 { 529 ContentID: 2, 530 ContentURL: "https://api.github.com/repos/otherOrg/someRepo/issues/1", 531 }, 532 }}, 533 ColumnIDMap: columnIDMap, 534 }, 535 projectManager: plugins.ProjectManager{ 536 OrgRepos: map[string]plugins.ManagedOrgRepo{ 537 "testOrg/testRepo": { 538 Projects: map[string]plugins.ManagedProject{ 539 "testProject": { 540 Columns: []plugins.ManagedColumn{ 541 { 542 Name: "testColumn", 543 State: "open", 544 Org: "otherOrg", 545 Labels: []string{"label1", "label2"}, 546 }, 547 { 548 Name: "testColumn2", 549 State: "open", 550 Org: "otherOrg", 551 Labels: []string{"label1", "label2"}, 552 }, 553 }, 554 }, 555 }, 556 }, 557 }, 558 }, 559 pe: pe, 560 ie: ie, 561 expectedColumnCards: map[int][]github.ProjectCard{ 562 1: { 563 { 564 ContentID: 2, 565 }, 566 }, 567 }, 568 }, 569 { 570 name: "add Issue/PR using column name into org and repo projects", 571 gc: &fakegithub.FakeClient{ 572 IssueLabelsAdded: labels, 573 IssueLabelsRemoved: []string{}, 574 RepoProjects: map[string][]github.Project{ 575 "testOrg/*": { 576 { 577 Name: "testProject", 578 ID: 1, 579 }, 580 }, 581 "testOrg/testRepo2": { 582 { 583 Name: "testProject2", 584 ID: 2, 585 }, 586 }, 587 }, 588 OrgProjects: map[string][]github.Project{}, 589 ProjectColumnsMap: map[string][]github.ProjectColumn{ 590 "testProject": { 591 { 592 Name: "testColumn2", 593 ID: 2, 594 }, 595 { 596 Name: "testColumn", 597 ID: 1, 598 }, 599 }, 600 "testProject2": { 601 { 602 Name: "testColumn2", 603 ID: 4, 604 }, 605 { 606 Name: "testColumn", 607 ID: 3, 608 }, 609 }, 610 }, 611 ColumnCardsMap: map[int][]github.ProjectCard{}, 612 ColumnIDMap: columnIDMap, 613 }, 614 projectManager: plugins.ProjectManager{ 615 OrgRepos: map[string]plugins.ManagedOrgRepo{ 616 "testOrg": { 617 Projects: map[string]plugins.ManagedProject{ 618 "testProject": { 619 Columns: []plugins.ManagedColumn{ 620 { 621 Name: "testColumn", 622 State: "open", 623 Org: "otherOrg", 624 Labels: []string{"label1"}, 625 }, 626 }, 627 }, 628 }, 629 }, 630 "testOrg/testRepo2": { 631 Projects: map[string]plugins.ManagedProject{ 632 "testProject2": { 633 Columns: []plugins.ManagedColumn{ 634 { 635 Name: "testColumn2", 636 State: "open", 637 Org: "otherOrg", 638 Labels: []string{"label1", "label2"}, 639 }, 640 }, 641 }, 642 }, 643 }, 644 }, 645 }, 646 pe: pe, 647 ie: ie, 648 expectedColumnCards: map[int][]github.ProjectCard{ 649 4: { 650 { 651 ContentID: 2, 652 }, 653 }, 654 1: { 655 { 656 ContentID: 2, 657 }, 658 }, 659 }, 660 }, 661 } 662 for _, c := range cases { 663 t.Run(c.name+"[PullRequests]", func(t *testing.T) { 664 if !handleIssueActions[ie.Action] { 665 t.Logf("%s: Event with Action %s will not be processed by this plugin", c.name, c.ie.Action) 666 return 667 } 668 eData := eventData{ 669 id: c.pe.Issue.ID, 670 number: c.pe.Issue.Number, 671 isPR: c.pe.Issue.IsPullRequest(), 672 org: c.pe.Repo.Owner.Login, 673 repo: c.pe.Repo.Name, 674 state: c.pe.Issue.State, 675 labels: c.pe.Issue.Labels, 676 remove: c.pe.Action == github.IssueActionUnlabeled, 677 } 678 679 err := handle(c.gc, c.projectManager, logrus.NewEntry(logrus.New()), eData) 680 if err != nil { 681 if c.expectedError == nil || c.expectedError.Error() != err.Error() { 682 // if we are not expecting an error or if the error did not match with 683 // what we are expecting 684 t.Fatalf("%s: handlePR error: %v", c.name, err) 685 } 686 } 687 err = checkCards(c.expectedColumnCards, c.gc.ColumnCardsMap) 688 if err != nil { 689 t.Fatalf("%s: %v", c.name, err) 690 } 691 }) 692 } 693 694 for _, c := range cases { 695 t.Run(c.name+"[Issues]", func(t *testing.T) { 696 // reset the cards at the beginning of new test cycle. 697 c.gc.ColumnCardsMap = map[int][]github.ProjectCard{} 698 if !handleIssueActions[ie.Action] { 699 t.Logf("%s: Event with Action %s will not be processed by this plugin", c.name, c.ie.Action) 700 return 701 } 702 eData := eventData{ 703 id: c.ie.Issue.ID, 704 number: c.ie.Issue.Number, 705 isPR: c.ie.Issue.IsPullRequest(), 706 org: c.ie.Repo.Owner.Login, 707 repo: c.ie.Repo.Name, 708 state: c.ie.Issue.State, 709 labels: c.ie.Issue.Labels, 710 remove: c.ie.Action == github.IssueActionUnlabeled, 711 } 712 713 err := handle(c.gc, c.projectManager, logrus.NewEntry(logrus.New()), eData) 714 if err != nil { 715 if c.expectedError == nil || c.expectedError.Error() != err.Error() { 716 // if we are not expecting an error or if the error did not match with 717 // what we are expecting 718 t.Fatalf("%s: handleIssue error: %v", c.name, err) 719 } 720 } 721 err = checkCards(c.expectedColumnCards, c.gc.ColumnCardsMap) 722 if err != nil { 723 t.Fatalf("%s: %v", c.name, err) 724 } 725 }) 726 } 727 } 728 729 func checkCards(expectedColumnCards, projectColumnCards map[int][]github.ProjectCard) error { 730 if len(expectedColumnCards) == 0 { 731 return nil 732 } 733 734 for columnID, expectedCards := range expectedColumnCards { 735 projectCards := projectColumnCards[columnID] 736 737 //make sure all expectedCard are in projectCards 738 if len(expectedCards) > len(projectCards) { 739 return fmt.Errorf("Not all expected cards can be found for column: %d, \nexpected: %v\n found: %v", columnID, expectedCards, projectCards) 740 } 741 for _, card := range expectedCards { 742 found := false 743 for _, pcard := range projectCards { 744 if pcard.ContentID == card.ContentID { 745 found = true 746 break 747 } 748 } 749 if !found { 750 return fmt.Errorf("unable to find project card: %v under column: %d", card, columnID) 751 } 752 } 753 } 754 return nil 755 } 756 757 func TestHelpProvider(t *testing.T) { 758 i := 0 759 enabledRepos := []config.OrgRepo{ 760 {Org: "org1", Repo: "repo"}, 761 {Org: "org2", Repo: "repo"}, 762 } 763 managedCol1 := plugins.ManagedColumn{ID: &i, Name: "col1", State: "open", Labels: []string{"area/conformance", "area/testing"}, Org: "org1"} 764 managedCol2 := plugins.ManagedColumn{ID: &i, Name: "col2", State: "open", Labels: []string{"area/conformance2", "area/testing2"}, Org: "org2"} 765 managedProj := plugins.ManagedProject{Columns: []plugins.ManagedColumn{managedCol1, managedCol2}} 766 managedOrgRepo := plugins.ManagedOrgRepo{Projects: map[string]plugins.ManagedProject{"project1": managedProj}} 767 cases := []struct { 768 name string 769 config *plugins.Configuration 770 enabledRepos []config.OrgRepo 771 expectedConfig string 772 expectedKey string 773 err bool 774 }{ 775 { 776 name: "Empty config", 777 config: &plugins.Configuration{}, 778 enabledRepos: enabledRepos, 779 }, 780 { 781 name: "Empty projects in ProjectManager Config", 782 config: &plugins.Configuration{ 783 ProjectManager: plugins.ProjectManager{ 784 OrgRepos: map[string]plugins.ManagedOrgRepo{}, 785 }, 786 }, 787 enabledRepos: enabledRepos, 788 expectedConfig: "", 789 expectedKey: "Config", 790 }, 791 { 792 name: "Properly formed ProjectManager Config", 793 config: &plugins.Configuration{ 794 ProjectManager: plugins.ProjectManager{ 795 OrgRepos: map[string]plugins.ManagedOrgRepo{"org1": managedOrgRepo}, 796 }, 797 }, 798 enabledRepos: enabledRepos, 799 }, 800 } 801 802 for _, c := range cases { 803 t.Run(c.name, func(t *testing.T) { 804 ph, err := helpProvider(c.config, c.enabledRepos) 805 if err != nil && !c.err { 806 t.Fatalf("helpProvider error: %v", err) 807 } 808 if ph.Config[c.expectedKey] != c.expectedConfig { 809 t.Fatalf("Error running the test %s, \nexpected: %s, \nreceived: %s", c.name, c.expectedConfig, ph.Config[c.expectedKey]) 810 } 811 }) 812 } 813 }