diff --git a/src/main/java/net/kemitix/wiser/assertions/WiserAssertions.java b/src/main/java/net/kemitix/wiser/assertions/WiserAssertions.java
index 5d93ae9..50dbf4c 100644
--- a/src/main/java/net/kemitix/wiser/assertions/WiserAssertions.java
+++ b/src/main/java/net/kemitix/wiser/assertions/WiserAssertions.java
@@ -1,5 +1,8 @@
package net.kemitix.wiser.assertions;
+import org.subethamail.wiser.Wiser;
+import org.subethamail.wiser.WiserMessage;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
@@ -7,55 +10,137 @@ import java.text.MessageFormat;
import java.util.List;
import java.util.function.Predicate;
import java.util.function.Supplier;
+
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
-import org.subethamail.wiser.Wiser;
-import org.subethamail.wiser.WiserMessage;
/**
- * Taken from the WiserAssetions class from Rafal Browiec at
- * http://blog.codeleak.pl/2014/09/testing-mail-code-in-spring-boot.html
+ * Provides a set of assertions for checking the status of any messages received
+ * by subethamail's Wiser.
+ *
+ *
+ * {@code
+ * {@literal @}Before
+ * public void setUp() throws IOException {
+ * wiser = new Wiser(PORT);
+ * wiser.start();
+ * }
+ *
+ * {@literal @}After public void tearDown() { wiser.stop(); }
+ *
+ * {@literal @}Test public void testMail() { //given ...
+ *
+ * //when ...
+ *
+ * //then WiserAssertions.assertReceivedMessage(wiser) .from(sender)
+ * .to(recipient_alpha) .to(recipient_beta) .withSubjectContains(subject_prefix)
+ * .withSubjectContains(subject_suffix) .withContentContains(message_element_1)
+ * .withContentContains(message_element_2)
+ * .withContentContains(message_element_3); }
+ * }
+ *
*/
-public class WiserAssertions {
+public final class WiserAssertions {
+ /**
+ * The messages received by Wiser.
+ */
private final List messages;
- public static WiserAssertions assertReceivedMessage(Wiser wiser) {
+ /**
+ * Creates an instance of {@code} WiserAssertions} ready to make assertions
+ * on any messages received by the {@link Wiser} server.
+ *
+ * @param wiser the SMTP server instance
+ *
+ * @return an instance of {@code WiserAssertions}
+ */
+ public static WiserAssertions assertReceivedMessage(final Wiser wiser) {
return new WiserAssertions(wiser.getMessages());
}
- private WiserAssertions(List messages) {
- this.messages = messages;
+ /**
+ * Private constructor.
+ *
+ * @param wiserMessages the messages to be tested by the assertions
+ */
+ private WiserAssertions(final List wiserMessages) {
+ this.messages = wiserMessages;
}
- public WiserAssertions from(String from) {
- findFirstOrElseThrow(m -> m.getEnvelopeSender().equals(from),
- assertionError("No message from [{0}] found!", from));
+ /**
+ * Checks that there was at least one email received that was sent from the
+ * {@code sender}.
+ *
+ * @param sender email address to search for
+ *
+ * @return the {@code WiserAssertions} instance
+ */
+ public WiserAssertions from(final String sender) {
+ findFirstOrElseThrow(m -> m.getEnvelopeSender().equals(sender),
+ assertionError("No message from [{0}] found!", sender));
return this;
}
- public WiserAssertions to(String to) {
- findFirstOrElseThrow(m -> m.getEnvelopeReceiver().equals(to),
- assertionError("No message to [{0}] found!", to));
+ /**
+ * Checks that there was at least one email received that was sent to the
+ * {@code recipient}.
+ *
+ * @param recipient email address to search for
+ *
+ * @return the {@code WiserAssertions} instance
+ */
+ public WiserAssertions to(final String recipient) {
+ findFirstOrElseThrow(m -> m.getEnvelopeReceiver().equals(recipient),
+ assertionError("No message to [{0}] found!", recipient));
return this;
}
- public WiserAssertions withSubject(String subject) {
- Predicate predicate = m -> subject.equals(unchecked(getMimeMessage(m)::getSubject));
+ /**
+ * Checks that there was at least one email received that has the required
+ * subject.
+ *
+ * @param subject the subject line to search for
+ *
+ * @return the {@code WiserAssertions} instance
+ */
+ public WiserAssertions withSubject(final String subject) {
+ Predicate predicate
+ = m -> subject.equals(unchecked(getMimeMessage(m)::getSubject));
findFirstOrElseThrow(predicate,
- assertionError("No message with subject [{0}] found!", subject));
+ assertionError("No message with subject [{0}] found!",
+ subject));
return this;
}
- public WiserAssertions withSubjectContains(String subject) {
- Predicate predicate = m -> unchecked(getMimeMessage(m)::getSubject).contains(subject);
+ /**
+ * Checks that there was at least one email received that has a subject that
+ * contains the search text.
+ *
+ * @param subject the text to search for in the subject
+ *
+ * @return the {@code WiserAssertions} instance
+ */
+ public WiserAssertions withSubjectContains(final String subject) {
+ Predicate predicate
+ = m -> unchecked(getMimeMessage(m)::getSubject)
+ .contains(subject);
findFirstOrElseThrow(predicate,
- assertionError("No message with subject [{0}] found!", subject));
+ assertionError("No message with subject [{0}] found!",
+ subject));
return this;
}
- public WiserAssertions withContent(String content) {
+ /**
+ * Check that there was at least one email received that has a body that
+ * matches the content.
+ *
+ * @param content the body of the email to search for
+ *
+ * @return the {@code WiserAssertions} instance
+ */
+ public WiserAssertions withContent(final String content) {
findFirstOrElseThrow(m -> {
ThrowingSupplier contentAsString
= () -> getMimeMessageBody(m).trim();
@@ -64,19 +149,40 @@ public class WiserAssertions {
return this;
}
- public WiserAssertions withContentContains(String content) {
+ /**
+ * Check that there was at least one email received that contains the search
+ * text.
+ *
+ * @param content the text to search for in the body of the email
+ *
+ * @return the {@code WiserAssertions} instance
+ */
+ public WiserAssertions withContentContains(final String content) {
StringBuilder messageContent = new StringBuilder();
findFirstOrElseThrow((WiserMessage m) -> {
ThrowingSupplier contentAsString
= () -> getMimeMessageBody(m).trim();
messageContent.append(unchecked(contentAsString));
return unchecked(contentAsString).contains(content);
- }, assertionError("No message with content containing [{0}] found! Was {1}", content, messageContent));
+ }, assertionError(
+ "No message with content containing [{0}] found! Was {1}",
+ content, messageContent));
return this;
}
- private String getMimeMessageBody(WiserMessage m) throws IOException, MessagingException {
- Object content = getMimeMessage(m).getContent();
+ /**
+ * Returns the body of the message.
+ *
+ * @param message the message
+ *
+ * @return the body of the message
+ *
+ * @throws IOException if error extracting the mime message
+ * @throws MessagingException if the message type is not known
+ */
+ private String getMimeMessageBody(final WiserMessage message)
+ throws IOException, MessagingException {
+ Object content = getMimeMessage(message).getContent();
if (content instanceof MimeMessage) {
return (String) content;
}
@@ -86,20 +192,63 @@ public class WiserAssertions {
throw new RuntimeException("Unexpected MimeMessage content");
}
- private void findFirstOrElseThrow(Predicate predicate, Supplier exceptionSupplier) {
+ /**
+ * Checks that at least on message matches the predicate or the supplied
+ * exception will be thrown.
+ *
+ * @param predicate the condition a message must match
+ * @param exceptionSupplier the supplier of the exception
+ */
+ private void findFirstOrElseThrow(
+ final Predicate predicate,
+ final Supplier exceptionSupplier
+ ) {
messages.stream().filter(predicate)
.findFirst().orElseThrow(exceptionSupplier);
}
- private MimeMessage getMimeMessage(WiserMessage wiserMessage) {
+ /**
+ * Returns the mime message within the {@link WiserMessage} converting any
+ * {@link MessagingException}s into {@link RuntimeException}s.
+ *
+ * @param wiserMessage the message
+ *
+ * @return the mime message
+ */
+ private MimeMessage getMimeMessage(final WiserMessage wiserMessage) {
return unchecked(wiserMessage::getMimeMessage);
}
- private static Supplier assertionError(String errorMessage, Object... args) {
- return () -> new AssertionError(MessageFormat.format(errorMessage, args));
+ /**
+ * Returns a {@link Supplier} for an {@link AssertionError}.
+ *
+ * @param errorMessage the message for the exception
+ * @param args the parameters to insert into the message using
+ * {@link MessageFormat}
+ *
+ * @return a supplier of an {@link AssertionError}
+ */
+ @SuppressWarnings(
+ {"ThrowableInstanceNotThrown", "ThrowableInstanceNeverThrown"})
+ private static Supplier assertionError(
+ final String errorMessage,
+ final Object... args
+ ) {
+ return ()
+ -> new AssertionError(MessageFormat.format(errorMessage, args));
}
- public static T unchecked(ThrowingSupplier supplier) {
+ /**
+ * Convert any checked Exceptions into unchecked Exceptions.
+ *
+ * @param the item type to be returned after suppressing any
+ * checked exceptions
+ * @param supplier the source of the return value that could cause a checked
+ * exception
+ *
+ * @return the product of the supplier
+ */
+ public static T unchecked(final ThrowingSupplier supplier) {
try {
return supplier.get();
} catch (Throwable e) {
@@ -107,14 +256,38 @@ public class WiserAssertions {
}
}
- private String getMimeMultipartAsString(MimeMultipart mimeMultipart) throws MessagingException, IOException {
+ /**
+ * Converts a {@link MimeMultipart} into a {@link String}.
+ *
+ * @param mimeMultipart the message part to convert
+ *
+ * @return the message part as a string
+ *
+ * @throws MessagingException if the part is empty
+ * @throws IOException if there is another error
+ */
+ private String getMimeMultipartAsString(final MimeMultipart mimeMultipart)
+ throws MessagingException, IOException {
OutputStream os = new ByteArrayOutputStream();
mimeMultipart.writeTo(os);
return os.toString();
}
+ /**
+ * Interface for providing a value that could thrown an exception when
+ * sought.
+ *
+ * @param the type of value to be supplied
+ */
public interface ThrowingSupplier {
+ /**
+ * Returns the value.
+ *
+ * @return the value
+ *
+ * @throws Throwable on error
+ */
T get() throws Throwable;
}
}