github.com/abayer/test-infra@v0.0.5/prow/plugins/lgtm/lgtm_test.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package lgtm
    18  
    19  import (
    20  	"fmt"
    21  	"testing"
    22  
    23  	"github.com/sirupsen/logrus"
    24  	"k8s.io/apimachinery/pkg/api/equality"
    25  	"k8s.io/apimachinery/pkg/util/sets"
    26  
    27  	"k8s.io/test-infra/prow/github"
    28  	"k8s.io/test-infra/prow/github/fakegithub"
    29  	"k8s.io/test-infra/prow/plugins"
    30  	"k8s.io/test-infra/prow/repoowners"
    31  )
    32  
    33  type fakeOwnersClient struct {
    34  	approvers map[string]sets.String
    35  	reviewers map[string]sets.String
    36  }
    37  
    38  var _ repoowners.Interface = &fakeOwnersClient{}
    39  
    40  func (f *fakeOwnersClient) LoadRepoAliases(org, repo, base string) (repoowners.RepoAliases, error) {
    41  	return nil, nil
    42  }
    43  
    44  func (f *fakeOwnersClient) LoadRepoOwners(org, repo, base string) (repoowners.RepoOwnerInterface, error) {
    45  	return &fakeRepoOwners{approvers: f.approvers, reviewers: f.reviewers}, nil
    46  }
    47  
    48  type fakeRepoOwners struct {
    49  	approvers map[string]sets.String
    50  	reviewers map[string]sets.String
    51  }
    52  
    53  var _ repoowners.RepoOwnerInterface = &fakeRepoOwners{}
    54  
    55  func (f *fakeRepoOwners) FindApproverOwnersForFile(path string) string  { return "" }
    56  func (f *fakeRepoOwners) FindReviewersOwnersForFile(path string) string { return "" }
    57  func (f *fakeRepoOwners) FindLabelsForFile(path string) sets.String     { return nil }
    58  func (f *fakeRepoOwners) IsNoParentOwners(path string) bool             { return false }
    59  func (f *fakeRepoOwners) LeafApprovers(path string) sets.String         { return nil }
    60  func (f *fakeRepoOwners) Approvers(path string) sets.String             { return f.approvers[path] }
    61  func (f *fakeRepoOwners) LeafReviewers(path string) sets.String         { return nil }
    62  func (f *fakeRepoOwners) Reviewers(path string) sets.String             { return f.reviewers[path] }
    63  func (f *fakeRepoOwners) RequiredReviewers(path string) sets.String     { return nil }
    64  
    65  var approvers = map[string]sets.String{
    66  	"doc/README.md": {
    67  		"cjwagner": {},
    68  		"jessica":  {},
    69  	},
    70  }
    71  
    72  var reviewers = map[string]sets.String{
    73  	"doc/README.md": {
    74  		"alice": {},
    75  		"bob":   {},
    76  		"mark":  {},
    77  		"sam":   {},
    78  	},
    79  }
    80  
    81  func TestLGTMComment(t *testing.T) {
    82  	var testcases = []struct {
    83  		name          string
    84  		body          string
    85  		commenter     string
    86  		hasLGTM       bool
    87  		shouldToggle  bool
    88  		shouldComment bool
    89  		shouldAssign  bool
    90  		skipCollab    bool
    91  		prs           map[int]*github.PullRequest
    92  		changes       map[int][]github.PullRequestChange
    93  	}{
    94  		{
    95  			name:         "non-lgtm comment",
    96  			body:         "uh oh",
    97  			commenter:    "o",
    98  			hasLGTM:      false,
    99  			shouldToggle: false,
   100  		},
   101  		{
   102  			name:         "lgtm comment by reviewer, no lgtm on pr",
   103  			body:         "/lgtm",
   104  			commenter:    "reviewer1",
   105  			hasLGTM:      false,
   106  			shouldToggle: true,
   107  		},
   108  		{
   109  			name:         "LGTM comment by reviewer, no lgtm on pr",
   110  			body:         "/LGTM",
   111  			commenter:    "reviewer1",
   112  			hasLGTM:      false,
   113  			shouldToggle: true,
   114  		},
   115  		{
   116  			name:         "lgtm comment by reviewer, lgtm on pr",
   117  			body:         "/lgtm",
   118  			commenter:    "reviewer1",
   119  			hasLGTM:      true,
   120  			shouldToggle: false,
   121  		},
   122  		{
   123  			name:          "lgtm comment by author",
   124  			body:          "/lgtm",
   125  			commenter:     "author",
   126  			hasLGTM:       false,
   127  			shouldToggle:  false,
   128  			shouldComment: true,
   129  		},
   130  		{
   131  			name:          "lgtm cancel by author",
   132  			body:          "/lgtm cancel",
   133  			commenter:     "author",
   134  			hasLGTM:       true,
   135  			shouldToggle:  true,
   136  			shouldAssign:  false,
   137  			shouldComment: false,
   138  		},
   139  		{
   140  			name:          "lgtm comment by non-reviewer",
   141  			body:          "/lgtm",
   142  			commenter:     "o",
   143  			hasLGTM:       false,
   144  			shouldToggle:  true,
   145  			shouldComment: false,
   146  			shouldAssign:  true,
   147  		},
   148  		{
   149  			name:          "lgtm comment by non-reviewer, with trailing space",
   150  			body:          "/lgtm ",
   151  			commenter:     "o",
   152  			hasLGTM:       false,
   153  			shouldToggle:  true,
   154  			shouldComment: false,
   155  			shouldAssign:  true,
   156  		},
   157  		{
   158  			name:          "lgtm comment by non-reviewer, with no-issue",
   159  			body:          "/lgtm no-issue",
   160  			commenter:     "o",
   161  			hasLGTM:       false,
   162  			shouldToggle:  true,
   163  			shouldComment: false,
   164  			shouldAssign:  true,
   165  		},
   166  		{
   167  			name:          "lgtm comment by non-reviewer, with no-issue and trailing space",
   168  			body:          "/lgtm no-issue \r",
   169  			commenter:     "o",
   170  			hasLGTM:       false,
   171  			shouldToggle:  true,
   172  			shouldComment: false,
   173  			shouldAssign:  true,
   174  		},
   175  		{
   176  			name:          "lgtm comment by rando",
   177  			body:          "/lgtm",
   178  			commenter:     "not-in-the-org",
   179  			hasLGTM:       false,
   180  			shouldToggle:  false,
   181  			shouldComment: true,
   182  			shouldAssign:  false,
   183  		},
   184  		{
   185  			name:          "lgtm cancel by non-reviewer",
   186  			body:          "/lgtm cancel",
   187  			commenter:     "o",
   188  			hasLGTM:       true,
   189  			shouldToggle:  true,
   190  			shouldComment: false,
   191  			shouldAssign:  true,
   192  		},
   193  		{
   194  			name:          "lgtm cancel by rando",
   195  			body:          "/lgtm cancel",
   196  			commenter:     "not-in-the-org",
   197  			hasLGTM:       true,
   198  			shouldToggle:  false,
   199  			shouldComment: true,
   200  			shouldAssign:  false,
   201  		},
   202  		{
   203  			name:         "lgtm cancel comment by reviewer",
   204  			body:         "/lgtm cancel",
   205  			commenter:    "reviewer1",
   206  			hasLGTM:      true,
   207  			shouldToggle: true,
   208  		},
   209  		{
   210  			name:         "lgtm cancel comment by reviewer, with trailing space",
   211  			body:         "/lgtm cancel \r",
   212  			commenter:    "reviewer1",
   213  			hasLGTM:      true,
   214  			shouldToggle: true,
   215  		},
   216  		{
   217  			name:         "lgtm cancel comment by reviewer, no lgtm",
   218  			body:         "/lgtm cancel",
   219  			commenter:    "reviewer1",
   220  			hasLGTM:      false,
   221  			shouldToggle: false,
   222  		},
   223  		{
   224  			name:         "lgtm comment, based off OWNERS only",
   225  			body:         "/lgtm",
   226  			commenter:    "sam",
   227  			hasLGTM:      false,
   228  			shouldToggle: true,
   229  			skipCollab:   true,
   230  			prs: map[int]*github.PullRequest{
   231  				5: {
   232  					Base: github.PullRequestBranch{
   233  						Ref: "master",
   234  					},
   235  				},
   236  			},
   237  			changes: map[int][]github.PullRequestChange{
   238  				5: {
   239  					{Filename: "doc/README.md"},
   240  				},
   241  			},
   242  		},
   243  	}
   244  	for _, tc := range testcases {
   245  		t.Logf("Running scenario %q", tc.name)
   246  		fc := &fakegithub.FakeClient{
   247  			IssueComments:      make(map[int][]github.IssueComment),
   248  			PullRequests:       tc.prs,
   249  			PullRequestChanges: tc.changes,
   250  		}
   251  		e := &github.GenericCommentEvent{
   252  			Action:      github.GenericCommentActionCreated,
   253  			IssueState:  "open",
   254  			IsPR:        true,
   255  			Body:        tc.body,
   256  			User:        github.User{Login: tc.commenter},
   257  			IssueAuthor: github.User{Login: "author"},
   258  			Number:      5,
   259  			Assignees:   []github.User{{Login: "reviewer1"}, {Login: "reviewer2"}},
   260  			Repo:        github.Repo{Owner: github.User{Login: "org"}, Name: "repo"},
   261  			HTMLURL:     "<url>",
   262  		}
   263  		if tc.hasLGTM {
   264  			fc.LabelsAdded = []string{"org/repo#5:" + lgtmLabel}
   265  		}
   266  		oc := &fakeOwnersClient{approvers: approvers, reviewers: reviewers}
   267  		pc := &plugins.Configuration{}
   268  		if tc.skipCollab {
   269  			pc.Owners.SkipCollaborators = []string{"org/repo"}
   270  		}
   271  		if err := handleGenericComment(fc, pc, oc, logrus.WithField("plugin", pluginName), *e); err != nil {
   272  			t.Errorf("didn't expect error from lgtmComment: %v", err)
   273  			continue
   274  		}
   275  		if tc.shouldAssign {
   276  			found := false
   277  			for _, a := range fc.AssigneesAdded {
   278  				if a == fmt.Sprintf("%s/%s#%d:%s", "org", "repo", 5, tc.commenter) {
   279  					found = true
   280  					break
   281  				}
   282  			}
   283  			if !found || len(fc.AssigneesAdded) != 1 {
   284  				t.Errorf("should have assigned %s but added assignees are %s", tc.commenter, fc.AssigneesAdded)
   285  			}
   286  		} else if len(fc.AssigneesAdded) != 0 {
   287  			t.Errorf("should not have assigned anyone but assigned %s", fc.AssigneesAdded)
   288  		}
   289  		if tc.shouldToggle {
   290  			if tc.hasLGTM {
   291  				if len(fc.LabelsRemoved) == 0 {
   292  					t.Errorf("should have removed LGTM.")
   293  				} else if len(fc.LabelsAdded) > 1 {
   294  					t.Errorf("should not have added LGTM.")
   295  				}
   296  			} else {
   297  				if len(fc.LabelsAdded) == 0 {
   298  					t.Errorf("should have added LGTM.")
   299  				} else if len(fc.LabelsRemoved) > 0 {
   300  					t.Errorf("should not have removed LGTM.")
   301  				}
   302  			}
   303  		} else if len(fc.LabelsRemoved) > 0 {
   304  			t.Errorf("should not have removed LGTM.")
   305  		} else if (tc.hasLGTM && len(fc.LabelsAdded) > 1) || (!tc.hasLGTM && len(fc.LabelsAdded) > 0) {
   306  			t.Errorf("should not have added LGTM.")
   307  		}
   308  		if tc.shouldComment && len(fc.IssueComments[5]) != 1 {
   309  			t.Errorf("should have commented.")
   310  		} else if !tc.shouldComment && len(fc.IssueComments[5]) != 0 {
   311  			t.Errorf("should not have commented.")
   312  		}
   313  	}
   314  }
   315  
   316  func TestLGTMCommentWithLGTMNoti(t *testing.T) {
   317  	var testcases = []struct {
   318  		name         string
   319  		body         string
   320  		commenter    string
   321  		shouldDelete bool
   322  	}{
   323  		{
   324  			name:         "non-lgtm comment",
   325  			body:         "uh oh",
   326  			commenter:    "o",
   327  			shouldDelete: false,
   328  		},
   329  		{
   330  			name:         "lgtm comment by reviewer, no lgtm on pr",
   331  			body:         "/lgtm",
   332  			commenter:    "reviewer1",
   333  			shouldDelete: true,
   334  		},
   335  		{
   336  			name:         "LGTM comment by reviewer, no lgtm on pr",
   337  			body:         "/LGTM",
   338  			commenter:    "reviewer1",
   339  			shouldDelete: true,
   340  		},
   341  		{
   342  			name:         "lgtm comment by author",
   343  			body:         "/lgtm",
   344  			commenter:    "author",
   345  			shouldDelete: false,
   346  		},
   347  		{
   348  			name:         "lgtm comment by non-reviewer",
   349  			body:         "/lgtm",
   350  			commenter:    "o",
   351  			shouldDelete: true,
   352  		},
   353  		{
   354  			name:         "lgtm comment by non-reviewer, with trailing space",
   355  			body:         "/lgtm ",
   356  			commenter:    "o",
   357  			shouldDelete: true,
   358  		},
   359  		{
   360  			name:         "lgtm comment by non-reviewer, with no-issue",
   361  			body:         "/lgtm no-issue",
   362  			commenter:    "o",
   363  			shouldDelete: true,
   364  		},
   365  		{
   366  			name:         "lgtm comment by non-reviewer, with no-issue and trailing space",
   367  			body:         "/lgtm no-issue \r",
   368  			commenter:    "o",
   369  			shouldDelete: true,
   370  		},
   371  		{
   372  			name:         "lgtm comment by rando",
   373  			body:         "/lgtm",
   374  			commenter:    "not-in-the-org",
   375  			shouldDelete: false,
   376  		},
   377  		{
   378  			name:         "lgtm cancel comment by reviewer, no lgtm",
   379  			body:         "/lgtm cancel",
   380  			commenter:    "reviewer1",
   381  			shouldDelete: false,
   382  		},
   383  	}
   384  	for _, tc := range testcases {
   385  		fc := &fakegithub.FakeClient{
   386  			IssueComments: make(map[int][]github.IssueComment),
   387  		}
   388  		e := &github.GenericCommentEvent{
   389  			Action:      github.GenericCommentActionCreated,
   390  			IssueState:  "open",
   391  			IsPR:        true,
   392  			Body:        tc.body,
   393  			User:        github.User{Login: tc.commenter},
   394  			IssueAuthor: github.User{Login: "author"},
   395  			Number:      5,
   396  			Assignees:   []github.User{{Login: "reviewer1"}, {Login: "reviewer2"}},
   397  			Repo:        github.Repo{Owner: github.User{Login: "org"}, Name: "repo"},
   398  			HTMLURL:     "<url>",
   399  		}
   400  		botName, err := fc.BotName()
   401  		if err != nil {
   402  			t.Fatalf("For case %s, could not get Bot nam", tc.name)
   403  		}
   404  		ic := github.IssueComment{
   405  			User: github.User{
   406  				Login: botName,
   407  			},
   408  			Body: removeLGTMLabelNoti,
   409  		}
   410  		fc.IssueComments[5] = append(fc.IssueComments[5], ic)
   411  		oc := &fakeOwnersClient{approvers: approvers, reviewers: reviewers}
   412  		pc := &plugins.Configuration{}
   413  		if err := handleGenericComment(fc, pc, oc, logrus.WithField("plugin", pluginName), *e); err != nil {
   414  			t.Errorf("For case %s, didn't expect error from lgtmComment: %v", tc.name, err)
   415  			continue
   416  		}
   417  		found := false
   418  		for _, v := range fc.IssueComments[5] {
   419  			if v.User.Login == botName && v.Body == removeLGTMLabelNoti {
   420  				found = true
   421  				break
   422  			}
   423  		}
   424  		if tc.shouldDelete {
   425  			if found {
   426  				t.Errorf("For case %s, LGTM removed notification should have been deleted", tc.name)
   427  			}
   428  		} else {
   429  			if !found {
   430  				t.Errorf("For case %s, LGTM removed notification should not have been deleted", tc.name)
   431  			}
   432  		}
   433  	}
   434  }
   435  
   436  func TestLGTMFromApproveReview(t *testing.T) {
   437  	var testcases = []struct {
   438  		name          string
   439  		state         github.ReviewState
   440  		body          string
   441  		reviewer      string
   442  		hasLGTM       bool
   443  		shouldToggle  bool
   444  		shouldComment bool
   445  		shouldAssign  bool
   446  	}{
   447  		{
   448  			name:          "Request changes review by reviewer, no lgtm on pr",
   449  			state:         github.ReviewStateChangesRequested,
   450  			reviewer:      "reviewer1",
   451  			hasLGTM:       false,
   452  			shouldToggle:  false,
   453  			shouldAssign:  false,
   454  			shouldComment: false,
   455  		},
   456  		{
   457  			name:         "Request changes review by reviewer, lgtm on pr",
   458  			state:        github.ReviewStateChangesRequested,
   459  			reviewer:     "reviewer1",
   460  			hasLGTM:      true,
   461  			shouldToggle: true,
   462  			shouldAssign: false,
   463  		},
   464  		{
   465  			name:         "Approve review by reviewer, no lgtm on pr",
   466  			state:        github.ReviewStateApproved,
   467  			reviewer:     "reviewer1",
   468  			hasLGTM:      false,
   469  			shouldToggle: true,
   470  		},
   471  		{
   472  			name:         "Approve review by reviewer, lgtm on pr",
   473  			state:        github.ReviewStateApproved,
   474  			reviewer:     "reviewer1",
   475  			hasLGTM:      true,
   476  			shouldToggle: false,
   477  			shouldAssign: false,
   478  		},
   479  		{
   480  			name:          "Approve review by non-reviewer, no lgtm on pr",
   481  			state:         github.ReviewStateApproved,
   482  			reviewer:      "o",
   483  			hasLGTM:       false,
   484  			shouldToggle:  true,
   485  			shouldComment: false,
   486  			shouldAssign:  true,
   487  		},
   488  		{
   489  			name:          "Request changes review by non-reviewer, no lgtm on pr",
   490  			state:         github.ReviewStateChangesRequested,
   491  			reviewer:      "o",
   492  			hasLGTM:       false,
   493  			shouldToggle:  false,
   494  			shouldComment: false,
   495  			shouldAssign:  true,
   496  		},
   497  		{
   498  			name:          "Approve review by rando",
   499  			state:         github.ReviewStateApproved,
   500  			reviewer:      "not-in-the-org",
   501  			hasLGTM:       false,
   502  			shouldToggle:  false,
   503  			shouldComment: true,
   504  			shouldAssign:  false,
   505  		},
   506  		{
   507  			name:          "Comment review by issue author, no lgtm on pr",
   508  			state:         github.ReviewStateCommented,
   509  			reviewer:      "author",
   510  			hasLGTM:       false,
   511  			shouldToggle:  false,
   512  			shouldComment: false,
   513  			shouldAssign:  false,
   514  		},
   515  		{
   516  			name:          "Comment body has /lgtm on Comment Review ",
   517  			state:         github.ReviewStateCommented,
   518  			reviewer:      "reviewer1",
   519  			body:          "/lgtm",
   520  			hasLGTM:       false,
   521  			shouldToggle:  false,
   522  			shouldComment: false,
   523  			shouldAssign:  false,
   524  		},
   525  		{
   526  			name:          "Comment body has /lgtm cancel on Approve Review",
   527  			state:         github.ReviewStateApproved,
   528  			reviewer:      "reviewer1",
   529  			body:          "/lgtm cancel",
   530  			hasLGTM:       false,
   531  			shouldToggle:  false,
   532  			shouldComment: false,
   533  			shouldAssign:  false,
   534  		},
   535  	}
   536  	for _, tc := range testcases {
   537  		fc := &fakegithub.FakeClient{
   538  			IssueComments: make(map[int][]github.IssueComment),
   539  		}
   540  		e := &github.ReviewEvent{
   541  			Review:      github.Review{Body: tc.body, State: tc.state, HTMLURL: "<url>", User: github.User{Login: tc.reviewer}},
   542  			PullRequest: github.PullRequest{User: github.User{Login: "author"}, Assignees: []github.User{{Login: "reviewer1"}, {Login: "reviewer2"}}, Number: 5},
   543  			Repo:        github.Repo{Owner: github.User{Login: "org"}, Name: "repo"},
   544  		}
   545  		if tc.hasLGTM {
   546  			fc.LabelsAdded = []string{"org/repo#5:" + lgtmLabel}
   547  		}
   548  		oc := &fakeOwnersClient{approvers: approvers, reviewers: reviewers}
   549  		pc := &plugins.Configuration{}
   550  		if err := handlePullRequestReview(fc, pc, oc, logrus.WithField("plugin", pluginName), *e); err != nil {
   551  			t.Errorf("For case %s, didn't expect error from pull request review: %v", tc.name, err)
   552  			continue
   553  		}
   554  		if tc.shouldAssign {
   555  			found := false
   556  			for _, a := range fc.AssigneesAdded {
   557  				if a == fmt.Sprintf("%s/%s#%d:%s", "org", "repo", 5, tc.reviewer) {
   558  					found = true
   559  					break
   560  				}
   561  			}
   562  			if !found || len(fc.AssigneesAdded) != 1 {
   563  				t.Errorf("For case %s, should have assigned %s but added assignees are %s", tc.name, tc.reviewer, fc.AssigneesAdded)
   564  			}
   565  		} else if len(fc.AssigneesAdded) != 0 {
   566  			t.Errorf("For case %s, should not have assigned anyone but assigned %s", tc.name, fc.AssigneesAdded)
   567  		}
   568  		if tc.shouldToggle {
   569  			if tc.hasLGTM {
   570  				if len(fc.LabelsRemoved) == 0 {
   571  					t.Errorf("For case %s, should have removed LGTM.", tc.name)
   572  				} else if len(fc.LabelsAdded) > 1 {
   573  					t.Errorf("For case %s, should not have added LGTM.", tc.name)
   574  				}
   575  			} else {
   576  				if len(fc.LabelsAdded) == 0 {
   577  					t.Errorf("For case %s, should have added LGTM.", tc.name)
   578  				} else if len(fc.LabelsRemoved) > 0 {
   579  					t.Errorf("For case %s, should not have removed LGTM.", tc.name)
   580  				}
   581  			}
   582  		} else if len(fc.LabelsRemoved) > 0 {
   583  			t.Errorf("For case %s, should not have removed LGTM.", tc.name)
   584  		} else if (tc.hasLGTM && len(fc.LabelsAdded) > 1) || (!tc.hasLGTM && len(fc.LabelsAdded) > 0) {
   585  			t.Errorf("For case %s, should not have added LGTM.", tc.name)
   586  		}
   587  		if tc.shouldComment && len(fc.IssueComments[5]) != 1 {
   588  			t.Errorf("For case %s, should have commented.", tc.name)
   589  		} else if !tc.shouldComment && len(fc.IssueComments[5]) != 0 {
   590  			t.Errorf("For case %s, should not have commented.", tc.name)
   591  		}
   592  	}
   593  }
   594  
   595  type fakeIssueComment struct {
   596  	Owner   string
   597  	Repo    string
   598  	Number  int
   599  	Comment string
   600  }
   601  
   602  type githubUnlabeler struct {
   603  	labelsRemoved    []string
   604  	issueComments    []fakeIssueComment
   605  	removeLabelErr   error
   606  	createCommentErr error
   607  }
   608  
   609  func (c *githubUnlabeler) RemoveLabel(owner, repo string, pr int, label string) error {
   610  	c.labelsRemoved = append(c.labelsRemoved, label)
   611  	return c.removeLabelErr
   612  }
   613  
   614  func (c *githubUnlabeler) CreateComment(owner, repo string, number int, comment string) error {
   615  	ic := fakeIssueComment{
   616  		Owner:   owner,
   617  		Repo:    repo,
   618  		Number:  number,
   619  		Comment: comment,
   620  	}
   621  	c.issueComments = append(c.issueComments, ic)
   622  	return c.createCommentErr
   623  }
   624  
   625  func TestHandlePullRequest(t *testing.T) {
   626  	cases := []struct {
   627  		name             string
   628  		event            github.PullRequestEvent
   629  		removeLabelErr   error
   630  		createCommentErr error
   631  
   632  		err           error
   633  		labelsRemoved []string
   634  		issueComments []fakeIssueComment
   635  
   636  		expectNoComments bool
   637  	}{
   638  		{
   639  			name: "pr_synchronize, no RemoveLabel error",
   640  			event: github.PullRequestEvent{
   641  				Action: github.PullRequestActionSynchronize,
   642  				PullRequest: github.PullRequest{
   643  					Number: 101,
   644  					Base: github.PullRequestBranch{
   645  						Repo: github.Repo{
   646  							Owner: github.User{
   647  								Login: "kubernetes",
   648  							},
   649  							Name: "kubernetes",
   650  						},
   651  					},
   652  				},
   653  			},
   654  			labelsRemoved: []string{lgtmLabel},
   655  			issueComments: []fakeIssueComment{
   656  				{
   657  					Owner:   "kubernetes",
   658  					Repo:    "kubernetes",
   659  					Number:  101,
   660  					Comment: removeLGTMLabelNoti,
   661  				},
   662  			},
   663  			expectNoComments: false,
   664  		},
   665  		{
   666  			name: "pr_assigned",
   667  			event: github.PullRequestEvent{
   668  				Action: "assigned",
   669  			},
   670  			expectNoComments: true,
   671  		},
   672  		{
   673  			name: "pr_synchronize, with RemoveLabel github.LabelNotFound error",
   674  			event: github.PullRequestEvent{
   675  				Action: github.PullRequestActionSynchronize,
   676  				PullRequest: github.PullRequest{
   677  					Number: 101,
   678  					Base: github.PullRequestBranch{
   679  						Repo: github.Repo{
   680  							Owner: github.User{
   681  								Login: "kubernetes",
   682  							},
   683  							Name: "kubernetes",
   684  						},
   685  					},
   686  				},
   687  			},
   688  			removeLabelErr: &github.LabelNotFound{
   689  				Owner:  "kubernetes",
   690  				Repo:   "kubernetes",
   691  				Number: 101,
   692  				Label:  lgtmLabel,
   693  			},
   694  			labelsRemoved:    []string{lgtmLabel},
   695  			expectNoComments: true,
   696  		},
   697  	}
   698  
   699  	for _, c := range cases {
   700  		t.Run(c.name, func(t *testing.T) {
   701  			fakeGitHub := &githubUnlabeler{
   702  				removeLabelErr:   c.removeLabelErr,
   703  				createCommentErr: c.createCommentErr,
   704  			}
   705  			err := handlePullRequest(fakeGitHub, c.event, logrus.WithField("plugin", pluginName))
   706  
   707  			if err != nil && c.err == nil {
   708  				t.Fatalf("handlePullRequest error: %v", err)
   709  			}
   710  
   711  			if err == nil && c.err != nil {
   712  				t.Fatalf("handlePullRequest wanted error: %v, got nil", c.err)
   713  			}
   714  
   715  			if got, want := err, c.err; !equality.Semantic.DeepEqual(got, want) {
   716  				t.Fatalf("handlePullRequest error mismatch: got %v, want %v", got, want)
   717  			}
   718  
   719  			if got, want := len(fakeGitHub.labelsRemoved), len(c.labelsRemoved); got != want {
   720  				t.Logf("labelsRemoved: got %v, want: %v", fakeGitHub.labelsRemoved, c.labelsRemoved)
   721  				t.Fatalf("labelsRemoved length mismatch: got %d, want %d", got, want)
   722  			}
   723  
   724  			if got, want := fakeGitHub.issueComments, c.issueComments; !equality.Semantic.DeepEqual(got, want) {
   725  				t.Fatalf("LGTM revmoved notifications mismatch: got %v, want %v", got, want)
   726  			}
   727  			if c.expectNoComments && len(fakeGitHub.issueComments) > 0 {
   728  				t.Fatalf("expected no comments but got %v", fakeGitHub.issueComments)
   729  			}
   730  			if !c.expectNoComments && len(fakeGitHub.issueComments) == 0 {
   731  				t.Fatalf("expected comments but got none")
   732  			}
   733  		})
   734  	}
   735  }