package com.stripe.wrap.pay.utils;
import android.util.Log;
import com.google.android.gms.wallet.LineItem;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
import java.math.BigDecimal;
import java.util.List;
import java.util.Locale;
import static com.stripe.wrap.pay.utils.LineItemBuilder.isPriceBreakdownConsistent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Test class for {@link LineItemBuilder}.
*/
@RunWith(RobolectricTestRunner.class)
@Config(sdk = 23)
public class LineItemBuilderTest {
@Test
public void setAllAttributes_thenBuild_createsExpectedLineItem() {
final String currencyCode = "EUR";
final String description = "a test item";
LineItemBuilder lineItemBuilder = new LineItemBuilder(currencyCode);
LineItem lineItem = lineItemBuilder.setDescription(description)
.setUnitPrice(100)
.setQuantity(2)
.setTotalPrice(200)
.build();
assertEquals(LineItem.Role.REGULAR, lineItem.getRole());
assertEquals(currencyCode, lineItem.getCurrencyCode());
assertEquals(description, lineItem.getDescription());
assertEquals("2", lineItem.getQuantity());
assertEquals("1.00", lineItem.getUnitPrice());
assertEquals("2.00", lineItem.getTotalPrice());
}
@Test
public void setHighPrice_thenBuild_createsExpectedLineItem() {
LineItemBuilder lineItemBuilder = new LineItemBuilder("USD");
LineItem lineItem = lineItemBuilder
.setUnitPrice(1000000L)
.setQuantity(2)
.setTotalPrice(2000000L)
.setDescription("Best smart watch ever")
.build();
assertEquals(LineItem.Role.REGULAR, lineItem.getRole());
assertEquals("USD", lineItem.getCurrencyCode());
assertEquals("Best smart watch ever", lineItem.getDescription());
assertEquals("2", lineItem.getQuantity());
assertEquals("10000.00", lineItem.getUnitPrice());
assertEquals("20000.00", lineItem.getTotalPrice());
}
@Test
public void setCurrency_withLowerCaseString_stillSetsCurrency() {
// If you try to create a Currency object with a lower-case code, it throws
// an IllegalArgumentException.
LineItemBuilder builder = new LineItemBuilder("eur");
LineItem item = builder.build();
assertEquals("EUR", item.getCurrencyCode());
}
@Test
public void setQuantityAndUnitPrice_whenNoTotalPriceSet_createsTotalPrice() {
LineItemBuilder builder = new LineItemBuilder("usd");
LineItem item = builder.setQuantity(1.5)
.setUnitPrice(399)
.build();
assertEquals("1.5", item.getQuantity());
assertEquals("3.99", item.getUnitPrice());
assertEquals("5.98", item.getTotalPrice());
}
@Test
public void setQuantity_whenMoreThanOneDigitAfterDecimal_getsRoundedAndLogsWarning() {
ShadowLog.stream = System.out;
Locale.setDefault(Locale.US);
LineItem item = new LineItemBuilder("USD").setQuantity(1.71).build();
String expectedWarning = String.format(
Locale.ENGLISH,
"Tried to create quantity %.2f, but Android Pay quantity" +
" may only have one digit after decimal. Value was rounded to 1.7",
1.71);
List<ShadowLog.LogItem> logItems = ShadowLog.getLogsForTag(LineItemBuilder.TAG);
assertFalse(logItems.isEmpty());
assertEquals(1, logItems.size());
assertEquals(expectedWarning, logItems.get(0).msg);
assertEquals(Log.WARN, logItems.get(0).type);
assertEquals("1.7", item.getQuantity());
}
@Test
public void setQuantity_withBigDecimalWithIncorrectScale_getsRoundedAndLogsWarning() {
ShadowLog.stream = System.out;
BigDecimal bigDecimal = BigDecimal.valueOf(1.71);
bigDecimal = bigDecimal.setScale(2, BigDecimal.ROUND_DOWN);
LineItem item = new LineItemBuilder("USD").setQuantity(bigDecimal).build();
String expectedWarning = String.format(
Locale.ENGLISH,
"Tried to create quantity %.2f, but Android Pay quantity" +
" may only have one digit after decimal. Value was rounded to 1.7",
1.71);
List<ShadowLog.LogItem> logItems = ShadowLog.getLogsForTag(LineItemBuilder.TAG);
assertFalse(logItems.isEmpty());
assertEquals(1, logItems.size());
assertEquals(expectedWarning, logItems.get(0).msg);
assertEquals(Log.WARN, logItems.get(0).type);
assertEquals("1.7", item.getQuantity());
}
@Test
public void setQuantity_withBigDecimalWithLowerScale_setsExpectedValue() {
ShadowLog.stream = System.out;
BigDecimal bigDecimal = BigDecimal.valueOf(2);
bigDecimal = bigDecimal.setScale(0, BigDecimal.ROUND_DOWN);
LineItem item = new LineItemBuilder("USD").setQuantity(bigDecimal).build();
assertNull(ShadowLog.getLogsForTag(LineItemBuilder.TAG));
assertEquals("2", item.getQuantity());
}
@Test
public void setQuantity_withBigDecimalWithCorrectScale_setsExpectedValue() {
ShadowLog.stream = System.out;
BigDecimal bigDecimal = BigDecimal.valueOf(2.5);
bigDecimal = bigDecimal.setScale(1, BigDecimal.ROUND_DOWN);
LineItem item = new LineItemBuilder("USD").setQuantity(bigDecimal).build();
assertNull(ShadowLog.getLogsForTag(LineItemBuilder.TAG));
assertEquals("2.5", item.getQuantity());
}
@Test(expected = RuntimeException.class)
public void setCurrencyCode_whenInvalid_throwsRuntimeException() {
new LineItemBuilder("notacurrency").build();
fail("Can't create a LineItemBuilder with an illegal currency.");
}
@Test
public void isWholeNumber_whenBigDecimalFromInteger_returnsTrue() {
assertTrue(LineItemBuilder.isWholeNumber(BigDecimal.ZERO));
assertTrue(LineItemBuilder.isWholeNumber(BigDecimal.valueOf(555)));
}
@Test
public void isWholeNumber_whenBigDecimalDoubleWithoutDecimalPart_returnsTrue() {
assertTrue(LineItemBuilder.isWholeNumber(BigDecimal.valueOf(1.0000)));
}
@Test
public void isWholeNumber_whenBigDecimalDoubleWithDecimalPart_returnsFalse() {
assertFalse(LineItemBuilder.isWholeNumber(BigDecimal.valueOf(1.5)));
assertFalse(LineItemBuilder.isWholeNumber(BigDecimal.valueOf(2.07)));
}
@Test
public void isPriceBreakdownConsistent_whenItemsMultiplyClosely_returnsTrue() {
assertTrue(isPriceBreakdownConsistent(1000L, BigDecimal.TEN, 10000L));
}
@Test
public void isPriceBreakdownConsistent_whenAnyItemIsNull_returnsTrue() {
assertTrue(isPriceBreakdownConsistent(null, BigDecimal.TEN, 1234L));
assertTrue(isPriceBreakdownConsistent(55L, null, 8888L));
assertTrue(isPriceBreakdownConsistent(33L, BigDecimal.ONE, null));
}
@Test
public void isPriceBreakdownConsistent_whenItemsAreOffByALot_returnsFalse() {
assertFalse(isPriceBreakdownConsistent(55L, BigDecimal.ONE, 56L));
}
@Test
public void isPriceBreakdownConsistent_whenItemsAreOffByOnlyALittle_returnsTrue() {
assertTrue(isPriceBreakdownConsistent(199L, BigDecimal.ONE, 200L));
}
@Test
public void build_whenPriceBreakdownIsNotConsistent_logsWarning() {
ShadowLog.stream = System.out;
Locale.setDefault(Locale.JAPAN);
LineItem item = new LineItemBuilder("USD")
.setQuantity(1.0)
.setUnitPrice(1500L)
.setTotalPrice(2000L).build();
String expectedWarning = "Price breakdown of 1500 * 1.0 = 2000 is off by more than 1 percent";
List<ShadowLog.LogItem> logItems = ShadowLog.getLogsForTag(LineItemBuilder.TAG);
assertEquals("15.00", item.getUnitPrice());
assertEquals("20.00", item.getTotalPrice());
assertEquals("1", item.getQuantity());
assertEquals(1, logItems.size());
assertEquals(Log.WARN, logItems.get(0).type);
assertEquals(expectedWarning, logItems.get(0).msg);
}
}