package co.smartreceipts.android.persistence.database.tables; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.support.annotation.NonNull; import org.junit.After; 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.verification.VerificationMode; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import java.io.File; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.TimeZone; import co.smartreceipts.android.model.Trip; import co.smartreceipts.android.model.factory.TripBuilderFactory; import co.smartreceipts.android.persistence.DatabaseHelper; import co.smartreceipts.android.persistence.PersistenceManager; import co.smartreceipts.android.persistence.database.defaults.TableDefaultsCustomizer; import co.smartreceipts.android.persistence.database.operations.DatabaseOperationMetadata; import co.smartreceipts.android.settings.UserPreferenceManager; import co.smartreceipts.android.settings.catalog.UserPreference; import wb.android.storage.StorageManager; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; @RunWith(RobolectricTestRunner.class) public class TripsTableTest { private static final String NAME_1 = "Trip1"; private static final String NAME_2 = "Trip2"; private static final String NAME_3 = "Trip3"; // Use the to verify that sort ordering is on the End Date (i.e. End3 > End1 > End2) private static final long START_DATE_2 = 1409703721000L; private static final long START_DATE_1 = 1409703722000L; private static final long END_DATE_2 = 1409703793000L; private static final long END_DATE_1 = 1409703794000L; private static final long START_DATE_3 = 1409703891000L; private static final long END_DATE_3 = 1409703893000L; private static final String START_TIMEZONE = TimeZone.getAvailableIDs()[0]; private static final String END_TIMEZONE = TimeZone.getAvailableIDs()[1]; private static final String COMMENT = "Comment"; private static final String COST_CENTER = "Cost Center"; private static final String CURRENCY_CODE = "USD"; private static final String USER_PREFERENCES_CURRENCY_CODE = "EUR"; // Class under test TripsTable mTripsTable; @Mock SQLiteDatabase mSQLiteDatabase; @Mock TableDefaultsCustomizer mTableDefaultsCustomizer; @Mock PersistenceManager mPersistenceManager; @Mock StorageManager mStorageManager; @Mock UserPreferenceManager mPreferences; @Captor ArgumentCaptor<String> mSqlCaptor; SQLiteOpenHelper mSQLiteOpenHelper; Trip mTrip1; Trip mTrip2; TripBuilderFactory mBuilder; @Before public void setup() { MockitoAnnotations.initMocks(this); mSQLiteOpenHelper = new TestSQLiteOpenHelper(RuntimeEnvironment.application); when(mPersistenceManager.getStorageManager()).thenReturn(mStorageManager); when(mPersistenceManager.getPreferenceManager()).thenReturn(mPreferences); when(mStorageManager.getFile(NAME_1)).thenReturn(new File(NAME_1)); when(mStorageManager.getFile(NAME_2)).thenReturn(new File(NAME_2)); when(mStorageManager.getFile(NAME_3)).thenReturn(new File(NAME_3)); when(mStorageManager.mkdir(NAME_1)).thenReturn(new File(NAME_1)); when(mStorageManager.mkdir(NAME_2)).thenReturn(new File(NAME_2)); when(mStorageManager.mkdir(NAME_3)).thenReturn(new File(NAME_3)); when(mPreferences.get(UserPreference.General.DefaultCurrency)).thenReturn(USER_PREFERENCES_CURRENCY_CODE); mTripsTable = new TripsTable(mSQLiteOpenHelper, mPersistenceManager.getStorageManager(), mPersistenceManager.getPreferenceManager()); // Now create the table and insert some defaults mTripsTable.onCreate(mSQLiteOpenHelper.getWritableDatabase(), mTableDefaultsCustomizer); mBuilder = new TripBuilderFactory(); mBuilder.setStartTimeZone(START_TIMEZONE).setEndTimeZone(END_TIMEZONE).setComment(COMMENT).setCostCenter(COST_CENTER).setDefaultCurrency(CURRENCY_CODE, mPreferences.get(UserPreference.General.DefaultCurrency)); mTrip1 = mTripsTable.insert(mBuilder.setStartDate(START_DATE_1).setEndDate(END_DATE_1).setDirectory(mStorageManager.getFile(NAME_1)).build(), new DatabaseOperationMetadata()).blockingGet(); mTrip2 = mTripsTable.insert(mBuilder.setStartDate(START_DATE_2).setEndDate(END_DATE_2).setDirectory(mStorageManager.getFile(NAME_2)).build(), new DatabaseOperationMetadata()).blockingGet(); } @After public void tearDown() { mSQLiteOpenHelper.getWritableDatabase().execSQL("DROP TABLE IF EXISTS " + mTripsTable.getTableName()); } @Test public void getTableName() { assertEquals("trips", mTripsTable.getTableName()); } @Test public void onCreate() { final TableDefaultsCustomizer customizer = mock(TableDefaultsCustomizer.class); mTripsTable.onCreate(mSQLiteDatabase, customizer); verify(mSQLiteDatabase).execSQL(mSqlCaptor.capture()); verifyZeroInteractions(customizer); assertTrue(mSqlCaptor.getValue().contains("CREATE TABLE trips")); // Table name assertTrue(mSqlCaptor.getValue().contains("name TEXT PRIMARY KEY")); assertTrue(mSqlCaptor.getValue().contains("from_date DATE")); assertTrue(mSqlCaptor.getValue().contains("to_date DATE")); assertTrue(mSqlCaptor.getValue().contains("from_timezone TEXT")); assertTrue(mSqlCaptor.getValue().contains("to_timezone TEXT")); assertTrue(mSqlCaptor.getValue().contains("trips_comment TEXT")); assertTrue(mSqlCaptor.getValue().contains("trips_cost_center TEXT")); assertTrue(mSqlCaptor.getValue().contains("trips_default_currency TEXT")); assertTrue(mSqlCaptor.getValue().contains("trips_filters TEXT")); assertTrue(mSqlCaptor.getValue().contains("trip_processing_status TEXT")); assertTrue(mSqlCaptor.getValue().contains("drive_sync_id TEXT")); assertTrue(mSqlCaptor.getValue().contains("drive_is_synced BOOLEAN DEFAULT 0")); assertTrue(mSqlCaptor.getValue().contains("drive_marked_for_deletion BOOLEAN DEFAULT 0")); assertTrue(mSqlCaptor.getValue().contains("last_local_modification_time DATE")); } @Test public void onUpgradeFromV8() { final int oldVersion = 8; final int newVersion = DatabaseHelper.DATABASE_VERSION; final TableDefaultsCustomizer customizer = mock(TableDefaultsCustomizer.class); mTripsTable.onUpgrade(mSQLiteDatabase, oldVersion, newVersion, customizer); verifyZeroInteractions(customizer); verifyV8Upgrade(times(1)); verifyV10Upgrade(times(1)); verifyV11Upgrade(times(1)); verifyV12Upgrade(times(1)); verifyV14Upgrade(times(1)); } @Test public void onUpgradeFromV10() { final int oldVersion = 10; final int newVersion = DatabaseHelper.DATABASE_VERSION; final TableDefaultsCustomizer customizer = mock(TableDefaultsCustomizer.class); mTripsTable.onUpgrade(mSQLiteDatabase, oldVersion, newVersion, customizer); verifyZeroInteractions(customizer); verifyV8Upgrade(never()); verifyV10Upgrade(times(1)); verifyV11Upgrade(times(1)); verifyV12Upgrade(times(1)); verifyV14Upgrade(times(1)); } @Test public void onUpgradeFromV11() { final int oldVersion = 11; final int newVersion = DatabaseHelper.DATABASE_VERSION; final TableDefaultsCustomizer customizer = mock(TableDefaultsCustomizer.class); mTripsTable.onUpgrade(mSQLiteDatabase, oldVersion, newVersion, customizer); verifyZeroInteractions(customizer); verifyV8Upgrade(never()); verifyV10Upgrade(never()); verifyV11Upgrade(times(1)); verifyV12Upgrade(times(1)); verifyV14Upgrade(times(1)); } @Test public void onUpgradeFromV12() { final int oldVersion = 12; final int newVersion = DatabaseHelper.DATABASE_VERSION; final TableDefaultsCustomizer customizer = mock(TableDefaultsCustomizer.class); mTripsTable.onUpgrade(mSQLiteDatabase, oldVersion, newVersion, customizer); verifyZeroInteractions(customizer); verifyV8Upgrade(never()); verifyV10Upgrade(never()); verifyV11Upgrade(never()); verifyV12Upgrade(times(1)); verifyV14Upgrade(times(1)); } @Test public void onUpgradeFromV14() { final int oldVersion = 14; final int newVersion = DatabaseHelper.DATABASE_VERSION; final TableDefaultsCustomizer customizer = mock(TableDefaultsCustomizer.class); mTripsTable.onUpgrade(mSQLiteDatabase, oldVersion, newVersion, customizer); verifyZeroInteractions(customizer); verifyV8Upgrade(never()); verifyV10Upgrade(never()); verifyV11Upgrade(never()); verifyV12Upgrade(never()); verifyV14Upgrade(times(1)); } private void verifyV8Upgrade(@NonNull VerificationMode verificationMode) { verify(mSQLiteDatabase, verificationMode).execSQL("ALTER TABLE trips ADD from_timezone TEXT"); verify(mSQLiteDatabase, verificationMode).execSQL("ALTER TABLE trips ADD to_timezone TEXT"); } private void verifyV10Upgrade(@NonNull VerificationMode verificationMode) { verify(mSQLiteDatabase, verificationMode).execSQL("ALTER TABLE trips ADD trips_comment TEXT"); verify(mSQLiteDatabase, verificationMode).execSQL("ALTER TABLE trips ADD trips_default_currency TEXT"); } private void verifyV11Upgrade(@NonNull VerificationMode verificationMode) { verify(mSQLiteDatabase, verificationMode).execSQL("ALTER TABLE trips ADD trips_filters TEXT"); } private void verifyV12Upgrade(@NonNull VerificationMode verificationMode) { verify(mSQLiteDatabase, verificationMode).execSQL("ALTER TABLE trips ADD trips_cost_center TEXT"); verify(mSQLiteDatabase, verificationMode).execSQL("ALTER TABLE trips ADD trip_processing_status TEXT"); } private void verifyV14Upgrade(@NonNull VerificationMode verificationMode) { verify(mSQLiteDatabase, verificationMode).execSQL("ALTER TABLE " + mTripsTable.getTableName() + " ADD drive_sync_id TEXT"); verify(mSQLiteDatabase, verificationMode).execSQL("ALTER TABLE " + mTripsTable.getTableName() + " ADD drive_is_synced BOOLEAN DEFAULT 0"); verify(mSQLiteDatabase, verificationMode).execSQL("ALTER TABLE " + mTripsTable.getTableName() + " ADD drive_marked_for_deletion BOOLEAN DEFAULT 0"); verify(mSQLiteDatabase, verificationMode).execSQL("ALTER TABLE " + mTripsTable.getTableName() + " ADD last_local_modification_time DATE"); } @Test public void onUpgradeAlreadyOccurred() { final int oldVersion = DatabaseHelper.DATABASE_VERSION; final int newVersion = DatabaseHelper.DATABASE_VERSION; final TableDefaultsCustomizer customizer = mock(TableDefaultsCustomizer.class); mTripsTable.onUpgrade(mSQLiteDatabase, oldVersion, newVersion, customizer); verify(mSQLiteDatabase, never()).execSQL(mSqlCaptor.capture()); verifyZeroInteractions(customizer); } @Test public void get() { final List<Trip> trips = mTripsTable.get().blockingGet(); assertEquals(trips, Arrays.asList(mTrip1, mTrip2)); } @Test public void insert() { final Trip trip = mTripsTable.insert(mBuilder.setStartDate(START_DATE_3).setEndDate(END_DATE_3).setDirectory(mStorageManager.getFile(NAME_3)).build(), new DatabaseOperationMetadata()).blockingGet(); assertNotNull(trip); final List<Trip> trips = mTripsTable.get().blockingGet(); // Also confirm the new one is first b/c of date ordering assertEquals(trips, Arrays.asList(trip, mTrip1, mTrip2)); } @Test public void findByPrimaryKey() { mTripsTable.findByPrimaryKey(NAME_1) .test() .assertNoErrors() .assertResult(mTrip1); } @Test public void findByPrimaryMissingKey() { mTripsTable.findByPrimaryKey("") .test() .assertError(Exception.class); } @Test public void update() { final Trip updatedTrip = mTripsTable.update(mTrip1, mBuilder.setDirectory(mStorageManager.getFile(NAME_3)).build(), new DatabaseOperationMetadata()).blockingGet(); assertNotNull(updatedTrip); assertFalse(mTrip1.equals(updatedTrip)); final List<Trip> trips = mTripsTable.get().blockingGet(); assertEquals(trips, Arrays.asList(updatedTrip, mTrip2)); } @Test public void delete() { assertEquals(mTrip1, mTripsTable.delete(mTrip1, new DatabaseOperationMetadata()).blockingGet()); final List<Trip> trips = mTripsTable.get().blockingGet(); assertEquals(trips, Collections.singletonList(mTrip2)); } }