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.mlbridge;
 24 
 25 import org.openjdk.skara.email.*;
 26 import org.openjdk.skara.forge.PullRequest;
 27 import org.openjdk.skara.network.URIBuilder;
 28 import org.openjdk.skara.mailinglist.*;
 29 import org.openjdk.skara.test.*;
 30 
 31 import org.junit.jupiter.api.*;
 32 
 33 import java.io.IOException;
 34 import java.nio.file.Path;
 35 import java.time.Duration;
 36 import java.util.*;
 37 
 38 import static org.junit.jupiter.api.Assertions.*;
 39 
 40 class MailingListArchiveReaderBotTests {
 41     private void addReply(Conversation conversation, MailingList mailingList, PullRequest pr) {
 42         var first = conversation.first();
 43 
 44         var reply = "Looks good";
 45         var references = first.id().toString();
 46         var email = Email.create(EmailAddress.from("Commenter", "c@test.test"), "Re: RFR: " + pr.title(), reply)
 47                          .recipient(first.author())
 48                          .id(EmailAddress.from(UUID.randomUUID() + "@id.id"))
 49                          .header("In-Reply-To", first.id().toString())
 50                          .header("References", references)
 51                          .build();
 52         mailingList.post(email);
 53     }
 54 
 55     @Test
 56     void simpleArchive(TestInfo testInfo) throws IOException {
 57         try (var credentials = new HostCredentials(testInfo);
 58              var tempFolder = new TemporaryDirectory();
 59              var listServer = new TestMailmanServer()) {
 60             var author = credentials.getHostedRepository();
 61             var archive = credentials.getHostedRepository();
 62             var ignored = credentials.getHostedRepository();
 63             var listAddress = EmailAddress.parse(listServer.createList("test"));
 64             var censusBuilder = credentials.getCensusBuilder()
 65                                            .addAuthor(author.forge().currentUser().id());
 66             var from = EmailAddress.from("test", "test@test.mail");
 67             var mlBot = new MailingListBridgeBot(from, author, archive, censusBuilder.build(), "master",
 68                                                  listAddress,
 69                                                  Set.of(ignored.forge().currentUser().userName()),
 70                                                  Set.of(),
 71                                                  listServer.getArchive(), listServer.getSMTP(),
 72                                                  archive, "webrev", Path.of("test"),
 73                                                  URIBuilder.base("http://www.test.test/").build(),
 74                                                  Set.of(), Map.of(),
 75                                                  URIBuilder.base("http://issues.test/browse/").build(),
 76                                                  Map.of(), Duration.ZERO);
 77 
 78             // The mailing list as well
 79             var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(),
 80                                                                              Duration.ZERO);
 81             var mailmanList = mailmanServer.getList(listAddress.address());
 82             var readerBot = new MailingListArchiveReaderBot(from, Set.of(mailmanList), Set.of(archive));
 83 
 84             // Populate the projects repository
 85             var localRepo = CheckableRepository.init(tempFolder.path(), author.repositoryType());
 86             var masterHash = localRepo.resolve("master").orElseThrow();
 87             localRepo.push(masterHash, author.url(), "master", true);
 88             localRepo.push(masterHash, archive.url(), "webrev", true);
 89 
 90             // Make a change with a corresponding PR
 91             var editHash = CheckableRepository.appendAndCommit(localRepo, "A simple change",
 92                                                                "Change msg\n\nWith several lines");
 93             localRepo.push(editHash, author.url(), "edit", true);
 94             var pr = credentials.createPullRequest(archive, "master", "edit", "This is a pull request");
 95             pr.setBody("This should now be ready");
 96 
 97             // Run an archive pass
 98             TestBotRunner.runPeriodicItems(mlBot);
 99             listServer.processIncoming();
100 
101             // Run an archive pass
102             TestBotRunner.runPeriodicItems(readerBot);
103             TestBotRunner.runPeriodicItems(readerBot);
104 
105             // Post a reply directly to the list
106             var conversations = mailmanList.conversations(Duration.ofDays(1));
107             assertEquals(1, conversations.size());
108             addReply(conversations.get(0), mailmanList, pr);
109             listServer.processIncoming();
110 
111             // Another archive reader pass - has to be done twice
112             TestBotRunner.runPeriodicItems(readerBot);
113             TestBotRunner.runPeriodicItems(readerBot);
114 
115             // The bridge should now have processed the reply
116             var updated = pr.comments();
117             assertEquals(2, updated.size());
118             assertTrue(updated.get(1).body().contains("Mailing list message from"));
119             assertTrue(updated.get(1).body().contains("[Commenter](mailto:c@test.test)"));
120             assertTrue(updated.get(1).body().contains("[test](mailto:test@" + listAddress.domain() + ")"));
121         }
122     }
123 
124     @Test
125     void rememberBridged(TestInfo testInfo) throws IOException {
126         try (var credentials = new HostCredentials(testInfo);
127              var tempFolder = new TemporaryDirectory();
128              var listServer = new TestMailmanServer()) {
129             var author = credentials.getHostedRepository();
130             var archive = credentials.getHostedRepository();
131             var ignored = credentials.getHostedRepository();
132             var listAddress = EmailAddress.parse(listServer.createList("test"));
133             var censusBuilder = credentials.getCensusBuilder()
134                                            .addAuthor(author.forge().currentUser().id());
135             var from = EmailAddress.from("test", "test@test.mail");
136             var mlBot = new MailingListBridgeBot(from, author, archive, censusBuilder.build(), "master",
137                                                  listAddress,
138                                                  Set.of(ignored.forge().currentUser().userName()),
139                                                  Set.of(),
140                                                  listServer.getArchive(), listServer.getSMTP(),
141                                                  archive, "webrev", Path.of("test"),
142                                                  URIBuilder.base("http://www.test.test/").build(),
143                                                  Set.of(), Map.of(),
144                                                  URIBuilder.base("http://issues.test/browse/").build(),
145                                                  Map.of(), Duration.ZERO);
146 
147             // The mailing list as well
148             var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(),
149                                                                              Duration.ZERO);
150             var mailmanList = mailmanServer.getList(listAddress.address());
151             var readerBot = new MailingListArchiveReaderBot(from, Set.of(mailmanList), Set.of(archive));
152 
153             // Populate the projects repository
154             var localRepo = CheckableRepository.init(tempFolder.path(), author.repositoryType());
155             var masterHash = localRepo.resolve("master").orElseThrow();
156             localRepo.push(masterHash, author.url(), "master", true);
157             localRepo.push(masterHash, archive.url(), "webrev", true);
158 
159             // Make a change with a corresponding PR
160             var editHash = CheckableRepository.appendAndCommit(localRepo, "A simple change",
161                                                                "Change msg\n\nWith several lines");
162             localRepo.push(editHash, author.url(), "edit", true);
163             var pr = credentials.createPullRequest(archive, "master", "edit", "This is a pull request");
164             pr.setBody("This should now be ready");
165 
166             // Run an archive pass
167             TestBotRunner.runPeriodicItems(mlBot);
168             listServer.processIncoming();
169 
170             // Post a reply directly to the list
171             var conversations = mailmanList.conversations(Duration.ofDays(1));
172             assertEquals(1, conversations.size());
173             addReply(conversations.get(0), mailmanList, pr);
174             listServer.processIncoming();
175 
176             // Another archive reader pass - has to be done twice
177             TestBotRunner.runPeriodicItems(readerBot);
178             TestBotRunner.runPeriodicItems(readerBot);
179 
180             // The bridge should now have processed the reply
181             var updated = pr.comments();
182             assertEquals(2, updated.size());
183 
184             var newReaderBot = new MailingListArchiveReaderBot(from, Set.of(mailmanList), Set.of(archive));
185             TestBotRunner.runPeriodicItems(newReaderBot);
186             TestBotRunner.runPeriodicItems(newReaderBot);
187 
188             // The new bridge should not have made duplicate posts
189             var notUpdated = pr.comments();
190             assertEquals(2, notUpdated.size());
191         }
192     }
193 }