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 5f5d4e5b9453d51acdc2d13e20ede73b0cec1aa5 Author: Benoit Tellier <[email protected]> AuthorDate: Fri Apr 30 21:49:18 2021 +0700 JAMES-3574 Add a unit test for the LMTP protocol --- server/protocols/protocols-imap4/pom.xml | 6 + .../james/imapserver/netty/IMAPServerTest.java | 26 +--- .../james/protocols/lib/mock/ConfigLoader.java | 43 +++++ server/protocols/protocols-lmtp/pom.xml | 58 +++++++ .../apache/james/lmtpserver/LmtpServerTest.java | 173 +++++++++++++++++++++ .../protocols-lmtp/src/test/resources/lmtp.xml | 34 ++++ 6 files changed, 316 insertions(+), 24 deletions(-) diff --git a/server/protocols/protocols-imap4/pom.xml b/server/protocols/protocols-imap4/pom.xml index 937f2e7..317fdbd 100644 --- a/server/protocols/protocols-imap4/pom.xml +++ b/server/protocols/protocols-imap4/pom.xml @@ -66,6 +66,12 @@ </dependency> <dependency> <groupId>${james.groupId}</groupId> + <artifactId>james-server-protocols-library</artifactId> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>${james.groupId}</groupId> <artifactId>james-server-testing</artifactId> <scope>test</scope> </dependency> diff --git a/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPServerTest.java b/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPServerTest.java index 2790b7e..417f5c4 100644 --- a/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPServerTest.java +++ b/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPServerTest.java @@ -23,8 +23,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.util.Properties; import javax.mail.Folder; @@ -38,11 +36,6 @@ import javax.mail.search.RecipientStringTerm; import javax.mail.search.SearchTerm; import javax.mail.search.SubjectTerm; -import org.apache.commons.configuration2.XMLConfiguration; -import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder; -import org.apache.commons.configuration2.builder.fluent.Parameters; -import org.apache.commons.configuration2.convert.DisabledListDelimiterHandler; -import org.apache.commons.configuration2.io.FileHandler; import org.apache.james.core.Username; import org.apache.james.imap.encode.main.DefaultImapEncoderFactory; import org.apache.james.imap.main.DefaultImapDecoderFactory; @@ -55,9 +48,7 @@ import org.apache.james.mailbox.store.FakeAuthenticator; import org.apache.james.mailbox.store.FakeAuthorizator; import org.apache.james.mailbox.store.StoreSubscriptionManager; import org.apache.james.metrics.tests.RecordingMetricFactory; -import org.apache.james.mime4j.codec.DecoderUtil; -import org.apache.james.mime4j.codec.EncoderUtil; -import org.apache.james.mime4j.util.MimeUtil; +import org.apache.james.protocols.lib.mock.ConfigLoader; import org.apache.james.util.ClassLoaderUtils; import org.apache.james.utils.TestIMAPClient; import org.junit.jupiter.api.AfterEach; @@ -75,19 +66,6 @@ class IMAPServerTest { public static final String SMALL_MESSAGE = "header: value\r\n\r\nBODY"; private InMemoryIntegrationResources memoryIntegrationResources; - private static XMLConfiguration getConfig(InputStream configStream) throws Exception { - FileBasedConfigurationBuilder<XMLConfiguration> builder = new FileBasedConfigurationBuilder<>(XMLConfiguration.class) - .configure(new Parameters() - .xml() - .setListDelimiterHandler(new DisabledListDelimiterHandler())); - XMLConfiguration xmlConfiguration = builder.getConfiguration(); - FileHandler fileHandler = new FileHandler(xmlConfiguration); - fileHandler.load(configStream); - configStream.close(); - - return xmlConfiguration; - } - @RegisterExtension public TestIMAPClient testIMAPClient = new TestIMAPClient(); @@ -119,7 +97,7 @@ class IMAPServerTest { metricFactory), new ImapMetrics(metricFactory)); - imapServer.configure(getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream(configurationFile))); + imapServer.configure(ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream(configurationFile))); imapServer.init(); return imapServer; diff --git a/server/protocols/protocols-library/src/test/java/org/apache/james/protocols/lib/mock/ConfigLoader.java b/server/protocols/protocols-library/src/test/java/org/apache/james/protocols/lib/mock/ConfigLoader.java new file mode 100644 index 0000000..3c610d3 --- /dev/null +++ b/server/protocols/protocols-library/src/test/java/org/apache/james/protocols/lib/mock/ConfigLoader.java @@ -0,0 +1,43 @@ +/**************************************************************** + * 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.protocols.lib.mock; + +import java.io.InputStream; + +import org.apache.commons.configuration2.XMLConfiguration; +import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder; +import org.apache.commons.configuration2.builder.fluent.Parameters; +import org.apache.commons.configuration2.convert.DisabledListDelimiterHandler; +import org.apache.commons.configuration2.io.FileHandler; + +public class ConfigLoader { + public static XMLConfiguration getConfig(InputStream configStream) throws Exception { + FileBasedConfigurationBuilder<XMLConfiguration> builder = new FileBasedConfigurationBuilder<>(XMLConfiguration.class) + .configure(new Parameters() + .xml() + .setListDelimiterHandler(new DisabledListDelimiterHandler())); + XMLConfiguration xmlConfiguration = builder.getConfiguration(); + FileHandler fileHandler = new FileHandler(xmlConfiguration); + fileHandler.load(configStream); + configStream.close(); + + return xmlConfiguration; + } +} diff --git a/server/protocols/protocols-lmtp/pom.xml b/server/protocols/protocols-lmtp/pom.xml index 8090e67..ca07fa0 100644 --- a/server/protocols/protocols-lmtp/pom.xml +++ b/server/protocols/protocols-lmtp/pom.xml @@ -39,18 +39,61 @@ </dependency> <dependency> <groupId>${james.groupId}</groupId> + <artifactId>apache-james-mailbox-api</artifactId> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>${james.groupId}</groupId> + <artifactId>apache-james-mailbox-memory</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>${james.groupId}</groupId> + <artifactId>apache-james-mailbox-memory</artifactId> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>${james.groupId}</groupId> <artifactId>apache-mailet-api</artifactId> </dependency> <dependency> <groupId>${james.groupId}</groupId> + <artifactId>event-bus-api</artifactId> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>${james.groupId}</groupId> <artifactId>james-server-data-api</artifactId> </dependency> <dependency> <groupId>${james.groupId}</groupId> + <artifactId>james-server-data-memory</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>${james.groupId}</groupId> + <artifactId>james-server-dnsservice-test</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>${james.groupId}</groupId> + <artifactId>james-server-mailetcontainer-api</artifactId> + </dependency> + <dependency> + <groupId>${james.groupId}</groupId> <artifactId>james-server-protocols-library</artifactId> </dependency> <dependency> <groupId>${james.groupId}</groupId> + <artifactId>james-server-protocols-library</artifactId> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>${james.groupId}</groupId> <artifactId>james-server-protocols-smtp</artifactId> <exclusions> <exclusion> @@ -73,6 +116,16 @@ </dependency> <dependency> <groupId>${james.groupId}</groupId> + <artifactId>james-server-testing</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>${james.groupId}</groupId> + <artifactId>metrics-tests</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>${james.groupId}</groupId> <artifactId>testing-base</artifactId> <scope>test</scope> </dependency> @@ -93,6 +146,11 @@ <artifactId>protocols-smtp</artifactId> </dependency> <dependency> + <groupId>com.google.inject</groupId> + <artifactId>guice</artifactId> + <scope>test</scope> + </dependency> + <dependency> <groupId>io.netty</groupId> <artifactId>netty</artifactId> </dependency> diff --git a/server/protocols/protocols-lmtp/src/test/java/org/apache/james/lmtpserver/LmtpServerTest.java b/server/protocols/protocols-lmtp/src/test/java/org/apache/james/lmtpserver/LmtpServerTest.java new file mode 100644 index 0000000..0de463c --- /dev/null +++ b/server/protocols/protocols-lmtp/src/test/java/org/apache/james/lmtpserver/LmtpServerTest.java @@ -0,0 +1,173 @@ +/**************************************************************** + * 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.lmtpserver; + +import static org.apache.james.jmap.JMAPTestingConstants.DOMAIN; +import static org.apache.james.jmap.JMAPTestingConstants.LOCALHOST_IP; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; +import java.nio.charset.StandardCharsets; + +import org.apache.james.core.Domain; +import org.apache.james.core.Username; +import org.apache.james.dnsservice.api.DNSService; +import org.apache.james.dnsservice.api.InMemoryDNSService; +import org.apache.james.domainlist.api.DomainList; +import org.apache.james.domainlist.lib.DomainListConfiguration; +import org.apache.james.domainlist.memory.MemoryDomainList; +import org.apache.james.filesystem.api.FileSystem; +import org.apache.james.lmtpserver.netty.LMTPServerFactory; +import org.apache.james.mailbox.MailboxManager; +import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.inmemory.InMemoryMailboxManager; +import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources; +import org.apache.james.mailbox.model.MailboxPath; +import org.apache.james.mailbox.model.MessageRange; +import org.apache.james.mailbox.store.FakeAuthenticator; +import org.apache.james.mailbox.store.FakeAuthorizator; +import org.apache.james.metrics.api.MetricFactory; +import org.apache.james.metrics.tests.RecordingMetricFactory; +import org.apache.james.protocols.lib.mock.ConfigLoader; +import org.apache.james.protocols.lib.mock.MockProtocolHandlerLoader; +import org.apache.james.rrt.api.AliasReverseResolver; +import org.apache.james.rrt.api.CanSendFrom; +import org.apache.james.rrt.api.RecipientRewriteTable; +import org.apache.james.rrt.api.RecipientRewriteTableConfiguration; +import org.apache.james.rrt.lib.AliasReverseResolverImpl; +import org.apache.james.rrt.lib.CanSendFromImpl; +import org.apache.james.rrt.memory.MemoryRecipientRewriteTable; +import org.apache.james.server.core.configuration.Configuration; +import org.apache.james.server.core.filesystem.FileSystemImpl; +import org.apache.james.user.api.UsersRepository; +import org.apache.james.user.memory.MemoryUsersRepository; +import org.awaitility.Awaitility; +import org.jboss.netty.util.HashedWheelTimer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.google.inject.name.Names; + +import reactor.core.publisher.Flux; + +class LmtpServerTest { + private InMemoryMailboxManager mailboxManager; + private LMTPServerFactory lmtpServerFactory; + + @BeforeEach + void setUp() throws Exception { + InMemoryDNSService dnsService = new InMemoryDNSService() + .registerMxRecord(Domain.LOCALHOST.asString(), "127.0.0.1") + .registerMxRecord("examplebis.local", "127.0.0.1") + .registerMxRecord("127.0.0.1", "127.0.0.1"); + MemoryDomainList domainList = new MemoryDomainList(dnsService); + domainList.configure(DomainListConfiguration.builder() + .autoDetect(false) + .autoDetectIp(false) + .build()); + + domainList.addDomain(Domain.of("examplebis.local")); + MemoryUsersRepository usersRepository = MemoryUsersRepository.withVirtualHosting(domainList); + + FakeAuthenticator authenticator = new FakeAuthenticator(); + mailboxManager = InMemoryIntegrationResources.builder() + .authenticator(authenticator) + .authorizator(FakeAuthorizator.defaultReject()) + .inVmEventBus() + .defaultAnnotationLimits() + .defaultMessageParser() + .scanningSearchIndex() + .noPreDeletionHooks() + .storeQuotaManager() + .build().getMailboxManager(); + + usersRepository.addUser(Username.of("[email protected]"), "pwd"); + + FileSystem fileSystem = new FileSystemImpl(Configuration.builder() + .workingDirectory("../") + .configurationFromClasspath() + .build().directories()); + MemoryRecipientRewriteTable rewriteTable = new MemoryRecipientRewriteTable(); + rewriteTable.setConfiguration(RecipientRewriteTableConfiguration.DEFAULT_ENABLED); + AliasReverseResolver aliasReverseResolver = new AliasReverseResolverImpl(rewriteTable); + CanSendFrom canSendFrom = new CanSendFromImpl(rewriteTable, aliasReverseResolver); + MockProtocolHandlerLoader loader = MockProtocolHandlerLoader.builder() + .put(binder -> binder.bind(DomainList.class).toInstance(domainList)) + .put(binder -> binder.bind(RecipientRewriteTable.class).toInstance(rewriteTable)) + .put(binder -> binder.bind(CanSendFrom.class).toInstance(canSendFrom)) + .put(binder -> binder.bind(FileSystem.class).toInstance(fileSystem)) + .put(binder -> binder.bind(DNSService.class).toInstance(dnsService)) + .put(binder -> binder.bind(UsersRepository.class).toInstance(usersRepository)) + .put(binder -> binder.bind(MetricFactory.class).to(RecordingMetricFactory.class)) + .put(binder -> binder.bind(MailboxManager.class).annotatedWith(Names.named("mailboxmanager")).toInstance(mailboxManager)) + .build(); + lmtpServerFactory = new LMTPServerFactory(loader, fileSystem, new RecordingMetricFactory(), new HashedWheelTimer()); + + lmtpServerFactory.configure(ConfigLoader.getConfig(ClassLoader.getSystemResourceAsStream("lmtp.xml"))); + lmtpServerFactory.init(); + } + + @AfterEach + void tearDown() { + lmtpServerFactory.destroy(); + } + + @Test + void emailsShouldWellBeReceived() throws Exception { + SocketChannel server = SocketChannel.open(); + server.connect(new InetSocketAddress(LOCALHOST_IP, getLmtpPort())); + + server.write(ByteBuffer.wrap(("LHLO <" + DOMAIN + ">\r\n").getBytes(StandardCharsets.UTF_8))); + server.write(ByteBuffer.wrap(("MAIL FROM: <bob@" + DOMAIN + ">\r\n").getBytes(StandardCharsets.UTF_8))); + server.write(ByteBuffer.wrap(("RCPT TO: <[email protected]>\r\n").getBytes(StandardCharsets.UTF_8))); + server.write(ByteBuffer.wrap(("DATA\r\n").getBytes(StandardCharsets.UTF_8))); + server.read(ByteBuffer.allocate(1024)); // needed to synchronize + server.write(ByteBuffer.wrap(("header:value\r\n\r\nbody").getBytes(StandardCharsets.UTF_8))); + server.write(ByteBuffer.wrap(("\r\n").getBytes(StandardCharsets.UTF_8))); + server.write(ByteBuffer.wrap((".").getBytes(StandardCharsets.UTF_8))); + server.write(ByteBuffer.wrap(("\r\n").getBytes(StandardCharsets.UTF_8))); + server.write(ByteBuffer.wrap(("QUIT\r\n").getBytes(StandardCharsets.UTF_8))); + + Awaitility.await() + .untilAsserted(() -> { + Username username = Username.of("[email protected]"); + MailboxSession systemSession = mailboxManager.createSystemSession(username); + assertThatCode(() -> + assertThat(Flux.from(mailboxManager.getMailbox(MailboxPath.inbox(username), systemSession) + .listMessagesMetadata(MessageRange.all(), systemSession)) + .count() + .block()) + .isEqualTo(1)) + .doesNotThrowAnyException(); + }); + } + + public int getLmtpPort() { + return lmtpServerFactory.getServers().stream() + .findFirst() + .flatMap(server -> server.getListenAddresses().stream().findFirst()) + .map(InetSocketAddress::getPort) + .orElseThrow(() -> new IllegalStateException("LMTP server not defined")); + } +} \ No newline at end of file diff --git a/server/protocols/protocols-lmtp/src/test/resources/lmtp.xml b/server/protocols/protocols-lmtp/src/test/resources/lmtp.xml new file mode 100644 index 0000000..7ac3705 --- /dev/null +++ b/server/protocols/protocols-lmtp/src/test/resources/lmtp.xml @@ -0,0 +1,34 @@ +<?xml version="1.0"?> +<!-- + 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. + --> + +<lmtpservers> + <lmtpserver enabled="true"> + <jmxName>lmtpserver</jmxName> + <bind>0.0.0.0:0</bind> + <connectionBacklog>200</connectionBacklog> + <connectiontimeout>1200</connectiontimeout> + <connectionLimit>0</connectionLimit> + <connectionLimitPerIP>0</connectionLimitPerIP> + <maxmessagesize>0</maxmessagesize> + <handlerchain> + <handler class="org.apache.james.lmtpserver.CoreCmdHandlerLoader"/> + </handlerchain> + </lmtpserver> +</lmtpservers> --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
