github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/plugins/releasenote/releasenote_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 releasenote 18 19 import ( 20 "fmt" 21 "reflect" 22 "sort" 23 "strings" 24 "testing" 25 26 "github.com/sirupsen/logrus" 27 28 "k8s.io/apimachinery/pkg/util/sets" 29 "sigs.k8s.io/prow/pkg/github" 30 "sigs.k8s.io/prow/pkg/github/fakegithub" 31 "sigs.k8s.io/prow/pkg/labels" 32 ) 33 34 func TestReleaseNoteComment(t *testing.T) { 35 var testcases = []struct { 36 name string 37 action github.IssueCommentEventAction 38 commentBody string 39 issueBody string 40 isMember bool 41 isAuthor bool 42 currentLabels []string 43 44 deletedLabels []string 45 addedLabel string 46 shouldComment bool 47 }{ 48 { 49 name: "unrelated comment", 50 action: github.IssueCommentActionCreated, 51 commentBody: "oh dear", 52 currentLabels: []string{labels.ReleaseNoteLabelNeeded, "other"}, 53 }, 54 { 55 name: "author release-note-none with missing block", 56 action: github.IssueCommentActionCreated, 57 isAuthor: true, 58 commentBody: "/release-note-none", 59 currentLabels: []string{labels.ReleaseNoteLabelNeeded, "other"}, 60 61 deletedLabels: []string{labels.ReleaseNoteLabelNeeded}, 62 addedLabel: labels.ReleaseNoteNone, 63 }, 64 { 65 name: "author release-note-none with empty block", 66 action: github.IssueCommentActionCreated, 67 isAuthor: true, 68 commentBody: "/release-note-none", 69 issueBody: "bologna ```release-note \n ```", 70 currentLabels: []string{labels.ReleaseNoteLabelNeeded, "other"}, 71 72 deletedLabels: []string{labels.ReleaseNoteLabelNeeded}, 73 addedLabel: labels.ReleaseNoteNone, 74 }, 75 { 76 name: "author release-note-none with \"none\" block", 77 action: github.IssueCommentActionCreated, 78 isAuthor: true, 79 commentBody: "/release-note-none", 80 issueBody: "bologna ```release-note \nnone \n ```", 81 currentLabels: []string{labels.ReleaseNoteLabelNeeded, "other"}, 82 83 deletedLabels: []string{labels.ReleaseNoteLabelNeeded}, 84 addedLabel: labels.ReleaseNoteNone, 85 }, 86 { 87 name: "author release-note-none with \"no\" block", 88 action: github.IssueCommentActionCreated, 89 isAuthor: true, 90 commentBody: "/release-note-none", 91 issueBody: "bologna ```release-note \nno \n ```", 92 currentLabels: []string{labels.ReleaseNoteLabelNeeded, "other"}, 93 94 deletedLabels: []string{labels.ReleaseNoteLabelNeeded}, 95 addedLabel: labels.ReleaseNoteNone, 96 }, 97 { 98 name: "author release-note-none, trailing space.", 99 action: github.IssueCommentActionCreated, 100 isAuthor: true, 101 commentBody: "/release-note-none ", 102 currentLabels: []string{labels.ReleaseNoteLabelNeeded, "other"}, 103 104 deletedLabels: []string{labels.ReleaseNoteLabelNeeded}, 105 addedLabel: labels.ReleaseNoteNone, 106 }, 107 { 108 name: "author release-note-none, no op.", 109 action: github.IssueCommentActionCreated, 110 isAuthor: true, 111 commentBody: "/release-note-none", 112 currentLabels: []string{labels.ReleaseNoteNone, "other"}, 113 }, 114 { 115 name: "member release-note", 116 action: github.IssueCommentActionCreated, 117 isMember: true, 118 commentBody: "/release-note", 119 currentLabels: []string{labels.ReleaseNoteLabelNeeded, "other"}, 120 121 shouldComment: true, 122 }, 123 { 124 name: "someone else release-note, trailing space.", 125 action: github.IssueCommentActionCreated, 126 commentBody: "/release-note \r", 127 currentLabels: []string{labels.ReleaseNoteLabelNeeded, "other"}, 128 shouldComment: true, 129 }, 130 { 131 name: "someone else release-note-none", 132 action: github.IssueCommentActionCreated, 133 commentBody: "/release-note-none", 134 currentLabels: []string{labels.ReleaseNoteLabelNeeded, "other"}, 135 shouldComment: true, 136 }, 137 { 138 name: "author release-note-action-required", 139 action: github.IssueCommentActionCreated, 140 isAuthor: true, 141 commentBody: "/release-note-action-required", 142 currentLabels: []string{labels.ReleaseNoteLabelNeeded, "other"}, 143 shouldComment: true, 144 }, 145 { 146 name: "release-note-none, delete multiple labels", 147 action: github.IssueCommentActionCreated, 148 isMember: true, 149 commentBody: "/release-note-none", 150 currentLabels: []string{labels.ReleaseNote, labels.ReleaseNoteLabelNeeded, labels.ReleaseNoteActionRequired, labels.ReleaseNoteNone, "other"}, 151 152 deletedLabels: []string{labels.ReleaseNoteLabelNeeded, labels.ReleaseNoteActionRequired, labels.ReleaseNote}, 153 }, 154 { 155 name: "no label present", 156 action: github.IssueCommentActionCreated, 157 isMember: true, 158 commentBody: "/release-note-none", 159 160 addedLabel: labels.ReleaseNoteNone, 161 }, 162 { 163 name: "member release-note-none, PR has kind/deprecation label", 164 action: github.IssueCommentActionCreated, 165 isMember: true, 166 commentBody: "/release-note-none", 167 currentLabels: []string{labels.DeprecationLabel}, 168 shouldComment: true, 169 }, 170 } 171 for _, tc := range testcases { 172 fc := fakegithub.NewFakeClient() 173 fc.IssueComments = make(map[int][]github.IssueComment) 174 fc.OrgMembers = map[string][]string{"": {"m"}} 175 ice := github.IssueCommentEvent{ 176 Action: tc.action, 177 Comment: github.IssueComment{ 178 Body: tc.commentBody, 179 }, 180 Issue: github.Issue{ 181 Body: tc.issueBody, 182 User: github.User{Login: "a"}, 183 Number: 5, 184 State: "open", 185 PullRequest: &struct{}{}, 186 Assignees: []github.User{{Login: "r"}}, 187 }, 188 } 189 if tc.isAuthor { 190 ice.Comment.User.Login = "a" 191 } else if tc.isMember { 192 ice.Comment.User.Login = "m" 193 } 194 for _, l := range tc.currentLabels { 195 ice.Issue.Labels = append(ice.Issue.Labels, github.Label{Name: l}) 196 } 197 if err := handleComment(fc, logrus.WithField("plugin", PluginName), ice); err != nil { 198 t.Errorf("For case %s, did not expect error: %v", tc.name, err) 199 } 200 if tc.shouldComment && len(fc.IssueComments[5]) == 0 { 201 t.Errorf("For case %s, didn't comment but should have.", tc.name) 202 } 203 if len(fc.IssueLabelsAdded) > 1 { 204 t.Errorf("For case %s, added more than one label: %v", tc.name, fc.IssueLabelsAdded) 205 } else if len(fc.IssueLabelsAdded) == 0 && tc.addedLabel != "" { 206 t.Errorf("For case %s, should have added %s but didn't.", tc.name, tc.addedLabel) 207 } else if len(fc.IssueLabelsAdded) == 1 && fc.IssueLabelsAdded[0] != "/#5:"+tc.addedLabel { 208 t.Errorf("For case %s, added wrong label. Got %s, expected %s", tc.name, fc.IssueLabelsAdded[0], tc.addedLabel) 209 } 210 211 var expectedDeleted []string 212 for _, expect := range tc.deletedLabels { 213 expectedDeleted = append(expectedDeleted, "/#5:"+expect) 214 } 215 sort.Strings(expectedDeleted) 216 sort.Strings(fc.IssueLabelsRemoved) 217 if !reflect.DeepEqual(expectedDeleted, fc.IssueLabelsRemoved) { 218 t.Errorf( 219 "For case %s, expected %q labels to be deleted, but %q were deleted.", 220 tc.name, 221 expectedDeleted, 222 fc.IssueLabelsRemoved, 223 ) 224 } 225 } 226 } 227 228 const lgtmLabel = labels.LGTM 229 230 func formatLabels(num int, labels ...string) []string { 231 out := make([]string, 0, len(labels)) 232 for _, l := range labels { 233 out = append(out, fmt.Sprintf("org/repo#%d:%s", num, l)) 234 } 235 return out 236 } 237 238 func newFakeClient(body, branch string, initialLabels, comments []string, parentPRs map[int]string) (*fakegithub.FakeClient, *github.PullRequestEvent) { 239 formattedLabels := formatLabels(1, initialLabels...) 240 for parent, l := range parentPRs { 241 formattedLabels = append(formattedLabels, formatLabels(parent, l)...) 242 } 243 var issueComments []github.IssueComment 244 for _, comment := range comments { 245 issueComments = append(issueComments, github.IssueComment{Body: comment}) 246 } 247 fghc := fakegithub.NewFakeClient() 248 fghc.IssueComments = map[int][]github.IssueComment{1: issueComments} 249 fghc.RepoLabelsExisting = []string{ 250 lgtmLabel, 251 labels.ReleaseNote, 252 labels.ReleaseNoteLabelNeeded, 253 labels.ReleaseNoteNone, 254 labels.ReleaseNoteActionRequired, 255 } 256 fghc.IssueLabelsAdded = formattedLabels 257 fghc.IssueLabelsRemoved = []string{} 258 return fghc, &github.PullRequestEvent{ 259 Action: github.PullRequestActionEdited, 260 Number: 1, 261 PullRequest: github.PullRequest{ 262 Base: github.PullRequestBranch{Ref: branch}, 263 Number: 1, 264 Body: body, 265 User: github.User{Login: "cjwagner"}, 266 }, 267 Repo: github.Repo{ 268 Owner: github.User{Login: "org"}, 269 Name: "repo", 270 }, 271 } 272 } 273 274 func TestReleaseNotePR(t *testing.T) { 275 tests := []struct { 276 name string 277 initialLabels []string 278 body string 279 branch string // Defaults to master 280 parentPRs map[int]string 281 issueComments []string 282 IssueLabelsAdded []string 283 IssueLabelsRemoved []string 284 merged bool 285 }{ 286 { 287 name: "LGTM with release-note", 288 initialLabels: []string{lgtmLabel, labels.ReleaseNote}, 289 body: "```release-note\n note note note.\n```", 290 }, 291 { 292 name: "LGTM with release-note, arbitrary comment", 293 initialLabels: []string{lgtmLabel, labels.ReleaseNote}, 294 body: "```release-note\n note note note.\n```", 295 issueComments: []string{"Release notes are great fun."}, 296 }, 297 { 298 name: "LGTM with release-note-none", 299 initialLabels: []string{lgtmLabel, labels.ReleaseNoteNone}, 300 body: "```release-note\nnone\n```", 301 }, 302 { 303 name: "LGTM with release-note-none, /release-note-none comment, empty block", 304 initialLabels: []string{lgtmLabel, labels.ReleaseNoteNone}, 305 body: "```release-note\n```", 306 issueComments: []string{"/release-note-none "}, 307 }, 308 { 309 name: "LGTM with release-note-action-required", 310 initialLabels: []string{lgtmLabel, labels.ReleaseNoteActionRequired}, 311 body: "```release-note\n Action required.\n```", 312 }, 313 { 314 name: "LGTM with release-note-action-required, /release-note-none comment", 315 initialLabels: []string{lgtmLabel, labels.ReleaseNoteActionRequired}, 316 body: "```release-note\n Action required.\n```", 317 issueComments: []string{"Release notes are great fun.", "Especially \n/release-note-none"}, 318 }, 319 { 320 name: "LGTM with do-not-merge/release-note-label-needed", 321 initialLabels: []string{lgtmLabel, labels.ReleaseNoteLabelNeeded}, 322 }, 323 { 324 name: "LGTM with do-not-merge/release-note-label-needed, /release-note-none comment", 325 initialLabels: []string{lgtmLabel, labels.ReleaseNoteLabelNeeded}, 326 issueComments: []string{"Release notes are great fun.", "Especially \n/release-note-none"}, 327 IssueLabelsAdded: []string{labels.ReleaseNoteNone}, 328 IssueLabelsRemoved: []string{labels.ReleaseNoteLabelNeeded}, 329 }, 330 { 331 name: "LGTM only", 332 initialLabels: []string{lgtmLabel}, 333 IssueLabelsAdded: []string{labels.ReleaseNoteLabelNeeded}, 334 }, 335 { 336 name: "No labels", 337 initialLabels: []string{}, 338 IssueLabelsAdded: []string{labels.ReleaseNoteLabelNeeded}, 339 }, 340 { 341 name: "release-note", 342 initialLabels: []string{labels.ReleaseNote}, 343 body: "```release-note normal note.```", 344 }, 345 { 346 name: "release-note, /release-note-none comment", 347 initialLabels: []string{labels.ReleaseNote}, 348 body: "```release-note normal note.```", 349 issueComments: []string{"/release-note-none "}, 350 }, 351 { 352 name: "release-note-none", 353 initialLabels: []string{labels.ReleaseNoteNone}, 354 body: "```release-note\nnone\n```", 355 }, 356 { 357 name: "release-note-action-required", 358 initialLabels: []string{labels.ReleaseNoteActionRequired}, 359 body: "```release-note\n action required```", 360 }, 361 { 362 name: "release-note and do-not-merge/release-note-label-needed with no note", 363 initialLabels: []string{labels.ReleaseNote, labels.ReleaseNoteLabelNeeded}, 364 IssueLabelsRemoved: []string{labels.ReleaseNote}, 365 }, 366 { 367 name: "release-note and do-not-merge/release-note-label-needed with note", 368 initialLabels: []string{labels.ReleaseNote, labels.ReleaseNoteLabelNeeded}, 369 body: "```release-note note ```", 370 IssueLabelsRemoved: []string{labels.ReleaseNoteLabelNeeded}, 371 }, 372 { 373 name: "release-note-none and do-not-merge/release-note-label-needed", 374 initialLabels: []string{labels.ReleaseNoteNone, labels.ReleaseNoteLabelNeeded}, 375 body: "```release-note\nnone\n```", 376 IssueLabelsRemoved: []string{labels.ReleaseNoteLabelNeeded}, 377 }, 378 { 379 name: "release-note-action-required and do-not-merge/release-note-label-needed", 380 initialLabels: []string{labels.ReleaseNoteActionRequired, labels.ReleaseNoteLabelNeeded}, 381 body: "```release-note\nSomething something dark side. Something something ACTION REQUIRED.```", 382 IssueLabelsRemoved: []string{labels.ReleaseNoteLabelNeeded}, 383 }, 384 { 385 name: "do not add needs label when parent PR has releaseNote label", 386 branch: "release-1.2", 387 initialLabels: []string{}, 388 body: "Cherry pick of #2 on release-1.2.", 389 parentPRs: map[int]string{2: labels.ReleaseNote}, 390 }, 391 { 392 name: "do not touch LGTM on non-master when parent PR has releaseNote label, but remove releaseNoteNeeded", 393 branch: "release-1.2", 394 initialLabels: []string{lgtmLabel, labels.ReleaseNoteLabelNeeded}, 395 body: "Cherry pick of #2 on release-1.2.", 396 parentPRs: map[int]string{2: labels.ReleaseNote}, 397 IssueLabelsRemoved: []string{labels.ReleaseNoteLabelNeeded}, 398 }, 399 { 400 name: "do nothing when PR has releaseNoteActionRequired, but parent PR does not have releaseNote label", 401 branch: "release-1.2", 402 initialLabels: []string{labels.ReleaseNoteActionRequired}, 403 body: "Cherry pick of #2 on release-1.2.\n```release-note note action required note\n```", 404 parentPRs: map[int]string{2: labels.ReleaseNoteNone}, 405 }, 406 { 407 name: "add releaseNoteNeeded on non-master when parent PR has releaseNoteNone label", 408 branch: "release-1.2", 409 initialLabels: []string{lgtmLabel}, 410 body: "Cherry pick of #2 on release-1.2.", 411 parentPRs: map[int]string{2: labels.ReleaseNoteNone}, 412 IssueLabelsAdded: []string{labels.ReleaseNoteLabelNeeded}, 413 }, 414 { 415 name: "add releaseNoteNeeded on non-master when 1 of 2 parent PRs has releaseNoteNone", 416 branch: "release-1.2", 417 initialLabels: []string{lgtmLabel}, 418 body: "Other text.\nCherry pick of #2 on release-1.2.\nCherry pick of #4 on release-1.2.\n", 419 parentPRs: map[int]string{2: labels.ReleaseNote, 4: labels.ReleaseNoteNone}, 420 IssueLabelsAdded: []string{labels.ReleaseNoteLabelNeeded}, 421 }, 422 { 423 name: "remove releaseNoteNeeded on non-master when both parent PRs have a release note", 424 branch: "release-1.2", 425 initialLabels: []string{lgtmLabel, labels.ReleaseNoteLabelNeeded}, 426 body: "Other text.\nCherry pick of #2 on release-1.2.\nCherry pick of #4 on release-1.2.\n", 427 parentPRs: map[int]string{2: labels.ReleaseNote, 4: labels.ReleaseNoteActionRequired}, 428 IssueLabelsRemoved: []string{labels.ReleaseNoteLabelNeeded}, 429 }, 430 { 431 name: "add releaseNoteActionRequired on non-master when body contains note even though both parent PRs have a release note (non-mandatory RN)", 432 branch: "release-1.2", 433 initialLabels: []string{lgtmLabel, labels.ReleaseNoteLabelNeeded}, 434 body: "Other text.\nCherry pick of #2 on release-1.2.\nCherry pick of #4 on release-1.2.\n```release-note\nSome changes were made but there still is action required.\n```", 435 parentPRs: map[int]string{2: labels.ReleaseNote, 4: labels.ReleaseNoteActionRequired}, 436 IssueLabelsAdded: []string{labels.ReleaseNoteActionRequired}, 437 IssueLabelsRemoved: []string{labels.ReleaseNoteLabelNeeded}, 438 }, 439 { 440 name: "add releaseNoteNeeded, remove release-note on non-master when release-note block is removed and parent PR has releaseNoteNone label", 441 branch: "release-1.2", 442 initialLabels: []string{lgtmLabel, labels.ReleaseNote}, 443 body: "Cherry pick of #2 on release-1.2.\n```release-note\n```\n/cc @cjwagner", 444 parentPRs: map[int]string{2: labels.ReleaseNoteNone}, 445 IssueLabelsAdded: []string{labels.ReleaseNoteLabelNeeded}, 446 IssueLabelsRemoved: []string{labels.ReleaseNote}, 447 }, 448 { 449 name: "add ReleaseNoteLabelNeeded, remove release-note on non-master when release-note block is removed and parent PR has releaseNoteNone label", 450 branch: "release-1.2", 451 initialLabels: []string{lgtmLabel, labels.ReleaseNote}, 452 body: "Cherry pick of #2 on release-1.2.\n```release-note\n```\n/cc @cjwagner", 453 parentPRs: map[int]string{2: labels.ReleaseNoteNone}, 454 IssueLabelsAdded: []string{labels.ReleaseNoteLabelNeeded}, 455 IssueLabelsRemoved: []string{labels.ReleaseNote}, 456 }, 457 { 458 name: "add ReleaseNoteLabelNeeded, remove ReleaseNoteNone when kind/deprecation label is added", 459 initialLabels: []string{labels.DeprecationLabel, labels.ReleaseNoteNone}, 460 body: "```release-note\nnone\n```", 461 IssueLabelsAdded: []string{labels.ReleaseNoteLabelNeeded}, 462 IssueLabelsRemoved: []string{labels.ReleaseNoteNone}, 463 }, 464 { 465 name: "release-note-none command cannot override deprecation label", 466 issueComments: []string{"/release-note-none "}, 467 initialLabels: []string{labels.DeprecationLabel}, 468 body: "", 469 IssueLabelsAdded: []string{labels.ReleaseNoteLabelNeeded}, 470 }, 471 { 472 name: "Add do-not-merge/release-note-label-needed", 473 body: "```release-note\n```", 474 initialLabels: []string{}, 475 IssueLabelsAdded: []string{labels.ReleaseNoteLabelNeeded}, 476 }, 477 { 478 name: "Release note edited after merge, do not add do-not-merge/release-note-label-needed", 479 body: "```release-note\n```", 480 merged: true, 481 initialLabels: []string{}, 482 IssueLabelsAdded: []string{}, 483 }, 484 } 485 for _, test := range tests { 486 if test.branch == "" { 487 test.branch = "master" 488 } 489 fc, pr := newFakeClient(test.body, test.branch, test.initialLabels, test.issueComments, test.parentPRs) 490 pr.PullRequest.Merged = test.merged 491 492 err := handlePR(fc, logrus.WithField("plugin", PluginName), pr) 493 if err != nil { 494 t.Fatalf("Unexpected error from handlePR: %v", err) 495 } 496 497 // Check that all the correct labels (and only the correct labels) were added. 498 expectAdded := formatLabels(1, append(test.initialLabels, test.IssueLabelsAdded...)...) 499 for parent, label := range test.parentPRs { 500 expectAdded = append(expectAdded, formatLabels(parent, label)...) 501 } 502 sort.Strings(expectAdded) 503 sort.Strings(fc.IssueLabelsAdded) 504 if !reflect.DeepEqual(expectAdded, fc.IssueLabelsAdded) { 505 t.Errorf("(%s): Expected labels to be added: %q, but got: %q.", test.name, expectAdded, fc.IssueLabelsAdded) 506 } 507 expectRemoved := formatLabels(1, test.IssueLabelsRemoved...) 508 sort.Strings(expectRemoved) 509 sort.Strings(fc.IssueLabelsRemoved) 510 if !reflect.DeepEqual(expectRemoved, fc.IssueLabelsRemoved) { 511 t.Errorf("(%s): Expected labels to be removed: %q, but got %q.", test.name, expectRemoved, fc.IssueLabelsRemoved) 512 } 513 } 514 } 515 516 func TestGetReleaseNote(t *testing.T) { 517 tests := []struct { 518 body string 519 labels sets.Set[string] 520 expectedReleaseNote string 521 expectedReleaseNoteVariable string 522 }{ 523 { 524 body: "**Release note**: ```NONE```", 525 expectedReleaseNote: "NONE", 526 expectedReleaseNoteVariable: labels.ReleaseNoteNone, 527 }, 528 { 529 body: "**Release note**:\n\n ```\nNONE\n```", 530 expectedReleaseNote: "NONE", 531 expectedReleaseNoteVariable: labels.ReleaseNoteNone, 532 }, 533 { 534 body: "**Release note**:\n<!-- Steps to write your release note:\n...\n-->\n```NONE\n```", 535 expectedReleaseNote: "NONE", 536 expectedReleaseNoteVariable: labels.ReleaseNoteNone, 537 }, 538 { 539 body: "**Release note**:\n\n ```This is a description of my feature```", 540 expectedReleaseNote: "This is a description of my feature", 541 expectedReleaseNoteVariable: labels.ReleaseNote, 542 }, 543 { 544 body: "**Release note**: ```This is my feature. There is some action required for my feature.```", 545 expectedReleaseNote: "This is my feature. There is some action required for my feature.", 546 expectedReleaseNoteVariable: labels.ReleaseNoteActionRequired, 547 }, 548 { 549 body: "```release-note\nsomething great.\n```", 550 expectedReleaseNote: "something great.", 551 expectedReleaseNoteVariable: labels.ReleaseNote, 552 }, 553 { 554 body: "```release-note\nNONE\n```", 555 expectedReleaseNote: "NONE", 556 expectedReleaseNoteVariable: labels.ReleaseNoteNone, 557 }, 558 { 559 body: "```release-note\n`NONE`\n```", 560 expectedReleaseNote: "`NONE`", 561 expectedReleaseNoteVariable: labels.ReleaseNoteNone, 562 }, 563 { 564 body: "```release-note\n`\"NONE\"`\n```", 565 expectedReleaseNote: "`\"NONE\"`", 566 expectedReleaseNoteVariable: labels.ReleaseNoteNone, 567 }, 568 { 569 body: "**Release note**:\n```release-note\nNONE\n```\n", 570 expectedReleaseNote: "NONE", 571 expectedReleaseNoteVariable: labels.ReleaseNoteNone, 572 }, 573 { 574 body: "", 575 expectedReleaseNote: "", 576 expectedReleaseNoteVariable: labels.ReleaseNoteLabelNeeded, 577 }, 578 { 579 body: "", 580 labels: sets.New[string](labels.ReleaseNoteNone), 581 expectedReleaseNote: "", 582 expectedReleaseNoteVariable: labels.ReleaseNoteNone, 583 }, 584 { 585 body: "", 586 labels: sets.New[string](labels.DeprecationLabel), 587 expectedReleaseNote: "", 588 expectedReleaseNoteVariable: labels.ReleaseNoteLabelNeeded, 589 }, 590 { 591 body: "", 592 labels: sets.New[string](labels.ReleaseNoteNone, labels.DeprecationLabel), 593 expectedReleaseNote: "", 594 expectedReleaseNoteVariable: labels.ReleaseNoteLabelNeeded, 595 }, 596 { 597 body: "```release-note\nNONE\n```", 598 labels: sets.New[string](labels.DeprecationLabel), 599 expectedReleaseNote: "NONE", 600 expectedReleaseNoteVariable: labels.ReleaseNoteLabelNeeded, 601 }, 602 } 603 604 for testNum, test := range tests { 605 calculatedReleaseNote := getReleaseNote(test.body) 606 if test.expectedReleaseNote != calculatedReleaseNote { 607 t.Errorf("Test %v: Expected %v as the release note, got %v", testNum, test.expectedReleaseNote, calculatedReleaseNote) 608 } 609 calculatedLabel := determineReleaseNoteLabel(test.body, test.labels) 610 if test.expectedReleaseNoteVariable != calculatedLabel { 611 t.Errorf("Test %v: Expected %v as the release note label, got %v", testNum, test.expectedReleaseNoteVariable, calculatedLabel) 612 } 613 } 614 } 615 616 func TestShouldHandlePR(t *testing.T) { 617 tests := []struct { 618 name string 619 action github.PullRequestEventAction 620 label string 621 expectedResult bool 622 }{ 623 { 624 name: "Pull Request Action: Opened", 625 action: github.PullRequestActionOpened, 626 label: "", 627 expectedResult: true, 628 }, 629 { 630 name: "Pull Request Action: Edited", 631 action: github.PullRequestActionEdited, 632 label: "", 633 expectedResult: true, 634 }, 635 { 636 name: "Pull Request Action: Release Note label", 637 action: github.PullRequestActionLabeled, 638 label: labels.ReleaseNoteLabelNeeded, 639 expectedResult: true, 640 }, 641 { 642 name: "Pull Request Action: Non Release Note label", 643 action: github.PullRequestActionLabeled, 644 label: "do-not-merge/cherry-pick-not-approved", 645 expectedResult: false, 646 }, 647 } 648 649 for _, test := range tests { 650 pr := github.PullRequestEvent{ 651 Action: test.action, 652 Label: github.Label{ 653 Name: test.label, 654 }, 655 } 656 result := shouldHandlePR(&pr) 657 658 if test.expectedResult != result { 659 t.Errorf("(%s): Expected value to be: %t, but got %t.", test.name, test.expectedResult, result) 660 } 661 } 662 } 663 664 func Test_editReleaseNote(t *testing.T) { 665 issueNum := 5 666 ts := []struct { 667 name string 668 event github.IssueCommentEvent 669 expectError bool 670 errorMessage string 671 comment string 672 fcFunc func(client *fakegithub.FakeClient) 673 expectedNote string 674 }{ 675 { 676 name: "is not an org member", 677 event: github.IssueCommentEvent{ 678 Action: github.IssueCommentActionCreated, 679 Issue: github.Issue{Number: issueNum, User: github.User{Login: "user"}}, 680 Comment: github.IssueComment{ 681 Body: "/release-note-edit\r\n```release-note\r\nThe new note\r\n```\r\n", 682 User: github.User{Login: "user"}, 683 }, 684 Repo: github.Repo{Owner: github.User{Login: "org"}}, 685 }, 686 comment: "org member", 687 fcFunc: func(fc *fakegithub.FakeClient) { 688 fc.OrgMembers["org"] = []string{} 689 }, 690 }, 691 { 692 name: "no release note block", 693 event: github.IssueCommentEvent{ 694 Action: github.IssueCommentActionCreated, 695 Issue: github.Issue{Number: issueNum, User: github.User{Login: "user"}}, 696 Comment: github.IssueComment{ 697 Body: "/release-note-edit\r\nNew note", 698 User: github.User{Login: "user"}, 699 }, 700 Repo: github.Repo{Owner: github.User{Login: "org"}}, 701 }, 702 comment: "release note block", 703 fcFunc: func(fc *fakegithub.FakeClient) { 704 fc.OrgMembers["org"] = []string{"user"} 705 }, 706 }, 707 { 708 name: "multiple release note blocks", 709 event: github.IssueCommentEvent{ 710 Action: github.IssueCommentActionCreated, 711 Issue: github.Issue{Number: issueNum, User: github.User{Login: "user"}}, 712 Comment: github.IssueComment{ 713 Body: "/release-note-edit\r\n```release-note\r\nThe new note\r\n```\r\n```release-note\r\nThe second note\r\n```\r\n", 714 User: github.User{Login: "user"}, 715 }, 716 Repo: github.Repo{Owner: github.User{Login: "org"}}, 717 }, 718 comment: "single release note block", 719 fcFunc: func(fc *fakegithub.FakeClient) { 720 fc.OrgMembers["org"] = []string{"user"} 721 }, 722 }, 723 { 724 name: "happy path", 725 event: github.IssueCommentEvent{ 726 Action: github.IssueCommentActionCreated, 727 Issue: github.Issue{Number: issueNum, User: github.User{Login: "user"}, Body: "Top\r\n```release-note\r\nNONE\r\n```\r\nBelow\r\n"}, 728 Comment: github.IssueComment{ 729 Body: "/release-note-edit\r\n```release-note\r\nThe new note\r\n```\r\n", 730 User: github.User{Login: "user"}, 731 }, 732 Repo: github.Repo{Owner: github.User{Login: "org"}}, 733 }, 734 fcFunc: func(fc *fakegithub.FakeClient) { 735 fc.OrgMembers["org"] = []string{"user"} 736 fc.Issues[issueNum] = &github.Issue{ 737 Number: issueNum, 738 User: github.User{Login: "user"}, 739 Body: "Top level\r\n```release-note\r\nNONE\r\n```\r\n", 740 } 741 }, 742 expectedNote: "Top\r\n```release-note\r\nThe new note\r\n```\r\nBelow\r\n", 743 }, 744 } 745 for _, tc := range ts { 746 t.Run(tc.name, func(t *testing.T) { 747 fc := fakegithub.NewFakeClient() 748 if tc.fcFunc != nil { 749 tc.fcFunc(fc) 750 } 751 err := editReleaseNote(fc, logrus.WithField("plugin", PluginName), tc.event) 752 if err != nil { 753 if !tc.expectError { 754 t.Fatalf("unexpected error: %v", err) 755 } 756 if m := err.Error(); !strings.Contains(m, tc.errorMessage) { 757 t.Fatalf("expected error to contain: %s got: %v", tc.errorMessage, m) 758 } 759 } 760 if err == nil && tc.expectError { 761 t.Fatalf("expected error but did not produce") 762 } 763 if len(tc.comment) != 0 { 764 if cm, ok := fc.IssueComments[tc.event.Issue.Number]; ok { 765 if !strings.Contains(cm[0].Body, tc.comment) { 766 t.Fatalf("expected comment to contain: %s got: %s", tc.comment, cm[0].Body) 767 } 768 } 769 } 770 if len(tc.comment) == 0 && len(fc.IssueComments[issueNum]) != 0 { 771 t.Fatalf("unexpected comment: %v", fc.IssueComments[issueNum]) 772 } 773 _, ok := fc.Issues[issueNum] 774 if ok && tc.expectedNote == "" { 775 t.Fatalf("unexpected issue exists: %v", fc.Issues[issueNum]) 776 } 777 if tc.expectedNote != "" { 778 if !ok { 779 t.Fatalf("expected release note to be edited but issue does not exist") 780 } 781 if i := fc.Issues[issueNum]; i.Body != tc.expectedNote { 782 t.Fatalf("expected release note to be edited to: %v \n got: %v", tc.expectedNote, i.Body) 783 } 784 } 785 }) 786 } 787 }