Repository: james-project Updated Branches: refs/heads/master e0ad36d00 -> dfd3f420b
JAMES-2262 Add unit testing for SpamAssassin Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/4d9d568c Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/4d9d568c Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/4d9d568c Branch: refs/heads/master Commit: 4d9d568c2ea640740ee2c0f88c87156e4aa5d5ff Parents: e0ad36d Author: quynhn <qngu...@linagora.com> Authored: Thu Dec 14 16:53:53 2017 +0700 Committer: benwa <btell...@linagora.com> Committed: Mon Dec 25 11:24:08 2017 +0700 ---------------------------------------------------------------------- .../transport/mailets/SpamAssassinTest.java | 167 ++++++++++++++ server/mailet/mailets/pom.xml | 13 ++ .../james/transport/mailets/SpamAssassin.java | 47 ++-- .../transport/mailets/SpamAssassinTest.java | 215 +++++++++++++++++++ 4 files changed, 425 insertions(+), 17 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/4d9d568c/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java ---------------------------------------------------------------------- diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java new file mode 100644 index 0000000..34c9a36 --- /dev/null +++ b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java @@ -0,0 +1,167 @@ +/**************************************************************** + * 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.james.transport.mailets; + +import static net.javacrumbs.jsonunit.fluent.JsonFluentAssert.assertThatJson; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Optional; + +import javax.mail.internet.MimeMessage; + +import org.apache.james.core.MailAddress; +import org.apache.james.jmap.mailet.VacationMailet; +import org.apache.james.mailets.TemporaryJamesServer; +import org.apache.james.mailets.configuration.CommonProcessors; +import org.apache.james.mailets.configuration.MailetConfiguration; +import org.apache.james.mailets.configuration.MailetContainer; +import org.apache.james.mailets.configuration.ProcessorConfiguration; +import org.apache.james.transport.mailets.amqp.AmqpRule; +import org.apache.james.transport.matchers.All; +import org.apache.james.transport.matchers.RecipientIsLocal; +import org.apache.james.transport.matchers.SMTPAuthSuccessful; +import org.apache.james.util.streams.ContainerNames; +import org.apache.james.util.streams.SwarmGenericContainer; +import org.apache.james.utils.DataProbeImpl; +import org.apache.james.utils.IMAPMessageReader; +import org.apache.james.utils.SMTPMessageSender; +import org.apache.mailet.base.test.FakeMail; +import org.apache.mailet.base.test.MimeMessageBuilder; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.RuleChain; +import org.junit.rules.TemporaryFolder; + +import com.jayway.awaitility.Awaitility; +import com.jayway.awaitility.Duration; + +public class ContactExtractorTest { + + public static final String JAMES_ORG = "james.org"; + public static final String SENDER = "sender@" + JAMES_ORG; + public static final String TO = "to@" + JAMES_ORG; + public static final String TO2 = "to2@" + JAMES_ORG; + public static final String CC = "cc@" + JAMES_ORG; + public static final String CC2 = "cc2@" + JAMES_ORG; + public static final String BCC = "bcc@" + JAMES_ORG; + public static final String BCC2 = "bcc2@" + JAMES_ORG; + public static final String PASSWORD = "secret"; + public static final String EXCHANGE = "collector:email"; + public static final String ROUTING_KEY = ""; + + public SwarmGenericContainer rabbit = new SwarmGenericContainer(ContainerNames.RABBITMQ); + public AmqpRule amqpRule = new AmqpRule(rabbit, EXCHANGE, ROUTING_KEY); + public TemporaryFolder folder = new TemporaryFolder(); + + @Rule + public RuleChain chain = RuleChain.outerRule(rabbit).around(amqpRule).around(folder); + + private TemporaryJamesServer jamesServer; + + @Before + public void setup() throws Exception { + String attribute = "ExtractedContacts"; + MailetContainer mailets = MailetContainer + .builder() + .threads(5) + .postmaster(SENDER) + .addProcessor(CommonProcessors.root()) + .addProcessor(CommonProcessors.error()) + .addProcessor( + ProcessorConfiguration.builder() + .state("transport") + .addMailet(MailetConfiguration.builder() + .matcher(SMTPAuthSuccessful.class) + .mailet(ContactExtractor.class) + .addProperty(ContactExtractor.Configuration.ATTRIBUTE, attribute) + .build()) + .addMailet(MailetConfiguration.builder() + .matcher(All.class) + .mailet(AmqpForwardAttribute.class) + .addProperty(AmqpForwardAttribute.URI_PARAMETER_NAME, amqpRule.getAmqpUri()) + .addProperty(AmqpForwardAttribute.EXCHANGE_PARAMETER_NAME, EXCHANGE) + .addProperty(AmqpForwardAttribute.ATTRIBUTE_PARAMETER_NAME, attribute) + .build()) + .addMailet(MailetConfiguration.builder() + .matcher(All.class) + .mailet(RemoveMimeHeader.class) + .addProperty("name", "bcc") + .build()) + .addMailet(MailetConfiguration.builder() + .matcher(RecipientIsLocal.class) + .mailet(VacationMailet.class) + .build()) + .addMailet(MailetConfiguration.builder() + .matcher(RecipientIsLocal.class) + .mailet(LocalDelivery.class) + .build()) + .build()) + .build(); + jamesServer = TemporaryJamesServer.builder().build(folder, mailets); + DataProbeImpl probe = jamesServer.getProbe(DataProbeImpl.class); + probe.addDomain(JAMES_ORG); + probe.addUser(SENDER, PASSWORD); + probe.addUser(TO, PASSWORD); + probe.addUser(TO2, PASSWORD); + probe.addUser(CC, PASSWORD); + probe.addUser(CC2, PASSWORD); + probe.addUser(BCC, PASSWORD); + probe.addUser(BCC2, PASSWORD); + } + + @After + public void tearDown() { + jamesServer.shutdown(); + } + + @Test + public void recipientsShouldBePublishedToAmqpWhenSendingEmail() throws Exception { + MimeMessage message = MimeMessageBuilder.mimeMessageBuilder() + .setSender(SENDER) + .addToRecipient(TO, "John To2 <" + TO2 + ">") + .addCcRecipient(CC, "John Cc2 <" + CC2 + ">") + .addBccRecipient(BCC, "John Bcc2 <" + BCC2 + ">") + .setSubject("Contact collection Rocks") + .setText("This is my email") + .build(); + FakeMail mail = FakeMail.builder() + .mimeMessage(message) + .sender(new MailAddress(SENDER)) + .recipients(new MailAddress(TO), new MailAddress(TO2), new MailAddress(CC), new MailAddress(CC2), new MailAddress(BCC), new MailAddress(BCC2)) + .build(); + try (SMTPMessageSender messageSender = SMTPMessageSender.authentication("localhost", 1025, JAMES_ORG, SENDER, PASSWORD); + IMAPMessageReader imap = new IMAPMessageReader("localhost", 1143)) { + + messageSender.sendMessage(mail); + Awaitility.await().pollDelay(Duration.FIVE_HUNDRED_MILLISECONDS) + .atMost(Duration.ONE_MINUTE) + .until(() -> imap.userReceivedMessage(TO, PASSWORD)); + + Optional<String> actual = amqpRule.readContent(); + assertThat(actual).isNotEmpty(); + assertThatJson(actual.get()).isEqualTo("{" + + "\"userEmail\" : \"sen...@james.org\", " + + "\"emails\" : [ \"t...@james.org\", \"John To2 <t...@james.org>\", \"c...@james.org\", \"John Cc2 <c...@james.org>\", \"b...@james.org\", \"John Bcc2 <b...@james.org>\" ]" + + "}"); + } + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/4d9d568c/server/mailet/mailets/pom.xml ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/pom.xml b/server/mailet/mailets/pom.xml index 9f81107..a8f409c 100644 --- a/server/mailet/mailets/pom.xml +++ b/server/mailet/mailets/pom.xml @@ -135,6 +135,19 @@ </dependency> <dependency> <groupId>${project.groupId}</groupId> + <artifactId>james-server-protocols-library</artifactId> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>james-server-protocols-smtp</artifactId> + <version>${project.version}</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> <artifactId>james-server-queue-api</artifactId> </dependency> <dependency> http://git-wip-us.apache.org/repos/asf/james-project/blob/4d9d568c/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SpamAssassin.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SpamAssassin.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SpamAssassin.java index 970bcf1..1b3de01 100644 --- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SpamAssassin.java +++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SpamAssassin.java @@ -19,6 +19,8 @@ package org.apache.james.transport.mailets; +import java.util.Optional; + import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; @@ -27,9 +29,13 @@ import org.apache.james.util.scanner.SpamAssassinInvoker; import org.apache.mailet.Experimental; import org.apache.mailet.Mail; import org.apache.mailet.base.GenericMailet; +import org.apache.mailet.base.MailetUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; + /** * Sends the message through daemonized SpamAssassin (spamd), visit <a * href="http://spamassassin.apache.org/">spamassassin.apache.org/</a> for info @@ -57,29 +63,26 @@ import org.slf4j.LoggerFactory; @Experimental public class SpamAssassin extends GenericMailet { private static final Logger LOGGER = LoggerFactory.getLogger(ManageSieveMailet.class); + public static final String SPAMD_HOST = "spamdHost"; + public static final String SPAMD_PORT = "spamdPort"; + public static final String DEFAULT_HOST = "127.0.0.1"; + public static final int DEFAULT_PORT = 783; + public static final int MAX_AVAILABLE_PORT = 65535; - String spamdHost; - int spamdPort; + private String spamdHost; + private int spamdPort; /** * @see org.apache.mailet.base.GenericMailet#init() */ public void init() throws MessagingException { - spamdHost = getInitParameter("spamdHost"); - if (spamdHost == null || spamdHost.equals("")) { - spamdHost = "127.0.0.1"; - } + spamdHost = Optional.ofNullable(getInitParameter(SPAMD_HOST)) + .filter(s -> !Strings.isNullOrEmpty(s)) + .orElse(DEFAULT_HOST); - String port = getInitParameter("spamdPort"); - if (port == null || port.equals("")) { - spamdPort = 783; - } else { - - try { - spamdPort = Integer.parseInt(getInitParameter("spamdPort")); - } catch (NumberFormatException e) { - throw new MessagingException("Please configure a valid port. Not valid: " + spamdPort); - } + spamdPort = MailetUtil.getInitParameterAsStrictlyPositiveInteger(getInitParameter(SPAMD_PORT), DEFAULT_PORT); + if (spamdPort > MAX_AVAILABLE_PORT) { + throw new MessagingException("Please configure a valid port. Not valid: " + spamdPort); } } @@ -90,7 +93,7 @@ public class SpamAssassin extends GenericMailet { try { MimeMessage message = mail.getMessage(); - // Invoke spamassian connection and scan the message + // Invoke SpamAssassin connection and scan the message SpamAssassinInvoker sa = new SpamAssassinInvoker(spamdHost, spamdPort); sa.scanMail(message); @@ -112,4 +115,14 @@ public class SpamAssassin extends GenericMailet { public String getMailetInfo() { return "Checks message against SpamAssassin"; } + + @VisibleForTesting + String getSpamdHost() { + return spamdHost; + } + + @VisibleForTesting + int getSpamdPort() { + return spamdPort; + } } http://git-wip-us.apache.org/repos/asf/james-project/blob/4d9d568c/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java new file mode 100644 index 0000000..e49d981 --- /dev/null +++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java @@ -0,0 +1,215 @@ +/**************************************************************** + * 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.james.transport.mailets; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import javax.mail.MessagingException; + +import org.apache.james.protocols.lib.PortUtil; +import org.apache.james.smtpserver.mock.util.MockSpamd; +import org.apache.james.util.scanner.SpamAssassinInvoker; +import org.apache.mailet.Mail; +import org.apache.mailet.base.test.FakeMail; +import org.apache.mailet.base.test.FakeMailetConfig; +import org.apache.mailet.base.test.MimeMessageBuilder; +import org.junit.Test; + +public class SpamAssassinTest { + private SpamAssassin mailet = new SpamAssassin(); + + @Test + public void initShouldSetDefaultSpamdHostWhenNone() throws Exception { + mailet.init(FakeMailetConfig.builder() + .mailetName("SpamAssassin") + .build()); + + assertThat(mailet.getSpamdHost()).isEqualTo(SpamAssassin.DEFAULT_HOST); + } + + @Test + public void initShouldSetDefaultSpamdPortWhenNone() throws Exception { + mailet.init(FakeMailetConfig.builder() + .mailetName("SpamAssassin") + .build()); + + assertThat(mailet.getSpamdPort()).isEqualTo(SpamAssassin.DEFAULT_PORT); + } + + @Test + public void initShouldSetSpamdHostWhenPresent() throws Exception { + String spamdHost = "any.host"; + mailet.init(FakeMailetConfig.builder() + .mailetName("SpamAssassin") + .setProperty(SpamAssassin.SPAMD_HOST, spamdHost) + .build()); + + assertThat(mailet.getSpamdHost()).isEqualTo(spamdHost); + } + + @Test + public void getSpamHostShouldReturnDefaultValueWhenEmpty() throws Exception { + mailet.init(FakeMailetConfig.builder() + .mailetName("SpamAssassin") + .setProperty(SpamAssassin.SPAMD_HOST, "") + .build()); + + assertThat(mailet.getSpamdHost()).isEqualTo(SpamAssassin.DEFAULT_HOST); + } + + @Test + public void initShouldSetDefaultSpamdPortWhenDefault() throws Exception { + mailet.init(FakeMailetConfig.builder() + .mailetName("SpamAssassin") + .build()); + + assertThat(mailet.getSpamdPort()).isEqualTo(SpamAssassin.DEFAULT_PORT); + } + + @Test + public void initShouldThrowWhenSpamdPortIsNotNumber() throws Exception { + assertThatThrownBy(() -> mailet.init(FakeMailetConfig.builder() + .mailetName("SpamAssassin") + .setProperty(SpamAssassin.SPAMD_PORT, "noNumber") + .build())).isInstanceOf(MessagingException.class); + } + + @Test + public void initShouldThrowWhenSpamdPortIsNegative() throws Exception { + assertThatThrownBy(() -> mailet.init(FakeMailetConfig.builder() + .mailetName("SpamAssassin") + .setProperty(SpamAssassin.SPAMD_PORT, "-1") + .build())).isInstanceOf(MessagingException.class); + } + + @Test + public void initShouldThrowWhenSpamdPortIsZero() throws Exception { + assertThatThrownBy(() -> mailet.init(FakeMailetConfig.builder() + .mailetName("SpamAssassin") + .setProperty(SpamAssassin.SPAMD_PORT, "0") + .build())).isInstanceOf(MessagingException.class); + } + + @Test + public void initShouldThrowWhenSpamdPortTooBig() throws Exception { + assertThatThrownBy(() -> mailet.init(FakeMailetConfig.builder() + .mailetName("SpamAssassin") + .setProperty(SpamAssassin.SPAMD_PORT, + String.valueOf(SpamAssassin.MAX_AVAILABLE_PORT + 1)) + .build())).isInstanceOf(MessagingException.class); + } + + @Test + public void initShouldSetSpamPortWhenPresent() throws Exception { + int spamPort = 1000; + mailet.init(FakeMailetConfig.builder() + .mailetName("SpamAssassin") + .setProperty(SpamAssassin.SPAMD_PORT, String.valueOf(spamPort)) + .build()); + + assertThat(mailet.getSpamdPort()).isEqualTo(spamPort); + } + + @Test + public void serviceShouldWriteSpamAttributeOnMail() throws Exception { + int port = PortUtil.getNonPrivilegedPort(); + new Thread(new MockSpamd(port)).start(); + + FakeMailetConfig mailetConfiguration = FakeMailetConfig.builder() + .mailetName("SpamAssassin") + .setProperty(SpamAssassin.SPAMD_HOST, "localhost") + .setProperty(SpamAssassin.SPAMD_PORT, String.valueOf(port)) + .build(); + mailet.init(mailetConfiguration); + + Mail mail = FakeMail.builder() + .mimeMessage(MimeMessageBuilder.mimeMessageBuilder() + .addToRecipient("us...@exemple.com") + .addFrom("sen...@exemple.com") + .setSubject("testing") + .setText("Please!") + .build()) + .build(); + + mailet.service(mail); + + assertThat(mail.getAttributeNames()) + .containsOnly(SpamAssassinInvoker.FLAG_MAIL_ATTRIBUTE_NAME, SpamAssassinInvoker.STATUS_MAIL_ATTRIBUTE_NAME); + } + + @Test + public void serviceShouldWriteMessageAsNotSpamWhenNotSpam() throws Exception { + int port = PortUtil.getNonPrivilegedPort(); + new Thread(new MockSpamd(port)).start(); + + FakeMailetConfig mailetConfiguration = FakeMailetConfig.builder() + .mailetName("SpamAssassin") + .setProperty(SpamAssassin.SPAMD_HOST, "localhost") + .setProperty(SpamAssassin.SPAMD_PORT, String.valueOf(port)) + .build(); + mailet.init(mailetConfiguration); + + Mail mail = FakeMail.builder() + .mimeMessage(MimeMessageBuilder.mimeMessageBuilder() + .addToRecipient("us...@exemple.com") + .addFrom("sen...@exemple.com") + .setSubject("testing") + .setText("Please!") + .build()) + .build(); + + mailet.service(mail); + + assertThat(mail.getAttribute(SpamAssassinInvoker.FLAG_MAIL_ATTRIBUTE_NAME)).isEqualTo("NO"); + } + + @Test + public void serviceShouldWriteMessageAsSpamWhenSpam() throws Exception { + int port = PortUtil.getNonPrivilegedPort(); + new Thread(new MockSpamd(port)).start(); + + FakeMailetConfig mailetConfiguration = FakeMailetConfig.builder() + .mailetName("SpamAssassin") + .setProperty(SpamAssassin.SPAMD_HOST, "localhost") + .setProperty(SpamAssassin.SPAMD_PORT, String.valueOf(port)) + .build(); + mailet.init(mailetConfiguration); + + Mail mail = FakeMail.builder() + .mimeMessage(MimeMessageBuilder.mimeMessageBuilder() + .addToRecipient("us...@exemple.com") + .addFrom("sen...@exemple.com") + .setSubject(MockSpamd.GTUBE + " testing") + .setText("Please!") + .build()) + .build(); + + mailet.service(mail); + + assertThat(mail.getAttribute(SpamAssassinInvoker.FLAG_MAIL_ATTRIBUTE_NAME)).isEqualTo("YES"); + } + + @Test + public void getMailetInfoShouldReturnSpamAssasinMailetInformation() throws Exception { + assertThat(mailet.getMailetInfo()).isEqualTo("Checks message against SpamAssassin"); + } + +} \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org