JAMES-1877 Provide tests for DeliveryRunnable decision making
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/fc1b1d3e Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/fc1b1d3e Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/fc1b1d3e Branch: refs/heads/master Commit: fc1b1d3e7054bf1c8ede4c3fb5d0727f677cf13f Parents: 9790170 Author: Benoit Tellier <[email protected]> Authored: Fri Dec 2 14:35:46 2016 +0700 Committer: Benoit Tellier <[email protected]> Committed: Tue Jan 10 15:12:51 2017 +0700 ---------------------------------------------------------------------- .../remoteDelivery/DeliveryRunnable.java | 33 ++- .../remoteDelivery/DeliveryRunnableTest.java | 249 +++++++++++++++++++ 2 files changed, 276 insertions(+), 6 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/fc1b1d3e/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java index d46a9f5..bc01245 100644 --- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java +++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java @@ -31,8 +31,19 @@ import org.apache.mailet.Mail; import org.apache.mailet.MailetContext; import org.slf4j.Logger; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Supplier; + public class DeliveryRunnable implements Runnable { + public static final Supplier<Date> CURRENT_DATE_SUPPLIER = new Supplier<Date>() { + @Override + public Date get() { + return new Date(); + } + }; + public static final AtomicBoolean DEFAULT_NOT_STARTED = new AtomicBoolean(false); + private final MailQueue queue; private final RemoteDeliveryConfiguration configuration; private final Metric outgoingMailsMetric; @@ -40,17 +51,26 @@ public class DeliveryRunnable implements Runnable { private final Bouncer bouncer; private final MailDelivrer mailDelivrer; private final VolatileIsDestroyed volatileIsDestroyed; + private final Supplier<Date> dateSupplier; public DeliveryRunnable(MailQueue queue, RemoteDeliveryConfiguration configuration, DNSService dnsServer, Metric outgoingMailsMetric, Logger logger, MailetContext mailetContext, VolatileIsDestroyed volatileIsDestroyed) { + this(queue, configuration, outgoingMailsMetric, logger, new Bouncer(configuration, mailetContext, logger), + new MailDelivrer(configuration, new MailDelivrerToHost(configuration, mailetContext, logger), dnsServer, logger), + volatileIsDestroyed, CURRENT_DATE_SUPPLIER); + } + + @VisibleForTesting + DeliveryRunnable(MailQueue queue, RemoteDeliveryConfiguration configuration, Metric outgoingMailsMetric, Logger logger, Bouncer bouncer, + MailDelivrer mailDelivrer, VolatileIsDestroyed volatileIsDestroyed, Supplier<Date> dateSupplier) { this.queue = queue; this.configuration = configuration; this.outgoingMailsMetric = outgoingMailsMetric; this.logger = logger; + this.bouncer = bouncer; + this.mailDelivrer = mailDelivrer; this.volatileIsDestroyed = volatileIsDestroyed; - this.bouncer = new Bouncer(configuration, mailetContext, logger); - MailDelivrerToHost mailDelivrerToHost = new MailDelivrerToHost(configuration, mailetContext, logger); - this.mailDelivrer = new MailDelivrer(configuration, mailDelivrerToHost, dnsServer, logger); + this.dateSupplier = dateSupplier; } @Override @@ -98,7 +118,8 @@ public class DeliveryRunnable implements Runnable { } } - private void attemptDelivery(Mail mail) throws MailQueue.MailQueueException { + @VisibleForTesting + void attemptDelivery(Mail mail) throws MailQueue.MailQueueException { ExecutionResult executionResult = mailDelivrer.deliver(mail); switch (executionResult.getExecutionState()) { case SUCCESS: @@ -117,7 +138,7 @@ public class DeliveryRunnable implements Runnable { if (!mail.getState().equals(Mail.ERROR)) { mail.setState(Mail.ERROR); DeliveryRetriesHelper.initRetries(mail); - mail.setLastUpdated(new Date()); + mail.setLastUpdated(dateSupplier.get()); } int retries = DeliveryRetriesHelper.retrieveRetries(mail); @@ -132,7 +153,7 @@ public class DeliveryRunnable implements Runnable { private void reAttemptDelivery(Mail mail, int retries) throws MailQueue.MailQueueException { logger.debug("Storing message " + mail.getName() + " into outgoing after " + retries + " retries"); DeliveryRetriesHelper.incrementRetries(mail); - mail.setLastUpdated(new Date()); + mail.setLastUpdated(dateSupplier.get()); // Something happened that will delay delivery. Store it back in the retry repository. long delay = getNextDelay(DeliveryRetriesHelper.retrieveRetries(mail)); http://git-wip-us.apache.org/repos/asf/james-project/blob/fc1b1d3e/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnableTest.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnableTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnableTest.java new file mode 100644 index 0000000..1bfec0d --- /dev/null +++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnableTest.java @@ -0,0 +1,249 @@ +/**************************************************************** + * 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.transport.mailets.remoteDelivery; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +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.util.Date; +import java.util.concurrent.TimeUnit; + +import org.apache.james.domainlist.api.DomainList; +import org.apache.james.metrics.api.Metric; +import org.apache.james.queue.api.MailQueue; +import org.apache.mailet.Mail; +import org.apache.mailet.base.test.FakeMail; +import org.apache.mailet.base.test.FakeMailetConfig; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Supplier; + +public class DeliveryRunnableTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(DeliveryRunnableTest.class); + public static final Date FIXED_DATE = new Date(1159599194961L); + public static final Supplier<Date> FIXED_DATE_SUPPLIER = new Supplier<Date>() { + @Override + public Date get() { + return FIXED_DATE; + } + }; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private DeliveryRunnable testee; + private Metric outgoingMailsMetric; + private Bouncer bouncer; + private MailDelivrer mailDelivrer; + private MailQueue mailQueue; + + @Before + public void setUp() { + FakeMailetConfig mailetConfig = FakeMailetConfig.builder() + .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1") + .setProperty(RemoteDeliveryConfiguration.DEBUG, "true") + .setProperty(RemoteDeliveryConfiguration.DELAY_TIME, "1000,2000,3000,4000,5000") + .build(); + + RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)); + outgoingMailsMetric = mock(Metric.class); + bouncer = mock(Bouncer.class); + mailDelivrer = mock(MailDelivrer.class); + mailQueue = mock(MailQueue.class); + testee = new DeliveryRunnable(mailQueue, configuration, outgoingMailsMetric, LOGGER, bouncer, mailDelivrer, DeliveryRunnable.DEFAULT_NOT_STARTED, FIXED_DATE_SUPPLIER); + } + + @Test + public void deliverySuccessShouldIncrementMetric() throws Exception { + FakeMail fakeMail = FakeMail.defaultFakeMail(); + when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.success()); + + testee.attemptDelivery(fakeMail); + + verify(outgoingMailsMetric).increment(); + verifyNoMoreInteractions(outgoingMailsMetric); + } + + @Test + public void deliveryPermanentFailureShouldBounceTheMail() throws Exception { + FakeMail fakeMail = FakeMail.defaultFakeMail(); + Exception exception = new Exception(); + when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.permanentFailure(exception)); + + testee.attemptDelivery(fakeMail); + + verify(bouncer).bounce(fakeMail, exception); + verifyNoMoreInteractions(bouncer); + } + + @Test + public void deliveryPermanentFailureShouldNotIncrementDeliveryMetric() throws Exception { + FakeMail fakeMail = FakeMail.defaultFakeMail(); + Exception exception = new Exception(); + when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.permanentFailure(exception)); + + testee.attemptDelivery(fakeMail); + + verifyNoMoreInteractions(outgoingMailsMetric); + } + + @Test + public void deliveryTemporaryFailureShouldNotIncrementDeliveryMetric() throws Exception { + FakeMail fakeMail = FakeMail.builder().state(Mail.DEFAULT).build(); + Exception exception = new Exception(); + when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.temporaryFailure(exception)); + + testee.attemptDelivery(fakeMail); + + verifyNoMoreInteractions(outgoingMailsMetric); + } + + @Test + public void deliveryTemporaryFailureShouldFailOnMailsWithoutState() throws Exception { + FakeMail fakeMail = FakeMail.defaultFakeMail(); + Exception exception = new Exception(); + when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.temporaryFailure(exception)); + + expectedException.expect(NullPointerException.class); + + testee.attemptDelivery(fakeMail); + } + + @Test + public void deliveryTemporaryFailureShouldRetryDelivery() throws Exception { + FakeMail fakeMail = FakeMail.builder().state(Mail.DEFAULT).build(); + Exception exception = new Exception(); + when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.temporaryFailure(exception)); + + testee.attemptDelivery(fakeMail); + + verify(mailQueue).enQueue(FakeMail.builder() + .attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, 1) + .state(Mail.ERROR) + .lastUpdated(FIXED_DATE) + .build(), + 1000, + TimeUnit.MILLISECONDS); + verifyNoMoreInteractions(mailQueue); + } + + @Test + public void deliveryTemporaryFailureShouldRetryDeliveryWithRightDelay() throws Exception { + FakeMail fakeMail = FakeMail.builder() + .state(Mail.ERROR) + .attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, 2) + .build(); + Exception exception = new Exception(); + when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.temporaryFailure(exception)); + + testee.attemptDelivery(fakeMail); + + verify(mailQueue).enQueue(FakeMail.builder() + .attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, 3) + .state(Mail.ERROR) + .lastUpdated(FIXED_DATE) + .build(), + 3000, + TimeUnit.MILLISECONDS); + verifyNoMoreInteractions(mailQueue); + } + + @Test + public void deliveryTemporaryFailureShouldRetryDeliveryOnMaximumRetryNumber() throws Exception { + FakeMail fakeMail = FakeMail.builder() + .state(Mail.ERROR) + .attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, 4) + .build(); + Exception exception = new Exception(); + when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.temporaryFailure(exception)); + + testee.attemptDelivery(fakeMail); + + verify(mailQueue).enQueue(FakeMail.builder() + .attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, 5) + .state(Mail.ERROR) + .lastUpdated(FIXED_DATE) + .build(), + 5000, + TimeUnit.MILLISECONDS); + verifyNoMoreInteractions(mailQueue); + } + + @Test + public void deliveryTemporaryFailureShouldNotRetryDeliveryOverMaximumRetryNumber() throws Exception { + FakeMail fakeMail = FakeMail.builder() + .state(Mail.ERROR) + .attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, 5) + .build(); + Exception exception = new Exception(); + when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.temporaryFailure(exception)); + + testee.attemptDelivery(fakeMail); + + verifyNoMoreInteractions(mailQueue); + } + + @Test + public void deliveryTemporaryFailureShouldBounceWhenRetryExceeded() throws Exception { + FakeMail fakeMail = FakeMail.builder() + .state(Mail.ERROR) + .attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, 5) + .build(); + Exception exception = new Exception(""); + when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.temporaryFailure(exception)); + + testee.attemptDelivery(fakeMail); + + verify(bouncer).bounce(eq(fakeMail), any(Exception.class)); + verifyNoMoreInteractions(bouncer); + } + + @Test + public void deliveryTemporaryFailureShouldResetDeliveryCountOnNonErrorState() throws Exception { + FakeMail fakeMail = FakeMail.builder() + .state(Mail.DEFAULT) + .attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, 5) + .build(); + Exception exception = new Exception(); + when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.temporaryFailure(exception)); + + testee.attemptDelivery(fakeMail); + + verify(mailQueue).enQueue(FakeMail.builder() + .attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, 1) + .state(Mail.ERROR) + .lastUpdated(FIXED_DATE) + .build(), + 1000, + TimeUnit.MILLISECONDS); + verifyNoMoreInteractions(mailQueue); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
