github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/cmd/hmac/main_test.go (about) 1 /* 2 Copyright 2020 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 main 18 19 import ( 20 "flag" 21 "reflect" 22 "strings" 23 "testing" 24 "time" 25 26 "github.com/google/go-cmp/cmp" 27 "k8s.io/apimachinery/pkg/util/sets" 28 29 "sigs.k8s.io/prow/cmd/hmac/fakeghhook" 30 "sigs.k8s.io/prow/pkg/config" 31 "sigs.k8s.io/prow/pkg/flagutil" 32 configflagutil "sigs.k8s.io/prow/pkg/flagutil/config" 33 "sigs.k8s.io/prow/pkg/github" 34 ) 35 36 func TestGatherOptions(t *testing.T) { 37 cases := []struct { 38 name string 39 args map[string]string 40 del sets.Set[string] 41 expected func(*options) 42 err bool 43 }{ 44 { 45 name: "minimal flags work", 46 }, 47 { 48 name: "explicitly set --config-path", 49 args: map[string]string{ 50 "--config-path": "/random/value", 51 }, 52 expected: func(o *options) { 53 o.config.ConfigPath = "/random/value" 54 }, 55 }, 56 { 57 name: "explicitly set --dry-run=false", 58 args: map[string]string{ 59 "--dry-run": "false", 60 }, 61 expected: func(o *options) { 62 o.dryRun = false 63 }, 64 }, 65 } 66 for _, tc := range cases { 67 t.Run(tc.name, func(t *testing.T) { 68 ghoptions := flagutil.GitHubOptions{} 69 ghoptions.AddFlags(&flag.FlagSet{}) 70 ghoptions.Validate(false) 71 expected := &options{ 72 config: configflagutil.ConfigOptions{ 73 ConfigPathFlagName: "config-path", 74 JobConfigPathFlagName: "job-config-path", 75 ConfigPath: "yo", 76 SupplementalProwConfigsFileNameSuffix: "_prowconfig.yaml", 77 InRepoConfigCacheSize: 200, 78 }, 79 dryRun: true, 80 github: ghoptions, 81 kubeconfigCtx: "whatever-kubeconfig-context", 82 hookUrl: "http://whatever-hook-url", 83 hmacTokenSecretNamespace: "default", 84 hmacTokenSecretName: "hmac-token", 85 hmacTokenKey: "hmac", 86 } 87 if tc.expected != nil { 88 tc.expected(expected) 89 } 90 91 argMap := map[string]string{ 92 "--config-path": "yo", 93 "--hook-url": "http://whatever-hook-url", 94 "--kubeconfig-context": "whatever-kubeconfig-context", 95 "--hmac-token-secret-name": "hmac-token", 96 "--hmac-token-key": "hmac", 97 } 98 for k, v := range tc.args { 99 argMap[k] = v 100 } 101 for k := range tc.del { 102 delete(argMap, k) 103 } 104 105 var args []string 106 for k, v := range argMap { 107 args = append(args, k+"="+v) 108 } 109 fs := flag.NewFlagSet("fake-flags", flag.PanicOnError) 110 actual := gatherOptions(fs, args...) 111 switch err := actual.validate(); { 112 case err != nil: 113 if !tc.err { 114 t.Errorf("unexpected error: %v", err) 115 } 116 case tc.err: 117 t.Errorf("failed to receive expected error") 118 case !reflect.DeepEqual(*expected, actual): 119 t.Errorf("\n%#v\n != expected \n%#v\n", actual, *expected) 120 } 121 }) 122 } 123 } 124 125 func TestPruneOldTokens(t *testing.T) { 126 // "2006-01-02T15:04:05+07:00" 127 time1, _ := time.Parse(time.RFC3339, "2020-01-05T19:07:08+00:00") 128 time2, _ := time.Parse(time.RFC3339, "2020-02-05T19:07:08+00:00") 129 time3, _ := time.Parse(time.RFC3339, "2020-03-05T19:07:08+00:00") 130 131 cases := []struct { 132 name string 133 current map[string]github.HMACsForRepo 134 repo string 135 expected map[string]github.HMACsForRepo 136 }{ 137 { 138 name: "three hmacs, only the latest one is left after pruning", 139 current: map[string]github.HMACsForRepo{ 140 "org1/repo1": []github.HMACToken{ 141 { 142 Value: "rand-val1", 143 CreatedAt: time1, 144 }, 145 { 146 Value: "rand-val2", 147 CreatedAt: time2, 148 }, 149 { 150 Value: "rand-val3", 151 CreatedAt: time3, 152 }, 153 }, 154 }, 155 repo: "org1/repo1", 156 expected: map[string]github.HMACsForRepo{ 157 "org1/repo1": []github.HMACToken{ 158 { 159 Value: "rand-val3", 160 CreatedAt: time3, 161 }, 162 }, 163 }, 164 }, 165 { 166 name: "two hmacs, only the latest one is left after pruning", 167 current: map[string]github.HMACsForRepo{ 168 "org1/repo1": []github.HMACToken{ 169 { 170 Value: "rand-val1", 171 CreatedAt: time1, 172 }, 173 { 174 Value: "rand-val2", 175 CreatedAt: time2, 176 }, 177 }, 178 }, 179 repo: "org1/repo1", 180 expected: map[string]github.HMACsForRepo{ 181 "org1/repo1": []github.HMACToken{ 182 { 183 Value: "rand-val2", 184 CreatedAt: time2, 185 }, 186 }, 187 }, 188 }, 189 { 190 name: "nothing will be changed if the repo is not in the map", 191 current: map[string]github.HMACsForRepo{ 192 "org1/repo1": []github.HMACToken{ 193 { 194 Value: "rand-val1", 195 CreatedAt: time1, 196 }, 197 }, 198 }, 199 repo: "org2/repo2", 200 expected: map[string]github.HMACsForRepo{ 201 "org1/repo1": []github.HMACToken{ 202 { 203 Value: "rand-val1", 204 CreatedAt: time1, 205 }, 206 }, 207 }, 208 }, 209 } 210 211 for _, tc := range cases { 212 t.Run(tc.name, func(t *testing.T) { 213 c := &client{currentHMACMap: tc.current} 214 c.pruneOldTokens(tc.repo) 215 if !reflect.DeepEqual(tc.expected, c.currentHMACMap) { 216 t.Errorf("%#v != expected %#v", c.currentHMACMap, tc.expected) 217 } 218 }) 219 } 220 } 221 222 func TestGenerateNewHMACToken(t *testing.T) { 223 token1, err := generateNewHMACToken() 224 if err != nil { 225 t.Errorf("error generating new hmac token1: %v", err) 226 } 227 228 token2, err := generateNewHMACToken() 229 if err != nil { 230 t.Errorf("error generating new hmac token2: %v", err) 231 } 232 if token1 == token2 { 233 t.Error("the generated hmac token should be random, but the two are equal") 234 } 235 } 236 237 func TestHandleRemovedRepo(t *testing.T) { 238 cases := []struct { 239 name string 240 toRemove map[string]bool 241 expectedHMACs map[string]github.HMACsForRepo 242 expectedHooks map[string][]github.Hook 243 }{ 244 { 245 name: "delete hmac and hook for one repo", 246 toRemove: map[string]bool{"repo1": true}, 247 expectedHMACs: map[string]github.HMACsForRepo{ 248 "repo2": { 249 github.HMACToken{ 250 Value: "val2", 251 }, 252 }, 253 }, 254 expectedHooks: map[string][]github.Hook{ 255 "repo2": { 256 github.Hook{ 257 ID: 0, 258 Name: "hook2", 259 Active: true, 260 Config: github.HookConfig{ 261 URL: "http://whatever-hook-url", 262 }, 263 }, 264 }, 265 }, 266 }, 267 { 268 name: "delete hmac and hook for multiple repos", 269 toRemove: map[string]bool{"repo1": true, "repo2": true}, 270 expectedHMACs: map[string]github.HMACsForRepo{}, 271 expectedHooks: map[string][]github.Hook{}, 272 }, 273 { 274 name: "delete hmac and hook for non-existed repo", 275 toRemove: map[string]bool{"repo1": true, "whatever-repo": true}, 276 expectedHMACs: map[string]github.HMACsForRepo{ 277 "repo2": { 278 github.HMACToken{ 279 Value: "val2", 280 }, 281 }, 282 }, 283 expectedHooks: map[string][]github.Hook{ 284 "repo2": { 285 github.Hook{ 286 Name: "hook2", 287 Active: true, 288 Config: github.HookConfig{ 289 URL: "http://whatever-hook-url", 290 }, 291 }, 292 }, 293 }, 294 }, 295 } 296 for _, tc := range cases { 297 t.Run(tc.name, func(t *testing.T) { 298 fakeClient := &fakeghhook.FakeClient{ 299 OrgHooks: map[string][]github.Hook{ 300 "repo1": { 301 github.Hook{ 302 ID: 0, 303 Name: "hook1", 304 Active: true, 305 Config: github.HookConfig{ 306 URL: "http://whatever-hook-url", 307 }, 308 }, 309 }, 310 "repo2": { 311 github.Hook{ 312 ID: 0, 313 Name: "hook2", 314 Active: true, 315 Config: github.HookConfig{ 316 URL: "http://whatever-hook-url", 317 }, 318 }, 319 }, 320 }, 321 } 322 c := &client{ 323 currentHMACMap: map[string]github.HMACsForRepo{ 324 "repo1": { 325 github.HMACToken{ 326 Value: "val1", 327 }, 328 }, 329 "repo2": { 330 github.HMACToken{ 331 Value: "val2", 332 }, 333 }, 334 }, 335 githubHookClient: fakeClient, 336 options: options{hookUrl: "http://whatever-hook-url"}, 337 } 338 if err := c.handleRemovedRepo(tc.toRemove); err != nil { 339 t.Errorf("unexpected error: %v", err) 340 } 341 if !reflect.DeepEqual(tc.expectedHMACs, c.currentHMACMap) { 342 t.Errorf("hmacs %#v != expected %#v", c.currentHMACMap, tc.expectedHMACs) 343 } 344 if !reflect.DeepEqual(tc.expectedHooks, fakeClient.OrgHooks) { 345 t.Errorf("hooks %#v != expected %#v", fakeClient.OrgHooks, tc.expectedHooks) 346 } 347 }) 348 } 349 } 350 351 func TestHandleAddedRepo(t *testing.T) { 352 globalToken := []github.HMACToken{ 353 { 354 Value: "global-rand-val1", 355 CreatedAt: time.Now().Add(-time.Hour), 356 }, 357 } 358 359 cases := []struct { 360 name string 361 toAdd map[string]config.ManagedWebhookInfo 362 currentHMACs map[string]github.HMACsForRepo 363 currentHMACMapForBatchUpdate map[string]string 364 expectedHMACsSize map[string]int 365 }{ 366 { 367 name: "add repos when global token does not exist", 368 toAdd: map[string]config.ManagedWebhookInfo{ 369 "repo1": {TokenCreatedAfter: time.Now()}, 370 "repo2": {TokenCreatedAfter: time.Now()}, 371 }, 372 currentHMACs: map[string]github.HMACsForRepo{}, 373 currentHMACMapForBatchUpdate: map[string]string{"whatever-repo": "whatever-token"}, 374 expectedHMACsSize: map[string]int{"repo1": 1, "repo2": 1}, 375 }, 376 { 377 name: "add repos when global token exists", 378 toAdd: map[string]config.ManagedWebhookInfo{ 379 "repo1": {TokenCreatedAfter: time.Now()}, 380 "repo2": {TokenCreatedAfter: time.Now()}, 381 }, 382 currentHMACs: map[string]github.HMACsForRepo{ 383 "*": globalToken, 384 }, 385 currentHMACMapForBatchUpdate: map[string]string{"whatever-repo": "whatever-token"}, 386 expectedHMACsSize: map[string]int{"repo1": 2, "repo2": 2}, 387 }, 388 } 389 390 for _, tc := range cases { 391 t.Run(tc.name, func(t *testing.T) { 392 c := &client{ 393 currentHMACMap: tc.currentHMACs, 394 hmacMapForBatchUpdate: tc.currentHMACMapForBatchUpdate, 395 } 396 if err := c.handleAddedRepo(tc.toAdd); err != nil { 397 t.Errorf("unexpected error: %v", err) 398 } 399 for repo, size := range tc.expectedHMACsSize { 400 if _, ok := c.currentHMACMap[repo]; !ok { 401 t.Errorf("repo %q does not exist in the updated HMAC map", repo) 402 } else if len(c.currentHMACMap[repo]) != size { 403 t.Errorf("repo %q hmac size %d != expected %d", repo, len(c.currentHMACMap[repo]), size) 404 } 405 } 406 for repo := range tc.toAdd { 407 if _, ok := c.hmacMapForBatchUpdate[repo]; !ok { 408 t.Errorf("repo %q is expected to be added to the batch update map, but not", repo) 409 } 410 } 411 }) 412 } 413 } 414 415 func TestHandleRotatedRepo(t *testing.T) { 416 pastTime, _ := time.Parse(time.RFC3339Nano, "2020-01-01T00:00:50Z") 417 418 globalToken := []github.HMACToken{ 419 { 420 Value: "global-rand-val1", 421 CreatedAt: pastTime, 422 }, 423 } 424 commonTokens := []github.HMACToken{ 425 { 426 Value: "rand-val1", 427 CreatedAt: pastTime, 428 }, 429 { 430 Value: "rand-val2", 431 CreatedAt: pastTime, 432 }, 433 } 434 435 cases := []struct { 436 name string 437 toRotate map[string]config.ManagedWebhookInfo 438 currentHMACs map[string]github.HMACsForRepo 439 currentHMACMapForBatchUpdate map[string]string 440 expectedHMACsSize map[string]int 441 expectedReposForBatchUpdate []string 442 expectedHMACMapForRecovery map[string]github.HMACsForRepo 443 }{ 444 { 445 name: "test a repo that needs its hmac to be rotated, and global token does not exist", 446 toRotate: map[string]config.ManagedWebhookInfo{ 447 "repo1": {TokenCreatedAfter: time.Now()}, 448 "repo2": {TokenCreatedAfter: time.Now()}, 449 }, 450 currentHMACs: map[string]github.HMACsForRepo{ 451 "repo1": commonTokens, 452 "repo2": commonTokens, 453 }, 454 currentHMACMapForBatchUpdate: map[string]string{"whatever-repo": "whatever-token"}, 455 expectedHMACsSize: map[string]int{"repo1": 3, "repo2": 3}, 456 expectedReposForBatchUpdate: []string{"repo1", "repo2"}, 457 expectedHMACMapForRecovery: map[string]github.HMACsForRepo{ 458 "repo1": commonTokens, 459 "repo2": commonTokens, 460 }, 461 }, 462 { 463 name: "test a repo that needs its hmac to be rotated, and global token exists", 464 toRotate: map[string]config.ManagedWebhookInfo{ 465 "repo1": {TokenCreatedAfter: time.Now()}, 466 "repo2": {TokenCreatedAfter: time.Now()}, 467 }, 468 currentHMACs: map[string]github.HMACsForRepo{ 469 "*": globalToken, 470 "repo1": commonTokens, 471 "repo2": commonTokens, 472 }, 473 currentHMACMapForBatchUpdate: map[string]string{"whatever-repo": "whatever-token"}, 474 expectedHMACsSize: map[string]int{"repo1": 3, "repo2": 3}, 475 expectedReposForBatchUpdate: []string{"repo1", "repo2"}, 476 expectedHMACMapForRecovery: map[string]github.HMACsForRepo{ 477 "repo1": commonTokens, 478 "repo2": commonTokens, 479 }, 480 }, 481 { 482 name: "test a repo that does not need its hmac to be rotated", 483 toRotate: map[string]config.ManagedWebhookInfo{ 484 "repo1": {TokenCreatedAfter: pastTime}, 485 "repo2": {TokenCreatedAfter: pastTime}, 486 }, 487 currentHMACs: map[string]github.HMACsForRepo{ 488 "repo1": []github.HMACToken{ 489 { 490 Value: "rand-val1", 491 CreatedAt: pastTime.Add(-1 * time.Hour), 492 }, 493 }, 494 "repo2": []github.HMACToken{ 495 { 496 Value: "rand-val2", 497 CreatedAt: pastTime.Add(1 * time.Hour), 498 }, 499 }, 500 }, 501 currentHMACMapForBatchUpdate: map[string]string{"whatever-repo": "whatever-token"}, 502 expectedHMACsSize: map[string]int{"repo1": 2, "repo2": 1}, 503 expectedReposForBatchUpdate: []string{"repo1"}, 504 expectedHMACMapForRecovery: map[string]github.HMACsForRepo{ 505 "repo1": []github.HMACToken{ 506 { 507 Value: "rand-val1", 508 CreatedAt: pastTime.Add(-1 * time.Hour), 509 }, 510 }, 511 }, 512 }, 513 } 514 515 for _, tc := range cases { 516 t.Run(tc.name, func(t *testing.T) { 517 c := &client{ 518 currentHMACMap: tc.currentHMACs, 519 hmacMapForBatchUpdate: tc.currentHMACMapForBatchUpdate, 520 hmacMapForRecovery: map[string]github.HMACsForRepo{}, 521 } 522 if err := c.handledRotatedRepo(tc.toRotate); err != nil { 523 t.Errorf("unexpected error: %v", err) 524 } 525 for repo, size := range tc.expectedHMACsSize { 526 if _, ok := c.currentHMACMap[repo]; !ok { 527 t.Errorf("repo %q does not exist in the updated HMAC map", repo) 528 } else if len(c.currentHMACMap[repo]) != size { 529 t.Errorf("repo %q hmac size %d != expected %d", repo, len(c.currentHMACMap[repo]), size) 530 } 531 } 532 for _, repo := range tc.expectedReposForBatchUpdate { 533 if _, ok := c.hmacMapForBatchUpdate[repo]; !ok { 534 t.Errorf("repo %q is expected to be added to the batch update map, but not", repo) 535 } 536 } 537 if !reflect.DeepEqual(tc.expectedHMACMapForRecovery, c.hmacMapForRecovery) { 538 t.Errorf("The hmacMapForRecovery %#v != expected %#v", c.hmacMapForRecovery, tc.expectedHMACMapForRecovery) 539 } 540 }) 541 } 542 } 543 544 func TestBatchOnboardNewTokenForRepos(t *testing.T) { 545 name := "web" 546 contentType := "json" 547 secretBeforeUpdate := "whatever-secret-before-update" 548 secretAfterUpdate := "whatever-secret-after-update" 549 hookBeforeUpdate := github.Hook{ 550 ID: 0, 551 Name: name, 552 Active: true, 553 Events: github.AllHookEvents, 554 Config: github.HookConfig{ 555 URL: "http://whatever-hook-url", 556 ContentType: &contentType, 557 Secret: &secretBeforeUpdate, 558 }, 559 } 560 hookAfterUpdate := github.Hook{ 561 ID: 0, 562 Name: name, 563 Active: true, 564 Events: github.AllHookEvents, 565 Config: github.HookConfig{ 566 URL: "http://whatever-hook-url", 567 ContentType: &contentType, 568 Secret: &secretAfterUpdate, 569 }, 570 } 571 572 cases := []struct { 573 name string 574 hmacMapForBatchUpdate map[string]string 575 currentOrgHooks map[string][]github.Hook 576 currentRepoHooks map[string][]github.Hook 577 expectedOrgHooks map[string][]github.Hook 578 expectedRepoHooks map[string][]github.Hook 579 }{ 580 { 581 name: "add hook for one repo", 582 hmacMapForBatchUpdate: map[string]string{"org/repo1": secretBeforeUpdate}, 583 currentRepoHooks: map[string][]github.Hook{}, 584 expectedRepoHooks: map[string][]github.Hook{ 585 "org/repo1": {hookBeforeUpdate}, 586 }, 587 }, 588 { 589 name: "add hook for one org", 590 hmacMapForBatchUpdate: map[string]string{"org1": secretBeforeUpdate}, 591 currentOrgHooks: map[string][]github.Hook{}, 592 expectedOrgHooks: map[string][]github.Hook{ 593 "org1": {hookBeforeUpdate}, 594 }, 595 }, 596 { 597 name: "update hook for one org", 598 hmacMapForBatchUpdate: map[string]string{"org1": secretAfterUpdate}, 599 currentOrgHooks: map[string][]github.Hook{ 600 "org1": {hookBeforeUpdate}, 601 }, 602 expectedOrgHooks: map[string][]github.Hook{ 603 "org1": {hookAfterUpdate}, 604 }, 605 }, 606 { 607 name: "update hook for one repo", 608 hmacMapForBatchUpdate: map[string]string{"org/repo1": secretAfterUpdate}, 609 currentRepoHooks: map[string][]github.Hook{ 610 "org/repo1": {hookBeforeUpdate}, 611 }, 612 expectedRepoHooks: map[string][]github.Hook{ 613 "org/repo1": {hookAfterUpdate}, 614 }, 615 }, 616 { 617 name: "add hook for one org, and update hook for one repo", 618 hmacMapForBatchUpdate: map[string]string{"org1": secretAfterUpdate, "org2/repo": secretAfterUpdate}, 619 currentOrgHooks: map[string][]github.Hook{}, 620 expectedOrgHooks: map[string][]github.Hook{ 621 "org1": {hookAfterUpdate}, 622 }, 623 currentRepoHooks: map[string][]github.Hook{ 624 "org2/repo": {hookBeforeUpdate}, 625 }, 626 expectedRepoHooks: map[string][]github.Hook{ 627 "org2/repo": {hookAfterUpdate}, 628 }, 629 }, 630 } 631 632 for _, tc := range cases { 633 t.Run(tc.name, func(t *testing.T) { 634 fakeclient := &fakeghhook.FakeClient{ 635 OrgHooks: tc.currentOrgHooks, 636 RepoHooks: tc.currentRepoHooks, 637 } 638 c := &client{ 639 githubHookClient: fakeclient, 640 hmacMapForBatchUpdate: tc.hmacMapForBatchUpdate, 641 options: options{hookUrl: "http://whatever-hook-url"}, 642 } 643 if err := c.batchOnboardNewTokenForRepos(); err != nil { 644 t.Errorf("unexpected error: %v", err) 645 } 646 if !reflect.DeepEqual(fakeclient.OrgHooks, tc.expectedOrgHooks) { 647 t.Errorf("org hooks %#v != expected %#v", fakeclient.OrgHooks, tc.expectedOrgHooks) 648 } 649 if !reflect.DeepEqual(fakeclient.RepoHooks, tc.expectedRepoHooks) { 650 t.Errorf("repo hooks %#v != expected %#v", fakeclient.RepoHooks, tc.expectedRepoHooks) 651 } 652 }) 653 } 654 } 655 656 func TestHandleInvitation(t *testing.T) { 657 tests := []struct { 658 name string 659 urivs []github.UserRepoInvitation 660 uoivs []github.UserOrgInvitation 661 newHMACConfig config.ManagedWebhooks 662 wantUrivs []github.UserRepoInvitation 663 wantUoivs []github.UserOrgInvitation 664 wantErr error 665 }{ 666 { 667 name: "accept repo invitation", 668 urivs: []github.UserRepoInvitation{ 669 { 670 Repository: &github.Repo{ 671 FullName: "org1/repo1", 672 }, 673 Permission: "admin", 674 }, 675 }, 676 newHMACConfig: config.ManagedWebhooks{ 677 AutoAcceptInvitation: true, 678 OrgRepoConfig: map[string]config.ManagedWebhookInfo{ 679 "org1/repo1": {}, 680 }, 681 }, 682 wantUrivs: []github.UserRepoInvitation{}, 683 }, 684 { 685 name: "accept org invitation", 686 uoivs: []github.UserOrgInvitation{ 687 { 688 Org: github.UserOrganization{ 689 Login: "org1", 690 }, 691 Role: "admin", 692 }, 693 }, 694 newHMACConfig: config.ManagedWebhooks{ 695 AutoAcceptInvitation: true, 696 OrgRepoConfig: map[string]config.ManagedWebhookInfo{ 697 "org1": {}, 698 }, 699 }, 700 wantUoivs: []github.UserOrgInvitation{}, 701 }, 702 { 703 name: "accept org invitation with single repo webhook", 704 uoivs: []github.UserOrgInvitation{ 705 { 706 Org: github.UserOrganization{ 707 Login: "org1", 708 }, 709 Role: "admin", 710 }, 711 }, 712 newHMACConfig: config.ManagedWebhooks{ 713 AutoAcceptInvitation: true, 714 OrgRepoConfig: map[string]config.ManagedWebhookInfo{ 715 "org1/repo1": {}, 716 }, 717 }, 718 wantUoivs: []github.UserOrgInvitation{}, 719 }, 720 { 721 name: "dont accept repo invitation with org webhook", 722 urivs: []github.UserRepoInvitation{ 723 { 724 Repository: &github.Repo{ 725 FullName: "org1/repo1", 726 }, 727 Permission: "admin", 728 }, 729 }, 730 newHMACConfig: config.ManagedWebhooks{ 731 AutoAcceptInvitation: true, 732 OrgRepoConfig: map[string]config.ManagedWebhookInfo{ 733 "org1": {}, 734 }, 735 }, 736 wantUrivs: []github.UserRepoInvitation{ 737 { 738 Repository: &github.Repo{ 739 FullName: "org1/repo1", 740 }, 741 Permission: "admin", 742 }, 743 }, 744 }, 745 { 746 name: "dont accept invitation when opt out", 747 urivs: []github.UserRepoInvitation{ 748 { 749 Repository: &github.Repo{ 750 FullName: "org1/repo1", 751 }, 752 Permission: "admin", 753 }, 754 }, 755 uoivs: []github.UserOrgInvitation{ 756 { 757 Org: github.UserOrganization{ 758 Login: "org2", 759 }, 760 Role: "admin", 761 }, 762 }, 763 newHMACConfig: config.ManagedWebhooks{ 764 AutoAcceptInvitation: false, 765 OrgRepoConfig: map[string]config.ManagedWebhookInfo{ 766 "org2": {}, 767 "org1/repo1": {}, 768 }, 769 }, 770 wantUrivs: []github.UserRepoInvitation{ 771 { 772 Repository: &github.Repo{ 773 FullName: "org1/repo1", 774 }, 775 Permission: "admin", 776 }, 777 }, 778 wantUoivs: []github.UserOrgInvitation{ 779 { 780 Org: github.UserOrganization{ 781 Login: "org2", 782 }, 783 Role: "admin", 784 }, 785 }, 786 }, 787 } 788 789 for _, tc := range tests { 790 t.Run(tc.name, func(t *testing.T) { 791 fgc := fakeghhook.FakeClient{ 792 UserRepoInvitations: tc.urivs, 793 UserOrgInvitations: tc.uoivs, 794 } 795 c := client{ 796 newHMACConfig: tc.newHMACConfig, 797 githubHookClient: &fgc, 798 } 799 800 if wantErr, gotErr := tc.wantErr, c.handleInvitation(); (wantErr == nil && gotErr != nil) || (wantErr != nil && gotErr == nil) || 801 (wantErr != nil && gotErr != nil && !strings.Contains(gotErr.Error(), wantErr.Error())) { 802 t.Fatalf("Error mismatch. Want: %v, got: %v", wantErr, gotErr) 803 } 804 if diff := cmp.Diff(tc.wantUrivs, fgc.UserRepoInvitations); diff != "" { 805 t.Fatalf("User repo invitation mismatch. Want(-), got(+): %s", diff) 806 } 807 if diff := cmp.Diff(tc.wantUoivs, fgc.UserOrgInvitations); diff != "" { 808 t.Fatalf("User org invitation mismatch. Want(-), got(+): %s", diff) 809 } 810 }) 811 } 812 }