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.notify; 24 25 import org.openjdk.skara.email.*; 26 import org.openjdk.skara.forge.HostedRepository; 27 import org.openjdk.skara.json.*; 28 import org.openjdk.skara.mailinglist.MailingListServerFactory; 29 import org.openjdk.skara.storage.StorageBuilder; 30 import org.openjdk.skara.test.*; 31 import org.openjdk.skara.vcs.Tag; 32 33 import org.junit.jupiter.api.*; 34 35 import java.io.IOException; 36 import java.nio.charset.StandardCharsets; 37 import java.nio.file.*; 38 import java.time.Duration; 39 import java.util.*; 40 import java.util.regex.Pattern; 41 import java.util.stream.Collectors; 42 43 import static org.junit.jupiter.api.Assertions.*; 44 45 class UpdaterTests { 46 private List<Path> findJsonFiles(Path folder, String partialName) throws IOException { 47 return Files.walk(folder) 48 .filter(path -> path.toString().endsWith(".json")) 49 .filter(path -> path.toString().contains(partialName)) 50 .collect(Collectors.toList()); 51 } 52 53 private StorageBuilder<Tag> createTagStorage(HostedRepository repository) throws IOException { 54 return new StorageBuilder<Tag>("tags.txt") 55 .remoteRepository(repository, "refs/heads/history", "Duke", "duke@openjdk.java.net", "Updated tags"); 56 } 57 58 private StorageBuilder<ResolvedBranch> createBranchStorage(HostedRepository repository) throws IOException { 59 return new StorageBuilder<ResolvedBranch>("branches.txt") 60 .remoteRepository(repository, "refs/heads/history", "Duke", "duke@openjdk.java.net", "Updated branches"); 61 } 62 63 @Test 64 void testJsonUpdaterBranch(TestInfo testInfo) throws IOException { 65 try (var credentials = new HostCredentials(testInfo); 66 var tempFolder = new TemporaryDirectory()) { 67 var repo = credentials.getHostedRepository(); 68 var localRepoFolder = tempFolder.path().resolve("repo"); 69 var localRepo = CheckableRepository.init(localRepoFolder, repo.repositoryType()); 70 credentials.commitLock(localRepo); 71 localRepo.pushAll(repo.url()); 72 73 var tagStorage = createTagStorage(repo); 74 var branchStorage = createBranchStorage(repo); 75 var jsonFolder = tempFolder.path().resolve("json"); 76 Files.createDirectory(jsonFolder); 77 var storageFolder = tempFolder.path().resolve("storage"); 78 79 var updater = new JsonUpdater(jsonFolder, "12", "team"); 80 var notifyBot = new JNotifyBot(repo, storageFolder, Pattern.compile("master"), tagStorage, branchStorage, List.of(updater)); 81 82 TestBotRunner.runPeriodicItems(notifyBot); 83 assertEquals(List.of(), findJsonFiles(jsonFolder, "")); 84 85 var editHash = CheckableRepository.appendAndCommit(localRepo, "One more line", "12345678: Fixes"); 86 localRepo.push(editHash, repo.url(), "master"); 87 TestBotRunner.runPeriodicItems(notifyBot); 88 var jsonFiles = findJsonFiles(jsonFolder, ""); 89 assertEquals(1, jsonFiles.size()); 90 var jsonData = Files.readString(jsonFiles.get(0), StandardCharsets.UTF_8); 91 var json = JSON.parse(jsonData); 92 assertEquals(1, json.asArray().size()); 93 assertEquals(repo.webUrl(editHash).toString(), json.asArray().get(0).get("url").asString()); 94 assertEquals(List.of("12345678"), json.asArray().get(0).get("issue").asArray().stream() 95 .map(JSONValue::asString) 96 .collect(Collectors.toList())); 97 } 98 } 99 100 @Test 101 void testJsonUpdaterTag(TestInfo testInfo) throws IOException { 102 try (var credentials = new HostCredentials(testInfo); 103 var tempFolder = new TemporaryDirectory()) { 104 var repo = credentials.getHostedRepository(); 105 var localRepoFolder = tempFolder.path().resolve("repo"); 106 var localRepo = CheckableRepository.init(localRepoFolder, repo.repositoryType()); 107 credentials.commitLock(localRepo); 108 var masterHash = localRepo.resolve("master").orElseThrow(); 109 localRepo.tag(masterHash, "jdk-12+1", "Added tag 1", "Duke", "duke@openjdk.java.net"); 110 localRepo.pushAll(repo.url()); 111 112 var tagStorage = createTagStorage(repo); 113 var branchStorage = createBranchStorage(repo); 114 var jsonFolder = tempFolder.path().resolve("json"); 115 Files.createDirectory(jsonFolder); 116 var storageFolder =tempFolder.path().resolve("storage"); 117 118 var updater = new JsonUpdater(jsonFolder, "12", "team"); 119 var notifyBot = new JNotifyBot(repo, storageFolder, Pattern.compile("master"), tagStorage, branchStorage, List.of(updater)); 120 121 TestBotRunner.runPeriodicItems(notifyBot); 122 assertEquals(List.of(), findJsonFiles(jsonFolder, "")); 123 124 var editHash = CheckableRepository.appendAndCommit(localRepo, "Another line", "23456789: More fixes"); 125 localRepo.fetch(repo.url(), "history:history"); 126 localRepo.tag(editHash, "jdk-12+2", "Added tag 2", "Duke", "duke@openjdk.java.net"); 127 var editHash2 = CheckableRepository.appendAndCommit(localRepo, "Another line", "34567890: Even more fixes"); 128 localRepo.tag(editHash2, "jdk-12+4", "Added tag 3", "Duke", "duke@openjdk.java.net"); 129 localRepo.pushAll(repo.url()); 130 131 TestBotRunner.runPeriodicItems(notifyBot); 132 var jsonFiles = findJsonFiles(jsonFolder, ""); 133 assertEquals(3, jsonFiles.size()); 134 135 for (var file : jsonFiles) { 136 var jsonData = Files.readString(file, StandardCharsets.UTF_8); 137 var json = JSON.parse(jsonData); 138 139 if (json.asArray().get(0).contains("date")) { 140 assertEquals(2, json.asArray().size()); 141 assertEquals(List.of("23456789"), json.asArray().get(0).get("issue").asArray().stream() 142 .map(JSONValue::asString) 143 .collect(Collectors.toList())); 144 assertEquals(repo.webUrl(editHash).toString(), json.asArray().get(0).get("url").asString()); 145 assertEquals("team", json.asArray().get(0).get("build").asString()); 146 assertEquals(List.of("34567890"), json.asArray().get(1).get("issue").asArray().stream() 147 .map(JSONValue::asString) 148 .collect(Collectors.toList())); 149 assertEquals(repo.webUrl(editHash2).toString(), json.asArray().get(1).get("url").asString()); 150 assertEquals("team", json.asArray().get(1).get("build").asString()); 151 } else { 152 assertEquals(1, json.asArray().size()); 153 if (json.asArray().get(0).get("build").asString().equals("b02")) { 154 assertEquals(List.of("23456789"), json.asArray().get(0).get("issue").asArray().stream() 155 .map(JSONValue::asString) 156 .collect(Collectors.toList())); 157 } else { 158 assertEquals("b04", json.asArray().get(0).get("build").asString()); 159 assertEquals(List.of("34567890"), json.asArray().get(0).get("issue").asArray().stream() 160 .map(JSONValue::asString) 161 .collect(Collectors.toList())); 162 } 163 } 164 } 165 } 166 } 167 168 @Test 169 void testMailingList(TestInfo testInfo) throws IOException { 170 try (var listServer = new TestMailmanServer(); 171 var credentials = new HostCredentials(testInfo); 172 var tempFolder = new TemporaryDirectory()) { 173 var repo = credentials.getHostedRepository(); 174 var repoFolder = tempFolder.path().resolve("repo"); 175 var localRepo = CheckableRepository.init(repoFolder, repo.repositoryType()); 176 var masterHash = localRepo.resolve("master").orElseThrow(); 177 credentials.commitLock(localRepo); 178 localRepo.pushAll(repo.url()); 179 180 var listAddress = EmailAddress.parse(listServer.createList("test")); 181 var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); 182 var mailmanList = mailmanServer.getList(listAddress.address()); 183 var tagStorage = createTagStorage(repo); 184 var branchStorage = createBranchStorage(repo); 185 var storageFolder = tempFolder.path().resolve("storage"); 186 187 var sender = EmailAddress.from("duke", "duke@duke.duke"); 188 var updater = new MailingListUpdater(mailmanList, listAddress, sender, null, false, MailingListUpdater.Mode.ALL, 189 Map.of("extra1", "value1", "extra2", "value2"), Pattern.compile("none")); 190 var notifyBot = new JNotifyBot(repo, storageFolder, Pattern.compile("master"), tagStorage, branchStorage, List.of(updater)); 191 192 // No mail should be sent on the first run as there is no history 193 TestBotRunner.runPeriodicItems(notifyBot); 194 assertThrows(RuntimeException.class, () -> listServer.processIncoming(Duration.ofMillis(1))); 195 196 var editHash = CheckableRepository.appendAndCommit(localRepo, "Another line", "23456789: More fixes"); 197 localRepo.push(editHash, repo.url(), "master"); 198 TestBotRunner.runPeriodicItems(notifyBot); 199 listServer.processIncoming(); 200 201 var conversations = mailmanList.conversations(Duration.ofDays(1)); 202 var email = conversations.get(0).first(); 203 assertEquals(listAddress, email.sender()); 204 assertEquals(sender, email.author()); 205 assertEquals(email.recipients(), List.of(listAddress)); 206 assertTrue(email.subject().contains(": 23456789: More fixes")); 207 assertFalse(email.subject().contains("master")); 208 assertTrue(email.body().contains("Changeset: " + editHash.abbreviate())); 209 assertTrue(email.body().contains("23456789: More fixes")); 210 assertFalse(email.body().contains("Committer")); 211 assertFalse(email.body().contains(masterHash.abbreviate())); 212 assertTrue(email.hasHeader("extra1")); 213 assertEquals("value1", email.headerValue("extra1")); 214 assertTrue(email.hasHeader("extra2")); 215 assertEquals("value2", email.headerValue("extra2")); 216 } 217 } 218 219 @Test 220 void testMailingListMultiple(TestInfo testInfo) throws IOException { 221 try (var listServer = new TestMailmanServer(); 222 var credentials = new HostCredentials(testInfo); 223 var tempFolder = new TemporaryDirectory()) { 224 var repo = credentials.getHostedRepository(); 225 var repoFolder = tempFolder.path().resolve("repo"); 226 var localRepo = CheckableRepository.init(repoFolder, repo.repositoryType()); 227 var masterHash = localRepo.resolve("master").orElseThrow(); 228 credentials.commitLock(localRepo); 229 localRepo.pushAll(repo.url()); 230 231 var listAddress = EmailAddress.parse(listServer.createList("test")); 232 var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); 233 var mailmanList = mailmanServer.getList(listAddress.address()); 234 var tagStorage = createTagStorage(repo); 235 var branchStorage = createBranchStorage(repo); 236 var storageFolder = tempFolder.path().resolve("storage"); 237 238 var sender = EmailAddress.from("duke", "duke@duke.duke"); 239 var updater = new MailingListUpdater(mailmanList, listAddress, sender, null, false, 240 MailingListUpdater.Mode.ALL, Map.of(), Pattern.compile(".*")); 241 var notifyBot = new JNotifyBot(repo, storageFolder, Pattern.compile("master"), tagStorage, branchStorage, List.of(updater)); 242 243 // No mail should be sent on the first run as there is no history 244 TestBotRunner.runPeriodicItems(notifyBot); 245 assertThrows(RuntimeException.class, () -> listServer.processIncoming(Duration.ofMillis(1))); 246 247 var editHash1 = CheckableRepository.appendAndCommit(localRepo, "Another line", "23456789: More fixes", 248 "first_author", "first@author.example.com"); 249 localRepo.push(editHash1, repo.url(), "master"); 250 var editHash2 = CheckableRepository.appendAndCommit(localRepo, "Yet another line", "3456789A: Even more fixes", 251 "another_author", "another@author.example.com"); 252 localRepo.push(editHash2, repo.url(), "master"); 253 254 TestBotRunner.runPeriodicItems(notifyBot); 255 listServer.processIncoming(); 256 257 var conversations = mailmanList.conversations(Duration.ofDays(1)); 258 var email = conversations.get(0).first(); 259 assertEquals(listAddress, email.sender()); 260 assertEquals(EmailAddress.from("another_author", "another@author.example.com"), email.author()); 261 assertEquals(email.recipients(), List.of(listAddress)); 262 assertTrue(email.subject().contains(": 2 new changesets")); 263 assertFalse(email.subject().contains("master")); 264 assertTrue(email.body().contains("Changeset: " + editHash1.abbreviate())); 265 assertTrue(email.body().contains("23456789: More fixes")); 266 assertTrue(email.body().contains("Changeset: " + editHash2.abbreviate())); 267 assertTrue(email.body().contains("3456789A: Even more fixes")); 268 assertFalse(email.body().contains(masterHash.abbreviate())); 269 } 270 } 271 272 @Test 273 void testMailingListSponsored(TestInfo testInfo) throws IOException { 274 try (var listServer = new TestMailmanServer(); 275 var credentials = new HostCredentials(testInfo); 276 var tempFolder = new TemporaryDirectory()) { 277 var repo = credentials.getHostedRepository(); 278 var repoFolder = tempFolder.path().resolve("repo"); 279 var localRepo = CheckableRepository.init(repoFolder, repo.repositoryType()); 280 var masterHash = localRepo.resolve("master").orElseThrow(); 281 credentials.commitLock(localRepo); 282 localRepo.pushAll(repo.url()); 283 284 var listAddress = EmailAddress.parse(listServer.createList("test")); 285 var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); 286 var mailmanList = mailmanServer.getList(listAddress.address()); 287 var tagStorage = createTagStorage(repo); 288 var branchStorage = createBranchStorage(repo); 289 var storageFolder = tempFolder.path().resolve("storage"); 290 291 var sender = EmailAddress.from("duke", "duke@duke.duke"); 292 var updater = new MailingListUpdater(mailmanList, listAddress, sender, null, false, 293 MailingListUpdater.Mode.ALL, Map.of(), Pattern.compile(".*")); 294 var notifyBot = new JNotifyBot(repo, storageFolder, Pattern.compile("master"), tagStorage, branchStorage, List.of(updater)); 295 296 // No mail should be sent on the first run as there is no history 297 TestBotRunner.runPeriodicItems(notifyBot); 298 assertThrows(RuntimeException.class, () -> listServer.processIncoming(Duration.ofMillis(1))); 299 300 var editHash = CheckableRepository.appendAndCommit(localRepo, "Another line", "23456789: More fixes", 301 "author", "author@test.test", 302 "committer", "committer@test.test"); 303 localRepo.push(editHash, repo.url(), "master"); 304 TestBotRunner.runPeriodicItems(notifyBot); 305 listServer.processIncoming(); 306 307 var conversations = mailmanList.conversations(Duration.ofDays(1)); 308 var email = conversations.get(0).first(); 309 assertEquals(listAddress, email.sender()); 310 assertEquals(EmailAddress.from("committer", "committer@test.test"), email.author()); 311 assertEquals(email.recipients(), List.of(listAddress)); 312 assertTrue(email.body().contains("Changeset: " + editHash.abbreviate())); 313 assertTrue(email.body().contains("23456789: More fixes")); 314 assertTrue(email.body().contains("Author: author <author@test.test>")); 315 assertTrue(email.body().contains("Committer: committer <committer@test.test>")); 316 assertFalse(email.body().contains(masterHash.abbreviate())); 317 } 318 } 319 320 @Test 321 void testMailingListMultipleBranches(TestInfo testInfo) throws IOException { 322 try (var listServer = new TestMailmanServer(); 323 var credentials = new HostCredentials(testInfo); 324 var tempFolder = new TemporaryDirectory()) { 325 var repo = credentials.getHostedRepository(); 326 var repoFolder = tempFolder.path().resolve("repo"); 327 var localRepo = CheckableRepository.init(repoFolder, repo.repositoryType()); 328 var masterHash = localRepo.resolve("master").orElseThrow(); 329 credentials.commitLock(localRepo); 330 var branch = localRepo.branch(masterHash, "another"); 331 localRepo.pushAll(repo.url()); 332 333 var listAddress = EmailAddress.parse(listServer.createList("test")); 334 var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); 335 var mailmanList = mailmanServer.getList(listAddress.address()); 336 var tagStorage = createTagStorage(repo); 337 var branchStorage = createBranchStorage(repo); 338 var storageFolder = tempFolder.path().resolve("storage"); 339 340 var sender = EmailAddress.from("duke", "duke@duke.duke"); 341 var author = EmailAddress.from("author", "author@duke.duke"); 342 var updater = new MailingListUpdater(mailmanList, listAddress, sender, author, true, 343 MailingListUpdater.Mode.ALL, Map.of(), Pattern.compile(".*")); 344 var notifyBot = new JNotifyBot(repo, storageFolder, Pattern.compile("master|another"), tagStorage, branchStorage, List.of(updater)); 345 346 // No mail should be sent on the first run as there is no history 347 TestBotRunner.runPeriodicItems(notifyBot); 348 assertThrows(RuntimeException.class, () -> listServer.processIncoming(Duration.ofMillis(1))); 349 350 var editHash1 = CheckableRepository.appendAndCommit(localRepo, "Another line", "23456789: More fixes"); 351 localRepo.push(editHash1, repo.url(), "master"); 352 var editHash2 = CheckableRepository.appendAndCommit(localRepo, "Yet another line", "3456789A: Even more fixes"); 353 localRepo.push(editHash2, repo.url(), "master"); 354 355 TestBotRunner.runPeriodicItems(notifyBot); 356 listServer.processIncoming(); 357 358 var conversations = mailmanList.conversations(Duration.ofDays(1)); 359 var email = conversations.get(0).first(); 360 assertEquals(listAddress, email.sender()); 361 assertEquals(author, email.author()); 362 assertEquals(email.recipients(), List.of(listAddress)); 363 assertFalse(email.subject().contains("another")); 364 assertTrue(email.subject().contains(": master: 2 new changesets")); 365 assertTrue(email.body().contains("Changeset: " + editHash1.abbreviate())); 366 assertTrue(email.body().contains("23456789: More fixes")); 367 assertTrue(email.body().contains("Changeset: " + editHash2.abbreviate())); 368 assertTrue(email.body().contains("3456789A: Even more fixes")); 369 assertFalse(email.body().contains(masterHash.abbreviate())); 370 assertFalse(email.body().contains("456789AB: Yet more fixes")); 371 372 localRepo.checkout(branch, true); 373 var editHash3 = CheckableRepository.appendAndCommit(localRepo, "Another branch", "456789AB: Yet more fixes"); 374 localRepo.push(editHash3, repo.url(), "another"); 375 376 TestBotRunner.runPeriodicItems(notifyBot); 377 listServer.processIncoming(); 378 379 conversations = mailmanList.conversations(Duration.ofDays(1)); 380 conversations.sort(Comparator.comparing(conversation -> conversation.first().subject())); 381 email = conversations.get(0).first(); 382 assertEquals(author, email.author()); 383 assertEquals(listAddress, email.sender()); 384 assertEquals(email.recipients(), List.of(listAddress)); 385 assertTrue(email.subject().contains(": another: 456789AB: Yet more fixes")); 386 assertFalse(email.subject().contains("master")); 387 assertTrue(email.body().contains("Changeset: " + editHash3.abbreviate())); 388 assertTrue(email.body().contains("456789AB: Yet more fixes")); 389 assertFalse(email.body().contains("Changeset: " + editHash2.abbreviate())); 390 assertFalse(email.body().contains("3456789A: Even more fixes")); 391 } 392 } 393 394 @Test 395 void testMailingListPROnly(TestInfo testInfo) throws IOException { 396 try (var listServer = new TestMailmanServer(); 397 var credentials = new HostCredentials(testInfo); 398 var tempFolder = new TemporaryDirectory()) { 399 var repo = credentials.getHostedRepository(); 400 var repoFolder = tempFolder.path().resolve("repo"); 401 var localRepo = CheckableRepository.init(repoFolder, repo.repositoryType()); 402 var masterHash = localRepo.resolve("master").orElseThrow(); 403 credentials.commitLock(localRepo); 404 localRepo.pushAll(repo.url()); 405 406 var listAddress = EmailAddress.parse(listServer.createList("test")); 407 var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); 408 var mailmanList = mailmanServer.getList(listAddress.address()); 409 var tagStorage = createTagStorage(repo); 410 var branchStorage = createBranchStorage(repo); 411 var storageFolder = tempFolder.path().resolve("storage"); 412 413 var sender = EmailAddress.from("duke", "duke@duke.duke"); 414 var author = EmailAddress.from("author", "author@duke.duke"); 415 var updater = new MailingListUpdater(mailmanList, listAddress, sender, author, false, 416 MailingListUpdater.Mode.PR_ONLY, Map.of("extra1", "value1"), 417 Pattern.compile(".*")); 418 var notifyBot = new JNotifyBot(repo, storageFolder, Pattern.compile("master"), tagStorage, branchStorage, List.of(updater)); 419 420 // No mail should be sent on the first run as there is no history 421 TestBotRunner.runPeriodicItems(notifyBot); 422 assertThrows(RuntimeException.class, () -> listServer.processIncoming(Duration.ofMillis(1))); 423 424 var editHash = CheckableRepository.appendAndCommit(localRepo, "Another line", "23456789: More fixes"); 425 localRepo.push(editHash, repo.url(), "edit"); 426 var pr = credentials.createPullRequest(repo, "master", "edit", "RFR: My PR"); 427 428 // Create a potentially conflicting one 429 var otherHash = CheckableRepository.appendAndCommit(localRepo, "Another line", "23456789: More fixes"); 430 localRepo.push(otherHash, repo.url(), "other"); 431 var otherPr = credentials.createPullRequest(repo, "master", "other", "RFR: My other PR"); 432 433 // PR hasn't been integrated yet, so there should be no mail 434 TestBotRunner.runPeriodicItems(notifyBot); 435 assertThrows(RuntimeException.class, () -> listServer.processIncoming(Duration.ofMillis(1))); 436 437 // Simulate an RFR email 438 var rfr = Email.create(sender, "RFR: My PR", "PR: " + pr.webUrl().toString()) 439 .recipient(listAddress) 440 .build(); 441 mailmanList.post(rfr); 442 listServer.processIncoming(); 443 444 // And an integration 445 pr.addComment("Pushed as commit " + editHash.hex() + "."); 446 localRepo.push(editHash, repo.url(), "master"); 447 TestBotRunner.runPeriodicItems(notifyBot); 448 listServer.processIncoming(); 449 450 var conversations = mailmanList.conversations(Duration.ofDays(1)); 451 assertEquals(1, conversations.size()); 452 var first = conversations.get(0).first(); 453 var email = conversations.get(0).replies(first).get(0); 454 assertEquals(listAddress, email.sender()); 455 assertEquals(author, email.author()); 456 assertEquals(email.recipients(), List.of(listAddress)); 457 assertEquals("Re: [Integrated] RFR: My PR", email.subject()); 458 assertFalse(email.subject().contains("master")); 459 assertTrue(email.body().contains("Changeset: " + editHash.abbreviate())); 460 assertTrue(email.body().contains("23456789: More fixes")); 461 assertFalse(email.body().contains("Committer")); 462 assertFalse(email.body().contains(masterHash.abbreviate())); 463 assertTrue(email.hasHeader("extra1")); 464 assertEquals("value1", email.headerValue("extra1")); 465 466 // Now push the other one without a matching PR - PR_ONLY will not generate a mail 467 localRepo.push(otherHash, repo.url(), "master"); 468 TestBotRunner.runPeriodicItems(notifyBot); 469 assertThrows(RuntimeException.class, () -> listServer.processIncoming(Duration.ofSeconds(1))); 470 } 471 } 472 473 @Test 474 void testMailingListPR(TestInfo testInfo) throws IOException { 475 try (var listServer = new TestMailmanServer(); 476 var credentials = new HostCredentials(testInfo); 477 var tempFolder = new TemporaryDirectory()) { 478 var repo = credentials.getHostedRepository(); 479 var repoFolder = tempFolder.path().resolve("repo"); 480 var localRepo = CheckableRepository.init(repoFolder, repo.repositoryType()); 481 var masterHash = localRepo.resolve("master").orElseThrow(); 482 credentials.commitLock(localRepo); 483 localRepo.pushAll(repo.url()); 484 485 var listAddress = EmailAddress.parse(listServer.createList("test")); 486 var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); 487 var mailmanList = mailmanServer.getList(listAddress.address()); 488 var tagStorage = createTagStorage(repo); 489 var branchStorage = createBranchStorage(repo); 490 var storageFolder = tempFolder.path().resolve("storage"); 491 492 var sender = EmailAddress.from("duke", "duke@duke.duke"); 493 var updater = new MailingListUpdater(mailmanList, listAddress, sender, null, false, 494 MailingListUpdater.Mode.PR, Map.of(), Pattern.compile(".*")); 495 var notifyBot = new JNotifyBot(repo, storageFolder, Pattern.compile("master"), tagStorage, branchStorage, List.of(updater)); 496 497 // No mail should be sent on the first run as there is no history 498 TestBotRunner.runPeriodicItems(notifyBot); 499 assertThrows(RuntimeException.class, () -> listServer.processIncoming(Duration.ofMillis(1))); 500 501 var editHash = CheckableRepository.appendAndCommit(localRepo, "Another line", "23456789: More fixes"); 502 localRepo.push(editHash, repo.url(), "edit"); 503 var pr = credentials.createPullRequest(repo, "master", "edit", "RFR: My PR"); 504 505 // Create a potentially conflicting one 506 var otherHash = CheckableRepository.appendAndCommit(localRepo, "Another line", "23456789: More fixes"); 507 localRepo.push(otherHash, repo.url(), "other"); 508 var otherPr = credentials.createPullRequest(repo, "master", "other", "RFR: My other PR"); 509 510 // PR hasn't been integrated yet, so there should be no mail 511 TestBotRunner.runPeriodicItems(notifyBot); 512 assertThrows(RuntimeException.class, () -> listServer.processIncoming(Duration.ofMillis(1))); 513 514 // Simulate an RFR email 515 var rfr = Email.create("RFR: My PR", "PR:\n" + pr.webUrl().toString()) 516 .author(EmailAddress.from("duke", "duke@duke.duke")) 517 .recipient(listAddress) 518 .build(); 519 mailmanList.post(rfr); 520 listServer.processIncoming(); 521 522 // And an integration 523 pr.addComment("Pushed as commit " + editHash.hex() + "."); 524 localRepo.push(editHash, repo.url(), "master"); 525 526 // Push the other one without a matching PR 527 localRepo.push(otherHash, repo.url(), "master"); 528 529 TestBotRunner.runPeriodicItems(notifyBot); 530 listServer.processIncoming(); 531 listServer.processIncoming(); 532 533 var conversations = mailmanList.conversations(Duration.ofDays(1)); 534 conversations.sort(Comparator.comparing(conversation -> conversation.first().subject())); 535 assertEquals(2, conversations.size()); 536 537 var prConversation = conversations.get(0); 538 var pushConversation = conversations.get(1); 539 540 var prEmail = prConversation.replies(prConversation.first()).get(0); 541 assertEquals(listAddress, prEmail.sender()); 542 assertEquals(EmailAddress.from("testauthor", "ta@none.none"), prEmail.author()); 543 assertEquals(prEmail.recipients(), List.of(listAddress)); 544 assertEquals("Re: [Integrated] RFR: My PR", prEmail.subject()); 545 assertFalse(prEmail.subject().contains("master")); 546 assertTrue(prEmail.body().contains("Changeset: " + editHash.abbreviate())); 547 assertTrue(prEmail.body().contains("23456789: More fixes")); 548 assertFalse(prEmail.body().contains("Committer")); 549 assertFalse(prEmail.body().contains(masterHash.abbreviate())); 550 551 var pushEmail = pushConversation.first(); 552 assertEquals(listAddress, pushEmail.sender()); 553 assertEquals(EmailAddress.from("testauthor", "ta@none.none"), pushEmail.author()); 554 assertEquals(pushEmail.recipients(), List.of(listAddress)); 555 assertTrue(pushEmail.subject().contains("23456789: More fixes")); 556 } 557 } 558 559 @Test 560 void testMailinglistTag(TestInfo testInfo) throws IOException { 561 try (var credentials = new HostCredentials(testInfo); 562 var tempFolder = new TemporaryDirectory(); 563 var listServer = new TestMailmanServer()) { 564 var repo = credentials.getHostedRepository(); 565 var localRepoFolder = tempFolder.path().resolve("repo"); 566 var localRepo = CheckableRepository.init(localRepoFolder, repo.repositoryType()); 567 credentials.commitLock(localRepo); 568 var masterHash = localRepo.resolve("master").orElseThrow(); 569 localRepo.tag(masterHash, "jdk-12+1", "Added tag 1", "Duke Tagger", "tagger@openjdk.java.net"); 570 localRepo.pushAll(repo.url()); 571 572 var listAddress = EmailAddress.parse(listServer.createList("test")); 573 var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); 574 var mailmanList = mailmanServer.getList(listAddress.address()); 575 var tagStorage = createTagStorage(repo); 576 var branchStorage = createBranchStorage(repo); 577 var storageFolder = tempFolder.path().resolve("storage"); 578 579 var sender = EmailAddress.from("duke", "duke@duke.duke"); 580 var updater = new MailingListUpdater(mailmanList, listAddress, sender, null, false, MailingListUpdater.Mode.ALL, 581 Map.of("extra1", "value1", "extra2", "value2"), 582 Pattern.compile(".*")); 583 var prOnlyUpdater = new MailingListUpdater(mailmanList, listAddress, sender, null, false, 584 MailingListUpdater.Mode.PR_ONLY, Map.of(), Pattern.compile(".*")); 585 var notifyBot = new JNotifyBot(repo, storageFolder, Pattern.compile("master"), tagStorage, branchStorage, 586 List.of(updater, prOnlyUpdater)); 587 588 // No mail should be sent on the first run as there is no history 589 TestBotRunner.runPeriodicItems(notifyBot); 590 assertThrows(RuntimeException.class, () -> listServer.processIncoming(Duration.ofMillis(1))); 591 592 var editHash = CheckableRepository.appendAndCommit(localRepo, "Another line", "23456789: More fixes"); 593 localRepo.fetch(repo.url(), "history:history"); 594 localRepo.tag(editHash, "jdk-12+2", "Added tag 2", "Duke Tagger", "tagger@openjdk.java.net"); 595 CheckableRepository.appendAndCommit(localRepo, "Another line 1", "34567890: Even more fixes"); 596 CheckableRepository.appendAndCommit(localRepo, "Another line 2", "45678901: Yet even more fixes"); 597 var editHash2 = CheckableRepository.appendAndCommit(localRepo, "Another line 3", "56789012: Still even more fixes"); 598 localRepo.tag(editHash2, "jdk-12+4", "Added tag 3", "Duke Tagger", "tagger@openjdk.java.net"); 599 CheckableRepository.appendAndCommit(localRepo, "Another line 4", "67890123: Brand new fixes"); 600 var editHash3 = CheckableRepository.appendAndCommit(localRepo, "Another line 5", "78901234: More brand new fixes"); 601 localRepo.tag(editHash3, "jdk-13+0", "Added tag 4", "Duke Tagger", "tagger@openjdk.java.net"); 602 localRepo.pushAll(repo.url()); 603 604 TestBotRunner.runPeriodicItems(notifyBot); 605 listServer.processIncoming(); 606 listServer.processIncoming(); 607 listServer.processIncoming(); 608 listServer.processIncoming(); 609 610 var conversations = mailmanList.conversations(Duration.ofDays(1)); 611 assertEquals(4, conversations.size()); 612 613 for (var conversation : conversations) { 614 var email = conversation.first(); 615 if (email.subject().equals("git: test: Added tag jdk-12+2 for changeset " + editHash.abbreviate())) { 616 assertTrue(email.body().contains("23456789: More fixes")); 617 assertFalse(email.body().contains("34567890: Even more fixes")); 618 assertFalse(email.body().contains("45678901: Yet even more fixes")); 619 assertFalse(email.body().contains("56789012: Still even more fixes")); 620 assertFalse(email.body().contains("67890123: Brand new fixes")); 621 assertFalse(email.body().contains("78901234: More brand new fixes")); 622 assertEquals(EmailAddress.from("Duke Tagger", "tagger@openjdk.java.net"), email.author()); 623 } else if (email.subject().equals("git: test: Added tag jdk-12+4 for changeset " + editHash2.abbreviate())) { 624 assertFalse(email.body().contains("23456789: More fixes")); 625 assertTrue(email.body().contains("34567890: Even more fixes")); 626 assertTrue(email.body().contains("45678901: Yet even more fixes")); 627 assertTrue(email.body().contains("56789012: Still even more fixes")); 628 assertFalse(email.body().contains("67890123: Brand new fixes")); 629 assertFalse(email.body().contains("78901234: More brand new fixes")); 630 assertEquals(EmailAddress.from("Duke Tagger", "tagger@openjdk.java.net"), email.author()); 631 } else if (email.subject().equals("git: test: Added tag jdk-13+0 for changeset " + editHash3.abbreviate())) { 632 assertFalse(email.body().contains("23456789: More fixes")); 633 assertFalse(email.body().contains("34567890: Even more fixes")); 634 assertFalse(email.body().contains("45678901: Yet even more fixes")); 635 assertFalse(email.body().contains("56789012: Still even more fixes")); 636 assertFalse(email.body().contains("67890123: Brand new fixes")); 637 assertTrue(email.body().contains("78901234: More brand new fixes")); 638 assertEquals(EmailAddress.from("Duke Tagger", "tagger@openjdk.java.net"), email.author()); 639 } else if (email.subject().equals("git: test: 6 new changesets")) { 640 assertTrue(email.body().contains("23456789: More fixes")); 641 assertTrue(email.body().contains("34567890: Even more fixes")); 642 assertTrue(email.body().contains("45678901: Yet even more fixes")); 643 assertTrue(email.body().contains("56789012: Still even more fixes")); 644 assertTrue(email.body().contains("67890123: Brand new fixes")); 645 assertTrue(email.body().contains("78901234: More brand new fixes")); 646 assertEquals(EmailAddress.from("testauthor", "ta@none.none"), email.author()); 647 } else { 648 fail("Mismatched subject: " + email.subject()); 649 } 650 assertTrue(email.hasHeader("extra1")); 651 assertEquals("value1", email.headerValue("extra1")); 652 assertTrue(email.hasHeader("extra2")); 653 assertEquals("value2", email.headerValue("extra2")); 654 } 655 } 656 } 657 658 @Test 659 void testMailingListBranch(TestInfo testInfo) throws IOException { 660 try (var listServer = new TestMailmanServer(); 661 var credentials = new HostCredentials(testInfo); 662 var tempFolder = new TemporaryDirectory()) { 663 var repo = credentials.getHostedRepository(); 664 var repoFolder = tempFolder.path().resolve("repo"); 665 var localRepo = CheckableRepository.init(repoFolder, repo.repositoryType()); 666 var masterHash = localRepo.resolve("master").orElseThrow(); 667 credentials.commitLock(localRepo); 668 localRepo.pushAll(repo.url()); 669 670 var listAddress = EmailAddress.parse(listServer.createList("test")); 671 var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); 672 var mailmanList = mailmanServer.getList(listAddress.address()); 673 var tagStorage = createTagStorage(repo); 674 var branchStorage = createBranchStorage(repo); 675 var storageFolder = tempFolder.path().resolve("storage"); 676 677 var sender = EmailAddress.from("duke", "duke@duke.duke"); 678 var updater = new MailingListUpdater(mailmanList, listAddress, sender, null, false, MailingListUpdater.Mode.ALL, 679 Map.of("extra1", "value1", "extra2", "value2"), 680 Pattern.compile(".*")); 681 var notifyBot = new JNotifyBot(repo, storageFolder, Pattern.compile("master|newbranch."), tagStorage, branchStorage, List.of(updater)); 682 683 // No mail should be sent on the first run as there is no history 684 TestBotRunner.runPeriodicItems(notifyBot); 685 assertThrows(RuntimeException.class, () -> listServer.processIncoming(Duration.ofMillis(1))); 686 687 CheckableRepository.appendAndCommit(localRepo, "Another line", "12345678: Some fixes"); 688 var editHash = CheckableRepository.appendAndCommit(localRepo, "Another line", "23456789: More fixes"); 689 localRepo.push(editHash, repo.url(), "newbranch1"); 690 TestBotRunner.runPeriodicItems(notifyBot); 691 listServer.processIncoming(); 692 693 var conversations = mailmanList.conversations(Duration.ofDays(1)); 694 var email = conversations.get(0).first(); 695 assertEquals(listAddress, email.sender()); 696 assertEquals(EmailAddress.from("testauthor", "ta@none.none"), email.author()); 697 assertEquals(email.recipients(), List.of(listAddress)); 698 assertEquals("git: test: created branch newbranch1 based on the branch master containing 2 unique commits", email.subject()); 699 assertTrue(email.body().contains("12345678: Some fixes")); 700 assertTrue(email.hasHeader("extra1")); 701 assertEquals("value1", email.headerValue("extra1")); 702 assertTrue(email.hasHeader("extra2")); 703 assertEquals("value2", email.headerValue("extra2")); 704 705 TestBotRunner.runPeriodicItems(notifyBot); 706 assertThrows(RuntimeException.class, () -> listServer.processIncoming(Duration.ofMillis(1))); 707 708 localRepo.push(editHash, repo.url(), "newbranch2"); 709 TestBotRunner.runPeriodicItems(notifyBot); 710 listServer.processIncoming(); 711 712 var newConversation = mailmanList.conversations(Duration.ofDays(1)).stream() 713 .filter(c -> !c.equals(conversations.get(0))) 714 .findFirst().orElseThrow(); 715 email = newConversation.first(); 716 assertEquals(listAddress, email.sender()); 717 assertEquals(sender, email.author()); 718 assertEquals(email.recipients(), List.of(listAddress)); 719 assertEquals("git: test: created branch newbranch2 based on the branch newbranch1 containing 0 unique commits", email.subject()); 720 assertEquals("The new branch newbranch2 is currently identical to the newbranch1 branch.", email.body()); 721 } 722 } 723 724 @Test 725 void testIssue(TestInfo testInfo) throws IOException { 726 try (var credentials = new HostCredentials(testInfo); 727 var tempFolder = new TemporaryDirectory()) { 728 var repo = credentials.getHostedRepository(); 729 var repoFolder = tempFolder.path().resolve("repo"); 730 var localRepo = CheckableRepository.init(repoFolder, repo.repositoryType()); 731 credentials.commitLock(localRepo); 732 localRepo.pushAll(repo.url()); 733 734 var tagStorage = createTagStorage(repo); 735 var branchStorage = createBranchStorage(repo); 736 var storageFolder = tempFolder.path().resolve("storage"); 737 738 var issueProject = credentials.getIssueProject(); 739 var updater = new IssueUpdater(issueProject); 740 var notifyBot = new JNotifyBot(repo, storageFolder, Pattern.compile("master"), tagStorage, branchStorage, List.of(updater)); 741 742 // Initialize history 743 TestBotRunner.runPeriodicItems(notifyBot); 744 745 // Create an issue and commit a fix 746 var issue = issueProject.createIssue("This is an issue", List.of("Indeed")); 747 var editHash = CheckableRepository.appendAndCommit(localRepo, "Another line", issue.id() + ": Fix that issue"); 748 localRepo.push(editHash, repo.url(), "master"); 749 TestBotRunner.runPeriodicItems(notifyBot); 750 751 // The changeset should be reflected in a comment 752 var comments = issue.comments(); 753 assertEquals(1, comments.size()); 754 var comment = comments.get(0); 755 assertTrue(comment.body().contains(editHash.abbreviate())); 756 757 // There should be no open issues 758 assertEquals(0, issueProject.issues().size()); 759 } 760 } 761 }