blob: db8869b83ecb21dae8bbee8e37c1992452360aea [file] [log] [blame]
/*
* 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.commons.mail;
import static org.easymock.EasyMock.expect;
import static org.junit.Assert.*;
import static org.powermock.api.easymock.PowerMock.createMock;
import static org.powermock.api.easymock.PowerMock.replay;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import javax.activation.DataHandler;
import javax.mail.Header;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.apache.commons.mail.settings.EmailConfiguration;
import org.junit.After;
import org.junit.Before;
import org.subethamail.wiser.Wiser;
import org.subethamail.wiser.WiserMessage;
/**
* Base test case for Email test classes.
*
* @since 1.0
* @version $Id$
*/
public abstract class AbstractEmailTest
{
/** Padding at end of body added by wiser/send */
public static final int BODY_END_PAD = 3;
/** Padding at start of body added by wiser/send */
public static final int BODY_START_PAD = 2;
/** Line separator in email messages */
private static final String LINE_SEPARATOR = "\r\n";
/** default port */
private static int mailServerPort = 2500;
/** The fake Wiser email server */
protected Wiser fakeMailServer;
/** Mail server used for testing */
protected String strTestMailServer = "localhost";
/** From address for the test email */
protected String strTestMailFrom = "test_from@apache.org";
/** Destination address for the test email */
protected String strTestMailTo = "test_to@apache.org";
/** Mailserver username (set if needed) */
protected String strTestUser = "user";
/** Mailserver strTestPasswd (set if needed) */
protected String strTestPasswd = "password";
/** URL to used to test URL attachments (Must be valid) */
protected String strTestURL = EmailConfiguration.TEST_URL;
/** Test characters acceptable to email */
protected String[] testCharsValid =
{
" ",
"a",
"A",
"\uc5ec",
"0123456789",
"012345678901234567890",
"\n"
};
/** Array of test strings */
protected String[] testCharsNotValid = {"", null};
/** Where to save email output **/
private File emailOutputDir;
/** counter for creating a file name */
private static int fileNameCounter;
@Before
public void setUpAbstractEmailTest()
{
emailOutputDir = new File("target/test-emails");
if (!emailOutputDir.exists())
{
emailOutputDir.mkdirs();
}
}
@After
public void tearDownEmailTest()
{
//stop the fake email server (if started)
if (this.fakeMailServer != null && !isMailServerStopped(fakeMailServer))
{
this.fakeMailServer.stop();
assertTrue("Mail server didn't stop", isMailServerStopped(fakeMailServer));
}
this.fakeMailServer = null;
}
/**
* Gets the mail server port.
* @return the port the server is running on.
*/
protected int getMailServerPort()
{
return mailServerPort;
}
/**
* Safe a mail to a file using a more or less unique file name.
*
* @param email email
* @throws IOException writing the email failed
* @throws MessagingException writing the email failed
*/
protected void saveEmailToFile(WiserMessage email) throws IOException, MessagingException
{
int currCounter = fileNameCounter++ % 10;
String emailFileName = "email" + new Date().getTime() + "-" + currCounter + ".eml";
File emailFile = new File(emailOutputDir, emailFileName);
EmailUtils.writeMimeMessage(emailFile, email.getMimeMessage() );
}
/**
* @param intMsgNo the message to retrieve
* @return message as string
*/
public String getMessageAsString(int intMsgNo)
{
List<?> receivedMessages = fakeMailServer.getMessages();
assertTrue("mail server didn't get enough messages", receivedMessages.size() >= intMsgNo);
WiserMessage emailMessage = (WiserMessage) receivedMessages.get(intMsgNo);
if (emailMessage != null)
{
try
{
return serializeEmailMessage(emailMessage);
}
catch (Exception e)
{
// ignore, since the test will fail on an empty string return
}
}
fail("Message not found");
return "";
}
/**
* Initializes the stub mail server. Fails if the server cannot be proven
* to have started. If the server is already started, this method returns
* without changing the state of the server.
*/
public void getMailServer()
{
if (this.fakeMailServer == null || isMailServerStopped(fakeMailServer))
{
mailServerPort++;
this.fakeMailServer = new Wiser();
this.fakeMailServer.setPort(getMailServerPort());
this.fakeMailServer.start();
assertFalse("fake mail server didn't start", isMailServerStopped(fakeMailServer));
Date dtStartWait = new Date();
while (isMailServerStopped(fakeMailServer))
{
// test for connected
if (this.fakeMailServer != null
&& !isMailServerStopped(fakeMailServer))
{
break;
}
// test for timeout
if ((dtStartWait.getTime() + EmailConfiguration.TIME_OUT)
<= new Date().getTime())
{
fail("Mail server failed to start");
}
}
}
}
/**
* Validate the message was sent properly
* @param mailServer reference to the fake mail server
* @param strSubject expected subject
* @param fromAdd expected from address
* @param toAdd list of expected to addresses
* @param ccAdd list of expected cc addresses
* @param bccAdd list of expected bcc addresses
* @param boolSaveToFile true will output to file, false doesnt
* @return WiserMessage email to check
* @throws IOException Exception
*/
protected WiserMessage validateSend(
Wiser mailServer,
String strSubject,
InternetAddress fromAdd,
List<InternetAddress> toAdd,
List<InternetAddress> ccAdd,
List<InternetAddress> bccAdd,
boolean boolSaveToFile)
throws IOException
{
assertTrue("mail server doesn't contain expected message",
mailServer.getMessages().size() == 1);
WiserMessage emailMessage = mailServer.getMessages().get(0);
if (boolSaveToFile)
{
try
{
this.saveEmailToFile(emailMessage);
}
catch(MessagingException me)
{
IllegalStateException ise =
new IllegalStateException("caught MessagingException during saving the email");
ise.initCause(me);
throw ise;
}
}
try
{
// get the MimeMessage
MimeMessage mimeMessage = emailMessage.getMimeMessage();
// test subject
assertEquals("got wrong subject from mail",
strSubject, mimeMessage.getHeader("Subject", null));
//test from address
assertEquals("got wrong From: address from mail",
fromAdd.toString(), mimeMessage.getHeader("From", null));
//test to address
assertTrue("got wrong To: address from mail",
toAdd.toString().contains(mimeMessage.getHeader("To", null)));
//test cc address
if (ccAdd.size() > 0)
{
assertTrue("got wrong Cc: address from mail",
ccAdd.toString().contains(mimeMessage.getHeader("Cc", null)));
}
//test bcc address
if (bccAdd.size() > 0)
{
assertTrue("got wrong Bcc: address from mail",
bccAdd.toString().contains(mimeMessage.getHeader("Bcc", null)));
}
}
catch (MessagingException me)
{
IllegalStateException ise =
new IllegalStateException("caught MessagingException in validateSend()");
ise.initCause(me);
throw ise;
}
return emailMessage;
}
/**
* Validate the message was sent properly
* @param mailServer reference to the fake mail server
* @param strSubject expected subject
* @param content the expected message content
* @param fromAdd expected from address
* @param toAdd list of expected to addresses
* @param ccAdd list of expected cc addresses
* @param bccAdd list of expected bcc addresses
* @param boolSaveToFile true will output to file, false doesnt
* @throws IOException Exception
*/
protected void validateSend(
Wiser mailServer,
String strSubject,
Multipart content,
InternetAddress fromAdd,
List<InternetAddress> toAdd,
List<InternetAddress> ccAdd,
List<InternetAddress> bccAdd,
boolean boolSaveToFile)
throws IOException
{
// test other properties
WiserMessage emailMessage = this.validateSend(
mailServer,
strSubject,
fromAdd,
toAdd,
ccAdd,
bccAdd,
boolSaveToFile);
// test message content
// get sent email content
String strSentContent =
content.getContentType();
// get received email content (chop off the auto-added \n
// and -- (front and end)
String emailMessageBody = getMessageBody(emailMessage);
String strMessageBody =
emailMessageBody.substring(AbstractEmailTest.BODY_START_PAD,
emailMessageBody.length()
- AbstractEmailTest.BODY_END_PAD);
assertTrue("didn't find expected content type in message body",
strMessageBody.contains(strSentContent));
}
/**
* Validate the message was sent properly
* @param mailServer reference to the fake mail server
* @param strSubject expected subject
* @param strMessage the expected message as a string
* @param fromAdd expected from address
* @param toAdd list of expected to addresses
* @param ccAdd list of expected cc addresses
* @param bccAdd list of expected bcc addresses
* @param boolSaveToFile true will output to file, false doesnt
* @throws IOException Exception
*/
protected void validateSend(
Wiser mailServer,
String strSubject,
String strMessage,
InternetAddress fromAdd,
List<InternetAddress> toAdd,
List<InternetAddress> ccAdd,
List<InternetAddress> bccAdd,
boolean boolSaveToFile)
throws IOException
{
// test other properties
WiserMessage emailMessage = this.validateSend(
mailServer,
strSubject,
fromAdd,
toAdd,
ccAdd,
bccAdd,
true);
// test message content
assertTrue("didn't find expected message content in message body",
getMessageBody(emailMessage).contains(strMessage));
}
/**
* Serializes the {@link MimeMessage} from the <code>WiserMessage</code>
* passed in. The headers are serialized first followed by the message
* body.
*
* @param wiserMessage The <code>WiserMessage</code> to serialize.
* @return The string format of the message.
* @throws MessagingException
* @throws IOException
* Thrown while serializing the body from
* {@link DataHandler#writeTo(java.io.OutputStream)}.
* @throws MessagingException
* Thrown while getting the body content from
* {@link MimeMessage#getDataHandler()}
* @since 1.1
*/
private String serializeEmailMessage(WiserMessage wiserMessage)
throws MessagingException, IOException
{
if (wiserMessage == null)
{
return "";
}
StringBuffer serializedEmail = new StringBuffer();
MimeMessage message = wiserMessage.getMimeMessage();
// Serialize the headers
for (Enumeration<?> headers = message.getAllHeaders(); headers
.hasMoreElements();)
{
Header header = (Header) headers.nextElement();
serializedEmail.append(header.getName());
serializedEmail.append(": ");
serializedEmail.append(header.getValue());
serializedEmail.append(LINE_SEPARATOR);
}
// Serialize the body
byte[] messageBody = getMessageBodyBytes(message);
serializedEmail.append(LINE_SEPARATOR);
serializedEmail.append(messageBody);
serializedEmail.append(LINE_SEPARATOR);
return serializedEmail.toString();
}
/**
* Returns a string representation of the message body. If the message body
* cannot be read, an empty string is returned.
*
* @param wiserMessage The wiser message from which to extract the message body
* @return The string representation of the message body
* @throws IOException
* Thrown while serializing the body from
* {@link DataHandler#writeTo(java.io.OutputStream)}.
* @since 1.1
*/
private String getMessageBody(WiserMessage wiserMessage)
throws IOException
{
if (wiserMessage == null)
{
return "";
}
byte[] messageBody = null;
try
{
MimeMessage message = wiserMessage.getMimeMessage();
messageBody = getMessageBodyBytes(message);
}
catch (MessagingException me)
{
// Thrown while getting the body content from
// {@link MimeMessage#getDataHandler()}
IllegalStateException ise =
new IllegalStateException("couldn't process MimeMessage from WiserMessage in getMessageBody()");
ise.initCause(me);
throw ise;
}
return (messageBody != null) ? (new String(messageBody).intern()) : "";
}
/**
* Gets the byte making up the body of the message.
*
* @param mimeMessage
* The mime message from which to extract the body.
* @return A byte array representing the message body
* @throws IOException
* Thrown while serializing the body from
* {@link DataHandler#writeTo(java.io.OutputStream)}.
* @throws MessagingException
* Thrown while getting the body content from
* {@link MimeMessage#getDataHandler()}
* @since 1.1
*/
private byte[] getMessageBodyBytes(MimeMessage mimeMessage)
throws IOException, MessagingException
{
DataHandler dataHandler = mimeMessage.getDataHandler();
ByteArrayOutputStream byteArrayOutStream = new ByteArrayOutputStream();
BufferedOutputStream buffOs = new BufferedOutputStream(
byteArrayOutStream);
dataHandler.writeTo(buffOs);
buffOs.flush();
return byteArrayOutStream.toByteArray();
}
/**
* Checks if an email server is running at the address stored in the
* <code>fakeMailServer</code>.
*
* @param fakeMailServer
* The server from which the address is picked up.
* @return <code>true</code> if the server claims to be running
* @since 1.1
*/
protected boolean isMailServerStopped(Wiser fakeMailServer) {
return !fakeMailServer.getServer().isRunning();
}
/**
* Create a mocked URL object which always throws an IOException
* when the openStream() method is called.
* <p>
* Several ISPs do resolve invalid URLs like {@code http://example.invalid}
* to some error page causing tests to fail otherwise.
*
* @return an invalid URL
*/
protected URL createInvalidURL() throws Exception {
URL url = createMock(URL.class);
expect(url.openStream()).andThrow(new IOException());
replay(url);
return url;
}
}