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 }