github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/plugins/dco/dco_test.go (about) 1 /* 2 Copyright 2018 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 dco 18 19 import ( 20 "fmt" 21 "reflect" 22 "testing" 23 24 "github.com/sirupsen/logrus" 25 26 "k8s.io/test-infra/prow/github" 27 "k8s.io/test-infra/prow/github/fakegithub" 28 ) 29 30 type fakePruner struct{} 31 32 func (fp *fakePruner) PruneComments(shouldPrune func(github.IssueComment) bool) {} 33 34 func strP(str string) *string { 35 return &str 36 } 37 38 func TestHandlePullRequest(t *testing.T) { 39 var testcases = []struct { 40 // test settings 41 name string 42 43 // PR settings 44 pullRequestEvent github.PullRequestEvent 45 commits []github.RepositoryCommit 46 issueState string 47 hasDCOYes bool 48 hasDCONo bool 49 // status of the DCO github context 50 status string 51 52 // expectations 53 addedLabel string 54 removedLabel string 55 expectedStatus string 56 // org/repo#number:body 57 addedComment string 58 // org/repo#issuecommentid 59 removedComment string 60 }{ 61 { 62 name: "should not do anything on pull request edited", 63 pullRequestEvent: github.PullRequestEvent{ 64 Action: github.PullRequestActionEdited, 65 PullRequest: github.PullRequest{Number: 3, Head: github.PullRequestBranch{SHA: "sha"}}, 66 }, 67 }, 68 { 69 name: "should add 'no' label & status context and add a comment if no commits have sign off", 70 pullRequestEvent: github.PullRequestEvent{ 71 Action: github.PullRequestActionOpened, 72 PullRequest: github.PullRequest{Number: 3, Head: github.PullRequestBranch{SHA: "sha"}}, 73 }, 74 commits: []github.RepositoryCommit{ 75 {SHA: "sha", Commit: github.GitCommit{Message: "not a sign off"}}, 76 }, 77 issueState: "open", 78 hasDCONo: false, 79 hasDCOYes: false, 80 81 addedLabel: fmt.Sprintf("/#3:%s", dcoNoLabel), 82 expectedStatus: github.StatusFailure, 83 addedComment: `/#3:Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits. 84 85 :memo: **Please follow instructions in the [contributing guide](https://github.com///blob/master/CONTRIBUTING.md) to update your commits with the DCO** 86 87 Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/). 88 89 **The list of commits missing DCO signoff**: 90 91 - [sha](https://github.com///commits/sha) not a sign off 92 93 <details> 94 95 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/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands). 96 </details> 97 `, 98 }, 99 { 100 name: "should add 'no' label & status context, remove old labels and add a comment if no commits have sign off", 101 pullRequestEvent: github.PullRequestEvent{ 102 Action: github.PullRequestActionOpened, 103 PullRequest: github.PullRequest{Number: 3, Head: github.PullRequestBranch{SHA: "sha"}}, 104 }, 105 commits: []github.RepositoryCommit{ 106 {SHA: "sha", Commit: github.GitCommit{Message: "not a sign off"}}, 107 }, 108 issueState: "open", 109 hasDCONo: false, 110 hasDCOYes: true, 111 112 addedLabel: fmt.Sprintf("/#3:%s", dcoNoLabel), 113 removedLabel: fmt.Sprintf("/#3:%s", dcoYesLabel), 114 expectedStatus: github.StatusFailure, 115 addedComment: `/#3:Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits. 116 117 :memo: **Please follow instructions in the [contributing guide](https://github.com///blob/master/CONTRIBUTING.md) to update your commits with the DCO** 118 119 Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/). 120 121 **The list of commits missing DCO signoff**: 122 123 - [sha](https://github.com///commits/sha) not a sign off 124 125 <details> 126 127 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/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands). 128 </details> 129 `, 130 }, 131 { 132 name: "should update comment if labels and status are up to date and sign off is failing", 133 pullRequestEvent: github.PullRequestEvent{ 134 Action: github.PullRequestActionOpened, 135 PullRequest: github.PullRequest{Number: 3, Head: github.PullRequestBranch{SHA: "sha"}}, 136 }, 137 commits: []github.RepositoryCommit{ 138 {SHA: "sha", Commit: github.GitCommit{Message: "not a sign off"}}, 139 }, 140 issueState: "open", 141 hasDCONo: true, 142 hasDCOYes: false, 143 status: github.StatusFailure, 144 145 expectedStatus: github.StatusFailure, 146 addedComment: `/#3:Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits. 147 148 :memo: **Please follow instructions in the [contributing guide](https://github.com///blob/master/CONTRIBUTING.md) to update your commits with the DCO** 149 150 Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/). 151 152 **The list of commits missing DCO signoff**: 153 154 - [sha](https://github.com///commits/sha) not a sign off 155 156 <details> 157 158 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/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands). 159 </details> 160 `, 161 }, 162 { 163 name: "should mark the PR as failed if just one commit is missing sign-off", 164 pullRequestEvent: github.PullRequestEvent{ 165 Action: github.PullRequestActionOpened, 166 PullRequest: github.PullRequest{Number: 3, Head: github.PullRequestBranch{SHA: "sha"}}, 167 }, 168 commits: []github.RepositoryCommit{ 169 {SHA: "sha1", Commit: github.GitCommit{Message: "Signed-off-by: someone"}}, 170 {SHA: "sha", Commit: github.GitCommit{Message: "not signed off"}}, 171 }, 172 issueState: "open", 173 hasDCONo: false, 174 hasDCOYes: true, 175 176 addedLabel: fmt.Sprintf("/#3:%s", dcoNoLabel), 177 removedLabel: fmt.Sprintf("/#3:%s", dcoYesLabel), 178 expectedStatus: github.StatusFailure, 179 addedComment: `/#3:Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits. 180 181 :memo: **Please follow instructions in the [contributing guide](https://github.com///blob/master/CONTRIBUTING.md) to update your commits with the DCO** 182 183 Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/). 184 185 **The list of commits missing DCO signoff**: 186 187 - [sha](https://github.com///commits/sha) not signed off 188 189 <details> 190 191 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/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands). 192 </details> 193 `, 194 }, 195 { 196 name: "should add label and update status context if all commits are signed-off", 197 pullRequestEvent: github.PullRequestEvent{ 198 Action: github.PullRequestActionOpened, 199 PullRequest: github.PullRequest{Number: 3, Head: github.PullRequestBranch{SHA: "sha"}}, 200 }, 201 commits: []github.RepositoryCommit{ 202 {SHA: "sha", Commit: github.GitCommit{Message: "Signed-off-by: someone"}}, 203 }, 204 issueState: "open", 205 hasDCONo: false, 206 hasDCOYes: false, 207 208 addedLabel: fmt.Sprintf("/#3:%s", dcoYesLabel), 209 expectedStatus: github.StatusSuccess, 210 }, 211 { 212 name: "should add label and update status context and remove old labels if all commits are signed-off", 213 pullRequestEvent: github.PullRequestEvent{ 214 Action: github.PullRequestActionOpened, 215 PullRequest: github.PullRequest{Number: 3, Head: github.PullRequestBranch{SHA: "sha"}}, 216 }, 217 commits: []github.RepositoryCommit{ 218 {SHA: "sha", Commit: github.GitCommit{Message: "Signed-off-by: someone"}}, 219 }, 220 issueState: "open", 221 hasDCONo: true, 222 hasDCOYes: false, 223 224 addedLabel: fmt.Sprintf("/#3:%s", dcoYesLabel), 225 removedLabel: fmt.Sprintf("/#3:%s", dcoNoLabel), 226 expectedStatus: github.StatusSuccess, 227 }, 228 } 229 for _, tc := range testcases { 230 t.Run(tc.name, func(t *testing.T) { 231 fc := &fakegithub.FakeClient{ 232 CreatedStatuses: make(map[string][]github.Status), 233 PullRequests: map[int]*github.PullRequest{tc.pullRequestEvent.PullRequest.Number: &tc.pullRequestEvent.PullRequest}, 234 IssueComments: make(map[int][]github.IssueComment), 235 CommitMap: map[string][]github.RepositoryCommit{ 236 "/#3": tc.commits, 237 }, 238 } 239 if tc.hasDCOYes { 240 fc.IssueLabelsAdded = append(fc.IssueLabelsAdded, fmt.Sprintf("/#3:%s", dcoYesLabel)) 241 } 242 if tc.hasDCONo { 243 fc.IssueLabelsAdded = append(fc.IssueLabelsAdded, fmt.Sprintf("/#3:%s", dcoNoLabel)) 244 } 245 if tc.status != "" { 246 fc.CreatedStatuses["sha"] = []github.Status{ 247 {Context: dcoContextName, State: tc.status}, 248 } 249 } 250 if err := handlePullRequest(fc, &fakePruner{}, logrus.WithField("plugin", pluginName), tc.pullRequestEvent); err != nil { 251 t.Errorf("For case %s, didn't expect error from dco plugin: %v", tc.name, err) 252 } 253 ok := tc.addedLabel == "" 254 if !ok { 255 for _, label := range fc.IssueLabelsAdded { 256 if reflect.DeepEqual(tc.addedLabel, label) { 257 ok = true 258 break 259 } 260 } 261 } 262 if !ok { 263 t.Errorf("Expected to add: %#v, Got %#v in case %s.", tc.addedLabel, fc.IssueLabelsAdded, tc.name) 264 } 265 ok = tc.removedLabel == "" 266 if !ok { 267 for _, label := range fc.IssueLabelsRemoved { 268 if reflect.DeepEqual(tc.removedLabel, label) { 269 ok = true 270 break 271 } 272 } 273 } 274 if !ok { 275 t.Errorf("Expected to remove: %#v, Got %#v in case %s.", tc.removedLabel, fc.IssueLabelsRemoved, tc.name) 276 } 277 278 // check status is set as expected 279 statuses := fc.CreatedStatuses["sha"] 280 if len(statuses) == 0 && tc.expectedStatus != "" { 281 t.Errorf("Expected dco status to be %q, but it was not set", tc.expectedStatus) 282 } 283 found := false 284 for _, s := range statuses { 285 if s.Context == dcoContextName { 286 found = true 287 if s.State != tc.expectedStatus { 288 t.Errorf("Expected dco status to be %q but it was %q", tc.expectedStatus, s.State) 289 } 290 } 291 } 292 if !found && tc.expectedStatus != "" { 293 t.Errorf("Expect dco status to be %q, but it was not found", tc.expectedStatus) 294 } 295 296 comments := fc.IssueCommentsAdded 297 if len(comments) == 0 && tc.addedComment != "" { 298 t.Errorf("Expected comment with body %q to be added, but it was not", tc.addedComment) 299 return 300 } 301 if len(comments) > 1 { 302 t.Errorf("did not expect more than one comment to be created") 303 } 304 if len(comments) != 0 && comments[0] != tc.addedComment { 305 t.Errorf("expected comment to be %q but it was %q", tc.addedComment, comments[0]) 306 } 307 }) 308 } 309 } 310 func TestHandleComment(t *testing.T) { 311 var testcases = []struct { 312 // test settings 313 name string 314 315 // PR settings 316 commentEvent github.GenericCommentEvent 317 pullRequests map[int]*github.PullRequest 318 commits []github.RepositoryCommit 319 issueState string 320 hasDCOYes bool 321 hasDCONo bool 322 // status of the DCO github context 323 status string 324 325 // expectations 326 addedLabel string 327 removedLabel string 328 expectedStatus string 329 // org/repo#number:body 330 addedComment string 331 // org/repo#issuecommentid 332 removedComment string 333 }{ 334 { 335 name: "should not do anything if comment does not match /check-dco", 336 commentEvent: github.GenericCommentEvent{ 337 IssueState: "open", 338 Action: github.GenericCommentActionCreated, 339 Body: "not-the-trigger", 340 IsPR: true, 341 Number: 3, 342 }, 343 pullRequests: map[int]*github.PullRequest{ 344 3: {Number: 3, Head: github.PullRequestBranch{SHA: "sha"}}, 345 }, 346 }, 347 { 348 name: "should add 'no' label & status context and add a comment if no commits have sign off", 349 commentEvent: github.GenericCommentEvent{ 350 IssueState: "open", 351 Action: github.GenericCommentActionCreated, 352 Body: "/check-dco", 353 IsPR: true, 354 Number: 3, 355 }, 356 pullRequests: map[int]*github.PullRequest{ 357 3: {Number: 3, Head: github.PullRequestBranch{SHA: "sha"}}, 358 }, 359 commits: []github.RepositoryCommit{ 360 {SHA: "sha", Commit: github.GitCommit{Message: "not a sign off"}}, 361 }, 362 issueState: "open", 363 hasDCONo: false, 364 hasDCOYes: false, 365 366 addedLabel: fmt.Sprintf("/#3:%s", dcoNoLabel), 367 expectedStatus: github.StatusFailure, 368 addedComment: `/#3:Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits. 369 370 :memo: **Please follow instructions in the [contributing guide](https://github.com///blob/master/CONTRIBUTING.md) to update your commits with the DCO** 371 372 Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/). 373 374 **The list of commits missing DCO signoff**: 375 376 - [sha](https://github.com///commits/sha) not a sign off 377 378 <details> 379 380 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/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands). 381 </details> 382 `, 383 }, 384 } 385 for _, tc := range testcases { 386 t.Run(tc.name, func(t *testing.T) { 387 fc := &fakegithub.FakeClient{ 388 CreatedStatuses: make(map[string][]github.Status), 389 PullRequests: tc.pullRequests, 390 IssueComments: make(map[int][]github.IssueComment), 391 CommitMap: map[string][]github.RepositoryCommit{ 392 "/#3": tc.commits, 393 }, 394 } 395 if tc.hasDCOYes { 396 fc.IssueLabelsAdded = append(fc.IssueLabelsAdded, fmt.Sprintf("/#3:%s", dcoYesLabel)) 397 } 398 if tc.hasDCONo { 399 fc.IssueLabelsAdded = append(fc.IssueLabelsAdded, fmt.Sprintf("/#3:%s", dcoNoLabel)) 400 } 401 if tc.status != "" { 402 fc.CreatedStatuses["sha"] = []github.Status{ 403 {Context: dcoContextName, State: tc.status}, 404 } 405 } 406 if err := handleComment(fc, &fakePruner{}, logrus.WithField("plugin", pluginName), tc.commentEvent); err != nil { 407 t.Errorf("For case %s, didn't expect error from dco plugin: %v", tc.name, err) 408 } 409 ok := tc.addedLabel == "" 410 if !ok { 411 for _, label := range fc.IssueLabelsAdded { 412 if reflect.DeepEqual(tc.addedLabel, label) { 413 ok = true 414 break 415 } 416 } 417 } 418 if !ok { 419 t.Errorf("Expected to add: %#v, Got %#v in case %s.", tc.addedLabel, fc.IssueLabelsAdded, tc.name) 420 } 421 ok = tc.removedLabel == "" 422 if !ok { 423 for _, label := range fc.IssueLabelsRemoved { 424 if reflect.DeepEqual(tc.removedLabel, label) { 425 ok = true 426 break 427 } 428 } 429 } 430 if !ok { 431 t.Errorf("Expected to remove: %#v, Got %#v in case %s.", tc.removedLabel, fc.IssueLabelsRemoved, tc.name) 432 } 433 434 // check status is set as expected 435 statuses := fc.CreatedStatuses["sha"] 436 if len(statuses) == 0 && tc.expectedStatus != "" { 437 t.Errorf("Expected dco status to be %q, but it was not set", tc.expectedStatus) 438 } 439 found := false 440 for _, s := range statuses { 441 if s.Context == dcoContextName { 442 found = true 443 if s.State != tc.expectedStatus { 444 t.Errorf("Expected dco status to be %q but it was %q", tc.expectedStatus, s.State) 445 } 446 } 447 } 448 if !found && tc.expectedStatus != "" { 449 t.Errorf("Expect dco status to be %q, but it was not found", tc.expectedStatus) 450 } 451 452 comments := fc.IssueCommentsAdded 453 if len(comments) == 0 && tc.addedComment != "" { 454 t.Errorf("Expected comment with body %q to be added, but it was not", tc.addedComment) 455 return 456 } 457 if len(comments) > 1 { 458 t.Errorf("did not expect more than one comment to be created") 459 } 460 if len(comments) != 0 && comments[0] != tc.addedComment { 461 t.Errorf("expected comment to be %q but it was %q", tc.addedComment, comments[0]) 462 } 463 }) 464 } 465 }