package co.smartreceipts.android.sync.drive.managers; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.robolectric.RobolectricTestRunner; import java.io.File; import java.util.Arrays; import co.smartreceipts.android.analytics.Analytics; import co.smartreceipts.android.model.Receipt; import co.smartreceipts.android.model.factory.ReceiptBuilderFactory; import co.smartreceipts.android.model.factory.ReceiptBuilderFactoryFactory; import co.smartreceipts.android.persistence.database.controllers.TableController; import co.smartreceipts.android.persistence.database.operations.DatabaseOperationMetadata; import co.smartreceipts.android.persistence.database.operations.OperationFamilyType; import co.smartreceipts.android.persistence.database.tables.ReceiptsTable; import co.smartreceipts.android.sync.drive.rx.DriveStreamMappings; import co.smartreceipts.android.sync.drive.rx.DriveStreamsManager; import co.smartreceipts.android.sync.model.SyncState; import co.smartreceipts.android.sync.model.impl.Identifier; import co.smartreceipts.android.sync.network.NetworkManager; import co.smartreceipts.android.sync.provider.SyncProvider; import io.reactivex.Single; import io.reactivex.schedulers.Schedulers; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(RobolectricTestRunner.class) public class DriveReceiptsManagerTest { // Class under testing DriveReceiptsManager mDriveReceiptsManager; @Mock TableController<Receipt> mReceiptTableController; @Mock ReceiptsTable mReceiptsTable; @Mock DriveStreamsManager mDriveTaskManager; @Mock DriveDatabaseManager mDriveDatabaseManager; @Mock NetworkManager mNetworkManager; @Mock Analytics mAnalytics; @Mock DriveStreamMappings mDriveStreamMappings; @Mock ReceiptBuilderFactoryFactory mReceiptBuilderFactoryFactory; @Mock ReceiptBuilderFactory mReceiptBuilderFactory1, mReceiptBuilderFactory2; @Mock Receipt mReceipt1, mReceipt2; @Mock SyncState mSyncState1, mSyncState2, mNewSyncState1, mNewSyncState2; @Mock File mFile; @Captor ArgumentCaptor<Receipt> mReceiptCaptor; @Captor ArgumentCaptor<Receipt> mUpdatedReceiptCaptor; @Captor ArgumentCaptor<DatabaseOperationMetadata> mOperationMetadataCaptor; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mReceipt1.getSyncState()).thenReturn(mSyncState1); when(mReceipt2.getSyncState()).thenReturn(mSyncState2); when(mReceiptBuilderFactory1.build()).thenReturn(mReceipt1); when(mReceiptBuilderFactory2.build()).thenReturn(mReceipt2); doAnswer(new Answer<ReceiptBuilderFactory>() { @Override public ReceiptBuilderFactory answer(InvocationOnMock invocation) throws Throwable { return mReceiptBuilderFactory1; } }).when(mReceiptBuilderFactoryFactory).build(mReceipt1); doAnswer(new Answer<ReceiptBuilderFactory>() { @Override public ReceiptBuilderFactory answer(InvocationOnMock invocation) throws Throwable { return mReceiptBuilderFactory2; } }).when(mReceiptBuilderFactoryFactory).build(mReceipt2); doAnswer(new Answer<ReceiptBuilderFactory>() { @Override public ReceiptBuilderFactory answer(InvocationOnMock invocation) throws Throwable { when(mReceipt1.getSyncState()).thenReturn((SyncState)invocation.getArguments()[0]); return mReceiptBuilderFactory1; } }).when(mReceiptBuilderFactory1).setSyncState(any(SyncState.class)); doAnswer(new Answer<ReceiptBuilderFactory>() { @Override public ReceiptBuilderFactory answer(InvocationOnMock invocation) throws Throwable { when(mReceipt2.getSyncState()).thenReturn((SyncState)invocation.getArguments()[0]); return mReceiptBuilderFactory2; } }).when(mReceiptBuilderFactory2).setSyncState(any(SyncState.class)); when(mNetworkManager.isNetworkAvailable()).thenReturn(true); mDriveReceiptsManager = new DriveReceiptsManager(mReceiptTableController, mReceiptsTable, mDriveTaskManager, mDriveDatabaseManager, mNetworkManager, mAnalytics, mDriveStreamMappings, mReceiptBuilderFactoryFactory, Schedulers.trampoline(), Schedulers.trampoline()); } @Test public void handleDelete() { when(mDriveTaskManager.deleteDriveFile(mSyncState1, true)).thenReturn(Single.just(mNewSyncState1)); when(mSyncState1.isSynced(SyncProvider.GoogleDrive)).thenReturn(false); when(mSyncState1.isMarkedForDeletion(SyncProvider.GoogleDrive)).thenReturn(true); mDriveReceiptsManager.handleDelete(mReceipt1); verify(mReceiptTableController).delete(mReceiptCaptor.capture(), mOperationMetadataCaptor.capture()); assertEquals(mReceipt1, mReceiptCaptor.getValue()); assertEquals(mNewSyncState1, mReceiptCaptor.getValue().getSyncState()); assertEquals(OperationFamilyType.Sync, mOperationMetadataCaptor.getValue().getOperationFamilyType()); } @Test public void handleDeleteWithoutNetwork() { when(mDriveTaskManager.deleteDriveFile(mSyncState1, true)).thenReturn(Single.just(mNewSyncState1)); when(mSyncState1.isSynced(SyncProvider.GoogleDrive)).thenReturn(false); when(mSyncState1.isMarkedForDeletion(SyncProvider.GoogleDrive)).thenReturn(true); when(mNetworkManager.isNetworkAvailable()).thenReturn(false); mDriveReceiptsManager.handleDelete(mReceipt1); verify(mReceiptTableController, never()).delete(mReceiptCaptor.capture(), mOperationMetadataCaptor.capture()); } @Test(expected = IllegalArgumentException.class) public void handleDeleteForIllegalSyncState() { when(mSyncState1.isSynced(SyncProvider.GoogleDrive)).thenReturn(true); mDriveReceiptsManager.handleDelete(mReceipt1); } @Test(expected = IllegalArgumentException.class) public void handleDeleteForIllegalMarkedForDeletionState() { when(mSyncState1.isMarkedForDeletion(SyncProvider.GoogleDrive)).thenReturn(false); mDriveReceiptsManager.handleDelete(mReceipt1); } @Test public void handleInsertOrUpdateWithoutNetwork() { when(mDriveTaskManager.uploadFileToDrive(mSyncState1, mFile)).thenReturn(Single.just(mNewSyncState1)); when(mSyncState1.getSyncId(SyncProvider.GoogleDrive)).thenReturn(null); when(mSyncState1.isSynced(SyncProvider.GoogleDrive)).thenReturn(false); when(mSyncState1.isMarkedForDeletion(SyncProvider.GoogleDrive)).thenReturn(false); when(mReceipt1.getFile()).thenReturn(mFile); when(mFile.exists()).thenReturn(true); when(mNetworkManager.isNetworkAvailable()).thenReturn(false); mDriveReceiptsManager.handleInsertOrUpdate(mReceipt1); verify(mReceiptTableController, never()).update(mReceiptCaptor.capture(), mUpdatedReceiptCaptor.capture(), mOperationMetadataCaptor.capture()); } @Test public void handleInsertOrUpdateForNewFile() { when(mDriveTaskManager.uploadFileToDrive(mSyncState1, mFile)).thenReturn(Single.just(mNewSyncState1)); when(mSyncState1.getSyncId(SyncProvider.GoogleDrive)).thenReturn(null); when(mSyncState1.isSynced(SyncProvider.GoogleDrive)).thenReturn(false); when(mSyncState1.isMarkedForDeletion(SyncProvider.GoogleDrive)).thenReturn(false); when(mReceipt1.getFile()).thenReturn(mFile); when(mFile.exists()).thenReturn(true); mDriveReceiptsManager.handleInsertOrUpdate(mReceipt1); verify(mReceiptTableController).update(mReceiptCaptor.capture(), mUpdatedReceiptCaptor.capture(), mOperationMetadataCaptor.capture()); assertNotNull(mReceiptCaptor.getValue()); assertEquals(mReceipt1, mUpdatedReceiptCaptor.getValue()); assertEquals(mNewSyncState1, mUpdatedReceiptCaptor.getValue().getSyncState()); assertEquals(OperationFamilyType.Sync, mOperationMetadataCaptor.getValue().getOperationFamilyType()); } @Test public void handleInsertOrUpdateForNonExistingNewFile() { when(mDriveStreamMappings.postInsertSyncState(mSyncState1, null)).thenReturn(mNewSyncState1); when(mSyncState1.getSyncId(SyncProvider.GoogleDrive)).thenReturn(null); when(mSyncState1.isSynced(SyncProvider.GoogleDrive)).thenReturn(false); when(mSyncState1.isMarkedForDeletion(SyncProvider.GoogleDrive)).thenReturn(false); when(mReceipt1.getFile()).thenReturn(mFile); when(mFile.exists()).thenReturn(false); mDriveReceiptsManager.handleInsertOrUpdate(mReceipt1); verify(mReceiptTableController).update(mReceiptCaptor.capture(), mUpdatedReceiptCaptor.capture(), mOperationMetadataCaptor.capture()); assertNotNull(mReceiptCaptor.getValue()); assertEquals(mReceipt1, mUpdatedReceiptCaptor.getValue()); assertEquals(mNewSyncState1, mUpdatedReceiptCaptor.getValue().getSyncState()); assertEquals(OperationFamilyType.Sync, mOperationMetadataCaptor.getValue().getOperationFamilyType()); } @Test public void handleInsertWithoutFile() { when(mDriveStreamMappings.postInsertSyncState(mSyncState1, null)).thenReturn(mNewSyncState1); when(mSyncState1.getSyncId(SyncProvider.GoogleDrive)).thenReturn(null); when(mSyncState1.isSynced(SyncProvider.GoogleDrive)).thenReturn(false); when(mSyncState1.isMarkedForDeletion(SyncProvider.GoogleDrive)).thenReturn(false); when(mReceipt1.getFile()).thenReturn(null); mDriveReceiptsManager.handleInsertOrUpdate(mReceipt1); verify(mReceiptTableController).update(mReceiptCaptor.capture(), mUpdatedReceiptCaptor.capture(), mOperationMetadataCaptor.capture()); assertNotNull(mReceiptCaptor.getValue()); assertEquals(mReceipt1, mUpdatedReceiptCaptor.getValue()); assertEquals(mNewSyncState1, mUpdatedReceiptCaptor.getValue().getSyncState()); assertEquals(OperationFamilyType.Sync, mOperationMetadataCaptor.getValue().getOperationFamilyType()); } @Test public void handleUpdateWithNewFile() { final Identifier identifier = new Identifier("id"); when(mDriveTaskManager.updateDriveFile(mSyncState1, mFile)).thenReturn(Single.just(mNewSyncState1)); when(mSyncState1.getSyncId(SyncProvider.GoogleDrive)).thenReturn(identifier); when(mSyncState1.isSynced(SyncProvider.GoogleDrive)).thenReturn(false); when(mSyncState1.isMarkedForDeletion(SyncProvider.GoogleDrive)).thenReturn(false); when(mReceipt1.getFile()).thenReturn(mFile); when(mFile.exists()).thenReturn(true); mDriveReceiptsManager.handleInsertOrUpdate(mReceipt1); verify(mReceiptTableController).update(mReceiptCaptor.capture(), mUpdatedReceiptCaptor.capture(), mOperationMetadataCaptor.capture()); assertNotNull(mReceiptCaptor.getValue()); assertEquals(mReceipt1, mUpdatedReceiptCaptor.getValue()); assertEquals(mNewSyncState1, mUpdatedReceiptCaptor.getValue().getSyncState()); assertEquals(OperationFamilyType.Sync, mOperationMetadataCaptor.getValue().getOperationFamilyType()); } @Test public void handleUpdateToDeleteExistingFile() { final Identifier identifier = new Identifier("id"); when(mDriveTaskManager.deleteDriveFile(mSyncState1, false)).thenReturn(Single.just(mNewSyncState1)); when(mSyncState1.getSyncId(SyncProvider.GoogleDrive)).thenReturn(identifier); when(mSyncState1.isSynced(SyncProvider.GoogleDrive)).thenReturn(false); when(mSyncState1.isMarkedForDeletion(SyncProvider.GoogleDrive)).thenReturn(false); when(mReceipt1.getFile()).thenReturn(null); mDriveReceiptsManager.handleInsertOrUpdate(mReceipt1); verify(mReceiptTableController).update(mReceiptCaptor.capture(), mUpdatedReceiptCaptor.capture(), mOperationMetadataCaptor.capture()); assertNotNull(mReceiptCaptor.getValue()); assertEquals(mReceipt1, mUpdatedReceiptCaptor.getValue()); assertEquals(mNewSyncState1, mUpdatedReceiptCaptor.getValue().getSyncState()); assertEquals(OperationFamilyType.Sync, mOperationMetadataCaptor.getValue().getOperationFamilyType()); } @Test(expected = IllegalArgumentException.class) public void handleInsertOrUpdateForIllegalSyncState() { when(mSyncState1.isSynced(SyncProvider.GoogleDrive)).thenReturn(true); mDriveReceiptsManager.handleInsertOrUpdate(mReceipt1); } @Test(expected = IllegalArgumentException.class) public void handleInsertOrUpdateForIllegalMarkedForDeletionState() { when(mSyncState1.isMarkedForDeletion(SyncProvider.GoogleDrive)).thenReturn(true); mDriveReceiptsManager.handleInsertOrUpdate(mReceipt1); } @Test public void initialize() { final DriveReceiptsManager spiedManager = spy(mDriveReceiptsManager); doNothing().when(spiedManager).handleInsertOrUpdateInternal(any(Receipt.class)); doNothing().when(spiedManager).handleDeleteInternal(any(Receipt.class)); when(mSyncState1.isSynced(SyncProvider.GoogleDrive)).thenReturn(false); when(mSyncState1.isMarkedForDeletion(SyncProvider.GoogleDrive)).thenReturn(false); when(mSyncState2.isSynced(SyncProvider.GoogleDrive)).thenReturn(false); when(mSyncState2.isMarkedForDeletion(SyncProvider.GoogleDrive)).thenReturn(true); when(mReceiptsTable.getUnsynced(SyncProvider.GoogleDrive)).thenReturn(Single.just(Arrays.asList(mReceipt1, mReceipt2))); spiedManager.initialize(); verify(spiedManager).handleInsertOrUpdateInternal(mReceipt1); verify(spiedManager).handleDeleteInternal(mReceipt2); verify(mDriveDatabaseManager).syncDatabase(); } @Test public void initializeWithoutNetwork() { final DriveReceiptsManager spiedManager = spy(mDriveReceiptsManager); doNothing().when(spiedManager).handleInsertOrUpdateInternal(any(Receipt.class)); doNothing().when(spiedManager).handleDeleteInternal(any(Receipt.class)); when(mSyncState1.isSynced(SyncProvider.GoogleDrive)).thenReturn(false); when(mSyncState1.isMarkedForDeletion(SyncProvider.GoogleDrive)).thenReturn(false); when(mSyncState2.isSynced(SyncProvider.GoogleDrive)).thenReturn(false); when(mSyncState2.isMarkedForDeletion(SyncProvider.GoogleDrive)).thenReturn(true); when(mReceiptsTable.getUnsynced(SyncProvider.GoogleDrive)).thenReturn(Single.just(Arrays.asList(mReceipt1, mReceipt2))); when(mNetworkManager.isNetworkAvailable()).thenReturn(false); spiedManager.initialize(); verify(spiedManager, never()).handleInsertOrUpdateInternal(mReceipt1); verify(spiedManager, never()).handleDeleteInternal(mReceipt2); verify(mDriveDatabaseManager, never()).syncDatabase(); } }