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 521c7e5264e541e409c2c42a96250aa192bc7bb4 Author: Benoit Tellier <btell...@linagora.com> AuthorDate: Thu Jul 11 15:45:45 2019 +0200 JAMES-2810 Inconsistencies testing upon store --- .../deleted-messages-vault-cassandra/pom.xml | 5 + .../CassandraDeletedMessageMetadataVaultTest.java | 214 ++++++++++++++++++++- 2 files changed, 215 insertions(+), 4 deletions(-) diff --git a/mailbox/plugin/deleted-messages-vault-cassandra/pom.xml b/mailbox/plugin/deleted-messages-vault-cassandra/pom.xml index 52e274b..19e7c54 100644 --- a/mailbox/plugin/deleted-messages-vault-cassandra/pom.xml +++ b/mailbox/plugin/deleted-messages-vault-cassandra/pom.xml @@ -81,6 +81,11 @@ <scope>test</scope> </dependency> <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers</artifactId> <scope>test</scope> diff --git a/mailbox/plugin/deleted-messages-vault-cassandra/src/test/java/org/apache/james/vault/metadata/CassandraDeletedMessageMetadataVaultTest.java b/mailbox/plugin/deleted-messages-vault-cassandra/src/test/java/org/apache/james/vault/metadata/CassandraDeletedMessageMetadataVaultTest.java index b384c35..0779d49 100644 --- a/mailbox/plugin/deleted-messages-vault-cassandra/src/test/java/org/apache/james/vault/metadata/CassandraDeletedMessageMetadataVaultTest.java +++ b/mailbox/plugin/deleted-messages-vault-cassandra/src/test/java/org/apache/james/vault/metadata/CassandraDeletedMessageMetadataVaultTest.java @@ -19,7 +19,20 @@ package org.apache.james.vault.metadata; +import static org.apache.james.vault.DeletedMessageFixture.MESSAGE_ID; +import static org.apache.james.vault.DeletedMessageFixture.USER; import static org.apache.james.vault.metadata.DeletedMessageMetadataModule.MODULE; +import static org.apache.james.vault.metadata.DeletedMessageVaultMetadataFixture.BUCKET_NAME; +import static org.apache.james.vault.metadata.DeletedMessageVaultMetadataFixture.DELETED_MESSAGE; +import static org.apache.james.vault.metadata.DeletedMessageVaultMetadataFixture.DELETED_MESSAGE_2; +import static org.apache.james.vault.metadata.DeletedMessageVaultMetadataFixture.STORAGE_INFORMATION; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import java.util.Optional; +import java.util.stream.Stream; import org.apache.james.backends.cassandra.CassandraCluster; import org.apache.james.backends.cassandra.CassandraClusterExtension; @@ -28,13 +41,21 @@ import org.apache.james.mailbox.inmemory.InMemoryId; import org.apache.james.mailbox.inmemory.InMemoryMessageId; import org.apache.james.vault.dto.DeletedMessageWithStorageInformationConverter; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + public class CassandraDeletedMessageMetadataVaultTest implements DeletedMessageMetadataVaultContract { @RegisterExtension static CassandraClusterExtension cassandraCluster = new CassandraClusterExtension(MODULE); private DeletedMessageMetadataVault testee; + private MetadataDAO metadataDAO; + private StorageInformationDAO storageInformationDAO; + private UserPerBucketDAO userPerBucketDAO; @BeforeEach void setUp(CassandraCluster cassandra) { @@ -42,14 +63,199 @@ public class CassandraDeletedMessageMetadataVaultTest implements DeletedMessageM InMemoryMessageId.Factory messageIdFactory = new InMemoryMessageId.Factory(); DeletedMessageWithStorageInformationConverter dtoConverter = new DeletedMessageWithStorageInformationConverter(blobIdFactory, messageIdFactory, new InMemoryId.Factory()); - testee = new CassandraDeletedMessageMetadataVault( - new MetadataDAO(cassandra.getConf(), messageIdFactory, dtoConverter), - new StorageInformationDAO(cassandra.getConf(), blobIdFactory), - new UserPerBucketDAO(cassandra.getConf())); + metadataDAO = spy(new MetadataDAO(cassandra.getConf(), messageIdFactory, dtoConverter)); + storageInformationDAO = spy(new StorageInformationDAO(cassandra.getConf(), blobIdFactory)); + userPerBucketDAO = spy(new UserPerBucketDAO(cassandra.getConf())); + + testee = new CassandraDeletedMessageMetadataVault(metadataDAO, storageInformationDAO, userPerBucketDAO); } @Override public DeletedMessageMetadataVault metadataVault() { return testee; } + + @Test + void listShouldNotReturnMessagesWhenStorageDAOFailed() { + when(storageInformationDAO.referenceStorageInformation(USER, MESSAGE_ID, STORAGE_INFORMATION)) + .thenReturn(Mono.error(new RuntimeException())); + + try { + Mono.from(testee.store(DELETED_MESSAGE)).block(); + } catch (Exception e) { + // ignored + } + + Stream<DeletedMessageWithStorageInformation> messages = Flux.from(metadataVault().listMessages(BUCKET_NAME, USER)).toStream(); + assertThat(messages).isEmpty(); + } + + @Test + void listShouldNotReturnMessagesWhenMetadataDAOFailed() { + when(metadataDAO.store(DELETED_MESSAGE)) + .thenReturn(Mono.error(new RuntimeException())); + + try { + Mono.from(testee.store(DELETED_MESSAGE)).block(); + } catch (Exception e) { + // ignored + } + + Stream<DeletedMessageWithStorageInformation> messages = Flux.from(metadataVault().listMessages(BUCKET_NAME, USER)).toStream(); + assertThat(messages).isEmpty(); + } + + @Test + void listShouldReturnMessagesWhenUserPerBucketDAOFailed() { + when(userPerBucketDAO.addUser(BUCKET_NAME, USER)) + .thenReturn(Mono.error(new RuntimeException())); + + try { + Mono.from(testee.store(DELETED_MESSAGE)).block(); + } catch (Exception e) { + // ignored + } + + Stream<DeletedMessageWithStorageInformation> messages = Flux.from(metadataVault().listMessages(BUCKET_NAME, USER)).toStream(); + assertThat(messages).contains(DELETED_MESSAGE); + } + + @Test + void retrieveStorageInformationShouldReturnMetadataWhenUserPerBucketDAOStoreFailed() { + when(userPerBucketDAO.addUser(BUCKET_NAME, USER)) + .thenReturn(Mono.error(new RuntimeException())); + + try { + Mono.from(testee.store(DELETED_MESSAGE)).block(); + } catch (Exception e) { + // ignored + } + + Optional<StorageInformation> maybeInfo = Mono.from(metadataVault().retrieveStorageInformation(DELETED_MESSAGE.getDeletedMessage().getOwner(), + DELETED_MESSAGE.getDeletedMessage().getMessageId())) + .blockOptional(); + assertThat(maybeInfo).isPresent(); + } + + @Disabled("The bucket being not referenced, the entry will not be dropped. Note that this get corrected by next " + + "metadata referenced for this user") + @Test + void removingBucketShouldCleanUpInvalidStateForList() { + when(userPerBucketDAO.addUser(BUCKET_NAME, USER)) + .thenReturn(Mono.error(new RuntimeException())); + + try { + Mono.from(testee.store(DELETED_MESSAGE)).block(); + } catch (Exception e) { + // ignored + } + + Mono.from(testee.removeMetadataRelatedToBucket(BUCKET_NAME)).block(); + + Stream<DeletedMessageWithStorageInformation> messages = Flux.from(metadataVault().listMessages(BUCKET_NAME, USER)).toStream(); + assertThat(messages).isEmpty(); + } + + @Disabled("The bucket being not referenced, the entry will not be dropped. Note that this get corrected by next " + + "metadata referenced for this user") + @Test + void removingBucketShouldCleanUpInvalidStateForRetrievingMetadata() { + when(userPerBucketDAO.addUser(BUCKET_NAME, USER)) + .thenReturn(Mono.error(new RuntimeException())); + + try { + Mono.from(testee.store(DELETED_MESSAGE)).block(); + } catch (Exception e) { + // ignored + } + + Mono.from(testee.removeMetadataRelatedToBucket(BUCKET_NAME)).block(); + + Optional<StorageInformation> maybeInfo = Mono.from(metadataVault().retrieveStorageInformation(DELETED_MESSAGE.getDeletedMessage().getOwner(), + DELETED_MESSAGE.getDeletedMessage().getMessageId())) + .blockOptional(); + assertThat(maybeInfo).isEmpty(); + } + + @Test + void removingBucketShouldBeEventuallyConsistentForList() { + when(userPerBucketDAO.addUser(BUCKET_NAME, USER)) + .thenReturn(Mono.error(new RuntimeException())); + + try { + Mono.from(testee.store(DELETED_MESSAGE)).block(); + } catch (Exception e) { + // ignored + } + reset(userPerBucketDAO); + Mono.from(testee.store(DELETED_MESSAGE_2)).block(); + + Mono.from(testee.removeMetadataRelatedToBucket(BUCKET_NAME)).block(); + + Stream<DeletedMessageWithStorageInformation> messages = Flux.from(metadataVault().listMessages(BUCKET_NAME, USER)).toStream(); + assertThat(messages).isEmpty(); + } + + @Test + void removingBucketShouldBeEventuallyConsistentForMetadata() { + when(userPerBucketDAO.addUser(BUCKET_NAME, USER)) + .thenReturn(Mono.error(new RuntimeException())); + + try { + Mono.from(testee.store(DELETED_MESSAGE)).block(); + } catch (Exception e) { + // ignored + } + reset(userPerBucketDAO); + Mono.from(testee.store(DELETED_MESSAGE_2)).block(); + + Mono.from(testee.removeMetadataRelatedToBucket(BUCKET_NAME)).block(); + + Optional<StorageInformation> maybeInfo = Mono.from(metadataVault().retrieveStorageInformation(DELETED_MESSAGE.getDeletedMessage().getOwner(), + DELETED_MESSAGE.getDeletedMessage().getMessageId())) + .blockOptional(); + assertThat(maybeInfo).isEmpty(); + } + + @Test + void directDeletionShouldCleanUpInvalidStateForList() { + when(userPerBucketDAO.addUser(BUCKET_NAME, USER)) + .thenReturn(Mono.error(new RuntimeException())); + + try { + Mono.from(testee.store(DELETED_MESSAGE)).block(); + } catch (Exception e) { + // ignored + } + + Mono.from(metadataVault().remove(BUCKET_NAME, + DELETED_MESSAGE.getDeletedMessage().getOwner(), + DELETED_MESSAGE.getDeletedMessage().getMessageId())) + .block(); + + Stream<DeletedMessageWithStorageInformation> messages = Flux.from(metadataVault().listMessages(BUCKET_NAME, USER)).toStream(); + assertThat(messages).isEmpty(); + } + + @Test + void directDeletionShouldCleanUpInvalidStateForRetrievingMetadata() { + when(userPerBucketDAO.addUser(BUCKET_NAME, USER)) + .thenReturn(Mono.error(new RuntimeException())); + + try { + Mono.from(testee.store(DELETED_MESSAGE)).block(); + } catch (Exception e) { + // ignored + } + + Mono.from(metadataVault().remove(BUCKET_NAME, + DELETED_MESSAGE.getDeletedMessage().getOwner(), + DELETED_MESSAGE.getDeletedMessage().getMessageId())) + .block(); + + Optional<StorageInformation> maybeInfo = Mono.from(metadataVault().retrieveStorageInformation(DELETED_MESSAGE.getDeletedMessage().getOwner(), + DELETED_MESSAGE.getDeletedMessage().getMessageId())) + .blockOptional(); + assertThat(maybeInfo).isEmpty(); + } } \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org