You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ra...@apache.org on 2019/05/02 14:55:31 UTC

[sling-org-apache-sling-committer-cli] branch master updated: SLING-8385 - Include the From header in the generated email sources

This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-committer-cli.git


The following commit(s) were added to refs/heads/master by this push:
     new eedba45  SLING-8385 - Include the From header in the generated email sources
eedba45 is described below

commit eedba45ecdb58952a0619a79e181ba98fe54952c
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Thu May 2 16:55:17 2019 +0200

    SLING-8385 - Include the From header in the generated email sources
---
 pom.xml                                            |  29 +++++
 .../org/apache/sling/cli/impl/people/Member.java   |   2 +-
 .../cli/impl/release/PrepareVoteEmailCommand.java  |  32 +++--
 .../sling/cli/impl/release/TallyVotesCommand.java  |  27 ++--
 .../impl/release/PrepareVoteEmailCommandTest.java  | 119 +++++++++++++++++
 .../cli/impl/release/TallyVotesCommandTest.java    | 141 +++++++++++++++++++++
 6 files changed, 325 insertions(+), 25 deletions(-)

diff --git a/pom.xml b/pom.xml
index 1a3a1fd..bf53e72 100644
--- a/pom.xml
+++ b/pom.xml
@@ -208,5 +208,34 @@
             <version>1.3</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.testing.osgi-mock.junit4</artifactId>
