From 9b86a5c51ebc7d551e39c35a894d0574855bbe89 Mon Sep 17 00:00:00 2001 From: Kush Donepudi Date: Mon, 16 Dec 2019 16:47:23 +0000 Subject: [PATCH 01/10] Initial Commit --- .gitignore | 8 +++ pom.xml | 59 ++++++++++++++++ .../model/MeasurementUnit.java | 5 ++ .../henrysgroceries/model/Product.java | 68 +++++++++++++++++++ .../henrysgroceries/model/ShoppingBasket.java | 36 ++++++++++ .../offers/ComboDiscountOffer.java | 37 ++++++++++ .../henrysgroceries/offers/Offer.java | 9 +++ .../offers/PercentageDiscountOffer.java | 63 +++++++++++++++++ .../service/PriceShoppingBasketService.java | 12 ++++ 9 files changed, 297 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/com/industriallogic/henrysgroceries/model/MeasurementUnit.java create mode 100644 src/main/java/com/industriallogic/henrysgroceries/model/Product.java create mode 100644 src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java create mode 100644 src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java create mode 100644 src/main/java/com/industriallogic/henrysgroceries/offers/Offer.java create mode 100644 src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java create mode 100644 src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..0a322217 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +target/ +bin/ + +## Intellij IDEA ## +.idea +.iml + +*.log \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..f481a054 --- /dev/null +++ b/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + com.industriallogic.henrysgroceries + PriceGroceryBasket + 1.0-SNAPSHOT + jar + + + org.springframework.boot + spring-boot-starter-parent + 2.2.1.RELEASE + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-devtools + true + + + + org.apache.commons + commons-lang3 + 3.0 + + + + junit + junit + 4.12 + test + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/src/main/java/com/industriallogic/henrysgroceries/model/MeasurementUnit.java b/src/main/java/com/industriallogic/henrysgroceries/model/MeasurementUnit.java new file mode 100644 index 00000000..3f64725b --- /dev/null +++ b/src/main/java/com/industriallogic/henrysgroceries/model/MeasurementUnit.java @@ -0,0 +1,5 @@ +package com.industriallogic.henrysgroceries.model; + +public enum MeasurementUnit { + TIN, LOAF, BOTTLE, SINGLE +} diff --git a/src/main/java/com/industriallogic/henrysgroceries/model/Product.java b/src/main/java/com/industriallogic/henrysgroceries/model/Product.java new file mode 100644 index 00000000..8e4ec5a6 --- /dev/null +++ b/src/main/java/com/industriallogic/henrysgroceries/model/Product.java @@ -0,0 +1,68 @@ +package com.industriallogic.henrysgroceries.model; + +import java.math.BigDecimal; +import java.util.Objects; + + +public class Product { + private String productCode; + private String name; + private BigDecimal price; + private MeasurementUnit unit; + + public Product(String productCode, String name, BigDecimal price, MeasurementUnit unit) { + this.productCode = productCode; + this.name = name; + this.price = price; + this.unit = unit; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Product)) return false; + Product product = (Product) o; + return Objects.equals(getProductCode(), product.getProductCode()) && + Objects.equals(getName(), product.getName()) && + Objects.equals(getPrice(), product.getPrice()) && + getUnit() == product.getUnit(); + } + + @Override + public int hashCode() { + return Objects.hash(getProductCode(), getName(), getPrice(), getUnit()); + } + + public String getProductCode() { + return productCode; + } + + public void setProductCode(String productCode) { + this.productCode = productCode; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public BigDecimal getPrice() { + return price; + } + + public void setPrice(BigDecimal price) { + this.price = price; + } + + public MeasurementUnit getUnit() { + return unit; + } + + public void setUnit(MeasurementUnit unit) { + this.unit = unit; + } + +} diff --git a/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java b/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java new file mode 100644 index 00000000..954cdfb3 --- /dev/null +++ b/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java @@ -0,0 +1,36 @@ +package com.industriallogic.henrysgroceries.model; + + +import java.time.LocalDate; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class ShoppingBasket { + + private Map basket = new HashMap<>(); + private LocalDate shoppingDate = LocalDate.now(); + + public void addProductToBasket(Product product) { + basket.compute(product, + (k, v) -> { + return v == null ? 1 : v + 1; + }); + } + + public void setShoppingDate(LocalDate date) { + this.shoppingDate = date; + } + + public LocalDate getShoppingDate() { + return this.shoppingDate; + } + + public Map getBasket() { + return Collections.unmodifiableMap(basket); + } + +} + + + diff --git a/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java b/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java new file mode 100644 index 00000000..8dcac07a --- /dev/null +++ b/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java @@ -0,0 +1,37 @@ +package com.industriallogic.henrysgroceries.offers; + +import com.industriallogic.henrysgroceries.model.Product; + +import java.math.BigDecimal; +import java.time.LocalDate; + +public class ComboDiscountOffer implements Offer { + + private String offerName; + private Product qualifyingProduct; + private Integer qualifyingProductQuantity; + private Product offerOnProduct; + private BigDecimal discountFactor; + private LocalDate offerStartDate; + private LocalDate offerEndDate; + + public ComboDiscountOffer(String offerName, Product qualifyingProduct, Integer qualifyingProductQuantity, Product offerOnProduct, BigDecimal discountFactor, LocalDate offerStartDate, LocalDate offerEndDate) { + this.offerName = offerName; + this.qualifyingProduct = qualifyingProduct; + this.qualifyingProductQuantity = qualifyingProductQuantity; + this.offerOnProduct = offerOnProduct; + this.discountFactor = discountFactor; + this.offerStartDate = offerStartDate; + this.offerEndDate = offerEndDate; + } + + public String getOfferName() { + return offerName; + } + + public void setOfferName(String offerName) { + this.offerName = offerName; + } + +} + diff --git a/src/main/java/com/industriallogic/henrysgroceries/offers/Offer.java b/src/main/java/com/industriallogic/henrysgroceries/offers/Offer.java new file mode 100644 index 00000000..b19ab653 --- /dev/null +++ b/src/main/java/com/industriallogic/henrysgroceries/offers/Offer.java @@ -0,0 +1,9 @@ +package com.industriallogic.henrysgroceries.offers; + +import java.time.LocalDate; + +public interface Offer { + default boolean isOfferStillValid(LocalDate shoppingDate, LocalDate offerStartDate, LocalDate offerEndDate) { + return ( shoppingDate.compareTo(offerStartDate) >= 0 && shoppingDate.compareTo(offerEndDate) <= 0) ; + } +} diff --git a/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java b/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java new file mode 100644 index 00000000..601a1bfb --- /dev/null +++ b/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java @@ -0,0 +1,63 @@ +package com.industriallogic.henrysgroceries.offers; + +import com.industriallogic.henrysgroceries.model.Product; + +import java.math.BigDecimal; +import java.time.LocalDate; + +public class PercentageDiscountOffer implements Offer{ + private String offerName; + private Product product; + private BigDecimal discountPercentage; + private LocalDate offerStartDate; + private LocalDate offerEndDate; + + public PercentageDiscountOffer(String offerName, Product product, BigDecimal discountPercentage, LocalDate offerStartDate, LocalDate offerEndDate) { + this.offerName = offerName; + this.product = product; + this.discountPercentage = discountPercentage; + this.offerStartDate = offerStartDate; + this.offerEndDate = offerEndDate; + } + + public String getOfferName() { + return offerName; + } + + public void setOfferName(String offerName) { + this.offerName = offerName; + } + + public Product getProduct() { + return product; + } + + public void setProduct(Product product) { + this.product = product; + } + + public BigDecimal getDiscountPercentage() { + return discountPercentage; + } + + public void setDiscountPercentage(BigDecimal discountPercentage) { + this.discountPercentage = discountPercentage; + } + + public LocalDate getOfferStartDate() { + return offerStartDate; + } + + public void setOfferStartDate(LocalDate offerStartDate) { + this.offerStartDate = offerStartDate; + } + + public LocalDate getOfferEndDate() { + return offerEndDate; + } + + public void setOfferEndDate(LocalDate offerEndDate) { + this.offerEndDate = offerEndDate; + } + +} diff --git a/src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java b/src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java new file mode 100644 index 00000000..634eb546 --- /dev/null +++ b/src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java @@ -0,0 +1,12 @@ +package com.industriallogic.henrysgroceries.service; + +import com.industriallogic.henrysgroceries.model.ShoppingBasket; + +import java.math.BigDecimal; + +public class PriceShoppingBasketService { + + public BigDecimal priceShoppingBasket(ShoppingBasket basket) { + return BigDecimal.ZERO; + } +} From 11ca78c276ffe7df12d00b95f0052ae58cce152c Mon Sep 17 00:00:00 2001 From: Kush Donepudi Date: Tue, 17 Dec 2019 20:29:35 +0000 Subject: [PATCH 02/10] Added Lombok and Providers for Products and Offers --- lombok.config | 6 ++ pom.xml | 6 ++ .../PriceShoppingBasketApplication.java | 11 ++++ .../offers/ComboDiscountOffer.java | 31 ++++------ .../henrysgroceries/offers/Offer.java | 1 + .../offers/PercentageDiscountOffer.java | 60 ++++--------------- .../provider/OffersProvider.java | 46 ++++++++++++++ .../provider/ProductProvider.java | 32 ++++++++++ .../provider/OffersProviderTest.java | 25 ++++++++ .../provider/ProductProviderTest.java | 43 +++++++++++++ .../testutil/TestMockUtil.java | 29 +++++++++ 11 files changed, 223 insertions(+), 67 deletions(-) create mode 100644 lombok.config create mode 100644 src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java create mode 100644 src/main/java/com/industriallogic/henrysgroceries/provider/OffersProvider.java create mode 100644 src/main/java/com/industriallogic/henrysgroceries/provider/ProductProvider.java create mode 100644 src/test/java/com/industriallogic/henrysgroceries/provider/OffersProviderTest.java create mode 100644 src/test/java/com/industriallogic/henrysgroceries/provider/ProductProviderTest.java create mode 100644 src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java diff --git a/lombok.config b/lombok.config new file mode 100644 index 00000000..de41e143 --- /dev/null +++ b/lombok.config @@ -0,0 +1,6 @@ +lombok.anyConstructor.addConstructorProperties=true +lombok.log.fieldName=LOGGER +lombok.log.log4j2.flagUsage=error +lombok.log.log4j.flagUsage=error +lombok.log.javaUtilLogging.flagUsage=error +lombok.log.apacheCommons.flagUsage=error \ No newline at end of file diff --git a/pom.xml b/pom.xml index f481a054..bac0d155 100644 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,12 @@ 3.0 + + org.projectlombok + lombok + provided + + junit junit diff --git a/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java b/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java new file mode 100644 index 00000000..ab9a67af --- /dev/null +++ b/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java @@ -0,0 +1,11 @@ +package com.industriallogic.henrysgroceries; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class PriceShoppingBasketApplication { + public static void main(String[] args) { + SpringApplication.run(PriceShoppingBasketApplication.class, args); + } +} diff --git a/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java b/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java index 8dcac07a..9cf50eb3 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java +++ b/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java @@ -1,37 +1,32 @@ package com.industriallogic.henrysgroceries.offers; import com.industriallogic.henrysgroceries.model.Product; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; import java.math.BigDecimal; import java.time.LocalDate; +@RequiredArgsConstructor +@Data public class ComboDiscountOffer implements Offer { + @NonNull private String offerName; + @NonNull private Product qualifyingProduct; + @NonNull private Integer qualifyingProductQuantity; + @NonNull private Product offerOnProduct; + @NonNull private BigDecimal discountFactor; + @NonNull private LocalDate offerStartDate; + @NonNull private LocalDate offerEndDate; - public ComboDiscountOffer(String offerName, Product qualifyingProduct, Integer qualifyingProductQuantity, Product offerOnProduct, BigDecimal discountFactor, LocalDate offerStartDate, LocalDate offerEndDate) { - this.offerName = offerName; - this.qualifyingProduct = qualifyingProduct; - this.qualifyingProductQuantity = qualifyingProductQuantity; - this.offerOnProduct = offerOnProduct; - this.discountFactor = discountFactor; - this.offerStartDate = offerStartDate; - this.offerEndDate = offerEndDate; - } - - public String getOfferName() { - return offerName; - } - - public void setOfferName(String offerName) { - this.offerName = offerName; - } - } diff --git a/src/main/java/com/industriallogic/henrysgroceries/offers/Offer.java b/src/main/java/com/industriallogic/henrysgroceries/offers/Offer.java index b19ab653..99cb0550 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/offers/Offer.java +++ b/src/main/java/com/industriallogic/henrysgroceries/offers/Offer.java @@ -3,6 +3,7 @@ import java.time.LocalDate; public interface Offer { + default boolean isOfferStillValid(LocalDate shoppingDate, LocalDate offerStartDate, LocalDate offerEndDate) { return ( shoppingDate.compareTo(offerStartDate) >= 0 && shoppingDate.compareTo(offerEndDate) <= 0) ; } diff --git a/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java b/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java index 601a1bfb..460fcf0f 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java +++ b/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java @@ -1,63 +1,25 @@ package com.industriallogic.henrysgroceries.offers; import com.industriallogic.henrysgroceries.model.Product; +import lombok.Data; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; import java.math.BigDecimal; import java.time.LocalDate; -public class PercentageDiscountOffer implements Offer{ +@RequiredArgsConstructor +@Data +public class PercentageDiscountOffer implements Offer { + @NonNull private String offerName; + @NonNull private Product product; + @NonNull private BigDecimal discountPercentage; + @NonNull private LocalDate offerStartDate; + @NonNull private LocalDate offerEndDate; - public PercentageDiscountOffer(String offerName, Product product, BigDecimal discountPercentage, LocalDate offerStartDate, LocalDate offerEndDate) { - this.offerName = offerName; - this.product = product; - this.discountPercentage = discountPercentage; - this.offerStartDate = offerStartDate; - this.offerEndDate = offerEndDate; - } - - public String getOfferName() { - return offerName; - } - - public void setOfferName(String offerName) { - this.offerName = offerName; - } - - public Product getProduct() { - return product; - } - - public void setProduct(Product product) { - this.product = product; - } - - public BigDecimal getDiscountPercentage() { - return discountPercentage; - } - - public void setDiscountPercentage(BigDecimal discountPercentage) { - this.discountPercentage = discountPercentage; - } - - public LocalDate getOfferStartDate() { - return offerStartDate; - } - - public void setOfferStartDate(LocalDate offerStartDate) { - this.offerStartDate = offerStartDate; - } - - public LocalDate getOfferEndDate() { - return offerEndDate; - } - - public void setOfferEndDate(LocalDate offerEndDate) { - this.offerEndDate = offerEndDate; - } - } diff --git a/src/main/java/com/industriallogic/henrysgroceries/provider/OffersProvider.java b/src/main/java/com/industriallogic/henrysgroceries/provider/OffersProvider.java new file mode 100644 index 00000000..cefec2c2 --- /dev/null +++ b/src/main/java/com/industriallogic/henrysgroceries/provider/OffersProvider.java @@ -0,0 +1,46 @@ +package com.industriallogic.henrysgroceries.provider; + +import com.industriallogic.henrysgroceries.model.Product; +import com.industriallogic.henrysgroceries.offers.ComboDiscountOffer; +import com.industriallogic.henrysgroceries.offers.Offer; +import com.industriallogic.henrysgroceries.offers.PercentageDiscountOffer; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.temporal.TemporalAdjusters; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +@Component +public class OffersProvider { + + private ProductProvider productProvider; + + private List offers; + + public OffersProvider(ProductProvider productProvider) { + this.productProvider = productProvider; + } + /** + * Populate the offers available + */ + @PostConstruct + public void init() { + Product apples = productProvider.getProduct("Apples").get(); + Product soup = productProvider.getProduct("Soup").get(); + Product bread = productProvider.getProduct("Bread").get(); + + LocalDate firstOfNextMonth = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth()); + LocalDate endOfNextMonth = firstOfNextMonth.with(TemporalAdjusters.lastDayOfMonth()); + PercentageDiscountOffer applesOffer = new PercentageDiscountOffer("Apple 10% OFF", apples, BigDecimal.valueOf(10), LocalDate.now().plusDays(3), endOfNextMonth); + ComboDiscountOffer discountOffer = new ComboDiscountOffer("COMBO OFFER", soup, 2, bread, BigDecimal.valueOf(50), LocalDate.now().minusDays(1), LocalDate.now().plusDays(7)); + offers = Collections.unmodifiableList(Arrays.asList(applesOffer, discountOffer)); + } + + public List getOffers() { + return offers; + } +} diff --git a/src/main/java/com/industriallogic/henrysgroceries/provider/ProductProvider.java b/src/main/java/com/industriallogic/henrysgroceries/provider/ProductProvider.java new file mode 100644 index 00000000..12c83837 --- /dev/null +++ b/src/main/java/com/industriallogic/henrysgroceries/provider/ProductProvider.java @@ -0,0 +1,32 @@ +package com.industriallogic.henrysgroceries.provider; + +import com.industriallogic.henrysgroceries.model.MeasurementUnit; +import com.industriallogic.henrysgroceries.model.Product; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.math.BigDecimal; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +@Component +public class ProductProvider { + + private Map productList; + + @PostConstruct + public void init() { + Map stock = new HashMap<>(); + stock.put("APPLES", new Product("A123","Apple", BigDecimal.valueOf(.10) , MeasurementUnit.SINGLE)); + stock.put("BREAD", new Product("B123","Bread", BigDecimal.valueOf(.80) , MeasurementUnit.LOAF)); + stock.put("MILK", new Product("M123", "Milk", BigDecimal.valueOf(1.30) , MeasurementUnit.BOTTLE)); + stock.put("SOUP", new Product("S123","Soup", BigDecimal.valueOf(.65) , MeasurementUnit.TIN)); + productList = Collections.unmodifiableMap(stock); + } + + public Optional getProduct(String productName) { + return Optional.ofNullable(productList.get(productName.toUpperCase())); + } +} diff --git a/src/test/java/com/industriallogic/henrysgroceries/provider/OffersProviderTest.java b/src/test/java/com/industriallogic/henrysgroceries/provider/OffersProviderTest.java new file mode 100644 index 00000000..22efcb60 --- /dev/null +++ b/src/test/java/com/industriallogic/henrysgroceries/provider/OffersProviderTest.java @@ -0,0 +1,25 @@ +package com.industriallogic.henrysgroceries.provider; + + +import com.industriallogic.henrysgroceries.testutil.TestMockUtil; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class OffersProviderTest { + + @Mock + OffersProvider offersProvider; + + @Test + public void getOffers() { + when(offersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); + assertEquals(offersProvider.getOffers(), TestMockUtil.getOffersList()); + } +} \ No newline at end of file diff --git a/src/test/java/com/industriallogic/henrysgroceries/provider/ProductProviderTest.java b/src/test/java/com/industriallogic/henrysgroceries/provider/ProductProviderTest.java new file mode 100644 index 00000000..34ac805c --- /dev/null +++ b/src/test/java/com/industriallogic/henrysgroceries/provider/ProductProviderTest.java @@ -0,0 +1,43 @@ +package com.industriallogic.henrysgroceries.provider; + +import com.industriallogic.henrysgroceries.model.MeasurementUnit; +import com.industriallogic.henrysgroceries.model.Product; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.math.BigDecimal; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class ProductProviderTest { + + @Mock + private ProductProvider productProvider; + + @Test + public void getProduct() { + Optional apple = Optional.of(new Product("A123", "Apple", BigDecimal.valueOf(.10), MeasurementUnit.SINGLE)); + when(productProvider.getProduct("Apples")).thenReturn(apple); + assertEquals(productProvider.getProduct("Apples"), apple); + } + + @Test + public void getProductWithSpace() { + Optional noProduct = Optional.empty(); + when(productProvider.getProduct(" ")).thenReturn(noProduct); + assertEquals(productProvider.getProduct(" "), noProduct); + } + + @Test + public void getInvalidProduct() { + Optional noProduct = Optional.empty(); + when(productProvider.getProduct("chocolate")).thenReturn(noProduct); + assertEquals(productProvider.getProduct("chocolate"), noProduct); + } + +} \ No newline at end of file diff --git a/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java b/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java new file mode 100644 index 00000000..a384906c --- /dev/null +++ b/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java @@ -0,0 +1,29 @@ +package com.industriallogic.henrysgroceries.testutil; + +import com.industriallogic.henrysgroceries.model.MeasurementUnit; +import com.industriallogic.henrysgroceries.model.Product; +import com.industriallogic.henrysgroceries.offers.ComboDiscountOffer; +import com.industriallogic.henrysgroceries.offers.Offer; +import com.industriallogic.henrysgroceries.offers.PercentageDiscountOffer; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.temporal.TemporalAdjusters; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class TestMockUtil { + + public static List getOffersList() { + Product apples = new Product("A123", "Apple", BigDecimal.valueOf(.10), MeasurementUnit.SINGLE); + Product bread = new Product("B123", "Bread", BigDecimal.valueOf(.80), MeasurementUnit.LOAF); + Product milk = new Product("M123", "Milk", BigDecimal.valueOf(1.30), MeasurementUnit.BOTTLE); + Product soup = new Product("S123", "Soup", BigDecimal.valueOf(.65), MeasurementUnit.TIN); + LocalDate firstOfNextMonth = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth()); + LocalDate endOfNextMonth = firstOfNextMonth.with(TemporalAdjusters.lastDayOfMonth()); + PercentageDiscountOffer applesOffer = new PercentageDiscountOffer("Apple 10% OFF", apples, BigDecimal.valueOf(10), LocalDate.now().plusDays(3), endOfNextMonth); + ComboDiscountOffer discountOffer = new ComboDiscountOffer("combo Offer", soup, 2, bread, BigDecimal.valueOf(50), LocalDate.now().minusDays(1), LocalDate.now().plusDays(7)); + return Collections.unmodifiableList(Arrays.asList(applesOffer, discountOffer)); + } +} From 16df7daf47e228131c6cc6aa1340417aeb167556 Mon Sep 17 00:00:00 2001 From: Kush Donepudi Date: Tue, 17 Dec 2019 22:59:42 +0000 Subject: [PATCH 03/10] Added exception and discout logic to offers --- .../exception/ProductNotFoundException.java | 15 +++++++++++ .../henrysgroceries/model/ShoppingBasket.java | 2 +- .../offers/ComboDiscountOffer.java | 18 ++++++++++--- .../henrysgroceries/offers/Offer.java | 7 ++++++ .../offers/PercentageDiscountOffer.java | 9 +++++++ .../provider/OffersProvider.java | 9 ++++--- .../provider/ProductProvider.java | 10 ++++++-- .../provider/ProductProviderTest.java | 25 ++++++++----------- 8 files changed, 71 insertions(+), 24 deletions(-) create mode 100644 src/main/java/com/industriallogic/henrysgroceries/exception/ProductNotFoundException.java diff --git a/src/main/java/com/industriallogic/henrysgroceries/exception/ProductNotFoundException.java b/src/main/java/com/industriallogic/henrysgroceries/exception/ProductNotFoundException.java new file mode 100644 index 00000000..9f70ae42 --- /dev/null +++ b/src/main/java/com/industriallogic/henrysgroceries/exception/ProductNotFoundException.java @@ -0,0 +1,15 @@ +package com.industriallogic.henrysgroceries.exception; + +public class ProductNotFoundException extends Exception { + + private static final long serialVersionUID = 1L; + + public ProductNotFoundException(String message) { + super(message); + } + + public ProductNotFoundException(String message, Throwable cause) { + super(message, cause); + } + +} \ No newline at end of file diff --git a/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java b/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java index 954cdfb3..6a2c9ffc 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java +++ b/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java @@ -26,7 +26,7 @@ public LocalDate getShoppingDate() { return this.shoppingDate; } - public Map getBasket() { + public Map getProductsInBasket() { return Collections.unmodifiableMap(basket); } diff --git a/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java b/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java index 9cf50eb3..79e3d267 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java +++ b/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java @@ -1,12 +1,12 @@ package com.industriallogic.henrysgroceries.offers; import com.industriallogic.henrysgroceries.model.Product; -import lombok.AllArgsConstructor; +import com.industriallogic.henrysgroceries.model.ShoppingBasket; import lombok.Data; import lombok.NonNull; import lombok.RequiredArgsConstructor; - import java.math.BigDecimal; +import java.math.RoundingMode; import java.time.LocalDate; @RequiredArgsConstructor @@ -18,7 +18,7 @@ public class ComboDiscountOffer implements Offer { @NonNull private Product qualifyingProduct; @NonNull - private Integer qualifyingProductQuantity; + private Integer qualifyingProdMinQnty; @NonNull private Product offerOnProduct; @NonNull @@ -28,5 +28,17 @@ public class ComboDiscountOffer implements Offer { @NonNull private LocalDate offerEndDate; + @Override + public BigDecimal getDiscount(ShoppingBasket basket) { + if (isOfferStillValid(basket.getShoppingDate(), offerStartDate, offerEndDate)) { + Integer qualifyingProdCountInBasket = basket.getProductsInBasket().getOrDefault(qualifyingProduct, 0); + if (qualifyingProdCountInBasket >= qualifyingProdMinQnty) { + int purchaseQnty = basket.getProductsInBasket().getOrDefault(offerOnProduct, 0); + int eligibleOfferCount = Math.min(qualifyingProdCountInBasket/qualifyingProdMinQnty, purchaseQnty); + return offerOnProduct.getPrice().multiply(discountFactor).divide(ONE_HUNDRED, RoundingMode.HALF_UP).multiply( new BigDecimal(eligibleOfferCount)); + } + } + return BigDecimal.ZERO; + } } diff --git a/src/main/java/com/industriallogic/henrysgroceries/offers/Offer.java b/src/main/java/com/industriallogic/henrysgroceries/offers/Offer.java index 99cb0550..b128bad4 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/offers/Offer.java +++ b/src/main/java/com/industriallogic/henrysgroceries/offers/Offer.java @@ -1,10 +1,17 @@ package com.industriallogic.henrysgroceries.offers; +import com.industriallogic.henrysgroceries.model.ShoppingBasket; + +import java.math.BigDecimal; import java.time.LocalDate; public interface Offer { + BigDecimal ONE_HUNDRED = new BigDecimal(100); + default boolean isOfferStillValid(LocalDate shoppingDate, LocalDate offerStartDate, LocalDate offerEndDate) { return ( shoppingDate.compareTo(offerStartDate) >= 0 && shoppingDate.compareTo(offerEndDate) <= 0) ; } + + public BigDecimal getDiscount(ShoppingBasket basket); } diff --git a/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java b/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java index 460fcf0f..281f3377 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java +++ b/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java @@ -1,11 +1,13 @@ package com.industriallogic.henrysgroceries.offers; import com.industriallogic.henrysgroceries.model.Product; +import com.industriallogic.henrysgroceries.model.ShoppingBasket; import lombok.Data; import lombok.NonNull; import lombok.RequiredArgsConstructor; import java.math.BigDecimal; +import java.math.RoundingMode; import java.time.LocalDate; @RequiredArgsConstructor @@ -22,4 +24,11 @@ public class PercentageDiscountOffer implements Offer { @NonNull private LocalDate offerEndDate; + public BigDecimal getDiscount(ShoppingBasket basket) { + if (isOfferStillValid(basket.getShoppingDate(), offerStartDate, offerEndDate)) { + Integer productQuantity = basket.getProductsInBasket().getOrDefault(product, 0); + return product.getPrice().multiply(discountPercentage).divide(ONE_HUNDRED, RoundingMode.HALF_UP).multiply(new BigDecimal(productQuantity)); + } + return BigDecimal.ZERO; + } } diff --git a/src/main/java/com/industriallogic/henrysgroceries/provider/OffersProvider.java b/src/main/java/com/industriallogic/henrysgroceries/provider/OffersProvider.java index cefec2c2..b95cd084 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/provider/OffersProvider.java +++ b/src/main/java/com/industriallogic/henrysgroceries/provider/OffersProvider.java @@ -1,5 +1,6 @@ package com.industriallogic.henrysgroceries.provider; +import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; import com.industriallogic.henrysgroceries.model.Product; import com.industriallogic.henrysgroceries.offers.ComboDiscountOffer; import com.industriallogic.henrysgroceries.offers.Offer; @@ -28,10 +29,10 @@ public OffersProvider(ProductProvider productProvider) { * Populate the offers available */ @PostConstruct - public void init() { - Product apples = productProvider.getProduct("Apples").get(); - Product soup = productProvider.getProduct("Soup").get(); - Product bread = productProvider.getProduct("Bread").get(); + public void init() throws ProductNotFoundException { + Product apples = productProvider.getProduct("Apples") ; + Product soup = productProvider.getProduct("Soup") ; + Product bread = productProvider.getProduct("Bread"); LocalDate firstOfNextMonth = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth()); LocalDate endOfNextMonth = firstOfNextMonth.with(TemporalAdjusters.lastDayOfMonth()); diff --git a/src/main/java/com/industriallogic/henrysgroceries/provider/ProductProvider.java b/src/main/java/com/industriallogic/henrysgroceries/provider/ProductProvider.java index 12c83837..438a1b5b 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/provider/ProductProvider.java +++ b/src/main/java/com/industriallogic/henrysgroceries/provider/ProductProvider.java @@ -1,7 +1,9 @@ package com.industriallogic.henrysgroceries.provider; +import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; import com.industriallogic.henrysgroceries.model.MeasurementUnit; import com.industriallogic.henrysgroceries.model.Product; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; @@ -12,6 +14,7 @@ import java.util.Optional; @Component +@Slf4j public class ProductProvider { private Map productList; @@ -26,7 +29,10 @@ public void init() { productList = Collections.unmodifiableMap(stock); } - public Optional getProduct(String productName) { - return Optional.ofNullable(productList.get(productName.toUpperCase())); + public Product getProduct(String productName) throws ProductNotFoundException { + return Optional.ofNullable(productList.get(productName.toUpperCase())).orElseThrow(() -> { + LOGGER.error("No Product found for {} ", productName); + return new ProductNotFoundException("No Product found for - " + productName); + }); } } diff --git a/src/test/java/com/industriallogic/henrysgroceries/provider/ProductProviderTest.java b/src/test/java/com/industriallogic/henrysgroceries/provider/ProductProviderTest.java index 34ac805c..2b74c2f3 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/provider/ProductProviderTest.java +++ b/src/test/java/com/industriallogic/henrysgroceries/provider/ProductProviderTest.java @@ -1,5 +1,6 @@ package com.industriallogic.henrysgroceries.provider; +import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; import com.industriallogic.henrysgroceries.model.MeasurementUnit; import com.industriallogic.henrysgroceries.model.Product; import org.junit.Test; @@ -8,7 +9,6 @@ import org.mockito.junit.MockitoJUnitRunner; import java.math.BigDecimal; -import java.util.Optional; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; @@ -20,24 +20,21 @@ public class ProductProviderTest { private ProductProvider productProvider; @Test - public void getProduct() { - Optional apple = Optional.of(new Product("A123", "Apple", BigDecimal.valueOf(.10), MeasurementUnit.SINGLE)); + public void getProduct() throws ProductNotFoundException { + Product apple = new Product("A123", "Apple", BigDecimal.valueOf(.10), MeasurementUnit.SINGLE); when(productProvider.getProduct("Apples")).thenReturn(apple); assertEquals(productProvider.getProduct("Apples"), apple); } - @Test - public void getProductWithSpace() { - Optional noProduct = Optional.empty(); - when(productProvider.getProduct(" ")).thenReturn(noProduct); - assertEquals(productProvider.getProduct(" "), noProduct); + @Test(expected=ProductNotFoundException.class) + public void getProductWithSpace() throws ProductNotFoundException { + when(productProvider.getProduct(" ")).thenThrow(new ProductNotFoundException("Product Not found")); + productProvider.getProduct(" "); } - @Test - public void getInvalidProduct() { - Optional noProduct = Optional.empty(); - when(productProvider.getProduct("chocolate")).thenReturn(noProduct); - assertEquals(productProvider.getProduct("chocolate"), noProduct); + @Test(expected=ProductNotFoundException.class) + public void getInvalidProduct() throws ProductNotFoundException { + when(productProvider.getProduct("chocolate")).thenThrow(new ProductNotFoundException("Product Not found")); + productProvider.getProduct("chocolate"); } - } \ No newline at end of file From 662ed6e877e89399d71e89b756cf7136a1f7b839 Mon Sep 17 00:00:00 2001 From: Kush Donepudi Date: Wed, 18 Dec 2019 00:19:37 +0000 Subject: [PATCH 04/10] Service to price the items in the basket --- .../offers/ComboDiscountOffer.java | 4 +- .../offers/PercentageDiscountOffer.java | 3 +- .../provider/OffersProvider.java | 2 +- .../service/PriceShoppingBasketService.java | 28 ++- .../provider/OffersProviderTest.java | 1 - .../PriceShoppingBasketServiceTest.java | 191 ++++++++++++++++++ .../testutil/TestMockUtil.java | 2 +- 7 files changed, 221 insertions(+), 10 deletions(-) create mode 100644 src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java diff --git a/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java b/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java index 79e3d267..529650b5 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java +++ b/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java @@ -5,8 +5,8 @@ import lombok.Data; import lombok.NonNull; import lombok.RequiredArgsConstructor; + import java.math.BigDecimal; -import java.math.RoundingMode; import java.time.LocalDate; @RequiredArgsConstructor @@ -35,7 +35,7 @@ public BigDecimal getDiscount(ShoppingBasket basket) { if (qualifyingProdCountInBasket >= qualifyingProdMinQnty) { int purchaseQnty = basket.getProductsInBasket().getOrDefault(offerOnProduct, 0); int eligibleOfferCount = Math.min(qualifyingProdCountInBasket/qualifyingProdMinQnty, purchaseQnty); - return offerOnProduct.getPrice().multiply(discountFactor).divide(ONE_HUNDRED, RoundingMode.HALF_UP).multiply( new BigDecimal(eligibleOfferCount)); + return offerOnProduct.getPrice().multiply(discountFactor).divide(ONE_HUNDRED ).multiply( new BigDecimal(eligibleOfferCount)); } } return BigDecimal.ZERO; diff --git a/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java b/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java index 281f3377..c97283ba 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java +++ b/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java @@ -7,7 +7,6 @@ import lombok.RequiredArgsConstructor; import java.math.BigDecimal; -import java.math.RoundingMode; import java.time.LocalDate; @RequiredArgsConstructor @@ -27,7 +26,7 @@ public class PercentageDiscountOffer implements Offer { public BigDecimal getDiscount(ShoppingBasket basket) { if (isOfferStillValid(basket.getShoppingDate(), offerStartDate, offerEndDate)) { Integer productQuantity = basket.getProductsInBasket().getOrDefault(product, 0); - return product.getPrice().multiply(discountPercentage).divide(ONE_HUNDRED, RoundingMode.HALF_UP).multiply(new BigDecimal(productQuantity)); + return product.getPrice().multiply(discountPercentage).divide(ONE_HUNDRED ).multiply(new BigDecimal(productQuantity)); } return BigDecimal.ZERO; } diff --git a/src/main/java/com/industriallogic/henrysgroceries/provider/OffersProvider.java b/src/main/java/com/industriallogic/henrysgroceries/provider/OffersProvider.java index b95cd084..ba56806f 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/provider/OffersProvider.java +++ b/src/main/java/com/industriallogic/henrysgroceries/provider/OffersProvider.java @@ -37,7 +37,7 @@ public void init() throws ProductNotFoundException { LocalDate firstOfNextMonth = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth()); LocalDate endOfNextMonth = firstOfNextMonth.with(TemporalAdjusters.lastDayOfMonth()); PercentageDiscountOffer applesOffer = new PercentageDiscountOffer("Apple 10% OFF", apples, BigDecimal.valueOf(10), LocalDate.now().plusDays(3), endOfNextMonth); - ComboDiscountOffer discountOffer = new ComboDiscountOffer("COMBO OFFER", soup, 2, bread, BigDecimal.valueOf(50), LocalDate.now().minusDays(1), LocalDate.now().plusDays(7)); + ComboDiscountOffer discountOffer = new ComboDiscountOffer("COMBO OFFER", soup, 2, bread, BigDecimal.valueOf(50), LocalDate.now().minusDays(1), LocalDate.now().plusDays(6)); offers = Collections.unmodifiableList(Arrays.asList(applesOffer, discountOffer)); } diff --git a/src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java b/src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java index 634eb546..6fc4a02c 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java +++ b/src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java @@ -1,12 +1,34 @@ package com.industriallogic.henrysgroceries.service; +import com.industriallogic.henrysgroceries.model.Product; import com.industriallogic.henrysgroceries.model.ShoppingBasket; - +import com.industriallogic.henrysgroceries.provider.OffersProvider; +import com.industriallogic.henrysgroceries.provider.ProductProvider; +import org.springframework.stereotype.Service; import java.math.BigDecimal; +import java.util.Map; +@Service public class PriceShoppingBasketService { - public BigDecimal priceShoppingBasket(ShoppingBasket basket) { - return BigDecimal.ZERO; + private ProductProvider productProvider; + + private OffersProvider offersProvider; + + public PriceShoppingBasketService(ProductProvider productProvider, OffersProvider offersProvider) { + this.productProvider = productProvider; + this.offersProvider = offersProvider; + } + + public BigDecimal priceShoppingBasket(ShoppingBasket shoppingBasket) { + Map basket = shoppingBasket.getProductsInBasket(); + BigDecimal totalDiscount = offersProvider.getOffers().stream() + .map(offer -> offer.getDiscount(shoppingBasket)) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + BigDecimal totalPrice = basket.entrySet().stream() + .map(e -> e.getKey().getPrice().multiply(BigDecimal.valueOf(e.getValue()))) + .reduce(BigDecimal.ZERO, BigDecimal::add); + return totalPrice.subtract(totalDiscount).setScale(2); } } diff --git a/src/test/java/com/industriallogic/henrysgroceries/provider/OffersProviderTest.java b/src/test/java/com/industriallogic/henrysgroceries/provider/OffersProviderTest.java index 22efcb60..464b03c4 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/provider/OffersProviderTest.java +++ b/src/test/java/com/industriallogic/henrysgroceries/provider/OffersProviderTest.java @@ -4,7 +4,6 @@ import com.industriallogic.henrysgroceries.testutil.TestMockUtil; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; diff --git a/src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java b/src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java new file mode 100644 index 00000000..0d6d9adb --- /dev/null +++ b/src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java @@ -0,0 +1,191 @@ +package com.industriallogic.henrysgroceries.service; + +import com.industriallogic.henrysgroceries.model.MeasurementUnit; +import com.industriallogic.henrysgroceries.model.Product; +import com.industriallogic.henrysgroceries.model.ShoppingBasket; +import com.industriallogic.henrysgroceries.provider.OffersProvider; +import com.industriallogic.henrysgroceries.provider.ProductProvider; +import com.industriallogic.henrysgroceries.testutil.TestMockUtil; +import lombok.SneakyThrows; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.temporal.TemporalAdjusters; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class PriceShoppingBasketServiceTest { + + @InjectMocks + private PriceShoppingBasketService pricingService; + + @Mock + private OffersProvider OffersProvider; + + @Mock + private static ProductProvider productProvider; + + @Test + public void price1SoupAnd1BreadNoOfferAppliedTest() { + ShoppingBasket basket = getBasket("Soup", "Bread"); + when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(1.45), bigDecimal); + } + + @Test + public void price2SoupAnd1BreadComboOfferAppliedTest() { + ShoppingBasket basket = getBasket("Soup", "Soup","Bread"); + when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(1.70).setScale(2), bigDecimal); + } + + @Test + public void price2SoupAnd1BreadBoughtYesterdayComboOfferAppliedTest() { + ShoppingBasket basket = getBasket("Soup", "Soup","Bread"); + basket.setShoppingDate(LocalDate.now().minusDays(1)); + when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(1.70).setScale(2), bigDecimal); + } + + @Test + public void price2SoupAnd1BreadBought2DaysAgoNoOfferAppliedTest() { + ShoppingBasket basket = getBasket("Soup", "Soup","Bread"); + basket.setShoppingDate(LocalDate.now().minusDays(2)); + when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(2.10).setScale(2), bigDecimal); + } + + @Test + public void price2SoupAnd1BreadBoughtIn6DaysComboOfferAppliedTest() { + ShoppingBasket basket = getBasket("Soup", "Soup","Bread"); + basket.setShoppingDate(LocalDate.now().plusDays(6)); + when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(1.70).setScale(2), bigDecimal); + } + + @Test + public void price2SoupAnd1BreadBoughtIn7DaysNoOfferAppliedTest() { + ShoppingBasket basket = getBasket("Soup", "Soup","Bread"); + basket.setShoppingDate(LocalDate.now().plusDays(7)); + when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(2.10).setScale(2), bigDecimal); + } + + @Test + public void price3SoupAnd2BreadComboOfferAppliedOn1BreadTest() { + ShoppingBasket basket = getBasket("Soup", "Soup", "Soup", "Bread", "Bread"); + when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); + + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(3.15), bigDecimal); + } + + @Test + public void price4SoupAnd3BreadComboOfferAppliedOn2BreadTest() { + ShoppingBasket basket = getBasket("Soup", "Soup", "Soup", "Soup", "Bread", "Bread", "Bread"); + when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); + + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(4.20).setScale(2), bigDecimal); + } + + @Test + public void price4SoupAnd1BreadComboOfferAppliedOn1BreadTest() { + ShoppingBasket basket = getBasket("Soup", "Soup", "Soup", "Soup", "Bread"); + when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); + + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(3.00).setScale(2), bigDecimal); + } + + @Test + public void price6ApplesAndMilkBoughtTodayNoOfferApplied() { + ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Apples", "Apples", "Apples", "Milk"); + + when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); + + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(1.90).setScale(2), bigDecimal); + } + + @Test + public void price6ApplesAndMilkBoughtIn5DaysPercentOfferApplied() { + ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Apples", "Apples", "Apples", "Milk"); + basket.setShoppingDate(LocalDate.now().plusDays(5)); + when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); + + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(1.84), bigDecimal); + } + + @Test + public void price6ApplesBoughtAfterEndOfFollowingMonthOfferApplied() { + ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Apples", "Apples", "Apples" ); + LocalDate firstOfNextMonth = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth()); + LocalDate endOfNextMonth = firstOfNextMonth.with(TemporalAdjusters.lastDayOfMonth()); + basket.setShoppingDate(endOfNextMonth.plusDays(1)); + when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); + + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(.60).setScale(2), bigDecimal); + } + + @Test + public void price3ApplesAnd2SoupBoughtIn5DaysPercentOfferApplied() { + ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Soup", "Soup"); + + basket.setShoppingDate(LocalDate.now().plusDays(5)); + when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); + + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(1.57), bigDecimal); + } + + @Test + public void price3ApplesAnd2SoupAnd1BreadBoughtIn5DaysComboAndPercentOfferApplied() { + ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Soup", "Soup", "Bread"); + + basket.setShoppingDate(LocalDate.now().plusDays(5)); + when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); + + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(1.97), bigDecimal); + } + + private Map productList() { + Map products = new HashMap(); + products.put("APPLES", new Product("A123", "Apple", BigDecimal.valueOf(.10), MeasurementUnit.SINGLE)); + products.put("BREAD", new Product("B123", "Bread", BigDecimal.valueOf(.80), MeasurementUnit.LOAF)); + products.put("MILK", new Product("M123", "Milk", BigDecimal.valueOf(1.30), MeasurementUnit.BOTTLE)); + products.put("SOUP", new Product("S123", "Soup", BigDecimal.valueOf(.65), MeasurementUnit.TIN)); + return products; + } + + @SneakyThrows + public ShoppingBasket getBasket(String... items) { + when(productProvider.getProduct("Apples")).thenReturn(productList().get("APPLES")); + when(productProvider.getProduct("Bread")).thenReturn(productList().get("BREAD")); + when(productProvider.getProduct("Milk")).thenReturn(productList().get("MILK")); + when(productProvider.getProduct("Soup")).thenReturn(productList().get("SOUP")); + ShoppingBasket basket = new ShoppingBasket(); + for (String item : items) { + basket.addProductToBasket(productProvider.getProduct(item)); + } + return basket; + } +} \ No newline at end of file diff --git a/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java b/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java index a384906c..1244c941 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java +++ b/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java @@ -23,7 +23,7 @@ public static List getOffersList() { LocalDate firstOfNextMonth = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth()); LocalDate endOfNextMonth = firstOfNextMonth.with(TemporalAdjusters.lastDayOfMonth()); PercentageDiscountOffer applesOffer = new PercentageDiscountOffer("Apple 10% OFF", apples, BigDecimal.valueOf(10), LocalDate.now().plusDays(3), endOfNextMonth); - ComboDiscountOffer discountOffer = new ComboDiscountOffer("combo Offer", soup, 2, bread, BigDecimal.valueOf(50), LocalDate.now().minusDays(1), LocalDate.now().plusDays(7)); + ComboDiscountOffer discountOffer = new ComboDiscountOffer("combo Offer", soup, 2, bread, BigDecimal.valueOf(50), LocalDate.now().minusDays(1), LocalDate.now().plusDays(6)); return Collections.unmodifiableList(Arrays.asList(applesOffer, discountOffer)); } } From 14db52d715812718a6d7fc08f51d8fcc7c8f4f16 Mon Sep 17 00:00:00 2001 From: Kush Donepudi Date: Wed, 18 Dec 2019 10:12:18 +0000 Subject: [PATCH 05/10] Implemented CommandLineRunner to take inputs from command lines --- .../PriceShoppingBasketApplication.java | 14 ++- .../service/PriceShoppingBasketService.java | 30 ++++++ ...eShoppingBasketServiceIntegrationTest.java | 101 ++++++++++++++++++ .../PriceShoppingBasketServiceTest.java | 8 +- 4 files changed, 148 insertions(+), 5 deletions(-) create mode 100644 src/test/java/com/industriallogic/henrysgroceries/integration/PriceShoppingBasketServiceIntegrationTest.java diff --git a/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java b/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java index ab9a67af..683f30ef 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java +++ b/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java @@ -1,11 +1,23 @@ package com.industriallogic.henrysgroceries; +import com.industriallogic.henrysgroceries.service.PriceShoppingBasketService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class PriceShoppingBasketApplication { +public class PriceShoppingBasketApplication implements CommandLineRunner { + + @Autowired + PriceShoppingBasketService pricingService; + public static void main(String[] args) { SpringApplication.run(PriceShoppingBasketApplication.class, args); } + + @Override + public void run(String... args) { + pricingService.startShopping(); + } } diff --git a/src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java b/src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java index 6fc4a02c..1bfe76c0 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java +++ b/src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java @@ -1,14 +1,22 @@ package com.industriallogic.henrysgroceries.service; +import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; import com.industriallogic.henrysgroceries.model.Product; import com.industriallogic.henrysgroceries.model.ShoppingBasket; import com.industriallogic.henrysgroceries.provider.OffersProvider; import com.industriallogic.henrysgroceries.provider.ProductProvider; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; + import java.math.BigDecimal; +import java.util.Arrays; +import java.util.List; import java.util.Map; +import java.util.Scanner; @Service +@Slf4j public class PriceShoppingBasketService { private ProductProvider productProvider; @@ -31,4 +39,26 @@ public BigDecimal priceShoppingBasket(ShoppingBasket shoppingBasket) { .reduce(BigDecimal.ZERO, BigDecimal::add); return totalPrice.subtract(totalDiscount).setScale(2); } + + public void startShopping() { + try (Scanner scanner = new Scanner(System.in)) { + while (true) { + LOGGER.info("Enter products separated with space to add to basket OR EXIT to quit: "); + ShoppingBasket basket = new ShoppingBasket(); + List productsNameList = Arrays.asList(StringUtils.split(scanner.nextLine())); + if (productsNameList.contains("EXIT")) return; + productsNameList.forEach(productName -> { + try { + Product product = productProvider.getProduct(productName); + basket.addProductToBasket(product); + LOGGER.info("Added .. " + productName + " to basket"); + } catch (ProductNotFoundException e) { + LOGGER.error("Product not found for .. " + productName); + } + }); + BigDecimal bigDecimal = priceShoppingBasket(basket); + LOGGER.info("Total to Pay " + bigDecimal); + } + } + } } diff --git a/src/test/java/com/industriallogic/henrysgroceries/integration/PriceShoppingBasketServiceIntegrationTest.java b/src/test/java/com/industriallogic/henrysgroceries/integration/PriceShoppingBasketServiceIntegrationTest.java new file mode 100644 index 00000000..a76ca424 --- /dev/null +++ b/src/test/java/com/industriallogic/henrysgroceries/integration/PriceShoppingBasketServiceIntegrationTest.java @@ -0,0 +1,101 @@ +package com.industriallogic.henrysgroceries.integration; + + +import com.industriallogic.henrysgroceries.PriceShoppingBasketApplication; +import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; +import com.industriallogic.henrysgroceries.model.ShoppingBasket; +import com.industriallogic.henrysgroceries.provider.ProductProvider; +import com.industriallogic.henrysgroceries.service.PriceShoppingBasketService; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.ConfigFileApplicationContextInitializer; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.math.BigDecimal; +import java.time.LocalDate; + +import static org.junit.Assert.assertEquals; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = PriceShoppingBasketApplication.class, + initializers = ConfigFileApplicationContextInitializer.class) +@Slf4j +public class PriceShoppingBasketServiceIntegrationTest { + + @Autowired + PriceShoppingBasketService pricingService; + + @Autowired + ProductProvider productProvider; + + @Test + public void price3SoupAnd2BreadComboOfferAppliedOn1BreadTest() { + ShoppingBasket basket = getBasket("Soup", "Soup", "Soup", "Bread", "Bread"); + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(3.15), bigDecimal); + } + + @Test + public void price6ApplesAndMilkBoughtTodayNoOfferApplied() { + ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Apples", "Apples", "Apples", "Milk"); + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(1.90).setScale(2), bigDecimal); + } + + @Test + public void price6ApplesAndMilkBoughtIn5DaysPercentOfferApplied() { + ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Apples", "Apples", "Apples", "Milk"); + basket.setShoppingDate(LocalDate.now().plusDays(5)); + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(1.84), bigDecimal); + } + + @Test + public void price3ApplesAnd2SoupBoughtIn5DaysPercentOfferApplied() { + ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Soup", "Soup"); + basket.setShoppingDate(LocalDate.now().plusDays(5)); + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(1.57), bigDecimal); + } + + @Test + public void price1SoupAnd1BreadNoOfferAppliedTest() { + ShoppingBasket basket = getBasket("Soup", "Bread"); + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + + assertEquals(BigDecimal.valueOf(1.45), bigDecimal); + } + + @Test + public void price2SoupAnd1BreadComboOfferAppliedTest() { + ShoppingBasket basket = getBasket("Soup", "Soup", "Bread"); + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(1.70).setScale(2), bigDecimal); + } + + @Test + public void price3ApplesAnd2SoupAnd1BreadBoughtIn5DaysComboAndPercentOfferApplied() { + ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Soup", "Soup", "Bread"); + + basket.setShoppingDate(LocalDate.now().plusDays(5)); + + BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + assertEquals(BigDecimal.valueOf(1.97), bigDecimal); + } + + public ShoppingBasket getBasket(String... productNames) { + ShoppingBasket basket = new ShoppingBasket(); + for (String productName : productNames) { + try { + basket.addProductToBasket(productProvider.getProduct(productName)); + } catch (ProductNotFoundException e) { + LOGGER.error("Product not found " + productName); + } + } + return basket; + } + +} diff --git a/src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java b/src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java index 0d6d9adb..022da994 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java +++ b/src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java @@ -177,15 +177,15 @@ private Map productList() { } @SneakyThrows - public ShoppingBasket getBasket(String... items) { + public ShoppingBasket getBasket(String... productNames) { when(productProvider.getProduct("Apples")).thenReturn(productList().get("APPLES")); when(productProvider.getProduct("Bread")).thenReturn(productList().get("BREAD")); when(productProvider.getProduct("Milk")).thenReturn(productList().get("MILK")); when(productProvider.getProduct("Soup")).thenReturn(productList().get("SOUP")); ShoppingBasket basket = new ShoppingBasket(); - for (String item : items) { - basket.addProductToBasket(productProvider.getProduct(item)); + for (String productName : productNames) { + basket.addProductToBasket(productProvider.getProduct(productName)); } return basket; } -} \ No newline at end of file +} From 7b9d693d26cdddae6941911229692eece8efb835 Mon Sep 17 00:00:00 2001 From: Kush Donepudi Date: Wed, 18 Dec 2019 11:01:24 +0000 Subject: [PATCH 06/10] Tests for discounts --- .../offers/ComboDiscountOfferTest.java | 38 +++++++++++++ .../offers/PercentageDiscountOfferTest.java | 40 ++++++++++++++ .../PriceShoppingBasketServiceTest.java | 55 +++++-------------- .../testutil/TestMockUtil.java | 31 ++++++++++- 4 files changed, 120 insertions(+), 44 deletions(-) create mode 100644 src/test/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOfferTest.java create mode 100644 src/test/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOfferTest.java diff --git a/src/test/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOfferTest.java b/src/test/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOfferTest.java new file mode 100644 index 00000000..dfa686cc --- /dev/null +++ b/src/test/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOfferTest.java @@ -0,0 +1,38 @@ +package com.industriallogic.henrysgroceries.offers; + +import com.industriallogic.henrysgroceries.model.ShoppingBasket; +import com.industriallogic.henrysgroceries.provider.ProductProvider; +import com.industriallogic.henrysgroceries.testutil.TestMockUtil; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.math.BigDecimal; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.Silent.class) +public class ComboDiscountOfferTest { + + @Mock + private ComboDiscountOffer comboDiscountOffer; + + @Mock + private ProductProvider productProvider; + + @Test + public void getDiscountOn2SoupsAnd1Bread() { + ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup", "Bread"); + when(comboDiscountOffer.getDiscount(basket)).thenReturn(new BigDecimal(.40)); + assertEquals(new BigDecimal(.40), comboDiscountOffer.getDiscount(basket)); + } + + @Test + public void getDiscountOn2SoupsAnd2Bread() { + ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup", "Bread","Bread"); + when(comboDiscountOffer.getDiscount(basket)).thenReturn(new BigDecimal(.40)); + assertEquals(new BigDecimal(.40), comboDiscountOffer.getDiscount(basket)); + } +} \ No newline at end of file diff --git a/src/test/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOfferTest.java b/src/test/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOfferTest.java new file mode 100644 index 00000000..a1461214 --- /dev/null +++ b/src/test/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOfferTest.java @@ -0,0 +1,40 @@ +package com.industriallogic.henrysgroceries.offers; + +import com.industriallogic.henrysgroceries.model.ShoppingBasket; +import com.industriallogic.henrysgroceries.provider.ProductProvider; +import com.industriallogic.henrysgroceries.testutil.TestMockUtil; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.math.BigDecimal; +import java.time.LocalDate; + +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.Silent.class) +public class PercentageDiscountOfferTest { + + @Mock + private PercentageDiscountOffer percentDiscountOffer; + + @Mock + private ProductProvider productProvider; + + @Test + public void getDiscountOn3ApplesBoughtToday() { + ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Apples", "Apples", "Apples"); + when(percentDiscountOffer.getDiscount(basket)).thenReturn(new BigDecimal(.00)); + Assertions.assertEquals(new BigDecimal(.00), percentDiscountOffer.getDiscount(basket)); + } + + @Test + public void getDiscountOn3ApplesBoughtIn5Days() { + ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Apples", "Apples", "Apples"); + basket.setShoppingDate(LocalDate.now().plusDays(5)); + when(percentDiscountOffer.getDiscount(basket)).thenReturn(new BigDecimal(.03)); + Assertions.assertEquals(new BigDecimal(.03), percentDiscountOffer.getDiscount(basket)); + } +} \ No newline at end of file diff --git a/src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java b/src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java index 022da994..ff72ed8b 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java +++ b/src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java @@ -1,12 +1,9 @@ package com.industriallogic.henrysgroceries.service; -import com.industriallogic.henrysgroceries.model.MeasurementUnit; -import com.industriallogic.henrysgroceries.model.Product; import com.industriallogic.henrysgroceries.model.ShoppingBasket; import com.industriallogic.henrysgroceries.provider.OffersProvider; import com.industriallogic.henrysgroceries.provider.ProductProvider; import com.industriallogic.henrysgroceries.testutil.TestMockUtil; -import lombok.SneakyThrows; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; @@ -16,8 +13,6 @@ import java.math.BigDecimal; import java.time.LocalDate; import java.time.temporal.TemporalAdjusters; -import java.util.HashMap; -import java.util.Map; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.when; @@ -36,7 +31,7 @@ public class PriceShoppingBasketServiceTest { @Test public void price1SoupAnd1BreadNoOfferAppliedTest() { - ShoppingBasket basket = getBasket("Soup", "Bread"); + ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Bread"); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); assertEquals(BigDecimal.valueOf(1.45), bigDecimal); @@ -44,7 +39,7 @@ public void price1SoupAnd1BreadNoOfferAppliedTest() { @Test public void price2SoupAnd1BreadComboOfferAppliedTest() { - ShoppingBasket basket = getBasket("Soup", "Soup","Bread"); + ShoppingBasket basket =TestMockUtil.getBasket(productProvider, "Soup", "Soup","Bread"); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); assertEquals(BigDecimal.valueOf(1.70).setScale(2), bigDecimal); @@ -52,7 +47,7 @@ public void price2SoupAnd1BreadComboOfferAppliedTest() { @Test public void price2SoupAnd1BreadBoughtYesterdayComboOfferAppliedTest() { - ShoppingBasket basket = getBasket("Soup", "Soup","Bread"); + ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup","Bread"); basket.setShoppingDate(LocalDate.now().minusDays(1)); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); @@ -61,7 +56,7 @@ public void price2SoupAnd1BreadBoughtYesterdayComboOfferAppliedTest() { @Test public void price2SoupAnd1BreadBought2DaysAgoNoOfferAppliedTest() { - ShoppingBasket basket = getBasket("Soup", "Soup","Bread"); + ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup","Bread"); basket.setShoppingDate(LocalDate.now().minusDays(2)); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); @@ -70,7 +65,7 @@ public void price2SoupAnd1BreadBought2DaysAgoNoOfferAppliedTest() { @Test public void price2SoupAnd1BreadBoughtIn6DaysComboOfferAppliedTest() { - ShoppingBasket basket = getBasket("Soup", "Soup","Bread"); + ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup","Bread"); basket.setShoppingDate(LocalDate.now().plusDays(6)); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); @@ -79,7 +74,7 @@ public void price2SoupAnd1BreadBoughtIn6DaysComboOfferAppliedTest() { @Test public void price2SoupAnd1BreadBoughtIn7DaysNoOfferAppliedTest() { - ShoppingBasket basket = getBasket("Soup", "Soup","Bread"); + ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup","Bread"); basket.setShoppingDate(LocalDate.now().plusDays(7)); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); @@ -88,7 +83,7 @@ public void price2SoupAnd1BreadBoughtIn7DaysNoOfferAppliedTest() { @Test public void price3SoupAnd2BreadComboOfferAppliedOn1BreadTest() { - ShoppingBasket basket = getBasket("Soup", "Soup", "Soup", "Bread", "Bread"); + ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup", "Soup", "Bread", "Bread"); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); @@ -97,7 +92,7 @@ public void price3SoupAnd2BreadComboOfferAppliedOn1BreadTest() { @Test public void price4SoupAnd3BreadComboOfferAppliedOn2BreadTest() { - ShoppingBasket basket = getBasket("Soup", "Soup", "Soup", "Soup", "Bread", "Bread", "Bread"); + ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup", "Soup", "Soup", "Bread", "Bread", "Bread"); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); @@ -106,7 +101,7 @@ public void price4SoupAnd3BreadComboOfferAppliedOn2BreadTest() { @Test public void price4SoupAnd1BreadComboOfferAppliedOn1BreadTest() { - ShoppingBasket basket = getBasket("Soup", "Soup", "Soup", "Soup", "Bread"); + ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup", "Soup", "Soup", "Bread"); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); @@ -115,7 +110,7 @@ public void price4SoupAnd1BreadComboOfferAppliedOn1BreadTest() { @Test public void price6ApplesAndMilkBoughtTodayNoOfferApplied() { - ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Apples", "Apples", "Apples", "Milk"); + ShoppingBasket basket =TestMockUtil.getBasket(productProvider, "Apples", "Apples", "Apples", "Apples", "Apples", "Apples", "Milk"); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); @@ -125,7 +120,7 @@ public void price6ApplesAndMilkBoughtTodayNoOfferApplied() { @Test public void price6ApplesAndMilkBoughtIn5DaysPercentOfferApplied() { - ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Apples", "Apples", "Apples", "Milk"); + ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Apples", "Apples", "Apples", "Apples", "Apples", "Apples", "Milk"); basket.setShoppingDate(LocalDate.now().plusDays(5)); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); @@ -135,7 +130,7 @@ public void price6ApplesAndMilkBoughtIn5DaysPercentOfferApplied() { @Test public void price6ApplesBoughtAfterEndOfFollowingMonthOfferApplied() { - ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Apples", "Apples", "Apples" ); + ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Apples", "Apples", "Apples", "Apples", "Apples", "Apples" ); LocalDate firstOfNextMonth = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth()); LocalDate endOfNextMonth = firstOfNextMonth.with(TemporalAdjusters.lastDayOfMonth()); basket.setShoppingDate(endOfNextMonth.plusDays(1)); @@ -147,7 +142,7 @@ public void price6ApplesBoughtAfterEndOfFollowingMonthOfferApplied() { @Test public void price3ApplesAnd2SoupBoughtIn5DaysPercentOfferApplied() { - ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Soup", "Soup"); + ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Apples", "Apples", "Apples", "Soup", "Soup"); basket.setShoppingDate(LocalDate.now().plusDays(5)); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); @@ -158,7 +153,7 @@ public void price3ApplesAnd2SoupBoughtIn5DaysPercentOfferApplied() { @Test public void price3ApplesAnd2SoupAnd1BreadBoughtIn5DaysComboAndPercentOfferApplied() { - ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Soup", "Soup", "Bread"); + ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Apples", "Apples", "Apples", "Soup", "Soup", "Bread"); basket.setShoppingDate(LocalDate.now().plusDays(5)); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); @@ -166,26 +161,4 @@ public void price3ApplesAnd2SoupAnd1BreadBoughtIn5DaysComboAndPercentOfferApplie BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); assertEquals(BigDecimal.valueOf(1.97), bigDecimal); } - - private Map productList() { - Map products = new HashMap(); - products.put("APPLES", new Product("A123", "Apple", BigDecimal.valueOf(.10), MeasurementUnit.SINGLE)); - products.put("BREAD", new Product("B123", "Bread", BigDecimal.valueOf(.80), MeasurementUnit.LOAF)); - products.put("MILK", new Product("M123", "Milk", BigDecimal.valueOf(1.30), MeasurementUnit.BOTTLE)); - products.put("SOUP", new Product("S123", "Soup", BigDecimal.valueOf(.65), MeasurementUnit.TIN)); - return products; - } - - @SneakyThrows - public ShoppingBasket getBasket(String... productNames) { - when(productProvider.getProduct("Apples")).thenReturn(productList().get("APPLES")); - when(productProvider.getProduct("Bread")).thenReturn(productList().get("BREAD")); - when(productProvider.getProduct("Milk")).thenReturn(productList().get("MILK")); - when(productProvider.getProduct("Soup")).thenReturn(productList().get("SOUP")); - ShoppingBasket basket = new ShoppingBasket(); - for (String productName : productNames) { - basket.addProductToBasket(productProvider.getProduct(productName)); - } - return basket; - } } diff --git a/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java b/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java index 1244c941..4b21960b 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java +++ b/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java @@ -2,16 +2,19 @@ import com.industriallogic.henrysgroceries.model.MeasurementUnit; import com.industriallogic.henrysgroceries.model.Product; +import com.industriallogic.henrysgroceries.model.ShoppingBasket; import com.industriallogic.henrysgroceries.offers.ComboDiscountOffer; import com.industriallogic.henrysgroceries.offers.Offer; import com.industriallogic.henrysgroceries.offers.PercentageDiscountOffer; +import com.industriallogic.henrysgroceries.provider.ProductProvider; +import lombok.SneakyThrows; import java.math.BigDecimal; import java.time.LocalDate; import java.time.temporal.TemporalAdjusters; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.*; + +import static org.mockito.Mockito.when; public class TestMockUtil { @@ -26,4 +29,26 @@ public static List getOffersList() { ComboDiscountOffer discountOffer = new ComboDiscountOffer("combo Offer", soup, 2, bread, BigDecimal.valueOf(50), LocalDate.now().minusDays(1), LocalDate.now().plusDays(6)); return Collections.unmodifiableList(Arrays.asList(applesOffer, discountOffer)); } + + private static Map productList() { + Map products = new HashMap(); + products.put("APPLES", new Product("A123", "Apple", BigDecimal.valueOf(.10), MeasurementUnit.SINGLE)); + products.put("BREAD", new Product("B123", "Bread", BigDecimal.valueOf(.80), MeasurementUnit.LOAF)); + products.put("MILK", new Product("M123", "Milk", BigDecimal.valueOf(1.30), MeasurementUnit.BOTTLE)); + products.put("SOUP", new Product("S123", "Soup", BigDecimal.valueOf(.65), MeasurementUnit.TIN)); + return products; + } + + @SneakyThrows + public static ShoppingBasket getBasket(ProductProvider productProvider, String... productNames) { + when(productProvider.getProduct("Apples")).thenReturn(productList().get("APPLES")); + when(productProvider.getProduct("Bread")).thenReturn(productList().get("BREAD")); + when(productProvider.getProduct("Milk")).thenReturn(productList().get("MILK")); + when(productProvider.getProduct("Soup")).thenReturn(productList().get("SOUP")); + ShoppingBasket basket = new ShoppingBasket(); + for (String productName : productNames) { + basket.addProductToBasket(productProvider.getProduct(productName)); + } + return basket; + } } From fbd0c4b77b8aef9b72e473c6decdf929bab3cfaa Mon Sep 17 00:00:00 2001 From: Kush Donepudi Date: Wed, 18 Dec 2019 14:13:36 +0000 Subject: [PATCH 07/10] Refactoring and Checkout option in command Line --- .../PriceShoppingBasketApplication.java | 61 +++++++++++++++- .../henrysgroceries/model/Product.java | 72 +++---------------- .../henrysgroceries/model/ShoppingBasket.java | 14 +++- .../service/PriceShoppingBasketService.java | 43 ++--------- ...eShoppingBasketServiceIntegrationTest.java | 41 +++++++---- .../PriceShoppingBasketServiceTest.java | 28 ++++---- 6 files changed, 128 insertions(+), 131 deletions(-) diff --git a/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java b/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java index 683f30ef..2cdf1043 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java +++ b/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java @@ -1,16 +1,29 @@ package com.industriallogic.henrysgroceries; +import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; +import com.industriallogic.henrysgroceries.model.ShoppingBasket; +import com.industriallogic.henrysgroceries.provider.ProductProvider; import com.industriallogic.henrysgroceries.service.PriceShoppingBasketService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; + @SpringBootApplication +@Slf4j public class PriceShoppingBasketApplication implements CommandLineRunner { @Autowired - PriceShoppingBasketService pricingService; + private PriceShoppingBasketService pricingService; + @Autowired + private ProductProvider productProvider; public static void main(String[] args) { SpringApplication.run(PriceShoppingBasketApplication.class, args); @@ -18,6 +31,50 @@ public static void main(String[] args) { @Override public void run(String... args) { - pricingService.startShopping(); + startShopping(); } + + public void startShopping() { + ShoppingBasket basket = new ShoppingBasket(); + try (Scanner scanner = new Scanner(System.in)) { + while (true) { + LOGGER.info("Enter 1. To add products to basket | Enter 2. To checkout | Enter X. To EXIT"); + String option = scanner.next(); + switch (option) { + case "1": + addProductsToBasket(basket, scanner); + break; + case "2": + checkout(basket); + break; + case "X": + return; + default: + LOGGER.info("Invalid entry, please re-enter '1' OR '2' OR 'X'"); + } + } + } + } + + private void addProductsToBasket(ShoppingBasket basket, Scanner scanner) { + LOGGER.info("Enter products separated with space to add to basket: "); + scanner.nextLine(); + List productsNameList = Arrays.asList(StringUtils.split(scanner.nextLine())); + productsNameList.forEach(productName -> { + try { + BigDecimal curntTotalAmount = basket.addProductToBasket(productProvider.getProduct(productName)); + LOGGER.info("Added .. " + productName + " to basket"); + LOGGER.info("Current Total Amount " + curntTotalAmount); + } catch (ProductNotFoundException e) { + LOGGER.error("Product not found for .. " + productName); + } + }); + } + + private void checkout(ShoppingBasket basket) { + BigDecimal price = basket.getCurTotalAmount(); + BigDecimal discount = pricingService.getTotalDiscount(basket); + LOGGER.info("Total to Pay " + price.subtract(discount).setScale(2)); + } + } diff --git a/src/main/java/com/industriallogic/henrysgroceries/model/Product.java b/src/main/java/com/industriallogic/henrysgroceries/model/Product.java index 8e4ec5a6..f75178c6 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/model/Product.java +++ b/src/main/java/com/industriallogic/henrysgroceries/model/Product.java @@ -1,68 +1,16 @@ package com.industriallogic.henrysgroceries.model; -import java.math.BigDecimal; -import java.util.Objects; +import lombok.Data; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import java.math.BigDecimal; +@RequiredArgsConstructor +@Data public class Product { - private String productCode; - private String name; - private BigDecimal price; - private MeasurementUnit unit; - - public Product(String productCode, String name, BigDecimal price, MeasurementUnit unit) { - this.productCode = productCode; - this.name = name; - this.price = price; - this.unit = unit; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Product)) return false; - Product product = (Product) o; - return Objects.equals(getProductCode(), product.getProductCode()) && - Objects.equals(getName(), product.getName()) && - Objects.equals(getPrice(), product.getPrice()) && - getUnit() == product.getUnit(); - } - - @Override - public int hashCode() { - return Objects.hash(getProductCode(), getName(), getPrice(), getUnit()); - } - - public String getProductCode() { - return productCode; - } - - public void setProductCode(String productCode) { - this.productCode = productCode; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public BigDecimal getPrice() { - return price; - } - - public void setPrice(BigDecimal price) { - this.price = price; - } - - public MeasurementUnit getUnit() { - return unit; - } - - public void setUnit(MeasurementUnit unit) { - this.unit = unit; - } - + @NonNull private String productCode; + @NonNull private String name; + @NonNull private BigDecimal price; + @NonNull private MeasurementUnit unit; } diff --git a/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java b/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java index 6a2c9ffc..c850fb2c 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java +++ b/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java @@ -1,21 +1,28 @@ package com.industriallogic.henrysgroceries.model; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; import java.time.LocalDate; import java.util.Collections; import java.util.HashMap; import java.util.Map; +@NoArgsConstructor public class ShoppingBasket { - private Map basket = new HashMap<>(); + private final Map basket = new HashMap<>(); private LocalDate shoppingDate = LocalDate.now(); + private BigDecimal curTotalAmount = BigDecimal.ZERO; - public void addProductToBasket(Product product) { + public BigDecimal addProductToBasket(Product product) { basket.compute(product, (k, v) -> { return v == null ? 1 : v + 1; }); + curTotalAmount = curTotalAmount.add(product.getPrice()); + return curTotalAmount; } public void setShoppingDate(LocalDate date) { @@ -30,6 +37,9 @@ public Map getProductsInBasket() { return Collections.unmodifiableMap(basket); } + public BigDecimal getCurTotalAmount() { + return curTotalAmount; + } } diff --git a/src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java b/src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java index 1bfe76c0..006584c4 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java +++ b/src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java @@ -1,19 +1,12 @@ package com.industriallogic.henrysgroceries.service; -import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; -import com.industriallogic.henrysgroceries.model.Product; import com.industriallogic.henrysgroceries.model.ShoppingBasket; import com.industriallogic.henrysgroceries.provider.OffersProvider; import com.industriallogic.henrysgroceries.provider.ProductProvider; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import java.math.BigDecimal; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Scanner; @Service @Slf4j @@ -28,37 +21,13 @@ public PriceShoppingBasketService(ProductProvider productProvider, OffersProvide this.offersProvider = offersProvider; } - public BigDecimal priceShoppingBasket(ShoppingBasket shoppingBasket) { - Map basket = shoppingBasket.getProductsInBasket(); - BigDecimal totalDiscount = offersProvider.getOffers().stream() - .map(offer -> offer.getDiscount(shoppingBasket)) - .reduce(BigDecimal.ZERO, BigDecimal::add); - - BigDecimal totalPrice = basket.entrySet().stream() - .map(e -> e.getKey().getPrice().multiply(BigDecimal.valueOf(e.getValue()))) - .reduce(BigDecimal.ZERO, BigDecimal::add); - return totalPrice.subtract(totalDiscount).setScale(2); + public BigDecimal totalPriceToPay(ShoppingBasket shoppingBasket) { + return shoppingBasket.getCurTotalAmount().subtract(getTotalDiscount(shoppingBasket)).setScale(2); } - public void startShopping() { - try (Scanner scanner = new Scanner(System.in)) { - while (true) { - LOGGER.info("Enter products separated with space to add to basket OR EXIT to quit: "); - ShoppingBasket basket = new ShoppingBasket(); - List productsNameList = Arrays.asList(StringUtils.split(scanner.nextLine())); - if (productsNameList.contains("EXIT")) return; - productsNameList.forEach(productName -> { - try { - Product product = productProvider.getProduct(productName); - basket.addProductToBasket(product); - LOGGER.info("Added .. " + productName + " to basket"); - } catch (ProductNotFoundException e) { - LOGGER.error("Product not found for .. " + productName); - } - }); - BigDecimal bigDecimal = priceShoppingBasket(basket); - LOGGER.info("Total to Pay " + bigDecimal); - } - } + public BigDecimal getTotalDiscount(ShoppingBasket shoppingBasket) { + return offersProvider.getOffers().stream() + .map(offer -> offer.getDiscount(shoppingBasket)) + .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2); } } diff --git a/src/test/java/com/industriallogic/henrysgroceries/integration/PriceShoppingBasketServiceIntegrationTest.java b/src/test/java/com/industriallogic/henrysgroceries/integration/PriceShoppingBasketServiceIntegrationTest.java index a76ca424..8ee70601 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/integration/PriceShoppingBasketServiceIntegrationTest.java +++ b/src/test/java/com/industriallogic/henrysgroceries/integration/PriceShoppingBasketServiceIntegrationTest.java @@ -26,45 +26,45 @@ public class PriceShoppingBasketServiceIntegrationTest { @Autowired - PriceShoppingBasketService pricingService; + private PriceShoppingBasketService pricingService; @Autowired - ProductProvider productProvider; + private ProductProvider productProvider; @Test public void price3SoupAnd2BreadComboOfferAppliedOn1BreadTest() { ShoppingBasket basket = getBasket("Soup", "Soup", "Soup", "Bread", "Bread"); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(3.15), bigDecimal); } @Test - public void price6ApplesAndMilkBoughtTodayNoOfferApplied() { + public void price6ApplesAndMilkBoughtTodayNoOfferAppliedTest() { ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Apples", "Apples", "Apples", "Milk"); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(1.90).setScale(2), bigDecimal); } @Test - public void price6ApplesAndMilkBoughtIn5DaysPercentOfferApplied() { + public void price6ApplesAndMilkBoughtIn5DaysPercentOfferAppliedTest() { ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Apples", "Apples", "Apples", "Milk"); basket.setShoppingDate(LocalDate.now().plusDays(5)); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(1.84), bigDecimal); } @Test - public void price3ApplesAnd2SoupBoughtIn5DaysPercentOfferApplied() { + public void price3ApplesAnd2SoupBoughtIn5DaysPercentOfferAppliedTest() { ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Soup", "Soup"); basket.setShoppingDate(LocalDate.now().plusDays(5)); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(1.57), bigDecimal); } @Test public void price1SoupAnd1BreadNoOfferAppliedTest() { ShoppingBasket basket = getBasket("Soup", "Bread"); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(1.45), bigDecimal); } @@ -72,17 +72,30 @@ public void price1SoupAnd1BreadNoOfferAppliedTest() { @Test public void price2SoupAnd1BreadComboOfferAppliedTest() { ShoppingBasket basket = getBasket("Soup", "Soup", "Bread"); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(1.70).setScale(2), bigDecimal); } @Test - public void price3ApplesAnd2SoupAnd1BreadBoughtIn5DaysComboAndPercentOfferApplied() { - ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Soup", "Soup", "Bread"); + public void price2SoupAnd1BreadGetDiscountAmountTest() { + ShoppingBasket basket = getBasket("Soup", "Soup", "Bread"); + BigDecimal bigDecimal = pricingService.getTotalDiscount(basket); + assertEquals(BigDecimal.valueOf(.40).setScale(2), bigDecimal); + } + @Test + public void price3ApplesAnd2SoupAnd1BreadBoughtIn5DaysComboAndPercentOfferAppliedTest() { + ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Soup", "Soup", "Bread"); basket.setShoppingDate(LocalDate.now().plusDays(5)); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); + assertEquals(BigDecimal.valueOf(1.97), bigDecimal); + } - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + @Test + public void price3ApplesAnd2SoupAnd1BreadBoughtIn5DaysGetDiscountAmountTest() { + ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Soup", "Soup", "Bread"); + basket.setShoppingDate(LocalDate.now().plusDays(5)); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(1.97), bigDecimal); } diff --git a/src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java b/src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java index ff72ed8b..d8bb0ddd 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java +++ b/src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java @@ -33,7 +33,7 @@ public class PriceShoppingBasketServiceTest { public void price1SoupAnd1BreadNoOfferAppliedTest() { ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Bread"); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(1.45), bigDecimal); } @@ -41,7 +41,7 @@ public void price1SoupAnd1BreadNoOfferAppliedTest() { public void price2SoupAnd1BreadComboOfferAppliedTest() { ShoppingBasket basket =TestMockUtil.getBasket(productProvider, "Soup", "Soup","Bread"); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(1.70).setScale(2), bigDecimal); } @@ -50,7 +50,7 @@ public void price2SoupAnd1BreadBoughtYesterdayComboOfferAppliedTest() { ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup","Bread"); basket.setShoppingDate(LocalDate.now().minusDays(1)); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(1.70).setScale(2), bigDecimal); } @@ -59,7 +59,7 @@ public void price2SoupAnd1BreadBought2DaysAgoNoOfferAppliedTest() { ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup","Bread"); basket.setShoppingDate(LocalDate.now().minusDays(2)); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(2.10).setScale(2), bigDecimal); } @@ -68,7 +68,7 @@ public void price2SoupAnd1BreadBoughtIn6DaysComboOfferAppliedTest() { ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup","Bread"); basket.setShoppingDate(LocalDate.now().plusDays(6)); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(1.70).setScale(2), bigDecimal); } @@ -77,7 +77,7 @@ public void price2SoupAnd1BreadBoughtIn7DaysNoOfferAppliedTest() { ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup","Bread"); basket.setShoppingDate(LocalDate.now().plusDays(7)); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(2.10).setScale(2), bigDecimal); } @@ -86,7 +86,7 @@ public void price3SoupAnd2BreadComboOfferAppliedOn1BreadTest() { ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup", "Soup", "Bread", "Bread"); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(3.15), bigDecimal); } @@ -95,7 +95,7 @@ public void price4SoupAnd3BreadComboOfferAppliedOn2BreadTest() { ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup", "Soup", "Soup", "Bread", "Bread", "Bread"); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(4.20).setScale(2), bigDecimal); } @@ -104,7 +104,7 @@ public void price4SoupAnd1BreadComboOfferAppliedOn1BreadTest() { ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup", "Soup", "Soup", "Bread"); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(3.00).setScale(2), bigDecimal); } @@ -114,7 +114,7 @@ public void price6ApplesAndMilkBoughtTodayNoOfferApplied() { when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(1.90).setScale(2), bigDecimal); } @@ -124,7 +124,7 @@ public void price6ApplesAndMilkBoughtIn5DaysPercentOfferApplied() { basket.setShoppingDate(LocalDate.now().plusDays(5)); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(1.84), bigDecimal); } @@ -136,7 +136,7 @@ public void price6ApplesBoughtAfterEndOfFollowingMonthOfferApplied() { basket.setShoppingDate(endOfNextMonth.plusDays(1)); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(.60).setScale(2), bigDecimal); } @@ -147,7 +147,7 @@ public void price3ApplesAnd2SoupBoughtIn5DaysPercentOfferApplied() { basket.setShoppingDate(LocalDate.now().plusDays(5)); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(1.57), bigDecimal); } @@ -158,7 +158,7 @@ public void price3ApplesAnd2SoupAnd1BreadBoughtIn5DaysComboAndPercentOfferApplie basket.setShoppingDate(LocalDate.now().plusDays(5)); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.priceShoppingBasket(basket); + BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); assertEquals(BigDecimal.valueOf(1.97), bigDecimal); } } From 7231b129b8367e8e80bd6dfe64e6f1f5c139fe1f Mon Sep 17 00:00:00 2001 From: Kush Donepudi Date: Wed, 18 Dec 2019 17:25:33 +0000 Subject: [PATCH 08/10] Refactoring the service and added Assumptions Notes --- Assumptions | 6 + pom.xml | 7 + .../PriceShoppingBasketApplication.java | 26 +- .../henrysgroceries/model/ShoppingBasket.java | 2 + .../offers/ComboDiscountOffer.java | 5 + .../offers/PercentageDiscountOffer.java | 5 + .../provider/OffersProvider.java | 2 + .../provider/ProductProvider.java | 5 +- .../service/PriceShoppingBasketService.java | 33 --- .../service/ShoppingBasketService.java | 14 + .../service/ShoppingBasketServiceImpl.java | 58 +++++ ...eShoppingBasketServiceIntegrationTest.java | 114 -------- .../ShoppingBasketServiceIntegrationTest.java | 45 ++++ .../offers/ComboDiscountOfferTest.java | 45 +++- .../offers/PercentageDiscountOfferTest.java | 43 +++- .../PriceShoppingBasketServiceTest.java | 164 ------------ .../service/ShoppingBasketServiceTest.java | 243 ++++++++++++++++++ .../testutil/TestMockUtil.java | 24 +- 18 files changed, 478 insertions(+), 363 deletions(-) create mode 100644 Assumptions delete mode 100644 src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java create mode 100644 src/main/java/com/industriallogic/henrysgroceries/service/ShoppingBasketService.java create mode 100644 src/main/java/com/industriallogic/henrysgroceries/service/ShoppingBasketServiceImpl.java delete mode 100644 src/test/java/com/industriallogic/henrysgroceries/integration/PriceShoppingBasketServiceIntegrationTest.java create mode 100644 src/test/java/com/industriallogic/henrysgroceries/integration/ShoppingBasketServiceIntegrationTest.java delete mode 100644 src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java create mode 100644 src/test/java/com/industriallogic/henrysgroceries/service/ShoppingBasketServiceTest.java diff --git a/Assumptions b/Assumptions new file mode 100644 index 00000000..2ce674f5 --- /dev/null +++ b/Assumptions @@ -0,0 +1,6 @@ +Notes: +1. Each offer is applied independently, offer applied previously have no bearing on next offer. +2. May be rules be externalized, may be a rules engine like Drools. +3. Possibly could have implemented visitor pattern. +4. Could have added more options at command line i.e show available products, offers or discount applied. but kept it to minimal to calculate the final price after applying discounts. +5. \ No newline at end of file diff --git a/pom.xml b/pom.xml index bac0d155..df7671e4 100644 --- a/pom.xml +++ b/pom.xml @@ -59,6 +59,13 @@ org.springframework.boot spring-boot-maven-plugin + + + + repackage + + + diff --git a/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java b/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java index 2cdf1043..d08ebb2a 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java +++ b/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java @@ -2,8 +2,7 @@ import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; import com.industriallogic.henrysgroceries.model.ShoppingBasket; -import com.industriallogic.henrysgroceries.provider.ProductProvider; -import com.industriallogic.henrysgroceries.service.PriceShoppingBasketService; +import com.industriallogic.henrysgroceries.service.ShoppingBasketServiceImpl; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -21,9 +20,7 @@ public class PriceShoppingBasketApplication implements CommandLineRunner { @Autowired - private PriceShoppingBasketService pricingService; - @Autowired - private ProductProvider productProvider; + private ShoppingBasketServiceImpl shoppingBasketService; public static void main(String[] args) { SpringApplication.run(PriceShoppingBasketApplication.class, args); @@ -35,17 +32,16 @@ public void run(String... args) { } public void startShopping() { - ShoppingBasket basket = new ShoppingBasket(); try (Scanner scanner = new Scanner(System.in)) { while (true) { LOGGER.info("Enter 1. To add products to basket | Enter 2. To checkout | Enter X. To EXIT"); String option = scanner.next(); switch (option) { case "1": - addProductsToBasket(basket, scanner); + addProductsToBasket( scanner); break; case "2": - checkout(basket); + checkout( ); break; case "X": return; @@ -56,25 +52,23 @@ public void startShopping() { } } - private void addProductsToBasket(ShoppingBasket basket, Scanner scanner) { + private void addProductsToBasket( Scanner scanner) { LOGGER.info("Enter products separated with space to add to basket: "); scanner.nextLine(); List productsNameList = Arrays.asList(StringUtils.split(scanner.nextLine())); productsNameList.forEach(productName -> { try { - BigDecimal curntTotalAmount = basket.addProductToBasket(productProvider.getProduct(productName)); - LOGGER.info("Added .. " + productName + " to basket"); - LOGGER.info("Current Total Amount " + curntTotalAmount); + BigDecimal curntTotalAmount = shoppingBasketService.addProductToBasket(productName); + LOGGER.info(String.format("Added %s to basket. Current total amount %.2f " ,productName, curntTotalAmount)); } catch (ProductNotFoundException e) { LOGGER.error("Product not found for .. " + productName); } }); } - private void checkout(ShoppingBasket basket) { - BigDecimal price = basket.getCurTotalAmount(); - BigDecimal discount = pricingService.getTotalDiscount(basket); - LOGGER.info("Total to Pay " + price.subtract(discount).setScale(2)); + private void checkout( ) { + BigDecimal toPay = shoppingBasketService.totalPriceToPay(); + LOGGER.info("Total to Pay after discount " + toPay); } } diff --git a/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java b/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java index c850fb2c..e1ad9082 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java +++ b/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java @@ -2,6 +2,7 @@ import lombok.NoArgsConstructor; +import org.springframework.stereotype.Component; import java.math.BigDecimal; import java.time.LocalDate; @@ -10,6 +11,7 @@ import java.util.Map; @NoArgsConstructor +@Component public class ShoppingBasket { private final Map basket = new HashMap<>(); diff --git a/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java b/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java index 529650b5..5269f531 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java +++ b/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java @@ -28,6 +28,11 @@ public class ComboDiscountOffer implements Offer { @NonNull private LocalDate offerEndDate; + /** + * Applies Combo discount offer if basket qualifies for the offer + * @param basket - basket with products + * @return - discount amount to be applied on the basket + */ @Override public BigDecimal getDiscount(ShoppingBasket basket) { if (isOfferStillValid(basket.getShoppingDate(), offerStartDate, offerEndDate)) { diff --git a/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java b/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java index c97283ba..6245e651 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java +++ b/src/main/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOffer.java @@ -23,6 +23,11 @@ public class PercentageDiscountOffer implements Offer { @NonNull private LocalDate offerEndDate; + /** + * Apples percentage discount offer if basket qualifies for the offer + * @param basket - basket with products + * @return - discount amount to be applied on the basket + */ public BigDecimal getDiscount(ShoppingBasket basket) { if (isOfferStillValid(basket.getShoppingDate(), offerStartDate, offerEndDate)) { Integer productQuantity = basket.getProductsInBasket().getOrDefault(product, 0); diff --git a/src/main/java/com/industriallogic/henrysgroceries/provider/OffersProvider.java b/src/main/java/com/industriallogic/henrysgroceries/provider/OffersProvider.java index ba56806f..4bcefaa8 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/provider/OffersProvider.java +++ b/src/main/java/com/industriallogic/henrysgroceries/provider/OffersProvider.java @@ -25,8 +25,10 @@ public class OffersProvider { public OffersProvider(ProductProvider productProvider) { this.productProvider = productProvider; } + /** * Populate the offers available + * */ @PostConstruct public void init() throws ProductNotFoundException { diff --git a/src/main/java/com/industriallogic/henrysgroceries/provider/ProductProvider.java b/src/main/java/com/industriallogic/henrysgroceries/provider/ProductProvider.java index 438a1b5b..367723b6 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/provider/ProductProvider.java +++ b/src/main/java/com/industriallogic/henrysgroceries/provider/ProductProvider.java @@ -19,6 +19,9 @@ public class ProductProvider { private Map productList; + /** + * Populates with all available products + */ @PostConstruct public void init() { Map stock = new HashMap<>(); @@ -31,7 +34,7 @@ public void init() { public Product getProduct(String productName) throws ProductNotFoundException { return Optional.ofNullable(productList.get(productName.toUpperCase())).orElseThrow(() -> { - LOGGER.error("No Product found for {} ", productName); + LOGGER.error("No Product found for", productName); return new ProductNotFoundException("No Product found for - " + productName); }); } diff --git a/src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java b/src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java deleted file mode 100644 index 006584c4..00000000 --- a/src/main/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketService.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.industriallogic.henrysgroceries.service; - -import com.industriallogic.henrysgroceries.model.ShoppingBasket; -import com.industriallogic.henrysgroceries.provider.OffersProvider; -import com.industriallogic.henrysgroceries.provider.ProductProvider; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.math.BigDecimal; - -@Service -@Slf4j -public class PriceShoppingBasketService { - - private ProductProvider productProvider; - - private OffersProvider offersProvider; - - public PriceShoppingBasketService(ProductProvider productProvider, OffersProvider offersProvider) { - this.productProvider = productProvider; - this.offersProvider = offersProvider; - } - - public BigDecimal totalPriceToPay(ShoppingBasket shoppingBasket) { - return shoppingBasket.getCurTotalAmount().subtract(getTotalDiscount(shoppingBasket)).setScale(2); - } - - public BigDecimal getTotalDiscount(ShoppingBasket shoppingBasket) { - return offersProvider.getOffers().stream() - .map(offer -> offer.getDiscount(shoppingBasket)) - .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2); - } -} diff --git a/src/main/java/com/industriallogic/henrysgroceries/service/ShoppingBasketService.java b/src/main/java/com/industriallogic/henrysgroceries/service/ShoppingBasketService.java new file mode 100644 index 00000000..aa210cb9 --- /dev/null +++ b/src/main/java/com/industriallogic/henrysgroceries/service/ShoppingBasketService.java @@ -0,0 +1,14 @@ +package com.industriallogic.henrysgroceries.service; + +import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; + +import java.math.BigDecimal; + +public interface ShoppingBasketService { + + BigDecimal totalPriceToPay( ) ; + + BigDecimal getTotalDiscount( ) ; + + BigDecimal addProductToBasket(String productName) throws ProductNotFoundException ; +} diff --git a/src/main/java/com/industriallogic/henrysgroceries/service/ShoppingBasketServiceImpl.java b/src/main/java/com/industriallogic/henrysgroceries/service/ShoppingBasketServiceImpl.java new file mode 100644 index 00000000..d53bc5dd --- /dev/null +++ b/src/main/java/com/industriallogic/henrysgroceries/service/ShoppingBasketServiceImpl.java @@ -0,0 +1,58 @@ +package com.industriallogic.henrysgroceries.service; + +import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; +import com.industriallogic.henrysgroceries.model.ShoppingBasket; +import com.industriallogic.henrysgroceries.provider.OffersProvider; +import com.industriallogic.henrysgroceries.provider.ProductProvider; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; + +@Service +@Slf4j +public class ShoppingBasketServiceImpl implements ShoppingBasketService { + + private ProductProvider productProvider; + + private OffersProvider offersProvider; + + private ShoppingBasket basket; + + public ShoppingBasketServiceImpl(ProductProvider productProvider, OffersProvider offersProvider, ShoppingBasket basket) { + this.productProvider = productProvider; + this.offersProvider = offersProvider; + this.basket = basket; + } + + /** + * Calculates the final amount to pay after applying discount if any + * @return totalAmount to pay + */ + @Override + public BigDecimal totalPriceToPay( ) { + return basket.getCurTotalAmount().subtract(getTotalDiscount()).setScale(2); + } + + /** + * Calculates the total discount amount to apply on the basket + * @return total discount + */ + @Override + public BigDecimal getTotalDiscount() { + return offersProvider.getOffers().stream() + .map(offer -> offer.getDiscount(basket)) + .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2); + } + + /** + * Adds a product in to basket + * @param productName + * @return current total amount + * @throws ProductNotFoundException, if product is not found in product repository + */ + @Override + public BigDecimal addProductToBasket(String productName) throws ProductNotFoundException { + return basket.addProductToBasket(productProvider.getProduct(productName)); + } +} diff --git a/src/test/java/com/industriallogic/henrysgroceries/integration/PriceShoppingBasketServiceIntegrationTest.java b/src/test/java/com/industriallogic/henrysgroceries/integration/PriceShoppingBasketServiceIntegrationTest.java deleted file mode 100644 index 8ee70601..00000000 --- a/src/test/java/com/industriallogic/henrysgroceries/integration/PriceShoppingBasketServiceIntegrationTest.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.industriallogic.henrysgroceries.integration; - - -import com.industriallogic.henrysgroceries.PriceShoppingBasketApplication; -import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; -import com.industriallogic.henrysgroceries.model.ShoppingBasket; -import com.industriallogic.henrysgroceries.provider.ProductProvider; -import com.industriallogic.henrysgroceries.service.PriceShoppingBasketService; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.ConfigFileApplicationContextInitializer; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import java.math.BigDecimal; -import java.time.LocalDate; - -import static org.junit.Assert.assertEquals; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = PriceShoppingBasketApplication.class, - initializers = ConfigFileApplicationContextInitializer.class) -@Slf4j -public class PriceShoppingBasketServiceIntegrationTest { - - @Autowired - private PriceShoppingBasketService pricingService; - - @Autowired - private ProductProvider productProvider; - - @Test - public void price3SoupAnd2BreadComboOfferAppliedOn1BreadTest() { - ShoppingBasket basket = getBasket("Soup", "Soup", "Soup", "Bread", "Bread"); - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(3.15), bigDecimal); - } - - @Test - public void price6ApplesAndMilkBoughtTodayNoOfferAppliedTest() { - ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Apples", "Apples", "Apples", "Milk"); - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(1.90).setScale(2), bigDecimal); - } - - @Test - public void price6ApplesAndMilkBoughtIn5DaysPercentOfferAppliedTest() { - ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Apples", "Apples", "Apples", "Milk"); - basket.setShoppingDate(LocalDate.now().plusDays(5)); - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(1.84), bigDecimal); - } - - @Test - public void price3ApplesAnd2SoupBoughtIn5DaysPercentOfferAppliedTest() { - ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Soup", "Soup"); - basket.setShoppingDate(LocalDate.now().plusDays(5)); - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(1.57), bigDecimal); - } - - @Test - public void price1SoupAnd1BreadNoOfferAppliedTest() { - ShoppingBasket basket = getBasket("Soup", "Bread"); - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - - assertEquals(BigDecimal.valueOf(1.45), bigDecimal); - } - - @Test - public void price2SoupAnd1BreadComboOfferAppliedTest() { - ShoppingBasket basket = getBasket("Soup", "Soup", "Bread"); - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(1.70).setScale(2), bigDecimal); - } - - @Test - public void price2SoupAnd1BreadGetDiscountAmountTest() { - ShoppingBasket basket = getBasket("Soup", "Soup", "Bread"); - BigDecimal bigDecimal = pricingService.getTotalDiscount(basket); - assertEquals(BigDecimal.valueOf(.40).setScale(2), bigDecimal); - } - - @Test - public void price3ApplesAnd2SoupAnd1BreadBoughtIn5DaysComboAndPercentOfferAppliedTest() { - ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Soup", "Soup", "Bread"); - basket.setShoppingDate(LocalDate.now().plusDays(5)); - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(1.97), bigDecimal); - } - - @Test - public void price3ApplesAnd2SoupAnd1BreadBoughtIn5DaysGetDiscountAmountTest() { - ShoppingBasket basket = getBasket("Apples", "Apples", "Apples", "Soup", "Soup", "Bread"); - basket.setShoppingDate(LocalDate.now().plusDays(5)); - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(1.97), bigDecimal); - } - - public ShoppingBasket getBasket(String... productNames) { - ShoppingBasket basket = new ShoppingBasket(); - for (String productName : productNames) { - try { - basket.addProductToBasket(productProvider.getProduct(productName)); - } catch (ProductNotFoundException e) { - LOGGER.error("Product not found " + productName); - } - } - return basket; - } - -} diff --git a/src/test/java/com/industriallogic/henrysgroceries/integration/ShoppingBasketServiceIntegrationTest.java b/src/test/java/com/industriallogic/henrysgroceries/integration/ShoppingBasketServiceIntegrationTest.java new file mode 100644 index 00000000..829735e6 --- /dev/null +++ b/src/test/java/com/industriallogic/henrysgroceries/integration/ShoppingBasketServiceIntegrationTest.java @@ -0,0 +1,45 @@ +package com.industriallogic.henrysgroceries.integration; + + +import com.industriallogic.henrysgroceries.PriceShoppingBasketApplication; +import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; +import com.industriallogic.henrysgroceries.service.ShoppingBasketService; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.ConfigFileApplicationContextInitializer; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.math.BigDecimal; + +import static org.junit.Assert.assertEquals; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = PriceShoppingBasketApplication.class, + initializers = ConfigFileApplicationContextInitializer.class) +@Slf4j +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +public class ShoppingBasketServiceIntegrationTest { + + @Autowired + private ShoppingBasketService shoppingBasketService; + + @Test + public void price3SoupAnd2BreadComboOfferAppliedOn1BreadTest() throws ProductNotFoundException { + shoppingBasketService.addProductToBasket("Soup"); + shoppingBasketService.addProductToBasket("Soup"); + shoppingBasketService.addProductToBasket("Soup"); + shoppingBasketService.addProductToBasket("Bread"); + BigDecimal cuurTotal = shoppingBasketService.addProductToBasket("Bread"); + BigDecimal totalDiscount = shoppingBasketService.getTotalDiscount(); + BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay( ); + assertEquals(BigDecimal.valueOf(3.55), cuurTotal); + assertEquals(BigDecimal.valueOf(0.40).setScale(2), totalDiscount); + assertEquals(BigDecimal.valueOf(3.15), totalPriceToPay); + } + + +} diff --git a/src/test/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOfferTest.java b/src/test/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOfferTest.java index dfa686cc..766a696f 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOfferTest.java +++ b/src/test/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOfferTest.java @@ -1,16 +1,22 @@ package com.industriallogic.henrysgroceries.offers; +import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; +import com.industriallogic.henrysgroceries.model.MeasurementUnit; +import com.industriallogic.henrysgroceries.model.Product; import com.industriallogic.henrysgroceries.model.ShoppingBasket; import com.industriallogic.henrysgroceries.provider.ProductProvider; -import com.industriallogic.henrysgroceries.testutil.TestMockUtil; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.HashMap; +import java.util.Map; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.Silent.class) @@ -22,17 +28,36 @@ public class ComboDiscountOfferTest { @Mock private ProductProvider productProvider; + @Mock + private static ShoppingBasket shoppingBasket; + @Before + public void setUP() throws ProductNotFoundException { + when(productProvider.getProduct("Bread")).thenReturn(new Product("B123", "Bread", BigDecimal.valueOf(.80), MeasurementUnit.LOAF)); + when(productProvider.getProduct("Soup")).thenReturn(new Product("S123", "Soup", BigDecimal.valueOf(.65), MeasurementUnit.TIN)); + } + @Test - public void getDiscountOn2SoupsAnd1Bread() { - ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup", "Bread"); - when(comboDiscountOffer.getDiscount(basket)).thenReturn(new BigDecimal(.40)); - assertEquals(new BigDecimal(.40), comboDiscountOffer.getDiscount(basket)); + public void getDiscountOn2SoupsAnd1Bread() throws ProductNotFoundException { + when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now()); + + Map productMap = new HashMap() {{ + put(productProvider.getProduct("Soup"), 2); + put(productProvider.getProduct("Bread"), 1); + }}; + when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); + when(comboDiscountOffer.getDiscount(shoppingBasket)).thenReturn(new BigDecimal(.40)); + assertEquals(new BigDecimal(.40), comboDiscountOffer.getDiscount(shoppingBasket)); } @Test - public void getDiscountOn2SoupsAnd2Bread() { - ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup", "Bread","Bread"); - when(comboDiscountOffer.getDiscount(basket)).thenReturn(new BigDecimal(.40)); - assertEquals(new BigDecimal(.40), comboDiscountOffer.getDiscount(basket)); + public void getDiscountOn2SoupsAnd2Bread() throws ProductNotFoundException { + when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now()); + + Map productMap = new HashMap() {{ + put(productProvider.getProduct("Soup"), 2); + put(productProvider.getProduct("Bread"), 2); + }}; + when(comboDiscountOffer.getDiscount(shoppingBasket)).thenReturn(new BigDecimal(.40)); + assertEquals(new BigDecimal(.40), comboDiscountOffer.getDiscount(shoppingBasket)); } } \ No newline at end of file diff --git a/src/test/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOfferTest.java b/src/test/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOfferTest.java index a1461214..be1c4ea6 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOfferTest.java +++ b/src/test/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOfferTest.java @@ -1,8 +1,11 @@ package com.industriallogic.henrysgroceries.offers; +import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; +import com.industriallogic.henrysgroceries.model.MeasurementUnit; +import com.industriallogic.henrysgroceries.model.Product; import com.industriallogic.henrysgroceries.model.ShoppingBasket; import com.industriallogic.henrysgroceries.provider.ProductProvider; -import com.industriallogic.henrysgroceries.testutil.TestMockUtil; +import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.junit.runner.RunWith; @@ -11,6 +14,8 @@ import java.math.BigDecimal; import java.time.LocalDate; +import java.util.HashMap; +import java.util.Map; import static org.mockito.Mockito.when; @@ -23,18 +28,36 @@ public class PercentageDiscountOfferTest { @Mock private ProductProvider productProvider; + @Mock + private static ShoppingBasket shoppingBasket; + @Before + public void setUP() throws ProductNotFoundException { + when(productProvider.getProduct("Apples")).thenReturn(new Product("A123", "Apple", BigDecimal.valueOf(.10), MeasurementUnit.SINGLE)); + } + + @Test - public void getDiscountOn3ApplesBoughtToday() { - ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Apples", "Apples", "Apples"); - when(percentDiscountOffer.getDiscount(basket)).thenReturn(new BigDecimal(.00)); - Assertions.assertEquals(new BigDecimal(.00), percentDiscountOffer.getDiscount(basket)); + public void getDiscountOn3ApplesBoughtToday() throws ProductNotFoundException { + when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now()); + + Map productMap = new HashMap() {{ + put(productProvider.getProduct("Apples"), 3); + }}; + when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); + + when(percentDiscountOffer.getDiscount(shoppingBasket)).thenReturn(new BigDecimal(.00)); + Assertions.assertEquals(new BigDecimal(.00), percentDiscountOffer.getDiscount(shoppingBasket)); } @Test - public void getDiscountOn3ApplesBoughtIn5Days() { - ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Apples", "Apples", "Apples"); - basket.setShoppingDate(LocalDate.now().plusDays(5)); - when(percentDiscountOffer.getDiscount(basket)).thenReturn(new BigDecimal(.03)); - Assertions.assertEquals(new BigDecimal(.03), percentDiscountOffer.getDiscount(basket)); + public void getDiscountOn3ApplesBoughtIn5Days() throws ProductNotFoundException { + when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now().plusDays(5)); + + Map productMap = new HashMap() {{ + put(productProvider.getProduct("Apples"), 3); + }}; + when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); + when(percentDiscountOffer.getDiscount(shoppingBasket)).thenReturn(new BigDecimal(.03)); + Assertions.assertEquals(new BigDecimal(.03), percentDiscountOffer.getDiscount(shoppingBasket)); } } \ No newline at end of file diff --git a/src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java b/src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java deleted file mode 100644 index d8bb0ddd..00000000 --- a/src/test/java/com/industriallogic/henrysgroceries/service/PriceShoppingBasketServiceTest.java +++ /dev/null @@ -1,164 +0,0 @@ -package com.industriallogic.henrysgroceries.service; - -import com.industriallogic.henrysgroceries.model.ShoppingBasket; -import com.industriallogic.henrysgroceries.provider.OffersProvider; -import com.industriallogic.henrysgroceries.provider.ProductProvider; -import com.industriallogic.henrysgroceries.testutil.TestMockUtil; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import java.math.BigDecimal; -import java.time.LocalDate; -import java.time.temporal.TemporalAdjusters; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class PriceShoppingBasketServiceTest { - - @InjectMocks - private PriceShoppingBasketService pricingService; - - @Mock - private OffersProvider OffersProvider; - - @Mock - private static ProductProvider productProvider; - - @Test - public void price1SoupAnd1BreadNoOfferAppliedTest() { - ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Bread"); - when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(1.45), bigDecimal); - } - - @Test - public void price2SoupAnd1BreadComboOfferAppliedTest() { - ShoppingBasket basket =TestMockUtil.getBasket(productProvider, "Soup", "Soup","Bread"); - when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(1.70).setScale(2), bigDecimal); - } - - @Test - public void price2SoupAnd1BreadBoughtYesterdayComboOfferAppliedTest() { - ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup","Bread"); - basket.setShoppingDate(LocalDate.now().minusDays(1)); - when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(1.70).setScale(2), bigDecimal); - } - - @Test - public void price2SoupAnd1BreadBought2DaysAgoNoOfferAppliedTest() { - ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup","Bread"); - basket.setShoppingDate(LocalDate.now().minusDays(2)); - when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(2.10).setScale(2), bigDecimal); - } - - @Test - public void price2SoupAnd1BreadBoughtIn6DaysComboOfferAppliedTest() { - ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup","Bread"); - basket.setShoppingDate(LocalDate.now().plusDays(6)); - when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(1.70).setScale(2), bigDecimal); - } - - @Test - public void price2SoupAnd1BreadBoughtIn7DaysNoOfferAppliedTest() { - ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup","Bread"); - basket.setShoppingDate(LocalDate.now().plusDays(7)); - when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(2.10).setScale(2), bigDecimal); - } - - @Test - public void price3SoupAnd2BreadComboOfferAppliedOn1BreadTest() { - ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup", "Soup", "Bread", "Bread"); - when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(3.15), bigDecimal); - } - - @Test - public void price4SoupAnd3BreadComboOfferAppliedOn2BreadTest() { - ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup", "Soup", "Soup", "Bread", "Bread", "Bread"); - when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(4.20).setScale(2), bigDecimal); - } - - @Test - public void price4SoupAnd1BreadComboOfferAppliedOn1BreadTest() { - ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Soup", "Soup", "Soup", "Soup", "Bread"); - when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(3.00).setScale(2), bigDecimal); - } - - @Test - public void price6ApplesAndMilkBoughtTodayNoOfferApplied() { - ShoppingBasket basket =TestMockUtil.getBasket(productProvider, "Apples", "Apples", "Apples", "Apples", "Apples", "Apples", "Milk"); - - when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(1.90).setScale(2), bigDecimal); - } - - @Test - public void price6ApplesAndMilkBoughtIn5DaysPercentOfferApplied() { - ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Apples", "Apples", "Apples", "Apples", "Apples", "Apples", "Milk"); - basket.setShoppingDate(LocalDate.now().plusDays(5)); - when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(1.84), bigDecimal); - } - - @Test - public void price6ApplesBoughtAfterEndOfFollowingMonthOfferApplied() { - ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Apples", "Apples", "Apples", "Apples", "Apples", "Apples" ); - LocalDate firstOfNextMonth = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth()); - LocalDate endOfNextMonth = firstOfNextMonth.with(TemporalAdjusters.lastDayOfMonth()); - basket.setShoppingDate(endOfNextMonth.plusDays(1)); - when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(.60).setScale(2), bigDecimal); - } - - @Test - public void price3ApplesAnd2SoupBoughtIn5DaysPercentOfferApplied() { - ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Apples", "Apples", "Apples", "Soup", "Soup"); - - basket.setShoppingDate(LocalDate.now().plusDays(5)); - when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(1.57), bigDecimal); - } - - @Test - public void price3ApplesAnd2SoupAnd1BreadBoughtIn5DaysComboAndPercentOfferApplied() { - ShoppingBasket basket = TestMockUtil.getBasket(productProvider, "Apples", "Apples", "Apples", "Soup", "Soup", "Bread"); - - basket.setShoppingDate(LocalDate.now().plusDays(5)); - when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - - BigDecimal bigDecimal = pricingService.totalPriceToPay(basket); - assertEquals(BigDecimal.valueOf(1.97), bigDecimal); - } -} diff --git a/src/test/java/com/industriallogic/henrysgroceries/service/ShoppingBasketServiceTest.java b/src/test/java/com/industriallogic/henrysgroceries/service/ShoppingBasketServiceTest.java new file mode 100644 index 00000000..5c58b29e --- /dev/null +++ b/src/test/java/com/industriallogic/henrysgroceries/service/ShoppingBasketServiceTest.java @@ -0,0 +1,243 @@ +package com.industriallogic.henrysgroceries.service; + +import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; +import com.industriallogic.henrysgroceries.model.MeasurementUnit; +import com.industriallogic.henrysgroceries.model.Product; +import com.industriallogic.henrysgroceries.model.ShoppingBasket; +import com.industriallogic.henrysgroceries.provider.OffersProvider; +import com.industriallogic.henrysgroceries.provider.ProductProvider; +import com.industriallogic.henrysgroceries.testutil.TestMockUtil; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.temporal.TemporalAdjusters; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.Silent.class) +public class ShoppingBasketServiceTest { + + @InjectMocks + private ShoppingBasketServiceImpl shoppingBasketService; + + @Mock + private OffersProvider OffersProvider; + + @Mock + private static ProductProvider productProvider; + + @Mock + private static ShoppingBasket shoppingBasket; + + @Before + public void setUP() throws ProductNotFoundException { + when(productProvider.getProduct("Apples")).thenReturn(new Product("A123", "Apple", BigDecimal.valueOf(.10), MeasurementUnit.SINGLE)); + when(productProvider.getProduct("Bread")).thenReturn(new Product("B123", "Bread", BigDecimal.valueOf(.80), MeasurementUnit.LOAF)); + when(productProvider.getProduct("Milk")).thenReturn(new Product("M123", "Milk", BigDecimal.valueOf(1.30), MeasurementUnit.BOTTLE)); + when(productProvider.getProduct("Soup")).thenReturn(new Product("S123", "Soup", BigDecimal.valueOf(.65), MeasurementUnit.TIN)); + + when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); + } + + @Test + public void add2ApplesToBasketTest() throws ProductNotFoundException { + when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now()); + when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(0.65)); + when(shoppingBasketService.addProductToBasket("Bread")).thenReturn(BigDecimal.valueOf(1.45)); + when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(1.45)); + assertEquals(BigDecimal.valueOf(1.45), shoppingBasketService.totalPriceToPay()); + } + + @Test + public void price1SoupAnd1BreadNoOfferAppliedTest() throws ProductNotFoundException { + when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now()); + when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(0.65)); + when(shoppingBasketService.addProductToBasket("Bread")).thenReturn(BigDecimal.valueOf(1.45)); + when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(1.45)); + BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); + assertEquals(BigDecimal.valueOf(1.45), totalPriceToPay); + } + + @Test + public void price2SoupAnd1BreadComboOfferAppliedTest() throws ProductNotFoundException { + when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now()); + when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(0.65)); + when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(1.30)); + when(shoppingBasketService.addProductToBasket("Bread")).thenReturn(BigDecimal.valueOf(2.10)); + Map productMap = new HashMap() {{ + put(productProvider.getProduct("Soup"), 2); + put(productProvider.getProduct("Bread"), 1); + }}; + when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); + when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(2.10)); + BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); + assertEquals(BigDecimal.valueOf(1.70).setScale(2), totalPriceToPay); + } + + @Test + public void price2SoupAnd1BreadBoughtYesterdayComboOfferAppliedTest() throws ProductNotFoundException { + when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now().minusDays(1)); + when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(0.65)); + when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(1.30)); + when(shoppingBasketService.addProductToBasket("Bread")).thenReturn(BigDecimal.valueOf(2.10)); + Map productMap = new HashMap() {{ + put(productProvider.getProduct("Soup"), 2); + put(productProvider.getProduct("Bread"), 1); + }}; + when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); + when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(2.10)); + BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); + assertEquals(BigDecimal.valueOf(1.70).setScale(2), totalPriceToPay); + } + + @Test + public void price2SoupAnd1BreadBoughtIn6DaysComboOfferAppliedTest() throws ProductNotFoundException{ + when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now().plusDays(6)); + + when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(0.65)); + when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(1.30)); + when(shoppingBasketService.addProductToBasket("Bread")).thenReturn(BigDecimal.valueOf(2.10)); + Map productMap = new HashMap() {{ + put(productProvider.getProduct("Soup"), 2); + put(productProvider.getProduct("Bread"), 1); + }}; + when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); + when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(2.10)); + BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); + assertEquals(BigDecimal.valueOf(1.70).setScale(2), totalPriceToPay); + } + + @Test + public void price2SoupAnd1BreadBoughtIn7DaysNoOfferAppliedTest() throws ProductNotFoundException{ + when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now().plusDays(7)); + + when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(0.65)); + when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(1.30)); + when(shoppingBasketService.addProductToBasket("Bread")).thenReturn(BigDecimal.valueOf(2.10)); + Map productMap = new HashMap() {{ + put(productProvider.getProduct("Soup"), 2); + put(productProvider.getProduct("Bread"), 1); + }}; + when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); + when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(2.10)); + BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); + assertEquals(BigDecimal.valueOf(2.10).setScale(2), totalPriceToPay); + } + + @Test + public void price3SoupAnd2BreadComboOfferAppliedOn1BreadTest() throws ProductNotFoundException{ + when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now()); + + when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(0.65)); + when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(1.30)); + when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(1.95)); + when(shoppingBasketService.addProductToBasket("Bread")).thenReturn(BigDecimal.valueOf(2.75)); + when(shoppingBasketService.addProductToBasket("Bread")).thenReturn(BigDecimal.valueOf(3.55)); + Map productMap = new HashMap() {{ + put(productProvider.getProduct("Soup"), 3); + put(productProvider.getProduct("Bread"), 2); + }}; + when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); + when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(3.55)); + BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); + assertEquals(BigDecimal.valueOf(3.15), totalPriceToPay); + } + + + @Test + public void price6ApplesAndMilkBoughtTodayNoOfferApplied() throws ProductNotFoundException { + when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now()); + + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.10)); + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.20)); + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.30)); + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.40)); + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.50)); + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.60)); + when(shoppingBasketService.addProductToBasket("Milk")).thenReturn(BigDecimal.valueOf(1.90)); + Map productMap = new HashMap() {{ + put(productProvider.getProduct("Apples"), 6); + put(productProvider.getProduct("Milk"), 1); + }}; + when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); + when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(1.90)); + BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); + assertEquals(BigDecimal.valueOf(1.90).setScale(2), totalPriceToPay); + } + + @Test + public void price6ApplesAndMilkBoughtIn5DaysPercentOfferApplied() throws ProductNotFoundException { + when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now().plusDays(5)); + + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.10)); + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.20)); + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.30)); + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.40)); + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.50)); + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.60)); + when(shoppingBasketService.addProductToBasket("Milk")).thenReturn(BigDecimal.valueOf(1.90)); + Map productMap = new HashMap() {{ + put(productProvider.getProduct("Apples"), 6); + put(productProvider.getProduct("Milk"), 1); + }}; + when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); + when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(1.90)); + BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); + assertEquals(BigDecimal.valueOf(1.84).setScale(2), totalPriceToPay); + } + + @Test + public void price6ApplesBoughtAfterEndOfFollowingMonthOfferApplied() throws ProductNotFoundException { + LocalDate firstOfNextMonth = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth()); + LocalDate endOfNextMonth = firstOfNextMonth.with(TemporalAdjusters.lastDayOfMonth()); + when(shoppingBasket.getShoppingDate()).thenReturn(endOfNextMonth.plusDays(1)); + + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.10)); + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.20)); + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.30)); + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.40)); + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.50)); + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.60)); + + Map productMap = new HashMap() {{ + put(productProvider.getProduct("Apples"), 6); + put(productProvider.getProduct("Milk"), 1); + }}; + when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); + when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(0.60)); + BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); + assertEquals(BigDecimal.valueOf(.60).setScale(2), totalPriceToPay); + } + + + @Test + public void price3ApplesAnd2SoupAnd1BreadBoughtIn5DaysComboAndPercentOfferApplied() throws ProductNotFoundException { + when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now().plusDays(5)); + + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.10)); + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.20)); + when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.30)); + when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(0.95)); + when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(1.60)); + when(shoppingBasketService.addProductToBasket("Bread")).thenReturn(BigDecimal.valueOf(2.40)); + + Map productMap = new HashMap() {{ + put(productProvider.getProduct("Apples"), 3); + put(productProvider.getProduct("Soup"), 2); + put(productProvider.getProduct("Bread"), 1); + }}; + when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); + when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(2.40)); + BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); + assertEquals(BigDecimal.valueOf(1.97), totalPriceToPay); + } +} diff --git a/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java b/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java index 4b21960b..fdebd2a6 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java +++ b/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java @@ -2,7 +2,6 @@ import com.industriallogic.henrysgroceries.model.MeasurementUnit; import com.industriallogic.henrysgroceries.model.Product; -import com.industriallogic.henrysgroceries.model.ShoppingBasket; import com.industriallogic.henrysgroceries.offers.ComboDiscountOffer; import com.industriallogic.henrysgroceries.offers.Offer; import com.industriallogic.henrysgroceries.offers.PercentageDiscountOffer; @@ -14,10 +13,17 @@ import java.time.temporal.TemporalAdjusters; import java.util.*; -import static org.mockito.Mockito.when; - public class TestMockUtil { + + @SneakyThrows + public static Map get2Soup1BreadProductMap(ProductProvider productProvider ) { + return new HashMap() {{ + put(productProvider.getProduct("Soup"), 2); + put(productProvider.getProduct("Bread"), 1); + }}; + } + public static List getOffersList() { Product apples = new Product("A123", "Apple", BigDecimal.valueOf(.10), MeasurementUnit.SINGLE); Product bread = new Product("B123", "Bread", BigDecimal.valueOf(.80), MeasurementUnit.LOAF); @@ -39,16 +45,4 @@ private static Map productList() { return products; } - @SneakyThrows - public static ShoppingBasket getBasket(ProductProvider productProvider, String... productNames) { - when(productProvider.getProduct("Apples")).thenReturn(productList().get("APPLES")); - when(productProvider.getProduct("Bread")).thenReturn(productList().get("BREAD")); - when(productProvider.getProduct("Milk")).thenReturn(productList().get("MILK")); - when(productProvider.getProduct("Soup")).thenReturn(productList().get("SOUP")); - ShoppingBasket basket = new ShoppingBasket(); - for (String productName : productNames) { - basket.addProductToBasket(productProvider.getProduct(productName)); - } - return basket; - } } From 4afdb4c159d62b14582087a1b3da160ec9ec7a73 Mon Sep 17 00:00:00 2001 From: Kush Donepudi Date: Thu, 2 Jan 2020 20:41:04 +0000 Subject: [PATCH 09/10] added few more tests --- .../henrysgroceries/model/ShoppingBasket.java | 4 -- .../service/ShoppingBasketServiceImpl.java | 2 +- .../ShoppingBasketServiceIntegrationTest.java | 45 ++++++++++++++++--- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java b/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java index e1ad9082..033bbc26 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java +++ b/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java @@ -27,10 +27,6 @@ public BigDecimal addProductToBasket(Product product) { return curTotalAmount; } - public void setShoppingDate(LocalDate date) { - this.shoppingDate = date; - } - public LocalDate getShoppingDate() { return this.shoppingDate; } diff --git a/src/main/java/com/industriallogic/henrysgroceries/service/ShoppingBasketServiceImpl.java b/src/main/java/com/industriallogic/henrysgroceries/service/ShoppingBasketServiceImpl.java index d53bc5dd..1132027e 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/service/ShoppingBasketServiceImpl.java +++ b/src/main/java/com/industriallogic/henrysgroceries/service/ShoppingBasketServiceImpl.java @@ -53,6 +53,6 @@ public BigDecimal getTotalDiscount() { */ @Override public BigDecimal addProductToBasket(String productName) throws ProductNotFoundException { - return basket.addProductToBasket(productProvider.getProduct(productName)); + return basket.addProductToBasket(productProvider.getProduct(productName)).setScale(2); } } diff --git a/src/test/java/com/industriallogic/henrysgroceries/integration/ShoppingBasketServiceIntegrationTest.java b/src/test/java/com/industriallogic/henrysgroceries/integration/ShoppingBasketServiceIntegrationTest.java index 829735e6..06e5d81e 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/integration/ShoppingBasketServiceIntegrationTest.java +++ b/src/test/java/com/industriallogic/henrysgroceries/integration/ShoppingBasketServiceIntegrationTest.java @@ -12,16 +12,14 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - import java.math.BigDecimal; - import static org.junit.Assert.assertEquals; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = PriceShoppingBasketApplication.class, initializers = ConfigFileApplicationContextInitializer.class) @Slf4j -@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class ShoppingBasketServiceIntegrationTest { @Autowired @@ -33,13 +31,50 @@ public void price3SoupAnd2BreadComboOfferAppliedOn1BreadTest() throws ProductN shoppingBasketService.addProductToBasket("Soup"); shoppingBasketService.addProductToBasket("Soup"); shoppingBasketService.addProductToBasket("Bread"); - BigDecimal cuurTotal = shoppingBasketService.addProductToBasket("Bread"); + BigDecimal currTotal = shoppingBasketService.addProductToBasket("Bread"); BigDecimal totalDiscount = shoppingBasketService.getTotalDiscount(); BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay( ); - assertEquals(BigDecimal.valueOf(3.55), cuurTotal); + assertEquals(BigDecimal.valueOf(3.55), currTotal); assertEquals(BigDecimal.valueOf(0.40).setScale(2), totalDiscount); assertEquals(BigDecimal.valueOf(3.15), totalPriceToPay); } + @Test + public void price6ApplesAndMilkBoughtTodayNoOfferAppliedTest() throws ProductNotFoundException { + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + BigDecimal currTotal = shoppingBasketService.addProductToBasket("Milk"); + BigDecimal totalDiscount = shoppingBasketService.getTotalDiscount(); + BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay( ); + assertEquals(BigDecimal.valueOf(1.90).setScale(2), currTotal); + assertEquals(BigDecimal.valueOf(0.00).setScale(2), totalDiscount); + assertEquals(BigDecimal.valueOf(1.90).setScale(2), totalPriceToPay); + } + @Test + public void price1SoupAnd1BreadNoOfferAppliedTest() throws ProductNotFoundException { + shoppingBasketService.addProductToBasket("Soup"); + BigDecimal currTotal = shoppingBasketService.addProductToBasket("Bread"); + BigDecimal totalDiscount = shoppingBasketService.getTotalDiscount(); + BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay( ); + assertEquals(BigDecimal.valueOf(1.45).setScale(2), currTotal); + assertEquals(BigDecimal.valueOf(0.00).setScale(2), totalDiscount); + assertEquals(BigDecimal.valueOf(1.45).setScale(2), totalPriceToPay); + } + + @Test + public void price2SoupAnd1BreadComboOfferAppliedTest() throws ProductNotFoundException { + shoppingBasketService.addProductToBasket("Soup"); + shoppingBasketService.addProductToBasket("Soup"); + BigDecimal currTotal = shoppingBasketService.addProductToBasket("Bread"); + BigDecimal totalDiscount = shoppingBasketService.getTotalDiscount(); + BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay( ); + assertEquals(BigDecimal.valueOf(2.10).setScale(2), currTotal); + assertEquals(BigDecimal.valueOf(0.40).setScale(2), totalDiscount); + assertEquals(BigDecimal.valueOf(1.70).setScale(2), totalPriceToPay); + } } From 7d07eb50b36bdaf6f286cc8b20cfe76a5e6e6b2a Mon Sep 17 00:00:00 2001 From: Kush Donepudi Date: Mon, 6 Jan 2020 11:25:36 +0000 Subject: [PATCH 10/10] Added couple more integration tests and tidying Mocks in unit tests --- .../PriceShoppingBasketApplication.java | 1 - .../henrysgroceries/model/ShoppingBasket.java | 4 + .../offers/ComboDiscountOffer.java | 2 +- .../ShoppingBasketServiceIntegrationTest.java | 59 +++++++- .../offers/ComboDiscountOfferTest.java | 10 +- .../offers/PercentageDiscountOfferTest.java | 14 +- .../provider/OffersProviderTest.java | 25 ++- .../provider/ProductProviderTest.java | 22 +-- .../service/ShoppingBasketServiceTest.java | 142 +++++++++--------- .../testutil/TestMockUtil.java | 27 +--- 10 files changed, 183 insertions(+), 123 deletions(-) diff --git a/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java b/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java index d08ebb2a..44e8cdab 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java +++ b/src/main/java/com/industriallogic/henrysgroceries/PriceShoppingBasketApplication.java @@ -1,7 +1,6 @@ package com.industriallogic.henrysgroceries; import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; -import com.industriallogic.henrysgroceries.model.ShoppingBasket; import com.industriallogic.henrysgroceries.service.ShoppingBasketServiceImpl; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; diff --git a/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java b/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java index 033bbc26..7812ecd9 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java +++ b/src/main/java/com/industriallogic/henrysgroceries/model/ShoppingBasket.java @@ -38,6 +38,10 @@ public Map getProductsInBasket() { public BigDecimal getCurTotalAmount() { return curTotalAmount; } + + public void setShoppingDate(LocalDate shoppingDate) { + this.shoppingDate = shoppingDate; + } } diff --git a/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java b/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java index 5269f531..7fbc094d 100644 --- a/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java +++ b/src/main/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOffer.java @@ -40,7 +40,7 @@ public BigDecimal getDiscount(ShoppingBasket basket) { if (qualifyingProdCountInBasket >= qualifyingProdMinQnty) { int purchaseQnty = basket.getProductsInBasket().getOrDefault(offerOnProduct, 0); int eligibleOfferCount = Math.min(qualifyingProdCountInBasket/qualifyingProdMinQnty, purchaseQnty); - return offerOnProduct.getPrice().multiply(discountFactor).divide(ONE_HUNDRED ).multiply( new BigDecimal(eligibleOfferCount)); + return offerOnProduct.getPrice().multiply(discountFactor).divide(ONE_HUNDRED ).multiply( new BigDecimal(eligibleOfferCount)).setScale(2); } } return BigDecimal.ZERO; diff --git a/src/test/java/com/industriallogic/henrysgroceries/integration/ShoppingBasketServiceIntegrationTest.java b/src/test/java/com/industriallogic/henrysgroceries/integration/ShoppingBasketServiceIntegrationTest.java index 06e5d81e..d59bfced 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/integration/ShoppingBasketServiceIntegrationTest.java +++ b/src/test/java/com/industriallogic/henrysgroceries/integration/ShoppingBasketServiceIntegrationTest.java @@ -1,29 +1,41 @@ package com.industriallogic.henrysgroceries.integration; -import com.industriallogic.henrysgroceries.PriceShoppingBasketApplication; import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; -import com.industriallogic.henrysgroceries.service.ShoppingBasketService; +import com.industriallogic.henrysgroceries.model.ShoppingBasket; +import com.industriallogic.henrysgroceries.provider.OffersProvider; +import com.industriallogic.henrysgroceries.provider.ProductProvider; +import com.industriallogic.henrysgroceries.service.ShoppingBasketServiceImpl; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.ConfigFileApplicationContextInitializer; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + import java.math.BigDecimal; +import java.time.LocalDate; + import static org.junit.Assert.assertEquals; @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = PriceShoppingBasketApplication.class, - initializers = ConfigFileApplicationContextInitializer.class) +@ContextConfiguration(classes = {ShoppingBasketServiceImpl.class, ProductProvider.class,OffersProvider.class, + ShoppingBasket.class} ) @Slf4j @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class ShoppingBasketServiceIntegrationTest { @Autowired - private ShoppingBasketService shoppingBasketService; + private ShoppingBasketServiceImpl shoppingBasketService; + + @Autowired + private ProductProvider productProvider; + @Autowired + private OffersProvider offersProvider; + @Autowired + private ShoppingBasket basket; + @Test public void price3SoupAnd2BreadComboOfferAppliedOn1BreadTest() throws ProductNotFoundException { @@ -77,4 +89,39 @@ public void price2SoupAnd1BreadComboOfferAppliedTest() throws ProductNotFoundExc assertEquals(BigDecimal.valueOf(0.40).setScale(2), totalDiscount); assertEquals(BigDecimal.valueOf(1.70).setScale(2), totalPriceToPay); } + + @Test + public void price6ApplesAnd1MilkBoughtIn5DaysTest() throws ProductNotFoundException { + basket.setShoppingDate(LocalDate.now().plusDays(5)); + ShoppingBasketServiceImpl shoppingService = new ShoppingBasketServiceImpl( productProvider, offersProvider, basket) ; + shoppingService.addProductToBasket("Apples"); + shoppingService.addProductToBasket("Apples"); + shoppingService.addProductToBasket("Apples"); + shoppingService.addProductToBasket("Apples"); + shoppingService.addProductToBasket("Apples"); + shoppingService.addProductToBasket("Apples"); + BigDecimal currTotal = shoppingService.addProductToBasket("Milk"); + BigDecimal totalDiscount = shoppingService.getTotalDiscount(); + BigDecimal totalPriceToPay = shoppingService.totalPriceToPay( ); + assertEquals(BigDecimal.valueOf(1.90).setScale(2), currTotal); + assertEquals(BigDecimal.valueOf(0.06).setScale(2), totalDiscount); + assertEquals(BigDecimal.valueOf(1.84).setScale(2), totalPriceToPay); + } + + @Test + public void price3Apples2SoupAnd1BreadBoughtIn5DaysTest() throws ProductNotFoundException { + basket.setShoppingDate(LocalDate.now().plusDays(5)); + ShoppingBasketServiceImpl shoppingService = new ShoppingBasketServiceImpl( productProvider, offersProvider, basket) ; + shoppingService.addProductToBasket("Apples"); + shoppingService.addProductToBasket("Apples"); + shoppingService.addProductToBasket("Apples"); + shoppingService.addProductToBasket("Soup"); + shoppingService.addProductToBasket("Soup"); + BigDecimal currTotal = shoppingService.addProductToBasket("Bread"); + BigDecimal totalDiscount = shoppingService.getTotalDiscount(); + BigDecimal totalPriceToPay = shoppingService.totalPriceToPay( ); + assertEquals(BigDecimal.valueOf(2.40).setScale(2), currTotal); + assertEquals(BigDecimal.valueOf(0.43).setScale(2), totalDiscount); + assertEquals(BigDecimal.valueOf(1.97).setScale(2), totalPriceToPay); + } } diff --git a/src/test/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOfferTest.java b/src/test/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOfferTest.java index 766a696f..f1ff231b 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOfferTest.java +++ b/src/test/java/com/industriallogic/henrysgroceries/offers/ComboDiscountOfferTest.java @@ -22,7 +22,6 @@ @RunWith(MockitoJUnitRunner.Silent.class) public class ComboDiscountOfferTest { - @Mock private ComboDiscountOffer comboDiscountOffer; @Mock @@ -34,6 +33,8 @@ public class ComboDiscountOfferTest { public void setUP() throws ProductNotFoundException { when(productProvider.getProduct("Bread")).thenReturn(new Product("B123", "Bread", BigDecimal.valueOf(.80), MeasurementUnit.LOAF)); when(productProvider.getProduct("Soup")).thenReturn(new Product("S123", "Soup", BigDecimal.valueOf(.65), MeasurementUnit.TIN)); + + comboDiscountOffer = new ComboDiscountOffer("COMBO OFFER", productProvider.getProduct("Soup"), 2, productProvider.getProduct("Bread"), BigDecimal.valueOf(50), LocalDate.now().minusDays(1), LocalDate.now().plusDays(6)); } @Test @@ -45,8 +46,7 @@ public void getDiscountOn2SoupsAnd1Bread() throws ProductNotFoundException { put(productProvider.getProduct("Bread"), 1); }}; when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); - when(comboDiscountOffer.getDiscount(shoppingBasket)).thenReturn(new BigDecimal(.40)); - assertEquals(new BigDecimal(.40), comboDiscountOffer.getDiscount(shoppingBasket)); + assertEquals( BigDecimal.valueOf(.40).setScale(2), comboDiscountOffer.getDiscount(shoppingBasket)); } @Test @@ -57,7 +57,7 @@ public void getDiscountOn2SoupsAnd2Bread() throws ProductNotFoundException { put(productProvider.getProduct("Soup"), 2); put(productProvider.getProduct("Bread"), 2); }}; - when(comboDiscountOffer.getDiscount(shoppingBasket)).thenReturn(new BigDecimal(.40)); - assertEquals(new BigDecimal(.40), comboDiscountOffer.getDiscount(shoppingBasket)); + when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); + assertEquals( BigDecimal.valueOf(.40).setScale(2), comboDiscountOffer.getDiscount(shoppingBasket)); } } \ No newline at end of file diff --git a/src/test/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOfferTest.java b/src/test/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOfferTest.java index be1c4ea6..ff40b0c6 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOfferTest.java +++ b/src/test/java/com/industriallogic/henrysgroceries/offers/PercentageDiscountOfferTest.java @@ -14,6 +14,7 @@ import java.math.BigDecimal; import java.time.LocalDate; +import java.time.temporal.TemporalAdjusters; import java.util.HashMap; import java.util.Map; @@ -22,7 +23,6 @@ @RunWith(MockitoJUnitRunner.Silent.class) public class PercentageDiscountOfferTest { - @Mock private PercentageDiscountOffer percentDiscountOffer; @Mock @@ -30,9 +30,14 @@ public class PercentageDiscountOfferTest { @Mock private static ShoppingBasket shoppingBasket; + @Before public void setUP() throws ProductNotFoundException { when(productProvider.getProduct("Apples")).thenReturn(new Product("A123", "Apple", BigDecimal.valueOf(.10), MeasurementUnit.SINGLE)); + + LocalDate firstOfNextMonth = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth()); + LocalDate endOfNextMonth = firstOfNextMonth.with(TemporalAdjusters.lastDayOfMonth()); + percentDiscountOffer = new PercentageDiscountOffer("Apple 10% OFF", productProvider.getProduct("Apples"), BigDecimal.valueOf(10), LocalDate.now().plusDays(3), endOfNextMonth); } @@ -44,9 +49,7 @@ public void getDiscountOn3ApplesBoughtToday() throws ProductNotFoundException { put(productProvider.getProduct("Apples"), 3); }}; when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); - - when(percentDiscountOffer.getDiscount(shoppingBasket)).thenReturn(new BigDecimal(.00)); - Assertions.assertEquals(new BigDecimal(.00), percentDiscountOffer.getDiscount(shoppingBasket)); + Assertions.assertEquals( BigDecimal.valueOf(0), percentDiscountOffer.getDiscount(shoppingBasket)); } @Test @@ -57,7 +60,6 @@ public void getDiscountOn3ApplesBoughtIn5Days() throws ProductNotFoundException put(productProvider.getProduct("Apples"), 3); }}; when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); - when(percentDiscountOffer.getDiscount(shoppingBasket)).thenReturn(new BigDecimal(.03)); - Assertions.assertEquals(new BigDecimal(.03), percentDiscountOffer.getDiscount(shoppingBasket)); + Assertions.assertEquals( BigDecimal.valueOf(.03).setScale(2), percentDiscountOffer.getDiscount(shoppingBasket)); } } \ No newline at end of file diff --git a/src/test/java/com/industriallogic/henrysgroceries/provider/OffersProviderTest.java b/src/test/java/com/industriallogic/henrysgroceries/provider/OffersProviderTest.java index 464b03c4..b6b14085 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/provider/OffersProviderTest.java +++ b/src/test/java/com/industriallogic/henrysgroceries/provider/OffersProviderTest.java @@ -1,24 +1,41 @@ package com.industriallogic.henrysgroceries.provider; +import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; +import com.industriallogic.henrysgroceries.model.MeasurementUnit; +import com.industriallogic.henrysgroceries.model.Product; import com.industriallogic.henrysgroceries.testutil.TestMockUtil; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import java.math.BigDecimal; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class OffersProviderTest { + @InjectMocks + private OffersProvider offersProvider; + @Mock - OffersProvider offersProvider; + private ProductProvider productProvider; + + @Before + public void prepare() throws ProductNotFoundException { + when(productProvider.getProduct("Bread")).thenReturn(new Product("B123", "Bread", BigDecimal.valueOf(.80), MeasurementUnit.LOAF)); + when(productProvider.getProduct("Soup")).thenReturn(new Product("S123", "Soup", BigDecimal.valueOf(.65), MeasurementUnit.TIN)); + when(productProvider.getProduct("Apples")).thenReturn(new Product("A123", "Apple", BigDecimal.valueOf(.10), MeasurementUnit.SINGLE)); + offersProvider.init(); + } @Test - public void getOffers() { - when(offersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); - assertEquals(offersProvider.getOffers(), TestMockUtil.getOffersList()); + public void getOffers() { + assertEquals(TestMockUtil.getOffersList(), offersProvider.getOffers()); } } \ No newline at end of file diff --git a/src/test/java/com/industriallogic/henrysgroceries/provider/ProductProviderTest.java b/src/test/java/com/industriallogic/henrysgroceries/provider/ProductProviderTest.java index 2b74c2f3..a7d17511 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/provider/ProductProviderTest.java +++ b/src/test/java/com/industriallogic/henrysgroceries/provider/ProductProviderTest.java @@ -3,38 +3,40 @@ import com.industriallogic.henrysgroceries.exception.ProductNotFoundException; import com.industriallogic.henrysgroceries.model.MeasurementUnit; import com.industriallogic.henrysgroceries.model.Product; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.math.BigDecimal; - import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = ProductProvider.class) public class ProductProviderTest { - @Mock + @Autowired private ProductProvider productProvider; + @Before + public void prepare() { + productProvider.init(); + } + @Test public void getProduct() throws ProductNotFoundException { Product apple = new Product("A123", "Apple", BigDecimal.valueOf(.10), MeasurementUnit.SINGLE); - when(productProvider.getProduct("Apples")).thenReturn(apple); assertEquals(productProvider.getProduct("Apples"), apple); } @Test(expected=ProductNotFoundException.class) public void getProductWithSpace() throws ProductNotFoundException { - when(productProvider.getProduct(" ")).thenThrow(new ProductNotFoundException("Product Not found")); productProvider.getProduct(" "); } @Test(expected=ProductNotFoundException.class) public void getInvalidProduct() throws ProductNotFoundException { - when(productProvider.getProduct("chocolate")).thenThrow(new ProductNotFoundException("Product Not found")); productProvider.getProduct("chocolate"); } } \ No newline at end of file diff --git a/src/test/java/com/industriallogic/henrysgroceries/service/ShoppingBasketServiceTest.java b/src/test/java/com/industriallogic/henrysgroceries/service/ShoppingBasketServiceTest.java index 5c58b29e..4ac4fdac 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/service/ShoppingBasketServiceTest.java +++ b/src/test/java/com/industriallogic/henrysgroceries/service/ShoppingBasketServiceTest.java @@ -46,23 +46,28 @@ public void setUP() throws ProductNotFoundException { when(productProvider.getProduct("Soup")).thenReturn(new Product("S123", "Soup", BigDecimal.valueOf(.65), MeasurementUnit.TIN)); when(OffersProvider.getOffers()).thenReturn(TestMockUtil.getOffersList()); + + when(shoppingBasket.addProductToBasket(productProvider.getProduct("Apples"))).thenReturn(BigDecimal.valueOf(0.10)); + when(shoppingBasket.addProductToBasket(productProvider.getProduct("Soup"))).thenReturn(BigDecimal.valueOf(0.65)); + when(shoppingBasket.addProductToBasket(productProvider.getProduct("Bread"))).thenReturn(BigDecimal.valueOf(0.80)); + when(shoppingBasket.addProductToBasket(productProvider.getProduct("Milk"))).thenReturn(BigDecimal.valueOf(1.30)); } @Test public void add2ApplesToBasketTest() throws ProductNotFoundException { when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now()); - when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(0.65)); - when(shoppingBasketService.addProductToBasket("Bread")).thenReturn(BigDecimal.valueOf(1.45)); - when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(1.45)); - assertEquals(BigDecimal.valueOf(1.45), shoppingBasketService.totalPriceToPay()); + when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(0.20).setScale(2)); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + assertEquals(BigDecimal.valueOf(0.20).setScale(2), shoppingBasketService.totalPriceToPay()); } @Test public void price1SoupAnd1BreadNoOfferAppliedTest() throws ProductNotFoundException { when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now()); - when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(0.65)); - when(shoppingBasketService.addProductToBasket("Bread")).thenReturn(BigDecimal.valueOf(1.45)); when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(1.45)); + shoppingBasketService.addProductToBasket("Soup"); + shoppingBasketService.addProductToBasket("Bread"); BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); assertEquals(BigDecimal.valueOf(1.45), totalPriceToPay); } @@ -70,15 +75,17 @@ public void price1SoupAnd1BreadNoOfferAppliedTest() throws ProductNotFoundExcept @Test public void price2SoupAnd1BreadComboOfferAppliedTest() throws ProductNotFoundException { when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now()); - when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(0.65)); - when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(1.30)); - when(shoppingBasketService.addProductToBasket("Bread")).thenReturn(BigDecimal.valueOf(2.10)); - Map productMap = new HashMap() {{ + Map productMap = new HashMap() {{ put(productProvider.getProduct("Soup"), 2); put(productProvider.getProduct("Bread"), 1); }}; when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(2.10)); + + shoppingBasketService.addProductToBasket("Soup"); + shoppingBasketService.addProductToBasket("Soup"); + shoppingBasketService.addProductToBasket("Bread"); + BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); assertEquals(BigDecimal.valueOf(1.70).setScale(2), totalPriceToPay); } @@ -86,68 +93,71 @@ public void price2SoupAnd1BreadComboOfferAppliedTest() throws ProductNotFoundExc @Test public void price2SoupAnd1BreadBoughtYesterdayComboOfferAppliedTest() throws ProductNotFoundException { when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now().minusDays(1)); - when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(0.65)); - when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(1.30)); - when(shoppingBasketService.addProductToBasket("Bread")).thenReturn(BigDecimal.valueOf(2.10)); - Map productMap = new HashMap() {{ + Map productMap = new HashMap() {{ put(productProvider.getProduct("Soup"), 2); put(productProvider.getProduct("Bread"), 1); }}; when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(2.10)); + + shoppingBasketService.addProductToBasket("Soup"); + shoppingBasketService.addProductToBasket("Soup"); + shoppingBasketService.addProductToBasket("Bread"); + BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); assertEquals(BigDecimal.valueOf(1.70).setScale(2), totalPriceToPay); } @Test - public void price2SoupAnd1BreadBoughtIn6DaysComboOfferAppliedTest() throws ProductNotFoundException{ + public void price2SoupAnd1BreadBoughtIn6DaysComboOfferAppliedTest() throws ProductNotFoundException { when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now().plusDays(6)); - - when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(0.65)); - when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(1.30)); - when(shoppingBasketService.addProductToBasket("Bread")).thenReturn(BigDecimal.valueOf(2.10)); - Map productMap = new HashMap() {{ + Map productMap = new HashMap() {{ put(productProvider.getProduct("Soup"), 2); put(productProvider.getProduct("Bread"), 1); }}; when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(2.10)); + + shoppingBasketService.addProductToBasket("Soup"); + shoppingBasketService.addProductToBasket("Soup"); + shoppingBasketService.addProductToBasket("Bread"); BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); assertEquals(BigDecimal.valueOf(1.70).setScale(2), totalPriceToPay); } @Test - public void price2SoupAnd1BreadBoughtIn7DaysNoOfferAppliedTest() throws ProductNotFoundException{ + public void price2SoupAnd1BreadBoughtIn7DaysNoOfferAppliedTest() throws ProductNotFoundException { when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now().plusDays(7)); - - when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(0.65)); - when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(1.30)); - when(shoppingBasketService.addProductToBasket("Bread")).thenReturn(BigDecimal.valueOf(2.10)); - Map productMap = new HashMap() {{ + Map productMap = new HashMap() {{ put(productProvider.getProduct("Soup"), 2); put(productProvider.getProduct("Bread"), 1); }}; when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(2.10)); + + shoppingBasketService.addProductToBasket("Soup"); + shoppingBasketService.addProductToBasket("Soup"); + shoppingBasketService.addProductToBasket("Bread"); + BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); assertEquals(BigDecimal.valueOf(2.10).setScale(2), totalPriceToPay); } @Test - public void price3SoupAnd2BreadComboOfferAppliedOn1BreadTest() throws ProductNotFoundException{ + public void price3SoupAnd2BreadComboOfferAppliedOn1BreadTest() throws ProductNotFoundException { when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now()); - - when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(0.65)); - when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(1.30)); - when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(1.95)); - when(shoppingBasketService.addProductToBasket("Bread")).thenReturn(BigDecimal.valueOf(2.75)); - when(shoppingBasketService.addProductToBasket("Bread")).thenReturn(BigDecimal.valueOf(3.55)); - Map productMap = new HashMap() {{ + Map productMap = new HashMap() {{ put(productProvider.getProduct("Soup"), 3); put(productProvider.getProduct("Bread"), 2); }}; when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(3.55)); + + shoppingBasketService.addProductToBasket("Soup"); + shoppingBasketService.addProductToBasket("Soup"); + shoppingBasketService.addProductToBasket("Soup"); + shoppingBasketService.addProductToBasket("Bread"); + shoppingBasketService.addProductToBasket("Bread"); BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); assertEquals(BigDecimal.valueOf(3.15), totalPriceToPay); } @@ -156,20 +166,21 @@ public void price3SoupAnd2BreadComboOfferAppliedOn1BreadTest() throws ProductNo @Test public void price6ApplesAndMilkBoughtTodayNoOfferApplied() throws ProductNotFoundException { when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now()); - - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.10)); - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.20)); - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.30)); - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.40)); - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.50)); - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.60)); - when(shoppingBasketService.addProductToBasket("Milk")).thenReturn(BigDecimal.valueOf(1.90)); Map productMap = new HashMap() {{ put(productProvider.getProduct("Apples"), 6); put(productProvider.getProduct("Milk"), 1); }}; when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(1.90)); + + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Milk"); + BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); assertEquals(BigDecimal.valueOf(1.90).setScale(2), totalPriceToPay); } @@ -177,20 +188,20 @@ public void price6ApplesAndMilkBoughtTodayNoOfferApplied() throws ProductNotFoun @Test public void price6ApplesAndMilkBoughtIn5DaysPercentOfferApplied() throws ProductNotFoundException { when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now().plusDays(5)); - - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.10)); - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.20)); - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.30)); - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.40)); - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.50)); - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.60)); - when(shoppingBasketService.addProductToBasket("Milk")).thenReturn(BigDecimal.valueOf(1.90)); Map productMap = new HashMap() {{ put(productProvider.getProduct("Apples"), 6); put(productProvider.getProduct("Milk"), 1); }}; when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(1.90)); + + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Milk"); BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); assertEquals(BigDecimal.valueOf(1.84).setScale(2), totalPriceToPay); } @@ -200,20 +211,18 @@ public void price6ApplesBoughtAfterEndOfFollowingMonthOfferApplied() throws Prod LocalDate firstOfNextMonth = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth()); LocalDate endOfNextMonth = firstOfNextMonth.with(TemporalAdjusters.lastDayOfMonth()); when(shoppingBasket.getShoppingDate()).thenReturn(endOfNextMonth.plusDays(1)); - - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.10)); - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.20)); - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.30)); - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.40)); - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.50)); - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.60)); - Map productMap = new HashMap() {{ put(productProvider.getProduct("Apples"), 6); - put(productProvider.getProduct("Milk"), 1); }}; when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(0.60)); + + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); assertEquals(BigDecimal.valueOf(.60).setScale(2), totalPriceToPay); } @@ -222,14 +231,6 @@ public void price6ApplesBoughtAfterEndOfFollowingMonthOfferApplied() throws Prod @Test public void price3ApplesAnd2SoupAnd1BreadBoughtIn5DaysComboAndPercentOfferApplied() throws ProductNotFoundException { when(shoppingBasket.getShoppingDate()).thenReturn(LocalDate.now().plusDays(5)); - - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.10)); - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.20)); - when(shoppingBasketService.addProductToBasket("Apples")).thenReturn(BigDecimal.valueOf(0.30)); - when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(0.95)); - when(shoppingBasketService.addProductToBasket("Soup")).thenReturn(BigDecimal.valueOf(1.60)); - when(shoppingBasketService.addProductToBasket("Bread")).thenReturn(BigDecimal.valueOf(2.40)); - Map productMap = new HashMap() {{ put(productProvider.getProduct("Apples"), 3); put(productProvider.getProduct("Soup"), 2); @@ -237,6 +238,13 @@ public void price3ApplesAnd2SoupAnd1BreadBoughtIn5DaysComboAndPercentOfferApplie }}; when(shoppingBasket.getProductsInBasket()).thenReturn(productMap); when(shoppingBasket.getCurTotalAmount()).thenReturn(BigDecimal.valueOf(2.40)); + + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Apples"); + shoppingBasketService.addProductToBasket("Soup"); + shoppingBasketService.addProductToBasket("Soup"); + shoppingBasketService.addProductToBasket("Bread"); BigDecimal totalPriceToPay = shoppingBasketService.totalPriceToPay(); assertEquals(BigDecimal.valueOf(1.97), totalPriceToPay); } diff --git a/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java b/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java index fdebd2a6..95bd9f47 100644 --- a/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java +++ b/src/test/java/com/industriallogic/henrysgroceries/testutil/TestMockUtil.java @@ -5,25 +5,15 @@ import com.industriallogic.henrysgroceries.offers.ComboDiscountOffer; import com.industriallogic.henrysgroceries.offers.Offer; import com.industriallogic.henrysgroceries.offers.PercentageDiscountOffer; -import com.industriallogic.henrysgroceries.provider.ProductProvider; -import lombok.SneakyThrows; - import java.math.BigDecimal; import java.time.LocalDate; import java.time.temporal.TemporalAdjusters; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; public class TestMockUtil { - - @SneakyThrows - public static Map get2Soup1BreadProductMap(ProductProvider productProvider ) { - return new HashMap() {{ - put(productProvider.getProduct("Soup"), 2); - put(productProvider.getProduct("Bread"), 1); - }}; - } - public static List getOffersList() { Product apples = new Product("A123", "Apple", BigDecimal.valueOf(.10), MeasurementUnit.SINGLE); Product bread = new Product("B123", "Bread", BigDecimal.valueOf(.80), MeasurementUnit.LOAF); @@ -32,17 +22,8 @@ public static List getOffersList() { LocalDate firstOfNextMonth = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth()); LocalDate endOfNextMonth = firstOfNextMonth.with(TemporalAdjusters.lastDayOfMonth()); PercentageDiscountOffer applesOffer = new PercentageDiscountOffer("Apple 10% OFF", apples, BigDecimal.valueOf(10), LocalDate.now().plusDays(3), endOfNextMonth); - ComboDiscountOffer discountOffer = new ComboDiscountOffer("combo Offer", soup, 2, bread, BigDecimal.valueOf(50), LocalDate.now().minusDays(1), LocalDate.now().plusDays(6)); + ComboDiscountOffer discountOffer = new ComboDiscountOffer("COMBO OFFER", soup, 2, bread, BigDecimal.valueOf(50), LocalDate.now().minusDays(1), LocalDate.now().plusDays(6)); return Collections.unmodifiableList(Arrays.asList(applesOffer, discountOffer)); } - private static Map productList() { - Map products = new HashMap(); - products.put("APPLES", new Product("A123", "Apple", BigDecimal.valueOf(.10), MeasurementUnit.SINGLE)); - products.put("BREAD", new Product("B123", "Bread", BigDecimal.valueOf(.80), MeasurementUnit.LOAF)); - products.put("MILK", new Product("M123", "Milk", BigDecimal.valueOf(1.30), MeasurementUnit.BOTTLE)); - products.put("SOUP", new Product("S123", "Soup", BigDecimal.valueOf(.65), MeasurementUnit.TIN)); - return products; - } - }