From b2546c2f07047a2938c2e3c3e31f333301c5de62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=95=A8=ED=83=9C=ED=9B=88?= Date: Mon, 16 Sep 2024 14:15:35 +0900 Subject: [PATCH 01/18] Refactor `DefaultImapDecoderFactory` to Use POJO Style and Constructor Injection - Updated `DefaultImapDecoderFactory` to adhere to POJO design principles. - Replaced internal `ImapParserFactory` creation with constructor injection of `UnpooledStatusResponseFactory`. - Improved decoupling and maintainability by explicitly managing dependencies via the constructor. - Updated class and method documentation to reflect changes and clarify the role of the factory. This change enhances the clarity and testability of the `DefaultImapDecoderFactory` by removing direct dependency creation and enforcing a cleaner, more modular structure. --- .../imap/main/DefaultImapDecoderFactory.java | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java b/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java index 87730468fbd..ecf1c2592d4 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java @@ -27,20 +27,35 @@ import org.apache.james.imap.message.response.UnpooledStatusResponseFactory; /** - * TODO: this is temporary: should let the container do the coupling. TODO: - * convert to POJO + * Factory class for creating `ImapDecoder` instances. + * + * This class is a POJO that manually manages its dependencies. + * Dependencies are injected through the constructor, which allows for + * better decoupling and easier testing. + * + * The creation of `ImapCommandParserFactory` is handled internally by + * this factory, based on the provided `UnpooledStatusResponseFactory`. */ public class DefaultImapDecoderFactory implements ImapDecoderFactory { - public static ImapDecoder createDecoder() { - final UnpooledStatusResponseFactory unpooledStatusResponseFactory = new UnpooledStatusResponseFactory(); - final ImapCommandParserFactory imapCommands = new ImapParserFactory(unpooledStatusResponseFactory); - return new DefaultImapDecoder(unpooledStatusResponseFactory, imapCommands); + private final UnpooledStatusResponseFactory unpooledStatusResponseFactory; + private final ImapCommandParserFactory imapCommandParserFactory; + + /** + * Constructs `DefaultImapDecoderFactory` with the given + * `UnpooledStatusResponseFactory`. The `ImapCommandParserFactory` + * is created internally using the provided `UnpooledStatusResponseFactory`. + * + * @param unpooledStatusResponseFactory The factory for creating status responses. + */ + public DefaultImapDecoderFactory(UnpooledStatusResponseFactory unpooledStatusResponseFactory) { + this.unpooledStatusResponseFactory = unpooledStatusResponseFactory; + this.imapCommandParserFactory = new ImapParserFactory(unpooledStatusResponseFactory); } @Override public ImapDecoder buildImapDecoder() { - return createDecoder(); + return new DefaultImapDecoder(unpooledStatusResponseFactory, imapCommandParserFactory); } } From 976e1c088d2242e6a4664dff9d1ee1b164007893 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=95=A8=ED=83=9C=ED=9B=88?= Date: Mon, 16 Sep 2024 14:15:35 +0900 Subject: [PATCH 02/18] Refactor DefaultImapDecoderFactory to Use Generics and Improve Flexibility Updated DefaultImapDecoderFactory to use a generic type T for flexibility, allowing any implementation of StatusResponseFactory. Introduced a parameterized constructor that accepts an instance of StatusResponseFactory to facilitate various implementations. Retained the default constructor, which now initializes with UnpooledStatusResponseFactory as the default implementation. Enhanced modularity by enabling constructor injection, improving decoupling, and reducing hardcoded dependencies. Updated class and method documentation to reflect the use of generics and the new constructor design. Modified IMAPHealthCheckTest and IMAPServerTest to replace static function calls with instance creation using the new keyword, enhancing readability and flexibility in test code. This change improves the flexibility and reusability of the DefaultImapDecoderFactory by supporting multiple StatusResponseFactory implementations while maintaining a default behavior. Additionally, it improves test code clarity by using a more explicit object creation approach. --- .../imap/main/DefaultImapDecoderFactory.java | 48 +++++++++++++++---- .../imapserver/netty/IMAPHealthCheckTest.java | 2 +- .../imapserver/netty/IMAPServerTest.java | 7 +-- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java b/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java index 87730468fbd..2fc970e303b 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java @@ -19,6 +19,7 @@ package org.apache.james.imap.main; +import org.apache.james.imap.api.message.response.StatusResponseFactory; import org.apache.james.imap.decode.ImapCommandParserFactory; import org.apache.james.imap.decode.ImapDecoder; import org.apache.james.imap.decode.ImapDecoderFactory; @@ -27,20 +28,49 @@ import org.apache.james.imap.message.response.UnpooledStatusResponseFactory; /** - * TODO: this is temporary: should let the container do the coupling. TODO: - * convert to POJO + * Factory class for creating `ImapDecoder` instances. + * + * This class is a POJO that manually manages its dependencies. + * Dependencies are injected through the constructor, which allows for + * better decoupling and easier testing. + * + * The creation of `ImapCommandParserFactory` is handled internally by + * this factory, based on the provided `UnpooledStatusResponseFactory`. */ -public class DefaultImapDecoderFactory implements ImapDecoderFactory { +public class DefaultImapDecoderFactory implements ImapDecoderFactory { - public static ImapDecoder createDecoder() { - final UnpooledStatusResponseFactory unpooledStatusResponseFactory = new UnpooledStatusResponseFactory(); - final ImapCommandParserFactory imapCommands = new ImapParserFactory(unpooledStatusResponseFactory); - return new DefaultImapDecoder(unpooledStatusResponseFactory, imapCommands); + private final T statusResponseFactory; + private final ImapCommandParserFactory imapCommandParserFactory; + + /** + * Default constructor. + * This constructor creates an instance of DefaultImapDecoderFactory with an UnpooledStatusResponseFactory as the default implementation. + * Since the type T is not known at runtime due to type erasure, we cast the new UnpooledStatusResponseFactory to T. + * It internally calls the other constructor to set up the necessary dependencies. + */ + public DefaultImapDecoderFactory() { + this((T) new UnpooledStatusResponseFactory()); + } + + /** + * Constructor that accepts a specific implementation of StatusResponseFactory. + * This constructor allows for flexibility by enabling different implementations of StatusResponseFactory to be used. + * + * @param statusResponseFactory An implementation of StatusResponseFactory to be used for creating IMAP command parsers and decoders. + */ + public DefaultImapDecoderFactory(T statusResponseFactory) { + this.statusResponseFactory = statusResponseFactory; + this.imapCommandParserFactory = new ImapParserFactory(statusResponseFactory); } + /** + * Builds and returns an instance of ImapDecoder. + * This method uses the statusResponseFactory and the imapCommandParserFactory to create a new DefaultImapDecoder instance. + * + * @return A new instance of DefaultImapDecoder. + */ @Override public ImapDecoder buildImapDecoder() { - return createDecoder(); + return new DefaultImapDecoder(statusResponseFactory, imapCommandParserFactory); } - } diff --git a/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPHealthCheckTest.java b/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPHealthCheckTest.java index 8e4937b1fdc..25b5fa509dd 100644 --- a/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPHealthCheckTest.java +++ b/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPHealthCheckTest.java @@ -76,7 +76,7 @@ void setUp() throws Exception { imapServerFactory = new IMAPServerFactory( FileSystemImpl.forTestingWithConfigurationFromClasspath(), - DefaultImapDecoderFactory.createDecoder(), + new DefaultImapDecoderFactory().buildImapDecoder(), new DefaultImapEncoderFactory().buildImapEncoder(), DefaultImapProcessorFactory.createXListSupportingProcessor( mailboxManager, 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 50d075e30fe..875a67cfe40 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 @@ -116,9 +116,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.stubbing.Answer; -import org.mockserver.integration.ClientAndServer; -import org.mockserver.model.HttpRequest; -import org.mockserver.model.HttpResponse; import org.slf4j.LoggerFactory; import com.github.fge.lambdas.Throwing; @@ -134,8 +131,6 @@ import io.netty.handler.codec.compression.JdkZlibEncoder; import io.netty.handler.codec.compression.ZlibWrapper; import io.netty.handler.ssl.SslContextBuilder; -import nl.altindag.ssl.exception.GenericKeyStoreException; -import nl.altindag.ssl.pem.exception.PrivateKeyParseException; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; @@ -166,7 +161,7 @@ private IMAPServer createImapServer(HierarchicalConfiguration con Set connectionChecks = defaultConnectionChecks(); mailboxManager = spy(memoryIntegrationResources.getMailboxManager()); IMAPServer imapServer = new IMAPServer( - DefaultImapDecoderFactory.createDecoder(), + new DefaultImapDecoderFactory().buildImapDecoder(), new DefaultImapEncoderFactory().buildImapEncoder(), DefaultImapProcessorFactory.createXListSupportingProcessor( mailboxManager, From 2c6cbd234bb50cb83733ffde168e8b37978fdd21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=95=A8=ED=83=9C=ED=9B=88?= Date: Tue, 24 Sep 2024 10:44:39 +0900 Subject: [PATCH 03/18] Revert "Merge remote-tracking branch 'origin/master'" This reverts commit 3ba64d476411780063692ab3a82fab9a1a1a5f3c, reversing changes made to 8c4a7c14c220b9ffad3664f903fe1ac1f4005e10. --- .../cassandra/host/CassandraHostSystem.java | 20 +- .../inmemory/host/InMemoryHostSystem.java | 38 +- .../imapmailbox/jpa/host/JPAHostSystem.java | 38 +- .../host/LuceneSearchHostSystem.java | 44 +- .../host/OpenSearchHostSystem.java | 62 +- .../host/RabbitMQEventBusHostSystem.java | 56 +- .../imapserver/netty/IMAPHealthCheckTest.java | 50 +- .../imapserver/netty/IMAPServerTest.java | 1136 ++++++++--------- 8 files changed, 722 insertions(+), 722 deletions(-) diff --git a/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java b/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java index 946d2d60528..bfe17476340 100644 --- a/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java +++ b/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java @@ -73,16 +73,16 @@ public class CassandraHostSystem extends JamesImapHostSystem { private static final ImapFeatures IMAP_FEATURES = ImapFeatures.of(Feature.NAMESPACE_SUPPORT, - Feature.MOVE_SUPPORT, - Feature.USER_FLAGS_SUPPORT, - Feature.QUOTA_SUPPORT, - Feature.ANNOTATION_SUPPORT, - Feature.MOD_SEQ_SEARCH); + Feature.MOVE_SUPPORT, + Feature.USER_FLAGS_SUPPORT, + Feature.QUOTA_SUPPORT, + Feature.ANNOTATION_SUPPORT, + Feature.MOD_SEQ_SEARCH); private final CassandraCluster cassandra; private CassandraMailboxManager mailboxManager; private CassandraPerUserMaxQuotaManagerV2 perUserMaxQuotaManager; - + public CassandraHostSystem(CassandraCluster cluster) { this.cassandra = cluster; } @@ -99,7 +99,7 @@ public void beforeTest() throws Exception { ThreadIdGuessingAlgorithm threadIdGuessingAlgorithm = new NaiveThreadIdGuessingAlgorithm(); UpdatableTickingClock clock = new UpdatableTickingClock(Instant.now()); CassandraMailboxSessionMapperFactory mapperFactory = TestCassandraMailboxSessionMapperFactory.forTests( - cassandra, messageIdFactory); + cassandra, messageIdFactory); InVMEventBus eventBus = new InVMEventBus(new InVmEventDelivery(new RecordingMetricFactory()), EventBusTestFixture.RETRY_BACKOFF_CONFIGURATION, new MemoryEventDeadLetters()); @@ -121,9 +121,9 @@ public void beforeTest() throws Exception { MessageSearchIndex index = new SimpleMessageSearchIndex(mapperFactory, mapperFactory, new DefaultTextExtractor(), attachmentManager); mailboxManager = new CassandraMailboxManager(mapperFactory, sessionProvider, - new JVMMailboxPathLocker(), new MessageParser(), messageIdFactory, - eventBus, annotationManager, storeRightManager, quotaComponents, index, MailboxManagerConfiguration.DEFAULT, - PreDeletionHooks.NO_PRE_DELETION_HOOK, threadIdGuessingAlgorithm, clock); + new JVMMailboxPathLocker(), new MessageParser(), messageIdFactory, + eventBus, annotationManager, storeRightManager, quotaComponents, index, MailboxManagerConfiguration.DEFAULT, + PreDeletionHooks.NO_PRE_DELETION_HOOK, threadIdGuessingAlgorithm, clock); eventBus.register(quotaUpdater); diff --git a/mpt/impl/imap-mailbox/inmemory/src/test/java/org/apache/james/mpt/imapmailbox/inmemory/host/InMemoryHostSystem.java b/mpt/impl/imap-mailbox/inmemory/src/test/java/org/apache/james/mpt/imapmailbox/inmemory/host/InMemoryHostSystem.java index 83fbaa1908e..a354f8e09d3 100644 --- a/mpt/impl/imap-mailbox/inmemory/src/test/java/org/apache/james/mpt/imapmailbox/inmemory/host/InMemoryHostSystem.java +++ b/mpt/impl/imap-mailbox/inmemory/src/test/java/org/apache/james/mpt/imapmailbox/inmemory/host/InMemoryHostSystem.java @@ -39,35 +39,35 @@ public class InMemoryHostSystem extends JamesImapHostSystem { private static final ImapFeatures SUPPORTED_FEATURES = ImapFeatures.of(Feature.NAMESPACE_SUPPORT, - Feature.MOVE_SUPPORT, - Feature.USER_FLAGS_SUPPORT, - Feature.QUOTA_SUPPORT, - Feature.ANNOTATION_SUPPORT, - Feature.MOD_SEQ_SEARCH); + Feature.MOVE_SUPPORT, + Feature.USER_FLAGS_SUPPORT, + Feature.QUOTA_SUPPORT, + Feature.ANNOTATION_SUPPORT, + Feature.MOD_SEQ_SEARCH); private StoreMailboxManager mailboxManager; private InMemoryPerUserMaxQuotaManager perUserMaxQuotaManager; - + @Override public void beforeTest() throws Exception { super.beforeTest(); InMemoryIntegrationResources resources = InMemoryIntegrationResources.builder() - .authenticator(authenticator) - .authorizator(authorizator) - .inVmEventBus() - .defaultAnnotationLimits() - .defaultMessageParser() - .scanningSearchIndex() - .noPreDeletionHooks() - .storeQuotaManager() - .build(); + .authenticator(authenticator) + .authorizator(authorizator) + .inVmEventBus() + .defaultAnnotationLimits() + .defaultMessageParser() + .scanningSearchIndex() + .noPreDeletionHooks() + .storeQuotaManager() + .build(); this.mailboxManager = resources.getMailboxManager(); this.perUserMaxQuotaManager = resources.getMaxQuotaManager(); - ImapProcessor defaultImapProcessorFactory = DefaultImapProcessorFactory.createDefaultProcessor(mailboxManager, mailboxManager.getEventBus(), new StoreSubscriptionManager(mailboxManager.getMapperFactory(), - mailboxManager.getMapperFactory(), - mailboxManager.getEventBus()), - mailboxManager.getQuotaComponents().getQuotaManager(), mailboxManager.getQuotaComponents().getQuotaRootResolver(), new DefaultMetricFactory()); + ImapProcessor defaultImapProcessorFactory = DefaultImapProcessorFactory.createDefaultProcessor(mailboxManager, mailboxManager.getEventBus(), new StoreSubscriptionManager(mailboxManager.getMapperFactory(), + mailboxManager.getMapperFactory(), + mailboxManager.getEventBus()), + mailboxManager.getQuotaComponents().getQuotaManager(), mailboxManager.getQuotaComponents().getQuotaRootResolver(), new DefaultMetricFactory()); configure(new DefaultImapDecoderFactory().buildImapDecoder(), new DefaultImapEncoderFactory().buildImapEncoder(), diff --git a/mpt/impl/imap-mailbox/jpa/src/test/java/org/apache/james/mpt/imapmailbox/jpa/host/JPAHostSystem.java b/mpt/impl/imap-mailbox/jpa/src/test/java/org/apache/james/mpt/imapmailbox/jpa/host/JPAHostSystem.java index 61eb337df9a..5f823df9b58 100644 --- a/mpt/impl/imap-mailbox/jpa/src/test/java/org/apache/james/mpt/imapmailbox/jpa/host/JPAHostSystem.java +++ b/mpt/impl/imap-mailbox/jpa/src/test/java/org/apache/james/mpt/imapmailbox/jpa/host/JPAHostSystem.java @@ -75,22 +75,22 @@ public class JPAHostSystem extends JamesImapHostSystem { private static final JpaTestCluster JPA_TEST_CLUSTER = JpaTestCluster.create( - ImmutableList.>builder() - .addAll(JPAMailboxFixture.MAILBOX_PERSISTANCE_CLASSES) - .addAll(JPAMailboxFixture.QUOTA_PERSISTANCE_CLASSES) - .build()); + ImmutableList.>builder() + .addAll(JPAMailboxFixture.MAILBOX_PERSISTANCE_CLASSES) + .addAll(JPAMailboxFixture.QUOTA_PERSISTANCE_CLASSES) + .build()); private static final ImapFeatures SUPPORTED_FEATURES = ImapFeatures.of(Feature.NAMESPACE_SUPPORT, - Feature.USER_FLAGS_SUPPORT, - Feature.ANNOTATION_SUPPORT, - Feature.QUOTA_SUPPORT, - Feature.MOVE_SUPPORT, - Feature.MOD_SEQ_SEARCH); + Feature.USER_FLAGS_SUPPORT, + Feature.ANNOTATION_SUPPORT, + Feature.QUOTA_SUPPORT, + Feature.MOVE_SUPPORT, + Feature.MOD_SEQ_SEARCH); static JamesImapHostSystem build() { return new JPAHostSystem(); } - + private JPAPerUserMaxQuotaManager maxQuotaManager; private OpenJPAMailboxManager mailboxManager; @@ -101,9 +101,9 @@ public void beforeTest() throws Exception { JPAUidProvider uidProvider = new JPAUidProvider(entityManagerFactory); JPAModSeqProvider modSeqProvider = new JPAModSeqProvider(entityManagerFactory); JPAConfiguration jpaConfiguration = JPAConfiguration.builder() - .driverName("driverName") - .driverURL("driverUrl") - .build(); + .driverName("driverName") + .driverURL("driverUrl") + .build(); JPAMailboxSessionMapperFactory mapperFactory = new JPAMailboxSessionMapperFactory(entityManagerFactory, uidProvider, modSeqProvider, jpaConfiguration); MailboxACLResolver aclResolver = new UnionMailboxACLResolver(); @@ -124,18 +124,18 @@ public void beforeTest() throws Exception { MessageSearchIndex index = new SimpleMessageSearchIndex(mapperFactory, mapperFactory, new DefaultTextExtractor(), attachmentContentLoader); mailboxManager = new OpenJPAMailboxManager(mapperFactory, sessionProvider, messageParser, new DefaultMessageId.Factory(), - eventBus, annotationManager, storeRightManager, quotaComponents, index, new NaiveThreadIdGuessingAlgorithm(), new UpdatableTickingClock(Instant.now())); + eventBus, annotationManager, storeRightManager, quotaComponents, index, new NaiveThreadIdGuessingAlgorithm(), new UpdatableTickingClock(Instant.now())); eventBus.register(quotaUpdater); eventBus.register(new MailboxAnnotationListener(mapperFactory, sessionProvider)); SubscriptionManager subscriptionManager = new StoreSubscriptionManager(mapperFactory, mapperFactory, eventBus); - + ImapProcessor defaultImapProcessorFactory = DefaultImapProcessorFactory.createDefaultProcessor( mailboxManager, eventBus, - subscriptionManager, + subscriptionManager, storeQuotaManager, quotaRootResolver, new DefaultMetricFactory()); @@ -148,9 +148,9 @@ public void beforeTest() throws Exception { @Override public void afterTest() { JPA_TEST_CLUSTER.clear(ImmutableList.builder() - .addAll(JPAMailboxFixture.MAILBOX_TABLE_NAMES) - .addAll(JPAMailboxFixture.QUOTA_TABLES_NAMES) - .build()); + .addAll(JPAMailboxFixture.MAILBOX_TABLE_NAMES) + .addAll(JPAMailboxFixture.QUOTA_TABLES_NAMES) + .build()); } @Override diff --git a/mpt/impl/imap-mailbox/lucenesearch/src/test/java/org/apache/james/mpt/imapmailbox/lucenesearch/host/LuceneSearchHostSystem.java b/mpt/impl/imap-mailbox/lucenesearch/src/test/java/org/apache/james/mpt/imapmailbox/lucenesearch/host/LuceneSearchHostSystem.java index 21703c3bb7e..ee82414000a 100644 --- a/mpt/impl/imap-mailbox/lucenesearch/src/test/java/org/apache/james/mpt/imapmailbox/lucenesearch/host/LuceneSearchHostSystem.java +++ b/mpt/impl/imap-mailbox/lucenesearch/src/test/java/org/apache/james/mpt/imapmailbox/lucenesearch/host/LuceneSearchHostSystem.java @@ -45,7 +45,7 @@ public class LuceneSearchHostSystem extends JamesImapHostSystem { private static final ImapFeatures SUPPORTED_FEATURES = ImapFeatures.of(Feature.NAMESPACE_SUPPORT, - Feature.MOD_SEQ_SEARCH); + Feature.MOD_SEQ_SEARCH); private InMemoryMailboxManager mailboxManager; private LuceneMessageSearchIndex searchIndex; @@ -58,18 +58,18 @@ public void beforeTest() throws Exception { private void initFields() { InMemoryIntegrationResources resources = InMemoryIntegrationResources.builder() - .authenticator(authenticator) - .authorizator(authorizator) - .inVmEventBus() - .defaultAnnotationLimits() - .defaultMessageParser() - .listeningSearchIndex(Throwing.function(preInstanciationStage -> new LuceneMessageSearchIndex( - preInstanciationStage.getMapperFactory(), new InMemoryId.Factory(), new ByteBuffersDirectory(), - new InMemoryMessageId.Factory(), - preInstanciationStage.getSessionProvider()))) - .noPreDeletionHooks() - .storeQuotaManager() - .build(); + .authenticator(authenticator) + .authorizator(authorizator) + .inVmEventBus() + .defaultAnnotationLimits() + .defaultMessageParser() + .listeningSearchIndex(Throwing.function(preInstanciationStage -> new LuceneMessageSearchIndex( + preInstanciationStage.getMapperFactory(), new InMemoryId.Factory(), new ByteBuffersDirectory(), + new InMemoryMessageId.Factory(), + preInstanciationStage.getSessionProvider()))) + .noPreDeletionHooks() + .storeQuotaManager() + .build(); mailboxManager = resources.getMailboxManager(); @@ -78,17 +78,17 @@ private void initFields() { SubscriptionManager subscriptionManager = new StoreSubscriptionManager(mailboxManager.getMapperFactory(), mailboxManager.getMapperFactory(), mailboxManager.getEventBus()); ImapProcessor defaultImapProcessorFactory = - DefaultImapProcessorFactory.createDefaultProcessor( - mailboxManager, - resources.getMailboxManager().getEventBus(), - subscriptionManager, - new NoQuotaManager(), - resources.getDefaultUserQuotaRootResolver(), - new DefaultMetricFactory()); + DefaultImapProcessorFactory.createDefaultProcessor( + mailboxManager, + resources.getMailboxManager().getEventBus(), + subscriptionManager, + new NoQuotaManager(), + resources.getDefaultUserQuotaRootResolver(), + new DefaultMetricFactory()); configure(new DefaultImapDecoderFactory().buildImapDecoder(), - new DefaultImapEncoderFactory().buildImapEncoder(), - defaultImapProcessorFactory); + new DefaultImapEncoderFactory().buildImapEncoder(), + defaultImapProcessorFactory); } @Override diff --git a/mpt/impl/imap-mailbox/opensearch/src/test/java/org/apache/james/mpt/imapmailbox/elasticsearch/host/OpenSearchHostSystem.java b/mpt/impl/imap-mailbox/opensearch/src/test/java/org/apache/james/mpt/imapmailbox/elasticsearch/host/OpenSearchHostSystem.java index 15da9b745bb..8ce82167aca 100644 --- a/mpt/impl/imap-mailbox/opensearch/src/test/java/org/apache/james/mpt/imapmailbox/elasticsearch/host/OpenSearchHostSystem.java +++ b/mpt/impl/imap-mailbox/opensearch/src/test/java/org/apache/james/mpt/imapmailbox/elasticsearch/host/OpenSearchHostSystem.java @@ -63,7 +63,7 @@ public class OpenSearchHostSystem extends JamesImapHostSystem { private static final ImapFeatures SUPPORTED_FEATURES = ImapFeatures.of(Feature.NAMESPACE_SUPPORT, - Feature.MOD_SEQ_SEARCH); + Feature.MOD_SEQ_SEARCH); private DockerOpenSearch dockerOpenSearch; private StoreMailboxManager mailboxManager; @@ -85,46 +85,46 @@ public void afterTest() throws IOException { private void initFields() throws Exception { client = MailboxIndexCreationUtil.prepareDefaultClient( - dockerOpenSearch.clientProvider().get(), - OpenSearchConfiguration.builder() - .addHost(dockerOpenSearch.getHttpHost()) - .build()); + dockerOpenSearch.clientProvider().get(), + OpenSearchConfiguration.builder() + .addHost(dockerOpenSearch.getHttpHost()) + .build()); InMemoryMessageId.Factory messageIdFactory = new InMemoryMessageId.Factory(); MailboxIdRoutingKeyFactory routingKeyFactory = new MailboxIdRoutingKeyFactory(); InMemoryIntegrationResources resources = InMemoryIntegrationResources.builder() - .authenticator(authenticator) - .authorizator(authorizator) - .inVmEventBus() - .defaultAnnotationLimits() - .defaultMessageParser() - .listeningSearchIndex(preInstanciationStage -> new OpenSearchListeningMessageSearchIndex( - preInstanciationStage.getMapperFactory(), - ImmutableSet.of(), - new OpenSearchIndexer(client, - MailboxOpenSearchConstants.DEFAULT_MAILBOX_WRITE_ALIAS), - new OpenSearchSearcher(client, new QueryConverter(new CriterionConverter()), OpenSearchSearcher.DEFAULT_SEARCH_SIZE, - MailboxOpenSearchConstants.DEFAULT_MAILBOX_READ_ALIAS, routingKeyFactory), - new MessageToOpenSearchJson(new DefaultTextExtractor(), ZoneId.of("Europe/Paris"), IndexAttachments.YES, IndexHeaders.YES), - preInstanciationStage.getSessionProvider(), routingKeyFactory, messageIdFactory, - OpenSearchMailboxConfiguration.builder().build(), new RecordingMetricFactory())) - .noPreDeletionHooks() - .storeQuotaManager() - .build(); + .authenticator(authenticator) + .authorizator(authorizator) + .inVmEventBus() + .defaultAnnotationLimits() + .defaultMessageParser() + .listeningSearchIndex(preInstanciationStage -> new OpenSearchListeningMessageSearchIndex( + preInstanciationStage.getMapperFactory(), + ImmutableSet.of(), + new OpenSearchIndexer(client, + MailboxOpenSearchConstants.DEFAULT_MAILBOX_WRITE_ALIAS), + new OpenSearchSearcher(client, new QueryConverter(new CriterionConverter()), OpenSearchSearcher.DEFAULT_SEARCH_SIZE, + MailboxOpenSearchConstants.DEFAULT_MAILBOX_READ_ALIAS, routingKeyFactory), + new MessageToOpenSearchJson(new DefaultTextExtractor(), ZoneId.of("Europe/Paris"), IndexAttachments.YES, IndexHeaders.YES), + preInstanciationStage.getSessionProvider(), routingKeyFactory, messageIdFactory, + OpenSearchMailboxConfiguration.builder().build(), new RecordingMetricFactory())) + .noPreDeletionHooks() + .storeQuotaManager() + .build(); mailboxManager = resources.getMailboxManager(); ImapProcessor defaultImapProcessorFactory = - DefaultImapProcessorFactory.createDefaultProcessor(mailboxManager, - resources.getMailboxManager().getEventBus(), - new StoreSubscriptionManager(mailboxManager.getMapperFactory(), mailboxManager.getMapperFactory(), mailboxManager.getEventBus()), - new NoQuotaManager(), - resources.getDefaultUserQuotaRootResolver(), - new DefaultMetricFactory()); + DefaultImapProcessorFactory.createDefaultProcessor(mailboxManager, + resources.getMailboxManager().getEventBus(), + new StoreSubscriptionManager(mailboxManager.getMapperFactory(), mailboxManager.getMapperFactory(), mailboxManager.getEventBus()), + new NoQuotaManager(), + resources.getDefaultUserQuotaRootResolver(), + new DefaultMetricFactory()); configure(new DefaultImapDecoderFactory().buildImapDecoder(), - new DefaultImapEncoderFactory().buildImapEncoder(), - defaultImapProcessorFactory); + new DefaultImapEncoderFactory().buildImapEncoder(), + defaultImapProcessorFactory); } @Override diff --git a/mpt/impl/imap-mailbox/rabbitmq/src/test/java/org/apache/james/mpt/imapmailbox/rabbitmq/host/RabbitMQEventBusHostSystem.java b/mpt/impl/imap-mailbox/rabbitmq/src/test/java/org/apache/james/mpt/imapmailbox/rabbitmq/host/RabbitMQEventBusHostSystem.java index 7bbf7b89d44..7a20f78d03c 100644 --- a/mpt/impl/imap-mailbox/rabbitmq/src/test/java/org/apache/james/mpt/imapmailbox/rabbitmq/host/RabbitMQEventBusHostSystem.java +++ b/mpt/impl/imap-mailbox/rabbitmq/src/test/java/org/apache/james/mpt/imapmailbox/rabbitmq/host/RabbitMQEventBusHostSystem.java @@ -58,11 +58,11 @@ public class RabbitMQEventBusHostSystem extends JamesImapHostSystem { private static final ImapFeatures SUPPORTED_FEATURES = ImapFeatures.of(Feature.NAMESPACE_SUPPORT, - Feature.MOVE_SUPPORT, - Feature.USER_FLAGS_SUPPORT, - Feature.QUOTA_SUPPORT, - Feature.ANNOTATION_SUPPORT, - Feature.MOD_SEQ_SEARCH); + Feature.MOVE_SUPPORT, + Feature.USER_FLAGS_SUPPORT, + Feature.QUOTA_SUPPORT, + Feature.ANNOTATION_SUPPORT, + Feature.MOD_SEQ_SEARCH); private final DockerRabbitMQ dockerRabbitMQ; private RabbitMQEventBus eventBus; @@ -82,35 +82,35 @@ public void beforeTest() throws Exception { .retries(2) .initialDelay(Duration.ofMillis(5))); reactorRabbitMQChannelPool = new ReactorRabbitMQChannelPool(connectionPool.getResilientConnection(), - ReactorRabbitMQChannelPool.Configuration.DEFAULT, - new DefaultMetricFactory(), new NoopGaugeRegistry()); + ReactorRabbitMQChannelPool.Configuration.DEFAULT, + new DefaultMetricFactory(), new NoopGaugeRegistry()); reactorRabbitMQChannelPool.start(); eventBus = createEventBus(); eventBus.start(); resources = InMemoryIntegrationResources.builder() - .authenticator(authenticator) - .authorizator(authorizator) - .eventBus(eventBus) - .defaultAnnotationLimits() - .defaultMessageParser() - .scanningSearchIndex() - .noPreDeletionHooks() - .storeQuotaManager() - .build(); + .authenticator(authenticator) + .authorizator(authorizator) + .eventBus(eventBus) + .defaultAnnotationLimits() + .defaultMessageParser() + .scanningSearchIndex() + .noPreDeletionHooks() + .storeQuotaManager() + .build(); ImapProcessor defaultImapProcessorFactory = - DefaultImapProcessorFactory.createDefaultProcessor( - resources.getMailboxManager(), - eventBus, - new StoreSubscriptionManager(resources.getMailboxManager().getMapperFactory(), resources.getMailboxManager().getMapperFactory(), resources.getMailboxManager().getEventBus()), - resources.getQuotaManager(), - resources.getDefaultUserQuotaRootResolver(), - new DefaultMetricFactory()); + DefaultImapProcessorFactory.createDefaultProcessor( + resources.getMailboxManager(), + eventBus, + new StoreSubscriptionManager(resources.getMailboxManager().getMapperFactory(), resources.getMailboxManager().getMapperFactory(), resources.getMailboxManager().getEventBus()), + resources.getQuotaManager(), + resources.getDefaultUserQuotaRootResolver(), + new DefaultMetricFactory()); configure(new DefaultImapDecoderFactory().buildImapDecoder(), - new DefaultImapEncoderFactory().buildImapEncoder(), - defaultImapProcessorFactory); + new DefaultImapEncoderFactory().buildImapEncoder(), + defaultImapProcessorFactory); } private RabbitMQEventBus createEventBus() throws Exception { @@ -119,9 +119,9 @@ private RabbitMQEventBus createEventBus() throws Exception { MailboxEventSerializer eventSerializer = new MailboxEventSerializer(mailboxIdFactory, messageIdFactory, new DefaultUserQuotaRootResolver.DefaultQuotaRootDeserializer()); RoutingKeyConverter routingKeyConverter = new RoutingKeyConverter(ImmutableSet.of(new MailboxIdRegistrationKey.Factory(mailboxIdFactory))); return new RabbitMQEventBus(MAILBOX_EVENT_NAMING_STRATEGY, reactorRabbitMQChannelPool.getSender(), reactorRabbitMQChannelPool::createReceiver, - eventSerializer, RetryBackoffConfiguration.DEFAULT, routingKeyConverter, new MemoryEventDeadLetters(), - new RecordingMetricFactory(), - reactorRabbitMQChannelPool, EventBusId.random(), dockerRabbitMQ.getConfiguration()); + eventSerializer, RetryBackoffConfiguration.DEFAULT, routingKeyConverter, new MemoryEventDeadLetters(), + new RecordingMetricFactory(), + reactorRabbitMQChannelPool, EventBusId.random(), dockerRabbitMQ.getConfiguration()); } @Override diff --git a/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPHealthCheckTest.java b/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPHealthCheckTest.java index 71674486469..25b5fa509dd 100644 --- a/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPHealthCheckTest.java +++ b/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPHealthCheckTest.java @@ -61,36 +61,36 @@ void setUp() throws Exception { FakeAuthenticator authenticator = new FakeAuthenticator(); InMemoryIntegrationResources memoryIntegrationResources = InMemoryIntegrationResources.builder() - .authenticator(authenticator) - .authorizator(FakeAuthorizator.defaultReject()) - .inVmEventBus() - .defaultAnnotationLimits() - .defaultMessageParser() - .scanningSearchIndex() - .noPreDeletionHooks() - .storeQuotaManager() - .build(); + .authenticator(authenticator) + .authorizator(FakeAuthorizator.defaultReject()) + .inVmEventBus() + .defaultAnnotationLimits() + .defaultMessageParser() + .scanningSearchIndex() + .noPreDeletionHooks() + .storeQuotaManager() + .build(); InMemoryMailboxManager mailboxManager = memoryIntegrationResources.getMailboxManager(); RecordingMetricFactory metricFactory = new RecordingMetricFactory(); imapServerFactory = new IMAPServerFactory( - FileSystemImpl.forTestingWithConfigurationFromClasspath(), - new DefaultImapDecoderFactory().buildImapDecoder(), - new DefaultImapEncoderFactory().buildImapEncoder(), - DefaultImapProcessorFactory.createXListSupportingProcessor( - mailboxManager, - memoryIntegrationResources.getEventBus(), - new StoreSubscriptionManager(mailboxManager.getMapperFactory(), - mailboxManager.getMapperFactory(), - mailboxManager.getEventBus()), - null, - memoryIntegrationResources.getQuotaManager(), - memoryIntegrationResources.getQuotaRootResolver(), - metricFactory), - new RecordingMetricFactory(), - new NoopGaugeRegistry(), - new DefaultConnectionCheckFactory()); + FileSystemImpl.forTestingWithConfigurationFromClasspath(), + new DefaultImapDecoderFactory().buildImapDecoder(), + new DefaultImapEncoderFactory().buildImapEncoder(), + DefaultImapProcessorFactory.createXListSupportingProcessor( + mailboxManager, + memoryIntegrationResources.getEventBus(), + new StoreSubscriptionManager(mailboxManager.getMapperFactory(), + mailboxManager.getMapperFactory(), + mailboxManager.getEventBus()), + null, + memoryIntegrationResources.getQuotaManager(), + memoryIntegrationResources.getQuotaRootResolver(), + metricFactory), + new RecordingMetricFactory(), + new NoopGaugeRegistry(), + new DefaultConnectionCheckFactory()); HierarchicalConfiguration config = ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("imapServerHealthCheck.xml")); imapServerFactory.configure(config); 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 06b745d564e..c39d54c0322 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 @@ -166,20 +166,20 @@ private IMAPServer createImapServer(HierarchicalConfiguration con Set connectionChecks = defaultConnectionChecks(); mailboxManager = spy(memoryIntegrationResources.getMailboxManager()); IMAPServer imapServer = new IMAPServer( - new DefaultImapDecoderFactory().buildImapDecoder(), - new DefaultImapEncoderFactory().buildImapEncoder(), - DefaultImapProcessorFactory.createXListSupportingProcessor( - mailboxManager, - memoryIntegrationResources.getEventBus(), - new StoreSubscriptionManager(mailboxManager.getMapperFactory(), - mailboxManager.getMapperFactory(), - mailboxManager.getEventBus()), - null, - memoryIntegrationResources.getQuotaManager(), - memoryIntegrationResources.getQuotaRootResolver(), - metricFactory), - new ImapMetrics(metricFactory), - new NoopGaugeRegistry(), connectionChecks); + new DefaultImapDecoderFactory().buildImapDecoder(), + new DefaultImapEncoderFactory().buildImapEncoder(), + DefaultImapProcessorFactory.createXListSupportingProcessor( + mailboxManager, + memoryIntegrationResources.getEventBus(), + new StoreSubscriptionManager(mailboxManager.getMapperFactory(), + mailboxManager.getMapperFactory(), + mailboxManager.getEventBus()), + null, + memoryIntegrationResources.getQuotaManager(), + memoryIntegrationResources.getQuotaRootResolver(), + metricFactory), + new ImapMetrics(metricFactory), + new NoopGaugeRegistry(), connectionChecks); FileSystemImpl fileSystem = FileSystemImpl.forTestingWithConfigurationFromClasspath(); imapServer.setFileSystem(fileSystem); @@ -197,15 +197,15 @@ private IMAPServer createImapServer(HierarchicalConfiguration con authenticator.addUser(USER3, USER_PASS); memoryIntegrationResources = InMemoryIntegrationResources.builder() - .authenticator(authenticator) - .authorizator(FakeAuthorizator.defaultReject()) - .inVmEventBus() - .defaultAnnotationLimits() - .defaultMessageParser() - .scanningSearchIndex() - .noPreDeletionHooks() - .storeQuotaManager() - .build(); + .authenticator(authenticator) + .authorizator(FakeAuthorizator.defaultReject()) + .inVmEventBus() + .defaultAnnotationLimits() + .defaultMessageParser() + .scanningSearchIndex() + .noPreDeletionHooks() + .storeQuotaManager() + .build(); return createImapServer(config, memoryIntegrationResources); } @@ -240,30 +240,30 @@ void tearDown() { @Test void banIpWhenBannedIpConnect() { imapServer.getConnectionChecks().stream() - .filter(check -> check instanceof IpConnectionCheck) - .map(check -> (IpConnectionCheck) check) - .forEach(ipCheck -> ipCheck.setBannedIps(Set.of("127.0.0.1"))); + .filter(check -> check instanceof IpConnectionCheck) + .map(check -> (IpConnectionCheck) check) + .forEach(ipCheck -> ipCheck.setBannedIps(Set.of("127.0.0.1"))); assertThatThrownBy(() -> testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .append("INBOX", SMALL_MESSAGE)); + .login(USER.asString(), USER_PASS) + .append("INBOX", SMALL_MESSAGE)); } @Test void allowConnectWithUnbannedIp() throws IOException { imapServer.getConnectionChecks().stream() - .filter(check -> check instanceof IpConnectionCheck) - .map(check -> (IpConnectionCheck) check) - .forEach(ipCheck -> ipCheck.setBannedIps(Set.of("127.0.0.2"))); + .filter(check -> check instanceof IpConnectionCheck) + .map(check -> (IpConnectionCheck) check) + .forEach(ipCheck -> ipCheck.setBannedIps(Set.of("127.0.0.2"))); testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .append("INBOX", SMALL_MESSAGE); + .login(USER.asString(), USER_PASS) + .append("INBOX", SMALL_MESSAGE); assertThat(testIMAPClient - .select("INBOX") - .readFirstMessage()) - .contains("* 1 FETCH (FLAGS (\\Recent \\Seen) BODY[] {21}\r\nheader: value\r\n\r\nBODY)\r\n"); + .select("INBOX") + .readFirstMessage()) + .contains("* 1 FETCH (FLAGS (\\Recent \\Seen) BODY[] {21}\r\nheader: value\r\n\r\nBODY)\r\n"); } } @@ -286,61 +286,61 @@ void tearDown() { @Test void fetchShouldRetrieveMessage() throws Exception { testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .append("INBOX", SMALL_MESSAGE); + .login(USER.asString(), USER_PASS) + .append("INBOX", SMALL_MESSAGE); assertThat(testIMAPClient - .select("INBOX") - .readFirstMessage()) - .contains("* 1 FETCH (FLAGS (\\Recent \\Seen) BODY[] {21}\r\nheader: value\r\n\r\nBODY)\r\n"); + .select("INBOX") + .readFirstMessage()) + .contains("* 1 FETCH (FLAGS (\\Recent \\Seen) BODY[] {21}\r\nheader: value\r\n\r\nBODY)\r\n"); } @Test void fetchShouldRetrieveMessageWhenOffsetAndLimitExceedingMessageSize() throws Exception { testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .append("INBOX", SMALL_MESSAGE); + .login(USER.asString(), USER_PASS) + .append("INBOX", SMALL_MESSAGE); assertThat(testIMAPClient - .select("INBOX") - .readFirstMessageInMailbox("BODY[]<8.20>")) - .contains("* 1 FETCH (FLAGS (\\Recent \\Seen) BODY[]<8> {13}\r\nvalue\r\n\r\nBODY)\r\n"); + .select("INBOX") + .readFirstMessageInMailbox("BODY[]<8.20>")) + .contains("* 1 FETCH (FLAGS (\\Recent \\Seen) BODY[]<8> {13}\r\nvalue\r\n\r\nBODY)\r\n"); } @Test void fetchShouldRetrieveMessageWhenOffsetAndLimitEqualMessageSize() throws Exception { testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .append("INBOX", SMALL_MESSAGE); + .login(USER.asString(), USER_PASS) + .append("INBOX", SMALL_MESSAGE); assertThat(testIMAPClient - .select("INBOX") - .readFirstMessageInMailbox("BODY[]<8.13>")) - .contains("* 1 FETCH (FLAGS (\\Recent \\Seen) BODY[]<8> {13}\r\nvalue\r\n\r\nBODY)\r\n"); + .select("INBOX") + .readFirstMessageInMailbox("BODY[]<8.13>")) + .contains("* 1 FETCH (FLAGS (\\Recent \\Seen) BODY[]<8> {13}\r\nvalue\r\n\r\nBODY)\r\n"); } @Test void fetchShouldRetrieveMessageWhenOffsetAndLimitBelowMessageSize() throws Exception { testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .append("INBOX", SMALL_MESSAGE); + .login(USER.asString(), USER_PASS) + .append("INBOX", SMALL_MESSAGE); assertThat(testIMAPClient - .select("INBOX") - .readFirstMessageInMailbox("BODY[]<8.12>")) - .contains("* 1 FETCH (FLAGS (\\Recent \\Seen) BODY[]<8> {12}\r\nvalue\r\n\r\nBOD)\r\n"); + .select("INBOX") + .readFirstMessageInMailbox("BODY[]<8.12>")) + .contains("* 1 FETCH (FLAGS (\\Recent \\Seen) BODY[]<8> {12}\r\nvalue\r\n\r\nBOD)\r\n"); } @Test void fetchShouldRetrieveMessageWhenOffsetAndNoLimitSpecified() throws Exception { testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .append("INBOX", SMALL_MESSAGE); + .login(USER.asString(), USER_PASS) + .append("INBOX", SMALL_MESSAGE); assertThat(testIMAPClient - .select("INBOX") - .readFirstMessageInMailbox("BODY[]<8>")) - .contains("* 1 FETCH (FLAGS (\\Recent \\Seen) BODY[]<8> {13}\r\nvalue\r\n\r\nBODY)\r\n"); + .select("INBOX") + .readFirstMessageInMailbox("BODY[]<8>")) + .contains("* 1 FETCH (FLAGS (\\Recent \\Seen) BODY[]<8> {13}\r\nvalue\r\n\r\nBODY)\r\n"); } } @@ -363,78 +363,78 @@ void tearDown() { @Test void smallAppendsShouldWork() throws Exception { assertThatCode(() -> - testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .append("INBOX", SMALL_MESSAGE)) - .doesNotThrowAnyException(); + testIMAPClient.connect("127.0.0.1", port) + .login(USER.asString(), USER_PASS) + .append("INBOX", SMALL_MESSAGE)) + .doesNotThrowAnyException(); assertThat(testIMAPClient.select("INBOX") - .readFirstMessage()) - .contains("\r\n" + SMALL_MESSAGE + ")\r\n"); + .readFirstMessage()) + .contains("\r\n" + SMALL_MESSAGE + ")\r\n"); } @Test void capabilityAdvertizeAppendLimit() throws Exception { assertThat( - testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .capability()) - .contains("APPENDLIMIT") - .doesNotContain("APPENDLIMIT="); + testIMAPClient.connect("127.0.0.1", port) + .login(USER.asString(), USER_PASS) + .capability()) + .contains("APPENDLIMIT") + .doesNotContain("APPENDLIMIT="); } @Test void statusAdvertizeAppendLimit() throws Exception { assertThat( - testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .sendCommand("STATUS \"INBOX\" (APPENDLIMIT)")) - .contains("* STATUS \"INBOX\" (APPENDLIMIT NIL)"); + testIMAPClient.connect("127.0.0.1", port) + .login(USER.asString(), USER_PASS) + .sendCommand("STATUS \"INBOX\" (APPENDLIMIT)")) + .contains("* STATUS \"INBOX\" (APPENDLIMIT NIL)"); } @Test void mediumAppendsShouldWork() throws Exception { assertThatCode(() -> - testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .append("INBOX", _65K_MESSAGE)) - .doesNotThrowAnyException(); + testIMAPClient.connect("127.0.0.1", port) + .login(USER.asString(), USER_PASS) + .append("INBOX", _65K_MESSAGE)) + .doesNotThrowAnyException(); assertThat(testIMAPClient.select("INBOX") - .readFirstMessage()) - .contains("\r\n" + _65K_MESSAGE + ")\r\n"); + .readFirstMessage()) + .contains("\r\n" + _65K_MESSAGE + ")\r\n"); } @Test void loginFixationShouldBeRejected() throws Exception { InMemoryMailboxManager mailboxManager = memoryIntegrationResources.getMailboxManager(); mailboxManager.createMailbox( - MailboxPath.forUser(USER, "pwnd"), - mailboxManager.createSystemSession(USER)); + MailboxPath.forUser(USER, "pwnd"), + mailboxManager.createSystemSession(USER)); mailboxManager.createMailbox( - MailboxPath.forUser(USER2, "notvuln"), - mailboxManager.createSystemSession(USER2)); + MailboxPath.forUser(USER2, "notvuln"), + mailboxManager.createSystemSession(USER2)); testIMAPClient.connect("127.0.0.1", port) - // Injected by a man in the middle attacker - .rawLogin(USER.asString(), USER_PASS); + // Injected by a man in the middle attacker + .rawLogin(USER.asString(), USER_PASS); assertThatThrownBy(() -> testIMAPClient.rawLogin(USER2.asString(), USER_PASS)) - .isInstanceOf(IOException.class) - .hasMessage("Login failed"); + .isInstanceOf(IOException.class) + .hasMessage("Login failed"); } @RepeatedTest(200) void largeAppendsShouldWork() throws Exception { assertThatCode(() -> - testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .append("INBOX", _129K_MESSAGE)) - .doesNotThrowAnyException(); + testIMAPClient.connect("127.0.0.1", port) + .login(USER.asString(), USER_PASS) + .append("INBOX", _129K_MESSAGE)) + .doesNotThrowAnyException(); assertThat(testIMAPClient.select("INBOX") - .readFirstMessage()) - .contains("\r\n" + _129K_MESSAGE + ")\r\n"); + .readFirstMessage()) + .contains("\r\n" + _129K_MESSAGE + ")\r\n"); } } @@ -448,7 +448,7 @@ void beforeEach() throws Exception { imapServer = createImapServer("imapServerNoLimits.xml"); int port = imapServer.getListenAddresses().get(0).getPort(); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), memoryIntegrationResources.getMailboxManager().createSystemSession(USER)); + .createMailbox(MailboxPath.inbox(USER), memoryIntegrationResources.getMailboxManager().createSystemSession(USER)); clientConnection = SocketChannel.open(); clientConnection.connect(new InetSocketAddress(LOCALHOST_IP, port)); readBytes(clientConnection); @@ -466,16 +466,16 @@ void appendShouldSucceedWhenNonSynchronized() throws Exception { readBytes(clientConnection); String msg = "Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)\r\n" + - "From: Fred Foobar \r\n" + - "Subject: afternoon meeting 2\r\n" + - "To: mooch@owatagu.siam.edu\r\n" + - "Message-Id: \r\n" + - "MIME-Version: 1.0\r\n" + - "Content-Type: TEXT/PLAIN; CHARSET=US-ASCII\r\n" + - "\r\n" + - "Hello Joe, could we change that to 4:00pm tomorrow?\r\n"; + "From: Fred Foobar \r\n" + + "Subject: afternoon meeting 2\r\n" + + "To: mooch@owatagu.siam.edu\r\n" + + "Message-Id: \r\n" + + "MIME-Version: 1.0\r\n" + + "Content-Type: TEXT/PLAIN; CHARSET=US-ASCII\r\n" + + "\r\n" + + "Hello Joe, could we change that to 4:00pm tomorrow?\r\n"; clientConnection.write(ByteBuffer.wrap(("A004 APPEND INBOX {" + msg.length() + "+}\r\n" + - msg + "\r\n").getBytes(StandardCharsets.UTF_8))); + msg + "\r\n").getBytes(StandardCharsets.UTF_8))); assertThat(new String(readBytes(clientConnection), StandardCharsets.US_ASCII)).contains("APPEND completed."); @@ -487,26 +487,26 @@ void fetchShouldNotFailWhenMixedWithUnselect() throws Exception { readBytes(clientConnection); String msg = "Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)\r\n" + - "From: Fred Foobar \r\n" + - "Subject: afternoon meeting 2\r\n" + - "To: mooch@owatagu.siam.edu\r\n" + - "Message-Id: \r\n" + - "MIME-Version: 1.0\r\n" + - "Content-Type: TEXT/PLAIN; CHARSET=US-ASCII\r\n" + - "\r\n" + - "Hello Joe, could we change that to 4:00pm tomorrow?\r\n"; + "From: Fred Foobar \r\n" + + "Subject: afternoon meeting 2\r\n" + + "To: mooch@owatagu.siam.edu\r\n" + + "Message-Id: \r\n" + + "MIME-Version: 1.0\r\n" + + "Content-Type: TEXT/PLAIN; CHARSET=US-ASCII\r\n" + + "\r\n" + + "Hello Joe, could we change that to 4:00pm tomorrow?\r\n"; clientConnection.write(ByteBuffer.wrap(("A004 APPEND INBOX {" + msg.length() + "+}\r\n" + - msg + "\r\n").getBytes(StandardCharsets.UTF_8))); + msg + "\r\n").getBytes(StandardCharsets.UTF_8))); assertThat(new String(readBytes(clientConnection), StandardCharsets.US_ASCII)).contains("APPEND completed."); for (int i = 0; i < 1000; i++) { clientConnection.write(ByteBuffer.wrap(("A005 SELECT INBOX\r\n") - .getBytes(StandardCharsets.UTF_8))); + .getBytes(StandardCharsets.UTF_8))); readStringUntil(clientConnection, s -> s.contains("A005 OK")); clientConnection.write(ByteBuffer.wrap(("A006 UID FETCH 1:1 FLAGS\r\nA007 UNSELECT\r\n") - .getBytes(StandardCharsets.UTF_8))); + .getBytes(StandardCharsets.UTF_8))); readStringUntil(clientConnection, s -> s.contains("FETCH completed.")); } @@ -518,16 +518,16 @@ void partialCommandAfterNonSynchronizedLiteralShouldNotFail() throws Exception { readBytes(clientConnection); String msg = "Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)\r\n" + - "From: Fred Foobar \r\n" + - "Subject: afternoon meeting 2\r\n" + - "To: mooch@owatagu.siam.edu\r\n" + - "Message-Id: \r\n" + - "MIME-Version: 1.0\r\n" + - "Content-Type: TEXT/PLAIN; CHARSET=US-ASCII\r\n" + - "\r\n" + - "Hello Joe, could we change that to 4:00pm tomorrow?\r\n"; + "From: Fred Foobar \r\n" + + "Subject: afternoon meeting 2\r\n" + + "To: mooch@owatagu.siam.edu\r\n" + + "Message-Id: \r\n" + + "MIME-Version: 1.0\r\n" + + "Content-Type: TEXT/PLAIN; CHARSET=US-ASCII\r\n" + + "\r\n" + + "Hello Joe, could we change that to 4:00pm tomorrow?\r\n"; clientConnection.write(ByteBuffer.wrap(("A004 APPEND INBOX {" + msg.length() + "+}\r\n" + - msg + "\r\nA005 NOOP").getBytes(StandardCharsets.UTF_8))); + msg + "\r\nA005 NOOP").getBytes(StandardCharsets.UTF_8))); assertThat(new String(readBytes(clientConnection), StandardCharsets.US_ASCII)).contains("APPEND completed."); } @@ -538,14 +538,14 @@ void extraDataAfterFirstLineShouldNotBeLost() throws Exception { readBytes(clientConnection); String msg = " Mon, 7 Feb 1994 21:52:25 -0800 (PST)\r\n" + - "From: Fred Foobar \r\n" + - "Subject: afternoon meeting 2\r\n" + - "To: mooch@owatagu.siam.edu\r\n" + - "Message-Id: \r\n" + - "MIME-Version: 1.0\r\n" + - "Content-Type: TEXT/PLAIN; CHARSET=US-ASCII\r\n" + - "\r\n" + - "Hello Joe, could we change that to 4:00pm tomorrow?\r\n"; + "From: Fred Foobar \r\n" + + "Subject: afternoon meeting 2\r\n" + + "To: mooch@owatagu.siam.edu\r\n" + + "Message-Id: \r\n" + + "MIME-Version: 1.0\r\n" + + "Content-Type: TEXT/PLAIN; CHARSET=US-ASCII\r\n" + + "\r\n" + + "Hello Joe, could we change that to 4:00pm tomorrow?\r\n"; clientConnection.write(ByteBuffer.wrap(("A004 APPEND INBOX {" + (msg.length() + 4) + "+}\r\nDATE").getBytes(StandardCharsets.UTF_8))); Thread.sleep(100); // Forces separate TCP messages @@ -574,7 +574,7 @@ void beforeEach() throws Exception { imapServer = createImapServer("imapServerProxy.xml"); int port = imapServer.getListenAddresses().get(0).getPort(); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), memoryIntegrationResources.getMailboxManager().createSystemSession(USER)); + .createMailbox(MailboxPath.inbox(USER), memoryIntegrationResources.getMailboxManager().createSystemSession(USER)); clientConnection = SocketChannel.open(); clientConnection.connect(new InetSocketAddress(LOCALHOST_IP, port)); readBytes(clientConnection); @@ -588,19 +588,19 @@ void tearDown() throws Exception { private void addBannedIps(String clientIp) { imapServer.getConnectionChecks().stream() - .filter(check -> check instanceof IpConnectionCheck) - .map(check -> (IpConnectionCheck) check) - .forEach(ipCheck -> ipCheck.setBannedIps(Set.of(clientIp))); + .filter(check -> check instanceof IpConnectionCheck) + .map(check -> (IpConnectionCheck) check) + .forEach(ipCheck -> ipCheck.setBannedIps(Set.of(clientIp))); } @Test void shouldNotFailOnProxyInformation() throws Exception { clientConnection.write(ByteBuffer.wrap(String.format("PROXY %s %s %s %d %d\r\na0 LOGIN %s %s\r\n", - "TCP4", CLIENT_IP, PROXY_IP, 65535, 65535, - USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); + "TCP4", CLIENT_IP, PROXY_IP, 65535, 65535, + USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); assertThat(new String(readBytes(clientConnection), StandardCharsets.US_ASCII)) - .startsWith("a0 OK"); + .startsWith("a0 OK"); } @Test @@ -609,12 +609,12 @@ void shouldDetectAndBanByClientIP() throws IOException { // WHEN connect as CLIENT_IP to PROXY_DESTINATION via PROXY_IP clientConnection.write(ByteBuffer.wrap(String.format("PROXY %s %s %s %d %d\r\na0 LOGIN %s %s\r\n", - "TCP4", CLIENT_IP, PROXY_IP, 65535, 65535, - USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); + "TCP4", CLIENT_IP, PROXY_IP, 65535, 65535, + USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); // THEN LOGIN should be rejected assertThat(new String(readBytes(clientConnection), StandardCharsets.US_ASCII)) - .doesNotStartWith("a0 OK"); + .doesNotStartWith("a0 OK"); } @Test @@ -623,12 +623,12 @@ void shouldNotBanByProxyIP() throws IOException { addBannedIps(PROXY_IP); clientConnection.write(ByteBuffer.wrap(String.format("PROXY %s %s %s %d %d\r\na0 LOGIN %s %s\r\n", - "TCP4", CLIENT_IP, PROXY_IP, 65535, 65535, - USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); + "TCP4", CLIENT_IP, PROXY_IP, 65535, 65535, + USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); // THEN CLIENT_IP still can connect assertThat(new String(readBytes(clientConnection), StandardCharsets.US_ASCII)) - .startsWith("a0 OK"); + .startsWith("a0 OK"); } @Test @@ -636,11 +636,11 @@ void clientUsageShouldBeNormalWhenClientIPIsNotBanned() throws IOException { addBannedIps(RANDOM_IP); clientConnection.write(ByteBuffer.wrap(String.format("PROXY %s %s %s %d %d\r\na0 LOGIN %s %s\r\n", - "TCP4", CLIENT_IP, PROXY_IP, 65535, 65535, - USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); + "TCP4", CLIENT_IP, PROXY_IP, 65535, 65535, + USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); assertThat(new String(readBytes(clientConnection), StandardCharsets.US_ASCII)) - .startsWith("a0 OK"); + .startsWith("a0 OK"); } } @@ -665,10 +665,10 @@ void shouldNotThrowWhenCompressionEnabled() throws Exception { InMemoryMailboxManager mailboxManager = memoryIntegrationResources.getMailboxManager(); MailboxSession mailboxSession = mailboxManager.createSystemSession(USER); mailboxManager.createMailbox( - MailboxPath.inbox(USER), - mailboxSession); + MailboxPath.inbox(USER), + mailboxSession); mailboxManager.getMailbox(MailboxPath.inbox(USER), mailboxSession) - .appendMessage(MessageManager.AppendCommand.builder().build("header: value\r\n\r\nbody"), mailboxSession); + .appendMessage(MessageManager.AppendCommand.builder().build("header: value\r\n\r\nbody"), mailboxSession); Properties props = new Properties(); props.put("mail.imap.user", USER.asString()); @@ -689,8 +689,8 @@ void shouldNotThrowWhenCompressionEnabled() throws Exception { @Test void compressShouldFailWhenUnknownCompressionAlgorithm() throws Exception { String reply = testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .sendCommand("COMPRESS BAD"); + .login(USER.asString(), USER_PASS) + .sendCommand("COMPRESS BAD"); assertThat(reply).contains("AAAB BAD COMPRESS failed. Illegal arguments."); } @@ -717,10 +717,10 @@ void shouldNotThrowWhenCompressionEnabled() throws Exception { InMemoryMailboxManager mailboxManager = memoryIntegrationResources.getMailboxManager(); MailboxSession mailboxSession = mailboxManager.createSystemSession(USER); mailboxManager.createMailbox( - MailboxPath.inbox(USER), - mailboxSession); + MailboxPath.inbox(USER), + mailboxSession); mailboxManager.getMailbox(MailboxPath.inbox(USER), mailboxSession) - .appendMessage(MessageManager.AppendCommand.builder().build("header: value\r\n\r\nbody"), mailboxSession); + .appendMessage(MessageManager.AppendCommand.builder().build("header: value\r\n\r\nbody"), mailboxSession); Properties props = new Properties(); props.put("mail.imaps.user", USER.asString()); @@ -781,15 +781,15 @@ void beforeEach() throws Exception { imapServer = createImapServer("imapServerStartTLS.xml"); port = imapServer.getListenAddresses().get(0).getPort(); connection = TcpClient.create() - .noSSL() - .remoteAddress(() -> new InetSocketAddress(LOCALHOST_IP, port)) - .option(ChannelOption.TCP_NODELAY, true) - .connectNow(); + .noSSL() + .remoteAddress(() -> new InetSocketAddress(LOCALHOST_IP, port)) + .option(ChannelOption.TCP_NODELAY, true) + .connectNow(); responses = new ConcurrentLinkedDeque<>(); connection.inbound().receive().asString() - .doOnNext(responses::addLast) - .subscribeOn(Schedulers.newSingle("imap-test")) - .subscribe(); + .doOnNext(responses::addLast) + .subscribeOn(Schedulers.newSingle("imap-test")) + .subscribe(); } @AfterEach @@ -802,8 +802,8 @@ void extraLinesBatchedWithStartTLSShouldBeSanitized() throws Exception { IMAPSClient imapClient = new IMAPSClient(); imapClient.connect("127.0.0.1", port); assertThatThrownBy(() -> imapClient.sendCommand("STARTTLS\r\nA1 NOOP\r\n")) - .isInstanceOf(EOFException.class) - .hasMessage("Connection closed without indication."); + .isInstanceOf(EOFException.class) + .hasMessage("Connection closed without indication."); } @Test @@ -811,8 +811,8 @@ void extraLFLinesBatchedWithStartTLSShouldBeSanitized() throws Exception { IMAPSClient imapClient = new IMAPSClient(); imapClient.connect("127.0.0.1", port); assertThatThrownBy(() -> imapClient.sendCommand("STARTTLS\nA1 NOOP\r\n")) - .isInstanceOf(EOFException.class) - .hasMessage("Connection closed without indication."); + .isInstanceOf(EOFException.class) + .hasMessage("Connection closed without indication."); } @Test @@ -820,8 +820,8 @@ void tagsShouldBeWellSanitized() throws Exception { IMAPSClient imapClient = new IMAPSClient(); imapClient.connect("127.0.0.1", port); assertThatThrownBy(() -> imapClient.sendCommand("NOOP\r\n A1 STARTTLS\r\nA2 NOOP")) - .isInstanceOf(EOFException.class) - .hasMessage("Connection closed without indication."); + .isInstanceOf(EOFException.class) + .hasMessage("Connection closed without indication."); } @Test @@ -829,8 +829,8 @@ void lineFollowingStartTLSShouldBeSanitized() throws Exception { IMAPSClient imapClient = new IMAPSClient(); imapClient.connect("127.0.0.1", port); assertThatThrownBy(() -> imapClient.sendCommand("STARTTLS A1 NOOP\r\n")) - .isInstanceOf(EOFException.class) - .hasMessage("Connection closed without indication."); + .isInstanceOf(EOFException.class) + .hasMessage("Connection closed without indication."); } @Test @@ -848,10 +848,10 @@ void startTLSShouldFailWhenAuthenticated() throws Exception { private void send(String format) { connection.outbound() - .send(Mono.just(Unpooled.wrappedBuffer(format - .getBytes(StandardCharsets.UTF_8)))) - .then() - .subscribe(); + .send(Mono.just(Unpooled.wrappedBuffer(format + .getBytes(StandardCharsets.UTF_8)))) + .then() + .subscribe(); } @RepeatedTest(10) @@ -864,8 +864,8 @@ void concurrencyShouldNotLeadToCommandInjection() throws Exception { Thread.sleep(50); assertThat(listAppender.list) - .filteredOn(event -> event.getFormattedMessage().contains("Processing org.apache.james.imap.message.request.NoopRequest")) - .isEmpty(); + .filteredOn(event -> event.getFormattedMessage().contains("Processing org.apache.james.imap.message.request.NoopRequest")) + .isEmpty(); } } @@ -883,85 +883,85 @@ void tearDown() { @Test void initShouldAcceptJKSFormat() { assertThatCode(() -> imapServer = createImapServer("imapServerSslJKS.xml")) - .doesNotThrowAnyException(); + .doesNotThrowAnyException(); } @Test void initShouldAcceptPKCS12Format() { assertThatCode(() -> imapServer = createImapServer("imapServerSslPKCS12.xml")) - .doesNotThrowAnyException(); + .doesNotThrowAnyException(); } @Test void initShouldAcceptPEMKeysWithPassword() { assertThatCode(() -> imapServer = createImapServer("imapServerSslPEM.xml")) - .doesNotThrowAnyException(); + .doesNotThrowAnyException(); } @Test void initShouldAcceptPEMKeysWithoutPassword() { assertThatCode(() -> imapServer = createImapServer("imapServerSslPEMNoPass.xml")) - .doesNotThrowAnyException(); + .doesNotThrowAnyException(); } @Test void initShouldAcceptJKSByDefault() { assertThatCode(() -> imapServer = createImapServer("imapServerSslDefaultJKS.xml")) - .doesNotThrowAnyException(); + .doesNotThrowAnyException(); } @Test void initShouldThrowWhenSslEnabledWithoutKeys() { assertThatThrownBy(() -> createImapServer("imapServerSslNoKeys.xml")) - .isInstanceOf(ConfigurationException.class) - .hasMessageContaining("keystore or (privateKey and certificates) needs to get configured"); + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining("keystore or (privateKey and certificates) needs to get configured"); } @Test void initShouldThrowWhenJKSWithBadPassword() { assertThatThrownBy(() -> createImapServer("imapServerSslJKSBadPassword.xml")) - .isInstanceOf(GenericKeyStoreException.class) - .hasMessageContaining("keystore password was incorrect"); + .isInstanceOf(GenericKeyStoreException.class) + .hasMessageContaining("keystore password was incorrect"); } @Test void initShouldThrowWhenPEMWithBadPassword() { assertThatThrownBy(() -> createImapServer("imapServerSslPEMBadPass.xml")) - .isInstanceOf(PrivateKeyParseException.class); + .isInstanceOf(PrivateKeyParseException.class); } @Test void initShouldThrowWhenPEMWithMissingPassword() { assertThatThrownBy(() -> createImapServer("imapServerSslPEMMissingPass.xml")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("A password is mandatory with an encrypted key"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("A password is mandatory with an encrypted key"); } @Test void initShouldNotThrowWhenPEMWithExtraPassword() { assertThatCode(() -> imapServer = createImapServer("imapServerSslPEMExtraPass.xml")) - .doesNotThrowAnyException(); + .doesNotThrowAnyException(); } @Test void initShouldThrowWhenJKSWenNotFound() { assertThatThrownBy(() -> createImapServer("imapServerSslJKSNotFound.xml")) - .isInstanceOf(FileNotFoundException.class) - .hasMessage("class path resource [keystore.notfound.jks] cannot be resolved to URL because it does not exist"); + .isInstanceOf(FileNotFoundException.class) + .hasMessage("class path resource [keystore.notfound.jks] cannot be resolved to URL because it does not exist"); } @Test void initShouldThrowWhenPKCS12WithBadPassword() { assertThatThrownBy(() -> createImapServer("imapServerSslPKCS12WrongPassword.xml")) - .isInstanceOf(GenericKeyStoreException.class) - .hasMessageContaining("keystore password was incorrect"); + .isInstanceOf(GenericKeyStoreException.class) + .hasMessageContaining("keystore password was incorrect"); } @Test void initShouldThrowWhenPKCS12WithMissingPassword() { assertThatThrownBy(() -> createImapServer("imapServerSslPKCS12MissingPassword.xml")) - .isInstanceOf(GenericKeyStoreException.class) - .hasMessageContaining("keystore password was incorrect"); + .isInstanceOf(GenericKeyStoreException.class) + .hasMessageContaining("keystore password was incorrect"); } } @@ -984,55 +984,55 @@ void tearDown() { @Test void smallAppendsShouldWork() throws Exception { assertThatCode(() -> - testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .append("INBOX", SMALL_MESSAGE)) - .doesNotThrowAnyException(); + testIMAPClient.connect("127.0.0.1", port) + .login(USER.asString(), USER_PASS) + .append("INBOX", SMALL_MESSAGE)) + .doesNotThrowAnyException(); assertThat(testIMAPClient.select("INBOX") - .readFirstMessage()) - .contains("\r\n" + SMALL_MESSAGE + ")\r\n"); + .readFirstMessage()) + .contains("\r\n" + SMALL_MESSAGE + ")\r\n"); } @Test void mediumAppendsShouldWork() throws Exception { assertThatCode(() -> - testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .append("INBOX", _65K_MESSAGE)) - .doesNotThrowAnyException(); + testIMAPClient.connect("127.0.0.1", port) + .login(USER.asString(), USER_PASS) + .append("INBOX", _65K_MESSAGE)) + .doesNotThrowAnyException(); assertThat(testIMAPClient.select("INBOX") - .readFirstMessage()) - .contains("\r\n" + _65K_MESSAGE + ")\r\n"); + .readFirstMessage()) + .contains("\r\n" + _65K_MESSAGE + ")\r\n"); } @Test void largeAppendsShouldBeRejected() { assertThatThrownBy(() -> - testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .append("INBOX", _129K_MESSAGE)) - .isInstanceOf(RuntimeException.class) - .hasMessageContaining("BAD APPEND failed."); + testIMAPClient.connect("127.0.0.1", port) + .login(USER.asString(), USER_PASS) + .append("INBOX", _129K_MESSAGE)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("BAD APPEND failed."); } @Test void capabilityAdvertizeAppendLimit() throws Exception { assertThat( - testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .capability()) - .contains("APPENDLIMIT=131072"); + testIMAPClient.connect("127.0.0.1", port) + .login(USER.asString(), USER_PASS) + .capability()) + .contains("APPENDLIMIT=131072"); } @Test void statusAdvertizeAppendLimit() throws Exception { assertThat( - testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .sendCommand("STATUS \"INBOX\" (APPENDLIMIT)")) - .contains("* STATUS \"INBOX\" (APPENDLIMIT 131072)"); + testIMAPClient.connect("127.0.0.1", port) + .login(USER.asString(), USER_PASS) + .sendCommand("STATUS \"INBOX\" (APPENDLIMIT)")) + .contains("* STATUS \"INBOX\" (APPENDLIMIT 131072)"); } } @@ -1055,17 +1055,17 @@ void tearDown() { @Test void loginShouldFail() { assertThatThrownBy(() -> - testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS)) - .hasMessage("Login failed"); + testIMAPClient.connect("127.0.0.1", port) + .login(USER.asString(), USER_PASS)) + .hasMessage("Login failed"); } @Test void authenticatePlainShouldFail() { assertThatThrownBy(() -> - testIMAPClient.connect("127.0.0.1", port) - .authenticatePlain(USER.asString(), USER_PASS)) - .hasMessage("Login failed"); + testIMAPClient.connect("127.0.0.1", port) + .authenticatePlain(USER.asString(), USER_PASS)) + .hasMessage("Login failed"); } @Test @@ -1073,8 +1073,8 @@ void capabilityShouldNotAdvertiseLoginAndAuthenticationPlain() throws Exception testIMAPClient.connect("127.0.0.1", port); assertThat(testIMAPClient.capability()) - .contains("LOGINDISABLED") - .doesNotContain("AUTH=PLAIN"); + .contains("LOGINDISABLED") + .doesNotContain("AUTH=PLAIN"); } } @@ -1097,17 +1097,17 @@ void tearDown() { @Test void loginShouldSucceed() { assertThatCode(() -> - testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS)) - .doesNotThrowAnyException(); + testIMAPClient.connect("127.0.0.1", port) + .login(USER.asString(), USER_PASS)) + .doesNotThrowAnyException(); } @RepeatedTest(100) void authenticatePlainShouldSucceed() { assertThatCode(() -> - testIMAPClient.connect("127.0.0.1", port) - .authenticatePlain(USER.asString(), USER_PASS)) - .doesNotThrowAnyException(); + testIMAPClient.connect("127.0.0.1", port) + .authenticatePlain(USER.asString(), USER_PASS)) + .doesNotThrowAnyException(); } @Test @@ -1115,8 +1115,8 @@ void capabilityShouldAdvertiseLoginAndAuthenticationPlain() throws Exception { testIMAPClient.connect("127.0.0.1", port); assertThat(testIMAPClient.capability()) - .doesNotContain("LOGINDISABLED") - .contains("AUTH=PLAIN"); + .doesNotContain("LOGINDISABLED") + .contains("AUTH=PLAIN"); } @Test @@ -1125,9 +1125,9 @@ void authenticatePlainShouldSucceedWhenPasswordHasMoreThan255Characters() { String user1Password = "1".repeat(300); authenticator.addUser(user1, user1Password); assertThatCode(() -> - testIMAPClient.connect("127.0.0.1", port) - .authenticatePlain(user1.asString(), user1Password)) - .doesNotThrowAnyException(); + testIMAPClient.connect("127.0.0.1", port) + .authenticatePlain(user1.asString(), user1Password)) + .doesNotThrowAnyException(); } } @@ -1150,9 +1150,9 @@ void tearDown() { @Test void loginShouldFailOnUnEncryptedChannel() { assertThatThrownBy(() -> - testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS)) - .hasMessage("Login failed"); + testIMAPClient.connect("127.0.0.1", port) + .login(USER.asString(), USER_PASS)) + .hasMessage("Login failed"); } @Test @@ -1160,8 +1160,8 @@ void capabilityShouldNotAdvertiseLoginOnUnEncryptedChannel() throws Exception { testIMAPClient.connect("127.0.0.1", port); assertThat(testIMAPClient.capability()) - .contains("LOGINDISABLED") - .doesNotContain("AUTH=PLAIN"); + .contains("LOGINDISABLED") + .doesNotContain("AUTH=PLAIN"); } } @@ -1184,9 +1184,9 @@ void tearDown() { @Test void loginShouldSucceedOnUnEncryptedChannel() { assertThatCode(() -> - testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS)) - .doesNotThrowAnyException(); + testIMAPClient.connect("127.0.0.1", port) + .login(USER.asString(), USER_PASS)) + .doesNotThrowAnyException(); } @Test @@ -1194,8 +1194,8 @@ void capabilityShouldAdvertiseLoginOnUnEncryptedChannel() throws Exception { testIMAPClient.connect("127.0.0.1", port); assertThat(testIMAPClient.capability()) - .doesNotContain("LOGINDISABLED") - .contains("AUTH=PLAIN"); + .doesNotContain("LOGINDISABLED") + .contains("AUTH=PLAIN"); } } @@ -1216,9 +1216,9 @@ void loginShouldFailWhenRequireSSLAndUnEncryptedChannel() throws Exception { int port = imapServer.getListenAddresses().get(0).getPort(); assertThatThrownBy(() -> - testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS)) - .hasMessage("Login failed"); + testIMAPClient.connect("127.0.0.1", port) + .login(USER.asString(), USER_PASS)) + .hasMessage("Login failed"); } @@ -1242,10 +1242,10 @@ void loginShouldSuccessWhenNOTRequireSSLAndUnEncryptedChannel() throws Exception int port = imapServer.getListenAddresses().get(0).getPort(); assertThatCode(() -> - testIMAPClient.connect("127.0.0.1", port) - .login(USER.asString(), USER_PASS) - .append("INBOX", SMALL_MESSAGE)) - .doesNotThrowAnyException(); + testIMAPClient.connect("127.0.0.1", port) + .login(USER.asString(), USER_PASS) + .append("INBOX", SMALL_MESSAGE)) + .doesNotThrowAnyException(); } @Test @@ -1276,25 +1276,25 @@ class Oidc { void authSetup() throws Exception { authServer = ClientAndServer.startClientAndServer(0); authServer - .when(HttpRequest.request().withPath(JWKS_URI_PATH)) - .respond(HttpResponse.response().withStatusCode(200) - .withHeader("Content-Type", "application/json") - .withBody(OidcTokenFixture.JWKS_RESPONSE, StandardCharsets.UTF_8)); + .when(HttpRequest.request().withPath(JWKS_URI_PATH)) + .respond(HttpResponse.response().withStatusCode(200) + .withHeader("Content-Type", "application/json") + .withBody(OidcTokenFixture.JWKS_RESPONSE, StandardCharsets.UTF_8)); FakeAuthenticator authenticator = new FakeAuthenticator(); authenticator.addUser(USER, USER_PASS); authenticator.addUser(USER2, USER_PASS); InMemoryIntegrationResources integrationResources = InMemoryIntegrationResources.builder() - .authenticator(authenticator) - .authorizator(FakeAuthorizator.forGivenUserAndDelegatedUser(USER, USER2)) - .inVmEventBus() - .defaultAnnotationLimits() - .defaultMessageParser() - .scanningSearchIndex() - .noPreDeletionHooks() - .storeQuotaManager() - .build(); + .authenticator(authenticator) + .authorizator(FakeAuthorizator.forGivenUserAndDelegatedUser(USER, USER2)) + .inVmEventBus() + .defaultAnnotationLimits() + .defaultMessageParser() + .scanningSearchIndex() + .noPreDeletionHooks() + .storeQuotaManager() + .build(); HierarchicalConfiguration config = ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("oauth.xml")); config.addProperty("auth.oidc.jwksURL", String.format("http://127.0.0.1:%s%s", authServer.getLocalPort(), JWKS_URI_PATH)); @@ -1408,10 +1408,10 @@ void oauthShouldFailWhenIntrospectTokenReturnActiveIsFalse() throws Exception { imapServer.destroy(); authServer - .when(HttpRequest.request().withPath(INTROSPECT_TOKEN_URI_PATH)) - .respond(HttpResponse.response().withStatusCode(200) - .withHeader("Content-Type", "application/json") - .withBody("{\"active\": false}", StandardCharsets.UTF_8)); + .when(HttpRequest.request().withPath(INTROSPECT_TOKEN_URI_PATH)) + .respond(HttpResponse.response().withStatusCode(200) + .withHeader("Content-Type", "application/json") + .withBody("{\"active\": false}", StandardCharsets.UTF_8)); HierarchicalConfiguration config = ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("oauth.xml")); config.addProperty("auth.oidc.jwksURL", String.format("http://127.0.0.1:%s%s", authServer.getLocalPort(), JWKS_URI_PATH)); @@ -1435,10 +1435,10 @@ void oauthShouldSuccessWhenIntrospectTokenReturnActiveIsTrue() throws Exception imapServer.destroy(); authServer - .when(HttpRequest.request().withPath(INTROSPECT_TOKEN_URI_PATH)) - .respond(HttpResponse.response().withStatusCode(200) - .withHeader("Content-Type", "application/json") - .withBody(INTROSPECTION_RESPONSE, StandardCharsets.UTF_8)); + .when(HttpRequest.request().withPath(INTROSPECT_TOKEN_URI_PATH)) + .respond(HttpResponse.response().withStatusCode(200) + .withHeader("Content-Type", "application/json") + .withBody(INTROSPECTION_RESPONSE, StandardCharsets.UTF_8)); HierarchicalConfiguration config = ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("oauth.xml")); config.addProperty("auth.oidc.jwksURL", String.format("http://127.0.0.1:%s%s", authServer.getLocalPort(), JWKS_URI_PATH)); @@ -1462,8 +1462,8 @@ void oauthShouldFailWhenIntrospectTokenServerError() throws Exception { imapServer.destroy(); String invalidURI = "/invalidURI"; authServer - .when(HttpRequest.request().withPath(invalidURI)) - .respond(HttpResponse.response().withStatusCode(401)); + .when(HttpRequest.request().withPath(invalidURI)) + .respond(HttpResponse.response().withStatusCode(401)); HierarchicalConfiguration config = ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("oauth.xml")); config.addProperty("auth.oidc.jwksURL", String.format("http://127.0.0.1:%s%s", authServer.getLocalPort(), JWKS_URI_PATH)); @@ -1487,10 +1487,10 @@ void oauthShouldSuccessWhenCheckTokenByUserInfoIsPassed() throws Exception { imapServer.destroy(); authServer - .when(HttpRequest.request().withPath(USERINFO_URI_PATH)) - .respond(HttpResponse.response().withStatusCode(200) - .withHeader("Content-Type", "application/json") - .withBody(OidcTokenFixture.USERINFO_RESPONSE, StandardCharsets.UTF_8)); + .when(HttpRequest.request().withPath(USERINFO_URI_PATH)) + .respond(HttpResponse.response().withStatusCode(200) + .withHeader("Content-Type", "application/json") + .withBody(OidcTokenFixture.USERINFO_RESPONSE, StandardCharsets.UTF_8)); HierarchicalConfiguration config = ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("oauth.xml")); config.addProperty("auth.oidc.jwksURL", String.format("http://127.0.0.1:%s%s", authServer.getLocalPort(), JWKS_URI_PATH)); @@ -1514,9 +1514,9 @@ void oauthShouldFailWhenCheckTokenByUserInfoIsFailed() throws Exception { imapServer.destroy(); authServer - .when(HttpRequest.request().withPath(USERINFO_URI_PATH)) - .respond(HttpResponse.response().withStatusCode(401) - .withHeader("Content-Type", "application/json")); + .when(HttpRequest.request().withPath(USERINFO_URI_PATH)) + .respond(HttpResponse.response().withStatusCode(401) + .withHeader("Content-Type", "application/json")); HierarchicalConfiguration config = ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("oauth.xml")); config.addProperty("auth.oidc.jwksURL", String.format("http://127.0.0.1:%s%s", authServer.getLocalPort(), JWKS_URI_PATH)); @@ -1556,7 +1556,7 @@ void impersonationShouldWorkWhenDelegated() throws Exception { // USER2: append a message try (TestIMAPClient client = new TestIMAPClient(imapsClient(port))) { client.login(USER2.asString(), USER_PASS) - .append("INBOX", SMALL_MESSAGE); + .append("INBOX", SMALL_MESSAGE); } // USER1 authenticate and impersonate as USER2 @@ -1566,7 +1566,7 @@ void impersonationShouldWorkWhenDelegated() throws Exception { assertThat(authenticateResponse).contains("OK AUTHENTICATE completed."); assertThat(client.select("INBOX") - .readFirstMessage()).contains(SMALL_MESSAGE); + .readFirstMessage()).contains(SMALL_MESSAGE); } } } @@ -1606,21 +1606,21 @@ void tearDown() throws Exception { void imapSearchShouldSupportThreadId() throws Exception { MailboxSession mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); MessageManager.AppendResult appendResult = memoryIntegrationResources.getMailboxManager() - .getMailbox(MailboxPath.inbox(USER), mailboxSession) - .appendMessage(MessageManager.AppendCommand.builder().build("MIME-Version: 1.0\r\n" + - "Content-Type: text/html; charset=UTF-8\r\n" + - "Content-Transfer-Encoding: quoted-printable\r\n" + - "From: =?ISO-8859-1?Q?Beno=EEt_TELLIER?= \r\n" + - "Sender: =?ISO-8859-1?Q?Beno=EEt_TELLIER?= \r\n" + - "Reply-To: b@linagora.com\r\n" + - "To: =?ISO-8859-1?Q?Beno=EEt_TELLIER?= \r\n" + - "Subject: Test utf-8 charset\r\n" + - "Message-ID: \r\n" + - "Date: Sun, 28 Mar 2021 03:58:06 +0000\r\n" + - "\r\n" + - "

