github.com/yrj2011/jx-test-infra@v0.0.0-20190529031832-7a2065ee98eb/prow/repoowners/repoowners_test.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package repoowners 18 19 import ( 20 "fmt" 21 "path/filepath" 22 "reflect" 23 "regexp" 24 "strings" 25 "testing" 26 27 "github.com/sirupsen/logrus" 28 "k8s.io/apimachinery/pkg/util/sets" 29 30 prowConf "k8s.io/test-infra/prow/config" 31 "k8s.io/test-infra/prow/git/localgit" 32 "k8s.io/test-infra/prow/github/fakegithub" 33 ) 34 35 var ( 36 testFiles = map[string][]byte{ 37 "foo": []byte(`approvers: 38 - bob`), 39 "OWNERS": []byte(`approvers: 40 - cjwagner 41 reviewers: 42 - Alice 43 - bob 44 required_reviewers: 45 - chris 46 labels: 47 - EVERYTHING`), 48 "src/OWNERS": []byte(`approvers: 49 - Best-Approvers`), 50 "src/dir/OWNERS": []byte(`approvers: 51 - bob 52 reviewers: 53 - alice 54 - CJWagner 55 - jakub 56 required_reviewers: 57 - ben 58 labels: 59 - src-code`), 60 "src/dir/conformance/OWNERS": []byte(`options: 61 no_parent_owners: true 62 approvers: 63 - mml`), 64 "docs/file.md": []byte(`--- 65 approvers: 66 - ALICE 67 68 labels: 69 - docs 70 ---`), 71 } 72 73 testFilesRe = map[string][]byte{ 74 // regexp filtered 75 "re/OWNERS": []byte(`filters: 76 ".*": 77 labels: 78 - re/all 79 "\\.go$": 80 labels: 81 - re/go`), 82 "re/a/OWNERS": []byte(`filters: 83 "\\.md$": 84 labels: 85 - re/md-in-a 86 "\\.go$": 87 labels: 88 - re/go-in-a`), 89 } 90 ) 91 92 // regexpAll is used to construct a default {regexp -> values} mapping for ".*" 93 func regexpAll(values ...string) map[*regexp.Regexp]sets.String { 94 return map[*regexp.Regexp]sets.String{nil: sets.NewString(values...)} 95 } 96 97 // patternAll is used to construct a default {regexp string -> values} mapping for ".*" 98 func patternAll(values ...string) map[string]sets.String { 99 // use "" to represent nil and distinguish it from a ".*" regexp (which shouldn't exist). 100 return map[string]sets.String{"": sets.NewString(values...)} 101 } 102 103 type testConfigGetter struct { 104 defaultBlacklist []string 105 repoBlacklist map[string][]string 106 } 107 108 func (c testConfigGetter) Config() *prowConf.Config { 109 return &prowConf.Config{ 110 ProwConfig: prowConf.ProwConfig{ 111 OwnersDirBlacklist: prowConf.OwnersDirBlacklist{ 112 Repos: c.repoBlacklist, 113 Default: c.defaultBlacklist, 114 }, 115 }, 116 } 117 } 118 119 func getTestClient( 120 files map[string][]byte, 121 enableMdYaml, 122 skipCollab, 123 includeAliases bool, 124 ownersDirBlacklistDefault []string, 125 ownersDirBlacklistByRepo map[string][]string, 126 extraBranchesAndFiles map[string]map[string][]byte, 127 ) (*Client, func(), error) { 128 testAliasesFile := map[string][]byte{ 129 "OWNERS_ALIASES": []byte("aliases:\n Best-approvers:\n - carl\n - cjwagner\n best-reviewers:\n - Carl\n - BOB"), 130 } 131 132 localGit, git, err := localgit.New() 133 if err != nil { 134 return nil, nil, err 135 } 136 if err := localGit.MakeFakeRepo("org", "repo"); err != nil { 137 return nil, nil, fmt.Errorf("cannot make fake repo: %v", err) 138 } 139 if err := localGit.AddCommit("org", "repo", files); err != nil { 140 return nil, nil, fmt.Errorf("cannot add initial commit: %v", err) 141 } 142 if includeAliases { 143 if err := localGit.AddCommit("org", "repo", testAliasesFile); err != nil { 144 return nil, nil, fmt.Errorf("cannot add OWNERS_ALIASES commit: %v", err) 145 } 146 } 147 if len(extraBranchesAndFiles) > 0 { 148 for branch, extraFiles := range extraBranchesAndFiles { 149 if err := localGit.CheckoutNewBranch("org", "repo", branch); err != nil { 150 return nil, nil, err 151 } 152 if len(extraFiles) > 0 { 153 if err := localGit.AddCommit("org", "repo", extraFiles); err != nil { 154 return nil, nil, fmt.Errorf("cannot add commit: %v", err) 155 } 156 } 157 } 158 if err := localGit.Checkout("org", "repo", "master"); err != nil { 159 return nil, nil, err 160 } 161 } 162 163 return &Client{ 164 git: git, 165 ghc: &fakegithub.FakeClient{Collaborators: []string{"cjwagner", "k8s-ci-robot", "alice", "bob", "carl", "mml", "maggie"}}, 166 logger: logrus.WithField("client", "repoowners"), 167 cache: make(map[string]cacheEntry), 168 169 mdYAMLEnabled: func(org, repo string) bool { 170 return enableMdYaml 171 }, 172 skipCollaborators: func(org, repo string) bool { 173 return skipCollab 174 }, 175 configGetter: testConfigGetter{ 176 repoBlacklist: ownersDirBlacklistByRepo, 177 defaultBlacklist: ownersDirBlacklistDefault, 178 }, 179 }, 180 // Clean up function 181 func() { 182 git.Clean() 183 localGit.Clean() 184 }, 185 nil 186 } 187 188 func TestOwnersDirBlacklist(t *testing.T) { 189 validatorExcluded := func(t *testing.T, ro *RepoOwners) { 190 for dir := range ro.approvers { 191 if strings.Contains(dir, "src") { 192 t.Errorf("Expected directory %s to be excluded from the approvers map", dir) 193 } 194 } 195 for dir := range ro.reviewers { 196 if strings.Contains(dir, "src") { 197 t.Errorf("Expected directory %s to be excluded from the reviewers map", dir) 198 } 199 } 200 } 201 202 validatorIncluded := func(t *testing.T, ro *RepoOwners) { 203 approverFound := false 204 for dir := range ro.approvers { 205 if strings.Contains(dir, "src") { 206 approverFound = true 207 break 208 } 209 } 210 if !approverFound { 211 t.Errorf("Expected to find approvers for a path matching */src/*") 212 } 213 214 reviewerFound := false 215 for dir := range ro.reviewers { 216 if strings.Contains(dir, "src") { 217 reviewerFound = true 218 break 219 } 220 } 221 if !reviewerFound { 222 t.Errorf("Expected to find reviewers for a path matching */src/*") 223 } 224 } 225 226 getRepoOwnersWithBlacklist := func(t *testing.T, defaults []string, byRepo map[string][]string) *RepoOwners { 227 client, cleanup, err := getTestClient(testFiles, true, false, true, defaults, byRepo, nil) 228 if err != nil { 229 t.Fatalf("Error creating test client: %v.", err) 230 } 231 defer cleanup() 232 233 ro, err := client.LoadRepoOwners("org", "repo", "master") 234 if err != nil { 235 t.Fatalf("Unexpected error loading RepoOwners: %v.", err) 236 } 237 238 return ro.(*RepoOwners) 239 } 240 241 type testConf struct { 242 blackistDefault []string 243 blacklistByRepo map[string][]string 244 validator func(t *testing.T, ro *RepoOwners) 245 } 246 247 tests := map[string]testConf{} 248 249 tests["blacklist by org"] = testConf{ 250 blacklistByRepo: map[string][]string{ 251 "org": {"src"}, 252 }, 253 validator: validatorExcluded, 254 } 255 tests["blacklist by org/repo"] = testConf{ 256 blacklistByRepo: map[string][]string{ 257 "org/repo": {"src"}, 258 }, 259 validator: validatorExcluded, 260 } 261 tests["blacklist by default"] = testConf{ 262 blackistDefault: []string{"src"}, 263 validator: validatorExcluded, 264 } 265 tests["no blacklist setup"] = testConf{ 266 validator: validatorIncluded, 267 } 268 tests["blacklist setup but not matching this repo"] = testConf{ 269 blacklistByRepo: map[string][]string{ 270 "not_org/not_repo": {"src"}, 271 "not_org": {"src"}, 272 }, 273 validator: validatorIncluded, 274 } 275 276 for name, conf := range tests { 277 t.Run(name, func(t *testing.T) { 278 ro := getRepoOwnersWithBlacklist(t, conf.blackistDefault, conf.blacklistByRepo) 279 conf.validator(t, ro) 280 }) 281 } 282 } 283 284 func TestOwnersRegexpFiltering(t *testing.T) { 285 tests := map[string]sets.String{ 286 "re/a/go.go": sets.NewString("re/all", "re/go", "re/go-in-a"), 287 "re/a/md.md": sets.NewString("re/all", "re/md-in-a"), 288 "re/a/txt.txt": sets.NewString("re/all"), 289 "re/go.go": sets.NewString("re/all", "re/go"), 290 "re/txt.txt": sets.NewString("re/all"), 291 "re/b/md.md": sets.NewString("re/all"), 292 } 293 294 client, cleanup, err := getTestClient(testFilesRe, true, false, true, nil, nil, nil) 295 if err != nil { 296 t.Fatalf("Error creating test client: %v.", err) 297 } 298 defer cleanup() 299 300 r, err := client.LoadRepoOwners("org", "repo", "master") 301 if err != nil { 302 t.Fatalf("Unexpected error loading RepoOwners: %v.", err) 303 } 304 ro := r.(*RepoOwners) 305 t.Logf("labels: %#v\n\n", ro.labels) 306 for file, expected := range tests { 307 if got := ro.FindLabelsForFile(file); !got.Equal(expected) { 308 t.Errorf("For file %q expected labels %q, but got %q.", file, expected.List(), got.List()) 309 } 310 } 311 } 312 313 func strP(str string) *string { 314 return &str 315 } 316 317 func TestLoadRepoOwners(t *testing.T) { 318 tests := []struct { 319 name string 320 mdEnabled bool 321 aliasesFileExists bool 322 skipCollaborators bool 323 // used for testing OWNERS from a branch different from master 324 branch *string 325 extraBranchesAndFiles map[string]map[string][]byte 326 327 expectedApprovers, expectedReviewers, expectedRequiredReviewers, expectedLabels map[string]map[string]sets.String 328 329 expectedOptions map[string]dirOptions 330 }{ 331 { 332 name: "no alias, no md", 333 expectedApprovers: map[string]map[string]sets.String{ 334 "": patternAll("cjwagner"), 335 "src": patternAll(), 336 "src/dir": patternAll("bob"), 337 "src/dir/conformance": patternAll("mml"), 338 }, 339 expectedReviewers: map[string]map[string]sets.String{ 340 "": patternAll("alice", "bob"), 341 "src/dir": patternAll("alice", "cjwagner"), 342 }, 343 expectedRequiredReviewers: map[string]map[string]sets.String{ 344 "": patternAll("chris"), 345 "src/dir": patternAll("ben"), 346 }, 347 expectedLabels: map[string]map[string]sets.String{ 348 "": patternAll("EVERYTHING"), 349 "src/dir": patternAll("src-code"), 350 }, 351 expectedOptions: map[string]dirOptions{ 352 "src/dir/conformance": { 353 NoParentOwners: true, 354 }, 355 }, 356 }, 357 { 358 name: "alias, no md", 359 aliasesFileExists: true, 360 expectedApprovers: map[string]map[string]sets.String{ 361 "": patternAll("cjwagner"), 362 "src": patternAll("carl", "cjwagner"), 363 "src/dir": patternAll("bob"), 364 "src/dir/conformance": patternAll("mml"), 365 }, 366 expectedReviewers: map[string]map[string]sets.String{ 367 "": patternAll("alice", "bob"), 368 "src/dir": patternAll("alice", "cjwagner"), 369 }, 370 expectedRequiredReviewers: map[string]map[string]sets.String{ 371 "": patternAll("chris"), 372 "src/dir": patternAll("ben"), 373 }, 374 expectedLabels: map[string]map[string]sets.String{ 375 "": patternAll("EVERYTHING"), 376 "src/dir": patternAll("src-code"), 377 }, 378 expectedOptions: map[string]dirOptions{ 379 "src/dir/conformance": { 380 NoParentOwners: true, 381 }, 382 }, 383 }, 384 { 385 name: "alias, md", 386 aliasesFileExists: true, 387 mdEnabled: true, 388 expectedApprovers: map[string]map[string]sets.String{ 389 "": patternAll("cjwagner"), 390 "src": patternAll("carl", "cjwagner"), 391 "src/dir": patternAll("bob"), 392 "src/dir/conformance": patternAll("mml"), 393 "docs/file.md": patternAll("alice"), 394 }, 395 expectedReviewers: map[string]map[string]sets.String{ 396 "": patternAll("alice", "bob"), 397 "src/dir": patternAll("alice", "cjwagner"), 398 }, 399 expectedRequiredReviewers: map[string]map[string]sets.String{ 400 "": patternAll("chris"), 401 "src/dir": patternAll("ben"), 402 }, 403 expectedLabels: map[string]map[string]sets.String{ 404 "": patternAll("EVERYTHING"), 405 "src/dir": patternAll("src-code"), 406 "docs/file.md": patternAll("docs"), 407 }, 408 expectedOptions: map[string]dirOptions{ 409 "src/dir/conformance": { 410 NoParentOwners: true, 411 }, 412 }, 413 }, 414 { 415 name: "OWNERS from non-default branch", 416 branch: strP("release-1.10"), 417 extraBranchesAndFiles: map[string]map[string][]byte{ 418 "release-1.10": { 419 "src/doc/OWNERS": []byte("approvers:\n - maggie\n"), 420 }, 421 }, 422 expectedApprovers: map[string]map[string]sets.String{ 423 "": patternAll("cjwagner"), 424 "src": patternAll(), 425 "src/dir": patternAll("bob"), 426 "src/dir/conformance": patternAll("mml"), 427 "src/doc": patternAll("maggie"), 428 }, 429 expectedReviewers: map[string]map[string]sets.String{ 430 "": patternAll("alice", "bob"), 431 "src/dir": patternAll("alice", "cjwagner"), 432 }, 433 expectedRequiredReviewers: map[string]map[string]sets.String{ 434 "": patternAll("chris"), 435 "src/dir": patternAll("ben"), 436 }, 437 expectedLabels: map[string]map[string]sets.String{ 438 "": patternAll("EVERYTHING"), 439 "src/dir": patternAll("src-code"), 440 }, 441 expectedOptions: map[string]dirOptions{ 442 "src/dir/conformance": { 443 NoParentOwners: true, 444 }, 445 }, 446 }, 447 { 448 name: "OWNERS from master branch while release branch diverges", 449 branch: strP("master"), 450 extraBranchesAndFiles: map[string]map[string][]byte{ 451 "release-1.10": { 452 "src/doc/OWNERS": []byte("approvers:\n - maggie\n"), 453 }, 454 }, 455 expectedApprovers: map[string]map[string]sets.String{ 456 "": patternAll("cjwagner"), 457 "src": patternAll(), 458 "src/dir": patternAll("bob"), 459 "src/dir/conformance": patternAll("mml"), 460 }, 461 expectedReviewers: map[string]map[string]sets.String{ 462 "": patternAll("alice", "bob"), 463 "src/dir": patternAll("alice", "cjwagner"), 464 }, 465 expectedRequiredReviewers: map[string]map[string]sets.String{ 466 "": patternAll("chris"), 467 "src/dir": patternAll("ben"), 468 }, 469 expectedLabels: map[string]map[string]sets.String{ 470 "": patternAll("EVERYTHING"), 471 "src/dir": patternAll("src-code"), 472 }, 473 expectedOptions: map[string]dirOptions{ 474 "src/dir/conformance": { 475 NoParentOwners: true, 476 }, 477 }, 478 }, 479 { 480 name: "Skip collaborator checks, use only OWNERS files", 481 skipCollaborators: true, 482 expectedApprovers: map[string]map[string]sets.String{ 483 "": patternAll("cjwagner"), 484 "src": patternAll("best-approvers"), 485 "src/dir": patternAll("bob"), 486 "src/dir/conformance": patternAll("mml"), 487 }, 488 expectedReviewers: map[string]map[string]sets.String{ 489 "": patternAll("alice", "bob"), 490 "src/dir": patternAll("alice", "cjwagner", "jakub"), 491 }, 492 expectedRequiredReviewers: map[string]map[string]sets.String{ 493 "": patternAll("chris"), 494 "src/dir": patternAll("ben"), 495 }, 496 expectedLabels: map[string]map[string]sets.String{ 497 "": patternAll("EVERYTHING"), 498 "src/dir": patternAll("src-code"), 499 }, 500 expectedOptions: map[string]dirOptions{ 501 "src/dir/conformance": { 502 NoParentOwners: true, 503 }, 504 }, 505 }, 506 } 507 508 for _, test := range tests { 509 t.Logf("Running scenario %q", test.name) 510 client, cleanup, err := getTestClient(testFiles, test.mdEnabled, test.skipCollaborators, test.aliasesFileExists, nil, nil, test.extraBranchesAndFiles) 511 if err != nil { 512 t.Errorf("Error creating test client: %v.", err) 513 continue 514 } 515 defer cleanup() 516 517 base := "master" 518 if test.branch != nil { 519 base = *test.branch 520 } 521 r, err := client.LoadRepoOwners("org", "repo", base) 522 if err != nil { 523 t.Errorf("Unexpected error loading RepoOwners: %v.", err) 524 continue 525 } 526 ro := r.(*RepoOwners) 527 528 if ro.baseDir == "" { 529 t.Errorf("Expected 'baseDir' to be populated.") 530 continue 531 } 532 if (ro.RepoAliases != nil) != test.aliasesFileExists { 533 t.Errorf("Expected 'RepoAliases' to be poplulated: %t, but got %t.", test.aliasesFileExists, ro.RepoAliases != nil) 534 continue 535 } 536 if ro.enableMDYAML != test.mdEnabled { 537 t.Errorf("Expected 'enableMdYaml' to be: %t, but got %t.", test.mdEnabled, ro.enableMDYAML) 538 continue 539 } 540 541 check := func(field string, expected map[string]map[string]sets.String, got map[string]map[*regexp.Regexp]sets.String) { 542 converted := map[string]map[string]sets.String{} 543 for path, m := range got { 544 converted[path] = map[string]sets.String{} 545 for re, s := range m { 546 var pattern string 547 if re != nil { 548 pattern = re.String() 549 } 550 converted[path][pattern] = s 551 } 552 } 553 if !reflect.DeepEqual(expected, converted) { 554 t.Errorf("Expected %s to be:\n%+v\ngot:\n%+v.", field, expected, converted) 555 } 556 } 557 check("approvers", test.expectedApprovers, ro.approvers) 558 check("reviewers", test.expectedReviewers, ro.reviewers) 559 check("required_reviewers", test.expectedRequiredReviewers, ro.requiredReviewers) 560 check("labels", test.expectedLabels, ro.labels) 561 if !reflect.DeepEqual(test.expectedOptions, ro.options) { 562 t.Errorf("Expected options to be:\n%#v\ngot:\n%#v.", test.expectedOptions, ro.options) 563 } 564 } 565 } 566 567 func TestLoadRepoAliases(t *testing.T) { 568 tests := []struct { 569 name string 570 571 aliasFileExists bool 572 branch *string 573 extraBranchesAndFiles map[string]map[string][]byte 574 575 expectedRepoAliases RepoAliases 576 }{ 577 { 578 name: "No aliases file", 579 aliasFileExists: false, 580 expectedRepoAliases: nil, 581 }, 582 { 583 name: "Normal aliases file", 584 aliasFileExists: true, 585 expectedRepoAliases: RepoAliases{ 586 "best-approvers": sets.NewString("carl", "cjwagner"), 587 "best-reviewers": sets.NewString("carl", "bob"), 588 }, 589 }, 590 { 591 name: "Aliases file from non-default branch", 592 593 aliasFileExists: true, 594 branch: strP("release-1.10"), 595 extraBranchesAndFiles: map[string]map[string][]byte{ 596 "release-1.10": { 597 "OWNERS_ALIASES": []byte("aliases:\n Best-approvers:\n - carl\n - cjwagner\n best-reviewers:\n - Carl\n - BOB\n - maggie"), 598 }, 599 }, 600 601 expectedRepoAliases: RepoAliases{ 602 "best-approvers": sets.NewString("carl", "cjwagner"), 603 "best-reviewers": sets.NewString("carl", "bob", "maggie"), 604 }, 605 }, 606 } 607 for _, test := range tests { 608 client, cleanup, err := getTestClient(testFiles, false, false, test.aliasFileExists, nil, nil, test.extraBranchesAndFiles) 609 if err != nil { 610 t.Errorf("[%s] Error creating test client: %v.", test.name, err) 611 continue 612 } 613 614 branch := "master" 615 if test.branch != nil { 616 branch = *test.branch 617 } 618 got, err := client.LoadRepoAliases("org", "repo", branch) 619 if err != nil { 620 t.Errorf("[%s] Unexpected error loading RepoAliases: %v.", test.name, err) 621 cleanup() 622 continue 623 } 624 if !reflect.DeepEqual(got, test.expectedRepoAliases) { 625 t.Errorf("[%s] Expected RepoAliases: %#v, but got: %#v.", test.name, test.expectedRepoAliases, got) 626 } 627 cleanup() 628 } 629 } 630 631 const ( 632 baseDir = "" 633 leafDir = "a/b/c" 634 noParentsDir = "d" 635 nonExistentDir = "DELETED_DIR" 636 ) 637 638 func TestGetApprovers(t *testing.T) { 639 ro := &RepoOwners{ 640 approvers: map[string]map[*regexp.Regexp]sets.String{ 641 baseDir: regexpAll("alice", "bob"), 642 leafDir: regexpAll("carl", "dave"), 643 noParentsDir: regexpAll("mml"), 644 }, 645 options: map[string]dirOptions{ 646 noParentsDir: { 647 NoParentOwners: true, 648 }, 649 }, 650 } 651 tests := []struct { 652 name string 653 filePath string 654 expectedOwnersPath string 655 expectedLeafOwners sets.String 656 expectedAllOwners sets.String 657 }{ 658 { 659 name: "Modified Base Dir Only", 660 filePath: filepath.Join(baseDir, "testFile.md"), 661 expectedOwnersPath: baseDir, 662 expectedLeafOwners: ro.approvers[baseDir][nil], 663 expectedAllOwners: ro.approvers[baseDir][nil], 664 }, 665 { 666 name: "Modified Leaf Dir Only", 667 filePath: filepath.Join(leafDir, "testFile.md"), 668 expectedOwnersPath: leafDir, 669 expectedLeafOwners: ro.approvers[leafDir][nil], 670 expectedAllOwners: ro.approvers[baseDir][nil].Union(ro.approvers[leafDir][nil]), 671 }, 672 { 673 name: "Modified NoParentOwners Dir Only", 674 filePath: filepath.Join(noParentsDir, "testFile.go"), 675 expectedOwnersPath: noParentsDir, 676 expectedLeafOwners: ro.approvers[noParentsDir][nil], 677 expectedAllOwners: ro.approvers[noParentsDir][nil], 678 }, 679 { 680 name: "Modified Nonexistent Dir (Default to Base)", 681 filePath: filepath.Join(nonExistentDir, "testFile.md"), 682 expectedOwnersPath: baseDir, 683 expectedLeafOwners: ro.approvers[baseDir][nil], 684 expectedAllOwners: ro.approvers[baseDir][nil], 685 }, 686 } 687 for testNum, test := range tests { 688 foundLeafApprovers := ro.LeafApprovers(test.filePath) 689 foundApprovers := ro.Approvers(test.filePath) 690 foundOwnersPath := ro.FindApproverOwnersForFile(test.filePath) 691 if !foundLeafApprovers.Equal(test.expectedLeafOwners) { 692 t.Errorf("The Leaf Approvers Found Do Not Match Expected For Test %d: %s", testNum, test.name) 693 t.Errorf("\tExpected Owners: %v\tFound Owners: %v ", test.expectedLeafOwners, foundLeafApprovers) 694 } 695 if !foundApprovers.Equal(test.expectedAllOwners) { 696 t.Errorf("The Approvers Found Do Not Match Expected For Test %d: %s", testNum, test.name) 697 t.Errorf("\tExpected Owners: %v\tFound Owners: %v ", test.expectedAllOwners, foundApprovers) 698 } 699 if foundOwnersPath != test.expectedOwnersPath { 700 t.Errorf("The Owners Path Found Does Not Match Expected For Test %d: %s", testNum, test.name) 701 t.Errorf("\tExpected Owners: %v\tFound Owners: %v ", test.expectedOwnersPath, foundOwnersPath) 702 } 703 } 704 } 705 706 func TestFindLabelsForPath(t *testing.T) { 707 tests := []struct { 708 name string 709 path string 710 expectedLabels sets.String 711 }{ 712 { 713 name: "base 1", 714 path: "foo.txt", 715 expectedLabels: sets.NewString("sig/godzilla"), 716 }, { 717 name: "base 2", 718 path: "./foo.txt", 719 expectedLabels: sets.NewString("sig/godzilla"), 720 }, { 721 name: "base 3", 722 path: "", 723 expectedLabels: sets.NewString("sig/godzilla"), 724 }, { 725 name: "base 4", 726 path: ".", 727 expectedLabels: sets.NewString("sig/godzilla"), 728 }, { 729 name: "leaf 1", 730 path: "a/b/c/foo.txt", 731 expectedLabels: sets.NewString("sig/godzilla", "wg/save-tokyo"), 732 }, { 733 name: "leaf 2", 734 path: "a/b/foo.txt", 735 expectedLabels: sets.NewString("sig/godzilla"), 736 }, 737 } 738 739 testOwners := &RepoOwners{ 740 labels: map[string]map[*regexp.Regexp]sets.String{ 741 baseDir: regexpAll("sig/godzilla"), 742 leafDir: regexpAll("wg/save-tokyo"), 743 }, 744 } 745 for _, test := range tests { 746 got := testOwners.FindLabelsForFile(test.path) 747 if !got.Equal(test.expectedLabels) { 748 t.Errorf( 749 "[%s] Expected labels %q for path %q, but got %q.", 750 test.name, 751 test.expectedLabels.List(), 752 test.path, 753 got.List(), 754 ) 755 } 756 } 757 } 758 759 func TestCanonicalize(t *testing.T) { 760 tests := []struct { 761 name string 762 path string 763 expectedPath string 764 }{ 765 { 766 name: "Empty String", 767 path: "", 768 expectedPath: "", 769 }, 770 { 771 name: "Dot (.) as Path", 772 path: ".", 773 expectedPath: "", 774 }, 775 { 776 name: "Github Style Input (No Root)", 777 path: "a/b/c/d.txt", 778 expectedPath: "a/b/c/d.txt", 779 }, 780 { 781 name: "Preceding Slash and Trailing Slash", 782 path: "/a/b/", 783 expectedPath: "/a/b", 784 }, 785 { 786 name: "Trailing Slash", 787 path: "foo/bar/baz/", 788 expectedPath: "foo/bar/baz", 789 }, 790 } 791 for _, test := range tests { 792 if got := canonicalize(test.path); test.expectedPath != got { 793 t.Errorf( 794 "[%s] Expected the canonical path for %v to be %v. Found %v instead", 795 test.name, 796 test.path, 797 test.expectedPath, 798 got, 799 ) 800 } 801 } 802 } 803 804 var ( 805 lowerCaseAliases = []byte(` 806 aliases: 807 team/t1: 808 - u1 809 - u2 810 team/t2: 811 - u1 812 - u3`) 813 mixedCaseAliases = []byte(` 814 aliases: 815 TEAM/T1: 816 - U1 817 - U2`) 818 ) 819 820 func TestExpandAliases(t *testing.T) { 821 testAliases := RepoAliases{ 822 "team/t1": sets.NewString("u1", "u2"), 823 "team/t2": sets.NewString("u1", "u3"), 824 } 825 tests := []struct { 826 name string 827 unexpanded sets.String 828 expectedExpanded sets.String 829 }{ 830 { 831 name: "No expansions.", 832 unexpanded: sets.NewString("abc", "def"), 833 expectedExpanded: sets.NewString("abc", "def"), 834 }, 835 { 836 name: "One alias to be expanded", 837 unexpanded: sets.NewString("abc", "team/t1"), 838 expectedExpanded: sets.NewString("abc", "u1", "u2"), 839 }, 840 { 841 name: "Duplicates inside and outside alias.", 842 unexpanded: sets.NewString("u1", "team/t1"), 843 expectedExpanded: sets.NewString("u1", "u2"), 844 }, 845 { 846 name: "Duplicates in multiple aliases.", 847 unexpanded: sets.NewString("u1", "team/t1", "team/t2"), 848 expectedExpanded: sets.NewString("u1", "u2", "u3"), 849 }, 850 { 851 name: "Mixed casing in aliases.", 852 unexpanded: sets.NewString("Team/T1"), 853 expectedExpanded: sets.NewString("u1", "u2"), 854 }, 855 } 856 857 for _, test := range tests { 858 if got := testAliases.ExpandAliases(test.unexpanded); !test.expectedExpanded.Equal(got) { 859 t.Errorf( 860 "[%s] Expected %q to expand to %q, but got %q.", 861 test.name, 862 test.unexpanded.List(), 863 test.expectedExpanded.List(), 864 got.List(), 865 ) 866 } 867 } 868 }