sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/plugins/bugzilla/bugzilla_test.go (about)

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package bugzilla
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"testing"
    23  
    24  	"github.com/google/go-cmp/cmp"
    25  	"github.com/sirupsen/logrus"
    26  	"k8s.io/apimachinery/pkg/util/sets"
    27  	"sigs.k8s.io/yaml"
    28  
    29  	cherrypicker "sigs.k8s.io/prow/cmd/external-plugins/cherrypicker/lib"
    30  	"sigs.k8s.io/prow/pkg/bugzilla"
    31  	prowconfig "sigs.k8s.io/prow/pkg/config"
    32  	"sigs.k8s.io/prow/pkg/github"
    33  	"sigs.k8s.io/prow/pkg/github/fakegithub"
    34  	"sigs.k8s.io/prow/pkg/pluginhelp"
    35  	"sigs.k8s.io/prow/pkg/plugins"
    36  )
    37  
    38  var allowEvent = cmp.AllowUnexported(event{})
    39  
    40  func TestHelpProvider(t *testing.T) {
    41  	rawConfig := `default:
    42    "*":
    43      target_release: global-default
    44    "global-branch":
    45      is_open: false
    46      target_release: global-branch-default
    47  orgs:
    48    my-org:
    49      default:
    50        "*":
    51          is_open: true
    52          target_release: my-org-default
    53          state_after_validation:
    54            status: "PRE"
    55        "my-org-branch":
    56          target_release: my-org-branch-default
    57          state_after_validation:
    58            status: "POST"
    59          add_external_link: true
    60      repos:
    61        my-repo:
    62          branches:
    63            "*":
    64              is_open: false
    65              target_release: my-repo-default
    66              valid_states:
    67              - status: VALIDATED
    68            "my-repo-branch":
    69              target_release: my-repo-branch
    70              valid_states:
    71              - status: MODIFIED
    72              add_external_link: true
    73              state_after_merge:
    74                status: MODIFIED
    75            "branch-that-likes-closed-bugs":
    76              valid_states:
    77              - status: VERIFIED
    78              - status: CLOSED
    79                resolution: ERRATA
    80              dependent_bug_states:
    81              - status: CLOSED
    82                resolution: ERRATA
    83              state_after_merge:
    84                status: CLOSED
    85                resolution: FIXED
    86              state_after_validation:
    87                status: CLOSED
    88                resolution: VALIDATED`
    89  
    90  	var config plugins.Bugzilla
    91  	if err := yaml.Unmarshal([]byte(rawConfig), &config); err != nil {
    92  		t.Fatalf("couldn't unmarshal config: %v", err)
    93  	}
    94  
    95  	pc := &plugins.Configuration{Bugzilla: config}
    96  	enabledRepos := []prowconfig.OrgRepo{
    97  		{Org: "some-org", Repo: "some-repo"},
    98  		{Org: "my-org", Repo: "some-repo"},
    99  		{Org: "my-org", Repo: "my-repo"},
   100  	}
   101  	help, err := helpProvider(pc, enabledRepos)
   102  	if err != nil {
   103  		t.Fatalf("unexpected error creating help provider: %v", err)
   104  	}
   105  	// don't check snippet
   106  	help.Snippet = ""
   107  
   108  	expected := &pluginhelp.PluginHelp{
   109  		Description: "The bugzilla plugin ensures that pull requests reference a valid Bugzilla bug in their title.",
   110  		Config: map[string]string{
   111  			"some-org/some-repo": `The plugin has the following configuration:<ul>
   112  <li>by default, valid bugs must target the "global-default" release.</li>
   113  <li>on the "global-branch" branch, valid bugs must be closed and target the "global-branch-default" release.</li>
   114  </ul>`,
   115  			"my-org/some-repo": `The plugin has the following configuration:<ul>
   116  <li>by default, valid bugs must be open and target the "my-org-default" release. After being linked to a pull request, bugs will be moved to the PRE state.</li>
   117  <li>on the "my-org-branch" branch, valid bugs must be open and target the "my-org-branch-default" release. After being linked to a pull request, bugs will be moved to the POST state and updated to refer to the pull request using the external bug tracker.</li>
   118  </ul>`,
   119  			"my-org/my-repo": `The plugin has the following configuration:<ul>
   120  <li>by default, valid bugs must be closed, target the "my-repo-default" release, and be in one of the following states: VALIDATED. After being linked to a pull request, bugs will be moved to the PRE state.</li>
   121  <li>on the "branch-that-likes-closed-bugs" branch, valid bugs must be closed, target the "my-repo-default" release, be in one of the following states: VERIFIED, CLOSED (ERRATA), depend on at least one other bug, and have all dependent bugs in one of the following states: CLOSED (ERRATA). After being linked to a pull request, bugs will be moved to the CLOSED (VALIDATED) state and moved to the CLOSED (FIXED) state when all linked pull requests are merged.</li>
   122  <li>on the "my-org-branch" branch, valid bugs must be closed, target the "my-repo-default" release, and be in one of the following states: VALIDATED. After being linked to a pull request, bugs will be moved to the POST state and updated to refer to the pull request using the external bug tracker.</li>
   123  <li>on the "my-repo-branch" branch, valid bugs must be closed, target the "my-repo-branch" release, and be in one of the following states: MODIFIED. After being linked to a pull request, bugs will be moved to the PRE state, updated to refer to the pull request using the external bug tracker, and moved to the MODIFIED state when all linked pull requests are merged.</li>
   124  </ul>`,
   125  		},
   126  		Commands: []pluginhelp.Command{
   127  			{
   128  				Usage:       "/bugzilla refresh",
   129  				Description: "Check Bugzilla for a valid bug referenced in the PR title",
   130  				Featured:    false,
   131  				WhoCanUse:   "Anyone",
   132  				Examples:    []string{"/bugzilla refresh"},
   133  			}, {
   134  				Usage:       "/bugzilla assign-qa",
   135  				Description: "(DEPRECATED) Assign PR to QA contact specified in Bugzilla",
   136  				Featured:    false,
   137  				WhoCanUse:   "Anyone",
   138  				Examples:    []string{"/bugzilla assign-qa"},
   139  			}, {
   140  				Usage:       "/bugzilla cc-qa",
   141  				Description: "Request PR review from QA contact specified in Bugzilla",
   142  				Featured:    false,
   143  				WhoCanUse:   "Anyone",
   144  				Examples:    []string{"/bugzilla cc-qa"},
   145  			},
   146  		},
   147  	}
   148  
   149  	if actual := help; !reflect.DeepEqual(actual, expected) {
   150  		t.Errorf("resolved incorrect plugin help: %v", cmp.Diff(actual, expected, allowEvent))
   151  	}
   152  }
   153  
   154  func TestDigestPR(t *testing.T) {
   155  	yes := true
   156  	var testCases = []struct {
   157  		name              string
   158  		pre               github.PullRequestEvent
   159  		validateByDefault *bool
   160  		expected          *event
   161  		expectedErr       bool
   162  	}{
   163  		{
   164  			name: "unrelated event gets ignored",
   165  			pre: github.PullRequestEvent{
   166  				Action: github.PullRequestFileAdded,
   167  				PullRequest: github.PullRequest{
   168  					Base: github.PullRequestBranch{
   169  						Repo: github.Repo{
   170  							Owner: github.User{
   171  								Login: "org",
   172  							},
   173  							Name: "repo",
   174  						},
   175  						Ref: "branch",
   176  					},
   177  					Number: 1,
   178  					Title:  "Bug 123: fixed it!",
   179  					State:  "open",
   180  				},
   181  			},
   182  		},
   183  		{
   184  			name: "unrelated title gets ignored",
   185  			pre: github.PullRequestEvent{
   186  				Action: github.PullRequestActionOpened,
   187  				PullRequest: github.PullRequest{
   188  					Base: github.PullRequestBranch{
   189  						Repo: github.Repo{
   190  							Owner: github.User{
   191  								Login: "org",
   192  							},
   193  							Name: "repo",
   194  						},
   195  						Ref: "branch",
   196  					},
   197  					Number: 1,
   198  					Title:  "fixing a typo",
   199  					State:  "open",
   200  				},
   201  			},
   202  		},
   203  		{
   204  			name: "unrelated title gets handled when validating by default",
   205  			pre: github.PullRequestEvent{
   206  				Action: github.PullRequestActionOpened,
   207  				PullRequest: github.PullRequest{
   208  					Base: github.PullRequestBranch{
   209  						Repo: github.Repo{
   210  							Owner: github.User{
   211  								Login: "org",
   212  							},
   213  							Name: "repo",
   214  						},
   215  						Ref: "branch",
   216  					},
   217  					Number:  1,
   218  					Title:   "fixing a typo",
   219  					State:   "open",
   220  					HTMLURL: "http.com",
   221  					User: github.User{
   222  						Login: "user",
   223  					},
   224  				},
   225  			},
   226  			validateByDefault: &yes,
   227  			expected: &event{
   228  				org: "org", repo: "repo", baseRef: "branch", number: 1, state: "open", missing: true, opened: true, bugId: 0, body: "fixing a typo", htmlUrl: "http.com", login: "user",
   229  			},
   230  		},
   231  		{
   232  			name: "title referencing bug gets an event",
   233  			pre: github.PullRequestEvent{
   234  				Action: github.PullRequestActionOpened,
   235  				PullRequest: github.PullRequest{
   236  					Base: github.PullRequestBranch{
   237  						Repo: github.Repo{
   238  							Owner: github.User{
   239  								Login: "org",
   240  							},
   241  							Name: "repo",
   242  						},
   243  						Ref: "branch",
   244  					},
   245  					Number:  1,
   246  					Title:   "Bug 123: fixed it!",
   247  					State:   "open",
   248  					HTMLURL: "http.com",
   249  					User: github.User{
   250  						Login: "user",
   251  					},
   252  				},
   253  			},
   254  			expected: &event{
   255  				org: "org", repo: "repo", baseRef: "branch", number: 1, state: "open", opened: true, bugId: 123, body: "Bug 123: fixed it!", htmlUrl: "http.com", login: "user",
   256  			},
   257  		},
   258  		{
   259  			name: "title referencing bug gets an event on PR merge",
   260  			pre: github.PullRequestEvent{
   261  				Action: github.PullRequestActionClosed,
   262  				PullRequest: github.PullRequest{
   263  					Merged: true,
   264  					Base: github.PullRequestBranch{
   265  						Repo: github.Repo{
   266  							Owner: github.User{
   267  								Login: "org",
   268  							},
   269  							Name: "repo",
   270  						},
   271  						Ref: "branch",
   272  					},
   273  					Number:  1,
   274  					Title:   "Bug 123: fixed it!",
   275  					HTMLURL: "http.com",
   276  					User: github.User{
   277  						Login: "user",
   278  					},
   279  				},
   280  			},
   281  			expected: &event{
   282  				org: "org", repo: "repo", baseRef: "branch", number: 1, merged: true, closed: true, bugId: 123, body: "Bug 123: fixed it!", htmlUrl: "http.com", login: "user",
   283  			},
   284  		},
   285  		{
   286  			name: "title referencing bug gets an event on PR close",
   287  			pre: github.PullRequestEvent{
   288  				Action: github.PullRequestActionClosed,
   289  				PullRequest: github.PullRequest{
   290  					Base: github.PullRequestBranch{
   291  						Repo: github.Repo{
   292  							Owner: github.User{
   293  								Login: "org",
   294  							},
   295  							Name: "repo",
   296  						},
   297  						Ref: "branch",
   298  					},
   299  					Number:  1,
   300  					Title:   "Bug 123: fixed it!",
   301  					HTMLURL: "http.com",
   302  					User: github.User{
   303  						Login: "user",
   304  					},
   305  				},
   306  			},
   307  			expected: &event{
   308  				org: "org", repo: "repo", baseRef: "branch", number: 1, merged: false, closed: true, bugId: 123, body: "Bug 123: fixed it!", htmlUrl: "http.com", login: "user",
   309  			},
   310  		},
   311  		{
   312  			name: "non-bugzilla cherrypick PR sets e.missing to true",
   313  			pre: github.PullRequestEvent{
   314  				Action: github.PullRequestActionOpened,
   315  				PullRequest: github.PullRequest{
   316  					Base: github.PullRequestBranch{
   317  						Repo: github.Repo{
   318  							Owner: github.User{
   319  								Login: "org",
   320  							},
   321  							Name: "repo",
   322  						},
   323  						Ref: "release-4.4",
   324  					},
   325  					Number:  3,
   326  					Title:   "[release-4.4] fixing a typo",
   327  					HTMLURL: "http.com",
   328  					User: github.User{
   329  						Login: "user",
   330  					},
   331  					Body: `This is an automated cherry-pick of #2
   332  
   333  /assign user`,
   334  				},
   335  			},
   336  			expected: &event{
   337  				org: "org", repo: "repo", baseRef: "release-4.4", number: 3, opened: true, body: "[release-4.4] fixing a typo", htmlUrl: "http.com", login: "user", cherrypick: true, cherrypickFromPRNum: 2, cherrypickTo: "release-4.4", missing: true,
   338  			},
   339  		},
   340  		{
   341  			name: "cherrypicked PR gets cherrypick event",
   342  			pre: github.PullRequestEvent{
   343  				Action: github.PullRequestActionOpened,
   344  				PullRequest: github.PullRequest{
   345  					Base: github.PullRequestBranch{
   346  						Repo: github.Repo{
   347  							Owner: github.User{
   348  								Login: "org",
   349  							},
   350  							Name: "repo",
   351  						},
   352  						Ref: "release-4.4",
   353  					},
   354  					Number:  3,
   355  					Title:   "[release-4.4] Bug 123: fixed it!",
   356  					HTMLURL: "http.com",
   357  					User: github.User{
   358  						Login: "user",
   359  					},
   360  					Body: `This is an automated cherry-pick of #2
   361  
   362  /assign user`,
   363  				},
   364  			},
   365  			expected: &event{
   366  				org: "org", repo: "repo", baseRef: "release-4.4", number: 3, opened: true, body: "[release-4.4] Bug 123: fixed it!", htmlUrl: "http.com", login: "user", cherrypick: true, cherrypickFromPRNum: 2, cherrypickTo: "release-4.4", bugId: 123,
   367  			},
   368  		},
   369  		{
   370  			name: "edited cherrypicked PR gets normal event",
   371  			pre: github.PullRequestEvent{
   372  				Action: github.PullRequestActionEdited,
   373  				PullRequest: github.PullRequest{
   374  					Base: github.PullRequestBranch{
   375  						Repo: github.Repo{
   376  							Owner: github.User{
   377  								Login: "org",
   378  							},
   379  							Name: "repo",
   380  						},
   381  						Ref: "release-4.4",
   382  					},
   383  					Number:  3,
   384  					Title:   "[release-4.4] Bug 123: fixed it!",
   385  					HTMLURL: "http.com",
   386  					User: github.User{
   387  						Login: "user",
   388  					},
   389  					Body: `This is an automated cherry-pick of #2
   390  
   391  /assign user`,
   392  				},
   393  			},
   394  			expected: &event{
   395  				org: "org", repo: "repo", baseRef: "release-4.4", number: 3, bugId: 123, body: "[release-4.4] Bug 123: fixed it!", htmlUrl: "http.com", login: "user",
   396  			},
   397  		},
   398  		{
   399  			name: "title change referencing same bug gets no event",
   400  			pre: github.PullRequestEvent{
   401  				Action: github.PullRequestActionOpened,
   402  				PullRequest: github.PullRequest{
   403  					Base: github.PullRequestBranch{
   404  						Repo: github.Repo{
   405  							Owner: github.User{
   406  								Login: "org",
   407  							},
   408  							Name: "repo",
   409  						},
   410  						Ref: "branch",
   411  					},
   412  					Number:  1,
   413  					Title:   "Bug 123: fixed it!",
   414  					HTMLURL: "http.com",
   415  					User: github.User{
   416  						Login: "user",
   417  					},
   418  				},
   419  				Changes: []byte(`{"title":{"from":"Bug 123: fixed it! (WIP)"}}`),
   420  			},
   421  		},
   422  		{
   423  			name: "title change referencing new bug gets event",
   424  			pre: github.PullRequestEvent{
   425  				Action: github.PullRequestActionOpened,
   426  				PullRequest: github.PullRequest{
   427  					Base: github.PullRequestBranch{
   428  						Repo: github.Repo{
   429  							Owner: github.User{
   430  								Login: "org",
   431  							},
   432  							Name: "repo",
   433  						},
   434  						Ref: "branch",
   435  					},
   436  					Number:  1,
   437  					Title:   "Bug 123: fixed it!",
   438  					HTMLURL: "http.com",
   439  					User: github.User{
   440  						Login: "user",
   441  					},
   442  				},
   443  				Changes: []byte(`{"title":{"from":"fixed it! (WIP)"}}`),
   444  			},
   445  			expected: &event{
   446  				org: "org", repo: "repo", baseRef: "branch", number: 1, opened: true, bugId: 123, body: "Bug 123: fixed it!", htmlUrl: "http.com", login: "user",
   447  			},
   448  		},
   449  		{
   450  			name: "title change dereferencing bug gets event",
   451  			pre: github.PullRequestEvent{
   452  				Action: github.PullRequestActionOpened,
   453  				PullRequest: github.PullRequest{
   454  					Base: github.PullRequestBranch{
   455  						Repo: github.Repo{
   456  							Owner: github.User{
   457  								Login: "org",
   458  							},
   459  							Name: "repo",
   460  						},
   461  						Ref: "branch",
   462  					},
   463  					Number:  1,
   464  					Title:   "fixed it!",
   465  					HTMLURL: "http.com",
   466  					User: github.User{
   467  						Login: "user",
   468  					},
   469  				},
   470  				Changes: []byte(`{"title":{"from":"Bug 123: fixed it! (WIP)"}}`),
   471  			},
   472  			expected: &event{
   473  				org: "org", repo: "repo", baseRef: "branch", number: 1, opened: true, missing: true, body: "fixed it!", htmlUrl: "http.com", login: "user",
   474  			},
   475  		},
   476  		{
   477  			name: "title change to no bug with unrelated changes gets no event",
   478  			pre: github.PullRequestEvent{
   479  				Action: github.PullRequestActionOpened,
   480  				PullRequest: github.PullRequest{
   481  					Base: github.PullRequestBranch{
   482  						Repo: github.Repo{
   483  							Owner: github.User{
   484  								Login: "org",
   485  							},
   486  							Name: "repo",
   487  						},
   488  						Ref: "branch",
   489  					},
   490  					Number:  1,
   491  					Title:   "fixed it!",
   492  					HTMLURL: "http.com",
   493  					User: github.User{
   494  						Login: "user",
   495  					},
   496  				},
   497  				Changes: []byte(`{"oops":{"doops":"payload"}}`),
   498  			},
   499  		},
   500  	}
   501  
   502  	for _, testCase := range testCases {
   503  		t.Run(testCase.name, func(t *testing.T) {
   504  			event, err := digestPR(logrus.WithField("testCase", testCase.name), testCase.pre, testCase.validateByDefault)
   505  			if err == nil && testCase.expectedErr {
   506  				t.Errorf("%s: expected an error but got none", testCase.name)
   507  			}
   508  			if err != nil && !testCase.expectedErr {
   509  				t.Errorf("%s: expected no error but got one: %v", testCase.name, err)
   510  			}
   511  
   512  			if actual, expected := event, testCase.expected; !reflect.DeepEqual(actual, expected) {
   513  				t.Errorf("%s: did not get correct event: %v", testCase.name, cmp.Diff(actual, expected, allowEvent))
   514  			}
   515  		})
   516  	}
   517  }
   518  
   519  func TestDigestComment(t *testing.T) {
   520  	var testCases = []struct {
   521  		name            string
   522  		e               github.GenericCommentEvent
   523  		title           string
   524  		merged          bool
   525  		expected        *event
   526  		expectedComment string
   527  		expectedErr     bool
   528  	}{
   529  		{
   530  			name: "unrelated event gets ignored",
   531  			e: github.GenericCommentEvent{
   532  				Action: github.GenericCommentActionDeleted,
   533  				IsPR:   true,
   534  				Body:   "/bugzilla refresh",
   535  				Repo: github.Repo{
   536  					Owner: github.User{
   537  						Login: "org",
   538  					},
   539  					Name: "repo",
   540  				},
   541  				Number: 1,
   542  			},
   543  			title: "Bug 123: oopsie doopsie",
   544  		},
   545  		{
   546  			name: "unrelated title gets an event saying so",
   547  			e: github.GenericCommentEvent{
   548  				Action: github.GenericCommentActionCreated,
   549  				IsPR:   true,
   550  				Body:   "/bugzilla refresh",
   551  				Repo: github.Repo{
   552  					Owner: github.User{
   553  						Login: "org",
   554  					},
   555  					Name: "repo",
   556  				},
   557  				Number: 1,
   558  				User: github.User{
   559  					Login: "user",
   560  				},
   561  				HTMLURL: "www.com",
   562  			},
   563  			title: "cole, please review this typo fix",
   564  			expected: &event{
   565  				org: "org", repo: "repo", baseRef: "branch", number: 1, missing: true, body: "/bugzilla refresh", htmlUrl: "www.com", login: "user", assign: false, cc: false,
   566  			},
   567  		},
   568  		{
   569  			name: "comment on issue gets no event but a comment",
   570  			e: github.GenericCommentEvent{
   571  				Action: github.GenericCommentActionCreated,
   572  				IsPR:   false,
   573  				Body:   "/bugzilla refresh",
   574  				Repo: github.Repo{
   575  					Owner: github.User{
   576  						Login: "org",
   577  					},
   578  					Name: "repo",
   579  				},
   580  				Number: 1,
   581  			},
   582  			title: "someone misspelled words in this repo",
   583  			expectedComment: `org/repo#1:@: Bugzilla bug referencing is only supported for Pull Requests, not issues.
   584  
   585  <details>
   586  
   587  In response to [this]():
   588  
   589  >/bugzilla refresh
   590  
   591  
   592  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
   593  </details>`,
   594  		},
   595  		{
   596  			name: "title referencing bug gets an event",
   597  			e: github.GenericCommentEvent{
   598  				Action: github.GenericCommentActionCreated,
   599  				IsPR:   true,
   600  				Body:   "/bugzilla refresh",
   601  				Repo: github.Repo{
   602  					Owner: github.User{
   603  						Login: "org",
   604  					},
   605  					Name: "repo",
   606  				},
   607  				Number: 1,
   608  				User: github.User{
   609  					Login: "user",
   610  				},
   611  				HTMLURL: "www.com",
   612  			},
   613  			title: "Bug 123: oopsie doopsie",
   614  			expected: &event{
   615  				org: "org", repo: "repo", baseRef: "branch", number: 1, bugId: 123, body: "/bugzilla refresh", htmlUrl: "www.com", login: "user", assign: false, cc: false,
   616  			},
   617  		},
   618  		{
   619  			name: "title referencing bug in a merged PR gets an event",
   620  			e: github.GenericCommentEvent{
   621  				Action: github.GenericCommentActionCreated,
   622  				IsPR:   true,
   623  				Body:   "/bugzilla refresh",
   624  				Repo: github.Repo{
   625  					Owner: github.User{
   626  						Login: "org",
   627  					},
   628  					Name: "repo",
   629  				},
   630  				Number: 1,
   631  				User: github.User{
   632  					Login: "user",
   633  				},
   634  				HTMLURL: "www.com",
   635  			},
   636  			title:  "Bug 123: oopsie doopsie",
   637  			merged: true,
   638  			expected: &event{
   639  				org: "org", repo: "repo", baseRef: "branch", number: 1, bugId: 123, merged: true, body: "/bugzilla refresh", htmlUrl: "www.com", login: "user", assign: false, cc: false,
   640  			},
   641  		},
   642  		{
   643  			name: "assign-qa comment event has assign bool set to true",
   644  			e: github.GenericCommentEvent{
   645  				Action: github.GenericCommentActionCreated,
   646  				IsPR:   true,
   647  				Body:   "/bugzilla assign-qa",
   648  				Repo: github.Repo{
   649  					Owner: github.User{
   650  						Login: "org",
   651  					},
   652  					Name: "repo",
   653  				},
   654  				Number: 1,
   655  				User: github.User{
   656  					Login: "user",
   657  				},
   658  				HTMLURL: "www.com",
   659  			},
   660  			title: "Bug 123: oopsie doopsie",
   661  			expected: &event{
   662  				org: "org", repo: "repo", baseRef: "branch", number: 1, bugId: 123, body: "/bugzilla assign-qa", htmlUrl: "www.com", login: "user", assign: true, cc: false,
   663  			},
   664  		},
   665  		{
   666  			name: "cc-qa comment event has cc bool set to true",
   667  			e: github.GenericCommentEvent{
   668  				Action: github.GenericCommentActionCreated,
   669  				IsPR:   true,
   670  				Body:   "/bugzilla cc-qa",
   671  				Repo: github.Repo{
   672  					Owner: github.User{
   673  						Login: "org",
   674  					},
   675  					Name: "repo",
   676  				},
   677  				Number: 1,
   678  				User: github.User{
   679  					Login: "user",
   680  				},
   681  				HTMLURL: "www.com",
   682  			},
   683  			title: "Bug 123: oopsie doopsie",
   684  			expected: &event{
   685  				org: "org", repo: "repo", baseRef: "branch", number: 1, bugId: 123, body: "/bugzilla cc-qa", htmlUrl: "www.com", login: "user", assign: false, cc: true,
   686  			},
   687  		},
   688  	}
   689  
   690  	for _, testCase := range testCases {
   691  		t.Run(testCase.name, func(t *testing.T) {
   692  			client := fakegithub.NewFakeClient()
   693  			client.PullRequests = map[int]*github.PullRequest{
   694  				1: {Base: github.PullRequestBranch{Ref: "branch"}, Title: testCase.title, Merged: testCase.merged},
   695  			}
   696  			event, err := digestComment(client, logrus.WithField("testCase", testCase.name), testCase.e)
   697  			if err == nil && testCase.expectedErr {
   698  				t.Errorf("%s: expected an error but got none", testCase.name)
   699  			}
   700  			if err != nil && !testCase.expectedErr {
   701  				t.Errorf("%s: expected no error but got one: %v", testCase.name, err)
   702  			}
   703  
   704  			if actual, expected := event, testCase.expected; !reflect.DeepEqual(actual, expected) {
   705  				t.Errorf("%s: did not get correct event: %v", testCase.name, cmp.Diff(actual, expected, allowEvent))
   706  			}
   707  
   708  			checkComments(client, testCase.name, testCase.expectedComment, t)
   709  		})
   710  	}
   711  }
   712  
   713  func TestHandle(t *testing.T) {
   714  	yes := true
   715  	open := true
   716  	v1 := "v1"
   717  	v2 := "v2"
   718  	updated := plugins.BugzillaBugState{Status: "UPDATED"}
   719  	modified := plugins.BugzillaBugState{Status: "MODIFIED"}
   720  	verified := []plugins.BugzillaBugState{{Status: "VERIFIED"}}
   721  	base := &event{
   722  		org: "org", repo: "repo", baseRef: "branch", number: 1, bugId: 123, body: "Bug 123: fixed it!", htmlUrl: "http.com", login: "user",
   723  	}
   724  	var testCases = []struct {
   725  		name                string
   726  		labels              []string
   727  		humanLabelled       bool
   728  		missing             bool
   729  		merged              bool
   730  		closed              bool
   731  		opened              bool
   732  		cherryPick          bool
   733  		cherryPickFromPRNum int
   734  		cherryPickTo        string
   735  		// the "e.body" for PRs is the PR title; this field can be used to replace the "body" for PR handles for cases where the body != description
   736  		body                  string
   737  		externalBugs          []bugzilla.ExternalBug
   738  		prs                   []github.PullRequest
   739  		bugs                  []bugzilla.Bug
   740  		bugComments           map[int][]bugzilla.Comment
   741  		bugErrors             []int
   742  		bugErrorMessages      map[int]string
   743  		bugCreateErrors       []string
   744  		subComponents         map[int]map[string][]string
   745  		options               plugins.BugzillaBranchOptions
   746  		expectedLabels        []string
   747  		expectedComment       string
   748  		expectedBug           *bugzilla.Bug
   749  		expectedBugComments   map[int][]bugzilla.Comment
   750  		expectedExternalBugs  []bugzilla.ExternalBug
   751  		expectedSubComponents map[int]map[string][]string
   752  	}{
   753  		{
   754  			name: "no bug found leaves a comment",
   755  			expectedComment: `org/repo#1:@user: No Bugzilla bug with ID 123 exists in the tracker at www.bugzilla.
   756  Once a valid bug is referenced in the title of this pull request, request a bug refresh with <code>/bugzilla refresh</code>.
   757  
   758  <details>
   759  
   760  In response to [this](http.com):
   761  
   762  >Bug 123: fixed it!
   763  
   764  
   765  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
   766  </details>`,
   767  		},
   768  		{
   769  			name:      "error fetching bug leaves a comment",
   770  			bugErrors: []int{123},
   771  			expectedComment: `org/repo#1:@user: An error was encountered searching for bug 123 on the Bugzilla server at www.bugzilla. No known errors were detected, please see the full error message for details.
   772  
   773  <details><summary>Full error message.</summary>
   774  
   775  <code>
   776  injected error getting bug
   777  </code>
   778  
   779  </details>
   780  
   781  Please contact an administrator to resolve this issue, then request a bug refresh with <code>/bugzilla refresh</code>.
   782  
   783  <details>
   784  
   785  In response to [this](http.com):
   786  
   787  >Bug 123: fixed it!
   788  
   789  
   790  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
   791  </details>`,
   792  		},
   793  		{
   794  			name:             "github 403 gets explained",
   795  			bugErrors:        []int{123},
   796  			bugErrorMessages: map[int]string{123: "There was an error reported for a GitHub REST call"},
   797  			expectedComment: `org/repo#1:@user: An error was encountered searching for bug 123 on the Bugzilla server at www.bugzilla. We were able to detect the following conditions from the error:
   798  
   799  - The Bugzilla server failed to load data from GitHub when creating the bug. This is usually caused by rate-limiting, please try again later.
   800  
   801  
   802  <details><summary>Full error message.</summary>
   803  
   804  <code>
   805  There was an error reported for a GitHub REST call
   806  </code>
   807  
   808  </details>
   809  
   810  Please contact an administrator to resolve this issue, then request a bug refresh with <code>/bugzilla refresh</code>.
   811  
   812  <details>
   813  
   814  In response to [this](http.com):
   815  
   816  >Bug 123: fixed it!
   817  
   818  
   819  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
   820  </details>`,
   821  		},
   822  		{
   823  			name:           "valid bug removes invalid label, adds valid/severity labels and comments",
   824  			bugs:           []bugzilla.Bug{{ID: 123, Severity: "urgent"}},
   825  			options:        plugins.BugzillaBranchOptions{}, // no requirements --> always valid
   826  			labels:         []string{"bugzilla/invalid-bug"},
   827  			expectedLabels: []string{"bugzilla/valid-bug", "bugzilla/severity-urgent"},
   828  			expectedComment: `org/repo#1:@user: This pull request references [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123), which is valid.
   829  
   830  <details><summary>No validations were run on this bug</summary></details>
   831  
   832  <details>
   833  
   834  In response to [this](http.com):
   835  
   836  >Bug 123: fixed it!
   837  
   838  
   839  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
   840  </details>`,
   841  		},
   842  		{
   843  			name:           "invalid bug adds invalid label, removes valid label and comments",
   844  			bugs:           []bugzilla.Bug{{ID: 123, Severity: "high"}},
   845  			options:        plugins.BugzillaBranchOptions{IsOpen: &open},
   846  			labels:         []string{"bugzilla/valid-bug", "bugzilla/severity-urgent"},
   847  			expectedLabels: []string{"bugzilla/invalid-bug", "bugzilla/severity-high"},
   848  			expectedComment: `org/repo#1:@user: This pull request references [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123), which is invalid:
   849   - expected the bug to be open, but it isn't
   850  
   851  Comment <code>/bugzilla refresh</code> to re-evaluate validity if changes to the Bugzilla bug are made, or edit the title of this pull request to link to a different bug.
   852  
   853  <details>
   854  
   855  In response to [this](http.com):
   856  
   857  >Bug 123: fixed it!
   858  
   859  
   860  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
   861  </details>`,
   862  		},
   863  		{
   864  			name:           "invalid bug adds keeps human-added valid bug label",
   865  			bugs:           []bugzilla.Bug{{ID: 123, Severity: "high"}},
   866  			options:        plugins.BugzillaBranchOptions{IsOpen: &open},
   867  			humanLabelled:  true,
   868  			labels:         []string{"bugzilla/valid-bug", "bugzilla/severity-urgent"},
   869  			expectedLabels: []string{"bugzilla/valid-bug", "bugzilla/severity-high"},
   870  			expectedComment: `org/repo#1:@user: This pull request references [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123), which is invalid:
   871   - expected the bug to be open, but it isn't
   872  
   873  Comment <code>/bugzilla refresh</code> to re-evaluate validity if changes to the Bugzilla bug are made, or edit the title of this pull request to link to a different bug.
   874  
   875  Retaining the bugzilla/valid-bug label as it was manually added.
   876  
   877  <details>
   878  
   879  In response to [this](http.com):
   880  
   881  >Bug 123: fixed it!
   882  
   883  
   884  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
   885  </details>`,
   886  		},
   887  		{
   888  			name:    "no bug removes all labels and comments",
   889  			missing: true,
   890  			labels:  []string{"bugzilla/valid-bug", "bugzilla/invalid-bug"},
   891  			expectedComment: `org/repo#1:@user: No Bugzilla bug is referenced in the title of this pull request.
   892  To reference a bug, add 'Bug XXX:' to the title of this pull request and request another bug refresh with <code>/bugzilla refresh</code>.
   893  
   894  <details>
   895  
   896  In response to [this](http.com):
   897  
   898  >Bug 123: fixed it!
   899  
   900  
   901  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
   902  </details>`,
   903  		},
   904  		{
   905  			name:           "valid bug with status update removes invalid label, adds valid label, comments and updates status",
   906  			bugs:           []bugzilla.Bug{{ID: 123, Severity: "medium"}},
   907  			options:        plugins.BugzillaBranchOptions{StateAfterValidation: &updated}, // no requirements --> always valid
   908  			labels:         []string{"bugzilla/invalid-bug"},
   909  			expectedLabels: []string{"bugzilla/valid-bug", "bugzilla/severity-medium"},
   910  			expectedComment: `org/repo#1:@user: This pull request references [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123), which is valid. The bug has been moved to the UPDATED state.
   911  
   912  <details><summary>No validations were run on this bug</summary></details>
   913  
   914  <details>
   915  
   916  In response to [this](http.com):
   917  
   918  >Bug 123: fixed it!
   919  
   920  
   921  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
   922  </details>`,
   923  			expectedBug: &bugzilla.Bug{ID: 123, Status: "UPDATED", Severity: "medium"},
   924  		},
   925  		{
   926  			name:           "valid bug with status update removes invalid label, adds valid label, comments and updates status with resolution",
   927  			bugs:           []bugzilla.Bug{{ID: 123, Status: "MODIFIED", Severity: "low"}},
   928  			options:        plugins.BugzillaBranchOptions{StateAfterValidation: &plugins.BugzillaBugState{Status: "CLOSED", Resolution: "VALIDATED"}}, // no requirements --> always valid
   929  			labels:         []string{"bugzilla/invalid-bug"},
   930  			expectedLabels: []string{"bugzilla/valid-bug", "bugzilla/severity-low"},
   931  			expectedComment: `org/repo#1:@user: This pull request references [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123), which is valid. The bug has been moved to the CLOSED (VALIDATED) state.
   932  
   933  <details><summary>No validations were run on this bug</summary></details>
   934  
   935  <details>
   936  
   937  In response to [this](http.com):
   938  
   939  >Bug 123: fixed it!
   940  
   941  
   942  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
   943  </details>`,
   944  			expectedBug: &bugzilla.Bug{ID: 123, Status: "CLOSED", Resolution: "VALIDATED", Severity: "low"},
   945  		},
   946  		{
   947  			name:           "valid bug with status update removes invalid label, adds valid label, comments and does not update status when it is already correct",
   948  			bugs:           []bugzilla.Bug{{ID: 123, Status: "UPDATED", Severity: "unspecified"}},
   949  			options:        plugins.BugzillaBranchOptions{StateAfterValidation: &updated}, // no requirements --> always valid
   950  			labels:         []string{"bugzilla/invalid-bug"},
   951  			expectedLabels: []string{"bugzilla/valid-bug", "bugzilla/severity-unspecified"},
   952  			expectedComment: `org/repo#1:@user: This pull request references [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123), which is valid.
   953  
   954  <details><summary>No validations were run on this bug</summary></details>
   955  
   956  <details>
   957  
   958  In response to [this](http.com):
   959  
   960  >Bug 123: fixed it!
   961  
   962  
   963  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
   964  </details>`,
   965  			expectedBug: &bugzilla.Bug{ID: 123, Status: "UPDATED", Severity: "unspecified"},
   966  		},
   967  		{
   968  			name:           "valid bug with external link removes invalid label, adds valid label, comments, makes an external bug link",
   969  			bugs:           []bugzilla.Bug{{ID: 123}},
   970  			options:        plugins.BugzillaBranchOptions{AddExternalLink: &yes}, // no requirements --> always valid
   971  			labels:         []string{"bugzilla/invalid-bug"},
   972  			expectedLabels: []string{"bugzilla/valid-bug"},
   973  			expectedComment: `org/repo#1:@user: This pull request references [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123), which is valid. The bug has been updated to refer to the pull request using the external bug tracker.
   974  
   975  <details><summary>No validations were run on this bug</summary></details>
   976  
   977  <details>
   978  
   979  In response to [this](http.com):
   980  
   981  >Bug 123: fixed it!
   982  
   983  
   984  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
   985  </details>`,
   986  			expectedBug:          &bugzilla.Bug{ID: 123},
   987  			expectedExternalBugs: []bugzilla.ExternalBug{{BugzillaBugID: 123, ExternalBugID: "org/repo/pull/1"}},
   988  		},
   989  		{
   990  			name: "valid bug with already existing external link removes invalid label, adds valid label, comments to say nothing changed",
   991  			bugs: []bugzilla.Bug{{ID: 123}},
   992  			externalBugs: []bugzilla.ExternalBug{{
   993  				BugzillaBugID: base.bugId,
   994  				ExternalBugID: fmt.Sprintf("%s/%s/pull/%d", base.org, base.repo, base.number),
   995  			}},
   996  			options:        plugins.BugzillaBranchOptions{AddExternalLink: &yes}, // no requirements --> always valid
   997  			labels:         []string{"bugzilla/invalid-bug"},
   998  			expectedLabels: []string{"bugzilla/valid-bug"},
   999  			expectedComment: `org/repo#1:@user: This pull request references [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123), which is valid.
  1000  
  1001  <details><summary>No validations were run on this bug</summary></details>
  1002  
  1003  <details>
  1004  
  1005  In response to [this](http.com):
  1006  
  1007  >Bug 123: fixed it!
  1008  
  1009  
  1010  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1011  </details>`,
  1012  			expectedBug:          &bugzilla.Bug{ID: 123},
  1013  			expectedExternalBugs: []bugzilla.ExternalBug{{BugzillaBugID: 123, ExternalBugID: "org/repo/pull/1"}},
  1014  		},
  1015  		{
  1016  			name:      "failure to fetch dependent bug results in a comment",
  1017  			bugs:      []bugzilla.Bug{{ID: 123, DependsOn: []int{124}}},
  1018  			bugErrors: []int{124},
  1019  			options:   plugins.BugzillaBranchOptions{DependentBugStates: &verified},
  1020  			expectedComment: `org/repo#1:@user: An error was encountered searching for dependent bug 124 for bug 123 on the Bugzilla server at www.bugzilla. No known errors were detected, please see the full error message for details.
  1021  
  1022  <details><summary>Full error message.</summary>
  1023  
  1024  <code>
  1025  injected error getting bug
  1026  </code>
  1027  
  1028  </details>
  1029  
  1030  Please contact an administrator to resolve this issue, then request a bug refresh with <code>/bugzilla refresh</code>.
  1031  
  1032  <details>
  1033  
  1034  In response to [this](http.com):
  1035  
  1036  >Bug 123: fixed it!
  1037  
  1038  
  1039  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1040  </details>`,
  1041  		},
  1042  		{
  1043  			name:           "valid bug with dependent bugs removes invalid label, adds valid label, comments",
  1044  			bugs:           []bugzilla.Bug{{IsOpen: true, ID: 123, DependsOn: []int{124}, TargetRelease: []string{v1}}, {ID: 124, Status: "VERIFIED", TargetRelease: []string{v2}}},
  1045  			options:        plugins.BugzillaBranchOptions{IsOpen: &yes, TargetRelease: &v1, DependentBugStates: &verified, DependentBugTargetReleases: &[]string{v2}},
  1046  			labels:         []string{"bugzilla/invalid-bug"},
  1047  			expectedLabels: []string{"bugzilla/valid-bug"},
  1048  			expectedComment: `org/repo#1:@user: This pull request references [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123), which is valid.
  1049  
  1050  <details><summary>5 validation(s) were run on this bug</summary>
  1051  
  1052  * bug is open, matching expected state (open)
  1053  * bug target release (v1) matches configured target release for branch (v1)
  1054  * dependent bug [Bugzilla bug 124](www.bugzilla/show_bug.cgi?id=124) is in the state VERIFIED, which is one of the valid states (VERIFIED)
  1055  * dependent [Bugzilla bug 124](www.bugzilla/show_bug.cgi?id=124) targets the "v2" release, which is one of the valid target releases: v2
  1056  * bug has dependents</details>
  1057  
  1058  <details>
  1059  
  1060  In response to [this](http.com):
  1061  
  1062  >Bug 123: fixed it!
  1063  
  1064  
  1065  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1066  </details>`,
  1067  		},
  1068  		{
  1069  			name:   "valid bug on merged PR with one external link migrates to new state with resolution and comments",
  1070  			merged: true,
  1071  			bugs:   []bugzilla.Bug{{ID: 123, Status: "MODIFIED"}},
  1072  			externalBugs: []bugzilla.ExternalBug{{
  1073  				BugzillaBugID: base.bugId,
  1074  				ExternalBugID: fmt.Sprintf("%s/%s/pull/%d", base.org, base.repo, base.number),
  1075  				Org:           base.org, Repo: base.repo, Num: base.number,
  1076  			}},
  1077  			prs:     []github.PullRequest{{Number: base.number, Merged: true}},
  1078  			options: plugins.BugzillaBranchOptions{StateAfterMerge: &plugins.BugzillaBugState{Status: "CLOSED", Resolution: "MERGED"}}, // no requirements --> always valid
  1079  			expectedComment: `org/repo#1:@user: All pull requests linked via external trackers have merged:
  1080   * [org/repo#1](https://github.com/org/repo/pull/1)
  1081  
  1082  [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123) has been moved to the CLOSED (MERGED) state.
  1083  
  1084  <details>
  1085  
  1086  In response to [this](http.com):
  1087  
  1088  >Bug 123: fixed it!
  1089  
  1090  
  1091  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1092  </details>`,
  1093  			expectedBug:          &bugzilla.Bug{ID: 123, Status: "CLOSED", Resolution: "MERGED"},
  1094  			expectedExternalBugs: []bugzilla.ExternalBug{{BugzillaBugID: 123, ExternalBugID: "org/repo/pull/1", Org: "org", Repo: "repo", Num: 1}},
  1095  		},
  1096  		{
  1097  			name:   "valid bug on merged PR with one external link migrates to new state and comments",
  1098  			merged: true,
  1099  			bugs:   []bugzilla.Bug{{ID: 123}},
  1100  			externalBugs: []bugzilla.ExternalBug{{
  1101  				BugzillaBugID: base.bugId,
  1102  				ExternalBugID: fmt.Sprintf("%s/%s/pull/%d", base.org, base.repo, base.number),
  1103  				Org:           base.org, Repo: base.repo, Num: base.number,
  1104  			}},
  1105  			prs:     []github.PullRequest{{Number: base.number, Merged: true}},
  1106  			options: plugins.BugzillaBranchOptions{StateAfterMerge: &modified}, // no requirements --> always valid
  1107  			expectedComment: `org/repo#1:@user: All pull requests linked via external trackers have merged:
  1108   * [org/repo#1](https://github.com/org/repo/pull/1)
  1109  
  1110  [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123) has been moved to the MODIFIED state.
  1111  
  1112  <details>
  1113  
  1114  In response to [this](http.com):
  1115  
  1116  >Bug 123: fixed it!
  1117  
  1118  
  1119  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1120  </details>`,
  1121  			expectedBug:          &bugzilla.Bug{ID: 123, Status: "MODIFIED"},
  1122  			expectedExternalBugs: []bugzilla.ExternalBug{{BugzillaBugID: 123, ExternalBugID: "org/repo/pull/1", Org: "org", Repo: "repo", Num: 1}},
  1123  		},
  1124  		{
  1125  			name:   "valid bug on merged PR with many external links migrates to new state and comments",
  1126  			merged: true,
  1127  			bugs:   []bugzilla.Bug{{ID: 123}},
  1128  			externalBugs: []bugzilla.ExternalBug{{
  1129  				BugzillaBugID: base.bugId,
  1130  				ExternalBugID: fmt.Sprintf("%s/%s/pull/%d", base.org, base.repo, base.number),
  1131  				Org:           base.org, Repo: base.repo, Num: base.number,
  1132  			}, {
  1133  				BugzillaBugID: base.bugId,
  1134  				ExternalBugID: fmt.Sprintf("%s/%s/pull/22", base.org, base.repo),
  1135  				Org:           base.org, Repo: base.repo, Num: 22,
  1136  			}},
  1137  			prs:     []github.PullRequest{{Number: base.number, Merged: true}, {Number: 22, Merged: true}},
  1138  			options: plugins.BugzillaBranchOptions{StateAfterMerge: &modified}, // no requirements --> always valid
  1139  			expectedComment: `org/repo#1:@user: All pull requests linked via external trackers have merged:
  1140   * [org/repo#1](https://github.com/org/repo/pull/1)
  1141   * [org/repo#22](https://github.com/org/repo/pull/22)
  1142  
  1143  [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123) has been moved to the MODIFIED state.
  1144  
  1145  <details>
  1146  
  1147  In response to [this](http.com):
  1148  
  1149  >Bug 123: fixed it!
  1150  
  1151  
  1152  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1153  </details>`,
  1154  			expectedBug: &bugzilla.Bug{ID: 123, Status: "MODIFIED"},
  1155  			expectedExternalBugs: []bugzilla.ExternalBug{
  1156  				{BugzillaBugID: 123, ExternalBugID: "org/repo/pull/1", Org: "org", Repo: "repo", Num: 1},
  1157  				{BugzillaBugID: 123, ExternalBugID: "org/repo/pull/22", Org: "org", Repo: "repo", Num: 22},
  1158  			},
  1159  		},
  1160  		{
  1161  			name:   "valid bug on merged PR with unmerged external links does nothing",
  1162  			merged: true,
  1163  			bugs:   []bugzilla.Bug{{ID: 123}},
  1164  			externalBugs: []bugzilla.ExternalBug{{
  1165  				BugzillaBugID: base.bugId,
  1166  				ExternalBugID: fmt.Sprintf("%s/%s/pull/%d", base.org, base.repo, base.number),
  1167  				Org:           base.org, Repo: base.repo, Num: base.number,
  1168  			}, {
  1169  				BugzillaBugID: base.bugId,
  1170  				ExternalBugID: fmt.Sprintf("%s/%s/pull/22", base.org, base.repo),
  1171  				Org:           base.org, Repo: base.repo, Num: 22,
  1172  			}},
  1173  			prs:         []github.PullRequest{{Number: base.number, Merged: true}, {Number: 22, Merged: false, State: "open"}},
  1174  			options:     plugins.BugzillaBranchOptions{StateAfterMerge: &modified}, // no requirements --> always valid
  1175  			expectedBug: &bugzilla.Bug{ID: 123},
  1176  			expectedExternalBugs: []bugzilla.ExternalBug{
  1177  				{BugzillaBugID: 123, ExternalBugID: "org/repo/pull/1", Org: "org", Repo: "repo", Num: 1},
  1178  				{BugzillaBugID: 123, ExternalBugID: "org/repo/pull/22", Org: "org", Repo: "repo", Num: 22},
  1179  			},
  1180  			expectedComment: `org/repo#1:@user: Some pull requests linked via external trackers have merged:
  1181   * [org/repo#1](https://github.com/org/repo/pull/1)
  1182  
  1183  The following pull requests linked via external trackers have not merged:
  1184   * [org/repo#22](https://github.com/org/repo/pull/22) is open
  1185  
  1186  These pull request must merge or be unlinked from the Bugzilla bug in order for it to move to the next state. Once unlinked, request a bug refresh with <code>/bugzilla refresh</code>.
  1187  
  1188  [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123) has not been moved to the MODIFIED state.
  1189  
  1190  <details>
  1191  
  1192  In response to [this](http.com):
  1193  
  1194  >Bug 123: fixed it!
  1195  
  1196  
  1197  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1198  </details>`,
  1199  		},
  1200  		{
  1201  			name:   "External bug on rep that is not in our config is ignored, bug gets set to MODIFIED",
  1202  			merged: true,
  1203  			bugs:   []bugzilla.Bug{{ID: 123}},
  1204  			externalBugs: []bugzilla.ExternalBug{{
  1205  				BugzillaBugID: base.bugId,
  1206  				ExternalBugID: "unreferenced/repo/pull/22",
  1207  				Org:           "unreferenced", Repo: "repo", Num: 22,
  1208  			}},
  1209  			prs:         []github.PullRequest{{Number: 22, Merged: false, State: "open"}},
  1210  			options:     plugins.BugzillaBranchOptions{StateAfterMerge: &modified}, // no requirements --> always valid
  1211  			expectedBug: &bugzilla.Bug{ID: 123, Status: "MODIFIED"},
  1212  			expectedExternalBugs: []bugzilla.ExternalBug{
  1213  				{BugzillaBugID: 123, ExternalBugID: "unreferenced/repo/pull/22", Org: "unreferenced", Repo: "repo", Num: 22},
  1214  			},
  1215  			expectedComment: `org/repo#1:@user: All pull requests linked via external trackers have merged:
  1216  
  1217  
  1218  [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123) has been moved to the MODIFIED state.
  1219  
  1220  <details>
  1221  
  1222  In response to [this](http.com):
  1223  
  1224  >Bug 123: fixed it!
  1225  
  1226  
  1227  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1228  </details>`,
  1229  		},
  1230  		{
  1231  			name:   "valid bug on merged PR with one external link but no status after merge configured does nothing",
  1232  			merged: true,
  1233  			bugs:   []bugzilla.Bug{{ID: 123}},
  1234  			externalBugs: []bugzilla.ExternalBug{{
  1235  				BugzillaBugID: base.bugId,
  1236  				ExternalBugID: fmt.Sprintf("%s/%s/pull/%d", base.org, base.repo, base.number),
  1237  				Org:           base.org, Repo: base.repo, Num: base.number,
  1238  			}},
  1239  			prs:                  []github.PullRequest{{Number: base.number, Merged: true}},
  1240  			options:              plugins.BugzillaBranchOptions{}, // no requirements --> always valid
  1241  			expectedBug:          &bugzilla.Bug{ID: 123},
  1242  			expectedExternalBugs: []bugzilla.ExternalBug{{BugzillaBugID: 123, ExternalBugID: "org/repo/pull/1", Org: "org", Repo: "repo", Num: 1}},
  1243  		},
  1244  		{
  1245  			name:    "valid bug on merged PR with one external link but no referenced bug in the title does nothing",
  1246  			merged:  true,
  1247  			missing: true,
  1248  			bugs:    []bugzilla.Bug{{ID: 123}},
  1249  			externalBugs: []bugzilla.ExternalBug{{
  1250  				BugzillaBugID: base.bugId,
  1251  				ExternalBugID: fmt.Sprintf("%s/%s/pull/%d", base.org, base.repo, base.number),
  1252  				Org:           base.org, Repo: base.repo, Num: base.number,
  1253  			}},
  1254  			prs:                  []github.PullRequest{{Number: base.number, Merged: true}},
  1255  			options:              plugins.BugzillaBranchOptions{StateAfterMerge: &modified}, // no requirements --> always valid
  1256  			expectedBug:          &bugzilla.Bug{ID: 123},
  1257  			expectedExternalBugs: []bugzilla.ExternalBug{{BugzillaBugID: 123, ExternalBugID: "org/repo/pull/1", Org: "org", Repo: "repo", Num: 1}},
  1258  		},
  1259  		{
  1260  			name:      "valid bug on merged PR with one external link fails to update bug and comments",
  1261  			merged:    true,
  1262  			bugs:      []bugzilla.Bug{{ID: 123}},
  1263  			bugErrors: []int{123},
  1264  			externalBugs: []bugzilla.ExternalBug{{
  1265  				BugzillaBugID: base.bugId,
  1266  				ExternalBugID: fmt.Sprintf("%s/%s/pull/%d", base.org, base.repo, base.number),
  1267  				Org:           base.org, Repo: base.repo, Num: base.number,
  1268  			}},
  1269  			prs:     []github.PullRequest{{Number: base.number, Merged: true}},
  1270  			options: plugins.BugzillaBranchOptions{StateAfterMerge: &modified}, // no requirements --> always valid
  1271  			expectedComment: `org/repo#1:@user: An error was encountered searching for bug 123 on the Bugzilla server at www.bugzilla. No known errors were detected, please see the full error message for details.
  1272  
  1273  <details><summary>Full error message.</summary>
  1274  
  1275  <code>
  1276  injected error getting bug
  1277  </code>
  1278  
  1279  </details>
  1280  
  1281  Please contact an administrator to resolve this issue, then request a bug refresh with <code>/bugzilla refresh</code>.
  1282  
  1283  <details>
  1284  
  1285  In response to [this](http.com):
  1286  
  1287  >Bug 123: fixed it!
  1288  
  1289  
  1290  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1291  </details>`,
  1292  			expectedBug:          &bugzilla.Bug{ID: 123},
  1293  			expectedExternalBugs: []bugzilla.ExternalBug{{BugzillaBugID: 123, ExternalBugID: "org/repo/pull/1", Org: "org", Repo: "repo", Num: 1}},
  1294  		},
  1295  		{
  1296  			name:   "valid bug on merged PR with merged external links but unknown status does not migrate to new state and comments",
  1297  			merged: true,
  1298  			bugs:   []bugzilla.Bug{{ID: 123, Status: "CLOSED", Severity: "urgent"}},
  1299  			externalBugs: []bugzilla.ExternalBug{{
  1300  				BugzillaBugID: base.bugId,
  1301  				ExternalBugID: fmt.Sprintf("%s/%s/pull/%d", base.org, base.repo, base.number),
  1302  				Org:           base.org, Repo: base.repo, Num: base.number,
  1303  			}},
  1304  			prs:     []github.PullRequest{{Number: base.number, Merged: true}},
  1305  			options: plugins.BugzillaBranchOptions{StateAfterValidation: &updated, StateAfterMerge: &modified}, // no requirements --> always valid
  1306  			expectedComment: `org/repo#1:@user: [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123) is in an unrecognized state (CLOSED) and will not be moved to the MODIFIED state.
  1307  
  1308  <details>
  1309  
  1310  In response to [this](http.com):
  1311  
  1312  >Bug 123: fixed it!
  1313  
  1314  
  1315  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1316  </details>`,
  1317  			expectedBug:          &bugzilla.Bug{ID: 123, Status: "CLOSED", Severity: "urgent"},
  1318  			expectedExternalBugs: []bugzilla.ExternalBug{{BugzillaBugID: 123, ExternalBugID: "org/repo/pull/1", Org: "org", Repo: "repo", Num: 1}},
  1319  		},
  1320  		{
  1321  			name:   "closed PR removes link and comments",
  1322  			merged: false,
  1323  			closed: true,
  1324  			bugs:   []bugzilla.Bug{{ID: 123, Status: "CLOSED", Severity: "urgent"}},
  1325  			externalBugs: []bugzilla.ExternalBug{{
  1326  				BugzillaBugID: base.bugId,
  1327  				ExternalBugID: fmt.Sprintf("%s/%s/pull/%d", base.org, base.repo, base.number),
  1328  				Org:           base.org, Repo: base.repo, Num: base.number,
  1329  			}},
  1330  			prs:     []github.PullRequest{{Number: base.number, Merged: false}},
  1331  			options: plugins.BugzillaBranchOptions{AddExternalLink: &yes},
  1332  			expectedComment: `org/repo#1:@user: This pull request references [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123). The bug has been updated to no longer refer to the pull request using the external bug tracker.
  1333  
  1334  <details>
  1335  
  1336  In response to [this](http.com):
  1337  
  1338  >Bug 123: fixed it!
  1339  
  1340  
  1341  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1342  </details>`,
  1343  			expectedBug:          &bugzilla.Bug{ID: 123, Status: "CLOSED", Severity: "urgent"},
  1344  			expectedExternalBugs: []bugzilla.ExternalBug{},
  1345  		},
  1346  		{
  1347  			name:        "closed PR without a link does nothing",
  1348  			merged:      false,
  1349  			closed:      true,
  1350  			bugs:        []bugzilla.Bug{{ID: 123, Status: "CLOSED", Severity: "urgent"}},
  1351  			prs:         []github.PullRequest{{Number: base.number, Merged: false}},
  1352  			options:     plugins.BugzillaBranchOptions{AddExternalLink: &yes},
  1353  			expectedBug: &bugzilla.Bug{ID: 123, Status: "CLOSED", Severity: "urgent"},
  1354  		},
  1355  		{
  1356  			name:        "closed PR removes link, changes bug state, and comments",
  1357  			merged:      false,
  1358  			closed:      true,
  1359  			bugs:        []bugzilla.Bug{{ID: 123, Status: "POST", Severity: "urgent"}},
  1360  			bugComments: map[int][]bugzilla.Comment{123: {{BugID: 123, Count: 0, Text: "This is a bug"}}},
  1361  			externalBugs: []bugzilla.ExternalBug{{
  1362  				BugzillaBugID: base.bugId,
  1363  				ExternalBugID: fmt.Sprintf("%s/%s/pull/%d", base.org, base.repo, base.number),
  1364  				Org:           base.org, Repo: base.repo, Num: base.number,
  1365  			}},
  1366  			prs:     []github.PullRequest{{Number: base.number, Merged: false}},
  1367  			options: plugins.BugzillaBranchOptions{AddExternalLink: &yes, StateAfterClose: &plugins.BugzillaBugState{Status: "NEW"}},
  1368  			expectedComment: `org/repo#1:@user: This pull request references [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123). The bug has been updated to no longer refer to the pull request using the external bug tracker. All external bug links have been closed. The bug has been moved to the NEW state.
  1369  
  1370  <details>
  1371  
  1372  In response to [this](http.com):
  1373  
  1374  >Bug 123: fixed it!
  1375  
  1376  
  1377  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1378  </details>`,
  1379  			expectedBug:          &bugzilla.Bug{ID: 123, Status: "NEW", Severity: "urgent"},
  1380  			expectedExternalBugs: []bugzilla.ExternalBug{},
  1381  			expectedBugComments:  map[int][]bugzilla.Comment{123: {{BugID: 123, Count: 0, Text: "This is a bug"}, {BugID: 123, ID: 1, Count: 1, Text: "Bug status changed to NEW as previous linked PR https://github.com/org/repo/pull/1 has been closed", IsPrivate: true}}},
  1382  		},
  1383  		{
  1384  			name:        "closed PR with missing bug does nothing",
  1385  			merged:      false,
  1386  			closed:      true,
  1387  			missing:     true,
  1388  			bugs:        []bugzilla.Bug{},
  1389  			prs:         []github.PullRequest{{Number: base.number, Merged: false}},
  1390  			options:     plugins.BugzillaBranchOptions{AddExternalLink: &yes, StateAfterClose: &plugins.BugzillaBugState{Status: "NEW"}},
  1391  			expectedBug: &bugzilla.Bug{},
  1392  		},
  1393  		{
  1394  			name:   "closed PR with multiple exernal links removes link, does not change bug state, and comments",
  1395  			merged: false,
  1396  			closed: true,
  1397  			bugs:   []bugzilla.Bug{{ID: 123, Status: "POST", Severity: "urgent"}},
  1398  			externalBugs: []bugzilla.ExternalBug{{
  1399  				BugzillaBugID: base.bugId,
  1400  				ExternalBugID: fmt.Sprintf("%s/%s/pull/%d", base.org, base.repo, base.number),
  1401  				Org:           base.org, Repo: base.repo, Num: base.number,
  1402  			}, {
  1403  				BugzillaBugID: base.bugId,
  1404  				ExternalBugID: fmt.Sprintf("%s/%s/pull/%d", base.org, base.repo, 42),
  1405  				Org:           base.org, Repo: base.repo, Num: 42,
  1406  			}},
  1407  			prs:     []github.PullRequest{{Number: base.number, Merged: false}},
  1408  			options: plugins.BugzillaBranchOptions{AddExternalLink: &yes, StateAfterClose: &plugins.BugzillaBugState{Status: "NEW"}},
  1409  			expectedComment: `org/repo#1:@user: This pull request references [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123). The bug has been updated to no longer refer to the pull request using the external bug tracker.
  1410  
  1411  <details>
  1412  
  1413  In response to [this](http.com):
  1414  
  1415  >Bug 123: fixed it!
  1416  
  1417  
  1418  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1419  </details>`,
  1420  			expectedBug: &bugzilla.Bug{ID: 123, Status: "POST", Severity: "urgent"},
  1421  			expectedExternalBugs: []bugzilla.ExternalBug{{
  1422  				BugzillaBugID: base.bugId,
  1423  				ExternalBugID: fmt.Sprintf("%s/%s/pull/%d", base.org, base.repo, 42),
  1424  				Org:           base.org, Repo: base.repo, Num: 42,
  1425  			}},
  1426  		},
  1427  		{
  1428  			name:                "Cherrypick PR results in cloned bug creation",
  1429  			bugs:                []bugzilla.Bug{{Product: "Test", Component: []string{"TestComponent"}, TargetRelease: []string{"v2"}, ID: 123, Status: "CLOSED", Severity: "urgent"}},
  1430  			bugComments:         map[int][]bugzilla.Comment{123: {{BugID: 123, Count: 0, Text: "This is a bug"}}},
  1431  			prs:                 []github.PullRequest{{Number: base.number, Body: base.body, Title: base.body}, {Number: 2, Body: "This is an automated cherry-pick of #1.\n\n/assign user", Title: "[v1] " + base.body}},
  1432  			body:                "[v1] " + base.body,
  1433  			cherryPick:          true,
  1434  			cherryPickFromPRNum: 1,
  1435  			cherryPickTo:        "v1",
  1436  			options:             plugins.BugzillaBranchOptions{TargetRelease: &v1, EnableBackporting: &yes},
  1437  			expectedComment: `org/repo#1:@user: [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123) has been cloned as [Bugzilla bug 124](www.bugzilla/show_bug.cgi?id=124). Retitling PR to link against new bug.
  1438  /retitle [v1] Bug 124: fixed it!
  1439  
  1440  <details>
  1441  
  1442  In response to [this](http.com):
  1443  
  1444  >[v1] Bug 123: fixed it!
  1445  
  1446  
  1447  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1448  </details>`,
  1449  			expectedBug: &bugzilla.Bug{Product: "Test", Component: []string{"TestComponent"}, TargetRelease: []string{"v1"}, ID: 124, DependsOn: []int{123}, Severity: "urgent"},
  1450  		},
  1451  		{
  1452  			name:                "parent PR of cherrypick not existing results in error",
  1453  			bugs:                []bugzilla.Bug{{Product: "Test", Component: []string{"TestComponent"}, TargetRelease: []string{"v2"}, ID: 123, Status: "CLOSED", Severity: "urgent"}},
  1454  			bugComments:         map[int][]bugzilla.Comment{123: {{BugID: 123, Count: 0, Text: "This is a bug"}}},
  1455  			prs:                 []github.PullRequest{{Number: 2, Body: "This is an automated cherry-pick of #1.\n\n/assign user", Title: "[v1] " + base.body}},
  1456  			body:                "[v1] " + base.body,
  1457  			cherryPick:          true,
  1458  			cherryPickFromPRNum: 1,
  1459  			cherryPickTo:        "v1",
  1460  			options:             plugins.BugzillaBranchOptions{TargetRelease: &v1, EnableBackporting: &yes},
  1461  			expectedComment: `org/repo#1:@user: Error creating a cherry-pick bug in Bugzilla: failed to check the state of cherrypicked pull request at https://github.com/org/repo/pull/1: pull request number 1 does not exist.
  1462  Please contact an administrator to resolve this issue, then request a bug refresh with <code>/bugzilla refresh</code>.
  1463  
  1464  <details>
  1465  
  1466  In response to [this](http.com):
  1467  
  1468  >[v1] Bug 123: fixed it!
  1469  
  1470  
  1471  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1472  </details>`,
  1473  		},
  1474  		{
  1475  			name:                "failure to obtain parent bug for cherrypick results in error",
  1476  			bugs:                []bugzilla.Bug{{Product: "Test", Component: []string{"TestComponent"}, TargetRelease: []string{"v2"}, ID: 123, Status: "CLOSED", Severity: "urgent"}},
  1477  			bugComments:         map[int][]bugzilla.Comment{123: {{BugID: 123, Count: 0, Text: "This is a bug"}}},
  1478  			bugErrors:           []int{123},
  1479  			prs:                 []github.PullRequest{{Number: base.number, Body: base.body, Title: base.body}, {Number: 2, Body: "This is an automated cherry-pick of #1.\n\n/assign user", Title: "[v1] " + base.body}},
  1480  			body:                "[v1] " + base.body,
  1481  			cherryPick:          true,
  1482  			cherryPickFromPRNum: 1,
  1483  			cherryPickTo:        "v1",
  1484  			options:             plugins.BugzillaBranchOptions{TargetRelease: &v1, EnableBackporting: &yes},
  1485  			expectedComment: `org/repo#1:@user: An error was encountered searching for bug 123 on the Bugzilla server at www.bugzilla. No known errors were detected, please see the full error message for details.
  1486  
  1487  <details><summary>Full error message.</summary>
  1488  
  1489  <code>
  1490  injected error getting bug
  1491  </code>
  1492  
  1493  </details>
  1494  
  1495  Please contact an administrator to resolve this issue, then request a bug refresh with <code>/bugzilla refresh</code>.
  1496  
  1497  <details>
  1498  
  1499  In response to [this](http.com):
  1500  
  1501  >[v1] Bug 123: fixed it!
  1502  
  1503  
  1504  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1505  </details>`,
  1506  		}, {
  1507  			name:                "failure to clone bug for cherrypick results in error",
  1508  			bugs:                []bugzilla.Bug{{Product: "Test", Component: []string{"TestComponent"}, TargetRelease: []string{"v2"}, ID: 123, Status: "CLOSED", Severity: "urgent"}},
  1509  			bugComments:         map[int][]bugzilla.Comment{123: {{BugID: 123, Count: 0, Text: "This is a bug"}}},
  1510  			bugCreateErrors:     []string{"+++ This bug was initially created as a clone of Bug #123 +++\n\nThis is a bug"},
  1511  			prs:                 []github.PullRequest{{Number: base.number, Body: base.body, Title: base.body}, {Number: 2, Body: "This is an automated cherry-pick of #1.\n\n/assign user", Title: "[v1] " + base.body}},
  1512  			body:                "[v1] " + base.body,
  1513  			cherryPick:          true,
  1514  			cherryPickFromPRNum: 1,
  1515  			cherryPickTo:        "v1",
  1516  			options:             plugins.BugzillaBranchOptions{TargetRelease: &v1, EnableBackporting: &yes},
  1517  			expectedComment: `org/repo#1:@user: An error was encountered cloning bug for cherrypick for bug 123 on the Bugzilla server at www.bugzilla. No known errors were detected, please see the full error message for details.
  1518  
  1519  <details><summary>Full error message.</summary>
  1520  
  1521  <code>
  1522  injected error creating new bug
  1523  </code>
  1524  
  1525  </details>
  1526  
  1527  Please contact an administrator to resolve this issue, then request a bug refresh with <code>/bugzilla refresh</code>.
  1528  
  1529  <details>
  1530  
  1531  In response to [this](http.com):
  1532  
  1533  >[v1] Bug 123: fixed it!
  1534  
  1535  
  1536  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1537  </details>`,
  1538  		}, {
  1539  			// Since the clone does an update operation as part of the clone, this error still occurs in the call to `CloneBug`.
  1540  			// We cannot easily test the error handling of the target release update call, as that happens after the DependsOn update done during cloning
  1541  			name:                "failure to update bug for results in error",
  1542  			bugs:                []bugzilla.Bug{{Product: "Test", Component: []string{"TestComponent"}, TargetRelease: []string{"v2"}, ID: 123, Status: "CLOSED", Severity: "urgent"}},
  1543  			bugComments:         map[int][]bugzilla.Comment{123: {{BugID: 123, Count: 0, Text: "This is a bug"}}},
  1544  			bugErrors:           []int{124},
  1545  			prs:                 []github.PullRequest{{Number: base.number, Body: base.body, Title: base.body}, {Number: 2, Body: "This is an automated cherry-pick of #1.\n\n/assign user", Title: "[v1] " + base.body}},
  1546  			body:                "[v1] " + base.body,
  1547  			cherryPick:          true,
  1548  			cherryPickFromPRNum: 1,
  1549  			cherryPickTo:        "v1",
  1550  			options:             plugins.BugzillaBranchOptions{TargetRelease: &v1, EnableBackporting: &yes},
  1551  			expectedComment: `org/repo#1:@user: An error was encountered cloning bug for cherrypick for bug 123 on the Bugzilla server at www.bugzilla. No known errors were detected, please see the full error message for details.
  1552  
  1553  <details><summary>Full error message.</summary>
  1554  
  1555  <code>
  1556  injected error updating bug
  1557  </code>
  1558  
  1559  </details>
  1560  
  1561  Please contact an administrator to resolve this issue, then request a bug refresh with <code>/bugzilla refresh</code>.
  1562  
  1563  <details>
  1564  
  1565  In response to [this](http.com):
  1566  
  1567  >[v1] Bug 123: fixed it!
  1568  
  1569  
  1570  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1571  </details>`,
  1572  		}, {
  1573  			name: "If bug clone with correct target version already exists, just retitle PR",
  1574  			bugs: []bugzilla.Bug{
  1575  				{Summary: "This is a test bug", Product: "Test", Component: []string{"TestComponent"}, TargetRelease: []string{"v2"}, ID: 123, Status: "CLOSED", Severity: "urgent", Blocks: []int{124}},
  1576  				{Summary: "This is a test bug", Product: "Test", Component: []string{"TestComponent"}, TargetRelease: []string{"v1"}, ID: 124, Status: "NEW", Severity: "urgent", DependsOn: []int{123}},
  1577  			},
  1578  			bugComments:         map[int][]bugzilla.Comment{123: {{BugID: 123, Count: 0, Text: "This is a bug"}}},
  1579  			prs:                 []github.PullRequest{{Number: base.number, Body: base.body, Title: base.body}, {Number: 2, Body: "This is an automated cherry-pick of #1.\n\n/assign user", Title: "[v1] " + base.body}},
  1580  			body:                "[v1] " + base.body,
  1581  			cherryPick:          true,
  1582  			cherryPickFromPRNum: 1,
  1583  			cherryPickTo:        "v1",
  1584  			options:             plugins.BugzillaBranchOptions{TargetRelease: &v1, EnableBackporting: &yes},
  1585  			expectedComment: `org/repo#1:@user: Detected clone of [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123) with correct target release. Retitling PR to link to clone:
  1586  /retitle [v1] Bug 124: fixed it!
  1587  
  1588  <details>
  1589  
  1590  In response to [this](http.com):
  1591  
  1592  >[v1] Bug 123: fixed it!
  1593  
  1594  
  1595  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1596  </details>`,
  1597  		}, {
  1598  			name: "Clone for different release does not block creation of new clone",
  1599  			bugs: []bugzilla.Bug{
  1600  				{Summary: "This is a test bug", Product: "Test", Component: []string{"TestComponent"}, TargetRelease: []string{"v2"}, ID: 123, Status: "CLOSED", Severity: "urgent", Blocks: []int{124}},
  1601  				{Summary: "This is a test bug", Product: "Test", Component: []string{"TestComponent"}, TargetRelease: []string{"v3"}, ID: 124, Status: "NEW", Severity: "urgent", DependsOn: []int{123}},
  1602  			},
  1603  			bugComments:         map[int][]bugzilla.Comment{123: {{BugID: 123, Count: 0, Text: "This is a bug"}}},
  1604  			prs:                 []github.PullRequest{{Number: base.number, Body: base.body, Title: base.body}, {Number: 2, Body: "This is an automated cherry-pick of #1.\n\n/assign user", Title: "[v1] " + base.body}},
  1605  			body:                "[v1] " + base.body,
  1606  			cherryPick:          true,
  1607  			cherryPickFromPRNum: 1,
  1608  			cherryPickTo:        "v1",
  1609  			options:             plugins.BugzillaBranchOptions{TargetRelease: &v1, EnableBackporting: &yes},
  1610  			expectedComment: `org/repo#1:@user: [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123) has been cloned as [Bugzilla bug 125](www.bugzilla/show_bug.cgi?id=125). Retitling PR to link against new bug.
  1611  /retitle [v1] Bug 125: fixed it!
  1612  
  1613  <details>
  1614  
  1615  In response to [this](http.com):
  1616  
  1617  >[v1] Bug 123: fixed it!
  1618  
  1619  
  1620  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1621  </details>`,
  1622  		}, {
  1623  			name:        "Bug with SubComponents creates bug with correct subcomponents",
  1624  			bugs:        []bugzilla.Bug{{Product: "Test", Component: []string{"TestComponent"}, ID: 123, Status: "CLOSED", Severity: "urgent"}},
  1625  			bugComments: map[int][]bugzilla.Comment{123: {{BugID: 123, Count: 0, Text: "This is a bug"}}},
  1626  			subComponents: map[int]map[string][]string{
  1627  				123: {
  1628  					"TestComponent": {
  1629  						"TestSubComponent",
  1630  					},
  1631  				},
  1632  			},
  1633  			prs:                 []github.PullRequest{{Number: base.number, Body: base.body, Title: base.body}, {Number: 2, Body: "This is an automated cherry-pick of #1.\n\n/assign user", Title: "[v1] " + base.body}},
  1634  			body:                "[v1] " + base.body,
  1635  			cherryPick:          true,
  1636  			cherryPickFromPRNum: 1,
  1637  			cherryPickTo:        "v1",
  1638  			options:             plugins.BugzillaBranchOptions{TargetRelease: &v1, EnableBackporting: &yes},
  1639  			expectedSubComponents: map[int]map[string][]string{
  1640  				123: {
  1641  					"TestComponent": {
  1642  						"TestSubComponent",
  1643  					},
  1644  				},
  1645  				124: {
  1646  					"TestComponent": {
  1647  						"TestSubComponent",
  1648  					},
  1649  				},
  1650  			},
  1651  			expectedComment: `org/repo#1:@user: [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123) has been cloned as [Bugzilla bug 124](www.bugzilla/show_bug.cgi?id=124). Retitling PR to link against new bug.
  1652  /retitle [v1] Bug 124: fixed it!
  1653  
  1654  <details>
  1655  
  1656  In response to [this](http.com):
  1657  
  1658  >[v1] Bug 123: fixed it!
  1659  
  1660  
  1661  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1662  </details>`,
  1663  		}, {
  1664  			name:    "Bug with non-allowed group is ignored",
  1665  			bugs:    []bugzilla.Bug{{ID: 123, Groups: []string{"security"}}},
  1666  			options: plugins.BugzillaBranchOptions{AllowedGroups: []string{"internal"}},
  1667  			prs:     []github.PullRequest{{Number: base.number, Body: base.body, Title: base.body}},
  1668  			// there should be no comment returned in this test case
  1669  		}, {
  1670  			name:           "Bug with non-allowed group on repo with no allowed groups results in comment on /bugzilla refresh",
  1671  			bugs:           []bugzilla.Bug{{ID: 123, Groups: []string{"security"}}},
  1672  			prs:            []github.PullRequest{{Number: base.number, Body: base.body, Title: base.body}},
  1673  			body:           "/bugzilla refresh",
  1674  			expectedLabels: []string{"bugzilla/valid-bug"},
  1675  			expectedComment: `org/repo#1:@user: This pull request references [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123), which is valid.
  1676  
  1677  <details><summary>No validations were run on this bug</summary></details>
  1678  
  1679  <details>
  1680  
  1681  In response to [this](http.com):
  1682  
  1683  >/bugzilla refresh
  1684  
  1685  
  1686  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1687  </details>`,
  1688  		}, {
  1689  			name:    "Bug with non-allowed group on repo with different allowed groups results in comment on /bugzilla refresh",
  1690  			bugs:    []bugzilla.Bug{{ID: 123, Groups: []string{"security"}}},
  1691  			prs:     []github.PullRequest{{Number: base.number, Body: base.body, Title: base.body}},
  1692  			body:    "/bugzilla refresh",
  1693  			options: plugins.BugzillaBranchOptions{AllowedGroups: []string{"internal"}},
  1694  			expectedComment: `org/repo#1:@user: [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123) is in a bug group that is not in the allowed groups for this repo.
  1695  Allowed groups for this repo are:
  1696  - internal
  1697  
  1698  <details>
  1699  
  1700  In response to [this](http.com):
  1701  
  1702  >/bugzilla refresh
  1703  
  1704  
  1705  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1706  </details>`,
  1707  		}, {
  1708  			name:    "Bug with non-allowed group on repo with different allowed groups results in comment on PR creation",
  1709  			bugs:    []bugzilla.Bug{{ID: 123, Groups: []string{"security"}}},
  1710  			prs:     []github.PullRequest{{Number: base.number, Body: base.body, Title: base.body}},
  1711  			body:    "/bugzilla refresh",
  1712  			opened:  true,
  1713  			options: plugins.BugzillaBranchOptions{AllowedGroups: []string{"internal"}},
  1714  			expectedComment: `org/repo#1:@user: [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123) is in a bug group that is not in the allowed groups for this repo.
  1715  Allowed groups for this repo are:
  1716  - internal
  1717  
  1718  <details>
  1719  
  1720  In response to [this](http.com):
  1721  
  1722  >/bugzilla refresh
  1723  
  1724  
  1725  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1726  </details>`,
  1727  		}, {
  1728  			name:           "Bug with allowed group is properly handled",
  1729  			bugs:           []bugzilla.Bug{{ID: 123, Severity: "medium", Groups: []string{"security"}}},
  1730  			options:        plugins.BugzillaBranchOptions{StateAfterValidation: &updated, AllowedGroups: []string{"security"}},
  1731  			labels:         []string{"bugzilla/invalid-bug"},
  1732  			expectedLabels: []string{"bugzilla/valid-bug", "bugzilla/severity-medium"},
  1733  			expectedComment: `org/repo#1:@user: This pull request references [Bugzilla bug 123](www.bugzilla/show_bug.cgi?id=123), which is valid. The bug has been moved to the UPDATED state.
  1734  
  1735  <details><summary>No validations were run on this bug</summary></details>
  1736  
  1737  <details>
  1738  
  1739  In response to [this](http.com):
  1740  
  1741  >Bug 123: fixed it!
  1742  
  1743  
  1744  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
  1745  </details>`,
  1746  			expectedBug: &bugzilla.Bug{ID: 123, Status: "UPDATED", Severity: "medium", Groups: []string{"security"}},
  1747  		},
  1748  	}
  1749  
  1750  	for _, testCase := range testCases {
  1751  		t.Run(testCase.name, func(t *testing.T) {
  1752  			e := *base // copy so parallel tests don't collide
  1753  			gc := fakegithub.NewFakeClient()
  1754  			gc.IssueLabelsExisting = []string{}
  1755  			gc.IssueComments = map[int][]github.IssueComment{}
  1756  			gc.PullRequests = map[int]*github.PullRequest{}
  1757  			gc.WasLabelAddedByHumanVal = testCase.humanLabelled
  1758  			for _, label := range testCase.labels {
  1759  				gc.IssueLabelsExisting = append(gc.IssueLabelsExisting, fmt.Sprintf("%s/%s#%d:%s", e.org, e.repo, e.number, label))
  1760  			}
  1761  			for _, pr := range testCase.prs {
  1762  				pr := pr
  1763  				gc.PullRequests[pr.Number] = &pr
  1764  			}
  1765  			bc := bugzilla.Fake{
  1766  				EndpointString:   "www.bugzilla",
  1767  				Bugs:             map[int]bugzilla.Bug{},
  1768  				SubComponents:    map[int]map[string][]string{},
  1769  				BugComments:      testCase.bugComments,
  1770  				BugErrors:        sets.New[int](),
  1771  				BugErrorMessages: testCase.bugErrorMessages,
  1772  				BugCreateErrors:  sets.New[string](),
  1773  				ExternalBugs:     map[int][]bugzilla.ExternalBug{},
  1774  			}
  1775  			for _, bug := range testCase.bugs {
  1776  				bc.Bugs[bug.ID] = bug
  1777  			}
  1778  			bc.BugErrors.Insert(testCase.bugErrors...)
  1779  			bc.BugCreateErrors.Insert(testCase.bugCreateErrors...)
  1780  			for _, externalBug := range testCase.externalBugs {
  1781  				bc.ExternalBugs[externalBug.BugzillaBugID] = append(bc.ExternalBugs[externalBug.BugzillaBugID], externalBug)
  1782  			}
  1783  			for id, subComponent := range testCase.subComponents {
  1784  				bc.SubComponents[id] = subComponent
  1785  			}
  1786  			e.missing = testCase.missing
  1787  			e.merged = testCase.merged
  1788  			e.closed = testCase.closed || testCase.merged
  1789  			e.opened = testCase.opened
  1790  			e.cherrypick = testCase.cherryPick
  1791  			e.cherrypickFromPRNum = testCase.cherryPickFromPRNum
  1792  			e.cherrypickTo = testCase.cherryPickTo
  1793  			if testCase.body != "" {
  1794  				e.body = testCase.body
  1795  			}
  1796  			err := handle(e, gc, &bc, testCase.options, logrus.WithField("testCase", testCase.name), sets.New[string]("org/repo"))
  1797  			if err != nil {
  1798  				t.Errorf("%s: expected no error but got one: %v", testCase.name, err)
  1799  			}
  1800  
  1801  			expected := sets.New[string]()
  1802  			for _, label := range testCase.expectedLabels {
  1803  				expected.Insert(fmt.Sprintf("%s/%s#%d:%s", e.org, e.repo, e.number, label))
  1804  			}
  1805  
  1806  			actual := sets.New[string](gc.IssueLabelsExisting...)
  1807  			actual.Insert(gc.IssueLabelsAdded...)
  1808  			actual.Delete(gc.IssueLabelsRemoved...)
  1809  
  1810  			if missing := expected.Difference(actual); missing.Len() > 0 {
  1811  				t.Errorf("%s: missing expected labels: %v", testCase.name, sets.List(missing))
  1812  			}
  1813  			if extra := actual.Difference(expected); extra.Len() > 0 {
  1814  				t.Errorf("%s: unexpected labels: %v", testCase.name, sets.List(extra))
  1815  			}
  1816  
  1817  			checkComments(gc, testCase.name, testCase.expectedComment, t)
  1818  
  1819  			if testCase.expectedBug != nil {
  1820  				if actual, expected := bc.Bugs[testCase.expectedBug.ID], *testCase.expectedBug; !reflect.DeepEqual(actual, expected) {
  1821  					t.Errorf("%s: got incorrect bug after update: %s", testCase.name, cmp.Diff(actual, expected, allowEvent))
  1822  				}
  1823  				if actual, expected := bc.ExternalBugs[testCase.expectedBug.ID], testCase.expectedExternalBugs; !reflect.DeepEqual(actual, expected) {
  1824  					t.Errorf("%s: got incorrect external bugs after update: %s", testCase.name, cmp.Diff(actual, expected, allowEvent))
  1825  				}
  1826  			}
  1827  			if testCase.expectedSubComponents != nil && !reflect.DeepEqual(bc.SubComponents, testCase.expectedSubComponents) {
  1828  				t.Errorf("%s: got incorrect subcomponents after update: %s", testCase.name, cmp.Diff(actual, expected))
  1829  			}
  1830  			for bugID, expectedComments := range testCase.expectedBugComments {
  1831  				if actualComments, ok := bc.BugComments[bugID]; ok {
  1832  					for index, comment := range expectedComments {
  1833  						if !reflect.DeepEqual(actualComments[index], comment) {
  1834  							t.Errorf("%s: got incorrect bug comments for bugID %d, index %d: %s", testCase.name, bugID, index, cmp.Diff(actualComments[index], comment))
  1835  						}
  1836  					}
  1837  				} else {
  1838  					t.Errorf("%s: Actual comments map missing bugID %d", testCase.name, bugID)
  1839  				}
  1840  			}
  1841  		})
  1842  	}
  1843  }
  1844  
  1845  func checkComments(client *fakegithub.FakeClient, name, expectedComment string, t *testing.T) {
  1846  	wantedComments := 0
  1847  	if expectedComment != "" {
  1848  		wantedComments = 1
  1849  	}
  1850  	if len(client.IssueCommentsAdded) != wantedComments {
  1851  		t.Errorf("%s: wanted %d comment, got %d: %v", name, wantedComments, len(client.IssueCommentsAdded), client.IssueCommentsAdded)
  1852  	}
  1853  
  1854  	if expectedComment != "" && len(client.IssueCommentsAdded) == 1 {
  1855  		if expectedComment != client.IssueCommentsAdded[0] {
  1856  			t.Errorf("%s: got incorrect comment: %v", name, cmp.Diff(expectedComment, client.IssueCommentsAdded[0]))
  1857  		}
  1858  	}
  1859  }
  1860  
  1861  func TestBugIDFromTitle(t *testing.T) {
  1862  	var testCases = []struct {
  1863  		title            string
  1864  		expectedNum      int
  1865  		expectedNotFound bool
  1866  	}{
  1867  		{
  1868  			title:            "no match",
  1869  			expectedNum:      0,
  1870  			expectedNotFound: true,
  1871  		},
  1872  		{
  1873  			title:       "Bug 12: Canonical",
  1874  			expectedNum: 12,
  1875  		},
  1876  		{
  1877  			title:       "bug 12: Lowercase",
  1878  			expectedNum: 12,
  1879  		},
  1880  		{
  1881  			title:            "Bug 12 : Space before colon",
  1882  			expectedNum:      0,
  1883  			expectedNotFound: true,
  1884  		},
  1885  		{
  1886  			title:       "[rebase release-1.0] Bug 12: Prefix",
  1887  			expectedNum: 12,
  1888  		},
  1889  		{
  1890  			title:       "Revert: \"Bug 12: Revert default\"",
  1891  			expectedNum: 12,
  1892  		},
  1893  		{
  1894  			title:       "Bug 34: Revert: \"Bug 12: Revert default\"",
  1895  			expectedNum: 34,
  1896  		},
  1897  		{
  1898  			title:       "Bug  34: A title with multiple whitespaces between Bug and number",
  1899  			expectedNum: 34,
  1900  		},
  1901  	}
  1902  	for _, testCase := range testCases {
  1903  		t.Run(testCase.title, func(t *testing.T) {
  1904  			num, notFound, err := bugIDFromTitle(testCase.title)
  1905  			if err != nil {
  1906  				t.Errorf("%s: Unexpected error: %v", testCase.title, err)
  1907  			}
  1908  			if num != testCase.expectedNum {
  1909  				t.Errorf("%s: unexpected %d != %d", testCase.title, num, testCase.expectedNum)
  1910  			}
  1911  			if notFound != testCase.expectedNotFound {
  1912  				t.Errorf("%s: unexpected %t != %t", testCase.title, notFound, testCase.expectedNotFound)
  1913  			}
  1914  		})
  1915  	}
  1916  }
  1917  
  1918  func TestValidateBug(t *testing.T) {
  1919  	open, closed := true, false
  1920  	one, two := "v1", "v2"
  1921  	verified := []plugins.BugzillaBugState{{Status: "VERIFIED"}}
  1922  	modified := []plugins.BugzillaBugState{{Status: "MODIFIED"}}
  1923  	updated := plugins.BugzillaBugState{Status: "UPDATED"}
  1924  	var testCases = []struct {
  1925  		name        string
  1926  		bug         bugzilla.Bug
  1927  		dependents  []bugzilla.Bug
  1928  		options     plugins.BugzillaBranchOptions
  1929  		valid       bool
  1930  		validations []string
  1931  		why         []string
  1932  	}{
  1933  		{
  1934  			name:    "no requirements means a valid bug",
  1935  			bug:     bugzilla.Bug{},
  1936  			options: plugins.BugzillaBranchOptions{},
  1937  			valid:   true,
  1938  		},
  1939  		{
  1940  			name:        "matching open requirement means a valid bug",
  1941  			bug:         bugzilla.Bug{IsOpen: true},
  1942  			options:     plugins.BugzillaBranchOptions{IsOpen: &open},
  1943  			valid:       true,
  1944  			validations: []string{"bug is open, matching expected state (open)"},
  1945  		},
  1946  		{
  1947  			name:        "matching closed requirement means a valid bug",
  1948  			bug:         bugzilla.Bug{IsOpen: false},
  1949  			options:     plugins.BugzillaBranchOptions{IsOpen: &closed},
  1950  			valid:       true,
  1951  			validations: []string{"bug isn't open, matching expected state (not open)"},
  1952  		},
  1953  		{
  1954  			name:    "not matching open requirement means an invalid bug",
  1955  			bug:     bugzilla.Bug{IsOpen: false},
  1956  			options: plugins.BugzillaBranchOptions{IsOpen: &open},
  1957  			valid:   false,
  1958  			why:     []string{"expected the bug to be open, but it isn't"},
  1959  		},
  1960  		{
  1961  			name:    "not matching closed requirement means an invalid bug",
  1962  			bug:     bugzilla.Bug{IsOpen: true},
  1963  			options: plugins.BugzillaBranchOptions{IsOpen: &closed},
  1964  			valid:   false,
  1965  			why:     []string{"expected the bug to not be open, but it is"},
  1966  		},
  1967  		{
  1968  			name:        "matching target release requirement means a valid bug",
  1969  			bug:         bugzilla.Bug{TargetRelease: []string{"v1"}},
  1970  			options:     plugins.BugzillaBranchOptions{TargetRelease: &one},
  1971  			valid:       true,
  1972  			validations: []string{"bug target release (v1) matches configured target release for branch (v1)"},
  1973  		},
  1974  		{
  1975  			name:    "not matching target release requirement means an invalid bug",
  1976  			bug:     bugzilla.Bug{TargetRelease: []string{"v2"}},
  1977  			options: plugins.BugzillaBranchOptions{TargetRelease: &one},
  1978  			valid:   false,
  1979  			why:     []string{"expected the bug to target the \"v1\" release, but it targets \"v2\" instead"},
  1980  		},
  1981  		{
  1982  			name:    "not setting target release requirement means an invalid bug",
  1983  			bug:     bugzilla.Bug{},
  1984  			options: plugins.BugzillaBranchOptions{TargetRelease: &one},
  1985  			valid:   false,
  1986  			why:     []string{"expected the bug to target the \"v1\" release, but no target release was set"},
  1987  		},
  1988  		{
  1989  			name:        "matching status requirement means a valid bug",
  1990  			bug:         bugzilla.Bug{Status: "MODIFIED"},
  1991  			options:     plugins.BugzillaBranchOptions{ValidStates: &modified},
  1992  			valid:       true,
  1993  			validations: []string{"bug is in the state MODIFIED, which is one of the valid states (MODIFIED)"},
  1994  		},
  1995  		{
  1996  			name:        "matching status requirement by being in the migrated state means a valid bug",
  1997  			bug:         bugzilla.Bug{Status: "UPDATED"},
  1998  			options:     plugins.BugzillaBranchOptions{ValidStates: &modified, StateAfterValidation: &updated},
  1999  			valid:       true,
  2000  			validations: []string{"bug is in the state UPDATED, which is one of the valid states (MODIFIED, UPDATED)"},
  2001  		},
  2002  		{
  2003  			name:    "not matching status requirement means an invalid bug",
  2004  			bug:     bugzilla.Bug{Status: "MODIFIED"},
  2005  			options: plugins.BugzillaBranchOptions{ValidStates: &verified},
  2006  			valid:   false,
  2007  			why:     []string{"expected the bug to be in one of the following states: VERIFIED, but it is MODIFIED instead"},
  2008  		},
  2009  		{
  2010  			name:    "dependent status requirement with no dependent bugs means a valid bug",
  2011  			bug:     bugzilla.Bug{DependsOn: []int{}},
  2012  			options: plugins.BugzillaBranchOptions{DependentBugStates: &verified},
  2013  			valid:   false,
  2014  			why:     []string{"expected [Bugzilla bug 0](bugzilla.com/show_bug.cgi?id=0) to depend on a bug in one of the following states: VERIFIED, but no dependents were found"},
  2015  		},
  2016  		{
  2017  			name:        "not matching dependent bug status requirement means an invalid bug",
  2018  			bug:         bugzilla.Bug{DependsOn: []int{1}},
  2019  			dependents:  []bugzilla.Bug{{ID: 1, Status: "MODIFIED"}},
  2020  			options:     plugins.BugzillaBranchOptions{DependentBugStates: &verified},
  2021  			valid:       false,
  2022  			validations: []string{"bug has dependents"},
  2023  			why:         []string{"expected dependent [Bugzilla bug 1](bugzilla.com/show_bug.cgi?id=1) to be in one of the following states: VERIFIED, but it is MODIFIED instead"},
  2024  		},
  2025  		{
  2026  			name:        "not matching dependent bug target release requirement means an invalid bug",
  2027  			bug:         bugzilla.Bug{DependsOn: []int{1}},
  2028  			dependents:  []bugzilla.Bug{{ID: 1, TargetRelease: []string{"v2"}}},
  2029  			options:     plugins.BugzillaBranchOptions{DependentBugTargetReleases: &[]string{one}},
  2030  			valid:       false,
  2031  			validations: []string{"bug has dependents"},
  2032  			why:         []string{"expected dependent [Bugzilla bug 1](bugzilla.com/show_bug.cgi?id=1) to target a release in v1, but it targets \"v2\" instead"},
  2033  		},
  2034  		{
  2035  			name:        "not having a dependent bug target release means an invalid bug",
  2036  			bug:         bugzilla.Bug{DependsOn: []int{1}},
  2037  			dependents:  []bugzilla.Bug{{ID: 1, TargetRelease: []string{}}},
  2038  			options:     plugins.BugzillaBranchOptions{DependentBugTargetReleases: &[]string{one}},
  2039  			valid:       false,
  2040  			validations: []string{"bug has dependents"},
  2041  			why:         []string{"expected dependent [Bugzilla bug 1](bugzilla.com/show_bug.cgi?id=1) to target a release in v1, but no target release was set"},
  2042  		},
  2043  		{
  2044  			name:       "matching all requirements means a valid bug",
  2045  			bug:        bugzilla.Bug{IsOpen: false, TargetRelease: []string{"v1"}, Status: "MODIFIED", DependsOn: []int{1}},
  2046  			dependents: []bugzilla.Bug{{ID: 1, Status: "MODIFIED", TargetRelease: []string{"v2"}}},
  2047  			options:    plugins.BugzillaBranchOptions{IsOpen: &closed, TargetRelease: &one, ValidStates: &modified, DependentBugStates: &modified, DependentBugTargetReleases: &[]string{two}},
  2048  			validations: []string{"bug isn't open, matching expected state (not open)",
  2049  				`bug target release (v1) matches configured target release for branch (v1)`,
  2050  				"bug is in the state MODIFIED, which is one of the valid states (MODIFIED)",
  2051  				"dependent bug [Bugzilla bug 1](bugzilla.com/show_bug.cgi?id=1) is in the state MODIFIED, which is one of the valid states (MODIFIED)",
  2052  				`dependent [Bugzilla bug 1](bugzilla.com/show_bug.cgi?id=1) targets the "v2" release, which is one of the valid target releases: v2`,
  2053  				"bug has dependents"},
  2054  			valid: true,
  2055  		},
  2056  		{
  2057  			name:        "matching no requirements means an invalid bug",
  2058  			bug:         bugzilla.Bug{IsOpen: false, TargetRelease: []string{"v1"}, Status: "MODIFIED", DependsOn: []int{1}},
  2059  			dependents:  []bugzilla.Bug{{ID: 1, Status: "MODIFIED"}},
  2060  			options:     plugins.BugzillaBranchOptions{IsOpen: &open, TargetRelease: &two, ValidStates: &verified, DependentBugStates: &verified},
  2061  			valid:       false,
  2062  			validations: []string{"bug has dependents"},
  2063  			why: []string{
  2064  				"expected the bug to be open, but it isn't",
  2065  				"expected the bug to target the \"v2\" release, but it targets \"v1\" instead",
  2066  				"expected the bug to be in one of the following states: VERIFIED, but it is MODIFIED instead",
  2067  				"expected dependent [Bugzilla bug 1](bugzilla.com/show_bug.cgi?id=1) to be in one of the following states: VERIFIED, but it is MODIFIED instead",
  2068  			},
  2069  		},
  2070  		{
  2071  			name:        "matching status means a valid bug when resolution is not required",
  2072  			bug:         bugzilla.Bug{Status: "CLOSED", Resolution: "LOL_GO_AWAY"},
  2073  			options:     plugins.BugzillaBranchOptions{ValidStates: &[]plugins.BugzillaBugState{{Status: "CLOSED"}}},
  2074  			valid:       true,
  2075  			validations: []string{"bug is in the state CLOSED (LOL_GO_AWAY), which is one of the valid states (CLOSED)"},
  2076  		},
  2077  		{
  2078  			name:    "matching just status means an invalid bug when resolution does not match",
  2079  			bug:     bugzilla.Bug{Status: "CLOSED", Resolution: "LOL_GO_AWAY"},
  2080  			options: plugins.BugzillaBranchOptions{ValidStates: &[]plugins.BugzillaBugState{{Status: "CLOSED", Resolution: "ERRATA"}}},
  2081  			valid:   false,
  2082  			why: []string{
  2083  				"expected the bug to be in one of the following states: CLOSED (ERRATA), but it is CLOSED (LOL_GO_AWAY) instead",
  2084  			},
  2085  		},
  2086  		{
  2087  			name:        "matching status and resolution means a valid bug when both are required",
  2088  			bug:         bugzilla.Bug{Status: "CLOSED", Resolution: "ERRATA"},
  2089  			options:     plugins.BugzillaBranchOptions{ValidStates: &[]plugins.BugzillaBugState{{Status: "CLOSED", Resolution: "ERRATA"}}},
  2090  			valid:       true,
  2091  			validations: []string{"bug is in the state CLOSED (ERRATA), which is one of the valid states (CLOSED (ERRATA))"},
  2092  		},
  2093  		{
  2094  			name:        "matching resolution means a valid bug when status is not required",
  2095  			bug:         bugzilla.Bug{Status: "CLOSED", Resolution: "ERRATA"},
  2096  			options:     plugins.BugzillaBranchOptions{ValidStates: &[]plugins.BugzillaBugState{{Resolution: "ERRATA"}}},
  2097  			valid:       true,
  2098  			validations: []string{"bug is in the state CLOSED (ERRATA), which is one of the valid states (any status with resolution ERRATA)"},
  2099  		},
  2100  		{
  2101  			name:    "matching just resolution means an invalid bug when status does not match",
  2102  			bug:     bugzilla.Bug{Status: "CLOSED", Resolution: "ERRATA"},
  2103  			options: plugins.BugzillaBranchOptions{ValidStates: &[]plugins.BugzillaBugState{{Status: "RESOLVED", Resolution: "ERRATA"}}},
  2104  			valid:   false,
  2105  			why: []string{
  2106  				"expected the bug to be in one of the following states: RESOLVED (ERRATA), but it is CLOSED (ERRATA) instead",
  2107  			},
  2108  		},
  2109  		{
  2110  			name:        "matching status on dependent bug means a valid bug when resolution is not required",
  2111  			bug:         bugzilla.Bug{Status: "CLOSED", Resolution: "LOL_GO_AWAY"},
  2112  			dependents:  []bugzilla.Bug{{ID: 1, Status: "CLOSED", Resolution: "LOL_GO_AWAY"}},
  2113  			options:     plugins.BugzillaBranchOptions{DependentBugStates: &[]plugins.BugzillaBugState{{Status: "CLOSED"}}},
  2114  			valid:       true,
  2115  			validations: []string{"dependent bug [Bugzilla bug 1](bugzilla.com/show_bug.cgi?id=1) is in the state CLOSED (LOL_GO_AWAY), which is one of the valid states (CLOSED)", "bug has dependents"},
  2116  		},
  2117  		{
  2118  			name:        "matching just status on dependent bug means an invalid bug when resolution does not match",
  2119  			bug:         bugzilla.Bug{Status: "CLOSED", Resolution: "LOL_GO_AWAY"},
  2120  			dependents:  []bugzilla.Bug{{ID: 1, Status: "CLOSED", Resolution: "LOL_GO_AWAY"}},
  2121  			options:     plugins.BugzillaBranchOptions{DependentBugStates: &[]plugins.BugzillaBugState{{Status: "CLOSED", Resolution: "ERRATA"}}},
  2122  			valid:       false,
  2123  			validations: []string{"bug has dependents"},
  2124  			why: []string{
  2125  				"expected dependent [Bugzilla bug 1](bugzilla.com/show_bug.cgi?id=1) to be in one of the following states: CLOSED (ERRATA), but it is CLOSED (LOL_GO_AWAY) instead",
  2126  			},
  2127  		},
  2128  		{
  2129  			name:        "matching status and resolution on dependent bug means a valid bug when both are required",
  2130  			bug:         bugzilla.Bug{Status: "CLOSED", Resolution: "ERRATA"},
  2131  			dependents:  []bugzilla.Bug{{ID: 1, Status: "CLOSED", Resolution: "ERRATA"}},
  2132  			options:     plugins.BugzillaBranchOptions{DependentBugStates: &[]plugins.BugzillaBugState{{Status: "CLOSED", Resolution: "ERRATA"}}},
  2133  			valid:       true,
  2134  			validations: []string{"dependent bug [Bugzilla bug 1](bugzilla.com/show_bug.cgi?id=1) is in the state CLOSED (ERRATA), which is one of the valid states (CLOSED (ERRATA))", "bug has dependents"},
  2135  		},
  2136  	}
  2137  
  2138  	for _, testCase := range testCases {
  2139  		t.Run(testCase.name, func(t *testing.T) {
  2140  			valid, validations, why := validateBug(testCase.bug, testCase.dependents, testCase.options, "bugzilla.com")
  2141  			if valid != testCase.valid {
  2142  				t.Errorf("%s: didn't validate bug correctly, expected %t got %t", testCase.name, testCase.valid, valid)
  2143  			}
  2144  			if !reflect.DeepEqual(validations, testCase.validations) {
  2145  				t.Errorf("%s: didn't get correct validations: %v", testCase.name, cmp.Diff(testCase.validations, validations, allowEvent))
  2146  			}
  2147  			if !reflect.DeepEqual(why, testCase.why) {
  2148  				t.Errorf("%s: didn't get correct reasons why: %v", testCase.name, cmp.Diff(testCase.why, why, allowEvent))
  2149  			}
  2150  		})
  2151  	}
  2152  }
  2153  
  2154  func TestProcessQuery(t *testing.T) {
  2155  	var testCases = []struct {
  2156  		name     string
  2157  		query    emailToLoginQuery
  2158  		email    string
  2159  		expected string
  2160  	}{
  2161  		{
  2162  			name: "single login returns cc",
  2163  			query: emailToLoginQuery{
  2164  				Search: querySearch{
  2165  					Edges: []queryEdge{{
  2166  						Node: queryNode{
  2167  							User: queryUser{
  2168  								Login: "ValidLogin",
  2169  							},
  2170  						},
  2171  					}},
  2172  				},
  2173  			},
  2174  			email:    "qa_tester@example.com",
  2175  			expected: "Requesting review from QA contact:\n/cc @ValidLogin",
  2176  		}, {
  2177  			name: "no login returns not found error",
  2178  			query: emailToLoginQuery{
  2179  				Search: querySearch{
  2180  					Edges: []queryEdge{},
  2181  				},
  2182  			},
  2183  			email:    "qa_tester@example.com",
  2184  			expected: "No GitHub users were found matching the public email listed for the QA contact in Bugzilla (qa_tester@example.com), skipping review request.",
  2185  		}, {
  2186  			name: "multiple logins returns multiple results error",
  2187  			query: emailToLoginQuery{
  2188  				Search: querySearch{
  2189  					Edges: []queryEdge{{
  2190  						Node: queryNode{
  2191  							User: queryUser{
  2192  								Login: "Login1",
  2193  							},
  2194  						},
  2195  					}, {
  2196  						Node: queryNode{
  2197  							User: queryUser{
  2198  								Login: "Login2",
  2199  							},
  2200  						},
  2201  					}},
  2202  				},
  2203  			},
  2204  			email:    "qa_tester@example.com",
  2205  			expected: "Multiple GitHub users were found matching the public email listed for the QA contact in Bugzilla (qa_tester@example.com), skipping review request. List of users with matching email:\n\t- Login1\n\t- Login2",
  2206  		},
  2207  	}
  2208  	for _, testCase := range testCases {
  2209  		t.Run(testCase.name, func(t *testing.T) {
  2210  			response := processQuery(&testCase.query, testCase.email, logrus.WithField("testCase", testCase.name))
  2211  			if response != testCase.expected {
  2212  				t.Errorf("%s: Expected \"%s\", got \"%s\"", testCase.name, testCase.expected, response)
  2213  			}
  2214  		})
  2215  	}
  2216  }
  2217  
  2218  func TestGetCherrypickPRMatch(t *testing.T) {
  2219  	var prNum = 123
  2220  	var branch = "v2"
  2221  	var testCases = []struct {
  2222  		name      string
  2223  		requestor string
  2224  		note      string
  2225  	}{{
  2226  		name: "No requestor or string",
  2227  	}, {
  2228  		name:      "Include requestor",
  2229  		requestor: "user",
  2230  	}, {
  2231  		name: "Include note",
  2232  		note: "this is a test",
  2233  	}, {
  2234  		name:      "Include requestor and note",
  2235  		requestor: "user",
  2236  		note:      "this is a test",
  2237  	}}
  2238  	var pr = &github.PullRequestEvent{
  2239  		PullRequest: github.PullRequest{
  2240  			Base: github.PullRequestBranch{
  2241  				Ref: branch,
  2242  			},
  2243  		},
  2244  	}
  2245  	for _, testCase := range testCases {
  2246  		testPR := *pr
  2247  		testPR.PullRequest.Body = cherrypicker.CreateCherrypickBody(prNum, testCase.requestor, testCase.note, []string{})
  2248  		cherrypick, cherrypickOfPRNum, cherrypickTo, err := getCherryPickMatch(testPR)
  2249  		if err != nil {
  2250  			t.Fatalf("%s: Got error but did not expect one: %v", testCase.name, err)
  2251  		}
  2252  		if !cherrypick {
  2253  			t.Errorf("%s: Expected cherrypick to be true, but got false", testCase.name)
  2254  		}
  2255  		if cherrypickOfPRNum != prNum {
  2256  			t.Errorf("%s: Got incorrect PR num: Expected %d, got %d", testCase.name, prNum, cherrypickOfPRNum)
  2257  		}
  2258  		if cherrypickTo != "v2" {
  2259  			t.Errorf("%s: Got incorrect cherrypick to branch: Expected %s, got %s", testCase.name, branch, cherrypickTo)
  2260  		}
  2261  	}
  2262  }
  2263  
  2264  func TestUpdateTitleBugID(t *testing.T) {
  2265  	testCases := []struct {
  2266  		name     string
  2267  		title    string
  2268  		expected string
  2269  	}{{
  2270  		name:     "handle `Bug`",
  2271  		title:    "Bug 123: Fix segfault",
  2272  		expected: "Bug 124: Fix segfault",
  2273  	}, {
  2274  		name:     "handle `BUG`",
  2275  		title:    "BUG 123: Fix segfault",
  2276  		expected: "BUG 124: Fix segfault",
  2277  	}, {
  2278  		name:     "handle `[release-4.5] BUG`",
  2279  		title:    "[release-4.5] BUG 123: Fix segfault",
  2280  		expected: "[release-4.5] BUG 124: Fix segfault",
  2281  	}}
  2282  	for _, testCase := range testCases {
  2283  		newTitle, err := updateTitleBugID(testCase.title, 123, 124)
  2284  		if err != nil {
  2285  			t.Errorf("%s: unexpected error: %v", testCase.name, err)
  2286  		}
  2287  		if newTitle != testCase.expected {
  2288  			t.Errorf("%s: Expected `%s`, got `%s`", testCase.name, testCase.expected, newTitle)
  2289  		}
  2290  	}
  2291  }
  2292  
  2293  func TestIsBugAllowed(t *testing.T) {
  2294  	testCases := []struct {
  2295  		name     string
  2296  		bug      *bugzilla.Bug
  2297  		groups   []string
  2298  		expected bool
  2299  	}{
  2300  		{
  2301  			name:     "no groups configured means always allowed",
  2302  			groups:   []string{},
  2303  			expected: true,
  2304  		},
  2305  		{
  2306  			name: "all groups matching is allowed",
  2307  			bug: &bugzilla.Bug{
  2308  				Groups: []string{"whoa", "really", "cool"},
  2309  			},
  2310  			groups:   []string{"whoa", "really", "cool"},
  2311  			expected: true,
  2312  		},
  2313  		{
  2314  			name: "some but not all groups matching is not allowed",
  2315  			bug: &bugzilla.Bug{
  2316  				Groups: []string{"whoa", "really", "cool"},
  2317  			},
  2318  			groups:   []string{"whoa", "really"},
  2319  			expected: false,
  2320  		},
  2321  		{
  2322  			name: "no groups matching is not allowed",
  2323  			bug: &bugzilla.Bug{
  2324  				Groups: []string{"whoa", "really", "cool"},
  2325  			},
  2326  			groups:   []string{"other"},
  2327  			expected: false,
  2328  		},
  2329  		{
  2330  			name: "a subset of groups matching is allowed",
  2331  			bug: &bugzilla.Bug{
  2332  				Groups: []string{"whoa", "really"},
  2333  			},
  2334  			groups:   []string{"whoa", "really", "cool"},
  2335  			expected: true,
  2336  		},
  2337  	}
  2338  	for _, testCase := range testCases {
  2339  		if actual, expected := isBugAllowed(testCase.bug, testCase.groups), testCase.expected; actual != expected {
  2340  			t.Errorf("%s: isBugAllowed returned %v incorrectly", testCase.name, actual)
  2341  		}
  2342  	}
  2343  }