1 /* 2 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.openjdk.skara.bots.pr; 24 25 import org.openjdk.skara.host.*; 26 import org.openjdk.skara.test.*; 27 28 import org.junit.jupiter.api.*; 29 30 import java.io.IOException; 31 import java.nio.file.Files; 32 import java.util.*; 33 import java.util.regex.Pattern; 34 35 import static org.junit.jupiter.api.Assertions.*; 36 import static org.junit.jupiter.api.Assumptions.assumeTrue; 37 38 class CheckTests { 39 @Test 40 void simpleCommit(TestInfo testInfo) throws IOException { 41 try (var credentials = new HostCredentials(testInfo); 42 var tempFolder = new TemporaryDirectory()) { 43 var author = credentials.getHostedRepository(); 44 var reviewer = credentials.getHostedRepository(); 45 46 var censusBuilder = credentials.getCensusBuilder() 47 .addAuthor(author.host().getCurrentUserDetails().id()) 48 .addReviewer(reviewer.host().getCurrentUserDetails().id()); 49 var checkBot = new PullRequestBot(author, censusBuilder.build(), "master"); 50 51 // Populate the projects repository 52 var localRepo = CheckableRepository.init(tempFolder.path(), author.getRepositoryType()); 53 var masterHash = localRepo.resolve("master").orElseThrow(); 54 localRepo.push(masterHash, author.getUrl(), "master", true); 55 56 // Make a change with a corresponding PR 57 var editHash = CheckableRepository.appendAndCommit(localRepo); 58 localRepo.push(editHash, author.getUrl(), "refs/heads/edit", true); 59 var pr = credentials.createPullRequest(author, "master", "edit", "This is a pull request"); 60 61 // Check the status 62 TestBotRunner.runPeriodicItems(checkBot); 63 64 // Verify that the check succeeded 65 var checks = pr.getChecks(editHash); 66 assertEquals(1, checks.size()); 67 var check = checks.get("jcheck"); 68 assertEquals(CheckStatus.SUCCESS, check.status()); 69 70 // The PR should now be ready for review 71 assertTrue(pr.getLabels().contains("rfr")); 72 assertFalse(pr.getLabels().contains("ready")); 73 74 // Approve it as another user 75 var approvalPr = reviewer.getPullRequest(pr.getId()); 76 approvalPr.addReview(Review.Verdict.APPROVED, "Approved"); 77 78 // Check the status again 79 TestBotRunner.runPeriodicItems(checkBot); 80 81 // The check should now be successful 82 checks = pr.getChecks(editHash); 83 assertEquals(1, checks.size()); 84 check = checks.get("jcheck"); 85 assertEquals(CheckStatus.SUCCESS, check.status()); 86 87 // The PR should not be flagged as ready for review, at it is already reviewed 88 assertFalse(pr.getLabels().contains("rfr")); 89 assertTrue(pr.getLabels().contains("ready")); 90 } 91 } 92 93 @Test 94 void whitespaceIssue(TestInfo testInfo) throws IOException { 95 try (var credentials = new HostCredentials(testInfo); 96 var tempFolder = new TemporaryDirectory()) { 97 98 var author = credentials.getHostedRepository(); 99 var reviewer = credentials.getHostedRepository(); 100 101 var censusBuilder = credentials.getCensusBuilder() 102 .addAuthor(author.host().getCurrentUserDetails().id()) 103 .addReviewer(reviewer.host().getCurrentUserDetails().id()); 104 var checkBot = new PullRequestBot(author, censusBuilder.build(), "master"); 105 106 // Populate the projects repository 107 var localRepo = CheckableRepository.init(tempFolder.path(), author.getRepositoryType()); 108 var masterHash = localRepo.resolve("master").orElseThrow(); 109 localRepo.push(masterHash, author.getUrl(), "master", true); 110 111 // Make a change with a corresponding PR 112 var editHash = CheckableRepository.appendAndCommit(localRepo, "A line with a trailing whitespace "); 113 localRepo.push(editHash, author.getUrl(), "refs/heads/edit", true); 114 var pr = credentials.createPullRequest(author, "master", "edit", "This is a pull request"); 115 116 // Check the status 117 TestBotRunner.runPeriodicItems(checkBot); 118 119 // The PR should not be flagged as ready for review 120 assertFalse(pr.getLabels().contains("rfr")); 121 122 // Approve it as another user 123 var approvalPr = reviewer.getPullRequest(pr.getId()); 124 approvalPr.addReview(Review.Verdict.APPROVED, "Approved"); 125 126 // Check the status 127 TestBotRunner.runPeriodicItems(checkBot); 128 129 // Verify that the check failed 130 var checks = pr.getChecks(editHash); 131 assertEquals(1, checks.size()); 132 var check = checks.get("jcheck"); 133 assertEquals(CheckStatus.FAILURE, check.status()); 134 135 // The PR should not still not be flagged as ready for review 136 assertFalse(pr.getLabels().contains("rfr")); 137 138 // Remove the trailing whitespace in a new commit 139 editHash = CheckableRepository.replaceAndCommit(localRepo, "A line without a trailing whitespace"); 140 localRepo.push(editHash, author.getUrl(), "refs/heads/edit", true); 141 142 // Make sure that the push registered 143 var lastHeadHash = pr.getHeadHash(); 144 var refreshCount = 0; 145 do { 146 pr = author.getPullRequest(pr.getId()); 147 if (refreshCount++ > 100) { 148 fail("The PR did not update after the new push"); 149 } 150 } while (pr.getHeadHash().equals(lastHeadHash)); 151 152 // Check the status again 153 TestBotRunner.runPeriodicItems(checkBot); 154 155 // The PR should not be flagged as ready for review, at it is already reviewed 156 assertFalse(pr.getLabels().contains("rfr")); 157 158 // The check should now be successful 159 checks = pr.getChecks(editHash); 160 assertEquals(1, checks.size()); 161 check = checks.get("jcheck"); 162 assertEquals(CheckStatus.SUCCESS, check.status()); 163 } 164 } 165 166 @Test 167 void multipleReviews(TestInfo testInfo) throws IOException { 168 try (var credentials = new HostCredentials(testInfo); 169 var tempFolder = new TemporaryDirectory()) { 170 171 var author = credentials.getHostedRepository(); 172 var reviewer = credentials.getHostedRepository(); 173 var commenter = credentials.getHostedRepository(); 174 175 var censusBuilder = credentials.getCensusBuilder() 176 .addAuthor(author.host().getCurrentUserDetails().id()) 177 .addReviewer(reviewer.host().getCurrentUserDetails().id()) 178 .addReviewer(commenter.host().getCurrentUserDetails().id()); 179 180 var checkBot = new PullRequestBot(author, censusBuilder.build(), "master"); 181 182 // Populate the projects repository 183 var localRepo = CheckableRepository.init(tempFolder.path(), author.getRepositoryType()); 184 var masterHash = localRepo.resolve("master").orElseThrow(); 185 localRepo.push(masterHash, author.getUrl(), "master", true); 186 187 // Make a change with a corresponding PR 188 var editHash = CheckableRepository.appendAndCommit(localRepo); 189 localRepo.push(editHash, author.getUrl(), "refs/heads/edit", true); 190 var authorPr = credentials.createPullRequest(author, "master", "edit", "This is a pull request"); 191 192 // Let the status bot inspect the PR 193 TestBotRunner.runPeriodicItems(checkBot); 194 assertFalse(authorPr.getBody().contains("Approvers")); 195 196 // Approve it 197 var reviewerPr = reviewer.getPullRequest(authorPr.getId()); 198 reviewerPr.addReview(Review.Verdict.APPROVED, "Approved"); 199 TestBotRunner.runPeriodicItems(checkBot); 200 201 // Refresh the PR and check that it has been approved 202 authorPr = author.getPullRequest(authorPr.getId()); 203 assertTrue(authorPr.getBody().contains("Approvers")); 204 205 // Update the file after approval 206 editHash = CheckableRepository.appendAndCommit(localRepo, "Now I've gone and changed it"); 207 localRepo.push(editHash, author.getUrl(), "edit", true); 208 209 // Make sure that the push registered 210 var lastHeadHash = authorPr.getHeadHash(); 211 var refreshCount = 0; 212 do { 213 authorPr = author.getPullRequest(authorPr.getId()); 214 if (refreshCount++ > 100) { 215 fail("The PR did not update after the new push"); 216 } 217 } while (authorPr.getHeadHash().equals(lastHeadHash)); 218 219 // Check that the review is flagged as stale 220 TestBotRunner.runPeriodicItems(checkBot); 221 authorPr = author.getPullRequest(authorPr.getId()); 222 assertTrue(authorPr.getBody().contains("Note")); 223 224 // Now we can approve it again 225 reviewerPr.addReview(Review.Verdict.APPROVED, "Approved"); 226 TestBotRunner.runPeriodicItems(checkBot); 227 228 // Refresh the PR and check that it has been approved (once) and is no longer stale 229 authorPr = author.getPullRequest(authorPr.getId()); 230 assertTrue(authorPr.getBody().contains("Approvers")); 231 assertEquals(1, authorPr.getBody().split("Generated Reviewer", -1).length - 1); 232 assertTrue(authorPr.getReviews().size() >= 1); 233 assertFalse(authorPr.getBody().contains("Note")); 234 235 // Add a review with disapproval 236 var commenterPr = commenter.getPullRequest(authorPr.getId()); 237 commenterPr.addReview(Review.Verdict.DISAPPROVED, "Disapproved"); 238 TestBotRunner.runPeriodicItems(checkBot); 239 240 // Refresh the PR and check that it still only approved once (but two reviews) and is no longer stale 241 authorPr = author.getPullRequest(authorPr.getId()); 242 assertTrue(authorPr.getBody().contains("Approvers")); 243 assertEquals(1, authorPr.getBody().split("Generated Reviewer", -1).length - 1); 244 assertTrue(authorPr.getReviews().size() >= 2); 245 assertFalse(authorPr.getBody().contains("Note")); 246 } 247 } 248 249 @Test 250 void selfReview(TestInfo testInfo) throws IOException { 251 try (var credentials = new HostCredentials(testInfo); 252 var tempFolder = new TemporaryDirectory()) { 253 254 var author = credentials.getHostedRepository(); 255 256 var censusBuilder = credentials.getCensusBuilder() 257 .addReviewer(author.host().getCurrentUserDetails().id()); 258 259 var checkBot = new PullRequestBot(author, censusBuilder.build(), "master"); 260 261 // Populate the projects repository 262 var localRepo = CheckableRepository.init(tempFolder.path(), author.getRepositoryType()); 263 var masterHash = localRepo.resolve("master").orElseThrow(); 264 localRepo.push(masterHash, author.getUrl(), "master", true); 265 266 // Make a change with a corresponding PR 267 var editHash = CheckableRepository.appendAndCommit(localRepo); 268 localRepo.push(editHash, author.getUrl(), "edit", true); 269 var authorPr = credentials.createPullRequest(author, "master", "edit", "This is a pull request"); 270 271 // Let the status bot inspect the PR 272 TestBotRunner.runPeriodicItems(checkBot); 273 assertFalse(authorPr.getBody().contains("Approvers")); 274 275 // Approve it 276 authorPr.addReview(Review.Verdict.APPROVED, "Approved"); 277 TestBotRunner.runPeriodicItems(checkBot); 278 279 // Refresh the PR and check that it has been approved 280 authorPr = author.getPullRequest(authorPr.getId()); 281 assertTrue(authorPr.getBody().contains("Approvers")); 282 283 // Verify that the check failed 284 var checks = authorPr.getChecks(editHash); 285 assertEquals(1, checks.size()); 286 var check = checks.get("jcheck"); 287 assertEquals(CheckStatus.FAILURE, check.status()); 288 } 289 } 290 291 @Test 292 void multipleCommitters(TestInfo testInfo) throws IOException { 293 try (var credentials = new HostCredentials(testInfo); 294 var tempFolder = new TemporaryDirectory()) { 295 var author = credentials.getHostedRepository(); 296 var reviewer = credentials.getHostedRepository(); 297 298 var censusBuilder = credentials.getCensusBuilder() 299 .addReviewer(reviewer.host().getCurrentUserDetails().id()); 300 var checkBot = new PullRequestBot(author, censusBuilder.build(), "master"); 301 302 // Populate the projects repository 303 var localRepo = CheckableRepository.init(tempFolder.path(), author.getRepositoryType()); 304 var masterHash = localRepo.resolve("master").orElseThrow(); 305 localRepo.push(masterHash, author.getUrl(), "master", true); 306 307 // Make two changes with different authors 308 CheckableRepository.appendAndCommit(localRepo, "First edit", "Edit by number 1", 309 "number1", "number1@none.none"); 310 var editHash = CheckableRepository.appendAndCommit(localRepo, "Second edit", "Edit by number 2", 311 "number2", "number2@none.none"); 312 localRepo.push(editHash, author.getUrl(), "refs/heads/edit", true); 313 var pr = credentials.createPullRequest(author, "master", "edit", "This is a pull request"); 314 315 // Check the status 316 TestBotRunner.runPeriodicItems(checkBot); 317 318 // Verify that the check failed 319 var checks = pr.getChecks(editHash); 320 assertEquals(1, checks.size()); 321 var check = checks.get("jcheck"); 322 assertEquals(CheckStatus.FAILURE, check.status()); 323 324 // Approve it as another user 325 var approvalPr = reviewer.getPullRequest(pr.getId()); 326 approvalPr.addReview(Review.Verdict.APPROVED, "Approved"); 327 328 // Check the status again 329 TestBotRunner.runPeriodicItems(checkBot); 330 331 // The check should still be failing 332 checks = pr.getChecks(editHash); 333 assertEquals(1, checks.size()); 334 check = checks.get("jcheck"); 335 assertEquals(CheckStatus.FAILURE, check.status()); 336 337 // The PR should not be flagged as ready for review, as multiple committers is a problem 338 assertFalse(pr.getLabels().contains("rfr")); 339 } 340 } 341 342 @Test 343 void updatedContentFailsCheck(TestInfo testInfo) throws IOException { 344 try (var credentials = new HostCredentials(testInfo); 345 var tempFolder = new TemporaryDirectory()) { 346 var author = credentials.getHostedRepository(); 347 var reviewer = credentials.getHostedRepository(); 348 349 var censusBuilder = credentials.getCensusBuilder() 350 .addAuthor(author.host().getCurrentUserDetails().id()) 351 .addReviewer(reviewer.host().getCurrentUserDetails().id()); 352 var checkBot = new PullRequestBot(author, censusBuilder.build(), "master"); 353 354 // Populate the projects repository 355 var localRepo = CheckableRepository.init(tempFolder.path(), author.getRepositoryType()); 356 var masterHash = localRepo.resolve("master").orElseThrow(); 357 localRepo.push(masterHash, author.getUrl(), "master", true); 358 359 // Make a change with a corresponding PR 360 var editHash = CheckableRepository.appendAndCommit(localRepo); 361 localRepo.push(editHash, author.getUrl(), "refs/heads/edit", true); 362 var pr = credentials.createPullRequest(author, "master", "edit", "This is a pull request"); 363 364 // Check the status 365 TestBotRunner.runPeriodicItems(checkBot); 366 367 // Verify that the check passed 368 var checks = pr.getChecks(editHash); 369 assertEquals(1, checks.size()); 370 var check = checks.get("jcheck"); 371 assertEquals(CheckStatus.SUCCESS, check.status()); 372 373 // The PR should now be ready for review 374 assertTrue(pr.getLabels().contains("rfr")); 375 assertFalse(pr.getLabels().contains("ready")); 376 377 // Approve it as another user 378 var approvalPr = reviewer.getPullRequest(pr.getId()); 379 approvalPr.addReview(Review.Verdict.APPROVED, "Approved"); 380 381 // Check the status again 382 TestBotRunner.runPeriodicItems(checkBot); 383 384 // The check should now be successful 385 checks = pr.getChecks(editHash); 386 assertEquals(1, checks.size()); 387 check = checks.get("jcheck"); 388 assertEquals(CheckStatus.SUCCESS, check.status()); 389 390 // The PR should not be flagged as ready for review, at it is already reviewed 391 assertFalse(pr.getLabels().contains("rfr")); 392 assertTrue(pr.getLabels().contains("ready")); 393 394 var addedHash = CheckableRepository.appendAndCommit(localRepo, "trailing whitespace "); 395 localRepo.push(addedHash, author.getUrl(), "edit"); 396 397 // Make sure that the push registered 398 var lastHeadHash = pr.getHeadHash(); 399 var refreshCount = 0; 400 do { 401 pr = author.getPullRequest(pr.getId()); 402 if (refreshCount++ > 100) { 403 fail("The PR did not update after the new push"); 404 } 405 } while (pr.getHeadHash().equals(lastHeadHash)); 406 407 // Check the status 408 TestBotRunner.runPeriodicItems(checkBot); 409 410 // The PR is now neither ready for review nor integration 411 assertFalse(pr.getLabels().contains("rfr")); 412 assertFalse(pr.getLabels().contains("ready")); 413 414 // The check should now be failing 415 checks = pr.getChecks(addedHash); 416 assertEquals(1, checks.size()); 417 check = checks.get("jcheck"); 418 assertEquals(CheckStatus.FAILURE, check.status()); 419 } 420 } 421 422 @Test 423 void individualReviewComments(TestInfo testInfo) throws IOException { 424 try (var credentials = new HostCredentials(testInfo); 425 var tempFolder = new TemporaryDirectory()) { 426 var author = credentials.getHostedRepository(); 427 var reviewer = credentials.getHostedRepository(); 428 429 // This test is only relevant on hosts not supporting proper review comment bodies 430 assumeTrue(!author.host().supportsReviewBody()); 431 432 var censusBuilder = credentials.getCensusBuilder() 433 .addAuthor(author.host().getCurrentUserDetails().id()) 434 .addReviewer(reviewer.host().getCurrentUserDetails().id()); 435 var checkBot = new PullRequestBot(author, censusBuilder.build(), "master"); 436 437 // Populate the projects repository 438 var localRepo = CheckableRepository.init(tempFolder.path(), author.getRepositoryType()); 439 var masterHash = localRepo.resolve("master").orElseThrow(); 440 localRepo.push(masterHash, author.getUrl(), "master", true); 441 442 // Make a change with a corresponding PR 443 var editHash = CheckableRepository.appendAndCommit(localRepo); 444 localRepo.push(editHash, author.getUrl(), "refs/heads/edit", true); 445 var pr = credentials.createPullRequest(author, "master", "edit", "This is a pull request"); 446 447 // Check the status 448 TestBotRunner.runPeriodicItems(checkBot); 449 var comments = pr.getComments(); 450 var commentCount = comments.size(); 451 452 // Approve it as another user 453 var approvalPr = reviewer.getPullRequest(pr.getId()); 454 approvalPr.addReview(Review.Verdict.APPROVED, "Approved"); 455 456 // Check the status again 457 TestBotRunner.runPeriodicItems(checkBot); 458 459 // There should now be two additional comments 460 comments = pr.getComments(); 461 assertEquals(commentCount + 2, comments.size()); 462 var comment = comments.get(commentCount); 463 assertTrue(comment.body().contains(reviewer.host().getCurrentUserDetails().userName())); 464 assertTrue(comment.body().contains("approved")); 465 466 // Drop the review 467 approvalPr.addReview(Review.Verdict.NONE, "Unreviewed"); 468 469 // Check the status again 470 TestBotRunner.runPeriodicItems(checkBot); 471 472 // There should now be yet another comment 473 comments = pr.getComments(); 474 assertEquals(commentCount + 3, comments.size()); 475 comment = comments.get(commentCount + 2); 476 assertTrue(comment.body().contains(reviewer.host().getCurrentUserDetails().userName())); 477 assertTrue(comment.body().contains("comment")); 478 479 // No changes should not generate additional comments 480 TestBotRunner.runPeriodicItems(checkBot); 481 comments = pr.getComments(); 482 assertEquals(commentCount + 3, comments.size()); 483 } 484 } 485 486 @Test 487 void mergeMessage(TestInfo testInfo) throws IOException { 488 try (var credentials = new HostCredentials(testInfo); 489 var tempFolder = new TemporaryDirectory(); 490 var pushedFolder = new TemporaryDirectory()) { 491 492 var author = credentials.getHostedRepository(); 493 var integrator = credentials.getHostedRepository(); 494 var censusBuilder = credentials.getCensusBuilder() 495 .addCommitter(author.host().getCurrentUserDetails().id()) 496 .addReviewer(integrator.host().getCurrentUserDetails().id()); 497 var mergeBot = new PullRequestBot(integrator, censusBuilder.build(), "master"); 498 499 // Populate the projects repository 500 var localRepo = CheckableRepository.init(tempFolder.path(), author.getRepositoryType()); 501 var masterHash = localRepo.resolve("master").orElseThrow(); 502 assertFalse(CheckableRepository.hasBeenEdited(localRepo)); 503 localRepo.push(masterHash, author.getUrl(), "master", true); 504 505 // Make a change with a corresponding PR 506 var editHash = CheckableRepository.appendAndCommit(localRepo); 507 localRepo.push(editHash, author.getUrl(), "edit", true); 508 var pr = credentials.createPullRequest(author, "master", "edit", "This is a pull request"); 509 510 // Approve it as another user 511 var approvalPr = integrator.getPullRequest(pr.getId()); 512 approvalPr.addReview(Review.Verdict.APPROVED, "Approved"); 513 514 // Get all messages up to date 515 TestBotRunner.runPeriodicItems(mergeBot); 516 517 // Push something unrelated to master 518 localRepo.checkout(masterHash, true); 519 var unrelated = localRepo.root().resolve("unrelated.txt"); 520 Files.writeString(unrelated, "Hello"); 521 localRepo.add(unrelated); 522 var unrelatedHash = localRepo.commit("Unrelated", "X", "x@y.z"); 523 localRepo.push(unrelatedHash, author.getUrl(), "master"); 524 525 // Let the bot see the changes 526 TestBotRunner.runPeriodicItems(mergeBot); 527 528 // The bot should reply with an ok message 529 var updated = pr.getComments().stream() 530 .filter(comment -> comment.body().contains("there has been 1 commit")) 531 .filter(comment -> comment.body().contains("please merge")) 532 .count(); 533 assertEquals(1, updated); 534 } 535 } 536 537 @Test 538 void cannotRebase(TestInfo testInfo) throws IOException { 539 try (var credentials = new HostCredentials(testInfo); 540 var tempFolder = new TemporaryDirectory(); 541 var pushedFolder = new TemporaryDirectory()) { 542 543 var author = credentials.getHostedRepository(); 544 var integrator = credentials.getHostedRepository(); 545 var censusBuilder = credentials.getCensusBuilder() 546 .addCommitter(author.host().getCurrentUserDetails().id()) 547 .addReviewer(integrator.host().getCurrentUserDetails().id()); 548 var mergeBot = new PullRequestBot(integrator, censusBuilder.build(), "master"); 549 550 // Populate the projects repository 551 var localRepo = CheckableRepository.init(tempFolder.path(), author.getRepositoryType()); 552 var masterHash = localRepo.resolve("master").orElseThrow(); 553 assertFalse(CheckableRepository.hasBeenEdited(localRepo)); 554 localRepo.push(masterHash, author.getUrl(), "master", true); 555 556 // Make a change with a corresponding PR 557 var editHash = CheckableRepository.appendAndCommit(localRepo); 558 localRepo.push(editHash, author.getUrl(), "edit", true); 559 var pr = credentials.createPullRequest(author, "master", "edit", "This is a pull request"); 560 561 // Approve it as another user 562 var approvalPr = integrator.getPullRequest(pr.getId()); 563 approvalPr.addReview(Review.Verdict.APPROVED, "Approved"); 564 565 // Get all messages up to date 566 TestBotRunner.runPeriodicItems(mergeBot); 567 568 // Push something conflicting to master 569 localRepo.checkout(masterHash, true); 570 var conflictingHash = CheckableRepository.appendAndCommit(localRepo, "This looks like a conflict"); 571 localRepo.push(conflictingHash, author.getUrl(), "master"); 572 573 // Let the bot see the changes 574 TestBotRunner.runPeriodicItems(mergeBot); 575 576 // The bot should reply with that there is a conflict 577 var updated = pr.getComments().stream() 578 .filter(comment -> comment.body().contains("there has been 1 commit")) 579 .filter(comment -> comment.body().contains("cannot be rebased automatically")) 580 .count(); 581 assertEquals(1, updated); 582 583 // The PR should be flagged as outdated 584 assertTrue(pr.getLabels().contains("outdated")); 585 586 // Restore the master branch 587 localRepo.push(masterHash, author.getUrl(), "master", true); 588 589 // Let the bot see the changes 590 TestBotRunner.runPeriodicItems(mergeBot); 591 592 // The bot should no longer detect a conflict 593 updated = pr.getComments().stream() 594 .filter(comment -> comment.body().contains("change can now be integrated")) 595 .count(); 596 assertEquals(1, updated); 597 598 // The PR should not be flagged as outdated 599 assertFalse(pr.getLabels().contains("outdated")); 600 } 601 } 602 603 @Test 604 void blockingLabel(TestInfo testInfo) throws IOException { 605 try (var credentials = new HostCredentials(testInfo); 606 var tempFolder = new TemporaryDirectory()) { 607 var author = credentials.getHostedRepository(); 608 var reviewer = credentials.getHostedRepository(); 609 610 var censusBuilder = credentials.getCensusBuilder() 611 .addAuthor(author.host().getCurrentUserDetails().id()) 612 .addReviewer(reviewer.host().getCurrentUserDetails().id()); 613 var checkBot = new PullRequestBot(author, censusBuilder.build(), "master", Map.of(), Map.of(), 614 Map.of("block", "Test Blocker"), Set.of(), Map.of()); 615 616 // Populate the projects repository 617 var localRepo = CheckableRepository.init(tempFolder.path(), author.getRepositoryType()); 618 var masterHash = localRepo.resolve("master").orElseThrow(); 619 localRepo.push(masterHash, author.getUrl(), "master", true); 620 621 // Make a change with a corresponding PR 622 var editHash = CheckableRepository.appendAndCommit(localRepo); 623 localRepo.push(editHash, author.getUrl(), "edit", true); 624 var pr = credentials.createPullRequest(author, "master", "edit", "This is a pull request"); 625 pr.addLabel("block"); 626 627 // Check the status 628 TestBotRunner.runPeriodicItems(checkBot); 629 630 // Verify that the check failed 631 var checks = pr.getChecks(editHash); 632 assertEquals(1, checks.size()); 633 var check = checks.get("jcheck"); 634 assertEquals(CheckStatus.FAILURE, check.status()); 635 assertTrue(check.summary().orElseThrow().contains("Test Blocker")); 636 637 // The PR should not yet be ready for review 638 assertTrue(pr.getLabels().contains("block")); 639 assertFalse(pr.getLabels().contains("rfr")); 640 assertFalse(pr.getLabels().contains("ready")); 641 642 // Check the status again 643 pr.removeLabel("block"); 644 TestBotRunner.runPeriodicItems(checkBot); 645 646 // The PR should now be ready for review 647 assertTrue(pr.getLabels().contains("rfr")); 648 assertFalse(pr.getLabels().contains("ready")); 649 } 650 } 651 652 @Test 653 void missingReadyLabel(TestInfo testInfo) throws IOException { 654 try (var credentials = new HostCredentials(testInfo); 655 var tempFolder = new TemporaryDirectory()) { 656 var author = credentials.getHostedRepository(); 657 var reviewer = credentials.getHostedRepository(); 658 659 var censusBuilder = credentials.getCensusBuilder() 660 .addAuthor(author.host().getCurrentUserDetails().id()) 661 .addReviewer(reviewer.host().getCurrentUserDetails().id()); 662 var checkBot = new PullRequestBot(author, censusBuilder.build(), "master", Map.of(), Map.of(), 663 Map.of(), Set.of("good-to-go"), Map.of()); 664 665 // Populate the projects repository 666 var localRepo = CheckableRepository.init(tempFolder.path(), author.getRepositoryType()); 667 var masterHash = localRepo.resolve("master").orElseThrow(); 668 localRepo.push(masterHash, author.getUrl(), "master", true); 669 670 // Make a change with a corresponding PR 671 var editHash = CheckableRepository.appendAndCommit(localRepo); 672 localRepo.push(editHash, author.getUrl(), "edit", true); 673 var pr = credentials.createPullRequest(author, "master", "edit", "This is a pull request"); 674 675 // Check the status 676 TestBotRunner.runPeriodicItems(checkBot); 677 678 // Verify that no checks have been run 679 var checks = pr.getChecks(editHash); 680 assertEquals(0, checks.size()); 681 682 // The PR should not yet be ready for review 683 assertFalse(pr.getLabels().contains("rfr")); 684 685 // Check the status again 686 pr.addLabel("good-to-go"); 687 TestBotRunner.runPeriodicItems(checkBot); 688 689 // The PR should now be ready for review 690 assertTrue(pr.getLabels().contains("rfr")); 691 } 692 } 693 694 @Test 695 void missingReadyComment(TestInfo testInfo) throws IOException { 696 try (var credentials = new HostCredentials(testInfo); 697 var tempFolder = new TemporaryDirectory()) { 698 var author = credentials.getHostedRepository(); 699 var reviewer = credentials.getHostedRepository(); 700 701 var censusBuilder = credentials.getCensusBuilder() 702 .addAuthor(author.host().getCurrentUserDetails().id()) 703 .addReviewer(reviewer.host().getCurrentUserDetails().id()); 704 var checkBot = new PullRequestBot(author, censusBuilder.build(), "master", Map.of(), Map.of(), 705 Map.of(), Set.of(), Map.of(reviewer.host().getCurrentUserDetails().userName(), Pattern.compile("proceed"))); 706 707 // Populate the projects repository 708 var localRepo = CheckableRepository.init(tempFolder.path(), author.getRepositoryType()); 709 var masterHash = localRepo.resolve("master").orElseThrow(); 710 localRepo.push(masterHash, author.getUrl(), "master", true); 711 712 // Make a change with a corresponding PR 713 var editHash = CheckableRepository.appendAndCommit(localRepo); 714 localRepo.push(editHash, author.getUrl(), "edit", true); 715 var pr = credentials.createPullRequest(author, "master", "edit", "This is a pull request"); 716 717 // Check the status 718 TestBotRunner.runPeriodicItems(checkBot); 719 720 // Verify that no checks have been run 721 var checks = pr.getChecks(editHash); 722 assertEquals(0, checks.size()); 723 724 // The PR should not yet be ready for review 725 assertFalse(pr.getLabels().contains("rfr")); 726 727 // Check the status again 728 var reviewerPr = reviewer.getPullRequest(pr.getId()); 729 reviewerPr.addComment("proceed"); 730 TestBotRunner.runPeriodicItems(checkBot); 731 732 // The PR should now be ready for review 733 assertTrue(pr.getLabels().contains("rfr")); 734 } 735 } 736 }