JAMES-1717 Implement a JMAP vacation mailet

Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/3ecc0cc5
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/3ecc0cc5
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/3ecc0cc5

Branch: refs/heads/master
Commit: 3ecc0cc57bc5b46c8d90ec74da7fc5ff9a827cb6
Parents: 661c15e
Author: Benoit Tellier <[email protected]>
Authored: Fri Apr 8 12:11:42 2016 +0700
Committer: Benoit Tellier <[email protected]>
Committed: Fri May 20 18:52:11 2016 +0700

----------------------------------------------------------------------
 .../org/apache/mailet/base/test/FakeMail.java   |  42 ++++
 .../org/apache/james/jmap/JMAPCommonModule.java |   6 +
 .../james/jmap/mailet/VacationMailet.java       | 104 ++++++++
 .../apache/james/jmap/mailet/VacationReply.java | 101 ++++++++
 .../james/jmap/mailet/VacationMailetTest.java   | 244 +++++++++++++++++++
 .../james/jmap/mailet/VacationReplyTest.java    |  69 ++++++
 .../jmap/src/test/resources/spamMail.eml        |  10 +-
 7 files changed, 567 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/3ecc0cc5/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java
----------------------------------------------------------------------
diff --git 
a/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java 
b/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java
index b28a7a2..37295a8 100644
--- a/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java
+++ b/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java
@@ -26,8 +26,11 @@ import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
 
 import javax.mail.MessagingException;
+import javax.mail.Session;
 import javax.mail.internet.InternetAddress;
 import javax.mail.internet.MimeMessage;
 
