From a1804cd11184dda0ea767beaf1865f2048d229ab Mon Sep 17 00:00:00 2001 From: Marc Nuri Date: Sun, 18 Aug 2019 10:01:21 +0200 Subject: [PATCH] #10: Code coverage for message related Runnable tasks --- build.gradle | 7 + .../mnimapsync/store/FolderCrawlerTest.java | 137 ++++++++++++++++ .../mnimapsync/store/MessageCopierTest.java | 149 ++++++++++++++++++ .../mnimapsync/store/MessageDeleterTest.java | 127 +++++++++++++++ .../org.mockito.plugins.MockMaker | 1 + 5 files changed, 421 insertions(+) create mode 100644 src/test/java/com/marcnuri/mnimapsync/store/FolderCrawlerTest.java create mode 100644 src/test/java/com/marcnuri/mnimapsync/store/MessageCopierTest.java create mode 100644 src/test/java/com/marcnuri/mnimapsync/store/MessageDeleterTest.java create mode 100644 src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker diff --git a/build.gradle b/build.gradle index 6d4724b..0be11c2 100644 --- a/build.gradle +++ b/build.gradle @@ -18,6 +18,9 @@ repositories { dependencies { compile('com.sun.mail:javax.mail:1.6.1') + testCompile('org.junit.jupiter:junit-jupiter:5.5.1') + testCompile('org.mockito:mockito-core:3.0.0') + testCompile('org.hamcrest:java-hamcrest:2.0.0.0') } group = 'com.marcnuri' @@ -27,6 +30,10 @@ sourceCompatibility = 1.8 mainClassName = 'com.marcnuri.mnimapsync.MNIMAPSync' +test { + useJUnitPlatform() +} + task fatJar(type: Jar, group: BasePlugin.BUILD_GROUP) { manifest { attributes ( diff --git a/src/test/java/com/marcnuri/mnimapsync/store/FolderCrawlerTest.java b/src/test/java/com/marcnuri/mnimapsync/store/FolderCrawlerTest.java new file mode 100644 index 0000000..c515576 --- /dev/null +++ b/src/test/java/com/marcnuri/mnimapsync/store/FolderCrawlerTest.java @@ -0,0 +1,137 @@ +/* + * FolderCrawlerTest.java + * + * Created on 2019-08-16, 7:00 + * + * Copyright 2019 Marc Nuri San Felix + * + * Licensed 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 com.marcnuri.mnimapsync.store; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsCollectionWithSize.hasSize; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import com.sun.mail.imap.IMAPMessage; +import com.sun.mail.imap.IMAPStore; +import javax.mail.Folder; +import javax.mail.Message; +import javax.mail.MessagingException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +/** + * Created by Marc Nuri on 2019-08-16. + */ +class FolderCrawlerTest { + + private IMAPStore imapStore; + private Folder folder; + private StoreIndex storeIndex; + + @BeforeEach + void setUp() throws Exception { + imapStore = Mockito.mock(IMAPStore.class); + folder = Mockito.mock(Folder.class); + doReturn(folder).when(imapStore).getFolder(anyString()); + storeIndex = Mockito.spy(new StoreIndex()); + } + + @AfterEach + void tearDown() { + storeIndex = null; + folder = null; + imapStore = null; + } + + @Test + void run_emptyFolder_shouldOnlyUpdateIndexes() throws Exception { + // Given + final FolderCrawler folderCrawler = new FolderCrawler( + imapStore, "FolderName", 0, 100, storeIndex); + doReturn(new Message[0]).when(folder).getMessages(eq(0), eq(100)); + // When + folderCrawler.run(); + // Then + verify(imapStore, times(1)).getFolder(eq("FolderName")); + verify(storeIndex, times(1)).updatedIndexedMessageCount(eq(0L)); + verify(storeIndex, times(1)).updatedSkippedMessageCount(eq(0L)); + } + + @Test + void run_notEmptyFolderAndStoreWithExceptions_shouldReturn() throws Exception { + // Given + final FolderCrawler folderCrawler = new FolderCrawler( + imapStore, "FolderName", 0, 100, storeIndex); + final Message message = Mockito.mock(Message.class); + doReturn(new Message[]{message}).when(folder).getMessages(eq(0), eq(100)); + doReturn(true).when(storeIndex).hasCrawlException(); + // When + folderCrawler.run(); + // Then + verify(imapStore, times(1)).getFolder(eq("FolderName")); + verify(storeIndex, times(0)).updatedIndexedMessageCount(anyLong()); + verify(storeIndex, times(0)).updatedSkippedMessageCount(anyLong()); + } + + @Test + void run_notEmptyFolderAndRepeatedMessages_shouldUpdateIndexes() throws Exception { + // Given + final FolderCrawler folderCrawler = new FolderCrawler( + imapStore, "FolderName", 0, 100, storeIndex); + final IMAPMessage message = Mockito.mock(IMAPMessage.class); + doReturn(new String[]{"1337"}).when(message).getHeader("Message-Id"); + final IMAPMessage repeatedMessage = Mockito.mock(IMAPMessage.class); + doReturn(new String[]{"313373"}).when(repeatedMessage).getHeader("Message-Id"); + storeIndex.getFolderMessages("FolderName").add(new MessageId(repeatedMessage)); + doReturn(new Message[]{message, repeatedMessage}).when(folder).getMessages(eq(0), eq(100)); + // When + folderCrawler.run(); + // Then + verify(imapStore, times(1)).getFolder(eq("FolderName")); + assertThat(storeIndex.getIndexedMessageCount(), equalTo(1L)); + assertThat(storeIndex.getSkippedMessageCount(), equalTo(1L)); + } + + @Test + void run_notEmptyFolderAndThrowsMessageIdExceptionWithCause_shouldUpdateIndexesAndAddCrawlException() throws Exception { + // Given + final FolderCrawler folderCrawler = new FolderCrawler( + imapStore, "FolderName", 0, 100, storeIndex); + final IMAPMessage message = Mockito.mock(IMAPMessage.class); + doThrow(new MessagingException()).when(message).getHeader("Message-Id"); + doReturn(new Message[]{message}).when(folder).getMessages(eq(0), eq(100)); + // When + folderCrawler.run(); + // Then + verify(imapStore, times(1)).getFolder(eq("FolderName")); + verify(storeIndex, times(1)).updatedIndexedMessageCount(eq(0L)); + verify(storeIndex, times(1)).updatedSkippedMessageCount(eq(0L)); + assertThat(storeIndex.getIndexedMessageCount(), equalTo(0L)); + assertThat(storeIndex.getSkippedMessageCount(), equalTo(0L)); + assertThat(storeIndex.hasCrawlException(), equalTo(true)); + assertThat(storeIndex.getCrawlExceptions(), hasSize(1)); + } + +} diff --git a/src/test/java/com/marcnuri/mnimapsync/store/MessageCopierTest.java b/src/test/java/com/marcnuri/mnimapsync/store/MessageCopierTest.java new file mode 100644 index 0000000..82acd33 --- /dev/null +++ b/src/test/java/com/marcnuri/mnimapsync/store/MessageCopierTest.java @@ -0,0 +1,149 @@ +/* + * MessageCopierTest.java + * + * Created on 2019-08-17, 8:24 + * + * Copyright 2019 Marc Nuri San Felix + * + * Licensed 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 com.marcnuri.mnimapsync.store; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import com.sun.mail.imap.IMAPFolder; +import com.sun.mail.imap.IMAPMessage; +import com.sun.mail.imap.IMAPStore; +import java.util.HashSet; +import java.util.Set; +import javax.mail.Message; +import javax.mail.MessagingException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.Mockito; + +/** + * Created by Marc Nuri on 2019-08-17. + */ +public class MessageCopierTest { + + private IMAPFolder imapFolder; + private IMAPStore imapStore; + private StoreIndex sourceIndex; + private StoreIndex targetIndex; + private StoreCopier storeCopier; + + @BeforeEach + void setUp() throws Exception { + imapFolder = Mockito.mock(IMAPFolder.class); + doReturn('.').when(imapFolder).getSeparator(); + imapStore = Mockito.mock(IMAPStore.class); + doReturn(imapFolder).when(imapStore).getFolder(anyString()); + doReturn(imapFolder).when(imapStore).getDefaultFolder(); + sourceIndex = Mockito.spy(new StoreIndex()); + targetIndex = Mockito.spy(new StoreIndex()); + storeCopier = Mockito.spy(new StoreCopier(imapStore, sourceIndex, imapStore, targetIndex, 1)); + } + + @AfterEach + void tearDown() { + storeCopier = null; + targetIndex = null; + sourceIndex = null; + imapStore = null; + imapFolder = null; + } + + @Test + void run_emptyFolder_shouldOnlyUpdateIndexes() throws Exception { + // Given + final MessageCopier messageCopier = new MessageCopier( + storeCopier, "Source Folder", "Target Folder", 0, 100, new HashSet<>()); + doReturn(new Message[0]).when(imapFolder).getMessages(eq(0), eq(100)); + // When + messageCopier.run(); + // Then + verify(storeCopier, times(1)).updatedMessagesCopiedCount(eq(0L)); + verify(storeCopier, times(1)).updateMessagesSkippedCount(eq(0L)); + verify(sourceIndex, times(1)).updatedIndexedMessageCount(eq(0L)); + } + + @Test + void run_folderWithAlreadyCopiedMessages_shouldOnlyUpdateIndexes() throws Exception { + // Given + final Set copiedMessages = new HashSet<>(); + final MessageCopier messageCopier = new MessageCopier( + storeCopier, "Source Folder", "Target Folder", 0, 100, copiedMessages); + final IMAPMessage message = Mockito.mock(IMAPMessage.class); + doReturn(new String[]{"1337"}).when(message).getHeader("Message-Id"); + copiedMessages.add(new MessageId(message)); + doReturn(new Message[]{message}).when(imapFolder).getMessages(eq(0), eq(100)); + // When + messageCopier.run(); + // Then + verify(storeCopier, times(1)).updatedMessagesCopiedCount(eq(0L)); + verify(storeCopier, times(1)).updateMessagesSkippedCount(eq(1L)); + verify(sourceIndex, times(1)).updatedIndexedMessageCount(eq(1L)); + assertThat(storeCopier.getMessagesSkippedCount(), equalTo(1L)); + } + + @Test + void run_folderWithCopiedAndNonCopiedMessages_shouldUpdateIndexesAndCopy() throws Exception { + // Given + final Set copiedMessages = new HashSet<>(); + final MessageCopier messageCopier = new MessageCopier( + storeCopier, "Source Folder", "Target Folder", 0, 100, copiedMessages); + final IMAPMessage copiedMessage = Mockito.mock(IMAPMessage.class); + doReturn(new String[]{"1337"}).when(copiedMessage).getHeader("Message-Id"); + copiedMessages.add(new MessageId(copiedMessage)); + final IMAPMessage newMessage = Mockito.mock(IMAPMessage.class); + doReturn(new String[]{"313373"}).when(newMessage).getHeader("Message-Id"); + doReturn(new Message[]{copiedMessage, newMessage}).when(imapFolder).getMessages(eq(0), eq(100)); + // When + messageCopier.run(); + // Then + verify(imapFolder, times(1)).appendMessages(ArgumentMatchers.any()); + verify(storeCopier, times(1)).updatedMessagesCopiedCount(eq(1L)); + verify(storeCopier, times(1)).updateMessagesSkippedCount(eq(1L)); + verify(sourceIndex, times(1)).updatedIndexedMessageCount(eq(2L)); + assertThat(storeCopier.getMessagesSkippedCount(), equalTo(1L)); + assertThat(storeCopier.getMessagesCopiedCount(), equalTo(1L)); + } + + @Test + void run_folderThrowsException_shouldOnlyUpdateIndexes() throws Exception { + // Given + final MessageCopier messageCopier = new MessageCopier( + storeCopier, "Source Folder", "Target Folder", 0, 100, new HashSet<>()); + doThrow(new MessagingException()).when(imapFolder).open(anyInt()); + // When + messageCopier.run(); + // Then + assertThat(storeCopier.getCopyExceptions(), hasSize(1)); + verify(storeCopier, times(1)).updatedMessagesCopiedCount(eq(0L)); + verify(storeCopier, times(1)).updateMessagesSkippedCount(eq(0L)); + verify(sourceIndex, times(1)).updatedIndexedMessageCount(eq(0L)); + } +} diff --git a/src/test/java/com/marcnuri/mnimapsync/store/MessageDeleterTest.java b/src/test/java/com/marcnuri/mnimapsync/store/MessageDeleterTest.java new file mode 100644 index 0000000..d635438 --- /dev/null +++ b/src/test/java/com/marcnuri/mnimapsync/store/MessageDeleterTest.java @@ -0,0 +1,127 @@ +/* + * MessageDeleterTest.java + * + * Created on 2019-08-18, 9:23 + * + * Copyright 2019 Marc Nuri San Felix + * + * Licensed 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 com.marcnuri.mnimapsync.store; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import com.sun.mail.imap.IMAPFolder; +import com.sun.mail.imap.IMAPMessage; +import com.sun.mail.imap.IMAPStore; +import java.util.HashSet; +import java.util.Set; +import javax.mail.Flags.Flag; +import javax.mail.Message; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +/** + * Created by Marc Nuri on 2019-08-18. + */ +public class MessageDeleterTest { + + private IMAPFolder imapFolder; + private IMAPStore imapStore; + private StoreIndex sourceIndex; + private StoreDeleter storeDeleter; + + @BeforeEach + void setUp() throws Exception { + imapFolder = Mockito.mock(IMAPFolder.class); + doReturn('.').when(imapFolder).getSeparator(); + imapStore = Mockito.mock(IMAPStore.class); + doReturn(imapFolder).when(imapStore).getFolder(anyString()); + doReturn(imapFolder).when(imapStore).getDefaultFolder(); + sourceIndex = Mockito.spy(new StoreIndex()); + storeDeleter = Mockito.spy(new StoreDeleter(imapStore, sourceIndex, imapStore, 1)); + } + + @AfterEach + void tearDown() { + storeDeleter = null; + sourceIndex = null; + imapStore = null; + } + + @Test + void run_emptyFolder_shouldOnlyUpdateIndexes() throws Exception { + // Given + final MessageDeleter messageDeleter = new MessageDeleter( + storeDeleter, "Source Folder", "Target Folder", + 0, 100, true, new HashSet<>()); + doReturn(new Message[0]).when(imapFolder).getMessages(eq(0), eq(100)); + // When + messageDeleter.run(); + // Then + verify(storeDeleter, times(1)).updatedMessagesDeletedCount(eq(0L)); + verify(storeDeleter, times(1)).updateMessagesSkippedCount(eq(0L)); + } + + @Test + void run_folderWithNonDeletableMessages_shouldOnlyUpdateIndexes() throws Exception { + // Given + final Set sourceFolderMessages = new HashSet<>(); + final MessageDeleter messageDeleter = new MessageDeleter( + storeDeleter, "Source Folder", "Target Folder", + 0, 100, true, sourceFolderMessages); + final IMAPMessage message = Mockito.mock(IMAPMessage.class); + doReturn(new String[]{"1337"}).when(message).getHeader("Message-Id"); + sourceFolderMessages.add(new MessageId(message)); + doReturn(new Message[]{message}).when(imapFolder).getMessages(eq(0), eq(100)); + // When + messageDeleter.run(); + // Then + verify(imapFolder, times(1)).close(eq(true)); + verify(storeDeleter, times(1)).updatedMessagesDeletedCount(eq(0L)); + verify(storeDeleter, times(1)).updateMessagesSkippedCount(eq(1L)); + assertThat(storeDeleter.getMessagesSkippedCount(), equalTo(1L)); + } + + @Test + void run_folderWithAllMessageTypes_shouldOnlyUpdateIndexes() throws Exception { + // Given + final Set sourceFolderMessages = new HashSet<>(); + final MessageDeleter messageDeleter = new MessageDeleter( + storeDeleter, "Source Folder", "Target Folder", + 0, 100, true, sourceFolderMessages); + final IMAPMessage existingSourceMessage = Mockito.mock(IMAPMessage.class); + doReturn(new String[]{"1337"}).when(existingSourceMessage).getHeader("Message-Id"); + sourceFolderMessages.add(new MessageId(existingSourceMessage)); + final IMAPMessage deletableMessage = Mockito.mock(IMAPMessage.class); + doReturn(new String[]{"313373"}).when(deletableMessage).getHeader("Message-Id"); + doReturn(new Message[]{existingSourceMessage, deletableMessage}).when(imapFolder).getMessages(eq(0), eq(100)); + // When + messageDeleter.run(); + // Then + verify(deletableMessage, times(1)).setFlag(eq(Flag.DELETED), eq(true)); + verify(imapFolder, times(1)).close(eq(true)); + verify(storeDeleter, times(1)).updatedMessagesDeletedCount(eq(1L)); + verify(storeDeleter, times(1)).updateMessagesSkippedCount(eq(1L)); + assertThat(storeDeleter.getMessagesSkippedCount(), equalTo(1L)); + } +} diff --git a/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000..1f0955d --- /dev/null +++ b/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline