github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/plugins/lifecycle/close_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 lifecycle
    18  
    19  import (
    20  	"errors"
    21  	"testing"
    22  
    23  	"github.com/sirupsen/logrus"
    24  
    25  	"sigs.k8s.io/prow/pkg/github"
    26  )
    27  
    28  type fakeClientClose struct {
    29  	commented      bool
    30  	closed         bool
    31  	stateReason    string
    32  	AssigneesAdded []string
    33  	labels         []string
    34  }
    35  
    36  func (c *fakeClientClose) CreateComment(owner, repo string, number int, comment string) error {
    37  	c.commented = true
    38  	return nil
    39  }
    40  
    41  func (c *fakeClientClose) CloseIssue(owner, repo string, number int) error {
    42  	c.closed = true
    43  	c.stateReason = "completed"
    44  	return nil
    45  }
    46  
    47  func (c *fakeClientClose) CloseIssueAsNotPlanned(org, repo string, number int) error {
    48  	c.closed = true
    49  	c.stateReason = "not_planned"
    50  	return nil
    51  }
    52  
    53  func (c *fakeClientClose) ClosePullRequest(owner, repo string, number int) error {
    54  	c.closed = true
    55  	return nil
    56  }
    57  
    58  func (c *fakeClientClose) IsCollaborator(owner, repo, login string) (bool, error) {
    59  	if login == "collaborator" {
    60  		return true, nil
    61  	}
    62  	return false, nil
    63  }
    64  
    65  func (c *fakeClientClose) GetIssueLabels(owner, repo string, number int) ([]github.Label, error) {
    66  	var labels []github.Label
    67  	for _, l := range c.labels {
    68  		if l == "error" {
    69  			return nil, errors.New("issue label 500")
    70  		}
    71  		labels = append(labels, github.Label{Name: l})
    72  	}
    73  	return labels, nil
    74  }
    75  
    76  func TestCloseComment(t *testing.T) {
    77  	var testcases = []struct {
    78  		name          string
    79  		action        github.GenericCommentEventAction
    80  		state         string
    81  		stateReason   string
    82  		body          string
    83  		commenter     string
    84  		labels        []string
    85  		shouldClose   bool
    86  		shouldComment bool
    87  		isPr          bool
    88  	}{
    89  		{
    90  			name:          "non-close comment",
    91  			action:        github.GenericCommentActionCreated,
    92  			state:         "open",
    93  			body:          "uh oh",
    94  			commenter:     "random-person",
    95  			shouldClose:   false,
    96  			shouldComment: false,
    97  		},
    98  		{
    99  			name:          "close by author",
   100  			action:        github.GenericCommentActionCreated,
   101  			state:         "open",
   102  			stateReason:   "completed",
   103  			body:          "/close",
   104  			commenter:     "author",
   105  			shouldClose:   true,
   106  			shouldComment: true,
   107  		},
   108  		{
   109  			name:          "close by author, trailing space.",
   110  			action:        github.GenericCommentActionCreated,
   111  			state:         "open",
   112  			stateReason:   "completed",
   113  			body:          "/close \r",
   114  			commenter:     "author",
   115  			shouldClose:   true,
   116  			shouldComment: true,
   117  		},
   118  		{
   119  			name:          "close by collaborator",
   120  			action:        github.GenericCommentActionCreated,
   121  			state:         "open",
   122  			stateReason:   "completed",
   123  			body:          "/close",
   124  			commenter:     "collaborator",
   125  			shouldClose:   true,
   126  			shouldComment: true,
   127  		},
   128  		{
   129  			name:          "close edited by author",
   130  			action:        github.GenericCommentActionEdited,
   131  			state:         "open",
   132  			body:          "/close",
   133  			commenter:     "author",
   134  			shouldClose:   false,
   135  			shouldComment: false,
   136  		},
   137  		{
   138  			name:          "close by author on closed issue",
   139  			action:        github.GenericCommentActionCreated,
   140  			state:         "closed",
   141  			body:          "/close",
   142  			commenter:     "author",
   143  			shouldClose:   false,
   144  			shouldComment: false,
   145  		},
   146  		{
   147  			name:          "close by non-collaborator on active issue, cannot close",
   148  			action:        github.GenericCommentActionCreated,
   149  			state:         "open",
   150  			body:          "/close",
   151  			commenter:     "non-collaborator",
   152  			shouldClose:   false,
   153  			shouldComment: true,
   154  		},
   155  		{
   156  			name:          "close by non-collaborator on stale issue",
   157  			action:        github.GenericCommentActionCreated,
   158  			state:         "open",
   159  			stateReason:   "completed",
   160  			body:          "/close",
   161  			commenter:     "non-collaborator",
   162  			labels:        []string{"lifecycle/stale"},
   163  			shouldClose:   true,
   164  			shouldComment: true,
   165  		},
   166  		{
   167  			name:          "close by non-collaborator on rotten issue",
   168  			action:        github.GenericCommentActionCreated,
   169  			state:         "open",
   170  			stateReason:   "completed",
   171  			body:          "/close",
   172  			commenter:     "non-collaborator",
   173  			labels:        []string{"lifecycle/rotten"},
   174  			shouldClose:   true,
   175  			shouldComment: true,
   176  		},
   177  		{
   178  			name:          "cannot close stale issue by non-collaborator when list issue fails",
   179  			action:        github.GenericCommentActionCreated,
   180  			state:         "open",
   181  			body:          "/close",
   182  			commenter:     "non-collaborator",
   183  			labels:        []string{"error"},
   184  			shouldClose:   false,
   185  			shouldComment: true,
   186  		},
   187  		{
   188  			name:          "close by author as not planned",
   189  			action:        github.GenericCommentActionCreated,
   190  			state:         "open",
   191  			stateReason:   "not_planned",
   192  			body:          "/close not-planned",
   193  			commenter:     "author",
   194  			shouldClose:   true,
   195  			shouldComment: true,
   196  		},
   197  		{
   198  			name:          "close by author as not planned, trailing space.",
   199  			action:        github.GenericCommentActionCreated,
   200  			state:         "open",
   201  			stateReason:   "not_planned",
   202  			body:          "/close not-planned \r",
   203  			commenter:     "author",
   204  			shouldClose:   true,
   205  			shouldComment: true,
   206  		},
   207  		{
   208  			name:          "close by author, multiple spaces in between.",
   209  			action:        github.GenericCommentActionCreated,
   210  			state:         "open",
   211  			stateReason:   "not_planned",
   212  			body:          "/close not-planned",
   213  			commenter:     "author",
   214  			shouldClose:   true,
   215  			shouldComment: true,
   216  		},
   217  		{
   218  			name:          "close as not planned by non-collaborator on active issue, cannot close",
   219  			action:        github.GenericCommentActionCreated,
   220  			state:         "open",
   221  			body:          "/close not-planned",
   222  			commenter:     "non-collaborator",
   223  			shouldClose:   false,
   224  			shouldComment: true,
   225  		},
   226  		{
   227  			name:          "close as not planned by non-collaborator on stale issue",
   228  			action:        github.GenericCommentActionCreated,
   229  			state:         "open",
   230  			stateReason:   "not_planned",
   231  			body:          "/close not-planned",
   232  			commenter:     "non-collaborator",
   233  			labels:        []string{"lifecycle/stale"},
   234  			shouldClose:   true,
   235  			shouldComment: true,
   236  		},
   237  		{
   238  			name:          "close as not planned by non-collaborator on rotten issue",
   239  			action:        github.GenericCommentActionCreated,
   240  			state:         "open",
   241  			stateReason:   "not_planned",
   242  			body:          "/close not-planned",
   243  			commenter:     "non-collaborator",
   244  			labels:        []string{"lifecycle/rotten"},
   245  			shouldClose:   true,
   246  			shouldComment: true,
   247  		},
   248  		{
   249  			name:          "cannot close stale issue as not planned by non-collaborator when list issue fails",
   250  			action:        github.GenericCommentActionCreated,
   251  			state:         "open",
   252  			body:          "/close not-planned",
   253  			commenter:     "non-collaborator",
   254  			labels:        []string{"error"},
   255  			shouldClose:   false,
   256  			shouldComment: true,
   257  		},
   258  		{
   259  			name:          "cannot close PR as not planned",
   260  			action:        github.GenericCommentActionCreated,
   261  			state:         "open",
   262  			body:          "/close not-planned",
   263  			commenter:     "author",
   264  			labels:        []string{"error"},
   265  			shouldClose:   false,
   266  			shouldComment: true,
   267  			isPr:          true,
   268  		},
   269  	}
   270  	for _, tc := range testcases {
   271  		fc := &fakeClientClose{labels: tc.labels}
   272  		e := &github.GenericCommentEvent{
   273  			Action:      tc.action,
   274  			IssueState:  tc.state,
   275  			Body:        tc.body,
   276  			User:        github.User{Login: tc.commenter},
   277  			Number:      5,
   278  			IssueAuthor: github.User{Login: "author"},
   279  			IsPR:        tc.isPr,
   280  		}
   281  		if err := handleClose(fc, logrus.WithField("plugin", "fake-close"), e); err != nil {
   282  			t.Errorf("For case %s, didn't expect error from handle: %v", tc.name, err)
   283  			continue
   284  		}
   285  		if tc.shouldClose && !fc.closed {
   286  			t.Errorf("For case %s, should have closed but didn't.", tc.name)
   287  		} else if !tc.shouldClose && fc.closed {
   288  			t.Errorf("For case %s, should not have closed but did.", tc.name)
   289  		}
   290  		if tc.shouldComment && !fc.commented {
   291  			t.Errorf("For case %s, should have commented but didn't.", tc.name)
   292  		} else if !tc.shouldComment && fc.commented {
   293  			t.Errorf("For case %s, should not have commented but did.", tc.name)
   294  		}
   295  		if !tc.isPr && fc.stateReason != tc.stateReason {
   296  			t.Errorf("For case %s, unexpected state_reason value, expected %s, but got %s", tc.name, tc.stateReason, fc.stateReason)
   297  		}
   298  	}
   299  }