@@ -36,6 +39,45 @@ import org.apache.mailet.MailAddress;
 
 public class FakeMail implements Mail {
 
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static class Builder {
+
+        private String fileName;
+        private List<MailAddress> recipients = new ArrayList<MailAddress>();
+        private MailAddress sender;
+
+        public Builder fileName(String fileName) {
+            this.fileName = fileName;
+            return this;
+        }
+
+        public Builder recipients(List<MailAddress> recipients) {
+            this.recipients.addAll(recipients);
+            return this;
+        }
+
+        public Builder recipient(MailAddress recipient) {
+            this.recipients.add(recipient);
+            return this;
+        }
+
+        public Builder sender(MailAddress sender) {
+            this.sender = sender;
+            return this;
+        }
+
+        public FakeMail build() throws MessagingException {
+            FakeMail mail = new FakeMail();
+            mail.setMessage(new MimeMessage(Session.getInstance(new 
Properties()) ,ClassLoader.getSystemResourceAsStream(fileName)));
+            mail.setSender(sender);
+            mail.setRecipients(recipients);
+            return mail;
+        }
+    }
+
     private MimeMessage msg = null;
 
     private Collection<MailAddress> recipients = new ArrayList<MailAddress>();

http://git-wip-us.apache.org/repos/asf/james-project/blob/3ecc0cc5/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/JMAPCommonModule.java
----------------------------------------------------------------------
diff --git 
a/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/JMAPCommonModule.java
 
b/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/JMAPCommonModule.java
index 405e0d7..9275010 100644
--- 
a/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/JMAPCommonModule.java
+++ 
b/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/JMAPCommonModule.java
@@ -32,10 +32,13 @@ import org.apache.james.jmap.send.MailFactory;
 import org.apache.james.jmap.send.MailSpool;
 import org.apache.james.jmap.utils.DefaultZonedDateTimeProvider;
 import org.apache.james.jmap.utils.ZonedDateTimeProvider;
+import org.apache.mailet.base.AutomaticallySentMailDetector;
+import org.apache.mailet.base.AutomaticallySentMailDetectorImpl;
 
 import com.google.common.collect.ImmutableList;
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
+import com.google.inject.Scopes;
 import com.google.inject.Singleton;
 import com.google.inject.name.Names;
 
@@ -54,6 +57,9 @@ public class JMAPCommonModule extends AbstractModule {
 
         bind(MailSpool.class).in(Singleton.class);
         bind(MailFactory.class).in(Singleton.class);
+
+        bind(AutomaticallySentMailDetectorImpl.class).in(Scopes.SINGLETON);
+        
bind(AutomaticallySentMailDetector.class).to(AutomaticallySentMailDetectorImpl.class);
     }
 
     @Provides

http://git-wip-us.apache.org/repos/asf/james-project/blob/3ecc0cc5/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/VacationMailet.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/VacationMailet.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/VacationMailet.java
new file mode 100644
index 0000000..8e9d181
--- /dev/null
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/VacationMailet.java
@@ -0,0 +1,104 @@
+/****************************************************************
+ * 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.james.jmap.mailet;
+
+import java.time.ZonedDateTime;
+import java.util.concurrent.CompletableFuture;
+
+import javax.inject.Inject;
+import javax.mail.MessagingException;
+
+import org.apache.james.jmap.api.vacation.AccountId;
+import org.apache.james.jmap.api.vacation.Vacation;
+import org.apache.james.jmap.api.vacation.VacationRepository;
+import org.apache.james.jmap.utils.ZonedDateTimeProvider;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+import org.apache.mailet.base.AutomaticallySentMailDetector;
+import org.apache.mailet.base.GenericMailet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class VacationMailet extends GenericMailet {
+
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(VacationMailet.class);
+
+    private final VacationRepository vacationRepository;
+    private final ZonedDateTimeProvider zonedDateTimeProvider;
+    private final AutomaticallySentMailDetector automaticallySentMailDetector;
+
+    @Inject
+    VacationMailet(VacationRepository vacationRepository, 
ZonedDateTimeProvider zonedDateTimeProvider, AutomaticallySentMailDetector 
automaticallySentMailDetector) {
+        this.vacationRepository = vacationRepository;
+        this.zonedDateTimeProvider = zonedDateTimeProvider;
+        this.automaticallySentMailDetector = automaticallySentMailDetector;
+    }
+
+    @Override
+    public void service(Mail mail) {
+        try {
+            ZonedDateTime processingDate = zonedDateTimeProvider.get();
+            mail.getRecipients()
+                .stream()
+                .map(mailAddress -> manageVacation(mailAddress, mail, 
processingDate))
+                .forEach(CompletableFuture::join);
+        } catch (Throwable e) {
+            LOGGER.warn("Can not process vacation for one or more recipients 
in {}", mail.getRecipients(), e);
+        }
+    }
+
+    public CompletableFuture<Void> manageVacation(MailAddress recipient, Mail 
processedMail, ZonedDateTime processingDate) {
+        AccountId accountId = AccountId.fromString(recipient.toString());
+        CompletableFuture<Vacation> vacationFuture = 
vacationRepository.retrieveVacation(accountId);
+        return vacationFuture.thenAccept(vacation -> {
+            if (shouldSendNotification(vacation, processedMail, recipient, 
processingDate)) {
+                sendNotification(recipient, processedMail, vacation);
+            }
+        });
+    }
+
+    private boolean shouldSendNotification(Vacation vacation, Mail 
processedMail, MailAddress recipient, ZonedDateTime processingDate) {
+        try {
+            return vacation.isActiveAtDate(processingDate)
+                && ! 
automaticallySentMailDetector.isAutomaticallySent(processedMail);
+        } catch (MessagingException e) {
+            LOGGER.warn("Failed detect automatic response in a mail from {} to 
{}", processedMail.getSender(), recipient, e);
+            return false;
+        }
+    }
+
+    private void sendNotification(MailAddress recipient, Mail processedMail, 
Vacation vacation) {
+        try {
+            VacationReply vacationReply = VacationReply.builder(processedMail)
+                .receivedMailRecipient(recipient)
+                .reason(vacation.getTextBody())
+                .build();
+            sendNotification(vacationReply);
+        } catch (MessagingException e) {
+            LOGGER.warn("Failed to send JMAP vacation notification from {} to 
{}", recipient, processedMail.getSender(), e);
+        }
+    }
+
+    private void sendNotification(VacationReply vacationReply) throws 
MessagingException {
+        getMailetContext().sendMail(vacationReply.getSender(),
+            vacationReply.getRecipients(),
+            vacationReply.getMimeMessage());
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/3ecc0cc5/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/VacationReply.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/VacationReply.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/VacationReply.java
new file mode 100644
index 0000000..bd15641
--- /dev/null
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/VacationReply.java
@@ -0,0 +1,101 @@
+/****************************************************************
+ * 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.james.jmap.mailet;
+
+import java.util.List;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+public class VacationReply {
+
+    public static Builder builder(Mail originalMail) {
+        return new Builder(originalMail);
+    }
+
+    public static class Builder {
+
+        public static final boolean NOT_REPLY_TO_ALL = false;
+        private final Mail originalMail;
+        private MailAddress mailRecipient;
+        private String reason;
+
+        private Builder(Mail originalMail) {
+            Preconditions.checkNotNull(originalMail, "Origin mail shall not be 
null");
+            this.originalMail = originalMail;
+        }
+
+        public Builder receivedMailRecipient(MailAddress mailRecipient) {
+            Preconditions.checkNotNull(mailRecipient);
+            this.mailRecipient = mailRecipient;
+            return this;
+        }
+
+        public Builder reason(String reason) {
+            Preconditions.checkNotNull(reason);
+            this.reason = reason;
+            return this;
+        }
+
+        public VacationReply build() throws MessagingException {
+            Preconditions.checkState(mailRecipient != null, "Original 
recipient address should not be null");
+            Preconditions.checkState(originalMail.getSender() != null, 
"Original sender address should not be null");
+
+            return new VacationReply(mailRecipient, 
ImmutableList.of(originalMail.getSender()), generateMimeMessage());
+        }
+
+        private MimeMessage generateMimeMessage() throws MessagingException {
+            MimeMessage reply = (MimeMessage) 
originalMail.getMessage().reply(NOT_REPLY_TO_ALL);
+            reply.setText(reason);
+            reply.setHeader("from", mailRecipient.toString());
+            reply.setHeader("to", originalMail.getSender().toString());
+            return reply;
+        }
+    }
+
+    private final MailAddress sender;
+    private final List<MailAddress> recipients;
+    private final MimeMessage mimeMessage;
+
+    private VacationReply(MailAddress sender, List<MailAddress> recipients, 
MimeMessage mimeMessage) {
+        this.sender = sender;
+        this.recipients = recipients;
+        this.mimeMessage = mimeMessage;
+    }
+
+    public MailAddress getSender() {
+        return sender;
+    }
+
+    public List<MailAddress> getRecipients() {
+        return recipients;
+    }
+
+    public MimeMessage getMimeMessage() {
+        return mimeMessage;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/james-project/blob/3ecc0cc5/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/VacationMailetTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/VacationMailetTest.java
 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/VacationMailetTest.java
new file mode 100644
index 0000000..ff73594
--- /dev/null
+++ 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/VacationMailetTest.java
@@ -0,0 +1,244 @@
+/****************************************************************
+ * 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.james.jmap.mailet;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import java.time.ZonedDateTime;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+
+import javax.mail.MessagingException;
+
+import org.apache.james.jmap.api.vacation.AccountId;
+import org.apache.james.jmap.api.vacation.Vacation;
+import org.apache.james.jmap.api.vacation.VacationRepository;
+import org.apache.james.jmap.utils.ZonedDateTimeProvider;
+import org.apache.mailet.MailAddress;
+import org.apache.mailet.MailetContext;
+import org.apache.mailet.base.AutomaticallySentMailDetector;
+import org.apache.mailet.base.test.FakeMail;
+import org.apache.mailet.base.test.FakeMailetConfig;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class VacationMailetTest {
+
+    public static final ZonedDateTime DATE_TIME_2016 = 
ZonedDateTime.parse("2016-10-09T08:07:06+07:00[Asia/Vientiane]");
+    public static final ZonedDateTime DATE_TIME_2017 = 
ZonedDateTime.parse("2017-10-09T08:07:06+07:00[Asia/Vientiane]");
+    public static final ZonedDateTime DATE_TIME_2018 = 
ZonedDateTime.parse("2018-10-09T08:07:06+07:00[Asia/Vientiane]");
+
+    public static final String USERNAME = "[email protected]";
+    private VacationMailet testee;
+    private VacationRepository vacationRepository;
+    private ZonedDateTimeProvider zonedDateTimeProvider;
+    private MailetContext mailetContext;
+    private MailAddress originalSender;
+    private MailAddress originalRecipient;
+    private AutomaticallySentMailDetector automaticallySentMailDetector;
+
+    @Before
+    public void setUp() throws Exception {
+        originalSender = new MailAddress("[email protected]");
+        originalRecipient = new MailAddress(USERNAME);
+
+        vacationRepository = mock(VacationRepository.class);
+        zonedDateTimeProvider = mock(ZonedDateTimeProvider.class);
+        automaticallySentMailDetector = 
mock(AutomaticallySentMailDetector.class);
+        testee = new VacationMailet(vacationRepository, zonedDateTimeProvider, 
automaticallySentMailDetector);
+        mailetContext = mock(MailetContext.class);
+        testee.init(new FakeMailetConfig("vacation", mailetContext));
+    }
+
+    @Test
+    public void unactivatedVacationShouldNotSendNotification() throws 
Exception {
+        FakeMail mail = FakeMail.builder()
+            .fileName("spamMail.eml")
+            .recipient(originalRecipient)
+            .sender(new MailAddress("[email protected]"))
+            .build();
+        when(zonedDateTimeProvider.get()).thenReturn(DATE_TIME_2017);
+        
when(vacationRepository.retrieveVacation(AccountId.fromString(USERNAME)))
+            
.thenReturn(CompletableFuture.completedFuture(VacationRepository.DEFAULT_VACATION));
+        
when(automaticallySentMailDetector.isAutomaticallySent(mail)).thenReturn(false);
+
+        testee.service(mail);
+
+        verifyNoMoreInteractions(mailetContext);
+    }
+
+    @Test
+    public void activateVacationShouldSendNotification() throws Exception {
+        FakeMail mail = FakeMail.builder()
+            .fileName("spamMail.eml")
+            .recipient(originalRecipient)
+            .sender(originalSender)
+            .build();
+        
when(vacationRepository.retrieveVacation(AccountId.fromString(USERNAME)))
+            .thenReturn(CompletableFuture.completedFuture(
+                Vacation.builder()
+                    .enabled(true)
+                    .fromDate(Optional.of(DATE_TIME_2016))
+                    .toDate(Optional.of(DATE_TIME_2018))
+                    .textBody("Explaining my vacation")
+                    .build()));
+        when(zonedDateTimeProvider.get()).thenReturn(DATE_TIME_2017);
+        
when(automaticallySentMailDetector.isAutomaticallySent(mail)).thenReturn(false);
+
+        testee.service(mail);
+
+        verify(mailetContext).sendMail(eq(originalRecipient), 
eq(ImmutableList.of(originalSender)), any());
+        verifyNoMoreInteractions(mailetContext);
+    }
+
+    @Test
+    public void activateVacationShouldNotSendNotificationToMailingList() 
throws Exception {
+        FakeMail mail = FakeMail.builder()
+            .fileName("spamMail.eml")
+            .recipient(originalRecipient)
+            .sender(new MailAddress("[email protected]"))
+            .build();
+        
when(vacationRepository.retrieveVacation(AccountId.fromString(USERNAME)))
+            .thenReturn(CompletableFuture.completedFuture(
+                Vacation.builder()
+                    .enabled(true)
+                    .fromDate(Optional.of(DATE_TIME_2016))
+                    .toDate(Optional.of(DATE_TIME_2018))
+                    .textBody("Explaining my vacation")
+                    .build()));
+        when(zonedDateTimeProvider.get()).thenReturn(DATE_TIME_2017);
+        
when(automaticallySentMailDetector.isAutomaticallySent(mail)).thenReturn(true);
+
+        testee.service(mail);
+
+        verifyNoMoreInteractions(mailetContext);
+    }
+
+    @Test
+    public void multipleRecipientShouldGenerateNotifications() throws 
MessagingException {
+        String secondUserName = "[email protected]";
+        MailAddress secondRecipient = new MailAddress(secondUserName);
+        FakeMail mail = FakeMail.builder()
+            .fileName("spamMail.eml")
+            .recipients(ImmutableList.of(originalRecipient, secondRecipient))
+            .sender(originalSender)
+            .build();
+        
when(vacationRepository.retrieveVacation(AccountId.fromString(USERNAME)))
+            .thenReturn(CompletableFuture.completedFuture(
+                Vacation.builder()
+                    .enabled(true)
+                    .fromDate(Optional.of(DATE_TIME_2016))
+                    .toDate(Optional.of(DATE_TIME_2018))
+                    .textBody("Explaining my vacation")
+                    .build()));
+        
when(vacationRepository.retrieveVacation(AccountId.fromString(secondUserName)))
+            .thenReturn(CompletableFuture.completedFuture(
+                Vacation.builder()
+                    .enabled(true)
+                    .fromDate(Optional.of(DATE_TIME_2016))
+                    .toDate(Optional.of(DATE_TIME_2018))
+                    .textBody("Explaining my vacation")
+                    .build()));
+        when(zonedDateTimeProvider.get()).thenReturn(DATE_TIME_2017);
+        
when(automaticallySentMailDetector.isAutomaticallySent(mail)).thenReturn(false);
+
+        testee.service(mail);
+
+        verify(mailetContext).sendMail(eq(originalRecipient), 
eq(ImmutableList.of(originalSender)), any());
+        verify(mailetContext).sendMail(eq(secondRecipient), 
eq(ImmutableList.of(originalSender)), any());
+        verifyNoMoreInteractions(mailetContext);
+    }
+
+    @Test
+    public void 
serviceShouldNotSendNotificationUponErrorsRetrievingVacationObject() throws 
Exception {
+        FakeMail mail = FakeMail.builder()
+            .fileName("spamMail.eml")
+            .recipient(originalRecipient)
+            .sender(new MailAddress("[email protected]"))
+            .build();
+        
when(vacationRepository.retrieveVacation(AccountId.fromString(USERNAME)))
+            .thenReturn(CompletableFuture.supplyAsync(() -> {
+                throw new RuntimeException();
+            }));
+        when(zonedDateTimeProvider.get()).thenReturn(DATE_TIME_2017);
+        
when(automaticallySentMailDetector.isAutomaticallySent(mail)).thenReturn(false);
+
+        testee.service(mail);
+
+        verifyNoMoreInteractions(mailetContext);
+    }
+
+    @Test
+    public void 
serviceShouldNotSendNotificationUponErrorsDetectingAutomaticallySentMails() 
throws Exception {
+        FakeMail mail = FakeMail.builder()
+            .fileName("spamMail.eml")
+            .recipient(originalRecipient)
+            .sender(originalSender)
+            .build();
+        
when(vacationRepository.retrieveVacation(AccountId.fromString(USERNAME)))
+            .thenReturn(CompletableFuture.completedFuture(
+                Vacation.builder()
+                    .enabled(true)
+                    .fromDate(Optional.of(DATE_TIME_2016))
+                    .toDate(Optional.of(DATE_TIME_2018))
+                    .textBody("Explaining my vacation")
+                    .build()));
+        when(zonedDateTimeProvider.get()).thenReturn(DATE_TIME_2017);
+        
when(automaticallySentMailDetector.isAutomaticallySent(mail)).thenThrow(new 
MessagingException());
+
+        testee.service(mail);
+
+        verifyNoMoreInteractions(mailetContext);
+    }
+
+    @Test
+    public void serviceShouldNotPropagateExceptionIfSendFails() throws 
Exception {
+        FakeMail mail = FakeMail.builder()
+            .fileName("spamMail.eml")
+            .recipient(originalRecipient)
+            .sender(originalSender)
+            .build();
+        
when(vacationRepository.retrieveVacation(AccountId.fromString(USERNAME)))
+            .thenReturn(CompletableFuture.completedFuture(
+                Vacation.builder()
+                    .enabled(true)
+                    .fromDate(Optional.of(DATE_TIME_2016))
+                    .toDate(Optional.of(DATE_TIME_2018))
+                    .textBody("Explaining my vacation")
+                    .build()));
+        when(zonedDateTimeProvider.get()).thenReturn(DATE_TIME_2017);
+        
when(automaticallySentMailDetector.isAutomaticallySent(mail)).thenReturn(false);
+        doThrow(new 
MessagingException()).when(mailetContext).sendMail(eq(originalSender), 
eq(ImmutableList.of(originalRecipient)), any());
+
+        testee.service(mail);
+
+        verify(mailetContext).sendMail(eq(originalRecipient), 
eq(ImmutableList.of(originalSender)), any());
+        verifyNoMoreInteractions(mailetContext);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/3ecc0cc5/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/VacationReplyTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/VacationReplyTest.java
 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/VacationReplyTest.java
new file mode 100644
index 0000000..64bb6bf
--- /dev/null
+++ 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/VacationReplyTest.java
@@ -0,0 +1,69 @@
+/****************************************************************
+ * 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.james.jmap.mailet;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Properties;
+
+import javax.mail.Session;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.mailet.MailAddress;
+import org.apache.mailet.base.test.FakeMail;
+import org.junit.Test;
+
+public class VacationReplyTest {
+
+    public static final String REASON = "I am in vacation dudes !";
+
+    @Test
+    public void vacationReplyShouldGenerateASuitableAnswer() throws Exception {
+        MailAddress originalSender = new MailAddress("[email protected]");
+        MailAddress originalRecipient = new MailAddress("[email protected]");
+        FakeMail mail = new FakeMail();
+        mail.setMessage(new MimeMessage(Session.getInstance(new Properties()) 
,ClassLoader.getSystemResourceAsStream("spamMail.eml")));
+        mail.setSender(originalSender);
+
+        VacationReply vacationReply = VacationReply.builder(mail)
+            .reason(REASON)
+            .receivedMailRecipient(originalRecipient)
+            .build();
+
+        
assertThat(vacationReply.getRecipients()).containsExactly(originalSender);
+        assertThat(vacationReply.getSender()).isEqualTo(originalRecipient);
+        
assertThat(vacationReply.getMimeMessage().getHeader("subject")).containsExactly("Re:
 Original subject");
+        
assertThat(IOUtils.toString(vacationReply.getMimeMessage().getInputStream())).contains(REASON);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void vacationReplyShouldThrowOnNullMail() {
+        VacationReply.builder(null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void vacationReplyShouldThrowOnNullOriginalEMailAddress() throws 
Exception {
+        VacationReply.builder(new FakeMail())
+            .receivedMailRecipient(null)
+            .build();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/3ecc0cc5/server/protocols/jmap/src/test/resources/spamMail.eml
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/resources/spamMail.eml 
b/server/protocols/jmap/src/test/resources/spamMail.eml
index 2359ec3..8121d99 100644
--- a/server/protocols/jmap/src/test/resources/spamMail.eml
+++ b/server/protocols/jmap/src/test/resources/spamMail.eml
@@ -34,17 +34,9 @@ From: "Content-filter at spam.minet.net" 
<[email protected]>
 Date: Wed, 3 Jun 2015 09:05:46 +0000 (UTC)
 To: <[email protected]>
 Message-ID: <[email protected]>
-Subject: [root] UNCHECKED contents in mail FROM <[email protected]>
+Subject: Original subject
 X-BeenThere: [email protected]
 X-Mailman-Version: 2.1.15
-Precedence: list
-List-Id: <root.listes.minet.net>
-List-Unsubscribe: <https://listes.minet.net/cgi-bin/mailman/options/root>,
- <mailto:[email protected]?subject=unsubscribe>
-List-Post: <mailto:[email protected]>
-List-Help: <mailto:[email protected]?subject=help>
-List-Subscribe: <https://listes.minet.net/cgi-bin/mailman/listinfo/root>,
- <mailto:[email protected]?subject=subscribe>
 Errors-To: [email protected]
 Sender: "root" <[email protected]>
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to