/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.mail.internal.thread; import java.util.Collections; import java.util.Iterator; import java.util.Properties; import java.util.UUID; import javax.inject.Provider; import javax.mail.Session; import javax.mail.internet.MimeMessage; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Mockito; import org.xwiki.component.util.DefaultParameterizedType; import org.xwiki.mail.ExtendedMimeMessage; import org.xwiki.mail.MailContentStore; import org.xwiki.mail.MailListener; import org.xwiki.mail.MailState; import org.xwiki.mail.MailStatus; import org.xwiki.mail.MailStoreException; import org.xwiki.mail.internal.MemoryMailListener; import org.xwiki.mail.internal.UpdateableMailStatusResult; import org.xwiki.test.annotation.ComponentList; import org.xwiki.test.mockito.MockitoComponentMockingRule; import com.xpn.xwiki.XWikiContext; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.when; /** * Unit tests for {@link SendMailRunnable}. * * @version $Id: 1e33c87757aeb41258a49f97db9fe9c98a2bf187 $ * @since 6.4 */ @ComponentList({ MemoryMailListener.class, SendMailQueueManager.class }) public class SendMailRunnableTest { @Rule public MockitoComponentMockingRule<SendMailRunnable> mocker = new MockitoComponentMockingRule<>(SendMailRunnable.class); @Before public void setUp() throws Exception { Provider<XWikiContext> xwikiContextProvider = this.mocker.registerMockComponent(XWikiContext.TYPE_PROVIDER); when(xwikiContextProvider.get()).thenReturn(Mockito.mock(XWikiContext.class)); } @Test public void sendMailWhenSendingFails() throws Exception { // Create a Session with an invalid host so that it generates an error Properties properties = new Properties(); properties.setProperty("mail.smtp.host", "xwiki-unknown"); Session session = Session.getDefaultInstance(properties); MimeMessage msg1 = new MimeMessage(session); msg1.setText("Content1"); ExtendedMimeMessage message1 = new ExtendedMimeMessage(msg1); String id1 = message1.getUniqueMessageId(); MimeMessage msg2 = new MimeMessage(session); msg2.setText("Content2"); ExtendedMimeMessage message2 = new ExtendedMimeMessage(msg2); String id2 = message2.getUniqueMessageId(); MemoryMailListener listener = this.mocker.getInstance(MailListener.class, "memory"); String batchId = UUID.randomUUID().toString(); listener.onPrepareBegin(batchId, Collections.<String, Object>emptyMap()); ((UpdateableMailStatusResult) listener.getMailStatusResult()).setTotalSize(2); SendMailQueueItem item1 = new SendMailQueueItem(id1, session, listener, batchId, "xwiki"); SendMailQueueItem item2 = new SendMailQueueItem(id2, session, listener, batchId, "xwiki"); MailQueueManager mailQueueManager = this.mocker.getInstance( new DefaultParameterizedType(null, MailQueueManager.class, SendMailQueueItem.class)); // Simulate loading the message from the content store MailContentStore contentStore = this.mocker.getInstance(MailContentStore.class, "filesystem"); when(contentStore.load(session, batchId, id1)).thenReturn(message1); when(contentStore.load(session, batchId, id2)).thenReturn(message2); // Send 2 mails. Both will fail but we want to verify that the second one is processed even though the first // one failed. mailQueueManager.addToQueue(item1); mailQueueManager.addToQueue(item2); MailRunnable runnable = this.mocker.getComponentUnderTest(); Thread thread = new Thread(runnable); thread.start(); // Wait for the mails to have been processed. try { listener.getMailStatusResult().waitTillProcessed(10000L); } finally { runnable.stopProcessing(); thread.interrupt(); thread.join(); } // This is the real test: we verify that there's been an error while sending each email. Iterator<MailStatus> statuses = listener.getMailStatusResult().getByState(MailState.SEND_ERROR); int errorCount = 0; while (statuses.hasNext()) { MailStatus status = statuses.next(); // Note: I would have liked to assert the exact message but it seems there can be different ones returned. // During my tests I got 2 different ones: // "UnknownHostException: xwiki-unknown" // "ConnectException: Connection refused" // Thus for now I only assert that there's an error set, but not its content. assertTrue(status.getErrorSummary() != null); errorCount++; } assertEquals(2, errorCount); } @Test public void sendMailWhenMailRetrievalFails() throws Exception { // Create a Session with an invalid host so that it generates an error Properties properties = new Properties(); Session session = Session.getDefaultInstance(properties); MimeMessage msg1 = new MimeMessage(session); msg1.setText("Content1"); ExtendedMimeMessage message1 = new ExtendedMimeMessage(msg1); String id1 = message1.getUniqueMessageId(); MimeMessage msg2 = new MimeMessage(session); msg2.setText("Content2"); ExtendedMimeMessage message2 = new ExtendedMimeMessage(msg2); String id2 = message2.getUniqueMessageId(); MemoryMailListener listener = this.mocker.getInstance(MailListener.class, "memory"); String batchId = UUID.randomUUID().toString(); listener.onPrepareBegin(batchId, Collections.<String, Object>emptyMap()); ((UpdateableMailStatusResult) listener.getMailStatusResult()).setTotalSize(2); listener.onPrepareMessageSuccess(message1, Collections.<String, Object>emptyMap()); SendMailQueueItem item1 = new SendMailQueueItem(id1, session, listener, batchId, "xwiki"); listener.onPrepareMessageSuccess(message2, Collections.<String, Object>emptyMap()); SendMailQueueItem item2 = new SendMailQueueItem(id2, session, listener, batchId, "xwiki"); MailQueueManager mailQueueManager = this.mocker.getInstance( new DefaultParameterizedType(null, MailQueueManager.class, SendMailQueueItem.class)); // Simulate loading the message from the content store MailContentStore contentStore = this.mocker.getInstance(MailContentStore.class, "filesystem"); when(contentStore.load(session, batchId, id1)).thenThrow(new MailStoreException("Store failure on message 1")); when(contentStore.load(session, batchId, id2)).thenThrow(new MailStoreException("Store failure on message 2")); // Send 2 mails. Both will fail but we want to verify that the second one is processed even though the first // one failed. mailQueueManager.addToQueue(item1); mailQueueManager.addToQueue(item2); MailRunnable runnable = this.mocker.getComponentUnderTest(); Thread thread = new Thread(runnable); thread.start(); // Wait for the mails to have been processed. try { listener.getMailStatusResult().waitTillProcessed(10000L); } finally { runnable.stopProcessing(); thread.interrupt(); thread.join(); } // This is the real test: we verify that there's been an error while sending each email. Iterator<MailStatus> statuses = listener.getMailStatusResult().getByState(MailState.SEND_FATAL_ERROR); int errorCount = 0; while (statuses.hasNext()) { MailStatus status = statuses.next(); // Note: I would have liked to assert the exact message but it seems there can be different ones returned. // During my tests I got 2 different ones: // "UnknownHostException: xwiki-unknown" // "ConnectException: Connection refused" // Thus for now I only assert that there's an error set, but not its content. assertEquals("MailStoreException: Store failure on message " + ++errorCount, status.getErrorSummary()); } assertEquals(2, errorCount); } }