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 e31d7894b0dd820553b3dfc86f96536bd02e2dc9
Author: LanKhuat <khuatdang...@gmail.com>
AuthorDate: Tue Apr 14 10:24:28 2020 +0700

    JAMES-3143 SolveMessageInconsistenciesService/Test
---
 .../cassandra/mail/CassandraMessageIdDAO.java      |   2 +-
 .../mail/CassandraMessageIdToImapUidDAO.java       |   2 +-
 .../task/SolveMessageInconsistenciesService.java   | 131 +++++++
 .../SolveMessageInconsistenciesServiceTest.java    | 398 +++++++++++++++++++++
 4 files changed, 531 insertions(+), 2 deletions(-)

diff --git 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAO.java
 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAO.java
index c2342b3..d15e121 100644
--- 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAO.java
+++ 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAO.java
@@ -149,7 +149,7 @@ public class CassandraMessageIdDAO {
     }
 
     private PreparedStatement prepareList(Session session) {
-        return session.prepare(select(new String[] {MESSAGE_ID, MAILBOX_ID, 
IMAP_UID})
+        return session.prepare(select(FIELDS)
             .from(TABLE_NAME));
     }
 
diff --git 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java
 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java
index 83099ba..91cf3e3 100644
--- 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java
+++ 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java
@@ -139,7 +139,7 @@ public class CassandraMessageIdToImapUidDAO {
     }
 
     private PreparedStatement prepareList(Session session) {
-        return session.prepare(select(new String[] {MESSAGE_ID, MAILBOX_ID, 
IMAP_UID}).from(TABLE_NAME));
+        return session.prepare(select(FIELDS).from(TABLE_NAME));
     }
 
     private PreparedStatement prepareSelect(Session session) {
diff --git 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMessageInconsistenciesService.java
 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMessageInconsistenciesService.java
new file mode 100644
index 0000000..f67ff73
--- /dev/null
+++ 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMessageInconsistenciesService.java
@@ -0,0 +1,131 @@
+/****************************************************************
+ * 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.mail.task;
+
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import org.apache.james.mailbox.cassandra.ids.CassandraId;
+import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
+import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdToImapUidDAO;
+import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
+import org.apache.james.task.Task;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class SolveMessageInconsistenciesService {
+    public static final Logger LOGGER = 
LoggerFactory.getLogger(SolveMessageInconsistenciesService.class);
+
+    private final CassandraMessageIdToImapUidDAO idToImapUidDAO;
+    private final CassandraMessageIdDAO messageIdDAO;
+
+    @Inject
+    SolveMessageInconsistenciesService(CassandraMessageIdToImapUidDAO 
idToImapUidDAO, CassandraMessageIdDAO messageIdDAO) {
+        this.idToImapUidDAO = idToImapUidDAO;
+        this.messageIdDAO = messageIdDAO;
+    }
+
+    Mono<Task.Result> fixMessageInconsistencies() {
+        return Flux.concat(
+            fixMessageIdInconsistencies(),
+            fixImapUidInconsistencies())
+            .reduce(Task.Result.COMPLETED, Task::combine);
+    }
+
+    private Mono<Task.Result> fixMessageIdInconsistencies() {
+        return idToImapUidDAO.retrieveAllMessages()
+            .concatMap(this::fetchAndFixMessageId)
+            .reduce(Task.Result.COMPLETED, Task::combine);
+    }
+
+    private Mono<Task.Result> 
fetchAndFixMessageId(ComposedMessageIdWithMetaData message) {
+        return idToImapUidDAO.retrieve((CassandraMessageId) 
message.getComposedMessageId().getMessageId(), Optional.of((CassandraId) 
message.getComposedMessageId().getMailboxId()))
+            .single()
+            .flatMap(upToDateMessage -> messageIdDAO.retrieve((CassandraId) 
upToDateMessage.getComposedMessageId().getMailboxId(), 
upToDateMessage.getComposedMessageId().getUid())
+                .flatMap(Mono::justOrEmpty)
+                .flatMap(fetchedFromMessageId -> 
fixWhenMessageFoundInMessageId(upToDateMessage, fetchedFromMessageId)))
+            .switchIfEmpty(fixWhenMessageNotFoundInMessageId(message));
+    }
+
+    private Mono<Task.Result> 
fixWhenMessageFoundInMessageId(ComposedMessageIdWithMetaData 
messageFromImapUid, ComposedMessageIdWithMetaData messageFromMessageId) {
+        return Mono.fromCallable(() -> 
messageFromImapUid.equals(messageFromMessageId))
+            .flatMap(isEqual -> {
+                if (isEqual) {
+                    return Mono.just(Task.Result.COMPLETED);
+                }
+
+                return messageIdDAO.updateMetadata(messageFromImapUid)
+                    .then(Mono.just(Task.Result.COMPLETED))
+                    .onErrorResume(error -> {
+                        LOGGER.error("Error when fixing inconsistency for 
message: {}", messageFromImapUid, error);
+                        return Mono.just(Task.Result.PARTIAL);
+                    });
+            });
+    }
+
+    private Mono<Task.Result> 
fixWhenMessageNotFoundInMessageId(ComposedMessageIdWithMetaData message) {
+        return messageIdDAO.insert(message)
+            .then(Mono.just(Task.Result.COMPLETED))
+            .onErrorResume(error -> {
+                LOGGER.error("Error when fixing inconsistency for message: 
{}", message, error);
+                return Mono.just(Task.Result.PARTIAL);
+            });
+    }
+
+    @VisibleForTesting
+    Mono<Task.Result> fixImapUidInconsistencies() {
+        return messageIdDAO.retrieveAllMessages()
+            .concatMap(message -> process(message))
+            .reduce(Task.Result.COMPLETED, Task::combine);
+    }
+
+    private Mono<Task.Result> process(ComposedMessageIdWithMetaData message) {
+        return messageIdDAO.retrieve((CassandraId) 
message.getComposedMessageId().getMailboxId(), 
message.getComposedMessageId().getUid())
+            .flatMap(Mono::justOrEmpty)
+            .flatMap(this::fixWhenMessageFound)
+            .switchIfEmpty(Mono.just(Task.Result.COMPLETED));
+    }
+
+    private Mono<Task.Result> 
fixWhenMessageFound(ComposedMessageIdWithMetaData message) {
+        return idToImapUidDAO.retrieve((CassandraMessageId) 
message.getComposedMessageId().getMessageId(), Optional.of((CassandraId) 
message.getComposedMessageId().getMailboxId()))
+            .flatMap(uidRecord -> {
+                if (uidRecord.equals(message)) {
+                    return Mono.just(Task.Result.COMPLETED);
+                }
+
+                return messageIdDAO.updateMetadata(uidRecord)
+                    .then(Mono.just(Task.Result.COMPLETED));
+            })
+            .switchIfEmpty(messageIdDAO.delete((CassandraId) 
message.getComposedMessageId().getMailboxId(), 
message.getComposedMessageId().getUid())
+                .then(Mono.just(Task.Result.COMPLETED)))
+            .single()
+            .onErrorResume(error -> {
+                LOGGER.error("Error when fixing inconsistency for message {}", 
message, error);
+                return Mono.just(Task.Result.PARTIAL);
+            });
+    }
+}
diff --git 
a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/SolveMessageInconsistenciesServiceTest.java
 
b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/SolveMessageInconsistenciesServiceTest.java
new file mode 100644
index 0000000..8bb845b
--- /dev/null
+++ 
b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/SolveMessageInconsistenciesServiceTest.java
@@ -0,0 +1,398 @@
+/****************************************************************
+ * 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.mail.task;
+
+import static org.apache.james.backends.cassandra.Scenario.Builder.fail;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.doReturn;
+
+import java.util.Optional;
+
+import javax.mail.Flags;
+
+import org.apache.james.backends.cassandra.CassandraCluster;
+import org.apache.james.backends.cassandra.CassandraClusterExtension;
+import org.apache.james.backends.cassandra.components.CassandraModule;
+import 
org.apache.james.backends.cassandra.versions.CassandraSchemaVersionModule;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.ModSeq;
+import org.apache.james.mailbox.cassandra.ids.CassandraId;
+import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
+import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdToImapUidDAO;
+import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule;
+import org.apache.james.mailbox.model.ComposedMessageId;
+import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
+import org.apache.james.task.Task;
+import org.assertj.core.api.SoftAssertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import reactor.core.publisher.Mono;
+
+public class SolveMessageInconsistenciesServiceTest {
+
+    private static final CassandraId MAILBOX_ID = CassandraId.timeBased();
+    private static final CassandraMessageId MESSAGE_ID_1 = new 
CassandraMessageId.Factory().fromString("d2bee791-7e63-11ea-883c-95b84008f979");
+    private static final CassandraMessageId MESSAGE_ID_2 = new 
CassandraMessageId.Factory().fromString("eeeeeeee-7e63-11ea-883c-95b84008f979");
+    private static final MessageUid MESSAGE_UID_1 = MessageUid.of(1L);
+    private static final MessageUid MESSAGE_UID_2 = MessageUid.of(2L);
+    private static final ModSeq MOD_SEQ_1 = ModSeq.of(1L);
+    private static final ModSeq MOD_SEQ_2 = ModSeq.of(2L);
+
+    private static final ComposedMessageIdWithMetaData MESSAGE_1 = 
ComposedMessageIdWithMetaData.builder()
+        .composedMessageId(new ComposedMessageId(MAILBOX_ID, MESSAGE_ID_1, 
MESSAGE_UID_1))
+        .modSeq(MOD_SEQ_1)
+        .flags(new Flags())
+        .build();
+
+    private static final ComposedMessageIdWithMetaData 
MESSAGE_1_WITH_SEEN_FLAG = ComposedMessageIdWithMetaData.builder()
+        .composedMessageId(new ComposedMessageId(MAILBOX_ID, MESSAGE_ID_1, 
MESSAGE_UID_1))
+        .modSeq(MOD_SEQ_1)
+        .flags(new Flags(Flags.Flag.SEEN))
+        .build();
+
+    private static final ComposedMessageIdWithMetaData 
MESSAGE_1_WITH_MOD_SEQ_2 = ComposedMessageIdWithMetaData.builder()
+        .composedMessageId(new ComposedMessageId(MAILBOX_ID, MESSAGE_ID_1, 
MESSAGE_UID_1))
+        .modSeq(MOD_SEQ_2)
+        .flags(new Flags(Flags.Flag.SEEN))
+        .build();
+
+    private static final ComposedMessageIdWithMetaData MESSAGE_2 = 
ComposedMessageIdWithMetaData.builder()
+        .composedMessageId(new ComposedMessageId(MAILBOX_ID, MESSAGE_ID_2, 
MESSAGE_UID_2))
+        .modSeq(MOD_SEQ_2)
+        .flags(new Flags())
+        .build();
+
+    @RegisterExtension
+    static CassandraClusterExtension cassandraCluster = new 
CassandraClusterExtension(
+        CassandraModule.aggregateModules(
+            CassandraSchemaVersionModule.MODULE,
+            CassandraMessageModule.MODULE));
+
+    CassandraMessageIdToImapUidDAO imapUidDAO;
+    CassandraMessageIdDAO messageIdDAO;
+    SolveMessageInconsistenciesService testee;
+
+    @BeforeEach
+    void setUp(CassandraCluster cassandra) {
+        imapUidDAO = new CassandraMessageIdToImapUidDAO(cassandra.getConf(), 
new CassandraMessageId.Factory());
+        messageIdDAO = new CassandraMessageIdDAO(cassandra.getConf(), new 
CassandraMessageId.Factory());
+        testee = new SolveMessageInconsistenciesService(imapUidDAO, 
messageIdDAO);
+    }
+
+    @Test
+    void fixMessageInconsistenciesShouldReturnCompletedWhenNoData() {
+        assertThat(testee.fixMessageInconsistencies().block())
+            .isEqualTo(Task.Result.COMPLETED);
+    }
+
+    @Test
+    void fixMessageInconsistenciesShouldReturnCompletedWhenConsistentData() {
+        imapUidDAO.insert(MESSAGE_1).block();
+        messageIdDAO.insert(MESSAGE_1).block();
+
+        assertThat(testee.fixMessageInconsistencies().block())
+            .isEqualTo(Task.Result.COMPLETED);
+    }
+
+    @Test
+    void fixMailboxInconsistenciesShouldNotAlterStateWhenEmpty() {
+        testee.fixMessageInconsistencies().block();
+
+        SoftAssertions.assertSoftly(softly -> {
+            
softly.assertThat(imapUidDAO.retrieveAllMessages().collectList().block()).isEmpty();
+            
softly.assertThat(messageIdDAO.retrieveAllMessages().collectList().block()).isEmpty();
+        });
+    }
+
+    @Test
+    void fixMailboxInconsistenciesShouldNotAlterStateWhenConsistent() {
+        imapUidDAO.insert(MESSAGE_1).block();
+        messageIdDAO.insert(MESSAGE_1).block();
+
+        testee.fixMessageInconsistencies().block();
+
+        SoftAssertions.assertSoftly(softly -> {
+            
softly.assertThat(imapUidDAO.retrieveAllMessages().collectList().block())
+                .containsExactlyInAnyOrder(MESSAGE_1);
+            
softly.assertThat(messageIdDAO.retrieveAllMessages().collectList().block())
+                .containsExactlyInAnyOrder(MESSAGE_1);
+        });
+    }
+
+    @Nested
+    class ImapUidScanningTest {
+
+        @Test
+        void 
fixMessageInconsistenciesShouldReturnCompletedWhenInconsistentData() {
+            imapUidDAO.insert(MESSAGE_1).block();
+
+            assertThat(testee.fixMessageInconsistencies().block())
+                .isEqualTo(Task.Result.COMPLETED);
+        }
+
+        @Test
+        void fixMessageInconsistenciesShouldResolveInconsistentData() {
+            imapUidDAO.insert(MESSAGE_1).block();
+
+            testee.fixMessageInconsistencies().block();
+
+            SoftAssertions.assertSoftly(softly -> {
+                softly.assertThat(imapUidDAO.retrieve(MESSAGE_ID_1, 
Optional.of(MAILBOX_ID)).collectList().block())
+                    .containsExactly(MESSAGE_1);
+                softly.assertThat(messageIdDAO.retrieve(MAILBOX_ID, 
MESSAGE_UID_1).block().get())
+                    .isEqualTo(MESSAGE_1);
+            });
+        }
+
+        @Test
+        void 
fixMessageInconsistenciesShouldReturnCompletedWhenInconsistentFlags() {
+            imapUidDAO.insert(MESSAGE_1).block();
+            messageIdDAO.insert(MESSAGE_1_WITH_SEEN_FLAG).block();
+
+            assertThat(testee.fixMessageInconsistencies().block())
+                .isEqualTo(Task.Result.COMPLETED);
+        }
+
+        @Test
+        void fixMessageInconsistenciesShouldResolveInconsistentFlags() {
+            imapUidDAO.insert(MESSAGE_1).block();
+            messageIdDAO.insert(MESSAGE_1_WITH_SEEN_FLAG).block();
+
+            testee.fixMessageInconsistencies().block();
+
+            SoftAssertions.assertSoftly(softly -> {
+                softly.assertThat(imapUidDAO.retrieve(MESSAGE_ID_1, 
Optional.of(MAILBOX_ID)).collectList().block())
+                    .containsExactly(MESSAGE_1);
+                softly.assertThat(messageIdDAO.retrieve(MAILBOX_ID, 
MESSAGE_UID_1).block().get())
+                    .isEqualTo(MESSAGE_1);
+            });
+        }
+
+        @Test
+        void 
fixMessageInconsistenciesShouldReturnCompletedWhenInconsistentModSeq() {
+            imapUidDAO.insert(MESSAGE_1).block();
+            messageIdDAO.insert(MESSAGE_1_WITH_MOD_SEQ_2).block();
+
+            assertThat(testee.fixMessageInconsistencies().block())
+                .isEqualTo(Task.Result.COMPLETED);
+        }
+
+        @Test
+        void fixMessageInconsistenciesShouldResolveInconsistentModSeq() {
+            imapUidDAO.insert(MESSAGE_1).block();
+            messageIdDAO.insert(MESSAGE_1_WITH_MOD_SEQ_2).block();
+
+            testee.fixMessageInconsistencies().block();
+
+            SoftAssertions.assertSoftly(softly -> {
+                softly.assertThat(imapUidDAO.retrieve(MESSAGE_ID_1, 
Optional.of(MAILBOX_ID)).collectList().block())
+                    .containsExactly(MESSAGE_1);
+                softly.assertThat(messageIdDAO.retrieve(MAILBOX_ID, 
MESSAGE_UID_1).block().get())
+                    .isEqualTo(MESSAGE_1);
+            });
+        }
+
+        @Nested
+        class FailureTesting {
+            @Test
+            void 
fixMessageInconsistenciesShouldReturnPartialWhenError(CassandraCluster 
cassandra) {
+                imapUidDAO.insert(MESSAGE_1).block();
+
+                cassandra.getConf()
+                    .registerScenario(fail()
+                        .forever()
+                        .whenQueryStartsWith("INSERT INTO messageIdTable 
(mailboxId,uid,modSeq,messageId,flagAnswered,flagDeleted,flagDraft,flagFlagged,flagRecent,flagSeen,flagUser,userFlags)
 VALUES 
(:mailboxId,:uid,:modSeq,:messageId,:flagAnswered,:flagDeleted,:flagDraft,:flagFlagged,:flagRecent,:flagSeen,:flagUser,:userFlags)"));
+
+                assertThat(testee.fixMessageInconsistencies().block())
+                    .isEqualTo(Task.Result.PARTIAL);
+            }
+
+            @Test
+            void 
fixMessageInconsistenciesShouldReturnPartialWhenPartialError(CassandraCluster 
cassandra) {
+                imapUidDAO.insert(MESSAGE_1).block();
+                imapUidDAO.insert(MESSAGE_2).block();
+
+                cassandra.getConf()
+                    .registerScenario(fail()
+                        .times(1)
+                        .whenQueryStartsWith("INSERT INTO messageIdTable 
(mailboxId,uid,modSeq,messageId,flagAnswered,flagDeleted,flagDraft,flagFlagged,flagRecent,flagSeen,flagUser,userFlags)
 VALUES 
(:mailboxId,:uid,:modSeq,:messageId,:flagAnswered,:flagDeleted,:flagDraft,:flagFlagged,:flagRecent,:flagSeen,:flagUser,:userFlags)"));
+
+                assertThat(testee.fixMessageInconsistencies().block())
+                    .isEqualTo(Task.Result.PARTIAL);
+            }
+
+            @Test
+            void 
fixMessageInconsistenciesShouldResolveSuccessPartially(CassandraCluster 
cassandra) {
+                imapUidDAO.insert(MESSAGE_1).block();
+                imapUidDAO.insert(MESSAGE_2).block();
+
+                cassandra.getConf()
+                    .registerScenario(fail()
+                        .times(1)
+                        .whenQueryStartsWith("INSERT INTO messageIdTable 
(mailboxId,uid,modSeq,messageId,flagAnswered,flagDeleted,flagDraft,flagFlagged,flagRecent,flagSeen,flagUser,userFlags)
 VALUES 
(:mailboxId,:uid,:modSeq,d2bee791-7e63-11ea-883c-95b84008f979,:flagAnswered,:flagDeleted,:flagDraft,:flagFlagged,:flagRecent,:flagSeen,:flagUser,:userFlags)"));
+
+                testee.fixMessageInconsistencies().block();
+
+                SoftAssertions.assertSoftly(softly -> {
+                    softly.assertThat(imapUidDAO.retrieve(MESSAGE_ID_2, 
Optional.of(MAILBOX_ID)).collectList().block())
+                        .containsExactly(MESSAGE_2);
+                    softly.assertThat(messageIdDAO.retrieve(MAILBOX_ID, 
MESSAGE_UID_2).block().get())
+                        .isEqualTo(MESSAGE_2);
+                });
+            }
+        }
+    }
+
+    @Nested
+    class MessageIdScanningTest {
+
+        @Test
+        void 
fixMessageInconsistenciesShouldReturnCompletedWhenInconsistentData() {
+            messageIdDAO.insert(MESSAGE_1).block();
+
+            assertThat(testee.fixMessageInconsistencies().block())
+                .isEqualTo(Task.Result.COMPLETED);
+        }
+
+        @Test
+        void fixMessageInconsistenciesShouldResolveInconsistentData() {
+            messageIdDAO.insert(MESSAGE_1).block();
+
+            testee.fixMessageInconsistencies().block();
+
+            SoftAssertions.assertSoftly(softly -> {
+                
softly.assertThat(imapUidDAO.retrieveAllMessages().collectList().block())
+                    .isEmpty();
+                
softly.assertThat(messageIdDAO.retrieveAllMessages().collectList().block())
+                    .isEmpty();
+            });
+        }
+
+        @Test
+        void 
fixMessageInconsistenciesShouldReturnCompletedWhenPartialInconsistentData() {
+            messageIdDAO.insert(MESSAGE_1).block();
+            messageIdDAO.insert(MESSAGE_2).block();
+
+            imapUidDAO.insert(MESSAGE_1).block();
+
+            assertThat(testee.fixMessageInconsistencies().block())
+                .isEqualTo(Task.Result.COMPLETED);
+        }
+
+        @Test
+        void fixMessageInconsistenciesShouldResolvePartialInconsistentData() {
+            messageIdDAO.insert(MESSAGE_1).block();
+            messageIdDAO.insert(MESSAGE_2).block();
+
+            imapUidDAO.insert(MESSAGE_1).block();
+
+            testee.fixMessageInconsistencies().block();
+
+            SoftAssertions.assertSoftly(softly -> {
+                
softly.assertThat(imapUidDAO.retrieveAllMessages().collectList().block())
+                    .containsExactly(MESSAGE_1);
+                
softly.assertThat(messageIdDAO.retrieveAllMessages().collectList().block())
+                    .containsExactly(MESSAGE_1);
+            });
+        }
+
+        @Test
+        void fixImapUidInconsistenciesShouldCompleteWhenInconsistent() {
+            messageIdDAO.insert(MESSAGE_1_WITH_MOD_SEQ_2).block();
+
+            imapUidDAO.insert(MESSAGE_1).block();
+
+            testee.fixMessageInconsistencies().block();
+
+            assertThat(testee.fixImapUidInconsistencies().block())
+                .isEqualTo(Task.Result.COMPLETED);
+        }
+
+        @Test
+        void fixImapUidInconsistenciesShouldResolveInconsistent() {
+            messageIdDAO.insert(MESSAGE_1_WITH_MOD_SEQ_2).block();
+
+            imapUidDAO.insert(MESSAGE_1).block();
+
+            testee.fixMessageInconsistencies().block();
+
+            SoftAssertions.assertSoftly(softly -> {
+                
softly.assertThat(imapUidDAO.retrieveAllMessages().collectList().block())
+                    .containsExactly(MESSAGE_1);
+                
softly.assertThat(messageIdDAO.retrieveAllMessages().collectList().block())
+                    .containsExactly(MESSAGE_1);
+            });
+        }
+
+        @Nested
+        class FailureTesting {
+            @Test
+            void 
fixMessageInconsistenciesShouldReturnPartialWhenError(CassandraCluster 
cassandra) {
+                messageIdDAO.insert(MESSAGE_1).block();
+
+                cassandra.getConf()
+                    .registerScenario(fail()
+                        .forever()
+                        .whenQueryStartsWith("DELETE FROM messageIdTable WHERE 
mailboxId=:mailboxId AND uid=:uid"));
+
+                assertThat(testee.fixMessageInconsistencies().block())
+                    .isEqualTo(Task.Result.PARTIAL);
+            }
+
+            @Test
+            void 
fixMessageInconsistenciesShouldReturnPartialWhenPartialError(CassandraCluster 
cassandra) {
+                messageIdDAO.insert(MESSAGE_1).block();
+                messageIdDAO.insert(MESSAGE_2).block();
+
+                cassandra.getConf()
+                    .registerScenario(fail()
+                        .times(1)
+                        .whenQueryStartsWith("DELETE FROM messageIdTable WHERE 
mailboxId=:mailboxId AND uid=:uid"));
+
+                assertThat(testee.fixMessageInconsistencies().block())
+                    .isEqualTo(Task.Result.PARTIAL);
+            }
+
+            @Test
+            void 
fixMessageInconsistenciesShouldResolveSuccessPartially(CassandraCluster 
cassandra) {
+                messageIdDAO.insert(MESSAGE_1).block();
+                messageIdDAO.insert(MESSAGE_2).block();
+
+                cassandra.getConf()
+                    .registerScenario(fail()
+                        .times(1)
+                        .whenQueryStartsWith("DELETE FROM messageIdTable WHERE 
mailboxId=:mailboxId AND uid=:uid;"));
+
+                testee.fixMessageInconsistencies().block();
+
+                SoftAssertions.assertSoftly(softly -> {
+                    
softly.assertThat(imapUidDAO.retrieveAllMessages().collectList().block())
+                        .isEmpty();
+                    
softly.assertThat(messageIdDAO.retrieveAllMessages().collectList().block())
+                        .containsExactly(MESSAGE_1);
+                });
+            }
+        }
+    }
+}


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

Reply via email to