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, "master",
 68                                                  censusBuilder.build(), "master",
 69                                                  listAddress,
 70                                                  Set.of(ignored.forge().currentUser().userName()),
 71                                                  Set.of(),
 72                                                  listServer.getArchive(), listServer.getSMTP(),
 73                                                  archive, "webrev", Path.of("test"),
 74                                                  URIBuilder.base("http://www.test.test/").build(),
 75                                                  Set.of(), Map.of(),
 76                                                  URIBuilder.base("http://issues.test/browse/").build(),
 77                                                  Map.of(), Duration.ZERO);
 78 
 79             // The mailing list as well
 80             var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(),
 81                                                                              Duration.ZERO);
 82             var mailmanList = mailmanServer.getList(listAddress.address());
 83             var readerBot = new MailingListArchiveReaderBot(from, Set.of(mailmanList), Set.of(archive));
 84 
 85             // Populate the projects repository
 86             var localRepo = CheckableRepository.init(tempFolder.path(), author.repositoryType());
 87             var masterHash = localRepo.resolve("master").orElseThrow();
 88             localRepo.push(masterHash, author.url(), "master", true);
 89             localRepo.push(masterHash, archive.url(), "webrev", true);
 90 
 91             // Make a change with a corresponding PR
 92             var editHash = CheckableRepository.appendAndCommit(localRepo, "A simple change",
 93                                                                "Change msg\n\nWith several lines");
 94             localRepo.push(editHash, author.url(), "edit", true);
 95             var pr = credentials.createPullRequest(archive, "master", "edit", "This is a pull request");
 96             pr.setBody("This should now be ready");
 97 
 98             // Run an archive pass
 99             TestBotRunner.runPeriodicItems(mlBot);
100             listServer.processIncoming();
101 
102             // Run an archive pass
103             TestBotRunner.runPeriodicItems(readerBot);
104             TestBotRunner.runPeriodicItems(readerBot);
105 
106             // Post a reply directly to the list
107             var conversations = mailmanList.conversations(Duration.ofDays(1));
108             assertEquals(1, conversations.size());
109             addReply(conversations.get(0), mailmanList, pr);
110             listServer.processIncoming();
111 
112             // Another archive reader pass - has to be done twice
113             TestBotRunner.runPeriodicItems(readerBot);
114             TestBotRunner.runPeriodicItems(readerBot);
115 
116             // The bridge should now have processed the reply
117             var updated = pr.comments();
118             assertEquals(2, updated.size());
119             assertTrue(updated.get(1).body().contains("Mailing list message from"));
120             assertTrue(updated.get(1).body().contains("[Commenter](mailto:c@test.test)"));
121             assertTrue(updated.get(1).body().contains("[test](mailto:test@" + listAddress.domain() + ")"));
122         }
123     }
124 
125     @Test
126     void rememberBridged(TestInfo testInfo) throws IOException {
127         try (var credentials = new HostCredentials(testInfo);
128              var tempFolder = new TemporaryDirectory();
129              var listServer = new TestMailmanServer()) {
130             var author = credentials.getHostedRepository();
131             var archive = credentials.getHostedRepository();
132             var ignored = credentials.getHostedRepository();
133             var listAddress = EmailAddress.parse(listServer.createList("test"));
134             var censusBuilder = credentials.getCensusBuilder()
135                                            .addAuthor(author.forge().currentUser().id());
136             var from = EmailAddress.from("test", "test@test.mail");
137             var mlBot = new MailingListBridgeBot(from, author, archive, "master",
138                                                  censusBuilder.build(), "master",
139                                                  listAddress,
140                                                  Set.of(ignored.forge().currentUser().userName()),
141                                                  Set.of(),
142                                                  listServer.getArchive(), listServer.getSMTP(),
143                                                  archive, "webrev", Path.of("test"),
144                                                  URIBuilder.base("http://www.test.test/").build(),
145                                                  Set.of(), Map.of(),
146                                                  URIBuilder.base("http://issues.test/browse/").build(),
147                                                  Map.of(), Duration.ZERO);
148 
149             // The mailing list as well
150             var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(),
151                                                                              Duration.ZERO);
152             var mailmanList = mailmanServer.getList(listAddress.address());
153             var readerBot = new MailingListArchiveReaderBot(from, Set.of(mailmanList), Set.of(archive));
154 
155             // Populate the projects repository
156             var localRepo = CheckableRepository.init(tempFolder.path(), author.repositoryType());
157             var masterHash = localRepo.resolve("master").orElseThrow();
158             localRepo.push(masterHash, author.url(), "master", true);
159             localRepo.push(masterHash, archive.url(), "webrev", true);
160 
161             // Make a change with a corresponding PR
162             var editHash = CheckableRepository.appendAndCommit(localRepo, "A simple change",
163                                                                "Change msg\n\nWith several lines");
164             localRepo.push(editHash, author.url(), "edit", true);
165             var pr = credentials.createPullRequest(archive, "master", "edit", "This is a pull request");
166             pr.setBody("This should now be ready");
167 
168             // Run an archive pass
169             TestBotRunner.runPeriodicItems(mlBot);
170             listServer.processIncoming();
171 
172             // Post a reply directly to the list
173             var conversations = mailmanList.conversations(Duration.ofDays(1));
174             assertEquals(1, conversations.size());
175             addReply(conversations.get(0), mailmanList, pr);
176             listServer.processIncoming();
177 
178             // Another archive reader pass - has to be done twice
179             TestBotRunner.runPeriodicItems(readerBot);
180             TestBotRunner.runPeriodicItems(readerBot);
181 
182             // The bridge should now have processed the reply
183             var updated = pr.comments();
184             assertEquals(2, updated.size());
185 
186             var newReaderBot = new MailingListArchiveReaderBot(from, Set.of(mailmanList), Set.of(archive));
187             TestBotRunner.runPeriodicItems(newReaderBot);
188             TestBotRunner.runPeriodicItems(newReaderBot);
189 
190             // The new bridge should not have made duplicate posts
191             var notUpdated = pr.comments();
192             assertEquals(2, notUpdated.size());
193         }
194     }
195 }