=E5=A4=A9=E5=A4=A9=E5=90=91=E4=B8=8A

\r\n"), mailboxSession); + .getMailbox(MailboxPath.inbox(USER), mailboxSession) + .appendMessage(MessageManager.AppendCommand.builder().build("MIME-Version: 1.0\r\n" + + "Content-Type: text/html; charset=UTF-8\r\n" + + "Content-Transfer-Encoding: quoted-printable\r\n" + + "From: =?ISO-8859-1?Q?Beno=EEt_TELLIER?= \r\n" + + "Sender: =?ISO-8859-1?Q?Beno=EEt_TELLIER?= \r\n" + + "Reply-To: b@linagora.com\r\n" + + "To: =?ISO-8859-1?Q?Beno=EEt_TELLIER?= \r\n" + + "Subject: Test utf-8 charset\r\n" + + "Message-ID: \r\n" + + "Date: Sun, 28 Mar 2021 03:58:06 +0000\r\n" + + "\r\n" + + "

=E5=A4=A9=E5=A4=A9=E5=90=91=E4=B8=8A

\r\n"), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readStringUntil(clientConnection, s -> s.contains("a0 OK")); @@ -1636,21 +1636,21 @@ void imapSearchShouldSupportThreadId() throws Exception { void imapSearchShouldSupportEmailId() throws Exception { MailboxSession mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); MessageManager.AppendResult appendResult = memoryIntegrationResources.getMailboxManager() - .getMailbox(MailboxPath.inbox(USER), mailboxSession) - .appendMessage(MessageManager.AppendCommand.builder().build("MIME-Version: 1.0\r\n" + - "Content-Type: text/html; charset=UTF-8\r\n" + - "Content-Transfer-Encoding: quoted-printable\r\n" + - "From: =?ISO-8859-1?Q?Beno=EEt_TELLIER?= \r\n" + - "Sender: =?ISO-8859-1?Q?Beno=EEt_TELLIER?= \r\n" + - "Reply-To: b@linagora.com\r\n" + - "To: =?ISO-8859-1?Q?Beno=EEt_TELLIER?= \r\n" + - "Subject: Test utf-8 charset\r\n" + - "Message-ID: \r\n" + - "Date: Sun, 28 Mar 2021 03:58:06 +0000\r\n" + - "\r\n" + - "

=E5=A4=A9=E5=A4=A9=E5=90=91=E4=B8=8A

\r\n"), mailboxSession); + .getMailbox(MailboxPath.inbox(USER), mailboxSession) + .appendMessage(MessageManager.AppendCommand.builder().build("MIME-Version: 1.0\r\n" + + "Content-Type: text/html; charset=UTF-8\r\n" + + "Content-Transfer-Encoding: quoted-printable\r\n" + + "From: =?ISO-8859-1?Q?Beno=EEt_TELLIER?= \r\n" + + "Sender: =?ISO-8859-1?Q?Beno=EEt_TELLIER?= \r\n" + + "Reply-To: b@linagora.com\r\n" + + "To: =?ISO-8859-1?Q?Beno=EEt_TELLIER?= \r\n" + + "Subject: Test utf-8 charset\r\n" + + "Message-ID: \r\n" + + "Date: Sun, 28 Mar 2021 03:58:06 +0000\r\n" + + "\r\n" + + "

=E5=A4=A9=E5=A4=A9=E5=90=91=E4=B8=8A

\r\n"), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readStringUntil(clientConnection, s -> s.contains("a0 OK")); @@ -1665,7 +1665,7 @@ void imapSearchShouldSupportEmailId() throws Exception { void shouldConsiderCumulativeSizeForLiterals() throws Exception { MailboxSession mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readStringUntil(clientConnection, s -> s.contains("a0 OK")); @@ -1677,18 +1677,18 @@ void shouldConsiderCumulativeSizeForLiterals() throws Exception { clientConnection.write(ByteBuffer.wrap(("a2 SEARCH CHARSET UTF-8 TO {" + literal.length() + "+}\r\n").getBytes(StandardCharsets.UTF_8))); assertThatThrownBy(() -> { - for (int i = 0; i < 7000; i++) { - clientConnection.write(ByteBuffer.wrap((literal + " TO {" + literal.length() + "+}\r\n").getBytes(StandardCharsets.UTF_8))); - } - clientConnection.write(ByteBuffer.wrap((literal + " ALL\r\n").getBytes(StandardCharsets.UTF_8))); - }).isInstanceOf(IOException.class); + for (int i = 0; i < 7000; i++) { + clientConnection.write(ByteBuffer.wrap((literal + " TO {" + literal.length() + "+}\r\n").getBytes(StandardCharsets.UTF_8))); + } + clientConnection.write(ByteBuffer.wrap((literal + " ALL\r\n").getBytes(StandardCharsets.UTF_8))); + }).isInstanceOf(IOException.class); } @Test void shouldRejectTooManyLiterals() throws Exception { MailboxSession mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readStringUntil(clientConnection, s -> s.contains("a0 OK")); @@ -1714,7 +1714,7 @@ void shouldRejectTooManyLiterals() throws Exception { void shouldRejectLongLineAfterLiteral() throws Exception { MailboxSession mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readStringUntil(clientConnection, s -> s.contains("a0 OK")); @@ -1733,7 +1733,7 @@ void shouldRejectLongLineAfterLiteral() throws Exception { void passing2literalOnDifferentNetworkPackage() throws Exception { MailboxSession mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readStringUntil(clientConnection, s -> s.contains("a0 OK")); @@ -1752,7 +1752,7 @@ void passing2literalOnDifferentNetworkPackage() throws Exception { void passing2literalOnSameNetworkPackage() throws Exception { MailboxSession mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readStringUntil(clientConnection, s -> s.contains("a0 OK")); @@ -1761,7 +1761,7 @@ void passing2literalOnSameNetworkPackage() throws Exception { String literal = "a".repeat(16); String s1 = "a2 SEARCH CHARSET UTF-8 TO {" + literal.length() + "+}\r\n" + - literal + " TO {" + literal.length() + "+}\r\n" + literal + " ALL\r\n"; + literal + " TO {" + literal.length() + "+}\r\n" + literal + " ALL\r\n"; clientConnection.write(ByteBuffer.wrap(s1.getBytes())); readStringUntil(clientConnection, s -> s.contains(("a2 OK "))); @@ -1771,7 +1771,7 @@ void passing2literalOnSameNetworkPackage() throws Exception { void passing2literalOnSameNetworkPackageWhenMoreThan16Chars() throws Exception { MailboxSession mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readStringUntil(clientConnection, s -> s.contains("a0 OK")); @@ -1780,7 +1780,7 @@ void passing2literalOnSameNetworkPackageWhenMoreThan16Chars() throws Exception { String literal = "a".repeat(17); String s1 = "a2 SEARCH CHARSET UTF-8 TO {" + literal.length() + "+}\r\n" + - literal + " TO {" + literal.length() + "+}\r\n" + literal + " ALL\r\n"; + literal + " TO {" + literal.length() + "+}\r\n" + literal + " ALL\r\n"; clientConnection.write(ByteBuffer.wrap(s1.getBytes())); readStringUntil(clientConnection, s -> s.contains(("a2 OK "))); @@ -1791,7 +1791,7 @@ void passing2literalOnSameNetworkPackageWhenMoreThan16Chars() throws Exception { void shouldAcceptSeveralFileLiteral() throws Exception { MailboxSession mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readStringUntil(clientConnection, s -> s.contains("a0 OK")); @@ -1810,7 +1810,7 @@ void shouldAcceptSeveralFileLiteral() throws Exception { void shouldRejectLongLineAfterLiteralWhenLogin() throws Exception { MailboxSession mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); clientConnection.write(ByteBuffer.wrap(("a0 LOGIN {" + USER.asString().length() + "+}\r\n").getBytes(StandardCharsets.UTF_8))); clientConnection.write(ByteBuffer.wrap((USER.asString() + " " + "0123456789".repeat(1024 * 1024)).getBytes())); @@ -1821,7 +1821,7 @@ void shouldRejectLongLineAfterLiteralWhenLogin() throws Exception { void shouldRejectLongLiteralsWhenUnauthenticated() throws Exception { MailboxSession mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); clientConnection.write(ByteBuffer.wrap(("a0 LOGIN " + USER.asString() + " {" + (10 * 1024) + "+}\r\n").getBytes(StandardCharsets.UTF_8))); clientConnection.write(ByteBuffer.wrap(("0123456789".repeat(1024) + " \r\n").getBytes())); @@ -1865,21 +1865,21 @@ void searchingShouldSupportMultipleUTF8Criteria() throws Exception { void searchingASingleUTF8CriterionShouldComplete() throws Exception { MailboxSession mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); memoryIntegrationResources.getMailboxManager() - .getMailbox(MailboxPath.inbox(USER), mailboxSession) - .appendMessage(MessageManager.AppendCommand.builder().build("MIME-Version: 1.0\r\n" + - "Content-Type: text/html; charset=UTF-8\r\n" + - "Content-Transfer-Encoding: quoted-printable\r\n" + - "From: =?ISO-8859-1?Q?Beno=EEt_TELLIER?= \r\n" + - "Sender: =?ISO-8859-1?Q?Beno=EEt_TELLIER?= \r\n" + - "Reply-To: b@linagora.com\r\n" + - "To: =?ISO-8859-1?Q?Beno=EEt_TELLIER?= \r\n" + - "Subject: Test utf-8 charset\r\n" + - "Message-ID: \r\n" + - "Date: Sun, 28 Mar 2021 03:58:06 +0000\r\n" + - "\r\n" + - "

