You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2020/02/20 03:27:42 UTC

[james-project] branch master updated (cf844df -> 6d395f1)

This is an automated email from the ASF dual-hosted git repository.

btellier pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git.


    from cf844df  JAMES-3057 Separate the rename and create mailbox logic in CassandraMailboxMapper
     new 6da6be0  JAMES-3056 Prepare the setUp for CassandraMailboxManager consistency test
     new c6e725c  JAMES-3056 Consistency test on failing creations
     new d86297c  JAMES-3056 Consistency test on failing renaming
     new c31b059  JAMES-3056 Consistency test on failing deleting
     new e9e38a5  JAMES-3056 Distinct on StoreMailboxManager.list()
     new 01c2b29  JAMES-2884 ADR for new JMAP specifications adoption
     new 89791f0  JAMES-3066 Refactor Alias related parts
     new 6d395f1  JAMES-3066 Add allowed From headers webadmin endpoint in the changelog

The 8 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CHANGELOG.md                                       |   1 +
 .../mail/CassandraMailboxPathDAOImpl.java          |   2 +-
 .../CassandraMailboxManagerConsistencyTest.java    | 770 +++++++++++++++++++++
 .../cassandra/CassandraMailboxManagerProvider.java |  25 +
 .../mailbox/cassandra/mail/utils/GuiceUtils.java   |  34 +-
 .../james/mailbox/store/StoreMailboxManager.java   |   1 +
 .../apache/james/webadmin/routes/UserRoutes.java   |   6 +-
 .../apache/james/webadmin/service/UserService.java |   2 +-
 .../james/webadmin/routes/UserRoutesTest.java      |  16 +-
 src/adr/0018-jmap-new-specs.md                     |  65 ++
 10 files changed, 900 insertions(+), 22 deletions(-)
 create mode 100644 mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java
 create mode 100644 src/adr/0018-jmap-new-specs.md


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[james-project] 08/08: JAMES-3066 Add allowed From headers webadmin endpoint in the changelog

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 6d395f1624364756756f00ca378b6baaa9baa2e3
Author: Gautier DI FOLCO <gd...@linagora.com>
AuthorDate: Tue Feb 18 10:40:09 2020 +0100

    JAMES-3066 Add allowed From headers webadmin endpoint in the changelog
---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9ad4ae1..93c2163 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ of tasks being currently executed.
 - JAMES-2563 Health check for ElasticSearch
 - JAMES-2904 Authentication and SSL support for Cassandra backend
 - JAMES-2904 Authentication and SSL support for ElasticSearch backend
+- JAMES-3066 Add "allowed From headers" webadmin endpoint
 
 ### Changed
 - Multiple changes have been made to enhance ElasticSearch performance:


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[james-project] 03/08: JAMES-3056 Consistency test on failing renaming

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit d86297c827fc63ea2e2ae22ec06bdcea11b77c59
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Thu Feb 13 17:03:05 2020 +0700

    JAMES-3056 Consistency test on failing renaming
---
 .../CassandraMailboxManagerConsistencyTest.java    | 160 +++++++++++++++++++++
 1 file changed, 160 insertions(+)

diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java
index bd6f93f..e2977fc 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java
@@ -58,6 +58,7 @@ class CassandraMailboxManagerConsistencyTest {
 
     private static final Username USER = Username.of("user");
     private static final String INBOX = "INBOX";
+    private static final String INBOX_RENAMED = "INBOX_RENAMED";
 
     @RegisterExtension
     static CassandraClusterExtension cassandra = new CassandraClusterExtension(MailboxAggregateModule.MODULE_WITH_QUOTA);
@@ -66,6 +67,7 @@ class CassandraMailboxManagerConsistencyTest {
     private MailboxSession mailboxSession;
 
     private MailboxPath inboxPath;
+    private MailboxPath inboxPathRenamed;
     private MailboxQuery.UserBound allMailboxesSearchQuery;
 
     private CassandraMailboxDAO mailboxDAO;
@@ -92,6 +94,7 @@ class CassandraMailboxManagerConsistencyTest {
         mailboxSession = testee.createSystemSession(USER);
 
         inboxPath = MailboxPath.forUser(USER, INBOX);
+        inboxPathRenamed = MailboxPath.forUser(USER, INBOX_RENAMED);
         allMailboxesSearchQuery = MailboxQuery.builder()
             .userAndNamespaceFrom(inboxPath)
             .expression(Wildcard.INSTANCE)
@@ -211,6 +214,163 @@ class CassandraMailboxManagerConsistencyTest {
         }
     }
 
+    @Nested
+    class FailsOnRename {
+
+        @Test
+        void renameShouldBeConsistentWhenMailboxDaoFails() throws Exception {
+            MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                .get();
+
+            doReturn(Mono.error(new RuntimeException("mock exception")))
+                .when(mailboxDAO)
+                .save(any(Mailbox.class));
+
+            doQuietly(() -> testee.renameMailbox(inboxPath, inboxPathRenamed, mailboxSession));
+
+            SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                    .hasOnlyOneElementSatisfying(mailboxMetaData -> {
+                        softly.assertThat(mailboxMetaData.getId()).isEqualTo(inboxId);
+                        softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPath);
+                    });
+                softly.assertThat(testee.list(mailboxSession))
+                    .containsExactly(inboxPath);
+            }));
+        }
+
+        @Test
+        void renameShouldBeConsistentWhenMailboxPathDaoFails() throws Exception {
+            MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                .get();
+
+            doReturn(Mono.error(new RuntimeException("mock exception")))
+                .when(mailboxPathV2DAO)
+                .save(any(MailboxPath.class), any(CassandraId.class));
+
+            doQuietly(() -> testee.renameMailbox(inboxPath, inboxPathRenamed, mailboxSession));
+
+            SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                    .hasOnlyOneElementSatisfying(mailboxMetaData -> {
+                        softly.assertThat(mailboxMetaData.getId()).isEqualTo(inboxId);
+                        softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPath);
+                    });
+                softly.assertThat(testee.list(mailboxSession))
+                    .containsExactly(inboxPath);
+            }));
+        }
+
+        @Disabled("JAMES-3056 cannot create a new mailbox because 'INBOX_RENAMED' already exists")
+        @Test
+        void createNewMailboxAfterAFailedRenameShouldCreateThatMailboxWhenMailboxDaoFails() throws Exception {
+            MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                .get();
+
+            doReturn(Mono.error(new RuntimeException("mock exception")))
+                .doCallRealMethod()
+                .when(mailboxDAO)
+                .save(any(Mailbox.class));
+
+            doQuietly(() -> testee.renameMailbox(inboxPath, inboxPathRenamed, mailboxSession));
+            MailboxId newMailboxId = testee.createMailbox(inboxPathRenamed, mailboxSession)
+                .get();
+
+            SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                    .hasSize(2)
+                    .anySatisfy(mailboxMetaData -> {
+                        softly.assertThat(mailboxMetaData.getId()).isEqualTo(inboxId);
+                        softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPath);
+                    })
+                    .anySatisfy(mailboxMetaData -> {
+                        softly.assertThat(mailboxMetaData.getId()).isEqualTo(newMailboxId);
+                        softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPathRenamed);
+                    });
+                softly.assertThat(testee.list(mailboxSession))
+                    .containsExactlyInAnyOrder(inboxPath, inboxPathRenamed);
+            }));
+        }
+
+        @Test
+        void createNewMailboxAfterAFailedRenameShouldCreateThatMailboxWhenMailboxPathDaoFails() throws Exception {
+            MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                .get();
+
+            doReturn(Mono.error(new RuntimeException("mock exception")))
+                .doCallRealMethod()
+                .when(mailboxPathV2DAO)
+                .save(any(MailboxPath.class), any(CassandraId.class));
+
+            doQuietly(() -> testee.renameMailbox(inboxPath, inboxPathRenamed, mailboxSession));
+            MailboxId newMailboxId = testee.createMailbox(inboxPathRenamed, mailboxSession)
+                .get();
+
+            SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                    .hasSize(2)
+                    .anySatisfy(mailboxMetaData -> {
+                        softly.assertThat(mailboxMetaData.getId()).isEqualTo(inboxId);
+                        softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPath);
+                    })
+                    .anySatisfy(mailboxMetaData -> {
+                        softly.assertThat(mailboxMetaData.getId()).isEqualTo(newMailboxId);
+                        softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPathRenamed);
+                    });
+                softly.assertThat(testee.list(mailboxSession))
+                    .containsExactlyInAnyOrder(inboxPath, inboxPathRenamed);
+            }));
+        }
+
+        @Disabled("JAMES-3056 creating mailbox returns an empty Optional")
+        @Test
+        void deleteMailboxAfterAFailedRenameShouldCreateThatMailboxWhenMailboxDaoFails() throws Exception {
+            MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                .get();
+
+            doReturn(Mono.error(new RuntimeException("mock exception")))
+                .doCallRealMethod()
+                .when(mailboxDAO)
+                .save(any(Mailbox.class));
+
+            doQuietly(() -> testee.renameMailbox(inboxPath, inboxPathRenamed, mailboxSession));
+            testee.deleteMailbox(inboxId, mailboxSession);
+            assertThat(testee.createMailbox(inboxPathRenamed, mailboxSession))
+                .isNotEmpty();
+        }
+
+        @Test
+        void deleteMailboxAfterAFailedRenameShouldCreateThatMailboxWhenMailboxPathDaoFails() throws Exception {
+            MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                .get();
+
+            doReturn(Mono.error(new RuntimeException("mock exception")))
+                .doCallRealMethod()
+                .when(mailboxPathV2DAO)
+                .save(any(MailboxPath.class), any(CassandraId.class));
+
+            doQuietly(() -> testee.renameMailbox(inboxPath, inboxPathRenamed, mailboxSession));
+            testee.deleteMailbox(inboxId, mailboxSession);
+            MailboxId newMailboxId = testee.createMailbox(inboxPathRenamed, mailboxSession)
+                .get();
+
+            SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                    .hasOnlyOneElementSatisfying(mailboxMetaData -> {
+                        softly.assertThat(mailboxMetaData.getId()).isEqualTo(newMailboxId);
+                        softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPathRenamed);
+                    });
+                softly.assertThat(testee.list(mailboxSession))
+                    .containsExactlyInAnyOrder(inboxPathRenamed);
+            }));
+        }
+    }
+
+    @Nested
+    class FailsOnDelete {
+        // TODO
+    }
+
     private void doQuietly(ThrowingRunnable runnable) {
         try {
             runnable.run();


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[james-project] 07/08: JAMES-3066 Refactor Alias related parts

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 89791f0f38f32ba2fd90576e2aaaea5d29c74cd6
Author: Gautier DI FOLCO <gd...@linagora.com>
AuthorDate: Tue Feb 18 10:10:07 2020 +0100

    JAMES-3066 Refactor Alias related parts
---
 .../org/apache/james/webadmin/routes/UserRoutes.java     |  6 +++---
 .../org/apache/james/webadmin/service/UserService.java   |  2 +-
 .../org/apache/james/webadmin/routes/UserRoutesTest.java | 16 +++++++---------
 3 files changed, 11 insertions(+), 13 deletions(-)

diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/UserRoutes.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/UserRoutes.java
index d0b78ee..cd8699d 100644
--- a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/UserRoutes.java
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/UserRoutes.java
@@ -219,12 +219,12 @@ public class UserRoutes implements Routes {
         Username username = extractUsername(request);
 
         try {
-            if (!userService.existUser(username)) {
-                LOGGER.info("allowed From headers on an unknown user: '{}", username.asString());
+            if (!userService.userExists(username)) {
+                LOGGER.info("Allowed FROM headers requested for an unknown user: '{}", username.asString());
                 throw ErrorResponder.builder()
                     .statusCode(HttpStatus.NOT_FOUND_404)
                     .type(ErrorType.INVALID_ARGUMENT)
-                    .message("user '" + username.asString() + "' does not exist")
+                    .message(String.format("user '%s' does not exist", username.asString()))
                     .haltError();
             }
 
diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/UserService.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/UserService.java
index cd36afb..a263234 100644
--- a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/UserService.java
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/UserService.java
@@ -66,7 +66,7 @@ public class UserService {
         upsert(user, username, password);
     }
 
-    public boolean existUser(Username username) throws UsersRepositoryException {
+    public boolean userExists(Username username) throws UsersRepositoryException {
         try {
             return usersRepository.contains(usersRepository.getUser(username.asMailAddress()));
         } catch (AddressException e) {
diff --git a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/UserRoutesTest.java b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/UserRoutesTest.java
index db1dba9..7936d4f 100644
--- a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/UserRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/UserRoutesTest.java
@@ -122,23 +122,21 @@ class UserRoutesTest {
 
         @Override
         public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
-            return parameterContext.getParameter()
-                .getType()
-                .isAssignableFrom(UsersRepository.class)
-                || parameterContext.getParameter()
-                .getType()
-                .isAssignableFrom(RecipientRewriteTable.class);
+            Class<?> parameterType = parameterContext.getParameter().getType();
+            return parameterType.isAssignableFrom(UsersRepository.class)
+                || parameterType.isAssignableFrom(RecipientRewriteTable.class);
         }
 
         @Override
         public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
-            if (parameterContext.getParameter().getType().isAssignableFrom(UsersRepository.class)) {
+            Class<?> parameterType = parameterContext.getParameter().getType();
+            if (parameterType.isAssignableFrom(UsersRepository.class)) {
                 return usersRepository;
             }
-            if (parameterContext.getParameter().getType().isAssignableFrom(RecipientRewriteTable.class)) {
+            if (parameterType.isAssignableFrom(RecipientRewriteTable.class)) {
                 return recipientRewriteTable;
             }
-            throw new RuntimeException("Unknown parameter type: " + parameterContext.getParameter().getType());
+            throw new RuntimeException("Unknown parameter type: " + parameterType);
         }
 
         private WebAdminServer startServer(UsersRepository usersRepository) {


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[james-project] 02/08: JAMES-3056 Consistency test on failing creations

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit c6e725c2394d8757197211a3b132b65b7eaf822a
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Thu Feb 13 14:53:26 2020 +0700

    JAMES-3056 Consistency test on failing creations
---
 .../CassandraMailboxManagerConsistencyTest.java    | 168 ++++++++++++++++++++-
 1 file changed, 165 insertions(+), 3 deletions(-)

diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java
index 84c2ad3..bd6f93f 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java
@@ -18,35 +18,68 @@
  ****************************************************************/
 package org.apache.james.mailbox.cassandra;
 
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 
 import org.apache.james.backends.cassandra.CassandraClusterExtension;
 import org.apache.james.backends.cassandra.init.CassandraTypesProvider;
 import org.apache.james.backends.cassandra.utils.CassandraUtils;
+import org.apache.james.core.Username;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.cassandra.ids.CassandraId;
 import org.apache.james.mailbox.cassandra.mail.CassandraMailboxDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMailboxPathDAOImpl;
 import org.apache.james.mailbox.cassandra.mail.CassandraMailboxPathV2DAO;
 import org.apache.james.mailbox.cassandra.mail.MailboxAggregateModule;
+import org.apache.james.mailbox.model.Mailbox;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.search.MailboxQuery;
+import org.apache.james.mailbox.model.search.Wildcard;
 import org.apache.james.mailbox.store.PreDeletionHooks;
+import org.assertj.core.api.SoftAssertions;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 import com.datastax.driver.core.Session;
+import com.github.fge.lambdas.Throwing;
+import com.github.fge.lambdas.runnable.ThrowingRunnable;
+
+import reactor.core.publisher.Mono;
 
 class CassandraMailboxManagerConsistencyTest {
+
+    private static final Username USER = Username.of("user");
+    private static final String INBOX = "INBOX";
+
     @RegisterExtension
     static CassandraClusterExtension cassandra = new CassandraClusterExtension(MailboxAggregateModule.MODULE_WITH_QUOTA);
 
     private CassandraMailboxManager testee;
+    private MailboxSession mailboxSession;
+
+    private MailboxPath inboxPath;
+    private MailboxQuery.UserBound allMailboxesSearchQuery;
+
+    private CassandraMailboxDAO mailboxDAO;
+    private CassandraMailboxPathDAOImpl mailboxPathDAO;
+    private CassandraMailboxPathV2DAO mailboxPathV2DAO;
 
     @BeforeEach
     void setUp() {
         Session session = cassandra.getCassandraCluster().getConf();
         CassandraTypesProvider typesProvider = cassandra.getCassandraCluster().getTypesProvider();
 
-        CassandraMailboxDAO mailboxDAO = spy(new CassandraMailboxDAO(session, typesProvider));
-        CassandraMailboxPathDAOImpl mailboxPathDAO = spy(new CassandraMailboxPathDAOImpl(session, typesProvider));
-        CassandraMailboxPathV2DAO mailboxPathV2DAO = spy(new CassandraMailboxPathV2DAO(session, CassandraUtils.WITH_DEFAULT_CONFIGURATION));
+        mailboxDAO = spy(new CassandraMailboxDAO(session, typesProvider));
+        mailboxPathDAO = spy(new CassandraMailboxPathDAOImpl(session, typesProvider));
+        mailboxPathV2DAO = spy(new CassandraMailboxPathV2DAO(session, CassandraUtils.WITH_DEFAULT_CONFIGURATION));
 
         testee = CassandraMailboxManagerProvider.provideMailboxManager(
             session,
@@ -55,5 +88,134 @@ class CassandraMailboxManagerConsistencyTest {
             binder -> binder.bind(CassandraMailboxDAO.class).toInstance(mailboxDAO),
             binder -> binder.bind(CassandraMailboxPathDAOImpl.class).toInstance(mailboxPathDAO),
             binder -> binder.bind(CassandraMailboxPathV2DAO.class).toInstance(mailboxPathV2DAO));
+
+        mailboxSession = testee.createSystemSession(USER);
+
+        inboxPath = MailboxPath.forUser(USER, INBOX);
+        allMailboxesSearchQuery = MailboxQuery.builder()
+            .userAndNamespaceFrom(inboxPath)
+            .expression(Wildcard.INSTANCE)
+            .build()
+            .asUserBound();
+    }
+
+    @Nested
+    class FailsOnCreate {
+
+        @Test
+        void createMailboxShouldBeConsistentWhenMailboxDaoFails() {
+            doReturn(Mono.error(new RuntimeException("mock exception")))
+                .when(mailboxDAO)
+                .save(any(Mailbox.class));
+
+            doQuietly(() -> testee.createMailbox(inboxPath, mailboxSession));
+
+            SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                    .isEmpty();
+                softly.assertThat(testee.list(mailboxSession))
+                    .isEmpty();
+            }));
+        }
+
+        @Test
+        void createMailboxShouldBeConsistentWhenMailboxPathDaoFails() {
+            doReturn(Mono.error(new RuntimeException("mock exception")))
+                .when(mailboxPathV2DAO)
+                .save(eq(inboxPath), isA(CassandraId.class));
+
+            doQuietly(() -> testee.createMailbox(inboxPath, mailboxSession));
+
+            SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                    .isEmpty();
+                softly.assertThat(testee.list(mailboxSession))
+                    .isEmpty();
+            }));
+        }
+
+        @Disabled("JAMES-3056 createMailbox() return an empty Optional")
+        @Test
+        void createMailboxAfterAFailedCreationShouldCreateTheMailboxWhenMailboxDaoFails() throws Exception {
+            doReturn(Mono.error(new RuntimeException("mock exception")))
+                .doCallRealMethod()
+                .when(mailboxDAO)
+                .save(any(Mailbox.class));
+
+            doQuietly(() -> testee.createMailbox(inboxPath, mailboxSession));
+
+            assertThat(testee.createMailbox(inboxPath, mailboxSession))
+                .isNotEmpty();
+        }
+
+        @Test
+        void createMailboxAfterAFailedCreationShouldCreateTheMailboxWhenMailboxPathDaoFails() throws Exception {
+            doReturn(Mono.error(new RuntimeException("mock exception")))
+                .doCallRealMethod()
+                .when(mailboxPathV2DAO)
+                .save(eq(inboxPath), isA(CassandraId.class));
+
+            doQuietly(() -> testee.createMailbox(inboxPath, mailboxSession));
+
+            MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                .get();
+
+            SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                    .hasOnlyOneElementSatisfying(mailboxMetaData -> {
+                        softly.assertThat(mailboxMetaData.getId()).isEqualTo(inboxId);
+                        softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPath);
+                    });
+                softly.assertThat(testee.list(mailboxSession))
+                    .containsExactly(inboxPath);
+            }));
+        }
+
+        @Disabled("JAMES-3056 createMailbox() return an empty Optional")
+        @Test
+        void createMailboxAfterDeletingShouldCreateTheMailboxWhenMailboxDaoFails() throws Exception {
+            doReturn(Mono.error(new RuntimeException("mock exception")))
+                .doCallRealMethod()
+                .when(mailboxDAO)
+                .save(any(Mailbox.class));
+
+            doQuietly(() -> testee.createMailbox(inboxPath, mailboxSession));
+            doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
+
+            assertThat(testee.createMailbox(inboxPath, mailboxSession))
+                .isNotEmpty();
+        }
+
+        @Test
+        void createMailboxAfterDeletingShouldCreateTheMailboxWhenMailboxPathDaoFails() throws Exception {
+            doReturn(Mono.error(new RuntimeException("mock exception")))
+                .doCallRealMethod()
+                .when(mailboxPathV2DAO)
+                .save(eq(inboxPath), isA(CassandraId.class));
+
+            doQuietly(() -> testee.createMailbox(inboxPath, mailboxSession));
+            doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
+
+            MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                .get();
+
+            SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                    .hasOnlyOneElementSatisfying(mailboxMetaData -> {
+                        softly.assertThat(mailboxMetaData.getId()).isEqualTo(inboxId);
+                        softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPath);
+                    });
+                softly.assertThat(testee.list(mailboxSession))
+                    .containsExactly(inboxPath);
+            }));
+        }
+    }
+
+    private void doQuietly(ThrowingRunnable runnable) {
+        try {
+            runnable.run();
+        } catch (Throwable th) {
+            // ignore
+        }
     }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[james-project] 05/08: JAMES-3056 Distinct on StoreMailboxManager.list()

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit e9e38a5ef2c1e1a9c3193d570db9fb8c78b6d8c9
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Fri Feb 14 10:20:49 2020 +0700

    JAMES-3056 Distinct on StoreMailboxManager.list()
