private CompletableFuture<FlagsUpdateStageResult> tryFlagsUpdate(FlagsUpdateCalculator flagUpdateCalculator, long newModSeq, ComposedMessageIdWithMetaData oldMetaData) { Flags oldFlags = oldMetaData.getFlags(); Flags newFlags = flagUpdateCalculator.buildNewFlags(oldFlags); if (identicalFlags(oldFlags, newFlags)) { return CompletableFuture.completedFuture(FlagsUpdateStageResult.success(UpdatedFlags.builder() .uid(oldMetaData.getComposedMessageId().getUid()) .modSeq(oldMetaData.getModSeq()) .oldFlags(oldFlags) .newFlags(newFlags) .build())); } return updateFlags(oldMetaData, newFlags, newModSeq) .thenApply(success -> { if (success) { return FlagsUpdateStageResult.success(UpdatedFlags.builder() .uid(oldMetaData.getComposedMessageId().getUid()) .modSeq(newModSeq) .oldFlags(oldFlags) .newFlags(newFlags) .build()); } else { return FlagsUpdateStageResult.fail(oldMetaData.getComposedMessageId().getUid()); } }); }
public MessageChangedFlags updateFlags(Mailbox mailbox, FlagsUpdateCalculator flagsUpdateCalculator, Iterator<MailboxMessage> messages) throws MailboxException { ImmutableList.Builder<UpdatedFlags> updatedFlags = ImmutableList.builder(); ImmutableList.Builder<MailboxMessage> changedFlags = ImmutableList.builder(); long modSeq = nextModSeq(mailbox); while (messages.hasNext()) { MailboxMessage member = messages.next(); Flags originalFlags = member.createFlags(); member.setFlags(flagsUpdateCalculator.buildNewFlags(originalFlags)); Flags newFlags = member.createFlags(); if (UpdatedFlags.flagsChanged(originalFlags, newFlags)) { member.setModSeq(modSeq); changedFlags.add(member); } updatedFlags.add(UpdatedFlags.builder() .uid(member.getUid()) .modSeq(member.getModSeq()) .newFlags(newFlags) .oldFlags(originalFlags) .build()); } return new MessageChangedFlags(updatedFlags.build().iterator(), changedFlags.build()); }
@Test public void updateShouldNotPropagateExceptionWhenExceptionOccurs() throws Exception { //Given Mailbox mailbox = mock(Mailbox.class); Flags flags = new Flags(); UpdatedFlags updatedFlags = UpdatedFlags.builder() .uid(MESSAGE_UID) .modSeq(MODSEQ) .oldFlags(flags) .newFlags(flags) .build(); when(mailbox.getMailboxId()) .thenReturn(MAILBOX_ID); ImmutableList<UpdatedRepresentation> expectedUpdatedRepresentations = ImmutableList.of(new UpdatedRepresentation(ELASTIC_SEARCH_ID, "json updated content")); when(elasticSearchIndexer.update(expectedUpdatedRepresentations)) .thenThrow(new ElasticsearchException("")); //When testee.update(session, mailbox, Lists.newArrayList(updatedFlags)); //Then //No exception }
@Test public void updateShouldWork() throws Exception { //Given Mailbox mailbox = mock(Mailbox.class); Flags flags = new Flags(); UpdatedFlags updatedFlags = UpdatedFlags.builder() .uid(MESSAGE_UID) .modSeq(MODSEQ) .oldFlags(flags) .newFlags(flags) .build(); when(mailbox.getMailboxId()) .thenReturn(MAILBOX_ID); when(messageToElasticSearchJson.getUpdatedJsonMessagePart(any(Flags.class), any(Long.class))) .thenReturn("json updated content"); //When testee.update(session, mailbox, Lists.newArrayList(updatedFlags)); //Then ImmutableList<UpdatedRepresentation> expectedUpdatedRepresentations = ImmutableList.of(new UpdatedRepresentation(ELASTIC_SEARCH_ID, "json updated content")); verify(elasticSearchIndexer).update(expectedUpdatedRepresentations); }
private Function<MailboxMessage, Pair<MailboxId, UpdatedFlags>> updateMessage(Flags newState, FlagsUpdateMode updateMode) { return Throwing.function((MailboxMessage message) -> { FlagsUpdateCalculator flagsUpdateCalculator = new FlagsUpdateCalculator(newState, updateMode); if (flagsUpdateCalculator.buildNewFlags(message.createFlags()).equals(message.createFlags())) { UpdatedFlags updatedFlags = UpdatedFlags.builder() .modSeq(message.getModSeq()) .uid(message.getUid()) .oldFlags(message.createFlags()) .newFlags(newState) .build(); return Pair.of(message.getMailboxId(), updatedFlags); } return Pair.of(message.getMailboxId(), messageMapper.updateFlags( mailboxMapper.findMailboxById(message.getMailboxId()), flagsUpdateCalculator, message.getUid().toRange()) .next()); }); } }
@Test void updateIndexOnFlagsUpdateShouldNotSaveMessageInDeletedMessageWhenDeletedFlagIsNotSet() { MailboxMessage message = mock(MailboxMessage.class); when(message.createFlags()).thenReturn(new Flags()); when(message.getUid()).thenReturn(MESSAGE_UID); testee.updateIndexOnAdd(message, MAILBOX_ID).join(); testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder() .uid(MESSAGE_UID) .newFlags(new Flags(Flags.Flag.RECENT)) .oldFlags(new Flags()) .modSeq(MODSEQ) .build()).join(); assertThat( deletedMessageDAO .retrieveDeletedMessage(MAILBOX_ID, MessageRange.all()) .join() .collect(Guavate.toImmutableList())) .isEmpty(); }
@Test void updateIndexOnFlagsUpdateShouldSaveMessageInDeletedMessageWhenDeletedFlagIsSet() { MailboxMessage message = mock(MailboxMessage.class); when(message.createFlags()).thenReturn(new Flags()); when(message.getUid()).thenReturn(MESSAGE_UID); testee.updateIndexOnAdd(message, MAILBOX_ID).join(); testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder() .uid(MESSAGE_UID) .newFlags(new Flags(Flags.Flag.DELETED)) .oldFlags(new Flags()) .modSeq(MODSEQ) .build()).join(); assertThat( deletedMessageDAO .retrieveDeletedMessage(MAILBOX_ID, MessageRange.all()) .join() .collect(Guavate.toImmutableList())) .containsExactly(MESSAGE_UID); }
@Test void updateIndexOnFlagsUpdateShouldRemoveMessageInDeletedMessageWhenDeletedFlagIsUnset() { MailboxMessage message = mock(MailboxMessage.class); when(message.createFlags()).thenReturn(new Flags()); when(message.getUid()).thenReturn(MESSAGE_UID); testee.updateIndexOnAdd(message, MAILBOX_ID).join(); deletedMessageDAO.addDeleted(MAILBOX_ID, MESSAGE_UID).join(); testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder() .uid(MESSAGE_UID) .newFlags(new Flags()) .oldFlags(new Flags(Flags.Flag.DELETED)) .modSeq(MODSEQ) .build()).join(); assertThat( deletedMessageDAO .retrieveDeletedMessage(MAILBOX_ID, MessageRange.all()) .join() .collect(Guavate.toImmutableList())) .isEmpty(); }
private Stream<Pair<MailboxId, UpdatedFlags>> flagsUpdateWithRetry(Flags newState, MessageManager.FlagsUpdateMode updateMode, MailboxId mailboxId, MessageId messageId) { try { Pair<Flags, ComposedMessageIdWithMetaData> pair = new FunctionRunnerWithRetry(cassandraConfiguration.getFlagsUpdateMessageIdMaxRetry()) .executeAndRetrieveObject(() -> tryFlagsUpdate(newState, updateMode, mailboxId, messageId)); ComposedMessageIdWithMetaData composedMessageIdWithMetaData = pair.getRight(); Flags oldFlags = pair.getLeft(); return Stream.of(Pair.of(composedMessageIdWithMetaData.getComposedMessageId().getMailboxId(), UpdatedFlags.builder() .uid(composedMessageIdWithMetaData.getComposedMessageId().getUid()) .modSeq(composedMessageIdWithMetaData.getModSeq()) .oldFlags(oldFlags) .newFlags(composedMessageIdWithMetaData.getFlags()) .build())); } catch (LightweightTransactionException e) { throw new RuntimeException(e); } catch (MailboxDeleteDuringUpdateException e) { LOGGER.info("Mailbox {} was deleted during flag update", mailboxId); return Stream.of(); } }
@Test void applicableFlagShouldKeepAllFlagsEvenTheMessageRemovesFlag() { Flags messageFlags = FlagsBuilder.builder() .add("custom1", "custom2", "custom3") .build(); MailboxMessage message = mock(MailboxMessage.class); when(message.createFlags()).thenReturn(messageFlags); when(message.getUid()).thenReturn(MESSAGE_UID); testee.updateIndexOnAdd(message, MAILBOX_ID).join(); testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder() .uid(MESSAGE_UID) .newFlags(new Flags()) .oldFlags(messageFlags) .modSeq(MODSEQ) .build()).join(); Flags applicableFlag = applicableFlagDAO.retrieveApplicableFlag(MAILBOX_ID).join().get(); assertThat(applicableFlag).isEqualTo(messageFlags); } }
@Test void updateIndexOnFlagsUpdateShouldNotRemoveMessageInDeletedMessageWhenDeletedFlagIsNotUnset() { MailboxMessage message = mock(MailboxMessage.class); when(message.createFlags()).thenReturn(new Flags()); when(message.getUid()).thenReturn(MESSAGE_UID); testee.updateIndexOnAdd(message, MAILBOX_ID).join(); deletedMessageDAO.addDeleted(MAILBOX_ID, MESSAGE_UID).join(); testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder() .uid(MESSAGE_UID) .newFlags(new Flags()) .oldFlags(new Flags(Flags.Flag.SEEN)) .modSeq(MODSEQ) .build()).join(); assertThat( deletedMessageDAO .retrieveDeletedMessage(MAILBOX_ID, MessageRange.all()) .join() .collect(Guavate.toImmutableList())) .containsExactly(MESSAGE_UID); }
@Test public void testShouldClearFlagUidsUponReset() { MessageUid uid = MessageUid.of(900); SelectedMailboxImpl analyser = this.testee; FakeMailboxListenerFlagsUpdate update = new FakeMailboxListenerFlagsUpdate(MAILBOX_SESSION, ImmutableList.of(uid), ImmutableList.of(UpdatedFlags.builder() .uid(uid) .modSeq(-1) .oldFlags(new Flags()) .newFlags(new Flags(Flags.Flag.ANSWERED)) .build()), MAILBOX_PATH); analyser.event(update); analyser.event(update); analyser.deselect(); assertThat(analyser.flagUpdateUids()).isEmpty(); }
@Test public void testShouldSetUidWhenSystemFlagChangeDifferentSessionInSilentMode() { MessageUid uid = MessageUid.of(900); FakeMailboxListenerFlagsUpdate update = new FakeMailboxListenerFlagsUpdate(OTHER_MAILBOX_SESSION, ImmutableList.of(uid), ImmutableList.of(UpdatedFlags.builder() .uid(uid) .modSeq(-1) .oldFlags(new Flags()) .newFlags(new Flags(Flags.Flag.ANSWERED)) .build()), MAILBOX_PATH); testee.event(update); testee.setSilentFlagChanges(true); testee.event(update); assertThat(testee.flagUpdateUids().iterator()).containsExactly(uid); }
@Test void updateIndexOnFlagsUpdateShouldUnionApplicableFlag() { Flags customFlag = new Flags("custom"); MailboxMessage message = mock(MailboxMessage.class); when(message.createFlags()).thenReturn(customFlag); when(message.getUid()).thenReturn(MESSAGE_UID); testee.updateIndexOnAdd(message, MAILBOX_ID).join(); Flags customBis = new Flags("customBis"); testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder() .uid(MESSAGE_UID) .newFlags(customBis) .oldFlags(customFlag) .modSeq(MODSEQ) .build()).join(); Flags applicableFlag = applicableFlagDAO.retrieveApplicableFlag(MAILBOX_ID).join().get(); assertThat(applicableFlag).isEqualTo(new FlagsBuilder().add(customFlag, customBis).build()); }
@Test void updateIndexOnFlagsUpdateShouldAddRecentOnSettingRecentFlag() { // Clean up strategy if some flags updates missed MailboxMessage message = mock(MailboxMessage.class); when(message.createFlags()).thenReturn(new Flags()); when(message.getUid()).thenReturn(MESSAGE_UID); testee.updateIndexOnAdd(message, MAILBOX_ID).join(); testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder() .uid(MESSAGE_UID) .newFlags(new Flags(Flags.Flag.RECENT)) .oldFlags(new Flags()) .modSeq(MODSEQ) .build()).join(); assertThat(mailboxRecentsDAO.getRecentMessageUidsInMailbox(MAILBOX_ID).join() .collect(Guavate.toImmutableList())) .containsOnly(MESSAGE_UID); }
@Test void updateIndexOnFlagsUpdateShouldRemoveRecentOnUnsettingRecentFlag() { // Clean up strategy if some flags updates missed MailboxMessage message = mock(MailboxMessage.class); when(message.createFlags()).thenReturn(new Flags(Flags.Flag.RECENT)); when(message.getUid()).thenReturn(MESSAGE_UID); testee.updateIndexOnAdd(message, MAILBOX_ID).join(); testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder() .uid(MESSAGE_UID) .newFlags(new Flags()) .oldFlags(new Flags(Flags.Flag.RECENT)) .modSeq(MODSEQ) .build()).join(); assertThat(mailboxRecentsDAO.getRecentMessageUidsInMailbox(MAILBOX_ID).join() .collect(Guavate.toImmutableList())) .isEmpty(); }
@Test public void testShouldNotSetUidWhenSystemFlagChangeSameSessionInSilentMode() { FakeMailboxListenerFlagsUpdate update = new FakeMailboxListenerFlagsUpdate(MAILBOX_SESSION, ImmutableList.of(MessageUid.of(345)), ImmutableList.of(UpdatedFlags.builder() .uid(MessageUid.of(345)) .modSeq(-1) .oldFlags(new Flags()) .newFlags(new Flags()) .build()), MAILBOX_PATH); testee.event(update); testee.setSilentFlagChanges(true); testee.event(update); assertThat(testee.flagUpdateUids().iterator()).isEmpty(); }
@Test void updateIndexOnFlagsUpdateShouldUpdateLastUnseenWhenSetToUnseen() { MailboxMessage message = mock(MailboxMessage.class); when(message.createFlags()).thenReturn(new Flags(Flags.Flag.SEEN)); when(message.getUid()).thenReturn(MESSAGE_UID); testee.updateIndexOnAdd(message, MAILBOX_ID).join(); testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder() .uid(MESSAGE_UID) .newFlags(new Flags()) .oldFlags(new Flags(Flags.Flag.SEEN)) .modSeq(MODSEQ) .build()).join(); Optional<MessageUid> actual = firstUnseenDAO.retrieveFirstUnread(MAILBOX_ID).join(); assertThat(actual.isPresent()).isTrue(); assertThat(actual.get()).isEqualTo(MESSAGE_UID); }
@Test void updateIndexOnFlagsUpdateShouldIncrementUnseenMessageCountWhenSeenIsUnset() throws Exception { MailboxMessage message = mock(MailboxMessage.class); when(message.createFlags()).thenReturn(new Flags(Flags.Flag.SEEN)); when(message.getUid()).thenReturn(MESSAGE_UID); testee.updateIndexOnAdd(message, MAILBOX_ID).join(); testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder() .uid(MESSAGE_UID) .newFlags(new Flags()) .oldFlags(new Flags(Flags.Flag.SEEN)) .modSeq(MODSEQ) .build()).join(); Optional<Long> actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).join(); assertThat(actual.isPresent()).isTrue(); assertThat(actual.get()).isEqualTo(1); }