=E5=A4=A9=E5=A4=A9=E5=90=91=E4=B8=8A

\r\n"), mailboxSession); + .getMailbox(MailboxPath.inbox(USER), mailboxSession) + .appendMessage(MessageManager.AppendCommand.builder().build("MIME-Version: 1.0\r\n" + + "Content-Type: text/html; charset=UTF-8\r\n" + + "Content-Transfer-Encoding: quoted-printable\r\n" + + "From: =?ISO-8859-1?Q?Beno=EEt_TELLIER?= \r\n" + + "Sender: =?ISO-8859-1?Q?Beno=EEt_TELLIER?= \r\n" + + "Reply-To: b@linagora.com\r\n" + + "To: =?ISO-8859-1?Q?Beno=EEt_TELLIER?= \r\n" + + "Subject: Test utf-8 charset\r\n" + + "Message-ID: \r\n" + + "Date: Sun, 28 Mar 2021 03:58:06 +0000\r\n" + + "\r\n" + + "

=E5=A4=A9=E5=A4=A9=E5=90=91=E4=B8=8A

\r\n"), mailboxSession); String host = "127.0.0.1"; Properties props = new Properties(); @@ -1912,7 +1912,7 @@ void beforeEach() throws Exception { int port = imapServer.getListenAddresses().get(0).getPort(); mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); inbox = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession); SSLContext ctx = SSLContext.getInstance("TLS"); @@ -1940,8 +1940,8 @@ void idleShouldSendInitialContinuation() throws Exception { clientConnection.getOutputStream().write("a3 IDLE\r\n".getBytes(StandardCharsets.UTF_8)); Awaitility.await().atMost(Duration.ofSeconds(2)).untilAsserted(() -> - assertThat(readStringUntil(clientConnection, s -> s.contains("+ Idling"))) - .isNotNull()); + assertThat(readStringUntil(clientConnection, s -> s.contains("+ Idling"))) + .isNotNull()); } @Test @@ -1958,8 +1958,8 @@ void idleShouldBeInterruptible() throws Exception { clientConnection.getOutputStream().write("DONE\r\n".getBytes(StandardCharsets.UTF_8)); Awaitility.await().atMost(Duration.ofSeconds(2)).untilAsserted(() -> - assertThat(readStringUntil(clientConnection, s -> s.contains("a3 OK IDLE completed."))) - .isNotNull()); + assertThat(readStringUntil(clientConnection, s -> s.contains("a3 OK IDLE completed."))) + .isNotNull()); } @Test @@ -1973,8 +1973,8 @@ void idleShouldBeInterruptibleWhenBatched() throws Exception { clientConnection.getOutputStream().write("a3 IDLE\r\nDONE\r\n".getBytes(StandardCharsets.UTF_8)); Awaitility.await().atMost(Duration.ofSeconds(2)).untilAsserted(() -> - assertThat(readStringUntil(clientConnection, s -> s.contains("a3 OK IDLE completed."))) - .isNotNull()); + assertThat(readStringUntil(clientConnection, s -> s.contains("a3 OK IDLE completed."))) + .isNotNull()); } @Test @@ -1989,9 +1989,9 @@ void idleResponsesShouldBeOrdered() throws Exception { // Assert continuation is sent before IDLE completion result Awaitility.await().atMost(Duration.ofSeconds(2)).untilAsserted(() -> - assertThat(readStringUntil(clientConnection, s -> s.contains("a3 OK IDLE completed."))) - .filteredOn(s -> s.contains("+ Idling")) - .hasSize(1)); + assertThat(readStringUntil(clientConnection, s -> s.contains("a3 OK IDLE completed."))) + .filteredOn(s -> s.contains("+ Idling")) + .hasSize(1)); } @Test @@ -2005,8 +2005,8 @@ void idleShouldReturnUnderstandableErrorMessageWhenBadDone() throws Exception { clientConnection.getOutputStream().write("a3 IDLE\r\nBAD\r\n".getBytes(StandardCharsets.UTF_8)); Awaitility.await().atMost(Duration.ofSeconds(2)).untilAsserted(() -> - assertThat(readStringUntil(clientConnection, s -> s.contains("a3 BAD IDLE failed. Continuation for IMAP IDLE was not understood. Expected 'DONE', got 'BAD'."))) - .isNotNull()); + assertThat(readStringUntil(clientConnection, s -> s.contains("a3 BAD IDLE failed. Continuation for IMAP IDLE was not understood. Expected 'DONE', got 'BAD'."))) + .isNotNull()); } // Repeated run to detect more reliably data races @@ -2024,8 +2024,8 @@ void idleShouldReturnUpdates() throws Exception { inbox.appendMessage(MessageManager.AppendCommand.builder().build("h: value\r\n\r\nbody".getBytes()), mailboxSession); Awaitility.await().atMost(Duration.ofSeconds(2)).untilAsserted(() -> - assertThat(readStringUntil(clientConnection, s -> s.contains("* 1 EXISTS"))) - .isNotNull()); + assertThat(readStringUntil(clientConnection, s -> s.contains("* 1 EXISTS"))) + .isNotNull()); } private byte[] readBytes(Socket channel) throws IOException { @@ -2060,7 +2060,7 @@ void beforeEach() throws Exception { int port = imapServer.getListenAddresses().get(0).getPort(); mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); inbox = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession); SSLContext ctx = SSLContext.getInstance("TLS"); @@ -2108,12 +2108,12 @@ void beforeEach() throws Exception { int port = imapServer.getListenAddresses().get(0).getPort(); MailboxSession mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); connection = TcpClient.create() - .secure(ssl -> ssl.sslContext(SslContextBuilder.forClient().trustManager(new BlindTrustManager()))) - .remoteAddress(() -> new InetSocketAddress(LOCALHOST_IP, port)) - .connectNow(); + .secure(ssl -> ssl.sslContext(SslContextBuilder.forClient().trustManager(new BlindTrustManager()))) + .remoteAddress(() -> new InetSocketAddress(LOCALHOST_IP, port)) + .connectNow(); responses = new ConcurrentLinkedDeque<>(); readBytes(connection); } @@ -2155,17 +2155,17 @@ void idleShouldBeInterruptible() { private void send(String format) { connection.outbound() - .send(Mono.just(Unpooled.wrappedBuffer(format - .getBytes(StandardCharsets.UTF_8)))) - .then() - .subscribe(); + .send(Mono.just(Unpooled.wrappedBuffer(format + .getBytes(StandardCharsets.UTF_8)))) + .then() + .subscribe(); } private void readBytes(Connection connection) { connection.inbound().receive().asString() - .doOnNext(responses::addLast) - .subscribeOn(Schedulers.newSingle("test")) - .subscribe(); + .doOnNext(responses::addLast) + .subscribeOn(Schedulers.newSingle("test")) + .subscribe(); } } @@ -2183,7 +2183,7 @@ void beforeEach() throws Exception { port = imapServer.getListenAddresses().get(0).getPort(); mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); inbox = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession); clientConnection = SocketChannel.open(); @@ -2209,8 +2209,8 @@ void idleShouldSendInitialContinuation() throws Exception { Awaitility.await().atMost(Duration.ofSeconds(2)).untilAsserted(() -> - assertThat(readStringUntil(clientConnection, s -> s.contains("+ Idling"))) - .isNotNull()); + assertThat(readStringUntil(clientConnection, s -> s.contains("+ Idling"))) + .isNotNull()); } @Test @@ -2227,8 +2227,8 @@ void idleShouldBeInterruptible() throws Exception { clientConnection.write(ByteBuffer.wrap(("DONE\r\n").getBytes(StandardCharsets.UTF_8))); Awaitility.await().atMost(Duration.ofSeconds(2)).untilAsserted(() -> - assertThat(readStringUntil(clientConnection, s -> s.contains("a3 OK IDLE completed."))) - .isNotNull()); + assertThat(readStringUntil(clientConnection, s -> s.contains("a3 OK IDLE completed."))) + .isNotNull()); } @Test @@ -2242,8 +2242,8 @@ void idleShouldBeInterruptibleWhenBatched() throws Exception { clientConnection.write(ByteBuffer.wrap(("a3 IDLE\r\nDONE\r\n").getBytes(StandardCharsets.UTF_8))); Awaitility.await().atMost(Duration.ofSeconds(2)).untilAsserted(() -> - assertThat(readStringUntil(clientConnection, s -> s.contains("a3 OK IDLE completed."))) - .isNotNull()); + assertThat(readStringUntil(clientConnection, s -> s.contains("a3 OK IDLE completed."))) + .isNotNull()); } @Test @@ -2258,9 +2258,9 @@ void idleResponsesShouldBeOrdered() throws Exception { // Assert continuation is sent before IDLE completion result Awaitility.await().atMost(Duration.ofSeconds(2)).untilAsserted(() -> - assertThat(readStringUntil(clientConnection, s -> s.contains("a3 OK IDLE completed."))) - .filteredOn(s -> s.contains("+ Idling")) - .hasSize(1)); + assertThat(readStringUntil(clientConnection, s -> s.contains("a3 OK IDLE completed."))) + .filteredOn(s -> s.contains("+ Idling")) + .hasSize(1)); } @Test @@ -2274,8 +2274,8 @@ void idleShouldReturnUnderstandableErrorMessageWhenBadDone() throws Exception { clientConnection.write(ByteBuffer.wrap(("a3 IDLE\r\nBAD\r\n").getBytes(StandardCharsets.UTF_8))); Awaitility.await().atMost(Duration.ofSeconds(2)).untilAsserted(() -> - assertThat(readStringUntil(clientConnection, s -> s.contains("a3 BAD IDLE failed. Continuation for IMAP IDLE was not understood. Expected 'DONE', got 'BAD'."))) - .isNotNull()); + assertThat(readStringUntil(clientConnection, s -> s.contains("a3 BAD IDLE failed. Continuation for IMAP IDLE was not understood. Expected 'DONE', got 'BAD'."))) + .isNotNull()); } // Repeated run to detect more reliably data races @@ -2293,8 +2293,8 @@ void idleShouldReturnUpdates() throws Exception { inbox.appendMessage(MessageManager.AppendCommand.builder().build("h: value\r\n\r\nbody".getBytes()), mailboxSession); Awaitility.await().atMost(Duration.ofSeconds(2)).untilAsserted(() -> - assertThat(readStringUntil(clientConnection, s -> s.contains("* 1 EXISTS"))) - .isNotNull()); + assertThat(readStringUntil(clientConnection, s -> s.contains("* 1 EXISTS"))) + .isNotNull()); } } @@ -2311,7 +2311,7 @@ void beforeEach() throws Exception { int port = imapServer.getListenAddresses().get(0).getPort(); mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); inbox = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession); setUpTestingData(); @@ -2328,8 +2328,8 @@ void tearDown() throws Exception { private void setUpTestingData() { IntStream.range(0, 37) - .forEach(Throwing.intConsumer(i -> inbox.appendMessage(MessageManager.AppendCommand.builder() - .build("MIME-Version: 1.0\r\n\r\nCONTENT\r\n"), mailboxSession))); + .forEach(Throwing.intConsumer(i -> inbox.appendMessage(MessageManager.AppendCommand.builder() + .build("MIME-Version: 1.0\r\n\r\nCONTENT\r\n"), mailboxSession))); } @Test @@ -2358,21 +2358,21 @@ void fetchShouldSupportChangedSince() throws Exception { List replies = readStringUntil(clientConnection, s -> s.contains("a3 OK FETCH completed.")); SoftAssertions.assertSoftly(softly -> { softly.assertThat(replies) - .filteredOn(s -> s.contains("* 2 FETCH (MODSEQ (41) FLAGS (\\Answered \\Recent) UID 2)")) - .hasSize(1); + .filteredOn(s -> s.contains("* 2 FETCH (MODSEQ (41) FLAGS (\\Answered \\Recent) UID 2)")) + .hasSize(1); softly.assertThat(replies) - .filteredOn(s -> s.contains("* 25 FETCH (MODSEQ (42) FLAGS (\\Answered \\Recent) UID 25)")) - .hasSize(1); + .filteredOn(s -> s.contains("* 25 FETCH (MODSEQ (42) FLAGS (\\Answered \\Recent) UID 25)")) + .hasSize(1); softly.assertThat(replies) - .filteredOn(s -> s.contains("* 35 FETCH (MODSEQ (43) FLAGS (\\Answered \\Recent) UID 35)")) - .hasSize(1); + .filteredOn(s -> s.contains("* 35 FETCH (MODSEQ (43) FLAGS (\\Answered \\Recent) UID 35)")) + .hasSize(1); softly.assertThat(replies) - .filteredOn(s -> s.contains("* 14 FETCH (MODSEQ (39) FLAGS (\\Answered \\Recent) UID 14)")) - .isEmpty(); + .filteredOn(s -> s.contains("* 14 FETCH (MODSEQ (39) FLAGS (\\Answered \\Recent) UID 14)")) + .isEmpty(); softly.assertThat(replies) - .filteredOn(s -> s.contains("* 31 FETCH (MODSEQ (40) FLAGS (\\Answered \\Recent) UID 31)")) - .isEmpty(); + .filteredOn(s -> s.contains("* 31 FETCH (MODSEQ (40) FLAGS (\\Answered \\Recent) UID 31)")) + .isEmpty(); }); } @@ -2400,8 +2400,8 @@ void searchShouldSupportModSeq() throws Exception { clientConnection.write(ByteBuffer.wrap(String.format("A150 SEARCH MODSEQ %d\r\n", highestModSeq.asLong()).getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("A150 OK SEARCH completed."))) - .filteredOn(s -> s.contains("* SEARCH 2 25 31 35 (MODSEQ 43)")) - .hasSize(1); + .filteredOn(s -> s.contains("* SEARCH 2 25 31 35 (MODSEQ 43)")) + .hasSize(1); } @Test @@ -2429,8 +2429,8 @@ void searchShouldSupportModSeqWithFlagRestrictions() throws Exception { // Restrictions are not applied however assertThat(readStringUntil(clientConnection, s -> s.contains("a OK SEARCH completed."))) - .filteredOn(s -> s.contains("* SEARCH 2 25 31 35 (MODSEQ 43)")) - .hasSize(1); + .filteredOn(s -> s.contains("* SEARCH 2 25 31 35 (MODSEQ 43)")) + .hasSize(1); } @Test @@ -2446,8 +2446,8 @@ void statusShouldAcceptHighestModSeqItem() throws Exception { clientConnection.write(ByteBuffer.wrap(("A042 STATUS INBOX (HIGHESTMODSEQ)\r\n").getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("A042 OK STATUS completed."))) - .filteredOn(s -> s.contains(String.format("* STATUS \"INBOX\" (HIGHESTMODSEQ %d)", highestModSeq.asLong()))) - .hasSize(1); + .filteredOn(s -> s.contains(String.format("* STATUS \"INBOX\" (HIGHESTMODSEQ %d)", highestModSeq.asLong()))) + .hasSize(1); } @Test @@ -2458,7 +2458,7 @@ void selectShouldAcceptCondstore() throws Exception { clientConnection.write(ByteBuffer.wrap(("A142 SELECT INBOX (CONDSTORE)\r\n").getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("A142 OK [READ-WRITE] SELECT completed."))) - .isNotNull(); + .isNotNull(); } @Test @@ -2474,8 +2474,8 @@ void selectShouldEnableCondstore() throws Exception { clientConnection.write(ByteBuffer.wrap(("a2 NOOP\r\n").getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("a2 OK NOOP completed."))) - .filteredOn(s -> s.contains("* 35 FETCH (MODSEQ (39) FLAGS (\\Answered \\Recent))")) - .hasSize(1); + .filteredOn(s -> s.contains("* 35 FETCH (MODSEQ (39) FLAGS (\\Answered \\Recent))")) + .hasSize(1); } @Test @@ -2495,8 +2495,8 @@ void storeShouldSucceedWhenUnchangedSinceIsNotExceeded() throws Exception { clientConnection.write(ByteBuffer.wrap((String.format("a103 UID STORE 35 (UNCHANGEDSINCE %d) +FLAGS.SILENT (\\Seen)\r\n", highestModSeq.asLong()).getBytes(StandardCharsets.UTF_8)))); assertThat(readStringUntil(clientConnection, s -> s.contains("a103 OK STORE completed."))) - .filteredOn(s -> s.contains("* 35 FETCH (MODSEQ (40) UID 35)")) - .hasSize(1); + .filteredOn(s -> s.contains("* 35 FETCH (MODSEQ (40) UID 35)")) + .hasSize(1); } @Test @@ -2516,8 +2516,8 @@ void storeShouldSucceedWhenUnchangedSinceIsNotExceededAndNotSilent() throws Exce clientConnection.write(ByteBuffer.wrap((String.format("a103 UID STORE 35 (UNCHANGEDSINCE %d) +FLAGS (\\Seen)\r\n", highestModSeq.asLong()).getBytes(StandardCharsets.UTF_8)))); assertThat(readStringUntil(clientConnection, s -> s.contains("a103 OK STORE completed."))) - .filteredOn(s -> s.contains("* 35 FETCH (MODSEQ (40) FLAGS (\\Answered \\Recent \\Seen) UID 35)")) - .hasSize(1); + .filteredOn(s -> s.contains("* 35 FETCH (MODSEQ (40) FLAGS (\\Answered \\Recent \\Seen) UID 35)")) + .hasSize(1); } @Test @@ -2537,7 +2537,7 @@ void storeShouldFailWhenUnchangedSinceIsExceeded() throws Exception { clientConnection.write(ByteBuffer.wrap((String.format("a103 UID STORE 35 (UNCHANGEDSINCE %d) +FLAGS.SILENT (\\Seen)\r\n", highestModSeq.asLong() - 1).getBytes(StandardCharsets.UTF_8)))); assertThat(readStringUntil(clientConnection, s -> s.contains("a103 OK [MODIFIED 0 35] STORE failed."))) - .isNotNull(); + .isNotNull(); } @Test @@ -2557,7 +2557,7 @@ void storeShouldFailWhenUnchangedSinceIsZero() throws Exception { clientConnection.write(ByteBuffer.wrap((String.format("a103 UID STORE 35 (UNCHANGEDSINCE 0) +FLAGS.SILENT (dcustom)\r\n", highestModSeq.asLong() - 1).getBytes(StandardCharsets.UTF_8)))); assertThat(readStringUntil(clientConnection, s -> s.contains("a103 OK [MODIFIED 0 35] STORE failed."))) - .isNotNull(); + .isNotNull(); } @Test @@ -2577,7 +2577,7 @@ void storeShouldFailWhenUnchangedSinceIsZeroAndMsn() throws Exception { clientConnection.write(ByteBuffer.wrap((String.format("a103 STORE 35 (UNCHANGEDSINCE 0) +FLAGS.SILENT (dcustom)\r\n", highestModSeq.asLong() - 1).getBytes(StandardCharsets.UTF_8)))); assertThat(readStringUntil(clientConnection, s -> s.contains("a103 OK [MODIFIED 0 35] STORE failed."))) - .isNotNull(); + .isNotNull(); } @Test @@ -2597,7 +2597,7 @@ void storeShouldFailWhenUnchangedSinceIsZeroAndSystemFlagsUpdate() throws Except clientConnection.write(ByteBuffer.wrap((String.format("a103 UID STORE 35 (UNCHANGEDSINCE 0) +FLAGS.SILENT (\\Answered)\r\n", highestModSeq.asLong() - 1).getBytes(StandardCharsets.UTF_8)))); assertThat(readStringUntil(clientConnection, s -> s.contains("a103 OK [MODIFIED 0 35] STORE failed."))) - .isNotNull(); + .isNotNull(); } @Test @@ -2619,8 +2619,8 @@ void storeShouldFailWhenSomeMessagesDoNotMatch() throws Exception { clientConnection.write(ByteBuffer.wrap((String.format("a103 UID STORE 5,7,9 (UNCHANGEDSINCE %d) +FLAGS.SILENT (\\Seen)\r\n", highestModSeq.asLong() - 1).getBytes(StandardCharsets.UTF_8)))); assertThat(readStringUntil(clientConnection, s -> s.contains("a103 OK [MODIFIED 0 5,7,9] STORE failed."))) - .filteredOn(s -> s.contains("FETCH")) - .isEmpty(); + .filteredOn(s -> s.contains("FETCH")) + .isEmpty(); } } @@ -2637,7 +2637,7 @@ void beforeEach() throws Exception { int port = imapServer.getListenAddresses().get(0).getPort(); mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); inbox = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession); setUpTestingData(); @@ -2654,15 +2654,15 @@ void tearDown() throws Exception { private void setUpTestingData() { IntStream.range(0, 37) - .forEach(Throwing.intConsumer(i -> inbox.appendMessage(MessageManager.AppendCommand.builder() - .build("MIME-Version: 1.0\r\n\r\nCONTENT\r\n"), mailboxSession))); + .forEach(Throwing.intConsumer(i -> inbox.appendMessage(MessageManager.AppendCommand.builder() + .build("MIME-Version: 1.0\r\n\r\nCONTENT\r\n"), mailboxSession))); } @Test void compressShouldFailWhenNotEnabled() throws Exception { String reply = testIMAPClient.connect("127.0.0.1", imapServer.getListenAddresses().get(0).getPort()) - .login(USER.asString(), USER_PASS) - .sendCommand("COMPRESS DEFLATE"); + .login(USER.asString(), USER_PASS) + .sendCommand("COMPRESS DEFLATE"); assertThat(reply).contains("AAAB BAD COMPRESS failed. Unknown command."); } @@ -2670,8 +2670,8 @@ void compressShouldFailWhenNotEnabled() throws Exception { @Test void ensureSequentialExecutionOfImapRequests() throws Exception { IntStream.range(0, 100) - .forEach(Throwing.intConsumer(i -> inbox.appendMessage(MessageManager.AppendCommand.builder() - .build("MIME-Version: 1.0\r\n\r\nCONTENT\r\n"), mailboxSession))); + .forEach(Throwing.intConsumer(i -> inbox.appendMessage(MessageManager.AppendCommand.builder() + .build("MIME-Version: 1.0\r\n\r\nCONTENT\r\n"), mailboxSession))); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readBytes(clientConnection); @@ -2688,14 +2688,14 @@ void ensureSequentialExecutionOfImapRequests() throws Exception { void fetchShouldBackPressureWhenNoRead() throws Exception { String msgIn = "MIME-Version: 1.0\r\n\r\nCONTENT\r\n\r\n" + "0123456789\r\n0123456789\r\n0123456789\r\n".repeat(1024); IntStream.range(0, 500) - .forEach(Throwing.intConsumer(i -> inbox.appendMessage(MessageManager.AppendCommand.builder() - .build(msgIn), mailboxSession))); + .forEach(Throwing.intConsumer(i -> inbox.appendMessage(MessageManager.AppendCommand.builder() + .build(msgIn), mailboxSession))); AtomicInteger loaded = new AtomicInteger(0); MessageManager inboxSpy = spy(inbox); doReturn(Mono.just(inboxSpy)).when(mailboxManager).getMailboxReactive(eq(MailboxPath.inbox(USER)), any()); doReturn(Mono.just(inboxSpy)).when(mailboxManager).getMailboxReactive(eq(inbox.getMailboxEntity().getMailboxId()), any()); doAnswer((Answer) invocationOnMock -> Flux.from(inbox.getMessagesReactive(invocationOnMock.getArgument(0), invocationOnMock.getArgument(1), invocationOnMock.getArgument(2))) - .doOnNext(any -> loaded.incrementAndGet())).when(inboxSpy).getMessagesReactive(any(), any(), any()); + .doOnNext(any -> loaded.incrementAndGet())).when(inboxSpy).getMessagesReactive(any(), any(), any()); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readBytes(clientConnection); @@ -2745,7 +2745,7 @@ void beforeEach() throws Exception { int port = imapServer.getListenAddresses().get(0).getPort(); mailboxSession = memoryIntegrationResources.getMailboxManager().createSystemSession(USER); memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.inbox(USER), mailboxSession); + .createMailbox(MailboxPath.inbox(USER), mailboxSession); inbox = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession); clientConnection = SocketChannel.open(); @@ -2766,11 +2766,11 @@ void tearDown() throws Exception { @Test void selectShouldNotAnswerEmptyVanishedResponses() throws Exception { memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .delete(ImmutableList.of(MessageUid.MIN_VALUE), mailboxSession); + .delete(ImmutableList.of(MessageUid.MIN_VALUE), mailboxSession); UidValidity uidValidity = memoryIntegrationResources.getMailboxManager() - .getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMailboxEntity().getUidValidity(); + .getMailbox(MailboxPath.inbox(USER), mailboxSession) + .getMailboxEntity().getUidValidity(); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readBytes(clientConnection); @@ -2779,8 +2779,8 @@ void selectShouldNotAnswerEmptyVanishedResponses() throws Exception { clientConnection.write(ByteBuffer.wrap(String.format("I00104 SELECT INBOX (QRESYNC (%d 88 2:37 (1,10,28 2,11,29)))\r\n", uidValidity.asLong()).getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("I00104 OK [READ-WRITE] SELECT completed."))) - .filteredOn(s -> s.contains("VANISHED")) - .isEmpty(); + .filteredOn(s -> s.contains("VANISHED")) + .isEmpty(); } @Test @@ -2788,15 +2788,15 @@ void selectShouldReturnDeletedMessagesWhenNoSubsequentModification() throws Exce inbox.delete(ImmutableList.of(MessageUid.MIN_VALUE), mailboxSession); ModSeq highestModSeq = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMetaData(IGNORE, mailboxSession, NO_COUNT) - .getHighestModSeq(); + .getMetaData(IGNORE, mailboxSession, NO_COUNT) + .getHighestModSeq(); UidValidity uidValidity = memoryIntegrationResources.getMailboxManager() - .getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMailboxEntity().getUidValidity(); + .getMailbox(MailboxPath.inbox(USER), mailboxSession) + .getMailboxEntity().getUidValidity(); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .delete(ImmutableList.of(MessageUid.of(10)), mailboxSession); + .delete(ImmutableList.of(MessageUid.of(10)), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readBytes(clientConnection); @@ -2805,8 +2805,8 @@ void selectShouldReturnDeletedMessagesWhenNoSubsequentModification() throws Exce clientConnection.write(ByteBuffer.wrap(String.format("I00104 SELECT INBOX (QRESYNC (%d %d 2:37 (1,10,28 2,11,29)))\r\n", uidValidity.asLong(), highestModSeq.asLong()).getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("I00104 OK [READ-WRITE] SELECT completed."))) - .filteredOn(s -> s.contains("* VANISHED (EARLIER) 10")) - .hasSize(1); + .filteredOn(s -> s.contains("* VANISHED (EARLIER) 10")) + .hasSize(1); } @Test @@ -2814,15 +2814,15 @@ void selectShouldReturnDeletedMessagesWhenSequenceMatchDataAndNoKnownUid() throw inbox.delete(ImmutableList.of(MessageUid.MIN_VALUE), mailboxSession); ModSeq highestModSeq = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMetaData(IGNORE, mailboxSession, NO_COUNT) - .getHighestModSeq(); + .getMetaData(IGNORE, mailboxSession, NO_COUNT) + .getHighestModSeq(); UidValidity uidValidity = memoryIntegrationResources.getMailboxManager() - .getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMailboxEntity().getUidValidity(); + .getMailbox(MailboxPath.inbox(USER), mailboxSession) + .getMailboxEntity().getUidValidity(); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .delete(ImmutableList.of(MessageUid.of(10)), mailboxSession); + .delete(ImmutableList.of(MessageUid.of(10)), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readBytes(clientConnection); @@ -2831,8 +2831,8 @@ void selectShouldReturnDeletedMessagesWhenSequenceMatchDataAndNoKnownUid() throw clientConnection.write(ByteBuffer.wrap(String.format("I00104 SELECT INBOX (QRESYNC (%d %d (1,10,28 2,11,29)))\r\n", uidValidity.asLong(), highestModSeq.asLong()).getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("I00104 OK [READ-WRITE] SELECT completed."))) - .filteredOn(s -> s.contains("* VANISHED (EARLIER) 10")) - .hasSize(1); + .filteredOn(s -> s.contains("* VANISHED (EARLIER) 10")) + .hasSize(1); } @Test @@ -2840,16 +2840,16 @@ void selectShouldReturnDeletedMessagesWhenKnownUidSet() throws Exception { inbox.delete(ImmutableList.of(MessageUid.MIN_VALUE), mailboxSession); ModSeq highestModSeq = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMetaData(IGNORE, mailboxSession, NO_COUNT) - .getHighestModSeq(); + .getMetaData(IGNORE, mailboxSession, NO_COUNT) + .getHighestModSeq(); UidValidity uidValidity = memoryIntegrationResources.getMailboxManager() - .getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMailboxEntity().getUidValidity(); + .getMailbox(MailboxPath.inbox(USER), mailboxSession) + .getMailboxEntity().getUidValidity(); inbox.delete(ImmutableList.of(MessageUid.of(10), MessageUid.of(11), MessageUid.of(12), - MessageUid.of(25), MessageUid.of(26), - MessageUid.of(32)), mailboxSession); + MessageUid.of(25), MessageUid.of(26), + MessageUid.of(32)), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readBytes(clientConnection); @@ -2858,8 +2858,8 @@ void selectShouldReturnDeletedMessagesWhenKnownUidSet() throws Exception { clientConnection.write(ByteBuffer.wrap(String.format("I00104 SELECT INBOX (QRESYNC (%d %d 5:11,28:36 (1,10,28 2,11,29)))\r\n", uidValidity.asLong(), highestModSeq.asLong()).getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("I00104 OK [READ-WRITE] SELECT completed."))) - .filteredOn(s -> s.contains("* VANISHED (EARLIER) 10:11,32")) - .hasSize(1); + .filteredOn(s -> s.contains("* VANISHED (EARLIER) 10:11,32")) + .hasSize(1); } @Test @@ -2867,16 +2867,16 @@ void knownUidSetShouldBeUsedToRestrictVanishedResponses() throws Exception { inbox.delete(ImmutableList.of(MessageUid.MIN_VALUE), mailboxSession); ModSeq highestModSeq = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMetaData(IGNORE, mailboxSession, NO_COUNT) - .getHighestModSeq(); + .getMetaData(IGNORE, mailboxSession, NO_COUNT) + .getHighestModSeq(); UidValidity uidValidity = memoryIntegrationResources.getMailboxManager() - .getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMailboxEntity().getUidValidity(); + .getMailbox(MailboxPath.inbox(USER), mailboxSession) + .getMailboxEntity().getUidValidity(); inbox.delete(ImmutableList.of(MessageUid.of(10), MessageUid.of(11), MessageUid.of(12), - MessageUid.of(25), MessageUid.of(26), - MessageUid.of(32)), mailboxSession); + MessageUid.of(25), MessageUid.of(26), + MessageUid.of(32)), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readBytes(clientConnection); @@ -2889,8 +2889,8 @@ void knownUidSetShouldBeUsedToRestrictVanishedResponses() throws Exception { clientConnection.write(ByteBuffer.wrap(String.format("I00104 SELECT INBOX (QRESYNC (%d %d 1:37 (1,13,28 2,17,30)))\r\n", uidValidity.asLong(), highestModSeq.asLong()).getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("I00104 OK [READ-WRITE] SELECT completed."))) - .filteredOn(s -> s.contains("* VANISHED (EARLIER) 25:26,32")) - .hasSize(1); + .filteredOn(s -> s.contains("* VANISHED (EARLIER) 25:26,32")) + .hasSize(1); } @Test @@ -2898,16 +2898,16 @@ void knownUidSetShouldTolerateDeletedMessages() throws Exception { inbox.delete(ImmutableList.of(MessageUid.MIN_VALUE), mailboxSession); ModSeq highestModSeq = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMetaData(IGNORE, mailboxSession, NO_COUNT) - .getHighestModSeq(); + .getMetaData(IGNORE, mailboxSession, NO_COUNT) + .getHighestModSeq(); UidValidity uidValidity = memoryIntegrationResources.getMailboxManager() - .getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMailboxEntity().getUidValidity(); + .getMailbox(MailboxPath.inbox(USER), mailboxSession) + .getMailboxEntity().getUidValidity(); inbox.delete(ImmutableList.of(MessageUid.of(10), MessageUid.of(11), MessageUid.of(12), - MessageUid.of(25), MessageUid.of(26), - MessageUid.of(32)), mailboxSession); + MessageUid.of(25), MessageUid.of(26), + MessageUid.of(32)), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readBytes(clientConnection); @@ -2920,8 +2920,8 @@ void knownUidSetShouldTolerateDeletedMessages() throws Exception { clientConnection.write(ByteBuffer.wrap(String.format("I00104 SELECT INBOX (QRESYNC (%d %d 1:37 (1,13,28 2,17,32)))\r\n", uidValidity.asLong(), highestModSeq.asLong()).getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("I00104 OK [READ-WRITE] SELECT completed."))) - .filteredOn(s -> s.contains("* VANISHED (EARLIER) 25:26,32")) - .hasSize(1); + .filteredOn(s -> s.contains("* VANISHED (EARLIER) 25:26,32")) + .hasSize(1); } @Test @@ -2929,15 +2929,15 @@ void selectShouldReturnDeletedMessagesWhenNoSequenceMatchData() throws Exception inbox.delete(ImmutableList.of(MessageUid.MIN_VALUE), mailboxSession); ModSeq highestModSeq = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMetaData(IGNORE, mailboxSession, NO_COUNT) - .getHighestModSeq(); + .getMetaData(IGNORE, mailboxSession, NO_COUNT) + .getHighestModSeq(); UidValidity uidValidity = memoryIntegrationResources.getMailboxManager() - .getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMailboxEntity().getUidValidity(); + .getMailbox(MailboxPath.inbox(USER), mailboxSession) + .getMailboxEntity().getUidValidity(); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .delete(ImmutableList.of(MessageUid.of(10)), mailboxSession); + .delete(ImmutableList.of(MessageUid.of(10)), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readBytes(clientConnection); @@ -2946,8 +2946,8 @@ void selectShouldReturnDeletedMessagesWhenNoSequenceMatchData() throws Exception clientConnection.write(ByteBuffer.wrap(String.format("I00104 SELECT INBOX (QRESYNC (%d %d 2:37))\r\n", uidValidity.asLong(), highestModSeq.asLong()).getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("I00104 OK [READ-WRITE] SELECT completed."))) - .filteredOn(s -> s.contains("* VANISHED (EARLIER) 10")) - .hasSize(1); + .filteredOn(s -> s.contains("* VANISHED (EARLIER) 10")) + .hasSize(1); } @Test @@ -2955,15 +2955,15 @@ void selectShouldReturnDeletedMessagesWhenNoSequenceMatchDataAndKnownUid() throw inbox.delete(ImmutableList.of(MessageUid.MIN_VALUE), mailboxSession); ModSeq highestModSeq = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMetaData(IGNORE, mailboxSession, NO_COUNT) - .getHighestModSeq(); + .getMetaData(IGNORE, mailboxSession, NO_COUNT) + .getHighestModSeq(); UidValidity uidValidity = memoryIntegrationResources.getMailboxManager() - .getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMailboxEntity().getUidValidity(); + .getMailbox(MailboxPath.inbox(USER), mailboxSession) + .getMailboxEntity().getUidValidity(); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .delete(ImmutableList.of(MessageUid.of(10)), mailboxSession); + .delete(ImmutableList.of(MessageUid.of(10)), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readBytes(clientConnection); @@ -2972,8 +2972,8 @@ void selectShouldReturnDeletedMessagesWhenNoSequenceMatchDataAndKnownUid() throw clientConnection.write(ByteBuffer.wrap(String.format("I00104 SELECT INBOX (QRESYNC (%d %d))\r\n", uidValidity.asLong(), highestModSeq.asLong()).getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("I00104 OK [READ-WRITE] SELECT completed."))) - .filteredOn(s -> s.contains("* VANISHED (EARLIER) 10")) - .hasSize(1); + .filteredOn(s -> s.contains("* VANISHED (EARLIER) 10")) + .hasSize(1); } @Test @@ -2981,16 +2981,16 @@ void selectShouldCombineIntoRangesWhenRespondingVanished() throws Exception { inbox.delete(ImmutableList.of(MessageUid.MIN_VALUE), mailboxSession); ModSeq highestModSeq = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMetaData(IGNORE, mailboxSession, NO_COUNT) - .getHighestModSeq(); + .getMetaData(IGNORE, mailboxSession, NO_COUNT) + .getHighestModSeq(); UidValidity uidValidity = memoryIntegrationResources.getMailboxManager() - .getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMailboxEntity().getUidValidity(); + .getMailbox(MailboxPath.inbox(USER), mailboxSession) + .getMailboxEntity().getUidValidity(); inbox.delete(ImmutableList.of(MessageUid.of(10), MessageUid.of(11), MessageUid.of(12), - MessageUid.of(25), MessageUid.of(26), - MessageUid.of(32)), mailboxSession); + MessageUid.of(25), MessageUid.of(26), + MessageUid.of(32)), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readBytes(clientConnection); @@ -2999,8 +2999,8 @@ void selectShouldCombineIntoRangesWhenRespondingVanished() throws Exception { clientConnection.write(ByteBuffer.wrap(String.format("I00104 SELECT INBOX (QRESYNC (%d %d 2:37 (1,10,28 2,11,29)))\r\n", uidValidity.asLong(), highestModSeq.asLong()).getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("I00104 OK [READ-WRITE] SELECT completed."))) - .filteredOn(s -> s.contains("* VANISHED (EARLIER) 10:12,25:26,32")) - .hasSize(1); + .filteredOn(s -> s.contains("* VANISHED (EARLIER) 10:12,25:26,32")) + .hasSize(1); } @Test @@ -3008,16 +3008,16 @@ void enableQRESYNCShouldReturnHighestModseq() throws Exception { inbox.delete(ImmutableList.of(MessageUid.MIN_VALUE), mailboxSession); ModSeq highestModSeq = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMetaData(IGNORE, mailboxSession, NO_COUNT) - .getHighestModSeq(); + .getMetaData(IGNORE, mailboxSession, NO_COUNT) + .getHighestModSeq(); UidValidity uidValidity = memoryIntegrationResources.getMailboxManager() - .getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMailboxEntity().getUidValidity(); + .getMailbox(MailboxPath.inbox(USER), mailboxSession) + .getMailboxEntity().getUidValidity(); inbox.delete(ImmutableList.of(MessageUid.of(10), MessageUid.of(11), MessageUid.of(12), - MessageUid.of(25), MessageUid.of(26), - MessageUid.of(32)), mailboxSession); + MessageUid.of(25), MessageUid.of(26), + MessageUid.of(32)), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readBytes(clientConnection); @@ -3029,18 +3029,18 @@ void enableQRESYNCShouldReturnHighestModseq() throws Exception { List replies = readStringUntil(clientConnection, s -> s.contains("a2 OK ENABLE completed.")); SoftAssertions.assertSoftly(softly -> { softly.assertThat(replies) - .filteredOn(s -> s.contains("* OK [HIGHESTMODSEQ 41] Highest")) - .hasSize(1); + .filteredOn(s -> s.contains("* OK [HIGHESTMODSEQ 41] Highest")) + .hasSize(1); softly.assertThat(replies) - .filteredOn(s -> s.contains("* ENABLED QRESYNC")) - .hasSize(1); + .filteredOn(s -> s.contains("* ENABLED QRESYNC")) + .hasSize(1); }); } private void setUpTestingData() { IntStream.range(0, 37) - .forEach(Throwing.intConsumer(i -> inbox.appendMessage(MessageManager.AppendCommand.builder() - .build("MIME-Version: 1.0\r\n\r\nCONTENT\r\n"), mailboxSession))); + .forEach(Throwing.intConsumer(i -> inbox.appendMessage(MessageManager.AppendCommand.builder() + .build("MIME-Version: 1.0\r\n\r\nCONTENT\r\n"), mailboxSession))); } @Test @@ -3053,16 +3053,16 @@ void fetchShouldAllowChangedSinceModifier() throws Exception { readStringUntil(clientConnection, s -> s.contains("a2 OK [READ-WRITE] SELECT completed.")); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(10)), mailboxSession); + .setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(10)), mailboxSession); ModSeq highestModSeq = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMetaData(IGNORE, mailboxSession, NO_COUNT) - .getHighestModSeq(); + .getMetaData(IGNORE, mailboxSession, NO_COUNT) + .getHighestModSeq(); clientConnection.write(ByteBuffer.wrap(String.format("I00104 UID FETCH 1:37 (FLAGS) (CHANGEDSINCE %d)\r\n", highestModSeq.asLong()).getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("I00104 OK FETCH completed."))) - .filteredOn(s -> s.contains("* 10 FETCH (MODSEQ (39) FLAGS (\\Answered \\Recent) UID 10)")) - .hasSize(1); + .filteredOn(s -> s.contains("* 10 FETCH (MODSEQ (39) FLAGS (\\Answered \\Recent) UID 10)")) + .hasSize(1); } @Test @@ -3077,13 +3077,13 @@ void fetchShouldNotReturnChangedItemsOutOfRange() throws Exception { inbox.setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(10)), mailboxSession); ModSeq highestModSeq = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMetaData(IGNORE, mailboxSession, NO_COUNT) - .getHighestModSeq(); + .getMetaData(IGNORE, mailboxSession, NO_COUNT) + .getHighestModSeq(); clientConnection.write(ByteBuffer.wrap(String.format("I00104 UID FETCH 12:37 (FLAGS) (CHANGEDSINCE %d)\r\n", highestModSeq.asLong()).getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("I00104 OK FETCH completed."))) - .filteredOn(s -> s.contains("FLAGS")) // No FLAGS FETCH responses - .hasSize(1); + .filteredOn(s -> s.contains("FLAGS")) // No FLAGS FETCH responses + .hasSize(1); } @Test @@ -3098,18 +3098,18 @@ void fetchShouldSupportVanishedModifiedWithEarlierTag() throws Exception { readStringUntil(clientConnection, s -> s.contains("a2 OK [READ-WRITE] SELECT completed.")); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(10)), mailboxSession); + .setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(10)), mailboxSession); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(25)), mailboxSession); + .setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(25)), mailboxSession); ModSeq highestModSeq = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMetaData(IGNORE, mailboxSession, NO_COUNT) - .getHighestModSeq(); + .getMetaData(IGNORE, mailboxSession, NO_COUNT) + .getHighestModSeq(); clientConnection.write(ByteBuffer.wrap(String.format("I00104 UID FETCH 12:37 (FLAGS) (CHANGEDSINCE %d VANISHED)\r\n", highestModSeq.asLong()).getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("I00104 OK FETCH completed."))) - .filteredOn(s -> s.contains("* VANISHED (EARLIER) 14")) - .hasSize(1); + .filteredOn(s -> s.contains("* VANISHED (EARLIER) 14")) + .hasSize(1); } @Test @@ -3124,13 +3124,13 @@ void fetchShouldSupportVanishedModifiedWithoutChangedSince() throws Exception { readStringUntil(clientConnection, s -> s.contains("a2 OK [READ-WRITE] SELECT completed.")); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(10)), mailboxSession); + .setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(10)), mailboxSession); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(25)), mailboxSession); + .setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(25)), mailboxSession); ModSeq highestModSeq = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMetaData(IGNORE, mailboxSession, NO_COUNT) - .getHighestModSeq(); + .getMetaData(IGNORE, mailboxSession, NO_COUNT) + .getHighestModSeq(); clientConnection.write(ByteBuffer.wrap(String.format("I00104 UID FETCH 12:37 (FLAGS) (VANISHED)\r\n", highestModSeq.asLong()).getBytes(StandardCharsets.UTF_8))); assertThat(new String(readBytes(clientConnection))).contains("I00104 BAD FETCH VANISHED used without CHANGEDSINCE"); @@ -3146,13 +3146,13 @@ void fetchShouldRejectVanishedWhenNoQRESYNC() throws Exception { readStringUntil(clientConnection, s -> s.contains("a2 OK [READ-WRITE] SELECT completed.")); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(10)), mailboxSession); + .setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(10)), mailboxSession); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(25)), mailboxSession); + .setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(25)), mailboxSession); ModSeq highestModSeq = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMetaData(IGNORE, mailboxSession, NO_COUNT) - .getHighestModSeq(); + .getMetaData(IGNORE, mailboxSession, NO_COUNT) + .getHighestModSeq(); clientConnection.write(ByteBuffer.wrap(String.format("I00104 UID FETCH 12:37 (FLAGS) (CHANGEDSINCE %d VANISHED)\r\n", highestModSeq.asLong()).getBytes(StandardCharsets.UTF_8))); assertThat(new String(readBytes(clientConnection))).contains("I00104 BAD FETCH QRESYNC not enabled."); @@ -3168,20 +3168,20 @@ void unsolicitedNotificationsShouldBeSent() throws Exception { readStringUntil(clientConnection, s -> s.contains("a2 OK [READ-WRITE] SELECT completed.")); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(10)), mailboxSession); + .setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(10)), mailboxSession); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(25)), mailboxSession); + .setFlags(new Flags(ANSWERED), REPLACE, MessageRange.one(MessageUid.of(25)), mailboxSession); inbox.delete(ImmutableList.of(MessageUid.of(14)), mailboxSession); ModSeq highestModSeq = memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .getMetaData(IGNORE, mailboxSession, NO_COUNT) - .getHighestModSeq(); + .getMetaData(IGNORE, mailboxSession, NO_COUNT) + .getHighestModSeq(); clientConnection.write(ByteBuffer.wrap("I00104 NOOP\r\n".getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("I00104 OK NOOP completed."))) - .filteredOn(s -> s.contains("* VANISHED 14")) - .hasSize(1); + .filteredOn(s -> s.contains("* VANISHED 14")) + .hasSize(1); } @Test @@ -3194,23 +3194,23 @@ void expungeShouldReturnVanishedWhenQResyncIsActive() throws Exception { readStringUntil(clientConnection, s -> s.contains("a2 OK [READ-WRITE] SELECT completed.")); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(10)), mailboxSession); + .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(10)), mailboxSession); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(11)), mailboxSession); + .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(11)), mailboxSession); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(12)), mailboxSession); + .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(12)), mailboxSession); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(25)), mailboxSession); + .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(25)), mailboxSession); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(26)), mailboxSession); + .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(26)), mailboxSession); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(31)), mailboxSession); + .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(31)), mailboxSession); clientConnection.write(ByteBuffer.wrap(("I00104 EXPUNGE\r\n").getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("I00104 OK [HIGHESTMODSEQ 44] EXPUNGE completed."))) - .filteredOn(s -> s.contains("* VANISHED 10:12,25:26,31")) - .hasSize(1); + .filteredOn(s -> s.contains("* VANISHED 10:12,25:26,31")) + .hasSize(1); } @Test @@ -3223,29 +3223,29 @@ void uidExpungeShouldReturnExpungededWhenQResyncIsActive() throws Exception { readStringUntil(clientConnection, s -> s.contains("a2 OK [READ-WRITE] SELECT completed.")); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(10)), mailboxSession); + .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(10)), mailboxSession); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(11)), mailboxSession); + .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(11)), mailboxSession); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(12)), mailboxSession); + .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(12)), mailboxSession); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(25)), mailboxSession); + .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(25)), mailboxSession); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(26)), mailboxSession); + .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(26)), mailboxSession); memoryIntegrationResources.getMailboxManager().getMailbox(MailboxPath.inbox(USER), mailboxSession) - .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(31)), mailboxSession); + .setFlags(new Flags(Flags.Flag.DELETED), REPLACE, MessageRange.one(MessageUid.of(31)), mailboxSession); clientConnection.write(ByteBuffer.wrap(("I00104 UID EXPUNGE 1:37\r\n").getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("I00104 OK [HIGHESTMODSEQ 44] EXPUNGE completed."))) - .filteredOn(s -> s.contains("* VANISHED 10:12,25:26,31")) - .hasSize(1); + .filteredOn(s -> s.contains("* VANISHED 10:12,25:26,31")) + .hasSize(1); } @Test void implicitMailboxSelectionChangesShouldReturnClosedNotifications() throws Exception { memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.forUser(USER, "other"), mailboxSession); + .createMailbox(MailboxPath.forUser(USER, "other"), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readBytes(clientConnection); @@ -3257,15 +3257,15 @@ void implicitMailboxSelectionChangesShouldReturnClosedNotifications() throws Exc clientConnection.write(ByteBuffer.wrap(("a3 SELECT other\r\n").getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("a3 OK [READ-WRITE] SELECT completed."))) - .filteredOn(s -> s.contains("* OK [CLOSED]")) - .hasSize(1); + .filteredOn(s -> s.contains("* OK [CLOSED]")) + .hasSize(1); } @Test void closeShouldNotReturnHighestModseqWhenUsingQResync() throws Exception { // See https://www.rfc-editor.org/errata_search.php?rfc=5162 memoryIntegrationResources.getMailboxManager() - .createMailbox(MailboxPath.forUser(USER, "other"), mailboxSession); + .createMailbox(MailboxPath.forUser(USER, "other"), mailboxSession); clientConnection.write(ByteBuffer.wrap(String.format("a0 LOGIN %s %s\r\n", USER.asString(), USER_PASS).getBytes(StandardCharsets.UTF_8))); readBytes(clientConnection); @@ -3278,7 +3278,7 @@ void closeShouldNotReturnHighestModseqWhenUsingQResync() throws Exception { clientConnection.write(ByteBuffer.wrap(("a3 CLOSE\r\n").getBytes(StandardCharsets.UTF_8))); assertThat(readStringUntil(clientConnection, s -> s.contains("a3 OK CLOSE completed."))) - .isNotNull(); + .isNotNull(); } } @@ -3305,15 +3305,15 @@ void tearDown() { void shouldSupportManyConcurrentSSLConnections() throws Exception { //Failed for 3.7.0, this serves as a non regression test ConcurrentTestRunner.builder() - .operation((a, b) -> { - IMAPSClient imapsClient = imapsImplicitClient(port); - boolean capability = imapsClient.capability(); - assertThat(capability).isTrue(); - imapsClient.close(); - }) - .threadCount(10) - .operationCount(200) - .runSuccessfullyWithin(Duration.ofMinutes(10)); + .operation((a, b) -> { + IMAPSClient imapsClient = imapsImplicitClient(port); + boolean capability = imapsClient.capability(); + assertThat(capability).isTrue(); + imapsClient.close(); + }) + .threadCount(10) + .operationCount(200) + .runSuccessfullyWithin(Duration.ofMinutes(10)); } private IMAPSClient imapsImplicitClient(int port) throws Exception { From bca9239f7de6b5c57bb01f48a48f361dcfc5692b Mon Sep 17 00:00:00 2001 From: Jean Helou Date: Thu, 19 Sep 2024 22:13:02 +0200 Subject: [PATCH 04/18] [devscout] fix execution of unstable group classes annotated unstable at classlevel are properly execucted with -Punstable --- pom.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index d63ed615e88..b04784ea661 100644 --- a/pom.xml +++ b/pom.xml @@ -644,7 +644,7 @@ 3.9.3 3.1.0 3.1.1 - 3.3.0 + 3.5.0 3.5.0 2.0.3 2.3.1 @@ -3326,7 +3326,7 @@ true 1800 - unstable + none() @@ -3778,7 +3778,6 @@ maven-surefire-plugin ${maven-surefire-plugin.version} - unstable From cbd152d50cb06d83ac6bc162a9e92ae6b7c14b4a Mon Sep 17 00:00:00 2001 From: Jean Helou Date: Thu, 5 Sep 2024 12:53:53 +0200 Subject: [PATCH 05/18] [devscout] upgrades to pulsar 3.3.1 This doesn't fix the test but it eliminates one possible variation --- backends-common/pulsar/pom.xml | 4 ++-- .../apache/james/backends/pulsar/DockerPulsarExtension.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backends-common/pulsar/pom.xml b/backends-common/pulsar/pom.xml index a9962887e8b..2636783b3bc 100644 --- a/backends-common/pulsar/pom.xml +++ b/backends-common/pulsar/pom.xml @@ -30,8 +30,8 @@ apache-james-backends-pulsar Apache James :: Backends Common :: Pulsar - 2.11.0 - 2.9.1 + 3.3.1 + 2.10.0 1.0.2 diff --git a/backends-common/pulsar/src/test/java/org/apache/james/backends/pulsar/DockerPulsarExtension.java b/backends-common/pulsar/src/test/java/org/apache/james/backends/pulsar/DockerPulsarExtension.java index 538f5ebd836..45fde26d889 100644 --- a/backends-common/pulsar/src/test/java/org/apache/james/backends/pulsar/DockerPulsarExtension.java +++ b/backends-common/pulsar/src/test/java/org/apache/james/backends/pulsar/DockerPulsarExtension.java @@ -57,7 +57,7 @@ private static void displayDockerLog(OutputFrame outputFrame) { @Inject public DockerPulsarExtension() { - container = new PulsarContainer(DEFAULT_IMAGE_NAME.withTag("3.3.0")) + container = new PulsarContainer(DEFAULT_IMAGE_NAME.withTag("3.3.1")) .withLogConsumer(DockerPulsarExtension::displayDockerLog); } From 314b6a74385582bff888132fcb6ea617e0a929c5 Mon Sep 17 00:00:00 2001 From: Jean Helou Date: Thu, 19 Sep 2024 14:43:55 +0200 Subject: [PATCH 06/18] [JAMES-3696] fixes broken MailQueueContract test The test started breaking when the deadletter policy was introduced on the pulsar mailqueue. It wasn't detected because test suites that are tagged unstable are never executed on CI (:scream:) The core issue is that the way the test was setup the same mail could end up being retried several times while others were being acknowledge in success immediately. As long as no dead letter policy was applied, it was fine and the email would be retried several times before being dequeued. With the dead letter policy this was no longer possible and the test never completed because at least one of the mail ended up in dead letter. I changed the test to track the state of each email and apply the proper action (retry or success) ensuring each mail is retried once and then acknowledged. --- .../james/queue/api/MailQueueContract.java | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/MailQueueContract.java b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/MailQueueContract.java index 4430ec7d372..32c5b865379 100644 --- a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/MailQueueContract.java +++ b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/MailQueueContract.java @@ -35,6 +35,7 @@ import java.util.EnumSet; import java.util.Iterator; import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -516,6 +517,12 @@ default void concurrentEnqueueDequeueShouldNotFail() throws Exception { .hasSize(totalDequeuedMessages); } + enum EnqueueDequeueSteps { + RETRY, + DEQUEUE, + END + } + @Test default void concurrentEnqueueDequeueWithAckNackShouldNotFail() throws Exception { MailQueue testee = getMailQueue(); @@ -526,22 +533,34 @@ default void concurrentEnqueueDequeueWithAckNackShouldNotFail() throws Exception int operationCount = 15; int totalDequeuedMessages = 50; LinkedBlockingDeque deque = new LinkedBlockingDeque<>(); + var mailStates = new ConcurrentHashMap(); Flux.from(testee.deQueue()).subscribeOn(SCHEDULER).doOnNext(deque::addFirst).subscribe(); ConcurrentTestRunner.builder() .operation((threadNumber, step) -> { if (step % 3 == 0) { + String name = "name" + threadNumber + "-" + step; + mailStates.put(name, EnqueueDequeueSteps.RETRY); testee.enQueue(defaultMail() - .name("name" + threadNumber + "-" + step) + .name(name) .build()); - } - if (step % 3 == 1) { - MailQueue.MailQueueItem mailQueueItem = deque.takeLast(); - mailQueueItem.done(MailQueue.MailQueueItem.CompletionStatus.RETRY); - } - if (step % 3 == 2) { + } else { MailQueue.MailQueueItem mailQueueItem = deque.takeLast(); - dequeuedMails.add(mailQueueItem.getMail()); - mailQueueItem.done(MailQueue.MailQueueItem.CompletionStatus.SUCCESS); + var name = mailQueueItem.getMail().getName(); + var currentState = mailStates.get(mailQueueItem.getMail().getName()); + var nextState = switch (currentState) { + case RETRY -> EnqueueDequeueSteps.DEQUEUE; + case DEQUEUE -> EnqueueDequeueSteps.END; + case END -> + throw new IllegalStateException("trying to dequeue mail " + name + " multiple times !"); + }; + mailStates.put(name, nextState); + if (currentState == EnqueueDequeueSteps.RETRY) { + mailQueueItem.done(MailQueue.MailQueueItem.CompletionStatus.RETRY); + } else { + dequeuedMails.add(mailQueueItem.getMail()); + mailQueueItem.done(MailQueue.MailQueueItem.CompletionStatus.SUCCESS); + } + } }) .threadCount(threadCount) From f2a56fc64c8dce852017a2b4c48caee595025ce5 Mon Sep 17 00:00:00 2001 From: Jean Helou Date: Fri, 20 Sep 2024 09:00:01 +0200 Subject: [PATCH 07/18] [devscout] actually consume the iterators in ManageableMailQueueContract The previous implementation using `Iterators.consumingIterator` doesn't actually consume anything. It only wraps the iterator it receives in an interator that removes the items from the underlying iterator when next is called. We implemented a very simple : consumeIterator method to actually consume and therefore trigger the potential concurrency issues the tests were trying to protect against. Doing so raised a failing test in ActiveMQMailQueueBlobTest. --- .../activemq/ActiveMQMailQueueBlobTest.java | 12 +++++++++ .../api/ManageableMailQueueContract.java | 27 +++++++++++++------ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/server/queue/queue-activemq/src/test/java/org/apache/james/queue/activemq/ActiveMQMailQueueBlobTest.java b/server/queue/queue-activemq/src/test/java/org/apache/james/queue/activemq/ActiveMQMailQueueBlobTest.java index c7abe08db00..cc41ceaef95 100644 --- a/server/queue/queue-activemq/src/test/java/org/apache/james/queue/activemq/ActiveMQMailQueueBlobTest.java +++ b/server/queue/queue-activemq/src/test/java/org/apache/james/queue/activemq/ActiveMQMailQueueBlobTest.java @@ -31,6 +31,7 @@ import org.apache.activemq.broker.BrokerService; import org.apache.commons.io.FileUtils; import org.apache.james.filesystem.api.FileSystem; +import org.apache.james.junit.categories.Unstable; import org.apache.james.metrics.api.GaugeRegistry; import org.apache.james.metrics.api.MetricFactory; import org.apache.james.queue.api.DelayedManageableMailQueueContract; @@ -157,6 +158,17 @@ public void delayedEmailsShouldBeDeletedWhenMixedWithOtherEmails() { } + @Tag(Unstable.TAG) + @Test + @Override + public void browseShouldNotFailWhenConcurrentClearWhenIterating() throws Exception { + // This test used to pass because the assertion did not actually check anything + // it used Iterators.consumingIterator which only wraps the underlying iterator without + // actually consuming it. When replaced with an actual consumption this implementation started + // failing. + DelayedManageableMailQueueContract.super.browseShouldNotFailWhenConcurrentClearWhenIterating(); + } + @Test void computeNextDeliveryTimestampShouldReturnLongMaxWhenOverflow() { long deliveryTimestamp = mailQueue.computeNextDeliveryTimestamp(ChronoUnit.FOREVER.getDuration()); diff --git a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java index 42cb4803e0f..cae74f4d775 100644 --- a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java +++ b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java @@ -31,6 +31,7 @@ import static org.assertj.core.api.SoftAssertions.assertSoftly; import java.time.Duration; +import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -41,10 +42,12 @@ import org.apache.james.core.MailAddress; import org.apache.james.core.builder.MimeMessageBuilder; +import org.apache.james.junit.categories.Unstable; import org.apache.mailet.Attribute; import org.apache.mailet.Mail; import org.apache.mailet.base.MailAddressFixture; import org.awaitility.Awaitility; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; @@ -53,7 +56,6 @@ import com.github.fge.lambdas.Throwing; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterators; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -217,7 +219,7 @@ default void browseShouldNotFailWhenConcurrentDequeue() throws Exception { Flux.from(getManageableMailQueue().deQueue()); - assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); } @Test @@ -266,7 +268,7 @@ default void browseShouldNotFailWhenConcurrentDequeueWhenIterating() throws Exce Flux.from(getManageableMailQueue().deQueue()); - assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); } @Test @@ -307,7 +309,7 @@ default void browseShouldNotFailWhenConcurrentEnqueue() throws Exception { .name("name4") .build()); - assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); } @Test @@ -352,7 +354,7 @@ default void browseShouldNotFailWhenConcurrentEnqueueWhenIterating() throws Exce .name("name2") .build()); - assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); } @Test @@ -392,7 +394,7 @@ default void browseShouldNotFailWhenConcurrentClearWhenIterating() throws Except getManageableMailQueue().clear(); - assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); } @Test @@ -432,7 +434,7 @@ default void browseShouldNotFailWhenConcurrentFlushWhenIterating() throws Except getManageableMailQueue().flush(); - assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); } @Test @@ -474,7 +476,7 @@ default void browseShouldNotFailWhenConcurrentRemoveWhenIterating() throws Excep awaitRemove(); - assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); } @Test @@ -738,4 +740,13 @@ default void removeShouldNotDeleteFutureEmailsFromBrowse() throws MessagingExcep .containsExactly("name2"); } + default int consumeIterator(Iterator iterator) { + var i = 0; + while (iterator.hasNext()) { + iterator.next(); + i++; + } + return i; + } + } From 1705d89822ac5f7f8abbca187bcf87cbae9b008a Mon Sep 17 00:00:00 2001 From: Jean Helou Date: Fri, 20 Sep 2024 09:04:19 +0200 Subject: [PATCH 08/18] [JAMES-3696] improves flaky tests from ManageableMailQueueContract The pulsar implementation uses a distributed and therefore asynchronous distribution of the removal filters. This change ensures that the tests allow for such an implementation by waiting for the system under test to converge to the desired state. It also ensures that the executor waits as little as possible to get into a consistent state, ensuring fast local execution while allowing a longer wait on the CI. If the default timeout (10s if I understand correctly) is not enough for the CI, it can be safely increased without slowing down local test execution like Thread.sleep() did. --- .../DelayedManageableMailQueueContract.java | 10 +- .../api/ManageableMailQueueContract.java | 113 ++++++++++-------- .../queue/pulsar/PulsarMailQueueTest.java | 26 ++-- 3 files changed, 75 insertions(+), 74 deletions(-) diff --git a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/DelayedManageableMailQueueContract.java b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/DelayedManageableMailQueueContract.java index 442c42af220..782b2078997 100644 --- a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/DelayedManageableMailQueueContract.java +++ b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/DelayedManageableMailQueueContract.java @@ -123,11 +123,11 @@ default void deletedDelayedMessagesShouldNotBeBrowseable() throws Exception { getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, RECIPIENT2.asString()); - awaitRemove(); - - assertThat(getManageableMailQueue().browse()).toIterable() - .extracting(mail -> mail.getMail().getName()) - .containsExactly("name2"); + Awaitility.await().untilAsserted(() -> + assertThat(getManageableMailQueue().browse()).toIterable() + .extracting(mail -> mail.getMail().getName()) + .containsExactly("name2") + ); } @Test diff --git a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java index cae74f4d775..c174fe6bc56 100644 --- a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java +++ b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java @@ -34,6 +34,7 @@ import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; import jakarta.mail.MessagingException; @@ -42,12 +43,10 @@ import org.apache.james.core.MailAddress; import org.apache.james.core.builder.MimeMessageBuilder; -import org.apache.james.junit.categories.Unstable; import org.apache.mailet.Attribute; import org.apache.mailet.Mail; import org.apache.mailet.base.MailAddressFixture; import org.awaitility.Awaitility; -import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; @@ -62,10 +61,6 @@ public interface ManageableMailQueueContract extends MailQueueContract { - default void awaitRemove() { - - } - ManageableMailQueue getManageableMailQueue(); @Test @@ -459,24 +454,27 @@ default void removeShouldNotFailWhenBrowsingIterating() throws Exception { @Test default void browseShouldNotFailWhenConcurrentRemoveWhenIterating() throws Exception { - enQueue(defaultMail() - .name("name1") - .build()); - enQueue(defaultMail() - .name("name2") - .build()); - enQueue(defaultMail() - .name("name3") - .build()); + // We use a large number of emails so that the probability of the remove being propagated + // through the queue is high enough that the test is not flaky. + IntStream.range(0, 100).forEach( + Throwing.intConsumer(i -> + enQueue(defaultMail() + .name("name" + i) + .build()) + ).sneakyThrow() + ); ManageableMailQueue.MailQueueIterator items = getManageableMailQueue().browse(); - items.next(); + items.next();// we consume 1 here - getManageableMailQueue().remove(ManageableMailQueue.Type.Name, "name2"); + getManageableMailQueue().remove(ManageableMailQueue.Type.Name, "name98"); // we remove 1 here - awaitRemove(); - assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); + assertThatCode( + // the remove may or may not be applied to an already started iterator + // as long as it doesn't make the iterator crash + () -> assertThat(consumeIterator(items)).isGreaterThanOrEqualTo(98).isLessThan(100) + ).doesNotThrowAnyException(); } @Test @@ -551,13 +549,13 @@ default void removeByNameShouldRemoveSpecificEmail() throws Exception { getManageableMailQueue().remove(ManageableMailQueue.Type.Name, "name2"); - awaitRemove(); - - assertThat(getManageableMailQueue().browse()) - .toIterable() - .extracting(ManageableMailQueue.MailQueueItemView::getMail) - .extracting(Mail::getName) - .containsExactly("name1"); + Awaitility.await().untilAsserted(() -> + assertThat(getManageableMailQueue().browse()) + .toIterable() + .extracting(ManageableMailQueue.MailQueueItemView::getMail) + .extracting(Mail::getName) + .containsExactly("name1") + ); } @Test @@ -573,13 +571,13 @@ default void removeBySenderShouldRemoveSpecificEmail() throws Exception { getManageableMailQueue().remove(ManageableMailQueue.Type.Sender, OTHER_AT_LOCAL.asString()); - awaitRemove(); - - assertThat(getManageableMailQueue().browse()) - .toIterable() - .extracting(ManageableMailQueue.MailQueueItemView::getMail) - .extracting(Mail::getName) - .containsExactly("name2"); + Awaitility.await().untilAsserted(() -> + assertThat(getManageableMailQueue().browse()) + .toIterable() + .extracting(ManageableMailQueue.MailQueueItemView::getMail) + .extracting(Mail::getName) + .containsExactly("name2") + ); } @Test @@ -595,13 +593,13 @@ default void removeByRecipientShouldRemoveSpecificEmail() throws Exception { getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, RECIPIENT2.asString()); - awaitRemove(); - - assertThat(getManageableMailQueue().browse()) - .toIterable() - .extracting(ManageableMailQueue.MailQueueItemView::getMail) - .extracting(Mail::getName) - .containsExactly("name1"); + Awaitility.await().untilAsserted(() -> + assertThat(getManageableMailQueue().browse()) + .toIterable() + .extracting(ManageableMailQueue.MailQueueItemView::getMail) + .extracting(Mail::getName) + .containsExactly("name1") + ); } static Stream removeByRecipientShouldRemoveSpecificEmailWhenMultipleRecipients() throws AddressException { @@ -630,13 +628,13 @@ default void removeByRecipientShouldRemoveSpecificEmailWhenMultipleRecipients(Li getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, toRemove.asString()); - awaitRemove(); - - assertThat(getManageableMailQueue().browse()) - .toIterable() - .extracting(ManageableMailQueue.MailQueueItemView::getMail) - .extracting(Mail::getName) - .containsExactly("name2"); + Awaitility.await().untilAsserted(() -> + assertThat(getManageableMailQueue().browse()) + .toIterable() + .extracting(ManageableMailQueue.MailQueueItemView::getMail) + .extracting(Mail::getName) + .containsExactly("name2") + ); } @Test @@ -693,7 +691,13 @@ default void deletedElementsShouldNotBeDequeued() throws Exception { getManageableMailQueue().remove(ManageableMailQueue.Type.Name, "name1"); - awaitRemove(); + Awaitility.await().untilAsserted(() -> + assertThat(getManageableMailQueue().browse()) + .toIterable() + .extracting(ManageableMailQueue.MailQueueItemView::getMail) + .extracting(Mail::getName) + .doesNotContain("name1") + ); assertThat(Flux.from(getManageableMailQueue().deQueue()).blockFirst().getMail().getName()) .isEqualTo("name2"); @@ -709,7 +713,11 @@ default void removeShouldNotDeleteFutureEmails() throws MessagingException { getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, MailAddressFixture.RECIPIENT1.asString()); - awaitRemove(); + Awaitility.await().untilAsserted(() -> + assertThat(getManageableMailQueue().browse()) + .toIterable() + .isEmpty() + ); enQueue(defaultMail() .name("name2") @@ -729,7 +737,12 @@ default void removeShouldNotDeleteFutureEmailsFromBrowse() throws MessagingExcep getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, MailAddressFixture.RECIPIENT1.asString()); - awaitRemove(); + Awaitility.await().untilAsserted(() -> + assertThat(getManageableMailQueue().browse()) + .toIterable() + .isEmpty() + ); + enQueue(defaultMail() .name("name2") diff --git a/server/queue/queue-pulsar/src/test/java/org/apache/james/queue/pulsar/PulsarMailQueueTest.java b/server/queue/queue-pulsar/src/test/java/org/apache/james/queue/pulsar/PulsarMailQueueTest.java index 135210a630a..c11d0bad5f4 100644 --- a/server/queue/queue-pulsar/src/test/java/org/apache/james/queue/pulsar/PulsarMailQueueTest.java +++ b/server/queue/queue-pulsar/src/test/java/org/apache/james/queue/pulsar/PulsarMailQueueTest.java @@ -41,7 +41,6 @@ import org.apache.james.blob.mail.MimeMessagePartsId; import org.apache.james.blob.mail.MimeMessageStore; import org.apache.james.blob.memory.MemoryBlobStoreDAO; -import org.apache.james.junit.categories.Unstable; import org.apache.james.queue.api.DelayedMailQueueContract; import org.apache.james.queue.api.DelayedManageableMailQueueContract; import org.apache.james.queue.api.MailQueue; @@ -61,7 +60,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -71,7 +69,6 @@ import reactor.core.publisher.Flux; import scala.jdk.javaapi.OptionConverters; -@Tag(Unstable.TAG) @ExtendWith(DockerPulsarExtension.class) public class PulsarMailQueueTest implements MailQueueContract, MailQueueMetricContract, ManageableMailQueueContract, DelayedMailQueueContract, DelayedManageableMailQueueContract { @@ -110,15 +107,6 @@ void tearDown() throws Exception { pulsarClients.stop(); } - @Override - public void awaitRemove() { - try { - Thread.sleep(50); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - @Override public MailQueue getMailQueue() { return mailQueue; @@ -272,13 +260,13 @@ void removeShouldRemoveMailFromStoreWhenFilteredOut() throws Exception { //this won't delete the mail from the store until we try a dequeue getManageableMailQueue().remove(ManageableMailQueue.Type.Name, "name2"); - awaitRemove(); - - assertThat(getManageableMailQueue().browse()) - .toIterable() - .extracting(ManageableMailQueue.MailQueueItemView::getMail) - .extracting(Mail::getName) - .containsExactly("name1", "name3"); + Awaitility.await().untilAsserted(() -> + assertThat(getManageableMailQueue().browse()) + .toIterable() + .extracting(ManageableMailQueue.MailQueueItemView::getMail) + .extracting(Mail::getName) + .containsExactly("name1", "name3") + ); Flux.from(getMailQueue().deQueue()).take(2).doOnNext(Throwing.consumer(x -> x.done(MailQueue.MailQueueItem.CompletionStatus.SUCCESS))).blockLast(); Awaitility.await().untilAsserted(this::assertThatStoreIsEmpty); From 01ab34dbcc7ec0f86722bd873a6588494d2d2a15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=95=A8=ED=83=9C=ED=9B=88?= Date: Mon, 16 Sep 2024 14:15:35 +0900 Subject: [PATCH 09/18] Refactor DefaultImapDecoderFactory to Use Generics and Improve Flexibility Updated DefaultImapDecoderFactory to use a generic type T for flexibility, allowing any implementation of StatusResponseFactory. Introduced a parameterized constructor that accepts an instance of StatusResponseFactory to facilitate various implementations. Retained the default constructor, which now initializes with UnpooledStatusResponseFactory as the default implementation. Enhanced modularity by enabling constructor injection, improving decoupling, and reducing hardcoded dependencies. Updated class and method documentation to reflect the use of generics and the new constructor design. Modified IMAPHealthCheckTest and IMAPServerTest to replace static function calls with instance creation using the new keyword, enhancing readability and flexibility in test code. This change improves the flexibility and reusability of the DefaultImapDecoderFactory by supporting multiple StatusResponseFactory implementations while maintaining a default behavior. Additionally, it improves test code clarity by using a more explicit object creation approach. --- .../imap/main/DefaultImapDecoderFactory.java | 48 +++++++++++++++---- .../imapserver/netty/IMAPHealthCheckTest.java | 2 +- .../imapserver/netty/IMAPServerTest.java | 7 +-- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java b/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java index 87730468fbd..2fc970e303b 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java @@ -19,6 +19,7 @@ package org.apache.james.imap.main; +import org.apache.james.imap.api.message.response.StatusResponseFactory; import org.apache.james.imap.decode.ImapCommandParserFactory; import org.apache.james.imap.decode.ImapDecoder; import org.apache.james.imap.decode.ImapDecoderFactory; @@ -27,20 +28,49 @@ import org.apache.james.imap.message.response.UnpooledStatusResponseFactory; /** - * TODO: this is temporary: should let the container do the coupling. TODO: - * convert to POJO + * Factory class for creating `ImapDecoder` instances. + * + * This class is a POJO that manually manages its dependencies. + * Dependencies are injected through the constructor, which allows for + * better decoupling and easier testing. + * + * The creation of `ImapCommandParserFactory` is handled internally by + * this factory, based on the provided `UnpooledStatusResponseFactory`. */ -public class DefaultImapDecoderFactory implements ImapDecoderFactory { +public class DefaultImapDecoderFactory implements ImapDecoderFactory { - public static ImapDecoder createDecoder() { - final UnpooledStatusResponseFactory unpooledStatusResponseFactory = new UnpooledStatusResponseFactory(); - final ImapCommandParserFactory imapCommands = new ImapParserFactory(unpooledStatusResponseFactory); - return new DefaultImapDecoder(unpooledStatusResponseFactory, imapCommands); + private final T statusResponseFactory; + private final ImapCommandParserFactory imapCommandParserFactory; + + /** + * Default constructor. + * This constructor creates an instance of DefaultImapDecoderFactory with an UnpooledStatusResponseFactory as the default implementation. + * Since the type T is not known at runtime due to type erasure, we cast the new UnpooledStatusResponseFactory to T. + * It internally calls the other constructor to set up the necessary dependencies. + */ + public DefaultImapDecoderFactory() { + this((T) new UnpooledStatusResponseFactory()); + } + + /** + * Constructor that accepts a specific implementation of StatusResponseFactory. + * This constructor allows for flexibility by enabling different implementations of StatusResponseFactory to be used. + * + * @param statusResponseFactory An implementation of StatusResponseFactory to be used for creating IMAP command parsers and decoders. + */ + public DefaultImapDecoderFactory(T statusResponseFactory) { + this.statusResponseFactory = statusResponseFactory; + this.imapCommandParserFactory = new ImapParserFactory(statusResponseFactory); } + /** + * Builds and returns an instance of ImapDecoder. + * This method uses the statusResponseFactory and the imapCommandParserFactory to create a new DefaultImapDecoder instance. + * + * @return A new instance of DefaultImapDecoder. + */ @Override public ImapDecoder buildImapDecoder() { - return createDecoder(); + return new DefaultImapDecoder(statusResponseFactory, imapCommandParserFactory); } - } diff --git a/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPHealthCheckTest.java b/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPHealthCheckTest.java index 8e4937b1fdc..25b5fa509dd 100644 --- a/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPHealthCheckTest.java +++ b/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPHealthCheckTest.java @@ -76,7 +76,7 @@ void setUp() throws Exception { imapServerFactory = new IMAPServerFactory( FileSystemImpl.forTestingWithConfigurationFromClasspath(), - DefaultImapDecoderFactory.createDecoder(), + new DefaultImapDecoderFactory().buildImapDecoder(), new DefaultImapEncoderFactory().buildImapEncoder(), DefaultImapProcessorFactory.createXListSupportingProcessor( mailboxManager, 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 50d075e30fe..875a67cfe40 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 @@ -116,9 +116,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.stubbing.Answer; -import org.mockserver.integration.ClientAndServer; -import org.mockserver.model.HttpRequest; -import org.mockserver.model.HttpResponse; import org.slf4j.LoggerFactory; import com.github.fge.lambdas.Throwing; @@ -134,8 +131,6 @@ import io.netty.handler.codec.compression.JdkZlibEncoder; import io.netty.handler.codec.compression.ZlibWrapper; import io.netty.handler.ssl.SslContextBuilder; -import nl.altindag.ssl.exception.GenericKeyStoreException; -import nl.altindag.ssl.pem.exception.PrivateKeyParseException; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; @@ -166,7 +161,7 @@ private IMAPServer createImapServer(HierarchicalConfiguration con Set connectionChecks = defaultConnectionChecks(); mailboxManager = spy(memoryIntegrationResources.getMailboxManager()); IMAPServer imapServer = new IMAPServer( - DefaultImapDecoderFactory.createDecoder(), + new DefaultImapDecoderFactory().buildImapDecoder(), new DefaultImapEncoderFactory().buildImapEncoder(), DefaultImapProcessorFactory.createXListSupportingProcessor( mailboxManager, From fbb880265e7a90bf3ad45e61b4480bbbffe08dfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=95=A8=ED=83=9C=ED=9B=88?= Date: Mon, 16 Sep 2024 14:15:35 +0900 Subject: [PATCH 10/18] Refactor `DefaultImapDecoderFactory` to Use POJO Style and Constructor Injection - Updated `DefaultImapDecoderFactory` to adhere to POJO design principles. - Replaced internal `ImapParserFactory` creation with constructor injection of `UnpooledStatusResponseFactory`. - Improved decoupling and maintainability by explicitly managing dependencies via the constructor. - Updated class and method documentation to reflect changes and clarify the role of the factory. This change enhances the clarity and testability of the `DefaultImapDecoderFactory` by removing direct dependency creation and enforcing a cleaner, more modular structure. --- .../imap/main/DefaultImapDecoderFactory.java | 37 ++++++------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java b/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java index 2fc970e303b..ecf1c2592d4 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java @@ -19,7 +19,6 @@ package org.apache.james.imap.main; -import org.apache.james.imap.api.message.response.StatusResponseFactory; import org.apache.james.imap.decode.ImapCommandParserFactory; import org.apache.james.imap.decode.ImapDecoder; import org.apache.james.imap.decode.ImapDecoderFactory; @@ -37,40 +36,26 @@ * The creation of `ImapCommandParserFactory` is handled internally by * this factory, based on the provided `UnpooledStatusResponseFactory`. */ -public class DefaultImapDecoderFactory implements ImapDecoderFactory { +public class DefaultImapDecoderFactory implements ImapDecoderFactory { - private final T statusResponseFactory; + private final UnpooledStatusResponseFactory unpooledStatusResponseFactory; private final ImapCommandParserFactory imapCommandParserFactory; /** - * Default constructor. - * This constructor creates an instance of DefaultImapDecoderFactory with an UnpooledStatusResponseFactory as the default implementation. - * Since the type T is not known at runtime due to type erasure, we cast the new UnpooledStatusResponseFactory to T. - * It internally calls the other constructor to set up the necessary dependencies. - */ - public DefaultImapDecoderFactory() { - this((T) new UnpooledStatusResponseFactory()); - } - - /** - * Constructor that accepts a specific implementation of StatusResponseFactory. - * This constructor allows for flexibility by enabling different implementations of StatusResponseFactory to be used. + * Constructs `DefaultImapDecoderFactory` with the given + * `UnpooledStatusResponseFactory`. The `ImapCommandParserFactory` + * is created internally using the provided `UnpooledStatusResponseFactory`. * - * @param statusResponseFactory An implementation of StatusResponseFactory to be used for creating IMAP command parsers and decoders. + * @param unpooledStatusResponseFactory The factory for creating status responses. */ - public DefaultImapDecoderFactory(T statusResponseFactory) { - this.statusResponseFactory = statusResponseFactory; - this.imapCommandParserFactory = new ImapParserFactory(statusResponseFactory); + public DefaultImapDecoderFactory(UnpooledStatusResponseFactory unpooledStatusResponseFactory) { + this.unpooledStatusResponseFactory = unpooledStatusResponseFactory; + this.imapCommandParserFactory = new ImapParserFactory(unpooledStatusResponseFactory); } - /** - * Builds and returns an instance of ImapDecoder. - * This method uses the statusResponseFactory and the imapCommandParserFactory to create a new DefaultImapDecoder instance. - * - * @return A new instance of DefaultImapDecoder. - */ @Override public ImapDecoder buildImapDecoder() { - return new DefaultImapDecoder(statusResponseFactory, imapCommandParserFactory); + return new DefaultImapDecoder(unpooledStatusResponseFactory, imapCommandParserFactory); } + } From 7a30a7f722c91693623d88df0080d51884b81c76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=95=A8=ED=83=9C=ED=9B=88?= Date: Mon, 16 Sep 2024 14:15:35 +0900 Subject: [PATCH 11/18] Refactor DefaultImapDecoderFactory to Use Generics and Improve Flexibility Updated DefaultImapDecoderFactory to use a generic type T for flexibility, allowing any implementation of StatusResponseFactory. Introduced a parameterized constructor that accepts an instance of StatusResponseFactory to facilitate various implementations. Retained the default constructor, which now initializes with UnpooledStatusResponseFactory as the default implementation. Enhanced modularity by enabling constructor injection, improving decoupling, and reducing hardcoded dependencies. Updated class and method documentation to reflect the use of generics and the new constructor design. Modified IMAPHealthCheckTest and IMAPServerTest to replace static function calls with instance creation using the new keyword, enhancing readability and flexibility in test code. This change improves the flexibility and reusability of the DefaultImapDecoderFactory by supporting multiple StatusResponseFactory implementations while maintaining a default behavior. Additionally, it improves test code clarity by using a more explicit object creation approach. --- .../imap/main/DefaultImapDecoderFactory.java | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java b/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java index ecf1c2592d4..2fc970e303b 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java @@ -19,6 +19,7 @@ package org.apache.james.imap.main; +import org.apache.james.imap.api.message.response.StatusResponseFactory; import org.apache.james.imap.decode.ImapCommandParserFactory; import org.apache.james.imap.decode.ImapDecoder; import org.apache.james.imap.decode.ImapDecoderFactory; @@ -36,26 +37,40 @@ * The creation of `ImapCommandParserFactory` is handled internally by * this factory, based on the provided `UnpooledStatusResponseFactory`. */ -public class DefaultImapDecoderFactory implements ImapDecoderFactory { +public class DefaultImapDecoderFactory implements ImapDecoderFactory { - private final UnpooledStatusResponseFactory unpooledStatusResponseFactory; + private final T statusResponseFactory; private final ImapCommandParserFactory imapCommandParserFactory; /** - * Constructs `DefaultImapDecoderFactory` with the given - * `UnpooledStatusResponseFactory`. The `ImapCommandParserFactory` - * is created internally using the provided `UnpooledStatusResponseFactory`. + * Default constructor. + * This constructor creates an instance of DefaultImapDecoderFactory with an UnpooledStatusResponseFactory as the default implementation. + * Since the type T is not known at runtime due to type erasure, we cast the new UnpooledStatusResponseFactory to T. + * It internally calls the other constructor to set up the necessary dependencies. + */ + public DefaultImapDecoderFactory() { + this((T) new UnpooledStatusResponseFactory()); + } + + /** + * Constructor that accepts a specific implementation of StatusResponseFactory. + * This constructor allows for flexibility by enabling different implementations of StatusResponseFactory to be used. * - * @param unpooledStatusResponseFactory The factory for creating status responses. + * @param statusResponseFactory An implementation of StatusResponseFactory to be used for creating IMAP command parsers and decoders. */ - public DefaultImapDecoderFactory(UnpooledStatusResponseFactory unpooledStatusResponseFactory) { - this.unpooledStatusResponseFactory = unpooledStatusResponseFactory; - this.imapCommandParserFactory = new ImapParserFactory(unpooledStatusResponseFactory); + public DefaultImapDecoderFactory(T statusResponseFactory) { + this.statusResponseFactory = statusResponseFactory; + this.imapCommandParserFactory = new ImapParserFactory(statusResponseFactory); } + /** + * Builds and returns an instance of ImapDecoder. + * This method uses the statusResponseFactory and the imapCommandParserFactory to create a new DefaultImapDecoder instance. + * + * @return A new instance of DefaultImapDecoder. + */ @Override public ImapDecoder buildImapDecoder() { - return new DefaultImapDecoder(unpooledStatusResponseFactory, imapCommandParserFactory); + return new DefaultImapDecoder(statusResponseFactory, imapCommandParserFactory); } - } From 79f75021a7c8962d15a892bbfff71dde246d7a0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=95=A8=ED=83=9C=ED=9B=88?= Date: Mon, 16 Sep 2024 14:15:35 +0900 Subject: [PATCH 12/18] Refactor `DefaultImapDecoderFactory` to Use POJO Style and Constructor Injection - Updated `DefaultImapDecoderFactory` to adhere to POJO design principles. - Replaced internal `ImapParserFactory` creation with constructor injection of `UnpooledStatusResponseFactory`. - Improved decoupling and maintainability by explicitly managing dependencies via the constructor. - Updated class and method documentation to reflect changes and clarify the role of the factory. This change enhances the clarity and testability of the `DefaultImapDecoderFactory` by removing direct dependency creation and enforcing a cleaner, more modular structure. --- .../imap/main/DefaultImapDecoderFactory.java | 37 ++++++------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java b/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java index 2fc970e303b..ecf1c2592d4 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java @@ -19,7 +19,6 @@ package org.apache.james.imap.main; -import org.apache.james.imap.api.message.response.StatusResponseFactory; import org.apache.james.imap.decode.ImapCommandParserFactory; import org.apache.james.imap.decode.ImapDecoder; import org.apache.james.imap.decode.ImapDecoderFactory; @@ -37,40 +36,26 @@ * The creation of `ImapCommandParserFactory` is handled internally by * this factory, based on the provided `UnpooledStatusResponseFactory`. */ -public class DefaultImapDecoderFactory implements ImapDecoderFactory { +public class DefaultImapDecoderFactory implements ImapDecoderFactory { - private final T statusResponseFactory; + private final UnpooledStatusResponseFactory unpooledStatusResponseFactory; private final ImapCommandParserFactory imapCommandParserFactory; /** - * Default constructor. - * This constructor creates an instance of DefaultImapDecoderFactory with an UnpooledStatusResponseFactory as the default implementation. - * Since the type T is not known at runtime due to type erasure, we cast the new UnpooledStatusResponseFactory to T. - * It internally calls the other constructor to set up the necessary dependencies. - */ - public DefaultImapDecoderFactory() { - this((T) new UnpooledStatusResponseFactory()); - } - - /** - * Constructor that accepts a specific implementation of StatusResponseFactory. - * This constructor allows for flexibility by enabling different implementations of StatusResponseFactory to be used. + * Constructs `DefaultImapDecoderFactory` with the given + * `UnpooledStatusResponseFactory`. The `ImapCommandParserFactory` + * is created internally using the provided `UnpooledStatusResponseFactory`. * - * @param statusResponseFactory An implementation of StatusResponseFactory to be used for creating IMAP command parsers and decoders. + * @param unpooledStatusResponseFactory The factory for creating status responses. */ - public DefaultImapDecoderFactory(T statusResponseFactory) { - this.statusResponseFactory = statusResponseFactory; - this.imapCommandParserFactory = new ImapParserFactory(statusResponseFactory); + public DefaultImapDecoderFactory(UnpooledStatusResponseFactory unpooledStatusResponseFactory) { + this.unpooledStatusResponseFactory = unpooledStatusResponseFactory; + this.imapCommandParserFactory = new ImapParserFactory(unpooledStatusResponseFactory); } - /** - * Builds and returns an instance of ImapDecoder. - * This method uses the statusResponseFactory and the imapCommandParserFactory to create a new DefaultImapDecoder instance. - * - * @return A new instance of DefaultImapDecoder. - */ @Override public ImapDecoder buildImapDecoder() { - return new DefaultImapDecoder(statusResponseFactory, imapCommandParserFactory); + return new DefaultImapDecoder(unpooledStatusResponseFactory, imapCommandParserFactory); } + } From 508337e61073a70224b4c2aaa8d3ebdb3b8ea4cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=95=A8=ED=83=9C=ED=9B=88?= Date: Thu, 26 Sep 2024 16:29:51 +0900 Subject: [PATCH 13/18] Revert "[JAMES-3696] improves flaky tests from ManageableMailQueueContract" This reverts commit 1705d89822ac5f7f8abbca187bcf87cbae9b008a. --- .../DelayedManageableMailQueueContract.java | 10 +- .../api/ManageableMailQueueContract.java | 113 ++++++++---------- .../queue/pulsar/PulsarMailQueueTest.java | 26 ++-- 3 files changed, 74 insertions(+), 75 deletions(-) diff --git a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/DelayedManageableMailQueueContract.java b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/DelayedManageableMailQueueContract.java index 782b2078997..442c42af220 100644 --- a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/DelayedManageableMailQueueContract.java +++ b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/DelayedManageableMailQueueContract.java @@ -123,11 +123,11 @@ default void deletedDelayedMessagesShouldNotBeBrowseable() throws Exception { getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, RECIPIENT2.asString()); - Awaitility.await().untilAsserted(() -> - assertThat(getManageableMailQueue().browse()).toIterable() - .extracting(mail -> mail.getMail().getName()) - .containsExactly("name2") - ); + awaitRemove(); + + assertThat(getManageableMailQueue().browse()).toIterable() + .extracting(mail -> mail.getMail().getName()) + .containsExactly("name2"); } @Test diff --git a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java index c174fe6bc56..cae74f4d775 100644 --- a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java +++ b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java @@ -34,7 +34,6 @@ import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; -import java.util.stream.IntStream; import java.util.stream.Stream; import jakarta.mail.MessagingException; @@ -43,10 +42,12 @@ import org.apache.james.core.MailAddress; import org.apache.james.core.builder.MimeMessageBuilder; +import org.apache.james.junit.categories.Unstable; import org.apache.mailet.Attribute; import org.apache.mailet.Mail; import org.apache.mailet.base.MailAddressFixture; import org.awaitility.Awaitility; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; @@ -61,6 +62,10 @@ public interface ManageableMailQueueContract extends MailQueueContract { + default void awaitRemove() { + + } + ManageableMailQueue getManageableMailQueue(); @Test @@ -454,27 +459,24 @@ default void removeShouldNotFailWhenBrowsingIterating() throws Exception { @Test default void browseShouldNotFailWhenConcurrentRemoveWhenIterating() throws Exception { - // We use a large number of emails so that the probability of the remove being propagated - // through the queue is high enough that the test is not flaky. - IntStream.range(0, 100).forEach( - Throwing.intConsumer(i -> - enQueue(defaultMail() - .name("name" + i) - .build()) - ).sneakyThrow() - ); + enQueue(defaultMail() + .name("name1") + .build()); + enQueue(defaultMail() + .name("name2") + .build()); + enQueue(defaultMail() + .name("name3") + .build()); ManageableMailQueue.MailQueueIterator items = getManageableMailQueue().browse(); - items.next();// we consume 1 here + items.next(); - getManageableMailQueue().remove(ManageableMailQueue.Type.Name, "name98"); // we remove 1 here + getManageableMailQueue().remove(ManageableMailQueue.Type.Name, "name2"); + awaitRemove(); - assertThatCode( - // the remove may or may not be applied to an already started iterator - // as long as it doesn't make the iterator crash - () -> assertThat(consumeIterator(items)).isGreaterThanOrEqualTo(98).isLessThan(100) - ).doesNotThrowAnyException(); + assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); } @Test @@ -549,13 +551,13 @@ default void removeByNameShouldRemoveSpecificEmail() throws Exception { getManageableMailQueue().remove(ManageableMailQueue.Type.Name, "name2"); - Awaitility.await().untilAsserted(() -> - assertThat(getManageableMailQueue().browse()) - .toIterable() - .extracting(ManageableMailQueue.MailQueueItemView::getMail) - .extracting(Mail::getName) - .containsExactly("name1") - ); + awaitRemove(); + + assertThat(getManageableMailQueue().browse()) + .toIterable() + .extracting(ManageableMailQueue.MailQueueItemView::getMail) + .extracting(Mail::getName) + .containsExactly("name1"); } @Test @@ -571,13 +573,13 @@ default void removeBySenderShouldRemoveSpecificEmail() throws Exception { getManageableMailQueue().remove(ManageableMailQueue.Type.Sender, OTHER_AT_LOCAL.asString()); - Awaitility.await().untilAsserted(() -> - assertThat(getManageableMailQueue().browse()) - .toIterable() - .extracting(ManageableMailQueue.MailQueueItemView::getMail) - .extracting(Mail::getName) - .containsExactly("name2") - ); + awaitRemove(); + + assertThat(getManageableMailQueue().browse()) + .toIterable() + .extracting(ManageableMailQueue.MailQueueItemView::getMail) + .extracting(Mail::getName) + .containsExactly("name2"); } @Test @@ -593,13 +595,13 @@ default void removeByRecipientShouldRemoveSpecificEmail() throws Exception { getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, RECIPIENT2.asString()); - Awaitility.await().untilAsserted(() -> - assertThat(getManageableMailQueue().browse()) - .toIterable() - .extracting(ManageableMailQueue.MailQueueItemView::getMail) - .extracting(Mail::getName) - .containsExactly("name1") - ); + awaitRemove(); + + assertThat(getManageableMailQueue().browse()) + .toIterable() + .extracting(ManageableMailQueue.MailQueueItemView::getMail) + .extracting(Mail::getName) + .containsExactly("name1"); } static Stream removeByRecipientShouldRemoveSpecificEmailWhenMultipleRecipients() throws AddressException { @@ -628,13 +630,13 @@ default void removeByRecipientShouldRemoveSpecificEmailWhenMultipleRecipients(Li getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, toRemove.asString()); - Awaitility.await().untilAsserted(() -> - assertThat(getManageableMailQueue().browse()) - .toIterable() - .extracting(ManageableMailQueue.MailQueueItemView::getMail) - .extracting(Mail::getName) - .containsExactly("name2") - ); + awaitRemove(); + + assertThat(getManageableMailQueue().browse()) + .toIterable() + .extracting(ManageableMailQueue.MailQueueItemView::getMail) + .extracting(Mail::getName) + .containsExactly("name2"); } @Test @@ -691,13 +693,7 @@ default void deletedElementsShouldNotBeDequeued() throws Exception { getManageableMailQueue().remove(ManageableMailQueue.Type.Name, "name1"); - Awaitility.await().untilAsserted(() -> - assertThat(getManageableMailQueue().browse()) - .toIterable() - .extracting(ManageableMailQueue.MailQueueItemView::getMail) - .extracting(Mail::getName) - .doesNotContain("name1") - ); + awaitRemove(); assertThat(Flux.from(getManageableMailQueue().deQueue()).blockFirst().getMail().getName()) .isEqualTo("name2"); @@ -713,11 +709,7 @@ default void removeShouldNotDeleteFutureEmails() throws MessagingException { getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, MailAddressFixture.RECIPIENT1.asString()); - Awaitility.await().untilAsserted(() -> - assertThat(getManageableMailQueue().browse()) - .toIterable() - .isEmpty() - ); + awaitRemove(); enQueue(defaultMail() .name("name2") @@ -737,12 +729,7 @@ default void removeShouldNotDeleteFutureEmailsFromBrowse() throws MessagingExcep getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, MailAddressFixture.RECIPIENT1.asString()); - Awaitility.await().untilAsserted(() -> - assertThat(getManageableMailQueue().browse()) - .toIterable() - .isEmpty() - ); - + awaitRemove(); enQueue(defaultMail() .name("name2") diff --git a/server/queue/queue-pulsar/src/test/java/org/apache/james/queue/pulsar/PulsarMailQueueTest.java b/server/queue/queue-pulsar/src/test/java/org/apache/james/queue/pulsar/PulsarMailQueueTest.java index c11d0bad5f4..135210a630a 100644 --- a/server/queue/queue-pulsar/src/test/java/org/apache/james/queue/pulsar/PulsarMailQueueTest.java +++ b/server/queue/queue-pulsar/src/test/java/org/apache/james/queue/pulsar/PulsarMailQueueTest.java @@ -41,6 +41,7 @@ import org.apache.james.blob.mail.MimeMessagePartsId; import org.apache.james.blob.mail.MimeMessageStore; import org.apache.james.blob.memory.MemoryBlobStoreDAO; +import org.apache.james.junit.categories.Unstable; import org.apache.james.queue.api.DelayedMailQueueContract; import org.apache.james.queue.api.DelayedManageableMailQueueContract; import org.apache.james.queue.api.MailQueue; @@ -60,6 +61,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -69,6 +71,7 @@ import reactor.core.publisher.Flux; import scala.jdk.javaapi.OptionConverters; +@Tag(Unstable.TAG) @ExtendWith(DockerPulsarExtension.class) public class PulsarMailQueueTest implements MailQueueContract, MailQueueMetricContract, ManageableMailQueueContract, DelayedMailQueueContract, DelayedManageableMailQueueContract { @@ -107,6 +110,15 @@ void tearDown() throws Exception { pulsarClients.stop(); } + @Override + public void awaitRemove() { + try { + Thread.sleep(50); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + @Override public MailQueue getMailQueue() { return mailQueue; @@ -260,13 +272,13 @@ void removeShouldRemoveMailFromStoreWhenFilteredOut() throws Exception { //this won't delete the mail from the store until we try a dequeue getManageableMailQueue().remove(ManageableMailQueue.Type.Name, "name2"); - Awaitility.await().untilAsserted(() -> - assertThat(getManageableMailQueue().browse()) - .toIterable() - .extracting(ManageableMailQueue.MailQueueItemView::getMail) - .extracting(Mail::getName) - .containsExactly("name1", "name3") - ); + awaitRemove(); + + assertThat(getManageableMailQueue().browse()) + .toIterable() + .extracting(ManageableMailQueue.MailQueueItemView::getMail) + .extracting(Mail::getName) + .containsExactly("name1", "name3"); Flux.from(getMailQueue().deQueue()).take(2).doOnNext(Throwing.consumer(x -> x.done(MailQueue.MailQueueItem.CompletionStatus.SUCCESS))).blockLast(); Awaitility.await().untilAsserted(this::assertThatStoreIsEmpty); From d2d4235d9e86832ff2467c2bcb66179e86097474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=95=A8=ED=83=9C=ED=9B=88?= Date: Thu, 26 Sep 2024 16:29:51 +0900 Subject: [PATCH 14/18] Revert "[devscout] actually consume the iterators in ManageableMailQueueContract" This reverts commit f2a56fc64c8dce852017a2b4c48caee595025ce5. --- .../activemq/ActiveMQMailQueueBlobTest.java | 12 --------- .../api/ManageableMailQueueContract.java | 27 ++++++------------- 2 files changed, 8 insertions(+), 31 deletions(-) diff --git a/server/queue/queue-activemq/src/test/java/org/apache/james/queue/activemq/ActiveMQMailQueueBlobTest.java b/server/queue/queue-activemq/src/test/java/org/apache/james/queue/activemq/ActiveMQMailQueueBlobTest.java index cc41ceaef95..c7abe08db00 100644 --- a/server/queue/queue-activemq/src/test/java/org/apache/james/queue/activemq/ActiveMQMailQueueBlobTest.java +++ b/server/queue/queue-activemq/src/test/java/org/apache/james/queue/activemq/ActiveMQMailQueueBlobTest.java @@ -31,7 +31,6 @@ import org.apache.activemq.broker.BrokerService; import org.apache.commons.io.FileUtils; import org.apache.james.filesystem.api.FileSystem; -import org.apache.james.junit.categories.Unstable; import org.apache.james.metrics.api.GaugeRegistry; import org.apache.james.metrics.api.MetricFactory; import org.apache.james.queue.api.DelayedManageableMailQueueContract; @@ -158,17 +157,6 @@ public void delayedEmailsShouldBeDeletedWhenMixedWithOtherEmails() { } - @Tag(Unstable.TAG) - @Test - @Override - public void browseShouldNotFailWhenConcurrentClearWhenIterating() throws Exception { - // This test used to pass because the assertion did not actually check anything - // it used Iterators.consumingIterator which only wraps the underlying iterator without - // actually consuming it. When replaced with an actual consumption this implementation started - // failing. - DelayedManageableMailQueueContract.super.browseShouldNotFailWhenConcurrentClearWhenIterating(); - } - @Test void computeNextDeliveryTimestampShouldReturnLongMaxWhenOverflow() { long deliveryTimestamp = mailQueue.computeNextDeliveryTimestamp(ChronoUnit.FOREVER.getDuration()); diff --git a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java index cae74f4d775..42cb4803e0f 100644 --- a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java +++ b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java @@ -31,7 +31,6 @@ import static org.assertj.core.api.SoftAssertions.assertSoftly; import java.time.Duration; -import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -42,12 +41,10 @@ import org.apache.james.core.MailAddress; import org.apache.james.core.builder.MimeMessageBuilder; -import org.apache.james.junit.categories.Unstable; import org.apache.mailet.Attribute; import org.apache.mailet.Mail; import org.apache.mailet.base.MailAddressFixture; import org.awaitility.Awaitility; -import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; @@ -56,6 +53,7 @@ import com.github.fge.lambdas.Throwing; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterators; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -219,7 +217,7 @@ default void browseShouldNotFailWhenConcurrentDequeue() throws Exception { Flux.from(getManageableMailQueue().deQueue()); - assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); } @Test @@ -268,7 +266,7 @@ default void browseShouldNotFailWhenConcurrentDequeueWhenIterating() throws Exce Flux.from(getManageableMailQueue().deQueue()); - assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); } @Test @@ -309,7 +307,7 @@ default void browseShouldNotFailWhenConcurrentEnqueue() throws Exception { .name("name4") .build()); - assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); } @Test @@ -354,7 +352,7 @@ default void browseShouldNotFailWhenConcurrentEnqueueWhenIterating() throws Exce .name("name2") .build()); - assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); } @Test @@ -394,7 +392,7 @@ default void browseShouldNotFailWhenConcurrentClearWhenIterating() throws Except getManageableMailQueue().clear(); - assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); } @Test @@ -434,7 +432,7 @@ default void browseShouldNotFailWhenConcurrentFlushWhenIterating() throws Except getManageableMailQueue().flush(); - assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); } @Test @@ -476,7 +474,7 @@ default void browseShouldNotFailWhenConcurrentRemoveWhenIterating() throws Excep awaitRemove(); - assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); } @Test @@ -740,13 +738,4 @@ default void removeShouldNotDeleteFutureEmailsFromBrowse() throws MessagingExcep .containsExactly("name2"); } - default int consumeIterator(Iterator iterator) { - var i = 0; - while (iterator.hasNext()) { - iterator.next(); - i++; - } - return i; - } - } From 3bff8344c414bd93e81dc26dc82bebca3e356110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=95=A8=ED=83=9C=ED=9B=88?= Date: Thu, 26 Sep 2024 16:29:51 +0900 Subject: [PATCH 15/18] Revert "[JAMES-3696] fixes broken MailQueueContract test" This reverts commit 314b6a74385582bff888132fcb6ea617e0a929c5. --- .../james/queue/api/MailQueueContract.java | 37 +++++-------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/MailQueueContract.java b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/MailQueueContract.java index 32c5b865379..4430ec7d372 100644 --- a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/MailQueueContract.java +++ b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/MailQueueContract.java @@ -35,7 +35,6 @@ import java.util.EnumSet; import java.util.Iterator; import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -517,12 +516,6 @@ default void concurrentEnqueueDequeueShouldNotFail() throws Exception { .hasSize(totalDequeuedMessages); } - enum EnqueueDequeueSteps { - RETRY, - DEQUEUE, - END - } - @Test default void concurrentEnqueueDequeueWithAckNackShouldNotFail() throws Exception { MailQueue testee = getMailQueue(); @@ -533,34 +526,22 @@ default void concurrentEnqueueDequeueWithAckNackShouldNotFail() throws Exception int operationCount = 15; int totalDequeuedMessages = 50; LinkedBlockingDeque deque = new LinkedBlockingDeque<>(); - var mailStates = new ConcurrentHashMap(); Flux.from(testee.deQueue()).subscribeOn(SCHEDULER).doOnNext(deque::addFirst).subscribe(); ConcurrentTestRunner.builder() .operation((threadNumber, step) -> { if (step % 3 == 0) { - String name = "name" + threadNumber + "-" + step; - mailStates.put(name, EnqueueDequeueSteps.RETRY); testee.enQueue(defaultMail() - .name(name) + .name("name" + threadNumber + "-" + step) .build()); - } else { + } + if (step % 3 == 1) { MailQueue.MailQueueItem mailQueueItem = deque.takeLast(); - var name = mailQueueItem.getMail().getName(); - var currentState = mailStates.get(mailQueueItem.getMail().getName()); - var nextState = switch (currentState) { - case RETRY -> EnqueueDequeueSteps.DEQUEUE; - case DEQUEUE -> EnqueueDequeueSteps.END; - case END -> - throw new IllegalStateException("trying to dequeue mail " + name + " multiple times !"); - }; - mailStates.put(name, nextState); - if (currentState == EnqueueDequeueSteps.RETRY) { - mailQueueItem.done(MailQueue.MailQueueItem.CompletionStatus.RETRY); - } else { - dequeuedMails.add(mailQueueItem.getMail()); - mailQueueItem.done(MailQueue.MailQueueItem.CompletionStatus.SUCCESS); - } - + mailQueueItem.done(MailQueue.MailQueueItem.CompletionStatus.RETRY); + } + if (step % 3 == 2) { + MailQueue.MailQueueItem mailQueueItem = deque.takeLast(); + dequeuedMails.add(mailQueueItem.getMail()); + mailQueueItem.done(MailQueue.MailQueueItem.CompletionStatus.SUCCESS); } }) .threadCount(threadCount) From 2adcf28e0abb4d79eb8c9e8f9bf4987e61edb3a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=95=A8=ED=83=9C=ED=9B=88?= Date: Thu, 26 Sep 2024 16:29:51 +0900 Subject: [PATCH 16/18] Revert "[devscout] upgrades to pulsar 3.3.1" This reverts commit cbd152d50cb06d83ac6bc162a9e92ae6b7c14b4a. --- backends-common/pulsar/pom.xml | 4 ++-- .../apache/james/backends/pulsar/DockerPulsarExtension.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backends-common/pulsar/pom.xml b/backends-common/pulsar/pom.xml index 2636783b3bc..a9962887e8b 100644 --- a/backends-common/pulsar/pom.xml +++ b/backends-common/pulsar/pom.xml @@ -30,8 +30,8 @@ apache-james-backends-pulsar Apache James :: Backends Common :: Pulsar - 3.3.1 - 2.10.0 + 2.11.0 + 2.9.1 1.0.2 diff --git a/backends-common/pulsar/src/test/java/org/apache/james/backends/pulsar/DockerPulsarExtension.java b/backends-common/pulsar/src/test/java/org/apache/james/backends/pulsar/DockerPulsarExtension.java index 45fde26d889..538f5ebd836 100644 --- a/backends-common/pulsar/src/test/java/org/apache/james/backends/pulsar/DockerPulsarExtension.java +++ b/backends-common/pulsar/src/test/java/org/apache/james/backends/pulsar/DockerPulsarExtension.java @@ -57,7 +57,7 @@ private static void displayDockerLog(OutputFrame outputFrame) { @Inject public DockerPulsarExtension() { - container = new PulsarContainer(DEFAULT_IMAGE_NAME.withTag("3.3.1")) + container = new PulsarContainer(DEFAULT_IMAGE_NAME.withTag("3.3.0")) .withLogConsumer(DockerPulsarExtension::displayDockerLog); } From 64fb4c984df3a6b211f94a57a2b5bf2483e4ee28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=95=A8=ED=83=9C=ED=9B=88?= Date: Thu, 26 Sep 2024 16:29:51 +0900 Subject: [PATCH 17/18] Revert "[devscout] fix execution of unstable group" This reverts commit bca9239f7de6b5c57bb01f48a48f361dcfc5692b. --- pom.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b04784ea661..d63ed615e88 100644 --- a/pom.xml +++ b/pom.xml @@ -644,7 +644,7 @@ 3.9.3 3.1.0 3.1.1 - 3.5.0 + 3.3.0 3.5.0 2.0.3 2.3.1 @@ -3326,7 +3326,7 @@ true 1800 - none() + unstable @@ -3778,6 +3778,7 @@ maven-surefire-plugin ${maven-surefire-plugin.version} + unstable From 409d74a5db219edf84dc64e9248d640f69a57ad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=95=A8=ED=83=9C=ED=9B=88?= Date: Mon, 16 Sep 2024 14:15:35 +0900 Subject: [PATCH 18/18] Refactor `DefaultImapDecoderFactory` to Use POJO Style and Constructor Injection - Updated `DefaultImapDecoderFactory` to adhere to POJO design principles. - Replaced internal `ImapParserFactory` creation with constructor injection of `UnpooledStatusResponseFactory`. - Improved decoupling and maintainability by explicitly managing dependencies via the constructor. - Updated class and method documentation to reflect changes and clarify the role of the factory. --- backends-common/pulsar/pom.xml | 4 +- .../pulsar/DockerPulsarExtension.java | 2 +- pom.xml | 5 +- .../imap/main/DefaultImapDecoderFactory.java | 37 ++--- .../activemq/ActiveMQMailQueueBlobTest.java | 12 -- .../DelayedManageableMailQueueContract.java | 10 +- .../james/queue/api/MailQueueContract.java | 37 ++--- .../api/ManageableMailQueueContract.java | 134 +++++++----------- .../queue/pulsar/PulsarMailQueueTest.java | 26 +++- 9 files changed, 105 insertions(+), 162 deletions(-) diff --git a/backends-common/pulsar/pom.xml b/backends-common/pulsar/pom.xml index 2636783b3bc..a9962887e8b 100644 --- a/backends-common/pulsar/pom.xml +++ b/backends-common/pulsar/pom.xml @@ -30,8 +30,8 @@ apache-james-backends-pulsar Apache James :: Backends Common :: Pulsar - 3.3.1 - 2.10.0 + 2.11.0 + 2.9.1 1.0.2 diff --git a/backends-common/pulsar/src/test/java/org/apache/james/backends/pulsar/DockerPulsarExtension.java b/backends-common/pulsar/src/test/java/org/apache/james/backends/pulsar/DockerPulsarExtension.java index 45fde26d889..538f5ebd836 100644 --- a/backends-common/pulsar/src/test/java/org/apache/james/backends/pulsar/DockerPulsarExtension.java +++ b/backends-common/pulsar/src/test/java/org/apache/james/backends/pulsar/DockerPulsarExtension.java @@ -57,7 +57,7 @@ private static void displayDockerLog(OutputFrame outputFrame) { @Inject public DockerPulsarExtension() { - container = new PulsarContainer(DEFAULT_IMAGE_NAME.withTag("3.3.1")) + container = new PulsarContainer(DEFAULT_IMAGE_NAME.withTag("3.3.0")) .withLogConsumer(DockerPulsarExtension::displayDockerLog); } diff --git a/pom.xml b/pom.xml index b04784ea661..d63ed615e88 100644 --- a/pom.xml +++ b/pom.xml @@ -644,7 +644,7 @@ 3.9.3 3.1.0 3.1.1 - 3.5.0 + 3.3.0 3.5.0 2.0.3 2.3.1 @@ -3326,7 +3326,7 @@ true 1800 - none() + unstable @@ -3778,6 +3778,7 @@ maven-surefire-plugin ${maven-surefire-plugin.version} + unstable diff --git a/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java b/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java index 2fc970e303b..ecf1c2592d4 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/main/DefaultImapDecoderFactory.java @@ -19,7 +19,6 @@ package org.apache.james.imap.main; -import org.apache.james.imap.api.message.response.StatusResponseFactory; import org.apache.james.imap.decode.ImapCommandParserFactory; import org.apache.james.imap.decode.ImapDecoder; import org.apache.james.imap.decode.ImapDecoderFactory; @@ -37,40 +36,26 @@ * The creation of `ImapCommandParserFactory` is handled internally by * this factory, based on the provided `UnpooledStatusResponseFactory`. */ -public class DefaultImapDecoderFactory implements ImapDecoderFactory { +public class DefaultImapDecoderFactory implements ImapDecoderFactory { - private final T statusResponseFactory; + private final UnpooledStatusResponseFactory unpooledStatusResponseFactory; private final ImapCommandParserFactory imapCommandParserFactory; /** - * Default constructor. - * This constructor creates an instance of DefaultImapDecoderFactory with an UnpooledStatusResponseFactory as the default implementation. - * Since the type T is not known at runtime due to type erasure, we cast the new UnpooledStatusResponseFactory to T. - * It internally calls the other constructor to set up the necessary dependencies. - */ - public DefaultImapDecoderFactory() { - this((T) new UnpooledStatusResponseFactory()); - } - - /** - * Constructor that accepts a specific implementation of StatusResponseFactory. - * This constructor allows for flexibility by enabling different implementations of StatusResponseFactory to be used. + * Constructs `DefaultImapDecoderFactory` with the given + * `UnpooledStatusResponseFactory`. The `ImapCommandParserFactory` + * is created internally using the provided `UnpooledStatusResponseFactory`. * - * @param statusResponseFactory An implementation of StatusResponseFactory to be used for creating IMAP command parsers and decoders. + * @param unpooledStatusResponseFactory The factory for creating status responses. */ - public DefaultImapDecoderFactory(T statusResponseFactory) { - this.statusResponseFactory = statusResponseFactory; - this.imapCommandParserFactory = new ImapParserFactory(statusResponseFactory); + public DefaultImapDecoderFactory(UnpooledStatusResponseFactory unpooledStatusResponseFactory) { + this.unpooledStatusResponseFactory = unpooledStatusResponseFactory; + this.imapCommandParserFactory = new ImapParserFactory(unpooledStatusResponseFactory); } - /** - * Builds and returns an instance of ImapDecoder. - * This method uses the statusResponseFactory and the imapCommandParserFactory to create a new DefaultImapDecoder instance. - * - * @return A new instance of DefaultImapDecoder. - */ @Override public ImapDecoder buildImapDecoder() { - return new DefaultImapDecoder(statusResponseFactory, imapCommandParserFactory); + return new DefaultImapDecoder(unpooledStatusResponseFactory, imapCommandParserFactory); } + } diff --git a/server/queue/queue-activemq/src/test/java/org/apache/james/queue/activemq/ActiveMQMailQueueBlobTest.java b/server/queue/queue-activemq/src/test/java/org/apache/james/queue/activemq/ActiveMQMailQueueBlobTest.java index cc41ceaef95..c7abe08db00 100644 --- a/server/queue/queue-activemq/src/test/java/org/apache/james/queue/activemq/ActiveMQMailQueueBlobTest.java +++ b/server/queue/queue-activemq/src/test/java/org/apache/james/queue/activemq/ActiveMQMailQueueBlobTest.java @@ -31,7 +31,6 @@ import org.apache.activemq.broker.BrokerService; import org.apache.commons.io.FileUtils; import org.apache.james.filesystem.api.FileSystem; -import org.apache.james.junit.categories.Unstable; import org.apache.james.metrics.api.GaugeRegistry; import org.apache.james.metrics.api.MetricFactory; import org.apache.james.queue.api.DelayedManageableMailQueueContract; @@ -158,17 +157,6 @@ public void delayedEmailsShouldBeDeletedWhenMixedWithOtherEmails() { } - @Tag(Unstable.TAG) - @Test - @Override - public void browseShouldNotFailWhenConcurrentClearWhenIterating() throws Exception { - // This test used to pass because the assertion did not actually check anything - // it used Iterators.consumingIterator which only wraps the underlying iterator without - // actually consuming it. When replaced with an actual consumption this implementation started - // failing. - DelayedManageableMailQueueContract.super.browseShouldNotFailWhenConcurrentClearWhenIterating(); - } - @Test void computeNextDeliveryTimestampShouldReturnLongMaxWhenOverflow() { long deliveryTimestamp = mailQueue.computeNextDeliveryTimestamp(ChronoUnit.FOREVER.getDuration()); diff --git a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/DelayedManageableMailQueueContract.java b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/DelayedManageableMailQueueContract.java index 782b2078997..442c42af220 100644 --- a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/DelayedManageableMailQueueContract.java +++ b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/DelayedManageableMailQueueContract.java @@ -123,11 +123,11 @@ default void deletedDelayedMessagesShouldNotBeBrowseable() throws Exception { getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, RECIPIENT2.asString()); - Awaitility.await().untilAsserted(() -> - assertThat(getManageableMailQueue().browse()).toIterable() - .extracting(mail -> mail.getMail().getName()) - .containsExactly("name2") - ); + awaitRemove(); + + assertThat(getManageableMailQueue().browse()).toIterable() + .extracting(mail -> mail.getMail().getName()) + .containsExactly("name2"); } @Test diff --git a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/MailQueueContract.java b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/MailQueueContract.java index 32c5b865379..4430ec7d372 100644 --- a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/MailQueueContract.java +++ b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/MailQueueContract.java @@ -35,7 +35,6 @@ import java.util.EnumSet; import java.util.Iterator; import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -517,12 +516,6 @@ default void concurrentEnqueueDequeueShouldNotFail() throws Exception { .hasSize(totalDequeuedMessages); } - enum EnqueueDequeueSteps { - RETRY, - DEQUEUE, - END - } - @Test default void concurrentEnqueueDequeueWithAckNackShouldNotFail() throws Exception { MailQueue testee = getMailQueue(); @@ -533,34 +526,22 @@ default void concurrentEnqueueDequeueWithAckNackShouldNotFail() throws Exception int operationCount = 15; int totalDequeuedMessages = 50; LinkedBlockingDeque deque = new LinkedBlockingDeque<>(); - var mailStates = new ConcurrentHashMap(); Flux.from(testee.deQueue()).subscribeOn(SCHEDULER).doOnNext(deque::addFirst).subscribe(); ConcurrentTestRunner.builder() .operation((threadNumber, step) -> { if (step % 3 == 0) { - String name = "name" + threadNumber + "-" + step; - mailStates.put(name, EnqueueDequeueSteps.RETRY); testee.enQueue(defaultMail() - .name(name) + .name("name" + threadNumber + "-" + step) .build()); - } else { + } + if (step % 3 == 1) { MailQueue.MailQueueItem mailQueueItem = deque.takeLast(); - var name = mailQueueItem.getMail().getName(); - var currentState = mailStates.get(mailQueueItem.getMail().getName()); - var nextState = switch (currentState) { - case RETRY -> EnqueueDequeueSteps.DEQUEUE; - case DEQUEUE -> EnqueueDequeueSteps.END; - case END -> - throw new IllegalStateException("trying to dequeue mail " + name + " multiple times !"); - }; - mailStates.put(name, nextState); - if (currentState == EnqueueDequeueSteps.RETRY) { - mailQueueItem.done(MailQueue.MailQueueItem.CompletionStatus.RETRY); - } else { - dequeuedMails.add(mailQueueItem.getMail()); - mailQueueItem.done(MailQueue.MailQueueItem.CompletionStatus.SUCCESS); - } - + mailQueueItem.done(MailQueue.MailQueueItem.CompletionStatus.RETRY); + } + if (step % 3 == 2) { + MailQueue.MailQueueItem mailQueueItem = deque.takeLast(); + dequeuedMails.add(mailQueueItem.getMail()); + mailQueueItem.done(MailQueue.MailQueueItem.CompletionStatus.SUCCESS); } }) .threadCount(threadCount) diff --git a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java index c174fe6bc56..42cb4803e0f 100644 --- a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java +++ b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java @@ -31,10 +31,8 @@ import static org.assertj.core.api.SoftAssertions.assertSoftly; import java.time.Duration; -import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; -import java.util.stream.IntStream; import java.util.stream.Stream; import jakarta.mail.MessagingException; @@ -55,12 +53,17 @@ import com.github.fge.lambdas.Throwing; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterators; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; public interface ManageableMailQueueContract extends MailQueueContract { + default void awaitRemove() { + + } + ManageableMailQueue getManageableMailQueue(); @Test @@ -214,7 +217,7 @@ default void browseShouldNotFailWhenConcurrentDequeue() throws Exception { Flux.from(getManageableMailQueue().deQueue()); - assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); } @Test @@ -263,7 +266,7 @@ default void browseShouldNotFailWhenConcurrentDequeueWhenIterating() throws Exce Flux.from(getManageableMailQueue().deQueue()); - assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); } @Test @@ -304,7 +307,7 @@ default void browseShouldNotFailWhenConcurrentEnqueue() throws Exception { .name("name4") .build()); - assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); } @Test @@ -349,7 +352,7 @@ default void browseShouldNotFailWhenConcurrentEnqueueWhenIterating() throws Exce .name("name2") .build()); - assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); } @Test @@ -389,7 +392,7 @@ default void browseShouldNotFailWhenConcurrentClearWhenIterating() throws Except getManageableMailQueue().clear(); - assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); } @Test @@ -429,7 +432,7 @@ default void browseShouldNotFailWhenConcurrentFlushWhenIterating() throws Except getManageableMailQueue().flush(); - assertThatCode(() -> consumeIterator(items)).doesNotThrowAnyException(); + assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); } @Test @@ -454,27 +457,24 @@ default void removeShouldNotFailWhenBrowsingIterating() throws Exception { @Test default void browseShouldNotFailWhenConcurrentRemoveWhenIterating() throws Exception { - // We use a large number of emails so that the probability of the remove being propagated - // through the queue is high enough that the test is not flaky. - IntStream.range(0, 100).forEach( - Throwing.intConsumer(i -> - enQueue(defaultMail() - .name("name" + i) - .build()) - ).sneakyThrow() - ); + enQueue(defaultMail() + .name("name1") + .build()); + enQueue(defaultMail() + .name("name2") + .build()); + enQueue(defaultMail() + .name("name3") + .build()); ManageableMailQueue.MailQueueIterator items = getManageableMailQueue().browse(); - items.next();// we consume 1 here + items.next(); - getManageableMailQueue().remove(ManageableMailQueue.Type.Name, "name98"); // we remove 1 here + getManageableMailQueue().remove(ManageableMailQueue.Type.Name, "name2"); + awaitRemove(); - assertThatCode( - // the remove may or may not be applied to an already started iterator - // as long as it doesn't make the iterator crash - () -> assertThat(consumeIterator(items)).isGreaterThanOrEqualTo(98).isLessThan(100) - ).doesNotThrowAnyException(); + assertThatCode(() -> Iterators.consumingIterator(items)).doesNotThrowAnyException(); } @Test @@ -549,13 +549,13 @@ default void removeByNameShouldRemoveSpecificEmail() throws Exception { getManageableMailQueue().remove(ManageableMailQueue.Type.Name, "name2"); - Awaitility.await().untilAsserted(() -> - assertThat(getManageableMailQueue().browse()) - .toIterable() - .extracting(ManageableMailQueue.MailQueueItemView::getMail) - .extracting(Mail::getName) - .containsExactly("name1") - ); + awaitRemove(); + + assertThat(getManageableMailQueue().browse()) + .toIterable() + .extracting(ManageableMailQueue.MailQueueItemView::getMail) + .extracting(Mail::getName) + .containsExactly("name1"); } @Test @@ -571,13 +571,13 @@ default void removeBySenderShouldRemoveSpecificEmail() throws Exception { getManageableMailQueue().remove(ManageableMailQueue.Type.Sender, OTHER_AT_LOCAL.asString()); - Awaitility.await().untilAsserted(() -> - assertThat(getManageableMailQueue().browse()) - .toIterable() - .extracting(ManageableMailQueue.MailQueueItemView::getMail) - .extracting(Mail::getName) - .containsExactly("name2") - ); + awaitRemove(); + + assertThat(getManageableMailQueue().browse()) + .toIterable() + .extracting(ManageableMailQueue.MailQueueItemView::getMail) + .extracting(Mail::getName) + .containsExactly("name2"); } @Test @@ -593,13 +593,13 @@ default void removeByRecipientShouldRemoveSpecificEmail() throws Exception { getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, RECIPIENT2.asString()); - Awaitility.await().untilAsserted(() -> - assertThat(getManageableMailQueue().browse()) - .toIterable() - .extracting(ManageableMailQueue.MailQueueItemView::getMail) - .extracting(Mail::getName) - .containsExactly("name1") - ); + awaitRemove(); + + assertThat(getManageableMailQueue().browse()) + .toIterable() + .extracting(ManageableMailQueue.MailQueueItemView::getMail) + .extracting(Mail::getName) + .containsExactly("name1"); } static Stream removeByRecipientShouldRemoveSpecificEmailWhenMultipleRecipients() throws AddressException { @@ -628,13 +628,13 @@ default void removeByRecipientShouldRemoveSpecificEmailWhenMultipleRecipients(Li getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, toRemove.asString()); - Awaitility.await().untilAsserted(() -> - assertThat(getManageableMailQueue().browse()) - .toIterable() - .extracting(ManageableMailQueue.MailQueueItemView::getMail) - .extracting(Mail::getName) - .containsExactly("name2") - ); + awaitRemove(); + + assertThat(getManageableMailQueue().browse()) + .toIterable() + .extracting(ManageableMailQueue.MailQueueItemView::getMail) + .extracting(Mail::getName) + .containsExactly("name2"); } @Test @@ -691,13 +691,7 @@ default void deletedElementsShouldNotBeDequeued() throws Exception { getManageableMailQueue().remove(ManageableMailQueue.Type.Name, "name1"); - Awaitility.await().untilAsserted(() -> - assertThat(getManageableMailQueue().browse()) - .toIterable() - .extracting(ManageableMailQueue.MailQueueItemView::getMail) - .extracting(Mail::getName) - .doesNotContain("name1") - ); + awaitRemove(); assertThat(Flux.from(getManageableMailQueue().deQueue()).blockFirst().getMail().getName()) .isEqualTo("name2"); @@ -713,11 +707,7 @@ default void removeShouldNotDeleteFutureEmails() throws MessagingException { getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, MailAddressFixture.RECIPIENT1.asString()); - Awaitility.await().untilAsserted(() -> - assertThat(getManageableMailQueue().browse()) - .toIterable() - .isEmpty() - ); + awaitRemove(); enQueue(defaultMail() .name("name2") @@ -737,12 +727,7 @@ default void removeShouldNotDeleteFutureEmailsFromBrowse() throws MessagingExcep getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, MailAddressFixture.RECIPIENT1.asString()); - Awaitility.await().untilAsserted(() -> - assertThat(getManageableMailQueue().browse()) - .toIterable() - .isEmpty() - ); - + awaitRemove(); enQueue(defaultMail() .name("name2") @@ -753,13 +738,4 @@ default void removeShouldNotDeleteFutureEmailsFromBrowse() throws MessagingExcep .containsExactly("name2"); } - default int consumeIterator(Iterator iterator) { - var i = 0; - while (iterator.hasNext()) { - iterator.next(); - i++; - } - return i; - } - } diff --git a/server/queue/queue-pulsar/src/test/java/org/apache/james/queue/pulsar/PulsarMailQueueTest.java b/server/queue/queue-pulsar/src/test/java/org/apache/james/queue/pulsar/PulsarMailQueueTest.java index c11d0bad5f4..135210a630a 100644 --- a/server/queue/queue-pulsar/src/test/java/org/apache/james/queue/pulsar/PulsarMailQueueTest.java +++ b/server/queue/queue-pulsar/src/test/java/org/apache/james/queue/pulsar/PulsarMailQueueTest.java @@ -41,6 +41,7 @@ import org.apache.james.blob.mail.MimeMessagePartsId; import org.apache.james.blob.mail.MimeMessageStore; import org.apache.james.blob.memory.MemoryBlobStoreDAO; +import org.apache.james.junit.categories.Unstable; import org.apache.james.queue.api.DelayedMailQueueContract; import org.apache.james.queue.api.DelayedManageableMailQueueContract; import org.apache.james.queue.api.MailQueue; @@ -60,6 +61,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -69,6 +71,7 @@ import reactor.core.publisher.Flux; import scala.jdk.javaapi.OptionConverters; +@Tag(Unstable.TAG) @ExtendWith(DockerPulsarExtension.class) public class PulsarMailQueueTest implements MailQueueContract, MailQueueMetricContract, ManageableMailQueueContract, DelayedMailQueueContract, DelayedManageableMailQueueContract { @@ -107,6 +110,15 @@ void tearDown() throws Exception { pulsarClients.stop(); } + @Override + public void awaitRemove() { + try { + Thread.sleep(50); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + @Override public MailQueue getMailQueue() { return mailQueue; @@ -260,13 +272,13 @@ void removeShouldRemoveMailFromStoreWhenFilteredOut() throws Exception { //this won't delete the mail from the store until we try a dequeue getManageableMailQueue().remove(ManageableMailQueue.Type.Name, "name2"); - Awaitility.await().untilAsserted(() -> - assertThat(getManageableMailQueue().browse()) - .toIterable() - .extracting(ManageableMailQueue.MailQueueItemView::getMail) - .extracting(Mail::getName) - .containsExactly("name1", "name3") - ); + awaitRemove(); + + assertThat(getManageableMailQueue().browse()) + .toIterable() + .extracting(ManageableMailQueue.MailQueueItemView::getMail) + .extracting(Mail::getName) + .containsExactly("name1", "name3"); Flux.from(getMailQueue().deQueue()).take(2).doOnNext(Throwing.consumer(x -> x.done(MailQueue.MailQueueItem.CompletionStatus.SUCCESS))).blockLast(); Awaitility.await().untilAsserted(this::assertThatStoreIsEmpty);