---
 .../CassandraMailboxManagerConsistencyTest.java    | 115 +++++++++++++++++++--
 .../james/mailbox/store/StoreMailboxManager.java   |   1 +
 2 files changed, 110 insertions(+), 6 deletions(-)

diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java
index 2278c2a..5ec2154 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java
@@ -103,7 +103,7 @@ class CassandraMailboxManagerConsistencyTest {
     }
 
     @Nested
-    class FailsOnCreate {
+    class FailuresDuringCreation {
 
         @Test
         void createMailboxShouldBeConsistentWhenMailboxDaoFails() {
@@ -137,7 +137,7 @@ class CassandraMailboxManagerConsistencyTest {
             }));
         }
 
-        @Disabled("JAMES-3056 createMailbox() return an empty Optional")
+        @Disabled("JAMES-3056 createMailbox() doesn't return mailboxId while it's supposed to")
         @Test
         void createMailboxAfterAFailedCreationShouldCreateTheMailboxWhenMailboxDaoFails() throws Exception {
             doReturn(Mono.error(new RuntimeException("mock exception")))
@@ -174,7 +174,7 @@ class CassandraMailboxManagerConsistencyTest {
             }));
         }
 
-        @Disabled("JAMES-3056 createMailbox() return an empty Optional")
+        @Disabled("JAMES-3056 createMailbox() doesn't return mailboxId while it's supposed to")
         @Test
         void createMailboxAfterDeletingShouldCreateTheMailboxWhenMailboxDaoFails() throws Exception {
             doReturn(Mono.error(new RuntimeException("mock exception")))
@@ -215,7 +215,7 @@ class CassandraMailboxManagerConsistencyTest {
     }
 
     @Nested
-    class FailsOnRename {
+    class FailuresDuringRenaming {
 
         @Test
         void renameShouldBeConsistentWhenMailboxDaoFails() throws Exception {
@@ -367,7 +367,7 @@ class CassandraMailboxManagerConsistencyTest {
     }
 
     @Nested
-    class FailsOnDelete {
+    class FailuresOnDeletion {
 
         @Nested
         class DeleteOnce {
@@ -463,6 +463,110 @@ class CassandraMailboxManagerConsistencyTest {
         }
 
         @Nested
+        class DeleteOnceThenCreate {
+
+            @Test
+            void createMailboxShouldCreateWhenMailboxDaoFailsOnDeleteByPath() throws Exception {
+                testee.createMailbox(inboxPath, mailboxSession);
+
+                doReturn(Mono.error(new RuntimeException("mock exception")))
+                    .doCallRealMethod()
+                    .when(mailboxDAO)
+                    .delete(any(CassandraId.class));
+
+                doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
+                MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                    .get();
+
+                SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                    softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                        .hasOnlyOneElementSatisfying(mailboxMetaData -> {
+                            softly.assertThat(mailboxMetaData.getId()).isEqualTo(inboxId);
+                            softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPath);
+                        });
+                    softly.assertThat(testee.list(mailboxSession))
+                        .containsExactly(inboxPath);
+                }));
+            }
+
+            @Test
+            void createMailboxShouldCreateWhenMailboxDaoFailsOnDeleteById() throws Exception {
+                MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                    .get();
+
+                doReturn(Mono.error(new RuntimeException("mock exception")))
+                    .doCallRealMethod()
+                    .when(mailboxDAO)
+                    .delete(any(CassandraId.class));
+
+                doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
+                MailboxId inboxNewId = testee.createMailbox(inboxPath, mailboxSession)
+                    .get();
+
+                SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                    softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                        .hasOnlyOneElementSatisfying(mailboxMetaData -> {
+                            softly.assertThat(mailboxMetaData.getId()).isEqualTo(inboxNewId);
+                            softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPath);
+                        });
+                    softly.assertThat(testee.list(mailboxSession))
+                        .containsExactly(inboxPath);
+                }));
+            }
+
+            @Disabled("JAMES-3056 cannot create because mailbox already exists")
+            @Test
+            void createMailboxShouldCreateWhenMailboxPathDaoFailsOnDeleteByPath() throws Exception {
+                testee.createMailbox(inboxPath, mailboxSession);
+
+                doReturn(Mono.error(new RuntimeException("mock exception")))
+                    .doCallRealMethod()
+                    .when(mailboxPathV2DAO)
+                    .delete(inboxPath);
+
+                doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
+                MailboxId inboxNewId = testee.createMailbox(inboxPath, mailboxSession)
+                    .get();
+
+                SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                    softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                        .hasOnlyOneElementSatisfying(mailboxMetaData -> {
+                            softly.assertThat(mailboxMetaData.getId()).isEqualTo(inboxNewId);
+                            softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPath);
+                        });
+                    softly.assertThat(testee.list(mailboxSession))
+                        .containsExactly(inboxPath);
+                }));
+            }
+
+            @Disabled("JAMES-3056 cannot create because mailbox already exists")
+            @Test
+            void createMailboxShouldCreateWhenMailboxPathDaoFailsOnDeleteById() throws Exception {
+                MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                    .get();
+
+                doReturn(Mono.error(new RuntimeException("mock exception")))
+                    .doCallRealMethod()
+                    .when(mailboxPathV2DAO)
+                    .delete(inboxPath);
+
+                doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
+                MailboxId inboxNewId = testee.createMailbox(inboxPath, mailboxSession)
+                    .get();
+
+                SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                    softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                        .hasOnlyOneElementSatisfying(mailboxMetaData -> {
+                            softly.assertThat(mailboxMetaData.getId()).isEqualTo(inboxNewId);
+                            softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPath);
+                        });
+                    softly.assertThat(testee.list(mailboxSession))
+                        .containsExactly(inboxPath);
+                }));
+            }
+        }
+
+        @Nested
         class DeleteTwice {
 
             @Disabled("JAMES-3056 list() returns one element with inboxPath")
@@ -552,7 +656,6 @@ class CassandraMailboxManagerConsistencyTest {
         @Nested
         class DeleteTwiceThenCreate {
 
-            @Disabled("JAMES-3056 list() returns two element with inboxPath being duplicated")
             @Test
             void createMailboxShouldCreateWhenMailboxDaoFailsOnDeleteByPath() throws Exception {
                 testee.createMailbox(inboxPath, mailboxSession);
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
index f023f5f..31f7f7b 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
@@ -742,6 +742,7 @@ public class StoreMailboxManager implements MailboxManager {
             .list()
             .stream()
             .map(Mailbox::generateAssociatedPath)
+            .distinct()
             .collect(Guavate.toImmutableList());
     }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[james-project] 01/08: JAMES-3056 Prepare the setUp for CassandraMailboxManager consistency test

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 6da6be056e2c8be9aa7764f062ea4104f8b9fec8
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Thu Feb 13 12:01:13 2020 +0700

    JAMES-3056 Prepare the setUp for CassandraMailboxManager consistency test
    
    It requires me to inject some spied DAOs into the Mapper layer. I know
    it's suck. But that's not the priority now.
---
 .../mail/CassandraMailboxPathDAOImpl.java          |  2 +-
 .../CassandraMailboxManagerConsistencyTest.java    | 59 ++++++++++++++++++++++
 .../cassandra/CassandraMailboxManagerProvider.java | 25 +++++++++
 .../mailbox/cassandra/mail/utils/GuiceUtils.java   | 34 ++++++++++---
 4 files changed, 111 insertions(+), 9 deletions(-)

diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAOImpl.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAOImpl.java
index a10d94c..34f49ae 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAOImpl.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAOImpl.java
@@ -82,7 +82,7 @@ public class CassandraMailboxPathDAOImpl implements CassandraMailboxPathDAO {
     }
 
     @VisibleForTesting
-     CassandraMailboxPathDAOImpl(Session session, CassandraTypesProvider typesProvider) {
+    public CassandraMailboxPathDAOImpl(Session session, CassandraTypesProvider typesProvider) {
         this(session, typesProvider, CassandraUtils.WITH_DEFAULT_CONFIGURATION);
     }
 
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java
new file mode 100644
index 0000000..84c2ad3
--- /dev/null
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java
@@ -0,0 +1,59 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+package org.apache.james.mailbox.cassandra;
+
+import static org.mockito.Mockito.spy;
+
+import org.apache.james.backends.cassandra.CassandraClusterExtension;
+import org.apache.james.backends.cassandra.init.CassandraTypesProvider;
+import org.apache.james.backends.cassandra.utils.CassandraUtils;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxPathDAOImpl;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxPathV2DAO;
+import org.apache.james.mailbox.cassandra.mail.MailboxAggregateModule;
+import org.apache.james.mailbox.store.PreDeletionHooks;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import com.datastax.driver.core.Session;
+
+class CassandraMailboxManagerConsistencyTest {
+    @RegisterExtension
+    static CassandraClusterExtension cassandra = new CassandraClusterExtension(MailboxAggregateModule.MODULE_WITH_QUOTA);
+
+    private CassandraMailboxManager testee;
+
+    @BeforeEach
+    void setUp() {
+        Session session = cassandra.getCassandraCluster().getConf();
+        CassandraTypesProvider typesProvider = cassandra.getCassandraCluster().getTypesProvider();
+
+        CassandraMailboxDAO mailboxDAO = spy(new CassandraMailboxDAO(session, typesProvider));
+        CassandraMailboxPathDAOImpl mailboxPathDAO = spy(new CassandraMailboxPathDAOImpl(session, typesProvider));
+        CassandraMailboxPathV2DAO mailboxPathV2DAO = spy(new CassandraMailboxPathV2DAO(session, CassandraUtils.WITH_DEFAULT_CONFIGURATION));
+
+        testee = CassandraMailboxManagerProvider.provideMailboxManager(
+            session,
+            typesProvider,
+            PreDeletionHooks.NO_PRE_DELETION_HOOK,
+            binder -> binder.bind(CassandraMailboxDAO.class).toInstance(mailboxDAO),
+            binder -> binder.bind(CassandraMailboxPathDAOImpl.class).toInstance(mailboxPathDAO),
+            binder -> binder.bind(CassandraMailboxPathV2DAO.class).toInstance(mailboxPathV2DAO));
+    }
+}
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerProvider.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerProvider.java
index 11d9588..8e07ab4 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerProvider.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerProvider.java
@@ -20,11 +20,13 @@
 package org.apache.james.mailbox.cassandra;
 
 import org.apache.james.backends.cassandra.init.CassandraTypesProvider;
+import org.apache.james.backends.cassandra.init.configuration.CassandraConfiguration;
 import org.apache.james.mailbox.acl.GroupMembershipResolver;
 import org.apache.james.mailbox.acl.MailboxACLResolver;
 import org.apache.james.mailbox.acl.SimpleGroupMembershipResolver;
 import org.apache.james.mailbox.acl.UnionMailboxACLResolver;
 import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
+import org.apache.james.mailbox.cassandra.mail.utils.GuiceUtils;
 import org.apache.james.mailbox.cassandra.quota.CassandraCurrentQuotaManager;
 import org.apache.james.mailbox.cassandra.quota.CassandraGlobalMaxQuotaDao;
 import org.apache.james.mailbox.cassandra.quota.CassandraPerDomainMaxQuotaDao;
@@ -32,6 +34,7 @@ import org.apache.james.mailbox.cassandra.quota.CassandraPerUserMaxQuotaDao;
 import org.apache.james.mailbox.cassandra.quota.CassandraPerUserMaxQuotaManager;
 import org.apache.james.mailbox.events.InVMEventBus;
 import org.apache.james.mailbox.events.delivery.InVmEventDelivery;
+import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.quota.QuotaRootResolver;
 import org.apache.james.mailbox.store.Authenticator;
 import org.apache.james.mailbox.store.Authorizator;
@@ -53,11 +56,26 @@ import org.apache.james.mailbox.store.search.SimpleMessageSearchIndex;
 import org.apache.james.metrics.tests.RecordingMetricFactory;
 
 import com.datastax.driver.core.Session;
+import com.google.inject.Module;
 
 public class CassandraMailboxManagerProvider {
     private static final int LIMIT_ANNOTATIONS = 3;
     private static final int LIMIT_ANNOTATION_SIZE = 30;
 
+    static CassandraMailboxManager provideMailboxManager(Session session, CassandraTypesProvider cassandraTypesProvider,
+                                                         PreDeletionHooks preDeletionHooks, Module... overriedGuiceModules) {
+        CassandraMessageId.Factory messageIdFactory = new CassandraMessageId.Factory();
+
+        CassandraMailboxSessionMapperFactory mapperFactory = GuiceUtils.testInjector(session,
+                cassandraTypesProvider,
+                messageIdFactory,
+                CassandraConfiguration.DEFAULT_CONFIGURATION,
+                overriedGuiceModules)
+            .getInstance(CassandraMailboxSessionMapperFactory.class);
+
+        return provideMailboxManager(session, preDeletionHooks, mapperFactory, messageIdFactory);
+    }
+
     public static CassandraMailboxManager provideMailboxManager(Session session, CassandraTypesProvider cassandraTypesProvider,
                                                                 PreDeletionHooks preDeletionHooks) {
         CassandraMessageId.Factory messageIdFactory = new CassandraMessageId.Factory();
@@ -67,6 +85,13 @@ public class CassandraMailboxManagerProvider {
             cassandraTypesProvider,
             messageIdFactory);
 
+        return provideMailboxManager(session, preDeletionHooks, mapperFactory, messageIdFactory);
+    }
+
+    private static CassandraMailboxManager provideMailboxManager(Session session,
+                                                                PreDeletionHooks preDeletionHooks,
+                                                                CassandraMailboxSessionMapperFactory mapperFactory,
+                                                                MessageId.Factory messageIdFactory) {
         MailboxACLResolver aclResolver = new UnionMailboxACLResolver();
         GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver();
         MessageParser messageParser = new MessageParser();
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/utils/GuiceUtils.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/utils/GuiceUtils.java
index e3218c3..4d010ca 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/utils/GuiceUtils.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/utils/GuiceUtils.java
@@ -36,6 +36,7 @@ import org.apache.james.mailbox.model.MessageId;
 import com.datastax.driver.core.Session;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
+import com.google.inject.Module;
 import com.google.inject.name.Names;
 import com.google.inject.util.Modules;
 
@@ -49,18 +50,35 @@ public class GuiceUtils {
         return testInjector(session, typesProvider, messageIdFactory, configuration);
     }
 
-    public static Injector testInjector(Session session, CassandraTypesProvider typesProvider, CassandraMessageId.Factory messageIdFactory, CassandraConfiguration configuration) {
+    public static Injector testInjector(Session session, CassandraTypesProvider typesProvider,
+                                        CassandraMessageId.Factory messageIdFactory,
+                                        CassandraConfiguration configuration, Module... guiceModules) {
         return Guice.createInjector(
             Modules.combine(
-                binder -> binder.bind(MessageId.Factory.class).toInstance(messageIdFactory),
-                binder -> binder.bind(BlobId.Factory.class).toInstance(new HashBlobId.Factory()),
-                binder -> binder.bind(BlobStore.class).to(CassandraBlobStore.class).in(SINGLETON),
-                binder -> binder.bind(CassandraDumbBlobStore.class).in(SINGLETON),
+                Modules.combine(commonModules(session, typesProvider, messageIdFactory, configuration)),
+                Modules.combine(guiceModules)));
+    }
+
+    public static Injector testInjector(Session session, CassandraTypesProvider typesProvider,
+                                        CassandraMessageId.Factory messageIdFactory,
+                                        CassandraConfiguration configuration) {
+        return Guice.createInjector(
+            commonModules(session, typesProvider, messageIdFactory, configuration));
+    }
+
+    private static Module commonModules(Session session, CassandraTypesProvider typesProvider,
+                                        CassandraMessageId.Factory messageIdFactory,
+                                        CassandraConfiguration configuration) {
+        return Modules.combine(
+            binder -> binder.bind(MessageId.Factory.class).toInstance(messageIdFactory),
+            binder -> binder.bind(BlobId.Factory.class).toInstance(new HashBlobId.Factory()),
+            binder -> binder.bind(BlobStore.class).to(CassandraBlobStore.class),
+            binder -> binder.bind(CassandraDumbBlobStore.class).in(SINGLETON),
                 binder -> binder.bind(BucketName.class)
                     .annotatedWith(Names.named(CassandraDumbBlobStore.DEFAULT_BUCKET))
                     .toInstance(BucketName.DEFAULT),
-                binder -> binder.bind(Session.class).toInstance(session),
-                binder -> binder.bind(CassandraTypesProvider.class).toInstance(typesProvider),
-                binder -> binder.bind(CassandraConfiguration.class).toInstance(configuration)));
+            binder -> binder.bind(Session.class).toInstance(session),
+            binder -> binder.bind(CassandraTypesProvider.class).toInstance(typesProvider),
+            binder -> binder.bind(CassandraConfiguration.class).toInstance(configuration));
     }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[james-project] 04/08: JAMES-3056 Consistency test on failing deleting

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit c31b05994f7a74d1ff2c3d334d4d690e550e15df
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Fri Feb 14 10:20:06 2020 +0700

    JAMES-3056 Consistency test on failing deleting
---
 .../CassandraMailboxManagerConsistencyTest.java    | 288 ++++++++++++++++++++-
 1 file changed, 287 insertions(+), 1 deletion(-)

diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java
index e2977fc..2278c2a 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java
@@ -368,7 +368,293 @@ class CassandraMailboxManagerConsistencyTest {
 
     @Nested
     class FailsOnDelete {
-        // TODO
+
+        @Nested
+        class DeleteOnce {
+            @Disabled("JAMES-3056 allMailboxesSearchQuery returns empty list")
+            @Test
+            void deleteMailboxByPathShouldBeConsistentWhenMailboxDaoFails() throws Exception {
+                MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                    .get();
+
+                doReturn(Mono.error(new RuntimeException("mock exception")))
+                    .when(mailboxDAO)
+                    .delete(any(CassandraId.class));
+
+                doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
+
+                SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                    softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                        .hasOnlyOneElementSatisfying(mailboxMetaData -> {
+                            softly.assertThat(mailboxMetaData.getId()).isEqualTo(inboxId);
+                            softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPath);
+                        });
+                    softly.assertThat(testee.list(mailboxSession))
+                        .containsExactly(inboxPath);
+                }));
+            }
+
+            @Disabled("JAMES-3056 allMailboxesSearchQuery returns empty list")
+            @Test
+            void deleteMailboxByIdShouldBeConsistentWhenMailboxDaoFails() throws Exception {
+                MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                    .get();
+
+                doReturn(Mono.error(new RuntimeException("mock exception")))
+                    .when(mailboxDAO)
+                    .delete(any(CassandraId.class));
+
+                doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
+
+                SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                    softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                        .hasOnlyOneElementSatisfying(mailboxMetaData -> {
+                            softly.assertThat(mailboxMetaData.getId()).isEqualTo(inboxId);
+                            softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPath);
+                        });
+                    softly.assertThat(testee.list(mailboxSession))
+                        .containsExactly(inboxPath);
+                }));
+            }
+
+            @Test
+            void deleteMailboxByPathShouldBeConsistentWhenMailboxPathDaoFails() throws Exception {
+                MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                    .get();
+
+                doReturn(Mono.error(new RuntimeException("mock exception")))
+                    .when(mailboxPathV2DAO)
+                    .delete(inboxPath);
+
+                doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
+
+                SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                    softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                        .hasOnlyOneElementSatisfying(mailboxMetaData -> {
+                            softly.assertThat(mailboxMetaData.getId()).isEqualTo(inboxId);
+                            softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPath);
+                        });
+                    softly.assertThat(testee.list(mailboxSession))
+                        .containsExactly(inboxPath);
+                }));
+            }
+
+            @Test
+            void deleteMailboxByIdShouldBeConsistentWhenMailboxPathDaoFails() throws Exception {
+                MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                    .get();
+
+                doReturn(Mono.error(new RuntimeException("mock exception")))
+                    .when(mailboxPathV2DAO)
+                    .delete(inboxPath);
+
+                doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
+
+                SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                    softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                        .hasOnlyOneElementSatisfying(mailboxMetaData -> {
+                            softly.assertThat(mailboxMetaData.getId()).isEqualTo(inboxId);
+                            softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPath);
+                        });
+                    softly.assertThat(testee.list(mailboxSession))
+                        .containsExactly(inboxPath);
+                }));
+            }
+        }
+
+        @Nested
+        class DeleteTwice {
+
+            @Disabled("JAMES-3056 list() returns one element with inboxPath")
+            @Test
+            void deleteMailboxByPathShouldDeleteWhenMailboxDaoFails() throws Exception {
+                testee.createMailbox(inboxPath, mailboxSession);
+
+                doReturn(Mono.error(new RuntimeException("mock exception")))
+                    .doCallRealMethod()
+                    .when(mailboxDAO)
+                    .delete(any(CassandraId.class));
+
+                doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
+                doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
+
+                SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                    softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                        .isEmpty();
+                    softly.assertThat(testee.list(mailboxSession))
+                        .isEmpty();
+                }));
+            }
+
+            @Test
+            void deleteMailboxByIdShouldDeleteWhenMailboxDaoFails() throws Exception {
+                MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                    .get();
+
+                doReturn(Mono.error(new RuntimeException("mock exception")))
+                    .doCallRealMethod()
+                    .when(mailboxDAO)
+                    .delete(any(CassandraId.class));
+
+                doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
+                doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
+
+                SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                    softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                        .isEmpty();
+                    softly.assertThat(testee.list(mailboxSession))
+                        .isEmpty();
+                }));
+            }
+
+            @Test
+            void deleteMailboxByPathShouldDeleteWhenMailboxPathDaoFails() throws Exception {
+                testee.createMailbox(inboxPath, mailboxSession);
+
+                doReturn(Mono.error(new RuntimeException("mock exception")))
+                    .doCallRealMethod()
+                    .when(mailboxPathV2DAO)
+                    .delete(inboxPath);
+
+                doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
+                doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
+
+                SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                    softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                        .isEmpty();
+                    softly.assertThat(testee.list(mailboxSession))
+                        .isEmpty();
+                }));
+            }
+
+            @Test
+            void deleteMailboxByIdShouldDeleteWhenMailboxPathDaoFails() throws Exception {
+                MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                    .get();
+
+                doReturn(Mono.error(new RuntimeException("mock exception")))
+                    .doCallRealMethod()
+                    .when(mailboxPathV2DAO)
+                    .delete(inboxPath);
+
+                doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
+                doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
+
+                SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                    softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                        .isEmpty();
+                    softly.assertThat(testee.list(mailboxSession))
+                        .isEmpty();
+                }));
+            }
+        }
+
+        @Nested
+        class DeleteTwiceThenCreate {
+
+            @Disabled("JAMES-3056 list() returns two element with inboxPath being duplicated")
+            @Test
+            void createMailboxShouldCreateWhenMailboxDaoFailsOnDeleteByPath() throws Exception {
+                testee.createMailbox(inboxPath, mailboxSession);
+
+                doReturn(Mono.error(new RuntimeException("mock exception")))
+                    .doCallRealMethod()
+                    .when(mailboxDAO)
+                    .delete(any(CassandraId.class));
+
+                doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
+                doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
+                MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                    .get();
+
+                SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                    softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                        .hasOnlyOneElementSatisfying(mailboxMetaData -> {
+                            softly.assertThat(mailboxMetaData.getId()).isEqualTo(inboxId);
+                            softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPath);
+                        });
+                    softly.assertThat(testee.list(mailboxSession))
+                        .containsExactly(inboxPath);
+                }));
+            }
+
+            @Test
+            void createMailboxShouldCreateWhenMailboxDaoFailsOnDeleteById() throws Exception {
+                MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                    .get();
+
+                doReturn(Mono.error(new RuntimeException("mock exception")))
+                    .doCallRealMethod()
+                    .when(mailboxDAO)
+                    .delete(any(CassandraId.class));
+
+                doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
+                doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
+                MailboxId inboxNewId = testee.createMailbox(inboxPath, mailboxSession)
+                    .get();
+
+                SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                    softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                        .hasOnlyOneElementSatisfying(mailboxMetaData -> {
+                            softly.assertThat(mailboxMetaData.getId()).isEqualTo(inboxNewId);
+                            softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPath);
+                        });
+                    softly.assertThat(testee.list(mailboxSession))
+                        .containsExactly(inboxPath);
+                }));
+            }
+
+            @Test
+            void createMailboxShouldCreateWhenMailboxPathDaoFailsOnDeleteByPath() throws Exception {
+                testee.createMailbox(inboxPath, mailboxSession);
+
+                doReturn(Mono.error(new RuntimeException("mock exception")))
+                    .doCallRealMethod()
+                    .when(mailboxPathV2DAO)
+                    .delete(inboxPath);
+
+                doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
+                doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
+                MailboxId inboxNewId = testee.createMailbox(inboxPath, mailboxSession)
+                    .get();
+
+                SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                    softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                        .hasOnlyOneElementSatisfying(mailboxMetaData -> {
+                            softly.assertThat(mailboxMetaData.getId()).isEqualTo(inboxNewId);
+                            softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPath);
+                        });
+                    softly.assertThat(testee.list(mailboxSession))
+                        .containsExactly(inboxPath);
+                }));
+            }
+
+            @Test
+            void createMailboxShouldCreateWhenMailboxPathDaoFailsOnDeleteById() throws Exception {
+                MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
+                    .get();
+
+                doReturn(Mono.error(new RuntimeException("mock exception")))
+                    .doCallRealMethod()
+                    .when(mailboxPathV2DAO)
+                    .delete(inboxPath);
+
+                doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
+                doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
+                MailboxId inboxNewId = testee.createMailbox(inboxPath, mailboxSession)
+                    .get();
+
+                SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                    softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
+                        .hasOnlyOneElementSatisfying(mailboxMetaData -> {
+                            softly.assertThat(mailboxMetaData.getId()).isEqualTo(inboxNewId);
+                            softly.assertThat(mailboxMetaData.getPath()).isEqualTo(inboxPath);
+                        });
+                    softly.assertThat(testee.list(mailboxSession))
+                        .containsExactly(inboxPath);
+                }));
+            }
+        }
     }
 
     private void doQuietly(ThrowingRunnable runnable) {


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[james-project] 06/08: JAMES-2884 ADR for new JMAP specifications adoption

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 01c2b29c6daa682851a6917b60427990eba5e9ff
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Thu Feb 6 17:32:37 2020 +0700

    JAMES-2884 ADR for new JMAP specifications adoption
---
 src/adr/0018-jmap-new-specs.md | 65 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/src/adr/0018-jmap-new-specs.md b/src/adr/0018-jmap-new-specs.md
new file mode 100644
index 0000000..7ba3c73
--- /dev/null
+++ b/src/adr/0018-jmap-new-specs.md
@@ -0,0 +1,65 @@
+# 18. New JMAP specifications adoption
+
+Date: 2020-02-06
+
+## Status
+
+Proposed
+
+## Context
+
+Historically, James has been an early adopter for the JMAP specification, and a first partial implementation was conducted when JMAP was just a draft. 
+But with time, the IETF draft went with radical changes and the community could not keep this implementation up to date with the spec changes.
+
+As of summer 2019, JMAP core ([RFC 8620](https://tools.ietf.org/html/rfc8620)) and JMAP mail ([RFC 8621](https://tools.ietf.org/html/rfc8621)) have been officially published. 
+Thus we should implement these new specifications to claim JMAP support.
+
+We need to keep in mind though that part of the community actively relies on the actual 'draft' implementation of JMAP existing in James. 
+
+## Decision
+
+We decided to do as follow:
+
+* Rename packages `server/protocols/jmap*` and guice packages `server/container/guice/protocols/jmap*` to `jmap-draft`. `JMAPServer` should also be renamed to `JMAPDraftServer` (this has already been contributed [here](https://github.com/apache/james-project/pull/164), thanks to @cketti).
+* Port `jmap-draft` to be served with a reactive technology
+* Implement a JMAP meta project to select the JMAP version specified in the accept header and map it to the correct implementation
+* Create a new `jmap` package
+* Implement the new JMAP request structure with the [echo](https://jmap.io/spec-core.html#the-coreecho-method) method
+* Implement authentication and session of the new JMAP protocol
+* Implement protocol-level error handling
+* Duplicate and adapt existing mailbox methods of `jmap-draft` to `jmap`
+* Duplicate and adapt existing email methods of `jmap-draft` to `jmap`
+* Duplicate and adapt existing vacation methods of `jmap-draft` to `jmap`
+* Support uploads/downloads
+
+Then when we finish to port our existing methods to the new JMAP specifications, we can implement these new features:
+
+* Accounts
+* Identities
+* EmailSubmission
+* Push and queryChanges
+* Threads
+
+We decided to support `jmap` on top of memory-guice and distributed-james products for now. 
+
+We should ensure no changes is done to `jmap-draft` while implementing the new `jmap` one.
+
+Regarding the versioning in the accept headers:
+
+* `Accept: application/json;jmapVersion=draft` would redirect to `jmap-draft`
+* `Accept: application/json;jmapVersion=rfc-8620` would redirect to `jmap`
+* When the `jmapVersion` is omitted, we will redirect first towards `jmap-draft`, then to `jmap`
+when `jmap-draft` becomes deprecated
+
+It's worth mentioning as well that we took the decision of writing this new implementation using `Scala`.
+
+## Consequences
+
+* Each feature implemented will respect the final specifications of JMAP
+* Getting missing features that are necessary to deliver a better mailing experience with James, like push, query changes and threads 
+* Separating the current implementation from the new one will allow existing `jmap-draft` clients to smoothly transition to `jmap`, then trigger the classic "deprecation-then-removal" process. 
+
+## References
+
+* A discussion around this already happened in September 2019 on the server-dev mailinglist: [JMAP protocol: Implementing RFC-8620 & RFC-8621](https://www.mail-archive.com/server-dev@james.apache.org/msg62072.html)
+* JIRA: [JAMES-2884](https://issues.apache.org/jira/browse/JAMES-2884)
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org