github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/plugins/approve/approvers/owners_test.go (about) 1 /* 2 Copyright 2016 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 approvers 18 19 import ( 20 "path/filepath" 21 "reflect" 22 "strings" 23 "testing" 24 25 "github.com/sirupsen/logrus" 26 27 "k8s.io/apimachinery/pkg/util/sets" 28 29 "sigs.k8s.io/prow/pkg/layeredsets" 30 "sigs.k8s.io/prow/pkg/plugins/ownersconfig" 31 ) 32 33 const ( 34 TestSeed = int64(0) 35 ) 36 37 type FakeRepo struct { 38 approversMap map[string]layeredsets.String 39 leafApproversMap map[string]sets.Set[string] 40 noParentOwnersMap map[string]bool 41 autoApproveUnownedSubfolders map[string]bool 42 } 43 44 func (f FakeRepo) Filenames() ownersconfig.Filenames { 45 return ownersconfig.FakeFilenames 46 } 47 48 func (f FakeRepo) Approvers(path string) layeredsets.String { 49 ret := f.approversMap[path] 50 if ret.Len() > 0 || path == "" { 51 return ret 52 } 53 54 p := filepath.Dir(path) 55 if p == "." { 56 p = "" 57 } 58 return f.Approvers(p) 59 } 60 61 func (f FakeRepo) LeafApprovers(path string) sets.Set[string] { 62 ret := f.leafApproversMap[path] 63 64 if ret.Len() > 0 || path == "" { 65 return ret 66 } 67 68 p := filepath.Dir(path) 69 if p == "." { 70 p = "" 71 } 72 return f.LeafApprovers(p) 73 } 74 75 func (f FakeRepo) FindApproverOwnersForFile(path string) string { 76 for dir := path; dir != "."; dir = filepath.Dir(dir) { 77 if _, ok := f.leafApproversMap[dir]; ok { 78 return dir 79 } 80 } 81 return "" 82 } 83 84 func (f FakeRepo) IsNoParentOwners(path string) bool { 85 return f.noParentOwnersMap[path] 86 } 87 88 func (f FakeRepo) IsAutoApproveUnownedSubfolders(ownerFilePath string) bool { 89 return f.autoApproveUnownedSubfolders[ownerFilePath] 90 } 91 92 func canonicalize(path string) string { 93 if path == "." { 94 return "" 95 } 96 return strings.TrimSuffix(path, "/") 97 } 98 99 func createFakeRepo(leafApproversMap map[string]sets.Set[string], modify ...func(*FakeRepo)) FakeRepo { 100 // github doesn't use / at the root 101 a := map[string]layeredsets.String{} 102 for dir, approvers := range leafApproversMap { 103 leafApproversMap[dir] = setToLower(approvers) 104 a[dir] = setToLowerMulti(approvers) 105 startingPath := dir 106 for { 107 dir = canonicalize(filepath.Dir(dir)) 108 if parentApprovers, ok := leafApproversMap[dir]; ok { 109 a[startingPath] = a[startingPath].Union(setToLowerMulti(parentApprovers)) 110 } 111 if dir == "" { 112 break 113 } 114 } 115 } 116 117 fr := FakeRepo{approversMap: a, leafApproversMap: leafApproversMap} 118 for _, m := range modify { 119 m(&fr) 120 } 121 return fr 122 } 123 124 func setToLower(s sets.Set[string]) sets.Set[string] { 125 lowered := sets.New[string]() 126 for _, elem := range sets.List(s) { 127 lowered.Insert(strings.ToLower(elem)) 128 } 129 return lowered 130 } 131 132 func setToLowerMulti(s sets.Set[string]) layeredsets.String { 133 lowered := layeredsets.NewString() 134 for _, elem := range sets.List(s) { 135 lowered.Insert(0, strings.ToLower(elem)) 136 } 137 return lowered 138 } 139 140 func TestCreateFakeRepo(t *testing.T) { 141 rootApprovers := sets.New[string]("Alice", "Bob") 142 aApprovers := sets.New[string]("Art", "Anne") 143 bApprovers := sets.New[string]("Bill", "Ben", "Barbara") 144 cApprovers := sets.New[string]("Chris", "Carol") 145 eApprovers := sets.New[string]("Eve", "Erin") 146 edcApprovers := eApprovers.Union(cApprovers) 147 FakeRepoMap := map[string]sets.Set[string]{ 148 "": rootApprovers, 149 "a": aApprovers, 150 "b": bApprovers, 151 "c": cApprovers, 152 "a/combo": edcApprovers, 153 } 154 fakeRepo := createFakeRepo(FakeRepoMap) 155 156 tests := []struct { 157 testName string 158 ownersFile string 159 expectedLeafApprovers sets.Set[string] 160 expectedApprovers sets.Set[string] 161 }{ 162 { 163 testName: "Root Owners", 164 ownersFile: "", 165 expectedApprovers: rootApprovers, 166 expectedLeafApprovers: rootApprovers, 167 }, 168 { 169 testName: "A Owners", 170 ownersFile: "a", 171 expectedLeafApprovers: aApprovers, 172 expectedApprovers: aApprovers.Union(rootApprovers), 173 }, 174 { 175 testName: "B Owners", 176 ownersFile: "b", 177 expectedLeafApprovers: bApprovers, 178 expectedApprovers: bApprovers.Union(rootApprovers), 179 }, 180 { 181 testName: "C Owners", 182 ownersFile: "c", 183 expectedLeafApprovers: cApprovers, 184 expectedApprovers: cApprovers.Union(rootApprovers), 185 }, 186 { 187 testName: "Combo Owners", 188 ownersFile: "a/combo", 189 expectedLeafApprovers: edcApprovers, 190 expectedApprovers: edcApprovers.Union(aApprovers).Union(rootApprovers), 191 }, 192 } 193 194 for _, test := range tests { 195 calculatedLeafApprovers := fakeRepo.LeafApprovers(test.ownersFile) 196 calculatedApprovers := fakeRepo.Approvers(test.ownersFile) 197 198 test.expectedLeafApprovers = setToLower(test.expectedLeafApprovers) 199 if !calculatedLeafApprovers.Equal(test.expectedLeafApprovers) { 200 t.Errorf("Failed for test %v. Expected Leaf Approvers: %v. Actual Leaf Approvers %v", test.testName, test.expectedLeafApprovers, calculatedLeafApprovers) 201 } 202 203 test.expectedApprovers = setToLower(test.expectedApprovers) 204 if !calculatedApprovers.Set().Equal(test.expectedApprovers) { 205 t.Errorf("Failed for test %v. Expected Approvers: %v. Actual Approvers %v", test.testName, test.expectedApprovers, calculatedApprovers) 206 } 207 } 208 } 209 210 func TestGetLeafApprovers(t *testing.T) { 211 rootApprovers := sets.New[string]("Alice", "Bob") 212 aApprovers := sets.New[string]("Art", "Anne") 213 bApprovers := sets.New[string]("Bill", "Ben", "Barbara") 214 dApprovers := sets.New[string]("David", "Dan", "Debbie") 215 FakeRepoMap := map[string]sets.Set[string]{ 216 "": rootApprovers, 217 "a": aApprovers, 218 "b": bApprovers, 219 "a/d": dApprovers, 220 } 221 222 tests := []struct { 223 testName string 224 filenames []string 225 expectedMap map[string]sets.Set[string] 226 }{ 227 { 228 testName: "Empty PR", 229 filenames: []string{}, 230 expectedMap: map[string]sets.Set[string]{}, 231 }, 232 { 233 testName: "Single Root File PR", 234 filenames: []string{"kubernetes.go"}, 235 expectedMap: map[string]sets.Set[string]{"": setToLower(rootApprovers)}, 236 }, 237 { 238 testName: "Internal Node File PR", 239 filenames: []string{"a/test.go"}, 240 expectedMap: map[string]sets.Set[string]{"a": setToLower(aApprovers)}, 241 }, 242 { 243 testName: "Two Leaf File PR", 244 filenames: []string{"a/d/test.go", "b/test.go"}, 245 expectedMap: map[string]sets.Set[string]{ 246 "a/d": setToLower(dApprovers), 247 "b": setToLower(bApprovers)}, 248 }, 249 { 250 testName: "Leaf and Parent 2 File PR", 251 filenames: []string{"a/test.go", "a/d/test.go"}, 252 expectedMap: map[string]sets.Set[string]{ 253 "a": setToLower(aApprovers), 254 }, 255 }, 256 } 257 258 for _, test := range tests { 259 testOwners := Owners{ 260 filenames: test.filenames, 261 repo: createFakeRepo(FakeRepoMap), 262 seed: TestSeed, 263 log: logrus.WithField("plugin", "some_plugin"), 264 } 265 oMap := testOwners.GetLeafApprovers() 266 if !reflect.DeepEqual(test.expectedMap, oMap) { 267 t.Errorf("Failed for test %v. Expected Owners: %v. Actual Owners %v", test.testName, test.expectedMap, oMap) 268 } 269 } 270 } 271 func TestGetOwnersSet(t *testing.T) { 272 rootApprovers := sets.New[string]("Alice", "Bob") 273 aApprovers := sets.New[string]("Art", "Anne") 274 bApprovers := sets.New[string]("Bill", "Ben", "Barbara") 275 dApprovers := sets.New[string]("David", "Dan", "Debbie") 276 FakeRepoMap := map[string]sets.Set[string]{ 277 "": rootApprovers, 278 "a": aApprovers, 279 "b": bApprovers, 280 "a/d": dApprovers, 281 } 282 283 tests := []struct { 284 testName string 285 filenames []string 286 expectedOwnersFiles sets.Set[string] 287 }{ 288 { 289 testName: "Empty PR", 290 filenames: []string{}, 291 expectedOwnersFiles: sets.New[string](), 292 }, 293 { 294 testName: "Single Root File PR", 295 filenames: []string{"kubernetes.go"}, 296 expectedOwnersFiles: sets.New[string](""), 297 }, 298 { 299 testName: "Multiple Root File PR", 300 filenames: []string{"test.go", "kubernetes.go"}, 301 expectedOwnersFiles: sets.New[string](""), 302 }, 303 { 304 testName: "Internal Node File PR", 305 filenames: []string{"a/test.go"}, 306 expectedOwnersFiles: sets.New[string]("a"), 307 }, 308 { 309 testName: "Two Leaf File PR", 310 filenames: []string{"a/test.go", "b/test.go"}, 311 expectedOwnersFiles: sets.New[string]("a", "b"), 312 }, 313 { 314 testName: "Leaf and Parent 2 File PR", 315 filenames: []string{"a/test.go", "a/c/test.go"}, 316 expectedOwnersFiles: sets.New[string]("a"), 317 }, 318 } 319 320 for _, test := range tests { 321 testOwners := Owners{ 322 filenames: test.filenames, 323 repo: createFakeRepo(FakeRepoMap), 324 seed: TestSeed, 325 log: logrus.WithField("plugin", "some_plugin"), 326 } 327 oSet := testOwners.GetOwnersSet() 328 if !oSet.Equal(test.expectedOwnersFiles) { 329 t.Errorf("Failed for test %v. Expected Owners: %v. Actual Owners %v", test.testName, test.expectedOwnersFiles, oSet) 330 } 331 } 332 } 333 334 func TestGetSuggestedApprovers(t *testing.T) { 335 var rootApprovers = sets.New[string]("Alice", "Bob") 336 var aApprovers = sets.New[string]("Art", "Anne") 337 var bApprovers = sets.New[string]("Bill", "Ben", "Barbara") 338 var dApprovers = sets.New[string]("David", "Dan", "Debbie") 339 var eApprovers = sets.New[string]("Eve", "Erin") 340 var edcApprovers = eApprovers.Union(dApprovers) 341 var FakeRepoMap = map[string]sets.Set[string]{ 342 "": rootApprovers, 343 "a": aApprovers, 344 "b": bApprovers, 345 "a/d": dApprovers, 346 "a/combo": edcApprovers, 347 } 348 tests := []struct { 349 testName string 350 filenames []string 351 // need at least one person from each set 352 expectedOwners []sets.Set[string] 353 }{ 354 { 355 testName: "Empty PR", 356 filenames: []string{}, 357 expectedOwners: []sets.Set[string]{}, 358 }, 359 { 360 testName: "Single Root File PR", 361 filenames: []string{"kubernetes.go"}, 362 expectedOwners: []sets.Set[string]{setToLower(rootApprovers)}, 363 }, 364 { 365 testName: "Internal Node File PR", 366 filenames: []string{"a/test.go"}, 367 expectedOwners: []sets.Set[string]{setToLower(aApprovers)}, 368 }, 369 { 370 testName: "Multiple Files Internal Node File PR", 371 filenames: []string{"a/test.go", "a/test1.go"}, 372 expectedOwners: []sets.Set[string]{setToLower(aApprovers)}, 373 }, 374 { 375 testName: "Two Leaf File PR", 376 filenames: []string{"a/test.go", "b/test.go"}, 377 expectedOwners: []sets.Set[string]{setToLower(aApprovers), setToLower(bApprovers)}, 378 }, 379 { 380 testName: "Leaf and Parent 2 File PR", 381 filenames: []string{"a/test.go", "a/d/test.go"}, 382 expectedOwners: []sets.Set[string]{setToLower(aApprovers)}, 383 }, 384 { 385 testName: "Combo and B", 386 filenames: []string{"a/combo/test.go", "b/test.go"}, 387 expectedOwners: []sets.Set[string]{setToLower(edcApprovers), setToLower(bApprovers)}, 388 }, 389 { 390 testName: "Lowest Leaf", 391 filenames: []string{"a/combo/test.go"}, 392 expectedOwners: []sets.Set[string]{setToLower(edcApprovers)}, 393 }, 394 } 395 396 for _, test := range tests { 397 testOwners := Owners{ 398 filenames: test.filenames, 399 repo: createFakeRepo(FakeRepoMap), 400 seed: TestSeed, 401 log: logrus.WithField("plugin", "some_plugin"), 402 } 403 suggested := testOwners.GetSuggestedApprovers(testOwners.GetReverseMap(testOwners.GetLeafApprovers()), testOwners.GetShuffledApprovers()) 404 for _, ownersSet := range test.expectedOwners { 405 if ownersSet.Intersection(suggested).Len() == 0 { 406 t.Errorf("Failed for test %v. Didn't find an approver from: %v. Actual Owners %v", test.testName, ownersSet, suggested) 407 t.Errorf("%v", test.filenames) 408 } 409 } 410 } 411 } 412 413 func TestGetAllPotentialApprovers(t *testing.T) { 414 rootApprovers := sets.New[string]("Alice", "Bob") 415 aApprovers := sets.New[string]("Art", "Anne") 416 bApprovers := sets.New[string]("Bill", "Ben", "Barbara") 417 cApprovers := sets.New[string]("Chris", "Carol") 418 dApprovers := sets.New[string]("David", "Dan", "Debbie") 419 eApprovers := sets.New[string]("Eve", "Erin") 420 edcApprovers := eApprovers.Union(dApprovers).Union(cApprovers) 421 FakeRepoMap := map[string]sets.Set[string]{ 422 "": rootApprovers, 423 "a": aApprovers, 424 "b": bApprovers, 425 "c": cApprovers, 426 "a/d": dApprovers, 427 "a/combo": edcApprovers, 428 } 429 tests := []struct { 430 testName string 431 filenames []string 432 // use an array because we expected output of this function to be sorted 433 expectedApprovers []string 434 }{ 435 { 436 testName: "Empty PR", 437 filenames: []string{}, 438 expectedApprovers: []string{}, 439 }, 440 { 441 testName: "Single Root File PR", 442 filenames: []string{"kubernetes.go"}, 443 expectedApprovers: sets.List(setToLower(rootApprovers)), 444 }, 445 { 446 testName: "Internal Node File PR", 447 filenames: []string{"a/test.go"}, 448 expectedApprovers: sets.List(setToLower(aApprovers)), 449 }, 450 { 451 testName: "One Leaf One Internal Node File PR", 452 filenames: []string{"a/test.go", "b/test.go"}, 453 expectedApprovers: sets.List(setToLower(aApprovers.Union(bApprovers))), 454 }, 455 { 456 testName: "Two Leaf Files PR", 457 filenames: []string{"a/d/test.go", "c/test.go"}, 458 expectedApprovers: sets.List(setToLower(dApprovers.Union(cApprovers))), 459 }, 460 { 461 testName: "Leaf and Parent 2 File PR", 462 filenames: []string{"a/test.go", "a/combo/test.go"}, 463 expectedApprovers: sets.List(setToLower(aApprovers)), 464 }, 465 { 466 testName: "Two Leafs", 467 filenames: []string{"a/d/test.go", "b/test.go"}, 468 expectedApprovers: sets.List(setToLower(dApprovers.Union(bApprovers))), 469 }, 470 { 471 testName: "Lowest Leaf", 472 filenames: []string{"a/combo/test.go"}, 473 expectedApprovers: sets.List(setToLower(edcApprovers)), 474 }, 475 { 476 testName: "Root And Everything Else PR", 477 filenames: []string{"a/combo/test.go", "b/test.go", "c/test.go", "d/test.go"}, 478 expectedApprovers: sets.List(setToLower(rootApprovers)), 479 }, 480 } 481 482 for _, test := range tests { 483 testOwners := Owners{ 484 filenames: test.filenames, 485 repo: createFakeRepo(FakeRepoMap), 486 seed: TestSeed, 487 log: logrus.WithField("plugin", "some_plugin"), 488 } 489 all := testOwners.GetAllPotentialApprovers() 490 if !reflect.DeepEqual(all, test.expectedApprovers) { 491 t.Errorf("Failed for test %v. Didn't correct approvers list. Expected: %v. Found %v", test.testName, test.expectedApprovers, all) 492 } 493 } 494 } 495 496 func TestFindMostCoveringApprover(t *testing.T) { 497 rootApprovers := sets.New[string]("Alice", "Bob") 498 aApprovers := sets.New[string]("Art", "Anne") 499 bApprovers := sets.New[string]("Bill", "Ben", "Barbara") 500 cApprovers := sets.New[string]("Chris", "Carol") 501 dApprovers := sets.New[string]("David", "Dan", "Debbie") 502 eApprovers := sets.New[string]("Eve", "Erin") 503 edcApprovers := eApprovers.Union(dApprovers).Union(cApprovers) 504 FakeRepoMap := map[string]sets.Set[string]{ 505 "": rootApprovers, 506 "a": aApprovers, 507 "b": bApprovers, 508 "c": cApprovers, 509 "a/d": dApprovers, 510 "a/combo": edcApprovers, 511 } 512 tests := []struct { 513 testName string 514 filenames []string 515 unapproved sets.Set[string] 516 // because most covering could be two or more people 517 expectedMostCovering sets.Set[string] 518 }{ 519 { 520 testName: "Empty PR", 521 filenames: []string{}, 522 unapproved: sets.Set[string]{}, 523 expectedMostCovering: sets.New[string](""), 524 }, 525 { 526 testName: "Single Root File PR", 527 filenames: []string{"kubernetes.go"}, 528 unapproved: sets.New[string](""), 529 expectedMostCovering: setToLower(rootApprovers), 530 }, 531 { 532 testName: "Internal Node File PR", 533 filenames: []string{"a/test.go"}, 534 unapproved: sets.New[string]("a"), 535 expectedMostCovering: setToLower(aApprovers), 536 }, 537 { 538 testName: "Combo and Intersecting Leaf PR", 539 filenames: []string{"a/combo/test.go", "a/d/test.go"}, 540 unapproved: sets.New[string]("a/combo", "a/d"), 541 expectedMostCovering: setToLower(edcApprovers.Intersection(dApprovers)), 542 }, 543 { 544 testName: "Three Leaf PR Only B Approved", 545 filenames: []string{"a/combo/test.go", "c/test.go", "b/test.go"}, 546 unapproved: sets.New[string]("a/combo", "c/"), 547 expectedMostCovering: setToLower(edcApprovers.Intersection(cApprovers)), 548 }, 549 { 550 testName: "Three Leaf PR Only B Left Unapproved", 551 filenames: []string{"a/combo/test.go", "a/d/test.go", "b/test.go"}, 552 unapproved: sets.New[string]("b"), 553 expectedMostCovering: setToLower(bApprovers), 554 }, 555 { 556 testName: "Leaf and Parent 2 File PR", 557 filenames: []string{"a/test.go", "a/d/test.go"}, 558 unapproved: sets.New[string]("a", "a/d"), 559 expectedMostCovering: setToLower(aApprovers.Union(dApprovers)), 560 }, 561 } 562 563 for _, test := range tests { 564 testOwners := Owners{ 565 filenames: test.filenames, 566 repo: createFakeRepo(FakeRepoMap), 567 seed: TestSeed, 568 log: logrus.WithField("plugin", "some_plugin"), 569 } 570 bestPerson := findMostCoveringApprover(testOwners.GetAllPotentialApprovers(), nil, testOwners.GetReverseMap(testOwners.GetLeafApprovers()), test.unapproved) 571 if test.expectedMostCovering.Intersection(sets.New[string](bestPerson)).Len() != 1 { 572 t.Errorf("Failed for test %v. Didn't correct approvers list. Expected: %v. Found %v", test.testName, test.expectedMostCovering, bestPerson) 573 } 574 575 } 576 } 577 578 func TestGetReverseMap(t *testing.T) { 579 rootApprovers := sets.New[string]("Alice", "Bob") 580 aApprovers := sets.New[string]("Art", "Anne") 581 cApprovers := sets.New[string]("Chris", "Carol") 582 dApprovers := sets.New[string]("David", "Dan", "Debbie") 583 eApprovers := sets.New[string]("Eve", "Erin") 584 edcApprovers := eApprovers.Union(dApprovers).Union(cApprovers) 585 FakeRepoMap := map[string]sets.Set[string]{ 586 "": rootApprovers, 587 "a": aApprovers, 588 "c": cApprovers, 589 "a/d": dApprovers, 590 "a/combo": edcApprovers, 591 } 592 tests := []struct { 593 testName string 594 filenames []string 595 expectedRevMap map[string]sets.Set[string] // people -> files they can approve 596 }{ 597 { 598 testName: "Empty PR", 599 filenames: []string{}, 600 expectedRevMap: map[string]sets.Set[string]{}, 601 }, 602 { 603 testName: "Single Root File PR", 604 filenames: []string{"kubernetes.go"}, 605 expectedRevMap: map[string]sets.Set[string]{ 606 "alice": sets.New[string](""), 607 "bob": sets.New[string](""), 608 }, 609 }, 610 { 611 testName: "Two Leaf PRs", 612 filenames: []string{"a/combo/test.go", "a/d/test.go"}, 613 expectedRevMap: map[string]sets.Set[string]{ 614 "david": sets.New[string]("a/d", "a/combo"), 615 "dan": sets.New[string]("a/d", "a/combo"), 616 "debbie": sets.New[string]("a/d", "a/combo"), 617 "eve": sets.New[string]("a/combo"), 618 "erin": sets.New[string]("a/combo"), 619 "chris": sets.New[string]("a/combo"), 620 "carol": sets.New[string]("a/combo"), 621 }, 622 }, 623 } 624 625 for _, test := range tests { 626 testOwners := Owners{ 627 filenames: test.filenames, 628 repo: createFakeRepo(FakeRepoMap), 629 seed: TestSeed, 630 log: logrus.WithField("plugin", "some_plugin"), 631 } 632 calculatedRevMap := testOwners.GetReverseMap(testOwners.GetLeafApprovers()) 633 if !reflect.DeepEqual(calculatedRevMap, test.expectedRevMap) { 634 t.Errorf("Failed for test %v. Didn't find correct reverse map.", test.testName) 635 t.Errorf("Person \t\t Expected \t\tFound ") 636 // printing the calculated vs expected in a nicer way for debugging 637 for k, v := range test.expectedRevMap { 638 if calcVal, ok := calculatedRevMap[k]; ok { 639 t.Errorf("%v\t\t%v\t\t%v ", k, v, calcVal) 640 } else { 641 t.Errorf("%v\t\t%v", k, v) 642 } 643 } 644 } 645 } 646 } 647 648 func TestGetShuffledApprovers(t *testing.T) { 649 rootApprovers := sets.New[string]("Alice", "Bob") 650 aApprovers := sets.New[string]("Art", "Anne") 651 bApprovers := sets.New[string]("Bill", "Ben", "Barbara") 652 cApprovers := sets.New[string]("Chris", "Carol") 653 dApprovers := sets.New[string]("David", "Dan", "Debbie") 654 eApprovers := sets.New[string]("Eve", "Erin") 655 edcApprovers := eApprovers.Union(dApprovers).Union(cApprovers) 656 FakeRepoMap := map[string]sets.Set[string]{ 657 "": rootApprovers, 658 "a": aApprovers, 659 "b": bApprovers, 660 "c": cApprovers, 661 "a/d": dApprovers, 662 "a/combo": edcApprovers, 663 } 664 tests := []struct { 665 testName string 666 filenames []string 667 seed int64 668 expectedOrder []string 669 }{ 670 { 671 testName: "Empty PR", 672 filenames: []string{}, 673 seed: 0, 674 expectedOrder: []string{}, 675 }, 676 { 677 testName: "Single Root File PR Approved", 678 filenames: []string{"kubernetes.go"}, 679 seed: 0, 680 expectedOrder: []string{"bob", "alice"}, 681 }, 682 { 683 testName: "Combo And B PR", 684 filenames: []string{"a/combo/test.go", "b/test.go"}, 685 seed: 0, 686 expectedOrder: []string{"erin", "bill", "carol", "barbara", "dan", "debbie", "ben", "david", "eve", "chris"}, 687 }, 688 { 689 testName: "Combo and D, Seed 0", 690 filenames: []string{"a/combo/test.go", "a/d/test.go"}, 691 seed: 0, 692 expectedOrder: []string{"erin", "dan", "dan", "carol", "david", "debbie", "chris", "debbie", "eve", "david"}, 693 }, 694 { 695 testName: "Combo and D, Seed 2", 696 filenames: []string{"a/combo/test.go", "a/d/test.go"}, 697 seed: 2, 698 expectedOrder: []string{"dan", "carol", "debbie", "dan", "erin", "chris", "eve", "david", "debbie", "david"}, 699 }, 700 } 701 702 for _, test := range tests { 703 testOwners := Owners{ 704 filenames: test.filenames, 705 repo: createFakeRepo(FakeRepoMap), 706 seed: test.seed, 707 log: logrus.WithField("plugin", "some_plugin"), 708 } 709 calculated := testOwners.GetShuffledApprovers() 710 if !reflect.DeepEqual(test.expectedOrder, calculated) { 711 t.Errorf("Failed for test %v. Expected unapproved files: %v. Found %v", test.testName, test.expectedOrder, calculated) 712 } 713 } 714 } 715 716 func TestRemoveSubdirs(t *testing.T) { 717 tests := []struct { 718 testName string 719 directories sets.Set[string] 720 noParentOwners map[string]bool 721 722 expected sets.Set[string] 723 }{ 724 { 725 testName: "Empty PR", 726 directories: sets.New[string](), 727 expected: sets.New[string](), 728 }, 729 { 730 testName: "Root and One Level Below PR", 731 directories: sets.New[string]("", "a/"), 732 expected: sets.New[string](""), 733 }, 734 { 735 testName: "Two Separate Branches", 736 directories: sets.New[string]("a/", "c/"), 737 expected: sets.New[string]("a/", "c/"), 738 }, 739 { 740 testName: "Lots of Branches and Leaves", 741 directories: sets.New[string]("a", "a/combo", "a/d", "b", "c"), 742 expected: sets.New[string]("a", "b", "c"), 743 }, 744 { 745 testName: "NoParentOwners", 746 directories: sets.New[string]("a", "a/combo"), 747 noParentOwners: map[string]bool{"a/combo": true}, 748 expected: sets.New[string]("a", "a/combo"), 749 }, 750 { 751 testName: "NoParentOwners in relative path", 752 directories: sets.New[string]("a", "a/b/combo"), 753 noParentOwners: map[string]bool{"a/b": true}, 754 expected: sets.New[string]("a", "a/b/combo"), 755 }, 756 { 757 testName: "NoParentOwners with child", 758 directories: sets.New[string]("a", "a/b", "a/b/combo"), 759 noParentOwners: map[string]bool{"a/b": true}, 760 expected: sets.New[string]("a", "a/b"), 761 }, 762 } 763 764 for _, test := range tests { 765 if test.noParentOwners == nil { 766 test.noParentOwners = map[string]bool{} 767 } 768 o := &Owners{repo: FakeRepo{noParentOwnersMap: test.noParentOwners}} 769 o.removeSubdirs(test.directories) 770 if !reflect.DeepEqual(test.expected, test.directories) { 771 t.Errorf("Failed to remove subdirectories for test %v. Expected files: %q. Found %q", test.testName, sets.List(test.expected), sets.List(test.directories)) 772 773 } 774 } 775 }