+            <version>2.4.8</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>2.25.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.cmpn</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-api-mockito2</artifactId>
+            <version>2.0.2</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-module-junit4</artifactId>
+            <version>2.0.2</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/src/main/java/org/apache/sling/cli/impl/people/Member.java b/src/main/java/org/apache/sling/cli/impl/people/Member.java
index 5602c66..f25726f 100644
--- a/src/main/java/org/apache/sling/cli/impl/people/Member.java
+++ b/src/main/java/org/apache/sling/cli/impl/people/Member.java
@@ -25,7 +25,7 @@ public class Member {
     private final boolean isPMCMember;
     private final String email;
 
-    Member(String id, String name, boolean isPMCMember) {
+    public Member(String id, String name, boolean isPMCMember) {
         this.id = id;
         this.name = name;
         this.isPMCMember = isPMCMember;
diff --git a/src/main/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommand.java b/src/main/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommand.java
index 4cc4222..9e50205 100644
--- a/src/main/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommand.java
+++ b/src/main/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommand.java
@@ -20,11 +20,14 @@ import java.io.IOException;
 import java.util.List;
 import java.util.stream.Collectors;
 
+import javax.mail.internet.InternetAddress;
+
 import org.apache.sling.cli.impl.Command;
 import org.apache.sling.cli.impl.jira.Version;
 import org.apache.sling.cli.impl.jira.VersionFinder;
 import org.apache.sling.cli.impl.nexus.StagingRepository;
 import org.apache.sling.cli.impl.nexus.StagingRepositoryFinder;
+import org.apache.sling.cli.impl.people.Member;
 import org.apache.sling.cli.impl.people.MembersFinder;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
@@ -37,11 +40,21 @@ import org.slf4j.LoggerFactory;
     Command.PROPERTY_NAME_SUMMARY + "=Prepares an email vote for the specified release." })
 public class PrepareVoteEmailCommand implements Command {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(PrepareVoteEmailCommand.class);
+
     @Reference
     private MembersFinder membersFinder;
 
+    @Reference
+    private StagingRepositoryFinder repoFinder;
+
+    @Reference
+    private VersionFinder versionFinder;
+
     // TODO - replace with file template
-    private static final String EMAIL_TEMPLATE ="To: \"Sling Developers List\" <de...@sling.apache.org>\n" + 
+    private static final String EMAIL_TEMPLATE =
+            "From: ##FROM##\n" +
+            "To: \"Sling Developers List\" <de...@sling.apache.org>\n" +
             "Subject: [VOTE] Release ##RELEASE_NAME##\n" + 
             "\n" + 
             "Hi,\n" + 
@@ -73,14 +86,6 @@ public class PrepareVoteEmailCommand implements Command {
 
     private static final String RELEASE_TEMPLATE = 
             "https://issues.apache.org/jira/browse/SLING/fixforversion/##VERSION_ID##";
-    
-    private final Logger logger = LoggerFactory.getLogger(getClass());
-    
-    @Reference
-    private StagingRepositoryFinder repoFinder;
-    
-    @Reference
-    private VersionFinder versionFinder;
 
     @Override
     public void execute(String target) {
@@ -104,19 +109,20 @@ public class PrepareVoteEmailCommand implements Command {
                 .map( v -> RELEASE_TEMPLATE.replace("##VERSION_ID##", String.valueOf(v.getId())))
                 .collect(Collectors.joining("\n"));
                 
-            
+            Member currentMember = membersFinder.getCurrentMember();
             String emailContents = EMAIL_TEMPLATE
+                    .replace("##FROM##", new InternetAddress(currentMember.getEmail(), currentMember.getName()).toString())
                     .replace("##RELEASE_NAME##", releaseName)
                     .replace("##RELEASE_ID##", String.valueOf(repoId))
                     .replace("##RELEASE_OR_RELEASES##", releaseOrReleases)
                     .replace("##RELEASE_JIRA_LINKS##", releaseJiraLinks)
                     .replace("##FIXED_ISSUES_COUNT##", String.valueOf(fixedIssueCounts))
-                    .replace("##USER_NAME##", membersFinder.getCurrentMember().getName());
+                    .replace("##USER_NAME##", currentMember.getName());
                     
-            logger.info(emailContents);
+            LOGGER.info(emailContents);
 
         } catch (IOException e) {
-            logger.warn("Failed executing command", e);
+            LOGGER.warn("Failed executing command", e);
         }
     }
 }
diff --git a/src/main/java/org/apache/sling/cli/impl/release/TallyVotesCommand.java b/src/main/java/org/apache/sling/cli/impl/release/TallyVotesCommand.java
index 4526707..48a2f69 100644
--- a/src/main/java/org/apache/sling/cli/impl/release/TallyVotesCommand.java
+++ b/src/main/java/org/apache/sling/cli/impl/release/TallyVotesCommand.java
@@ -25,6 +25,8 @@ import java.util.Locale;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import javax.mail.internet.InternetAddress;
+
 import org.apache.sling.cli.impl.Command;
 import org.apache.sling.cli.impl.mail.Email;
 import org.apache.sling.cli.impl.mail.VoteThreadFinder;
@@ -44,11 +46,20 @@ import org.slf4j.LoggerFactory;
 })
 public class TallyVotesCommand implements Command {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(TallyVotesCommand.class);
+
     @Reference
     private MembersFinder membersFinder;
 
+    @Reference
+    private StagingRepositoryFinder repoFinder;
+
+    @Reference
+    private VoteThreadFinder voteThreadFinder;
+
     // TODO - move to file
     private static final String EMAIL_TEMPLATE =
+            "From: ##FROM## \n" +
             "To: \"Sling Developers List\" <de...@sling.apache.org>\n" + 
             "Subject: [RESULT] [VOTE] Release ##RELEASE_NAME##\n" + 
             "\n" + 
@@ -65,14 +76,7 @@ public class TallyVotesCommand implements Command {
             "Regards,\n" +
             "##USER_NAME##\n" +
             "\n";
-    private final Logger logger = LoggerFactory.getLogger(getClass());
 
-    @Reference
-    private StagingRepositoryFinder repoFinder;
-    
-    @Reference
-    private VoteThreadFinder voteThreadFinder;
-    
     @Override
     public void execute(String target) {
         try {
@@ -87,7 +91,7 @@ public class TallyVotesCommand implements Command {
             collator.setDecomposition(Collator.NO_DECOMPOSITION);
             List<Email> emailThread = voteThreadFinder.findVoteThread(releaseName);
             if (emailThread.isEmpty()) {
-                logger.error("Could not find a corresponding email voting thread for release \"{}\".", releaseName);
+                LOGGER.error("Could not find a corresponding email voting thread for release \"{}\".", releaseName);
             } else {
                 emailThread.stream().skip(1).filter(this::isPositiveVote).forEachOrdered(
                         email -> {
@@ -105,8 +109,9 @@ public class TallyVotesCommand implements Command {
                             }
                         }
                 );
-
+                Member currentMember = membersFinder.getCurrentMember();
                 String email = EMAIL_TEMPLATE
+                        .replace("##FROM##", new InternetAddress(currentMember.getEmail(), currentMember.getName()).toUnicodeString())
                         .replace("##RELEASE_NAME##", releaseFullName)
                         .replace("##BINDING_VOTERS##", String.join(", ", bindingVoters))
                         .replace("##USER_NAME##", membersFinder.getCurrentMember().getName());
@@ -116,11 +121,11 @@ public class TallyVotesCommand implements Command {
                     email = email.replace("##NON_BINDING_VOTERS##", String.join(", ", nonBindingVoters));
                 }
 
-                logger.info(email);
+                LOGGER.info(email);
             }
             
         } catch (IOException e) {
-            logger.warn("Command execution failed", e);
+            LOGGER.warn("Command execution failed", e);
         }
     }
 
diff --git a/src/test/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommandTest.java b/src/test/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommandTest.java
new file mode 100644
index 0000000..4e971f2
--- /dev/null
+++ b/src/test/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommandTest.java
@@ -0,0 +1,119 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.cli.impl.release;
+
+import org.apache.sling.cli.impl.Command;
+import org.apache.sling.cli.impl.jira.Version;
+import org.apache.sling.cli.impl.jira.VersionFinder;
+import org.apache.sling.cli.impl.nexus.StagingRepository;
+import org.apache.sling.cli.impl.nexus.StagingRepositoryFinder;
+import org.apache.sling.cli.impl.people.Member;
+import org.apache.sling.cli.impl.people.MembersFinder;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.osgi.framework.ServiceReference;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.powermock.api.mockito.PowerMockito.mockStatic;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({PrepareVoteEmailCommand.class, LoggerFactory.class})
+@PowerMockIgnore({
+                         // https://github.com/powermock/powermock/issues/864
+                         "com.sun.org.apache.xerces.*",
+                         "javax.xml.*",
+                         "org.w3c.dom.*"
+                 })
+public class PrepareVoteEmailCommandTest {
+
+    @Rule
+    public final OsgiContext osgiContext = new OsgiContext();
+
+    @Test
+    public void testPrepareEmailGeneration() throws Exception {
+        mockStatic(LoggerFactory.class);
+        Logger logger = mock(Logger.class);
+        when(LoggerFactory.getLogger(PrepareVoteEmailCommand.class)).thenReturn(logger);
+        MembersFinder membersFinder = mock(MembersFinder.class);
+        when(membersFinder.getCurrentMember()).thenReturn(new Member("johndoe", "John Doe", true));
+
+        StagingRepository stagingRepository = mock(StagingRepository.class);
+        when(stagingRepository.getDescription()).thenReturn("Apache Sling CLI Test 1.0.0");
+        StagingRepositoryFinder stagingRepositoryFinder = mock(StagingRepositoryFinder.class);
+        when(stagingRepositoryFinder.find(123)).thenReturn(stagingRepository);
+
+        VersionFinder versionFinder = mock(VersionFinder.class);
+        Version version = mock(Version.class);
+        when(version.getName()).thenReturn("CLI Test 1.0.0");
+        when(version.getId()).thenReturn(1);
+        when(version.getIssuesFixedCount()).thenReturn(42);
+        when(versionFinder.find("CLI Test 1.0.0")).thenReturn(version);
+
+        osgiContext.registerService(MembersFinder.class, membersFinder);
+        osgiContext.registerService(StagingRepositoryFinder.class, stagingRepositoryFinder);
+        osgiContext.registerService(VersionFinder.class, versionFinder);
+
+        osgiContext.registerInjectActivateService(new PrepareVoteEmailCommand());
+
+        ServiceReference<?> reference =
+                osgiContext.bundleContext().getServiceReference(Command.class.getName());
+        Command command = (Command) osgiContext.bundleContext().getService(reference);
+        command.execute("123");
+        verify(logger).info(
+                "From: John Doe <jo...@apache.org>\n" +
+                        "To: \"Sling Developers List\" <de...@sling.apache.org>\n" +
+                        "Subject: [VOTE] Release Apache Sling CLI Test 1.0.0\n" +
+                        "\n" +
+                        "Hi,\n" +
+                        "\n" +
+                        "We solved 42 issue(s) in this release:\n" +
+                        "\n" +
+                        "https://issues.apache.org/jira/browse/SLING/fixforversion/1\n" +
+                        "\n" +
+                        "Staging repository:\n" +
+                        "https://repository.apache.org/content/repositories/orgapachesling-123/\n" +
+                        "\n" +
+                        "You can use this UNIX script to download the release and verify the signatures:\n" +
+                        "https://gitbox.apache.org/repos/asf?p=sling-tooling-release.git;a=blob;f=check_staged_release.sh;hb=HEAD\n" +
+                        "\n" +
+                        "Usage:\n" +
+                        "sh check_staged_release.sh 123 /tmp/sling-staging\n" +
+                        "\n" +
+                        "Please vote to approve this release:\n" +
+                        "\n" +
+                        "  [ ] +1 Approve the release\n" +
+                        "  [ ]  0 Don't care\n" +
+                        "  [ ] -1 Don't release, because ...\n" +
+                        "\n" +
+                        "This majority vote is open for at least 72 hours.\n" +
+                        "\n" +
+                        "Regards,\n" +
+                        "John Doe\n" +
+                        "\n");
+    }
+}
diff --git a/src/test/java/org/apache/sling/cli/impl/release/TallyVotesCommandTest.java b/src/test/java/org/apache/sling/cli/impl/release/TallyVotesCommandTest.java
new file mode 100644
index 0000000..29f6012
--- /dev/null
+++ b/src/test/java/org/apache/sling/cli/impl/release/TallyVotesCommandTest.java
@@ -0,0 +1,141 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.cli.impl.release;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.mail.internet.InternetAddress;
+
+import org.apache.sling.cli.impl.Command;
+import org.apache.sling.cli.impl.Credentials;
+import org.apache.sling.cli.impl.CredentialsService;
+import org.apache.sling.cli.impl.mail.Email;
+import org.apache.sling.cli.impl.mail.VoteThreadFinder;
+import org.apache.sling.cli.impl.nexus.StagingRepository;
+import org.apache.sling.cli.impl.nexus.StagingRepositoryFinder;
+import org.apache.sling.cli.impl.people.Member;
+import org.apache.sling.cli.impl.people.MembersFinder;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.osgi.framework.ServiceReference;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.powermock.reflect.Whitebox;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.powermock.api.mockito.PowerMockito.mockStatic;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({PrepareVoteEmailCommand.class, LoggerFactory.class})
+@PowerMockIgnore({
+                         // https://github.com/powermock/powermock/issues/864
+                         "com.sun.org.apache.xerces.*",
+                         "javax.xml.*",
+                         "org.w3c.dom.*"
+                 })
+public class TallyVotesCommandTest {
+    @Rule
+    public final OsgiContext osgiContext = new OsgiContext();
+
+    @Test
+    public void testPrepareEmailGeneration() throws Exception {
+        mockStatic(LoggerFactory.class);
+        Logger logger = mock(Logger.class);
+        when(LoggerFactory.getLogger(TallyVotesCommand.class)).thenReturn(logger);
+
+        CredentialsService credentialsService = mock(CredentialsService.class);
+        when(credentialsService.getCredentials()).thenReturn(new Credentials("johndoe", "secret"));
+
+        MembersFinder membersFinder = spy(new MembersFinder());
+        Set<Member> members = new HashSet<>(){{
+            add(new Member("johndoe", "John Doe", true));
+            add(new Member("alice", "Alice", true));
+            add(new Member("bob", "Bob", true));
+            add(new Member("charlie", "Charlie", true));
+            add(new Member("daniel", "Daniel", false));
+        }};
+        Whitebox.setInternalState(membersFinder, "members", members);
+        Whitebox.setInternalState(membersFinder, "lastCheck", System.currentTimeMillis());
+
+        StagingRepository stagingRepository = mock(StagingRepository.class);
+        when(stagingRepository.getDescription()).thenReturn("Apache Sling CLI Test 1.0.0");
+        StagingRepositoryFinder stagingRepositoryFinder = mock(StagingRepositoryFinder.class);
+        when(stagingRepositoryFinder.find(123)).thenReturn(stagingRepository);
+
+        VoteThreadFinder voteThreadFinder = mock(VoteThreadFinder.class);
+        List<Email> thread = new ArrayList<>(){{
+            add(mockEmail("johndoe@apache.org", "John Doe"));
+            add(mockEmail("alice@apache.org", "Alice"));
+            add(mockEmail("bob@apache.org", "Bob"));
+            add(mockEmail("charlie@apache.org", "Charlie"));
+            add(mockEmail("daniel@apache.org", "Daniel"));
+            add(mockEmail("johndoe@apache.org", "John Doe"));
+        }};
+        when(voteThreadFinder.findVoteThread("CLI Test 1.0.0")).thenReturn(thread);
+
+        osgiContext.registerService(CredentialsService.class, credentialsService);
+        osgiContext.registerInjectActivateService(membersFinder);
+        osgiContext.registerService(StagingRepositoryFinder.class, stagingRepositoryFinder);
+        osgiContext.registerService(VoteThreadFinder.class, voteThreadFinder);
+
+        osgiContext.registerInjectActivateService(new TallyVotesCommand());
+
+        ServiceReference<?> reference =
+                osgiContext.bundleContext().getServiceReference(Command.class.getName());
+        Command command = (Command) osgiContext.bundleContext().getService(reference);
+        command.execute("123");
+        verify(logger).info(
+                "From: John Doe <jo...@apache.org> \n" +
+                "To: \"Sling Developers List\" <de...@sling.apache.org>\n" +
+                "Subject: [RESULT] [VOTE] Release Apache Sling CLI Test 1.0.0\n" +
+                "\n" +
+                "Hi,\n" +
+                "\n" +
+                "The vote has passed with the following result:\n" +
+                "\n" +
+                "+1 (binding): Alice, Bob, Charlie, John Doe\n" +
+                "+1 (non-binding): Daniel\n" +
+                "\n" +
+                "I will copy this release to the Sling dist directory and\n" +
+                "promote the artifacts to the central Maven repository.\n" +
+                "\n" +
+                "Regards,\n" +
+                "John Doe\n" +
+                "\n"
+        );
+    }
+
+    private Email mockEmail(String address, String name) throws Exception {
+        Email email = mock(Email.class);
+        when(email.getBody()).thenReturn("+1");
+        when(email.getFrom()).thenReturn(new InternetAddress(address, name));
+        return email;
+    }
+}