github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/plugins/verify-owners/verify-owners_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 verifyowners 18 19 import ( 20 "bytes" 21 "fmt" 22 "os" 23 "path/filepath" 24 "regexp" 25 "strings" 26 "testing" 27 28 "github.com/google/go-cmp/cmp" 29 "github.com/sirupsen/logrus" 30 "sigs.k8s.io/yaml" 31 32 "k8s.io/apimachinery/pkg/util/sets" 33 34 "sigs.k8s.io/prow/pkg/config" 35 "sigs.k8s.io/prow/pkg/git/localgit" 36 "sigs.k8s.io/prow/pkg/github" 37 "sigs.k8s.io/prow/pkg/github/fakegithub" 38 "sigs.k8s.io/prow/pkg/labels" 39 "sigs.k8s.io/prow/pkg/layeredsets" 40 "sigs.k8s.io/prow/pkg/pluginhelp" 41 "sigs.k8s.io/prow/pkg/plugins" 42 "sigs.k8s.io/prow/pkg/plugins/ownersconfig" 43 "sigs.k8s.io/prow/pkg/repoowners" 44 ) 45 46 var defaultBranch = localgit.DefaultBranch("") 47 48 var ownerFiles = map[string][]byte{ 49 "emptyApprovers": []byte(`approvers: 50 reviewers: 51 - alice 52 - bob 53 labels: 54 - label1 55 `), 56 "emptyApproversFilters": []byte(`filters: 57 ".*": 58 approvers: 59 reviewers: 60 - alice 61 - bob 62 labels: 63 - label1 64 `), 65 "invalidSyntax": []byte(`approvers: 66 - jdoe 67 reviewers: 68 - alice 69 - bob 70 labels 71 - label1 72 `), 73 "invalidSyntaxFilters": []byte(`filters: 74 ".*": 75 approvers: 76 - jdoe 77 reviewers: 78 - alice 79 - bob 80 labels 81 - label1 82 `), 83 "invalidLabels": []byte(`approvers: 84 - jdoe 85 reviewers: 86 - alice 87 - bob 88 labels: 89 - lgtm 90 `), 91 "invalidLabelsFilters": []byte(`filters: 92 ".*": 93 approvers: 94 - jdoe 95 reviewers: 96 - alice 97 - bob 98 labels: 99 - lgtm 100 `), 101 "noApprovers": []byte(`reviewers: 102 - alice 103 - bob 104 labels: 105 - label1 106 `), 107 "noApproversFilters": []byte(`filters: 108 ".*": 109 reviewers: 110 - alice 111 - bob 112 labels: 113 - label1 114 `), 115 "valid": []byte(`approvers: 116 - jdoe 117 reviewers: 118 - alice 119 - bob 120 labels: 121 - label1 122 `), 123 "validFilters": []byte(`filters: 124 ".*": 125 approvers: 126 - jdoe 127 reviewers: 128 - alice 129 - bob 130 labels: 131 - label1 132 `), 133 "referencesToBeAddedAlias": []byte(`approvers: 134 - not-yet-existing-alias 135 `), 136 } 137 138 var patches = map[string]string{ 139 "referencesToBeAddedAlias": ` 140 + - not-yet-existing-alias 141 `, 142 } 143 144 var ownerAliasesFiles = map[string][]byte{ 145 "toBeAddedAlias": []byte(`aliases: 146 not-yet-existing-alias: 147 - bob 148 `), 149 } 150 151 func IssueLabelsContain(arr []string, str string) bool { 152 for _, a := range arr { 153 // IssueLabels format is owner/repo#number:label 154 b := strings.Split(a, ":") 155 if b[len(b)-1] == str { 156 return true 157 } 158 } 159 return false 160 } 161 162 func ownersFilePatch(files []string, ownersFile string, useEmptyPatch bool) map[string]string { 163 changes := emptyPatch(files) 164 if useEmptyPatch { 165 return changes 166 } 167 for _, file := range files { 168 if strings.Contains(file, "OWNERS") && !strings.Contains(file, "OWNERS_ALIASES") { 169 changes[file] = patches[ownersFile] 170 } 171 } 172 return changes 173 } 174 175 func emptyPatch(files []string) map[string]string { 176 changes := make(map[string]string, len(files)) 177 for _, f := range files { 178 changes[f] = "" 179 } 180 return changes 181 } 182 183 func newFakeGitHubClient(changed map[string]string, removed []string, pr int) *fakegithub.FakeClient { 184 var changes []github.PullRequestChange 185 for file, patch := range changed { 186 changes = append(changes, github.PullRequestChange{Filename: file, Patch: patch}) 187 } 188 for _, file := range removed { 189 changes = append(changes, github.PullRequestChange{Filename: file, Status: github.PullRequestFileRemoved}) 190 } 191 fgc := fakegithub.NewFakeClient() 192 fgc.PullRequestChanges = map[int][]github.PullRequestChange{pr: changes} 193 fgc.Reviews = map[int][]github.Review{} 194 fgc.Collaborators = []string{"alice", "bob", "jdoe"} 195 fgc.IssueComments = map[int][]github.IssueComment{} 196 return fgc 197 } 198 199 type fakePruner struct{} 200 201 func (fp *fakePruner) PruneComments(shouldPrune func(github.IssueComment) bool) {} 202 203 type fakeRepoownersClient struct { 204 foc *fakeOwnersClient 205 } 206 207 func (froc fakeRepoownersClient) LoadRepoOwners(org, repo, base string) (repoowners.RepoOwner, error) { 208 return froc.foc, nil 209 } 210 211 type fakeOwnersClient struct { 212 owners map[string]string 213 approvers map[string]layeredsets.String 214 leafApprovers map[string]sets.Set[string] 215 reviewers map[string]layeredsets.String 216 requiredReviewers map[string]sets.Set[string] 217 leafReviewers map[string]sets.Set[string] 218 dirIgnorelist []*regexp.Regexp 219 } 220 221 func (foc *fakeOwnersClient) AllApprovers() sets.Set[string] { 222 return sets.Set[string]{} 223 } 224 225 func (foc *fakeOwnersClient) AllOwners() sets.Set[string] { 226 return sets.Set[string]{} 227 } 228 229 func (foc *fakeOwnersClient) AllReviewers() sets.Set[string] { 230 return sets.Set[string]{} 231 } 232 233 func (foc *fakeOwnersClient) Filenames() ownersconfig.Filenames { 234 return ownersconfig.FakeFilenames 235 } 236 237 func (foc *fakeOwnersClient) Approvers(path string) layeredsets.String { 238 return foc.approvers[path] 239 } 240 241 func (foc *fakeOwnersClient) LeafApprovers(path string) sets.Set[string] { 242 return foc.leafApprovers[path] 243 } 244 245 func (foc *fakeOwnersClient) FindApproverOwnersForFile(path string) string { 246 return foc.owners[path] 247 } 248 249 func (foc *fakeOwnersClient) Reviewers(path string) layeredsets.String { 250 return foc.reviewers[path] 251 } 252 253 func (foc *fakeOwnersClient) RequiredReviewers(path string) sets.Set[string] { 254 return foc.requiredReviewers[path] 255 } 256 257 func (foc *fakeOwnersClient) LeafReviewers(path string) sets.Set[string] { 258 return foc.leafReviewers[path] 259 } 260 261 func (foc *fakeOwnersClient) FindReviewersOwnersForFile(path string) string { 262 return foc.owners[path] 263 } 264 265 func (foc *fakeOwnersClient) FindLabelsForFile(path string) sets.Set[string] { 266 return sets.Set[string]{} 267 } 268 269 func (foc *fakeOwnersClient) IsNoParentOwners(path string) bool { 270 return false 271 } 272 273 func (foc *fakeOwnersClient) IsAutoApproveUnownedSubfolders(path string) bool { 274 return false 275 } 276 277 func (foc *fakeOwnersClient) ParseSimpleConfig(path string) (repoowners.SimpleConfig, error) { 278 dir := filepath.Dir(path) 279 for _, re := range foc.dirIgnorelist { 280 if re.MatchString(dir) { 281 return repoowners.SimpleConfig{}, filepath.SkipDir 282 } 283 } 284 285 b, err := os.ReadFile(path) 286 if err != nil { 287 return repoowners.SimpleConfig{}, err 288 } 289 full := new(repoowners.SimpleConfig) 290 err = yaml.Unmarshal(b, full) 291 return *full, err 292 } 293 294 func (foc *fakeOwnersClient) ParseFullConfig(path string) (repoowners.FullConfig, error) { 295 dir := filepath.Dir(path) 296 for _, re := range foc.dirIgnorelist { 297 if re.MatchString(dir) { 298 return repoowners.FullConfig{}, filepath.SkipDir 299 } 300 } 301 302 b, err := os.ReadFile(path) 303 if err != nil { 304 return repoowners.FullConfig{}, err 305 } 306 full := new(repoowners.FullConfig) 307 err = yaml.Unmarshal(b, full) 308 return *full, err 309 } 310 311 func (foc *fakeOwnersClient) TopLevelApprovers() sets.Set[string] { 312 return sets.Set[string]{} 313 } 314 315 func makeFakeRepoOwnersClient() fakeRepoownersClient { 316 return fakeRepoownersClient{ 317 foc: &fakeOwnersClient{}, 318 } 319 } 320 321 func addFilesToRepo(lg *localgit.LocalGit, paths []string, ownersFile string) error { 322 origFiles := map[string][]byte{} 323 for _, file := range paths { 324 if strings.Contains(file, "OWNERS_ALIASES") { 325 origFiles[file] = ownerAliasesFiles[ownersFile] 326 } else if strings.Contains(file, "OWNERS") { 327 origFiles[file] = ownerFiles[ownersFile] 328 } else { 329 origFiles[file] = []byte("foo") 330 } 331 } 332 return lg.AddCommit("org", "repo", origFiles) 333 } 334 335 func TestHandleV2(t *testing.T) { 336 testHandle(localgit.NewV2, t) 337 } 338 339 func testHandle(clients localgit.Clients, t *testing.T) { 340 var tests = []struct { 341 name string 342 filesChanged []string 343 usePatch bool 344 filesRemoved []string 345 ownersFile string 346 filesChangedAfterPR []string 347 addedContent string 348 shouldLabel bool 349 }{ 350 { 351 name: "no OWNERS file", 352 filesChanged: []string{"a.go", "b.go"}, 353 ownersFile: "valid", 354 shouldLabel: false, 355 }, 356 { 357 name: "no OWNERS file with filters", 358 filesChanged: []string{"a.go", "b.go"}, 359 ownersFile: "validFilters", 360 shouldLabel: false, 361 }, 362 { 363 name: "good OWNERS file", 364 filesChanged: []string{"OWNERS", "b.go"}, 365 ownersFile: "valid", 366 shouldLabel: false, 367 }, 368 { 369 name: "good OWNERS file with filters", 370 filesChanged: []string{"OWNERS", "b.go"}, 371 ownersFile: "validFilters", 372 shouldLabel: false, 373 }, 374 { 375 name: "invalid syntax OWNERS file", 376 filesChanged: []string{"OWNERS", "b.go"}, 377 ownersFile: "invalidSyntax", 378 shouldLabel: true, 379 }, 380 { 381 name: "invalid syntax OWNERS file with filters", 382 filesChanged: []string{"OWNERS", "b.go"}, 383 ownersFile: "invalidSyntaxFilters", 384 shouldLabel: true, 385 }, 386 { 387 name: "forbidden labels in OWNERS file", 388 filesChanged: []string{"OWNERS", "b.go"}, 389 ownersFile: "invalidLabels", 390 shouldLabel: true, 391 }, 392 { 393 name: "forbidden labels in OWNERS file with filters", 394 filesChanged: []string{"OWNERS", "b.go"}, 395 ownersFile: "invalidLabelsFilters", 396 shouldLabel: true, 397 }, 398 { 399 name: "empty approvers in OWNERS file", 400 filesChanged: []string{"OWNERS", "b.go"}, 401 ownersFile: "emptyApprovers", 402 shouldLabel: true, 403 }, 404 { 405 name: "empty approvers in OWNERS file with filters", 406 filesChanged: []string{"OWNERS", "b.go"}, 407 ownersFile: "emptyApproversFilters", 408 shouldLabel: true, 409 }, 410 { 411 name: "no approvers in OWNERS file", 412 filesChanged: []string{"OWNERS", "b.go"}, 413 ownersFile: "noApprovers", 414 shouldLabel: true, 415 }, 416 { 417 name: "no approvers in OWNERS file with filters", 418 filesChanged: []string{"OWNERS", "b.go"}, 419 ownersFile: "noApproversFilters", 420 shouldLabel: true, 421 }, 422 { 423 name: "no approvers in pkg/OWNERS file", 424 filesChanged: []string{"pkg/OWNERS", "b.go"}, 425 ownersFile: "noApprovers", 426 shouldLabel: false, 427 }, 428 { 429 name: "no approvers in pkg/OWNERS file with filters", 430 filesChanged: []string{"pkg/OWNERS", "b.go"}, 431 ownersFile: "noApproversFilters", 432 shouldLabel: false, 433 }, 434 { 435 name: "OWNERS file was removed", 436 filesRemoved: []string{"pkg/OWNERS"}, 437 ownersFile: "valid", 438 shouldLabel: false, 439 }, 440 { 441 name: "OWNERS_ALIASES file was removed", 442 filesRemoved: []string{"OWNERS_ALIASES"}, 443 shouldLabel: false, 444 }, 445 { 446 name: "new alias added after a PR references that alias", 447 filesChanged: []string{"OWNERS"}, 448 usePatch: true, 449 ownersFile: "referencesToBeAddedAlias", 450 filesChangedAfterPR: []string{"OWNERS_ALIASES"}, 451 addedContent: "toBeAddedAlias", 452 shouldLabel: false, 453 }, 454 } 455 lg, c, err := clients() 456 if err != nil { 457 t.Fatalf("Making localgit: %v", err) 458 } 459 defer func() { 460 if err := lg.Clean(); err != nil { 461 t.Errorf("Cleaning up localgit: %v", err) 462 } 463 if err := c.Clean(); err != nil { 464 t.Errorf("Cleaning up client: %v", err) 465 } 466 }() 467 if err := lg.MakeFakeRepo("org", "repo"); err != nil { 468 t.Fatalf("Making fake repo: %v", err) 469 } 470 for i, test := range tests { 471 t.Run(test.name, func(t *testing.T) { 472 pr := i + 1 473 // make sure we're on master before branching 474 if err := lg.Checkout("org", "repo", defaultBranch); err != nil { 475 t.Fatalf("Switching to master branch: %v", err) 476 } 477 if len(test.filesRemoved) > 0 { 478 if err := addFilesToRepo(lg, test.filesRemoved, test.ownersFile); err != nil { 479 t.Fatalf("Adding base commit: %v", err) 480 } 481 } 482 483 if err := lg.CheckoutNewBranch("org", "repo", fmt.Sprintf("pull/%d/head", pr)); err != nil { 484 t.Fatalf("Checking out pull branch: %v", err) 485 } 486 487 if len(test.filesChanged) > 0 { 488 if err := addFilesToRepo(lg, test.filesChanged, test.ownersFile); err != nil { 489 t.Fatalf("Adding PR commit: %v", err) 490 } 491 } 492 if len(test.filesRemoved) > 0 { 493 if err := lg.RmCommit("org", "repo", test.filesRemoved); err != nil { 494 t.Fatalf("Adding PR commit (removing files): %v", err) 495 } 496 } 497 498 sha, err := lg.RevParse("org", "repo", "HEAD") 499 if err != nil { 500 t.Fatalf("Getting commit SHA: %v", err) 501 } 502 if len(test.filesChangedAfterPR) > 0 { 503 if err := lg.Checkout("org", "repo", defaultBranch); err != nil { 504 t.Fatalf("Switching to master branch: %v", err) 505 } 506 if err := addFilesToRepo(lg, test.filesChangedAfterPR, test.addedContent); err != nil { 507 t.Fatalf("Adding commit to master: %v", err) 508 } 509 } 510 pre := &github.PullRequestEvent{ 511 PullRequest: github.PullRequest{ 512 User: github.User{Login: "author"}, 513 Base: github.PullRequestBranch{ 514 Ref: defaultBranch, 515 }, 516 Head: github.PullRequestBranch{ 517 SHA: sha, 518 }, 519 }, 520 } 521 changes := ownersFilePatch(test.filesChanged, test.ownersFile, !test.usePatch) 522 fghc := newFakeGitHubClient(changes, test.filesRemoved, pr) 523 fghc.PullRequests = map[int]*github.PullRequest{} 524 fghc.PullRequests[pr] = &github.PullRequest{ 525 Base: github.PullRequestBranch{ 526 Ref: defaultBranch, 527 }, 528 } 529 530 prInfo := info{ 531 org: "org", 532 repo: "repo", 533 repoFullName: "org/repo", 534 number: pr, 535 } 536 537 if err := handle(fghc, c, makeFakeRepoOwnersClient(), logrus.WithField("plugin", PluginName), &pre.PullRequest, prInfo, []string{labels.Approved, labels.LGTM}, plugins.Trigger{}, false, &fakePruner{}, ownersconfig.FakeResolver); err != nil { 538 t.Fatalf("Handle PR: %v", err) 539 } 540 if !test.shouldLabel && IssueLabelsContain(fghc.IssueLabelsAdded, labels.InvalidOwners) { 541 t.Fatalf("%s: didn't expect label %s in %s", test.name, labels.InvalidOwners, fghc.IssueLabelsAdded) 542 } else if test.shouldLabel && !IssueLabelsContain(fghc.IssueLabelsAdded, labels.InvalidOwners) { 543 t.Fatalf("%s: expected label %s in %s", test.name, labels.InvalidOwners, fghc.IssueLabelsAdded) 544 } 545 }) 546 } 547 } 548 549 func TestParseOwnersFileV2(t *testing.T) { 550 testParseOwnersFile(localgit.NewV2, t) 551 } 552 553 func testParseOwnersFile(clients localgit.Clients, t *testing.T) { 554 tests := []struct { 555 name string 556 document []byte 557 patch string 558 errLine int 559 }{ 560 { 561 name: "emptyApprovers", 562 document: ownerFiles["emptyApprovers"], 563 errLine: 1, 564 }, 565 { 566 name: "emptyApproversFilters", 567 document: ownerFiles["emptyApproversFilters"], 568 errLine: 1, 569 }, 570 { 571 name: "invalidSyntax", 572 document: ownerFiles["invalidSyntax"], 573 errLine: 7, 574 }, 575 { 576 name: "invalidSyntaxFilters", 577 document: ownerFiles["invalidSyntaxFilters"], 578 errLine: 9, 579 }, 580 { 581 name: "invalidSyntax edit", 582 document: ownerFiles["invalidSyntax"], 583 patch: `@@ -3,6 +3,6 @@ approvers: 584 reviewers: 585 - alice 586 - bob 587 -labels: 588 +labels 589 - label1 590 `, 591 errLine: 1, 592 }, 593 { 594 name: "noApprovers", 595 document: ownerFiles["noApprovers"], 596 errLine: 1, 597 }, 598 { 599 name: "noApproversFilters", 600 document: ownerFiles["noApproversFilters"], 601 errLine: 1, 602 }, 603 { 604 name: "valid", 605 document: ownerFiles["valid"], 606 }, 607 { 608 name: "validFilters", 609 document: ownerFiles["validFilters"], 610 }, 611 } 612 613 for i, test := range tests { 614 t.Run(test.name, func(t *testing.T) { 615 pr := i + 1 616 lg, c, err := clients() 617 if err != nil { 618 t.Fatalf("Making localgit: %v", err) 619 } 620 defer func() { 621 if err := lg.Clean(); err != nil { 622 t.Errorf("Cleaning up localgit: %v", err) 623 } 624 if err := c.Clean(); err != nil { 625 t.Errorf("Cleaning up client: %v", err) 626 } 627 }() 628 if err := lg.MakeFakeRepo("org", "repo"); err != nil { 629 t.Fatalf("Making fake repo: %v", err) 630 } 631 // make sure we're on master before branching 632 if err := lg.Checkout("org", "repo", defaultBranch); err != nil { 633 t.Fatalf("Switching to master branch: %v", err) 634 } 635 if err := lg.CheckoutNewBranch("org", "repo", fmt.Sprintf("pull/%d/head", pr)); err != nil { 636 t.Fatalf("Checking out pull branch: %v", err) 637 } 638 pullFiles := map[string][]byte{} 639 pullFiles["OWNERS"] = test.document 640 if err := lg.AddCommit("org", "repo", pullFiles); err != nil { 641 t.Fatalf("Adding PR commit: %v", err) 642 } 643 644 if test.patch == "" { 645 test.patch = makePatch(test.document) 646 } 647 change := github.PullRequestChange{ 648 Filename: "OWNERS", 649 Patch: test.patch, 650 } 651 652 r, err := c.ClientFor("org", "repo") 653 if err != nil { 654 t.Fatalf("error cloning the repo: %v", err) 655 } 656 defer func() { 657 if err := r.Clean(); err != nil { 658 t.Fatalf("error cleaning up repo: %v", err) 659 } 660 }() 661 662 path := filepath.Join(r.Directory(), "OWNERS") 663 message, _ := parseOwnersFile(&fakeOwnersClient{}, path, change, &logrus.Entry{}, []string{}, ownersconfig.FakeFilenames) 664 if message != nil { 665 if test.errLine == 0 { 666 t.Errorf("%s: expected no error, got one: %s", test.name, message.message) 667 } 668 if message.line != test.errLine { 669 t.Errorf("%s: wrong line for message, expected %d, got %d", test.name, test.errLine, message.line) 670 } 671 } else if test.errLine != 0 { 672 t.Errorf("%s: expected an error, got none", test.name) 673 } 674 }) 675 } 676 } 677 678 func makePatch(b []byte) string { 679 p := bytes.Replace(b, []byte{'\n'}, []byte{'\n', '+'}, -1) 680 nbLines := bytes.Count(p, []byte{'+'}) + 1 681 return fmt.Sprintf("@@ -0,0 +1,%d @@\n+%s", nbLines, p) 682 } 683 684 func TestHelpProvider(t *testing.T) { 685 enabledRepos := []config.OrgRepo{ 686 {Org: "org1", Repo: "repo"}, 687 {Org: "org2", Repo: "repo"}, 688 } 689 cases := []struct { 690 name string 691 config *plugins.Configuration 692 enabledRepos []config.OrgRepo 693 err bool 694 expected *pluginhelp.PluginHelp 695 }{ 696 { 697 name: "Empty config", 698 config: &plugins.Configuration{}, 699 enabledRepos: enabledRepos, 700 expected: &pluginhelp.PluginHelp{ 701 Description: "The verify-owners plugin validates OWNERS and OWNERS_ALIASES files (by default) and ensures that they always contain collaborators of the org, if they are modified in a PR. On validation failure it automatically adds the 'do-not-merge/invalid-owners-file' label to the PR, and a review comment on the incriminating file(s). Per-repo configuration for filenames is possible.", 702 Config: map[string]string{ 703 "default": "OWNERS and OWNERS_ALIASES files are validated.", 704 }, 705 Commands: []pluginhelp.Command{{ 706 Usage: "/verify-owners", 707 Description: "do-not-merge/invalid-owners-file", 708 Examples: []string{"/verify-owners"}, 709 WhoCanUse: "Anyone", 710 }}, 711 }, 712 }, 713 { 714 name: "ReviewerCount specified", 715 config: &plugins.Configuration{ 716 Owners: plugins.Owners{ 717 LabelsDenyList: []string{"label1", "label2"}, 718 }, 719 }, 720 enabledRepos: enabledRepos, 721 expected: &pluginhelp.PluginHelp{ 722 Description: "The verify-owners plugin validates OWNERS and OWNERS_ALIASES files (by default) and ensures that they always contain collaborators of the org, if they are modified in a PR. On validation failure it automatically adds the 'do-not-merge/invalid-owners-file' label to the PR, and a review comment on the incriminating file(s). Per-repo configuration for filenames is possible.", 723 Config: map[string]string{ 724 "default": "OWNERS and OWNERS_ALIASES files are validated. The verify-owners plugin will complain if OWNERS files contain any of the following banned labels: label1, label2.", 725 }, 726 Commands: []pluginhelp.Command{{ 727 Usage: "/verify-owners", 728 Description: "do-not-merge/invalid-owners-file", 729 Examples: []string{"/verify-owners"}, 730 WhoCanUse: "Anyone", 731 }}, 732 }, 733 }, 734 } 735 for _, c := range cases { 736 t.Run(c.name, func(t *testing.T) { 737 pluginHelp, err := helpProvider(c.config, c.enabledRepos) 738 if err != nil && !c.err { 739 t.Fatalf("helpProvider error: %v", err) 740 } 741 if diff := cmp.Diff(pluginHelp, c.expected); diff != "" { 742 t.Errorf("%s: did not get correct help: %v", c.name, diff) 743 } 744 }) 745 } 746 } 747 748 var ownersFiles = map[string][]byte{ 749 "nonCollaborators": []byte(`reviewers: 750 - phippy 751 - alice 752 approvers: 753 - zee 754 - bob 755 `), 756 "collaboratorsWithAliases": []byte(`reviewers: 757 - foo-reviewers 758 - alice 759 approvers: 760 - bob 761 `), 762 "nonCollaboratorsWithAliases": []byte(`reviewers: 763 - foo-reviewers 764 - alice 765 - goldie 766 approvers: 767 - bob 768 `), 769 } 770 771 var ownersPatch = map[string]string{ 772 "collaboratorAdditions": `@@ -1,4 +1,6 @@ 773 reviewers: 774 - phippy 775 +- alice 776 approvers: 777 - zee 778 +- bob 779 `, 780 "collaboratorRemovals": `@@ -1,8 +1,6 @@ 781 reviewers: 782 - phippy 783 - alice 784 -- bob 785 approvers: 786 - zee 787 - bob 788 -- alice 789 `, 790 "nonCollaboratorAdditions": `@@ -1,4 +1,6 @@ 791 reviewers: 792 +- phippy 793 - alice 794 approvers: 795 +- zee 796 - bob 797 `, 798 "nonCollaboratorRemovals": `@@ -1,8 +1,6 @@ 799 reviewers: 800 - phippy 801 - alice 802 -- al 803 approvers: 804 - zee 805 - bob 806 -- bo 807 `, 808 "nonCollaboratorsWithAliases": `@@ -1,4 +1,6 @@ 809 reviewers: 810 +- foo-reviewers 811 - alice 812 +- goldie 813 approvers: 814 - bob 815 `, 816 "collaboratorsWithAliases": `@@ -1,2 +1,5 @@ 817 +reviewers: 818 +- foo-reviewers 819 +- alice 820 approvers: 821 - bob 822 `, 823 } 824 825 var ownersAliases = map[string][]byte{ 826 "nonCollaborators": []byte(`aliases: 827 foo-reviewers: 828 - alice 829 - phippy 830 - zee 831 `), 832 "collaborators": []byte(`aliases: 833 foo-reviewers: 834 - alice 835 `), 836 } 837 838 var ownersAliasesPatch = map[string]string{ 839 "nonCollaboratorAdditions": `@@ -1,3 +1,5 @@ 840 aliases: 841 foo-reviewers: 842 - alice 843 + - phippy 844 + - zee 845 `, 846 "nonCollaboratorRemovals": `@@ -1,6 +1,5 @@ 847 aliases: 848 foo-reviewers: 849 - alice 850 - - al 851 - phippy 852 - zee 853 `, 854 "collaboratorAdditions": `@@ -1,4 +1,5 @@ 855 aliases: 856 foo-reviewers: 857 + - alice 858 - phippy 859 - zee 860 `, 861 "collaboratorRemovals": `@@ -1,6 +1,5 @@ 862 aliases: 863 foo-reviewers: 864 - alice 865 - - bob 866 - phippy 867 - zee 868 `, 869 } 870 871 func TestNonCollaboratorsV2(t *testing.T) { 872 testNonCollaborators(localgit.NewV2, t) 873 } 874 875 func testNonCollaborators(clients localgit.Clients, t *testing.T) { 876 const nonTrustedNotMemberNotCollaborator = "User is not a member of the org. User is not a collaborator." 877 var tests = []struct { 878 name string 879 filesChanged []string 880 ownersFile string 881 ownersPatch string 882 ownersAliasesFile string 883 ownersAliasesPatch string 884 includeVendorOwners bool 885 skipTrustedUserCheck bool 886 shouldLabel bool 887 shouldComment bool 888 commentShouldContain string 889 }{ 890 { 891 name: "collaborators additions in OWNERS file", 892 filesChanged: []string{"OWNERS"}, 893 ownersFile: "nonCollaborators", 894 ownersPatch: "collaboratorAdditions", 895 shouldLabel: false, 896 shouldComment: false, 897 }, 898 { 899 name: "collaborators removals in OWNERS file", 900 filesChanged: []string{"OWNERS"}, 901 ownersFile: "nonCollaborators", 902 ownersPatch: "collaboratorRemovals", 903 shouldLabel: false, 904 shouldComment: false, 905 }, 906 { 907 name: "non-collaborators additions in OWNERS file", 908 filesChanged: []string{"OWNERS"}, 909 ownersFile: "nonCollaborators", 910 ownersPatch: "nonCollaboratorAdditions", 911 shouldLabel: true, 912 shouldComment: true, 913 commentShouldContain: nonTrustedNotMemberNotCollaborator, 914 }, 915 { 916 name: "non-collaborators removal in OWNERS file", 917 filesChanged: []string{"OWNERS"}, 918 ownersFile: "nonCollaborators", 919 ownersPatch: "nonCollaboratorRemovals", 920 shouldLabel: false, 921 shouldComment: false, 922 }, 923 { 924 name: "non-collaborators additions in OWNERS file, with skipTrustedUserCheck=true", 925 filesChanged: []string{"OWNERS"}, 926 ownersFile: "nonCollaborators", 927 ownersPatch: "nonCollaboratorAdditions", 928 skipTrustedUserCheck: true, 929 shouldLabel: false, 930 shouldComment: false, 931 }, 932 { 933 name: "non-collaborators additions in OWNERS_ALIASES file", 934 filesChanged: []string{"OWNERS_ALIASES"}, 935 ownersFile: "collaboratorsWithAliases", 936 ownersAliasesFile: "nonCollaborators", 937 ownersAliasesPatch: "nonCollaboratorAdditions", 938 shouldLabel: true, 939 shouldComment: true, 940 commentShouldContain: nonTrustedNotMemberNotCollaborator, 941 }, 942 { 943 name: "non-collaborators removals in OWNERS_ALIASES file", 944 filesChanged: []string{"OWNERS_ALIASES"}, 945 ownersFile: "collaboratorsWithAliases", 946 ownersAliasesFile: "nonCollaborators", 947 ownersAliasesPatch: "nonCollaboratorRemovals", 948 shouldLabel: false, 949 shouldComment: false, 950 }, 951 { 952 name: "collaborators additions in OWNERS_ALIASES file", 953 filesChanged: []string{"OWNERS_ALIASES"}, 954 ownersFile: "collaboratorsWithAliases", 955 ownersAliasesFile: "nonCollaborators", 956 ownersAliasesPatch: "collaboratorAdditions", 957 shouldLabel: false, 958 shouldComment: false, 959 }, 960 { 961 name: "collaborators removals in OWNERS_ALIASES file", 962 filesChanged: []string{"OWNERS_ALIASES"}, 963 ownersFile: "collaboratorsWithAliases", 964 ownersAliasesFile: "nonCollaborators", 965 ownersAliasesPatch: "collaboratorRemovals", 966 shouldLabel: false, 967 shouldComment: false, 968 }, 969 { 970 name: "non-collaborators additions in OWNERS_ALIASES file, with skipTrustedUserCheck=true", 971 filesChanged: []string{"OWNERS_ALIASES"}, 972 ownersFile: "collaboratorsWithAliases", 973 ownersAliasesFile: "nonCollaborators", 974 ownersAliasesPatch: "nonCollaboratorAdditions", 975 skipTrustedUserCheck: true, 976 shouldLabel: false, 977 shouldComment: false, 978 }, 979 { 980 name: "non-collaborators additions in both OWNERS and OWNERS_ALIASES file", 981 filesChanged: []string{"OWNERS", "OWNERS_ALIASES"}, 982 ownersFile: "nonCollaboratorsWithAliases", 983 ownersPatch: "nonCollaboratorsWithAliases", 984 ownersAliasesFile: "nonCollaborators", 985 ownersAliasesPatch: "nonCollaboratorAdditions", 986 shouldLabel: true, 987 shouldComment: true, 988 commentShouldContain: nonTrustedNotMemberNotCollaborator, 989 }, 990 { 991 name: "collaborator additions in both OWNERS and OWNERS_ALIASES file", 992 filesChanged: []string{"OWNERS", "OWNERS_ALIASES"}, 993 ownersFile: "collaboratorsWithAliases", 994 ownersPatch: "collaboratorsWithAliases", 995 ownersAliasesFile: "collaborators", 996 ownersAliasesPatch: "collaboratorAdditions", 997 shouldLabel: false, 998 shouldComment: false, 999 }, 1000 { 1001 name: "non-collaborators additions in OWNERS file in vendor subdir", 1002 filesChanged: []string{"vendor/k8s.io/client-go/OWNERS"}, 1003 ownersFile: "nonCollaborators", 1004 ownersPatch: "nonCollaboratorAdditions", 1005 shouldLabel: false, 1006 shouldComment: false, 1007 }, 1008 { 1009 name: "non-collaborators additions in OWNERS file in vendor subdir, but include it", 1010 filesChanged: []string{"vendor/k8s.io/client-go/OWNERS"}, 1011 ownersFile: "nonCollaborators", 1012 ownersPatch: "nonCollaboratorAdditions", 1013 includeVendorOwners: true, 1014 shouldLabel: true, 1015 shouldComment: true, 1016 commentShouldContain: nonTrustedNotMemberNotCollaborator, 1017 }, 1018 { 1019 name: "non-collaborators additions in OWNERS file in vendor dir", 1020 filesChanged: []string{"vendor/OWNERS"}, 1021 ownersFile: "nonCollaborators", 1022 ownersPatch: "nonCollaboratorAdditions", 1023 shouldLabel: true, 1024 shouldComment: true, 1025 commentShouldContain: nonTrustedNotMemberNotCollaborator, 1026 }, 1027 } 1028 lg, c, err := clients() 1029 if err != nil { 1030 t.Fatalf("Making localgit: %v", err) 1031 } 1032 defer func() { 1033 if err := lg.Clean(); err != nil { 1034 t.Errorf("Cleaning up localgit: %v", err) 1035 } 1036 if err := c.Clean(); err != nil { 1037 t.Errorf("Cleaning up client: %v", err) 1038 } 1039 }() 1040 if err := lg.MakeFakeRepo("org", "repo"); err != nil { 1041 t.Fatalf("Making fake repo: %v", err) 1042 } 1043 for i, test := range tests { 1044 t.Run(test.name, func(t *testing.T) { 1045 pr := i + 1 1046 // make sure we're on master before branching 1047 if err := lg.Checkout("org", "repo", defaultBranch); err != nil { 1048 t.Fatalf("Switching to master branch: %v", err) 1049 } 1050 if err := lg.CheckoutNewBranch("org", "repo", fmt.Sprintf("pull/%d/head", pr)); err != nil { 1051 t.Fatalf("Checking out pull branch: %v", err) 1052 } 1053 pullFiles := map[string][]byte{} 1054 changes := []github.PullRequestChange{} 1055 1056 for _, file := range test.filesChanged { 1057 if strings.Contains(file, "OWNERS_ALIASES") { 1058 pullFiles[file] = ownersAliases[test.ownersAliasesFile] 1059 changes = append(changes, github.PullRequestChange{ 1060 Filename: file, 1061 Patch: ownersAliasesPatch[test.ownersAliasesPatch], 1062 }) 1063 } else if strings.Contains(file, "OWNERS") { 1064 pullFiles[file] = ownersFiles[test.ownersFile] 1065 changes = append(changes, github.PullRequestChange{ 1066 Filename: file, 1067 Patch: ownersPatch[test.ownersPatch], 1068 }) 1069 } 1070 } 1071 1072 if err := lg.AddCommit("org", "repo", pullFiles); err != nil { 1073 t.Fatalf("Adding PR commit: %v", err) 1074 } 1075 sha, err := lg.RevParse("org", "repo", "HEAD") 1076 if err != nil { 1077 t.Fatalf("Getting commit SHA: %v", err) 1078 } 1079 pre := &github.PullRequestEvent{ 1080 PullRequest: github.PullRequest{ 1081 User: github.User{Login: "author"}, 1082 Base: github.PullRequestBranch{ 1083 Ref: defaultBranch, 1084 }, 1085 Head: github.PullRequestBranch{ 1086 SHA: sha, 1087 }, 1088 }, 1089 } 1090 fghc := newFakeGitHubClient(emptyPatch(test.filesChanged), nil, pr) 1091 fghc.PullRequestChanges[pr] = changes 1092 1093 fghc.PullRequests = map[int]*github.PullRequest{} 1094 fghc.PullRequests[pr] = &github.PullRequest{ 1095 Base: github.PullRequestBranch{ 1096 Ref: fakegithub.TestRef, 1097 }, 1098 } 1099 1100 froc := makeFakeRepoOwnersClient() 1101 if !test.includeVendorOwners { 1102 var ignorePatterns []*regexp.Regexp 1103 re, err := regexp.Compile("vendor/.*/.*$") 1104 if err != nil { 1105 t.Fatalf("error compiling regex: %v", err) 1106 } 1107 ignorePatterns = append(ignorePatterns, re) 1108 froc.foc.dirIgnorelist = ignorePatterns 1109 } 1110 1111 prInfo := info{ 1112 org: "org", 1113 repo: "repo", 1114 repoFullName: "org/repo", 1115 number: pr, 1116 } 1117 1118 if err := handle(fghc, c, froc, logrus.WithField("plugin", PluginName), &pre.PullRequest, prInfo, []string{labels.Approved, labels.LGTM}, plugins.Trigger{}, test.skipTrustedUserCheck, &fakePruner{}, ownersconfig.FakeResolver); err != nil { 1119 t.Fatalf("Handle PR: %v", err) 1120 } 1121 if !test.shouldLabel && IssueLabelsContain(fghc.IssueLabelsAdded, labels.InvalidOwners) { 1122 t.Errorf("%s: didn't expect label %s in %s", test.name, labels.InvalidOwners, fghc.IssueLabelsAdded) 1123 } 1124 if test.shouldLabel && !IssueLabelsContain(fghc.IssueLabelsAdded, labels.InvalidOwners) { 1125 t.Errorf("%s: expected label %s in %s", test.name, labels.InvalidOwners, fghc.IssueLabelsAdded) 1126 } 1127 if !test.shouldComment && len(fghc.IssueComments[pr]) > 0 { 1128 t.Errorf("%s: didn't expect comment", test.name) 1129 } 1130 if test.shouldComment && len(fghc.IssueComments[pr]) == 0 { 1131 t.Errorf("%s: expected comment but didn't receive", test.name) 1132 } 1133 if test.shouldComment && len(test.commentShouldContain) > 0 && !strings.Contains(fghc.IssueComments[pr][0].Body, test.commentShouldContain) { 1134 t.Errorf("%s: expected comment to contain\n%s\nbut it was actually\n%s", test.name, test.commentShouldContain, fghc.IssueComments[pr][0].Body) 1135 } 1136 }) 1137 } 1138 } 1139 1140 func TestHandleGenericCommentV2(t *testing.T) { 1141 testHandleGenericComment(localgit.NewV2, t) 1142 } 1143 1144 func testHandleGenericComment(clients localgit.Clients, t *testing.T) { 1145 var tests = []struct { 1146 name string 1147 commentEvent github.GenericCommentEvent 1148 filesChanged []string 1149 filesRemoved []string 1150 ownersFile string 1151 shouldLabel bool 1152 }{ 1153 { 1154 name: "no OWNERS file", 1155 commentEvent: github.GenericCommentEvent{ 1156 Action: github.GenericCommentActionCreated, 1157 IssueState: "open", 1158 IsPR: true, 1159 Body: "/verify-owners", 1160 }, 1161 filesChanged: []string{"a.go", "b.go"}, 1162 ownersFile: "valid", 1163 shouldLabel: false, 1164 }, 1165 { 1166 name: "good OWNERS file", 1167 commentEvent: github.GenericCommentEvent{ 1168 Action: github.GenericCommentActionCreated, 1169 IssueState: "open", 1170 IsPR: true, 1171 Body: "/verify-owners", 1172 }, 1173 filesChanged: []string{"OWNERS", "b.go"}, 1174 ownersFile: "valid", 1175 shouldLabel: false, 1176 }, 1177 { 1178 name: "invalid syntax OWNERS file", 1179 commentEvent: github.GenericCommentEvent{ 1180 Action: github.GenericCommentActionCreated, 1181 IssueState: "open", 1182 IsPR: true, 1183 Body: "/verify-owners", 1184 }, 1185 filesChanged: []string{"OWNERS", "b.go"}, 1186 ownersFile: "invalidSyntax", 1187 shouldLabel: true, 1188 }, 1189 { 1190 name: "invalid syntax OWNERS file, unrelated comment", 1191 commentEvent: github.GenericCommentEvent{ 1192 Action: github.GenericCommentActionCreated, 1193 IsPR: true, 1194 Body: "/verify owners", 1195 }, 1196 filesChanged: []string{"OWNERS", "b.go"}, 1197 ownersFile: "invalidSyntax", 1198 shouldLabel: false, 1199 }, 1200 { 1201 name: "invalid syntax OWNERS file, comment edited", 1202 commentEvent: github.GenericCommentEvent{ 1203 Action: github.GenericCommentActionEdited, 1204 IsPR: true, 1205 Body: "/verify-owners", 1206 }, 1207 filesChanged: []string{"OWNERS", "b.go"}, 1208 ownersFile: "invalidSyntax", 1209 shouldLabel: false, 1210 }, 1211 { 1212 name: "invalid syntax OWNERS file, comment on an issue", 1213 commentEvent: github.GenericCommentEvent{ 1214 Action: github.GenericCommentActionCreated, 1215 IsPR: false, 1216 Body: "/verify-owners", 1217 }, 1218 filesChanged: []string{"OWNERS", "b.go"}, 1219 ownersFile: "invalidSyntax", 1220 shouldLabel: false, 1221 }, 1222 } 1223 1224 lg, c, err := clients() 1225 if err != nil { 1226 t.Fatalf("Making localgit: %v", err) 1227 } 1228 defer func() { 1229 if err := lg.Clean(); err != nil { 1230 t.Errorf("Cleaning up localgit: %v", err) 1231 } 1232 if err := c.Clean(); err != nil { 1233 t.Errorf("Cleaning up client: %v", err) 1234 } 1235 }() 1236 if err := lg.MakeFakeRepo("org", "repo"); err != nil { 1237 t.Fatalf("Making fake repo: %v", err) 1238 } 1239 for i, test := range tests { 1240 t.Run(test.name, func(t *testing.T) { 1241 pr := i + 1 1242 // make sure we're on master before branching 1243 if err := lg.Checkout("org", "repo", defaultBranch); err != nil { 1244 t.Fatalf("Switching to master branch: %v", err) 1245 } 1246 if len(test.filesRemoved) > 0 { 1247 if err := addFilesToRepo(lg, test.filesRemoved, test.ownersFile); err != nil { 1248 t.Fatalf("Adding base commit: %v", err) 1249 } 1250 } 1251 1252 if err := lg.CheckoutNewBranch("org", "repo", fmt.Sprintf("pull/%d/head", pr)); err != nil { 1253 t.Fatalf("Checking out pull branch: %v", err) 1254 } 1255 1256 if len(test.filesChanged) > 0 { 1257 if err := addFilesToRepo(lg, test.filesChanged, test.ownersFile); err != nil { 1258 t.Fatalf("Adding PR commit: %v", err) 1259 } 1260 } 1261 if len(test.filesRemoved) > 0 { 1262 if err := lg.RmCommit("org", "repo", test.filesRemoved); err != nil { 1263 t.Fatalf("Adding PR commit (removing files): %v", err) 1264 } 1265 } 1266 1267 sha, err := lg.RevParse("org", "repo", "HEAD") 1268 if err != nil { 1269 t.Fatalf("Getting commit SHA: %v", err) 1270 } 1271 1272 test.commentEvent.Repo.Owner.Login = "org" 1273 test.commentEvent.Repo.Name = "repo" 1274 test.commentEvent.Repo.FullName = "org/repo" 1275 test.commentEvent.Number = pr 1276 1277 fghc := newFakeGitHubClient(emptyPatch(test.filesChanged), test.filesRemoved, pr) 1278 fghc.PullRequests = map[int]*github.PullRequest{} 1279 fghc.PullRequests[pr] = &github.PullRequest{ 1280 User: github.User{Login: "author"}, 1281 Head: github.PullRequestBranch{ 1282 SHA: sha, 1283 }, 1284 Base: github.PullRequestBranch{ 1285 Ref: defaultBranch, 1286 }, 1287 } 1288 1289 if err := handleGenericComment(fghc, c, makeFakeRepoOwnersClient(), logrus.WithField("plugin", PluginName), &test.commentEvent, []string{labels.Approved, labels.LGTM}, plugins.Trigger{}, false, &fakePruner{}, ownersconfig.FakeResolver); err != nil { 1290 t.Fatalf("Handle PR: %v", err) 1291 } 1292 if !test.shouldLabel && IssueLabelsContain(fghc.IssueLabelsAdded, labels.InvalidOwners) { 1293 t.Errorf("%s: didn't expect label %s in %s", test.name, labels.InvalidOwners, fghc.IssueLabelsAdded) 1294 } else if test.shouldLabel && !IssueLabelsContain(fghc.IssueLabelsAdded, labels.InvalidOwners) { 1295 t.Errorf("%s: expected label %s in %s", test.name, labels.InvalidOwners, fghc.IssueLabelsAdded) 1296 } 1297 }) 1298 } 1299 } 1300 1301 func testOwnersRemoval(clients localgit.Clients, t *testing.T) { 1302 var tests = []struct { 1303 name string 1304 ownersRestored bool 1305 aliasesRestored bool 1306 shouldRemoveLabel bool 1307 }{ 1308 { 1309 name: "OWNERS and OWNERS_ALIASES files restored", 1310 ownersRestored: true, 1311 aliasesRestored: true, 1312 shouldRemoveLabel: true, 1313 }, 1314 { 1315 name: "OWNERS file restored, OWNERS_ALIASES left", 1316 ownersRestored: true, 1317 aliasesRestored: false, 1318 shouldRemoveLabel: true, 1319 }, 1320 { 1321 name: "OWNERS file left, OWNERS_ALIASES left", 1322 ownersRestored: false, 1323 aliasesRestored: false, 1324 shouldRemoveLabel: false, 1325 }, 1326 } 1327 lg, c, err := clients() 1328 if err != nil { 1329 t.Fatalf("Making localgit: %v", err) 1330 } 1331 defer func() { 1332 if err := lg.Clean(); err != nil { 1333 t.Errorf("Cleaning up localgit: %v", err) 1334 } 1335 if err := c.Clean(); err != nil { 1336 t.Errorf("Cleaning up client: %v", err) 1337 } 1338 }() 1339 if err := lg.MakeFakeRepo("org", "repo"); err != nil { 1340 t.Fatalf("Making fake repo: %v", err) 1341 } 1342 1343 if err := addFilesToRepo(lg, []string{"OWNERS"}, "valid"); err != nil { 1344 t.Fatalf("Adding base commit: %v", err) 1345 } 1346 1347 if err := addFilesToRepo(lg, []string{"OWNERS_ALIASES"}, "collaborators"); err != nil { 1348 t.Fatalf("Adding base commit: %v", err) 1349 } 1350 1351 for i, test := range tests { 1352 t.Run(test.name, func(t *testing.T) { 1353 pr := i + 1 1354 // make sure we're on master before branching 1355 if err := lg.Checkout("org", "repo", defaultBranch); err != nil { 1356 t.Fatalf("Switching to master branch: %v", err) 1357 } 1358 pullFiles := map[string][]byte{} 1359 pullFiles["a.go"] = []byte("foo") 1360 1361 if err := lg.CheckoutNewBranch("org", "repo", fmt.Sprintf("pull/%d/head", pr)); err != nil { 1362 t.Fatalf("Checking out pull branch: %v", err) 1363 } 1364 1365 if test.ownersRestored == false { 1366 if err := addFilesToRepo(lg, []string{"OWNERS"}, "invalidSyntax"); err != nil { 1367 t.Fatalf("Adding OWNERS file: %v", err) 1368 } 1369 } 1370 1371 if test.aliasesRestored == false { 1372 if err := addFilesToRepo(lg, []string{"OWNERS_ALIASES"}, "toBeAddedAlias"); err != nil { 1373 t.Fatalf("Adding OWNERS_ALIASES file: %v", err) 1374 } 1375 } 1376 1377 if err := lg.AddCommit("org", "repo", pullFiles); err != nil { 1378 t.Fatalf("Adding PR commit: %v", err) 1379 } 1380 sha, err := lg.RevParse("org", "repo", "HEAD") 1381 if err != nil { 1382 t.Fatalf("Getting commit SHA: %v", err) 1383 } 1384 pre := &github.PullRequestEvent{ 1385 PullRequest: github.PullRequest{ 1386 User: github.User{Login: "author"}, 1387 Base: github.PullRequestBranch{ 1388 Ref: defaultBranch, 1389 }, 1390 Head: github.PullRequestBranch{ 1391 SHA: sha, 1392 }, 1393 }, 1394 } 1395 files := make([]string, 3) 1396 files = append(files, "a.go") 1397 if !test.ownersRestored { 1398 files = append(files, "OWNERS") 1399 } 1400 if !test.aliasesRestored { 1401 files = append(files, "OWNERS_ALIASES") 1402 } 1403 fghc := newFakeGitHubClient(emptyPatch(files), nil, pr) 1404 1405 fghc.PullRequests = map[int]*github.PullRequest{} 1406 fghc.PullRequests[pr] = &github.PullRequest{ 1407 Base: github.PullRequestBranch{ 1408 Ref: fakegithub.TestRef, 1409 }, 1410 } 1411 1412 prInfo := info{ 1413 org: "org", 1414 repo: "repo", 1415 repoFullName: "org/repo", 1416 number: pr, 1417 } 1418 fghc.AddLabel(prInfo.org, prInfo.repo, prInfo.number, labels.InvalidOwners) 1419 1420 froc := makeFakeRepoOwnersClient() 1421 1422 if err := handle(fghc, c, froc, logrus.WithField("plugin", PluginName), &pre.PullRequest, prInfo, []string{labels.Approved, labels.LGTM}, plugins.Trigger{}, false, &fakePruner{}, ownersconfig.FakeResolver); err != nil { 1423 t.Fatalf("Handle PR: %v", err) 1424 } 1425 if test.shouldRemoveLabel && !IssueLabelsContain(fghc.IssueLabelsRemoved, labels.InvalidOwners) { 1426 t.Errorf("%s: expected label %s in %s", test.name, labels.InvalidOwners, fghc.IssueLabelsRemoved) 1427 } 1428 if !test.shouldRemoveLabel && IssueLabelsContain(fghc.IssueLabelsRemoved, labels.InvalidOwners) { 1429 t.Errorf("%s: didn't expect label %q in %s", test.name, labels.InvalidOwners, fghc.IssueLabelsRemoved) 1430 } 1431 }) 1432 } 1433 } 1434 1435 func TestOwnersRemovalV2(t *testing.T) { 1436 testOwnersRemoval(localgit.NewV2, t) 1437 }