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/04/29 01:14:37 UTC
[james-project] 03/27: JAMES-3143
SolveMessageInconsistenciesService/Test
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 <kh...@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-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org