This is an automated email from the ASF dual-hosted git repository. btellier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 77bc6458388abb3491cbb3ed6de351f4df656aae Author: Tran Tien Duc <[email protected]> AuthorDate: Thu Aug 22 14:51:39 2019 +0700 JAMES-2865 MockSMTPServer with mock behaviors - Condition filtering will be tested later - Counting Number of Anwsers is not implemented --- .../james/mock/smtp/server/MockMessageHandler.java | 81 +++++++++++++++++--- .../james/mock/smtp/server/MockSMTPServer.java | 16 +++- .../james/mock/smtp/server/MockSMTPServerTest.java | 87 ++++++++++++++++++++-- .../org/apache/james/utils/SMTPMessageSender.java | 2 +- 4 files changed, 167 insertions(+), 19 deletions(-) diff --git a/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/MockMessageHandler.java b/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/MockMessageHandler.java index 7322fa7..15323ec 100644 --- a/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/MockMessageHandler.java +++ b/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/MockMessageHandler.java @@ -22,51 +22,92 @@ package org.apache.james.mock.smtp.server; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Optional; import javax.mail.internet.AddressException; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.NotImplementedException; import org.apache.james.core.MailAddress; import org.apache.james.mock.smtp.server.model.Mail; +import org.apache.james.mock.smtp.server.model.MockSMTPBehavior; +import org.apache.james.mock.smtp.server.model.Response; +import org.apache.james.mock.smtp.server.model.SMTPCommand; import org.subethamail.smtp.MessageHandler; import org.subethamail.smtp.RejectException; import org.subethamail.smtp.TooMuchDataException; public class MockMessageHandler implements MessageHandler { + @FunctionalInterface + interface Behavior<T> { + void behave(T input) throws RejectException; + } + + class MockBehavior<T> implements Behavior<T> { + + private final MockSMTPBehavior behavior; + + MockBehavior(MockSMTPBehavior behavior) { + this.behavior = behavior; + } + + @Override + public void behave(T input) throws RejectException { + Response response = behavior.getResponse(); + if (response.isServerRejected()) { + throw new RejectException(response.getCode().getRawCode(), response.getMessage()); + } + throw new NotImplementedException("Not rejecting commands in mock behaviours is not supported yet"); + } + } + private static final int ARGUMENT_SYNTAX_ERROR_501 = 501; private final Mail.Envelope.Builder envelopeBuilder; private final Mail.Builder mailBuilder; private final ReceivedMailRepository mailRepository; + private final List<MockSMTPBehavior> behaviors; - MockMessageHandler(ReceivedMailRepository mailRepository) { + MockMessageHandler(ReceivedMailRepository mailRepository, List<MockSMTPBehavior> behaviors) { this.mailRepository = mailRepository; + this.behaviors = behaviors; this.envelopeBuilder = new Mail.Envelope.Builder(); this.mailBuilder = new Mail.Builder(); } @Override public void from(String from) throws RejectException { - try { - envelopeBuilder.from(new MailAddress(from)); - } catch (AddressException e) { - throw new RejectException(ARGUMENT_SYNTAX_ERROR_501, "invalid email address supplied"); - } + Behavior<MailAddress> fromBehavior = firstMatchedBehavior(SMTPCommand.MAIL_FROM) + .<Behavior<MailAddress>>map(MockBehavior::new) + .orElseGet(() -> envelopeBuilder::from); + + fromBehavior.behave(parse(from)); } @Override public void recipient(String recipient) throws RejectException { - try { - envelopeBuilder.addRecipient(new MailAddress(recipient)); - } catch (AddressException e) { - throw new RejectException(ARGUMENT_SYNTAX_ERROR_501, "invalid email address supplied"); - } + Behavior<MailAddress> recipientBehavior = firstMatchedBehavior(SMTPCommand.RCPT_TO) + .<Behavior<MailAddress>>map(MockBehavior::new) + .orElseGet(() -> envelopeBuilder::addRecipient); + + recipientBehavior.behave(parse(recipient)); } @Override public void data(InputStream data) throws RejectException, TooMuchDataException, IOException { - mailBuilder.message(IOUtils.toString(data, StandardCharsets.UTF_8)); + Behavior<InputStream> dataBehavior = firstMatchedBehavior(SMTPCommand.DATA) + .<Behavior<InputStream>>map(MockBehavior::new) + .orElseGet(() -> content -> mailBuilder.message(readData(content))); + + dataBehavior.behave(data); + } + + private Optional<MockSMTPBehavior> firstMatchedBehavior(SMTPCommand data) { + return behaviors.stream() + .filter(behavior -> behavior.getCommand().equals(data)) + .findFirst(); } @Override @@ -75,4 +116,20 @@ public class MockMessageHandler implements MessageHandler { .build(); mailRepository.store(mail); } + + private String readData(InputStream data) { + try { + return IOUtils.toString(data, StandardCharsets.UTF_8); + } catch (IOException e) { + throw new RejectException(ARGUMENT_SYNTAX_ERROR_501, "invalid data supplied"); + } + } + + private MailAddress parse(String mailAddress) { + try { + return new MailAddress(mailAddress); + } catch (AddressException e) { + throw new RejectException(ARGUMENT_SYNTAX_ERROR_501, "invalid email address supplied"); + } + } } diff --git a/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/MockSMTPServer.java b/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/MockSMTPServer.java index 96246c4..9f05e25 100644 --- a/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/MockSMTPServer.java +++ b/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/MockSMTPServer.java @@ -19,9 +19,12 @@ package org.apache.james.mock.smtp.server; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.apache.james.mock.smtp.server.model.Mail; +import org.apache.james.mock.smtp.server.model.MockSMTPBehavior; import org.apache.james.util.Port; import org.subethamail.smtp.server.SMTPServer; @@ -29,10 +32,12 @@ class MockSMTPServer { private final SMTPServer server; private final ReceivedMailRepository mailRepository; + private final List<MockSMTPBehavior> behaviors; MockSMTPServer() { + this.behaviors = new ArrayList<>(); this.mailRepository = new ReceivedMailRepository(); - this.server = new SMTPServer(ctx -> new MockMessageHandler(mailRepository)); + this.server = new SMTPServer(ctx -> new MockMessageHandler(mailRepository, behaviors)); this.server.setPort(0); } @@ -52,5 +57,14 @@ class MockSMTPServer { void stop() { server.stop(); + clearBehavior(); + } + + void setBehaviors(MockSMTPBehavior... behaviors) { + this.behaviors.addAll(Arrays.asList(behaviors)); + } + + private void clearBehavior() { + behaviors.clear(); } } diff --git a/server/mailet/mock-smtp-server/src/test/java/org/apache/james/mock/smtp/server/MockSMTPServerTest.java b/server/mailet/mock-smtp-server/src/test/java/org/apache/james/mock/smtp/server/MockSMTPServerTest.java index c2c5bdc..4e9cb09 100644 --- a/server/mailet/mock-smtp-server/src/test/java/org/apache/james/mock/smtp/server/MockSMTPServerTest.java +++ b/server/mailet/mock-smtp-server/src/test/java/org/apache/james/mock/smtp/server/MockSMTPServerTest.java @@ -23,6 +23,10 @@ import static org.apache.james.mock.smtp.server.Fixture.ALICE; import static org.apache.james.mock.smtp.server.Fixture.BOB; import static org.apache.james.mock.smtp.server.Fixture.DOMAIN; import static org.apache.james.mock.smtp.server.Fixture.JACK; +import static org.apache.james.mock.smtp.server.model.Response.SMTPStatusCode.SERVICE_NOT_AVAILABLE_421; +import static org.apache.james.mock.smtp.server.model.SMTPCommand.DATA; +import static org.apache.james.mock.smtp.server.model.SMTPCommand.MAIL_FROM; +import static org.apache.james.mock.smtp.server.model.SMTPCommand.RCPT_TO; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -32,9 +36,12 @@ import java.util.List; import javax.mail.internet.MimeMessage; +import org.apache.commons.net.smtp.SMTPConnectionClosedException; import org.apache.james.core.MailAddress; import org.apache.james.core.builder.MimeMessageBuilder; +import org.apache.james.mock.smtp.server.model.Condition; import org.apache.james.mock.smtp.server.model.Mail; +import org.apache.james.mock.smtp.server.model.MockSMTPBehavior; import org.apache.james.mock.smtp.server.model.Response; import org.apache.james.util.MimeMessageUtil; import org.apache.james.util.Port; @@ -52,8 +59,6 @@ import com.github.fge.lambdas.Throwing; class MockSMTPServerTest { - private static final Response.SMTPStatusCode SERVICE_NOT_AVAILABLE_421 = Response.SMTPStatusCode.of(421); - @Nested class NormalBehaviorTests { private MockSMTPServer mockServer; @@ -75,9 +80,9 @@ class MockSMTPServerTest { .connect("localhost", mockServer.getPort()); MimeMessage message = MimeMessageBuilder.mimeMessageBuilder() - .setSubject("test") - .setText("any text") - .build(); + .setSubject("test") + .setText("any text") + .build(); FakeMail mail = FakeMail.builder() .name("name") @@ -104,6 +109,78 @@ class MockSMTPServerTest { } } + @Nested + class MailMockBehaviorTest { + private MockSMTPServer mockServer; + private FakeMail mail1; + private MimeMessage mimeMessage1; + private SMTPMessageSender smtpClient; + + @BeforeEach + void setUp() throws Exception { + mockServer = new MockSMTPServer(); + + mimeMessage1 = MimeMessageBuilder.mimeMessageBuilder() + .setSubject("test") + .setText("any text") + .build(); + mail1 = FakeMail.builder() + .name("name") + .sender(BOB) + .recipients(ALICE, JACK) + .mimeMessage(mimeMessage1) + .build(); + + mockServer.start(); + smtpClient = new SMTPMessageSender(DOMAIN) + .connect("localhost", mockServer.getPort()); + } + + @AfterEach + void tearDown() { + mockServer.stop(); + } + + @Test + void serverShouldReceiveMessageFromClient() throws Exception { + mockServer.setBehaviors(new MockSMTPBehavior( + MAIL_FROM, + Condition.MATCH_ALL, + Response.serverReject(SERVICE_NOT_AVAILABLE_421, "mock response"), + MockSMTPBehavior.NumberOfAnswersPolicy.anytime())); + + assertThatThrownBy(() -> smtpClient.sendMessage(mail1)) + .isInstanceOf(SMTPConnectionClosedException.class) + .hasMessageContaining("421"); + } + + @Test + void serverShouldReceiveMessageRecipientClient() throws Exception { + mockServer.setBehaviors(new MockSMTPBehavior( + RCPT_TO, + Condition.MATCH_ALL, + Response.serverReject(SERVICE_NOT_AVAILABLE_421, "mock response"), + MockSMTPBehavior.NumberOfAnswersPolicy.anytime())); + + assertThatThrownBy(() -> smtpClient.sendMessage(mail1)) + .isInstanceOf(SMTPConnectionClosedException.class) + .hasMessageContaining("421"); + } + + @Test + void serverShouldReceiveMessageDataClient() throws Exception { + mockServer.setBehaviors(new MockSMTPBehavior( + DATA, + Condition.MATCH_ALL, + Response.serverReject(SERVICE_NOT_AVAILABLE_421, "mock response"), + MockSMTPBehavior.NumberOfAnswersPolicy.anytime())); + + assertThatThrownBy(() -> smtpClient.sendMessage(mail1)) + .isInstanceOf(SMTPConnectionClosedException.class) + .hasMessageContaining("421"); + } + } + @Test void serverStartShouldOpenASmtpPort() { MockSMTPServer mockServer = new MockSMTPServer(); diff --git a/server/testing/src/main/java/org/apache/james/utils/SMTPMessageSender.java b/server/testing/src/main/java/org/apache/james/utils/SMTPMessageSender.java index 7a249b8..cdae5c8 100644 --- a/server/testing/src/main/java/org/apache/james/utils/SMTPMessageSender.java +++ b/server/testing/src/main/java/org/apache/james/utils/SMTPMessageSender.java @@ -119,7 +119,7 @@ public class SMTPMessageSender extends ExternalResource implements Closeable { doSetSender(from); mail.getRecipients().stream() .map(MailAddress::asString) - .forEach(Throwing.consumer(this::doAddRcpt)); + .forEach(Throwing.consumer(this::doAddRcpt).sneakyThrow()); doData(asString(mail.getMessage())); return this; } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
