package co.smartreceipts.android.persistence.database.tables.adapters;
import android.content.ContentValues;
import android.database.Cursor;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import java.io.File;
import java.math.BigDecimal;
import java.sql.Date;
import java.util.Collections;
import java.util.TimeZone;
import co.smartreceipts.android.model.Category;
import co.smartreceipts.android.model.PaymentMethod;
import co.smartreceipts.android.model.Price;
import co.smartreceipts.android.model.PriceCurrency;
import co.smartreceipts.android.model.Receipt;
import co.smartreceipts.android.model.Source;
import co.smartreceipts.android.model.Trip;
import co.smartreceipts.android.model.factory.ReceiptBuilderFactory;
import co.smartreceipts.android.model.gson.ExchangeRate;
import co.smartreceipts.android.model.impl.ImmutableCategoryImpl;
import co.smartreceipts.android.model.impl.ImmutablePaymentMethodImpl;
import co.smartreceipts.android.persistence.database.operations.DatabaseOperationMetadata;
import co.smartreceipts.android.persistence.database.operations.OperationFamilyType;
import co.smartreceipts.android.persistence.database.tables.Table;
import co.smartreceipts.android.persistence.database.tables.keys.PrimaryKey;
import co.smartreceipts.android.sync.model.SyncState;
import io.reactivex.Single;
import wb.android.storage.StorageManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(RobolectricTestRunner.class)
public class ReceiptDatabaseAdapterTest {
private static final int ID = 5;
private static final int PRIMARY_KEY_ID = 11;
private static final String PATH = "Image.jpg";
private static final String NAME = "Name";
private static final String PARENT = "Trip";
private static final String CATEGORY_NAME = "Category";
private static final Category CATEGORY = new ImmutableCategoryImpl(CATEGORY_NAME, "code");
private static final double PRICE = 12.55d;
private static final double TAX = 2.50d;
private static final String CURRENCY_CODE = "USD";
private static final double EXCHANGE_RATE_FOR_USD = 1.00d;
private static final ExchangeRate EXCHANGE_RATE = new ExchangeRate(CURRENCY_CODE, Collections.singletonMap(CURRENCY_CODE, EXCHANGE_RATE_FOR_USD));
private static final long DATE = 1409703721000L;
private static final String TIMEZONE = TimeZone.getDefault().getID();
private static final String COMMENT = "Comment";
private static final boolean REIMBURSABLE = true;
private static final int PAYMENT_METHOD_ID = 2;
private static final int DESCENDING_INDEX = 3;
private static final int ASCENDING_INDEX = 2;
private static final int CURSOR_COUNT = 4;
private static final PaymentMethod PAYMENT_METHOD = new ImmutablePaymentMethodImpl(PAYMENT_METHOD_ID, "method");
private static final boolean FULL_PAGE = true;
private static final String EXTRA1 = "extra1";
private static final String EXTRA2 = "extra2";
private static final String EXTRA3 = "extra3";
// Class under test
ReceiptDatabaseAdapter mReceiptDatabaseAdapter;
@Mock
Table<Trip, String> mTripsTable;
@Mock
Table<PaymentMethod, Integer> mPaymentMethodsTable;
@Mock
Table<Category, String> mCategoriesTable;
@Mock
StorageManager mStorageManager;
@Mock
Trip mTrip;
@Mock
Cursor mCursor;
@Mock
Receipt mReceipt;
@Mock
Price mPrice, mTax;
@Mock
PrimaryKey<Receipt, Integer> mPrimaryKey;
@Mock
SyncStateAdapter mSyncStateAdapter;
@Mock
SyncState mSyncState, mGetSyncState;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
final int idIndex = 1;
final int pathIndex = 2;
final int nameIndex = 3;
final int parentIndex = 4;
final int categoryIndex = 5;
final int priceIndex = 6;
final int taxIndex = 7;
final int exchangeRateIndex = 8;
final int dateIndex = 9;
final int timezoneIndex = 10;
final int commentIndex = 11;
final int expenseableIndex = 12;
final int currencyCodeIndex = 13;
final int paymentMethodKeyIndex = 14;
final int fullPageImageIndex = 15;
final int extraEdittext1Index = 17;
final int extraEdittext2Index = 18;
final int extraEdittext3Index = 19;
when(mCursor.getColumnIndex("id")).thenReturn(idIndex);
when(mCursor.getColumnIndex("path")).thenReturn(pathIndex);
when(mCursor.getColumnIndex("name")).thenReturn(nameIndex);
when(mCursor.getColumnIndex("parent")).thenReturn(parentIndex);
when(mCursor.getColumnIndex("category")).thenReturn(categoryIndex);
when(mCursor.getColumnIndex("price")).thenReturn(priceIndex);
when(mCursor.getColumnIndex("tax")).thenReturn(taxIndex);
when(mCursor.getColumnIndex("exchange_rate")).thenReturn(exchangeRateIndex);
when(mCursor.getColumnIndex("rcpt_date")).thenReturn(dateIndex);
when(mCursor.getColumnIndex("timezone")).thenReturn(timezoneIndex);
when(mCursor.getColumnIndex("comment")).thenReturn(commentIndex);
when(mCursor.getColumnIndex("expenseable")).thenReturn(expenseableIndex);
when(mCursor.getColumnIndex("isocode")).thenReturn(currencyCodeIndex);
when(mCursor.getColumnIndex("paymentMethodKey")).thenReturn(paymentMethodKeyIndex);
when(mCursor.getColumnIndex("fullpageimage")).thenReturn(fullPageImageIndex);
when(mCursor.getColumnIndex("extra_edittext_1")).thenReturn(extraEdittext1Index);
when(mCursor.getColumnIndex("extra_edittext_2")).thenReturn(extraEdittext2Index);
when(mCursor.getColumnIndex("extra_edittext_3")).thenReturn(extraEdittext3Index);
when(mCursor.getInt(idIndex)).thenReturn(ID);
when(mCursor.getString(pathIndex)).thenReturn(PATH);
when(mCursor.getString(nameIndex)).thenReturn(NAME);
when(mCursor.getString(parentIndex)).thenReturn(PARENT);
when(mCursor.getString(categoryIndex)).thenReturn(CATEGORY_NAME);
when(mCursor.getDouble(priceIndex)).thenReturn(PRICE);
when(mCursor.getDouble(taxIndex)).thenReturn(TAX);
when(mCursor.getDouble(exchangeRateIndex)).thenReturn(EXCHANGE_RATE_FOR_USD);
when(mCursor.getLong(dateIndex)).thenReturn(DATE);
when(mCursor.getString(timezoneIndex)).thenReturn(TIMEZONE);
when(mCursor.getString(commentIndex)).thenReturn(COMMENT);
when(mCursor.getInt(expenseableIndex)).thenReturn(REIMBURSABLE ? 1 : 0);
when(mCursor.getString(currencyCodeIndex)).thenReturn(CURRENCY_CODE);
when(mCursor.getInt(paymentMethodKeyIndex)).thenReturn(PAYMENT_METHOD_ID);
when(mCursor.getInt(fullPageImageIndex)).thenReturn(FULL_PAGE ? 1 : 0);
when(mCursor.getString(extraEdittext1Index)).thenReturn(EXTRA1);
when(mCursor.getString(extraEdittext2Index)).thenReturn(EXTRA2);
when(mCursor.getString(extraEdittext3Index)).thenReturn(EXTRA3);
when(mCursor.getCount()).thenReturn(CURSOR_COUNT);
when(mCursor.getPosition()).thenReturn(ASCENDING_INDEX - 1);
when(mReceipt.getId()).thenReturn(ID);
when(mReceipt.getFile()).thenReturn(new File(PATH));
when(mReceipt.getName()).thenReturn(NAME);
when(mReceipt.getTrip()).thenReturn(mTrip);
when(mReceipt.getCategory()).thenReturn(CATEGORY);
when(mReceipt.getPrice()).thenReturn(mPrice);
when(mReceipt.getTax()).thenReturn(mTax);
when(mReceipt.getDate()).thenReturn(new Date(DATE));
when(mReceipt.getTimeZone()).thenReturn(TimeZone.getTimeZone(TIMEZONE));
when(mReceipt.getComment()).thenReturn(COMMENT);
when(mReceipt.isReimbursable()).thenReturn(REIMBURSABLE);
when(mReceipt.getPaymentMethod()).thenReturn(PAYMENT_METHOD);
when(mReceipt.isFullPage()).thenReturn(FULL_PAGE);
when(mReceipt.getExtraEditText1()).thenReturn(EXTRA1);
when(mReceipt.getExtraEditText2()).thenReturn(EXTRA2);
when(mReceipt.getExtraEditText3()).thenReturn(EXTRA3);
when(mReceipt.getIndex()).thenReturn(DESCENDING_INDEX);
when(mReceipt.getSource()).thenReturn(Source.Undefined);
when(mReceipt.getSyncState()).thenReturn(mSyncState);
when(mTrip.getName()).thenReturn(PARENT);
when(mTrip.getDirectory()).thenReturn(new File(PARENT));
when(mTrip.getDefaultCurrencyCode()).thenReturn(CURRENCY_CODE);
when(mTrip.getTripCurrency()).thenReturn(PriceCurrency.getInstance(CURRENCY_CODE));
when(mPrice.getPrice()).thenReturn(new BigDecimal(PRICE));
when(mPrice.getCurrencyCode()).thenReturn(CURRENCY_CODE);
when(mPrice.getCurrency()).thenReturn(PriceCurrency.getInstance(CURRENCY_CODE));
when(mPrice.getExchangeRate()).thenReturn(EXCHANGE_RATE);
when(mTax.getPrice()).thenReturn(new BigDecimal(TAX));
when(mTax.getCurrencyCode()).thenReturn(CURRENCY_CODE);
when(mTax.getCurrency()).thenReturn(PriceCurrency.getInstance(CURRENCY_CODE));
when(mTax.getExchangeRate()).thenReturn(EXCHANGE_RATE);
when(mTripsTable.findByPrimaryKey(PARENT)).thenReturn(Single.just(mTrip));
when(mPaymentMethodsTable.findByPrimaryKey(PAYMENT_METHOD_ID)).thenReturn(Single.just(PAYMENT_METHOD));
when(mCategoriesTable.findByPrimaryKey(CATEGORY_NAME)).thenReturn(Single.just(CATEGORY));
when(mPrimaryKey.getPrimaryKeyValue(mReceipt)).thenReturn(PRIMARY_KEY_ID);
when(mStorageManager.getFile(new File(PARENT), PATH)).thenReturn(new File(PATH));
when(mSyncStateAdapter.read(mCursor)).thenReturn(mSyncState);
when(mSyncStateAdapter.get(any(SyncState.class), any(DatabaseOperationMetadata.class))).thenReturn(mGetSyncState);
mReceiptDatabaseAdapter = new ReceiptDatabaseAdapter(mTripsTable, mPaymentMethodsTable, mCategoriesTable, mStorageManager, mSyncStateAdapter);
}
@Test
public void read() throws Exception {
// Note: Full page is backwards in the database
final Receipt receipt = new ReceiptBuilderFactory(ID)
.setTrip(mTrip)
.setName(NAME)
.setPrice(PRICE)
.setTax(TAX)
.setExchangeRate(EXCHANGE_RATE)
.setCategory(CATEGORY)
.setFile(new File(PATH))
.setDate(DATE)
.setTimeZone(TIMEZONE)
.setComment(COMMENT)
.setIsReimbursable(REIMBURSABLE)
.setCurrency(CURRENCY_CODE)
.setIsFullPage(!FULL_PAGE)
.setIndex(DESCENDING_INDEX)
.setPaymentMethod(PAYMENT_METHOD)
.setExtraEditText1(EXTRA1)
.setExtraEditText2(EXTRA2)
.setExtraEditText3(EXTRA3)
.setSyncState(mSyncState)
.build();
assertEquals(receipt, mReceiptDatabaseAdapter.read(mCursor));
}
@Test
public void readForSelectionDescending() throws Exception {
// Note: Full page is backwards in the database
final Receipt receipt = new ReceiptBuilderFactory(ID)
.setTrip(mTrip)
.setName(NAME)
.setPrice(PRICE)
.setTax(TAX)
.setExchangeRate(EXCHANGE_RATE)
.setCategory(CATEGORY)
.setFile(new File(PATH))
.setDate(DATE)
.setTimeZone(TIMEZONE)
.setComment(COMMENT)
.setIsReimbursable(REIMBURSABLE)
.setCurrency(CURRENCY_CODE)
.setIsFullPage(!FULL_PAGE)
.setIndex(DESCENDING_INDEX)
.setPaymentMethod(PAYMENT_METHOD)
.setExtraEditText1(EXTRA1)
.setExtraEditText2(EXTRA2)
.setExtraEditText3(EXTRA3)
.setSyncState(mSyncState)
.build();
assertEquals(receipt, mReceiptDatabaseAdapter.readForSelection(mCursor, mTrip, true));
}
@Test
public void readForSelectionAscending() throws Exception {
// Note: Full page is backwards in the database
final Receipt receipt = new ReceiptBuilderFactory(ID)
.setTrip(mTrip)
.setName(NAME)
.setPrice(PRICE)
.setTax(TAX)
.setExchangeRate(EXCHANGE_RATE)
.setCategory(CATEGORY)
.setFile(new File(PATH))
.setDate(DATE)
.setTimeZone(TIMEZONE)
.setComment(COMMENT)
.setIsReimbursable(REIMBURSABLE)
.setCurrency(CURRENCY_CODE)
.setIsFullPage(!FULL_PAGE)
.setIndex(ASCENDING_INDEX)
.setPaymentMethod(PAYMENT_METHOD)
.setExtraEditText1(EXTRA1)
.setExtraEditText2(EXTRA2)
.setExtraEditText3(EXTRA3)
.setSyncState(mSyncState)
.build();
assertEquals(receipt, mReceiptDatabaseAdapter.readForSelection(mCursor, mTrip, false));
}
@Test
public void readForUnmappedCategory() throws Exception {
when(mCategoriesTable.findByPrimaryKey(CATEGORY_NAME)).thenReturn(Single.error(new Exception()));
// Note: Full page is backwards in the database
final Receipt receipt = new ReceiptBuilderFactory(ID)
.setTrip(mTrip)
.setName(NAME)
.setPrice(PRICE)
.setTax(TAX)
.setExchangeRate(EXCHANGE_RATE)
.setCategory(new ImmutableCategoryImpl(CATEGORY_NAME, CATEGORY_NAME))
.setFile(new File(PATH))
.setDate(DATE)
.setTimeZone(TIMEZONE)
.setComment(COMMENT)
.setIsReimbursable(REIMBURSABLE)
.setCurrency(CURRENCY_CODE)
.setIsFullPage(!FULL_PAGE)
.setIndex(DESCENDING_INDEX)
.setPaymentMethod(PAYMENT_METHOD)
.setExtraEditText1(EXTRA1)
.setExtraEditText2(EXTRA2)
.setExtraEditText3(EXTRA3)
.setSyncState(mSyncState)
.build();
assertEquals(receipt, mReceiptDatabaseAdapter.read(mCursor));
}
@Test
public void readForUnmappedPaymentMethod() throws Exception {
when(mPaymentMethodsTable.findByPrimaryKey(PAYMENT_METHOD_ID)).thenReturn(Single.error(new Exception()));
// Note: Full page is backwards in the database
final Receipt receipt = new ReceiptBuilderFactory(ID)
.setTrip(mTrip)
.setName(NAME)
.setPrice(PRICE)
.setTax(TAX)
.setExchangeRate(EXCHANGE_RATE)
.setCategory(CATEGORY)
.setFile(new File(PATH))
.setDate(DATE)
.setTimeZone(TIMEZONE)
.setComment(COMMENT)
.setIsReimbursable(REIMBURSABLE)
.setCurrency(CURRENCY_CODE)
.setIsFullPage(!FULL_PAGE)
.setIndex(DESCENDING_INDEX)
.setPaymentMethod(null)
.setExtraEditText1(EXTRA1)
.setExtraEditText2(EXTRA2)
.setExtraEditText3(EXTRA3)
.setSyncState(mSyncState)
.build();
assertEquals(receipt, mReceiptDatabaseAdapter.read(mCursor));
}
@Test
public void writeUnsycned() throws Exception {
final String sync = "sync";
final ContentValues syncValues = new ContentValues();
syncValues.put(sync, sync);
when(mSyncStateAdapter.writeUnsynced(mSyncState)).thenReturn(syncValues);
final ContentValues contentValues = mReceiptDatabaseAdapter.write(mReceipt, new DatabaseOperationMetadata());
// Note: Full page is backwards in the database
assertEquals(PATH, contentValues.getAsString("path"));
assertEquals(NAME, contentValues.getAsString("name"));
assertEquals(PARENT, contentValues.getAsString("parent"));
assertEquals(CATEGORY_NAME, contentValues.getAsString("category"));
assertEquals(PRICE, contentValues.getAsDouble("price"), 0.0001d);
assertEquals(TAX, contentValues.getAsDouble("tax"), 0.0001d);
assertEquals(EXCHANGE_RATE_FOR_USD, contentValues.getAsDouble("exchange_rate"), 0.0001d);
assertEquals(DATE, (long) contentValues.getAsLong("rcpt_date"));
assertEquals(TIMEZONE, contentValues.getAsString("timezone"));
assertEquals(COMMENT, contentValues.getAsString("comment"));
assertEquals(REIMBURSABLE, contentValues.getAsBoolean("expenseable"));
assertEquals(CURRENCY_CODE, contentValues.getAsString("isocode"));
assertEquals(PAYMENT_METHOD_ID, (int) contentValues.getAsInteger("paymentMethodKey"));
assertEquals(!FULL_PAGE, contentValues.getAsBoolean("fullpageimage"));
assertEquals(EXTRA1, contentValues.getAsString("extra_edittext_1"));
assertEquals(EXTRA2, contentValues.getAsString("extra_edittext_2"));
assertEquals(EXTRA3, contentValues.getAsString("extra_edittext_3"));
assertEquals(sync, contentValues.getAsString(sync));
assertFalse(contentValues.containsKey("id"));
}
@Test
public void write() throws Exception {
final String sync = "sync";
final ContentValues syncValues = new ContentValues();
syncValues.put(sync, sync);
when(mSyncStateAdapter.write(mSyncState)).thenReturn(syncValues);
final ContentValues contentValues = mReceiptDatabaseAdapter.write(mReceipt, new DatabaseOperationMetadata(OperationFamilyType.Sync));
// Note: Full page is backwards in the database
assertEquals(PATH, contentValues.getAsString("path"));
assertEquals(NAME, contentValues.getAsString("name"));
assertEquals(PARENT, contentValues.getAsString("parent"));
assertEquals(CATEGORY_NAME, contentValues.getAsString("category"));
assertEquals(PRICE, contentValues.getAsDouble("price"), 0.0001d);
assertEquals(TAX, contentValues.getAsDouble("tax"), 0.0001d);
assertEquals(EXCHANGE_RATE_FOR_USD, contentValues.getAsDouble("exchange_rate"), 0.0001d);
assertEquals(DATE, (long) contentValues.getAsLong("rcpt_date"));
assertEquals(TIMEZONE, contentValues.getAsString("timezone"));
assertEquals(COMMENT, contentValues.getAsString("comment"));
assertEquals(REIMBURSABLE, contentValues.getAsBoolean("expenseable"));
assertEquals(CURRENCY_CODE, contentValues.getAsString("isocode"));
assertEquals(PAYMENT_METHOD_ID, (int) contentValues.getAsInteger("paymentMethodKey"));
assertEquals(!FULL_PAGE, contentValues.getAsBoolean("fullpageimage"));
assertEquals(EXTRA1, contentValues.getAsString("extra_edittext_1"));
assertEquals(EXTRA2, contentValues.getAsString("extra_edittext_2"));
assertEquals(EXTRA3, contentValues.getAsString("extra_edittext_3"));
assertEquals(sync, contentValues.getAsString(sync));
assertFalse(contentValues.containsKey("id"));
}
@Test
public void build() throws Exception {
final Receipt receipt = new ReceiptBuilderFactory(PRIMARY_KEY_ID)
.setTrip(mTrip)
.setName(NAME)
.setPrice(PRICE)
.setTax(TAX)
.setExchangeRate(EXCHANGE_RATE)
.setCategory(CATEGORY)
.setFile(new File(PATH))
.setDate(DATE)
.setTimeZone(TIMEZONE)
.setComment(COMMENT)
.setIsReimbursable(REIMBURSABLE)
.setCurrency(CURRENCY_CODE)
.setIsFullPage(FULL_PAGE)
.setIndex(DESCENDING_INDEX)
.setPaymentMethod(PAYMENT_METHOD)
.setExtraEditText1(EXTRA1)
.setExtraEditText2(EXTRA2)
.setExtraEditText3(EXTRA3)
.setSyncState(mGetSyncState)
.build();
assertEquals(receipt, mReceiptDatabaseAdapter.build(mReceipt, mPrimaryKey, mock(DatabaseOperationMetadata.class)));
assertEquals(receipt.getSyncState(), mReceiptDatabaseAdapter.build(mReceipt, mPrimaryKey, mock(DatabaseOperationMetadata.class)).getSyncState());
}
}