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