net.kemitix.wiser.assertions.WiserAssertions Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of wiser-assertions Show documentation
Show all versions of wiser-assertions Show documentation
Assertions for Wiser SMTP test server from subethamail
The newest version!
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 Paul Campbell
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package net.kemitix.wiser.assertions;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import net.kemitix.mon.maybe.Maybe;
import net.kemitix.mon.result.Result;
import org.subethamail.wiser.Wiser;
import org.subethamail.wiser.WiserMessage;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
/**
* Provides a set of assertions for checking the status of any messages received
* by subethamail's Wiser.
*
*
* {@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);
* }
*
*
*/
@SuppressWarnings("methodcount")
public final class WiserAssertions {
private static final String ERROR_MESSAGE_SUBJECT = "No message with subject [{0}] found!";
private static final String ERROR_MESSAGE_CONTENT_CONTAINS = "No message with content containing [{0}] found!";
private static final String ERROR_MESSAGE_CONTENT = "No message with content [{0}] found!";
private static final String ERROR_MESSAGE_TO = "No message to [{0}] found!";
/**
* The messages received by Wiser.
*/
private final List messages;
/**
* Private constructor.
*
* @param wiserMessages the messages to be tested by the assertions
*/
private WiserAssertions(final List wiserMessages) {
this.messages = wiserMessages;
}
/**
* 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());
}
/**
* 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) {
messageMatches(m -> m.getEnvelopeSender().equals(sender))
.orElseThrow(assertionError("No message from [{0}] found!", sender));
return this;
}
private Optional messageMatches(final Predicate predicate) {
return messages.stream()
.filter(predicate)
.findAny();
}
/**
* 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));
}
/**
* 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) {
messageMatches(m -> m.getEnvelopeReceiver().equals(recipient))
.orElseThrow(assertionError(ERROR_MESSAGE_TO, recipient));
return this;
}
/**
* 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) {
messageMatches(m -> subject(m).equals(subject))
.orElseThrow(assertionError(ERROR_MESSAGE_SUBJECT, subject));
return this;
}
@SuppressFBWarnings("EXS_EXCEPTION_SOFTENING_NO_CONSTRAINTS")
private String subject(final WiserMessage wiserMessage) {
try {
return wiserMessage.getMimeMessage().getSubject();
} catch (MessagingException e) {
throw new IllegalArgumentException("Invalid email message", e);
}
}
/**
* 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) {
messageMatches(m -> subject(m).contains(subject))
.orElseThrow(assertionError(ERROR_MESSAGE_SUBJECT, subject));
return this;
}
/**
* 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) {
messageMatches(m -> messageBody(m).trim().equals(content.trim()))
.orElseThrow(assertionError(ERROR_MESSAGE_CONTENT, content));
return this;
}
@SuppressFBWarnings("EXS_EXCEPTION_SOFTENING_NO_CONSTRAINTS")
private String messageBody(final WiserMessage m) {
try {
return messageBody(m.getMimeMessage().getContent());
} catch (IOException | MessagingException e) {
throw new RuntimeException(e);
}
}
private String messageBody(final Object content) {
return contentAsString(content)
.or(() -> contentAsMimeMessage(content))
.or(() -> contentAsMultiPartMime(content))
.orElseThrow(() -> new RuntimeException("Unexpected MimeMessage content"));
}
private Maybe contentAsString(final Object content) {
if (content instanceof String) {
return Maybe.just((String) content);
}
return Maybe.nothing();
}
private Maybe contentAsMimeMessage(final Object content) {
if (content instanceof MimeMessage) {
return Maybe.just(content.toString());
}
return Maybe.nothing();
}
private Maybe contentAsMultiPartMime(final Object content) {
if (content instanceof MimeMultipart) {
return mimeMultipartAsString((MimeMultipart) content);
}
return Maybe.nothing();
}
/**
* 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) {
messageMatches((WiserMessage m) -> messageBody(m).trim().contains(content))
.orElseThrow(assertionError(ERROR_MESSAGE_CONTENT_CONTAINS, content));
return this;
}
/**
* Converts a {@link MimeMultipart} into a {@link String} stripping out the
* mime part boundary and headers..
*
* @param mimeMultipart the message part to convert
*
* @return the message part as a string
*/
private Maybe mimeMultipartAsString(final MimeMultipart mimeMultipart) {
return Result.toMaybe(Result.of(mimeMultipart::getCount)
.map(count -> bodyPartsAsString(mimeMultipart, count)));
}
private String bodyPartsAsString(final MimeMultipart mimeMultipart, final int count) {
return IntStream.range(0, count)
.mapToObj(i -> bodyPart(mimeMultipart, i))
.map(this::bodyPartAsString)
.map(Result::orElseThrowUnchecked)
.collect(Collectors.joining());
}
private Result bodyPartAsString(final Result bodyPart) {
return bodyPartContent(bodyPart)
.flatMap(this::contentObjectAsString);
}
private Result © 2015 - 2025 Weber Informatics LLC | Privacy Policy