github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/plugins/assign/assign_test.go (about) 1 /* 2 Copyright 2017 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 assign 18 19 import ( 20 "sort" 21 "testing" 22 23 "github.com/sirupsen/logrus" 24 25 "k8s.io/test-infra/prow/github" 26 ) 27 28 type fakeClient struct { 29 assigned map[string]int 30 unassigned map[string]int 31 32 requested map[string]int 33 unrequested map[string]int 34 contributors map[string]bool 35 36 commented bool 37 } 38 39 func (c *fakeClient) UnassignIssue(owner, repo string, number int, assignees []string) error { 40 for _, who := range assignees { 41 c.unassigned[who]++ 42 } 43 44 return nil 45 } 46 47 func (c *fakeClient) AssignIssue(owner, repo string, number int, assignees []string) error { 48 var missing github.MissingUsers 49 sort.Strings(assignees) 50 if len(assignees) > 10 { 51 for _, who := range assignees[10:] { 52 missing.Users = append(missing.Users, who) 53 } 54 for _, who := range assignees[:10] { 55 c.assigned[who]++ 56 } 57 } else { 58 for _, who := range assignees { 59 if who != "evil" { 60 c.assigned[who]++ 61 } else { 62 missing.Users = append(missing.Users, who) 63 } 64 } 65 } 66 67 if len(missing.Users) == 0 { 68 return nil 69 } 70 return missing 71 } 72 73 func (c *fakeClient) RequestReview(org, repo string, number int, logins []string) error { 74 var missing github.MissingUsers 75 for _, user := range logins { 76 if c.contributors[user] { 77 c.requested[user]++ 78 } else { 79 missing.Users = append(missing.Users, user) 80 } 81 } 82 if len(missing.Users) > 0 { 83 return missing 84 } 85 return nil 86 } 87 88 func (c *fakeClient) UnrequestReview(org, repo string, number int, logins []string) error { 89 for _, user := range logins { 90 c.unrequested[user]++ 91 } 92 return nil 93 } 94 95 func (c *fakeClient) CreateComment(owner, repo string, number int, comment string) error { 96 c.commented = comment != "" 97 return nil 98 } 99 100 func newFakeClient(contribs []string) *fakeClient { 101 c := &fakeClient{ 102 contributors: make(map[string]bool), 103 requested: make(map[string]int), 104 unrequested: make(map[string]int), 105 assigned: make(map[string]int), 106 unassigned: make(map[string]int), 107 } 108 for _, user := range contribs { 109 c.contributors[user] = true 110 } 111 return c 112 } 113 114 func TestParseLogins(t *testing.T) { 115 var testcases = []struct { 116 name string 117 text string 118 logins []string 119 }{ 120 { 121 name: "empty", 122 text: "", 123 }, 124 { 125 name: "one", 126 text: " @jungle", 127 logins: []string{"jungle"}, 128 }, 129 { 130 name: "two", 131 text: " @erick @fejta", 132 logins: []string{"erick", "fejta"}, 133 }, 134 { 135 name: "one team", 136 text: " @kubernetes/sig-testing-misc", 137 logins: []string{"kubernetes/sig-testing-misc"}, 138 }, 139 { 140 name: "two teams", 141 text: " @kubernetes/sig-testing-misc @kubernetes/sig-testing-bugs", 142 logins: []string{"kubernetes/sig-testing-misc", "kubernetes/sig-testing-bugs"}, 143 }, 144 } 145 for _, tc := range testcases { 146 l := parseLogins(tc.text) 147 if len(l) != len(tc.logins) { 148 t.Errorf("For case %s, expected %s and got %s", tc.name, tc.logins, l) 149 } 150 for n, who := range l { 151 if tc.logins[n] != who { 152 t.Errorf("For case %s, expected %s and got %s", tc.name, tc.logins, l) 153 } 154 } 155 } 156 } 157 158 // TestAssignAndReview tests that the handle function uses the github client 159 // to correctly create and/or delete assignments and PR review requests. 160 func TestAssignAndReview(t *testing.T) { 161 var testcases = []struct { 162 name string 163 body string 164 commenter string 165 assigned []string 166 unassigned []string 167 requested []string 168 unrequested []string 169 commented bool 170 }{ 171 { 172 name: "unrelated comment", 173 body: "uh oh", 174 commenter: "o", 175 }, 176 { 177 name: "assign on open", 178 body: "/assign", 179 commenter: "rando", 180 assigned: []string{"rando"}, 181 }, 182 { 183 name: "assign me", 184 body: "/assign", 185 commenter: "rando", 186 assigned: []string{"rando"}, 187 }, 188 { 189 name: "unassign myself", 190 body: "/unassign", 191 commenter: "rando", 192 unassigned: []string{"rando"}, 193 }, 194 { 195 name: "tab completion", 196 body: "/assign @fejta ", 197 commenter: "rando", 198 assigned: []string{"fejta"}, 199 }, 200 { 201 name: "no @ works too", 202 body: "/assign fejta", 203 commenter: "rando", 204 assigned: []string{"fejta"}, 205 }, 206 { 207 name: "multi commands", 208 body: "/assign @fejta\n/unassign @spxtr", 209 commenter: "rando", 210 assigned: []string{"fejta"}, 211 unassigned: []string{"spxtr"}, 212 }, 213 { 214 name: "interesting names", 215 body: "/assign @hello-world @allow_underscore", 216 commenter: "rando", 217 assigned: []string{"hello-world", "allow_underscore"}, 218 }, 219 { 220 name: "bad login", 221 commenter: "rando", 222 body: "/assign @Invalid$User", 223 }, 224 { 225 name: "bad login, no @", 226 commenter: "rando", 227 body: "/assign Invalid$User", 228 }, 229 { 230 name: "assign friends", 231 body: "/assign @bert @ernie", 232 commenter: "rando", 233 assigned: []string{"bert", "ernie"}, 234 }, 235 { 236 name: "assign greater than 10 users", 237 body: "/assign @user1 @user2 @user3 @user4 @user5 @user6 @user7 @user8 @user9 @user10 @user11 @user12 @user13", 238 commenter: "rando", 239 commented: true, 240 assigned: []string{"user12", "user13", "user6", "user1", "user11", "user2", "user3", "user4", "user5", "user10"}, 241 }, 242 { 243 name: "unassign buddies", 244 body: "/unassign @ashitaka @eboshi", 245 commenter: "san", 246 unassigned: []string{"ashitaka", "eboshi"}, 247 }, 248 { 249 name: "unassign buddies, trailing space.", 250 body: "/unassign @ashitaka @eboshi \r", 251 commenter: "san", 252 unassigned: []string{"ashitaka", "eboshi"}, 253 }, 254 { 255 name: "evil commenter", 256 body: "/assign @merlin", 257 commenter: "evil", 258 assigned: []string{"merlin"}, 259 }, 260 { 261 name: "evil commenter self assign", 262 body: "/assign", 263 commenter: "evil", 264 commented: true, 265 }, 266 { 267 name: "evil assignee", 268 body: "/assign @evil @evil @evil @evil @merlin", 269 commenter: "innocent", 270 assigned: []string{"merlin"}, 271 commented: true, 272 }, 273 { 274 name: "evil unassignee", 275 body: "/unassign @evil @merlin", 276 commenter: "innocent", 277 unassigned: []string{"evil", "merlin"}, 278 }, 279 { 280 name: "review on open", 281 body: "/cc @merlin", 282 commenter: "rando", 283 requested: []string{"merlin"}, 284 }, 285 { 286 name: "tab completion", 287 body: "/cc @cjwagner ", 288 commenter: "rando", 289 requested: []string{"cjwagner"}, 290 }, 291 { 292 name: "no @ works too", 293 body: "/cc cjwagner ", 294 commenter: "rando", 295 requested: []string{"cjwagner"}, 296 }, 297 { 298 name: "multi commands", 299 body: "/cc @cjwagner\n/uncc @spxtr", 300 commenter: "rando", 301 requested: []string{"cjwagner"}, 302 unrequested: []string{"spxtr"}, 303 }, 304 { 305 name: "interesting names", 306 body: "/cc @hello-world @allow_underscore", 307 commenter: "rando", 308 requested: []string{"hello-world", "allow_underscore"}, 309 }, 310 { 311 name: "bad login", 312 commenter: "rando", 313 body: "/cc @Invalid$User", 314 }, 315 { 316 name: "bad login", 317 commenter: "rando", 318 body: "/cc Invalid$User", 319 }, 320 { 321 name: "request multiple", 322 body: "/cc @cjwagner @merlin", 323 commenter: "rando", 324 requested: []string{"cjwagner", "merlin"}, 325 }, 326 { 327 name: "unrequest buddies", 328 body: "/uncc @ashitaka @eboshi", 329 commenter: "san", 330 unrequested: []string{"ashitaka", "eboshi"}, 331 }, 332 { 333 name: "evil commenter", 334 body: "/cc @merlin", 335 commenter: "evil", 336 requested: []string{"merlin"}, 337 }, 338 { 339 name: "evil reviewer requested", 340 body: "/cc @evil @merlin", 341 commenter: "innocent", 342 requested: []string{"merlin"}, 343 commented: true, 344 }, 345 { 346 name: "evil reviewer unrequested", 347 body: "/uncc @evil @merlin", 348 commenter: "innocent", 349 unrequested: []string{"evil", "merlin"}, 350 }, 351 { 352 name: "multi command types", 353 body: "/assign @fejta\n/unassign @spxtr @cjwagner\n/uncc @merlin \n/cc @cjwagner", 354 commenter: "rando", 355 assigned: []string{"fejta"}, 356 unassigned: []string{"spxtr", "cjwagner"}, 357 requested: []string{"cjwagner"}, 358 unrequested: []string{"merlin"}, 359 }, 360 { 361 name: "request review self", 362 body: "/cc", 363 commenter: "cjwagner", 364 requested: []string{"cjwagner"}, 365 }, 366 { 367 name: "unrequest review self", 368 body: "/uncc", 369 commenter: "cjwagner", 370 unrequested: []string{"cjwagner"}, 371 }, 372 { 373 name: "request review self, with unrequest friend, with trailing space.", 374 body: "/cc \n/uncc @spxtr ", 375 commenter: "cjwagner", 376 requested: []string{"cjwagner"}, 377 unrequested: []string{"spxtr"}, 378 }, 379 { 380 name: "request team review", 381 body: "/cc @kubernetes/sig-testing-misc", 382 commenter: "rando", 383 requested: []string{"kubernetes/sig-testing-misc"}, 384 }, 385 { 386 name: "unrequest team review", 387 body: "/uncc @kubernetes/sig-testing-misc", 388 commenter: "rando", 389 unrequested: []string{"kubernetes/sig-testing-misc"}, 390 }, 391 } 392 for _, tc := range testcases { 393 fc := newFakeClient([]string{"hello-world", "allow_underscore", "cjwagner", "merlin", "kubernetes/sig-testing-misc"}) 394 e := github.GenericCommentEvent{ 395 Body: tc.body, 396 User: github.User{Login: tc.commenter}, 397 Repo: github.Repo{Name: "repo", Owner: github.User{Login: "org"}}, 398 Number: 5, 399 } 400 if err := handle(newAssignHandler(e, fc, logrus.WithField("plugin", pluginName))); err != nil { 401 t.Errorf("For case %s, didn't expect error from handle: %v", tc.name, err) 402 continue 403 } 404 if err := handle(newReviewHandler(e, fc, logrus.WithField("plugin", pluginName))); err != nil { 405 t.Errorf("For case %s, didn't expect error from handle: %v", tc.name, err) 406 continue 407 } 408 409 if tc.commented != fc.commented { 410 t.Errorf("For case %s, expect commented: %v, got commented %v", tc.name, tc.commented, fc.commented) 411 } 412 413 if len(fc.assigned) != len(tc.assigned) { 414 t.Errorf("For case %s, assigned actual %v != expected %s", tc.name, fc.assigned, tc.assigned) 415 } else { 416 for _, who := range tc.assigned { 417 if n, ok := fc.assigned[who]; !ok || n < 1 { 418 t.Errorf("For case %s, assigned actual %v != expected %s", tc.name, fc.assigned, tc.assigned) 419 break 420 } 421 } 422 } 423 if len(fc.unassigned) != len(tc.unassigned) { 424 t.Errorf("For case %s, unassigned %v != %s", tc.name, fc.unassigned, tc.unassigned) 425 } else { 426 for _, who := range tc.unassigned { 427 if n, ok := fc.unassigned[who]; !ok || n < 1 { 428 t.Errorf("For case %s, unassigned %v != %s", tc.name, fc.unassigned, tc.unassigned) 429 break 430 } 431 } 432 } 433 434 if len(fc.requested) != len(tc.requested) { 435 t.Errorf("For case %s, requested actual %v != expected %s", tc.name, fc.requested, tc.requested) 436 } else { 437 for _, who := range tc.requested { 438 if n, ok := fc.requested[who]; !ok || n < 1 { 439 t.Errorf("For case %s, requested actual %v != expected %s", tc.name, fc.requested, tc.requested) 440 break 441 } 442 } 443 } 444 if len(fc.unrequested) != len(tc.unrequested) { 445 t.Errorf("For case %s, unrequested %v != %s", tc.name, fc.unrequested, tc.unrequested) 446 } else { 447 for _, who := range tc.unrequested { 448 if n, ok := fc.unrequested[who]; !ok || n < 1 { 449 t.Errorf("For case %s, unrequested %v != %s", tc.name, fc.unrequested, tc.unrequested) 450 break 451 } 452 } 453 } 454 } 455 }