diff --git a/src/main/java/fr/arolla/modec/entity/CartId.java b/src/main/java/fr/arolla/modec/entity/CartId.java index c93454c..4610f31 100644 --- a/src/main/java/fr/arolla/modec/entity/CartId.java +++ b/src/main/java/fr/arolla/modec/entity/CartId.java @@ -25,12 +25,11 @@ public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof CartId)) return false; CartId cartId = (CartId) o; - return id == cartId.id; + return Objects.equals(id, cartId.id); } @Override public int hashCode() { - return Objects.hash(id); } } diff --git a/src/main/java/fr/arolla/modec/entity/CartLine.java b/src/main/java/fr/arolla/modec/entity/CartLine.java index 33b4cb5..567af52 100644 --- a/src/main/java/fr/arolla/modec/entity/CartLine.java +++ b/src/main/java/fr/arolla/modec/entity/CartLine.java @@ -10,12 +10,12 @@ public class CartLine { @Embedded @Column(name = "productSku") - Sku productSku; + private Sku productSku; - String productName; + private String productName; @Embedded - Quantity quantity; + private Quantity quantity; public CartLine() { //for JPA } diff --git a/src/main/java/fr/arolla/modec/entity/DeliveryId.java b/src/main/java/fr/arolla/modec/entity/DeliveryId.java index f602129..550a86c 100644 --- a/src/main/java/fr/arolla/modec/entity/DeliveryId.java +++ b/src/main/java/fr/arolla/modec/entity/DeliveryId.java @@ -11,12 +11,10 @@ public DeliveryId() { //for JPA } public DeliveryId(Long id) { - this.id = id; } public Long getId() { - return id; } @@ -30,7 +28,6 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(id); } } diff --git a/src/main/java/fr/arolla/modec/entity/Order.java b/src/main/java/fr/arolla/modec/entity/Order.java index e18cb5c..a3f7515 100644 --- a/src/main/java/fr/arolla/modec/entity/Order.java +++ b/src/main/java/fr/arolla/modec/entity/Order.java @@ -1,7 +1,7 @@ package fr.arolla.modec.entity; import javax.persistence.*; -import java.util.Calendar; +import java.time.Instant; import java.util.List; @Entity @@ -18,7 +18,7 @@ public class Order { private Status status; - private Calendar creationDate; + private Instant creationDate; @Embedded private ShippingAddress shippingAddress; @@ -29,7 +29,7 @@ public class Order { public Order() { // for JPA } - public Order(List lines, Calendar creationDate, Recipient recipient, ShippingAddress shippingAddress) { + public Order(List lines, Instant creationDate, Recipient recipient, ShippingAddress shippingAddress) { this.lines = lines; this.creationDate = creationDate; this.recipient = recipient; @@ -56,7 +56,7 @@ public void setStatus(Status status) { this.status = status; } - public Calendar getCreationDate() { + public Instant getCreationDate() { return creationDate; } @@ -64,7 +64,7 @@ public enum Status { CREATED, IN_PREPARATION; public String toString() { - return super.toString().toLowerCase().replaceAll("_", " "); + return name().toLowerCase().replaceAll("_", " "); } } } diff --git a/src/main/java/fr/arolla/modec/entity/OrderId.java b/src/main/java/fr/arolla/modec/entity/OrderId.java index ab28264..7de400c 100644 --- a/src/main/java/fr/arolla/modec/entity/OrderId.java +++ b/src/main/java/fr/arolla/modec/entity/OrderId.java @@ -25,12 +25,11 @@ public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof OrderId)) return false; OrderId orderId = (OrderId) o; - return id == orderId.id; + return Objects.equals(id, orderId.id); } @Override public int hashCode() { - return Objects.hash(id); } diff --git a/src/main/java/fr/arolla/modec/entity/OrderLine.java b/src/main/java/fr/arolla/modec/entity/OrderLine.java index 20ce279..0786847 100644 --- a/src/main/java/fr/arolla/modec/entity/OrderLine.java +++ b/src/main/java/fr/arolla/modec/entity/OrderLine.java @@ -9,11 +9,11 @@ public class OrderLine { private Long id; @Embedded - Sku productSku; - String productName; + private Sku productSku; + private String productName; @Embedded - Quantity quantity; + private Quantity quantity; public OrderLine() { //for JPA } diff --git a/src/main/java/fr/arolla/modec/entity/Product.java b/src/main/java/fr/arolla/modec/entity/Product.java index 873a2cb..0a67dd7 100644 --- a/src/main/java/fr/arolla/modec/entity/Product.java +++ b/src/main/java/fr/arolla/modec/entity/Product.java @@ -19,6 +19,16 @@ public class Product { @Embedded private Weight weight; + public Product() { //for JPA + } + + public Product(Sku sku, String name, String description, Weight weight) { + this.sku = sku; + this.name = name; + this.description = description; + this.weight = weight; + } + public Sku getSku() { return sku; } @@ -35,13 +45,4 @@ public Weight getWeight() { return weight; } - public Product() { //for JPA - } - - public Product(Sku sku, String name, String description, Weight weight) { - this.sku = sku; - this.name = name; - this.description = description; - this.weight = weight; - } } diff --git a/src/main/java/fr/arolla/modec/entity/Quantity.java b/src/main/java/fr/arolla/modec/entity/Quantity.java index 49cb70d..a1d722d 100644 --- a/src/main/java/fr/arolla/modec/entity/Quantity.java +++ b/src/main/java/fr/arolla/modec/entity/Quantity.java @@ -4,11 +4,7 @@ @Embeddable public class Quantity { - long quantity; - - public long getQuantity() { - return quantity; - } + private long quantity; public Quantity() { //for JPA } @@ -18,6 +14,10 @@ public Quantity(long quantity) { this.quantity = quantity; } + public long getQuantity() { + return quantity; + } + @Override public String toString() { return Long.toString(quantity); diff --git a/src/main/java/fr/arolla/modec/entity/Recipient.java b/src/main/java/fr/arolla/modec/entity/Recipient.java index 05cdafb..c34d626 100644 --- a/src/main/java/fr/arolla/modec/entity/Recipient.java +++ b/src/main/java/fr/arolla/modec/entity/Recipient.java @@ -14,4 +14,12 @@ public Recipient(String recipientFullName, String email) { this.recipientFullName = recipientFullName; this.email = email; } + + public String getRecipientFullName() { + return recipientFullName; + } + + public String getEmail() { + return email; + } } diff --git a/src/main/java/fr/arolla/modec/entity/ShippingAddress.java b/src/main/java/fr/arolla/modec/entity/ShippingAddress.java index 41991b4..8eb59f2 100644 --- a/src/main/java/fr/arolla/modec/entity/ShippingAddress.java +++ b/src/main/java/fr/arolla/modec/entity/ShippingAddress.java @@ -13,6 +13,9 @@ public class ShippingAddress implements Serializable { private String zipCode; private String isoCountryCode; + public ShippingAddress() {//for JPA + } + public ShippingAddress(String fullName, String line1, String city, String zipCode, String isoCountryCode) { this.fullName = fullName; this.line1 = line1; @@ -21,9 +24,6 @@ public ShippingAddress(String fullName, String line1, String city, String zipCod this.isoCountryCode = isoCountryCode; } - public ShippingAddress() {//for JPA - } - public String getFullName() { return fullName; } @@ -58,7 +58,6 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(fullName, line1, city, zipCode, isoCountryCode); } } diff --git a/src/main/java/fr/arolla/modec/entity/Sku.java b/src/main/java/fr/arolla/modec/entity/Sku.java index 07c1228..5169e9c 100644 --- a/src/main/java/fr/arolla/modec/entity/Sku.java +++ b/src/main/java/fr/arolla/modec/entity/Sku.java @@ -20,17 +20,15 @@ public String getSku() { @Override public String toString() { - return "Sku{" + - "sku='" + sku + '\'' + - '}'; + return "Sku{'" + sku + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Sku)) return false; - Sku sku1 = (Sku) o; - return Objects.equals(sku, sku1.sku); + Sku oSku = (Sku) o; + return Objects.equals(this.sku, oSku.sku); } @Override diff --git a/src/main/java/fr/arolla/modec/entity/Weight.java b/src/main/java/fr/arolla/modec/entity/Weight.java index fffcbc4..06f87be 100644 --- a/src/main/java/fr/arolla/modec/entity/Weight.java +++ b/src/main/java/fr/arolla/modec/entity/Weight.java @@ -19,8 +19,6 @@ public double getWeight() { @Override public String toString() { - return "Weight{" + - "weight=" + weight + - '}'; + return "Weight{" + weight + '}'; } } diff --git a/src/main/java/fr/arolla/modec/BusinessException.java b/src/main/java/fr/arolla/modec/exception/BusinessException.java similarity index 93% rename from src/main/java/fr/arolla/modec/BusinessException.java rename to src/main/java/fr/arolla/modec/exception/BusinessException.java index 7df3540..3526297 100644 --- a/src/main/java/fr/arolla/modec/BusinessException.java +++ b/src/main/java/fr/arolla/modec/exception/BusinessException.java @@ -1,4 +1,4 @@ -package fr.arolla.modec; +package fr.arolla.modec.exception; public class BusinessException extends Exception { public BusinessException() { diff --git a/src/main/java/fr/arolla/modec/exception/CartNotFoundException.java b/src/main/java/fr/arolla/modec/exception/CartNotFoundException.java new file mode 100644 index 0000000..894b49c --- /dev/null +++ b/src/main/java/fr/arolla/modec/exception/CartNotFoundException.java @@ -0,0 +1,11 @@ +package fr.arolla.modec.exception; + +import fr.arolla.modec.entity.CartId; + +/** + */ +public class CartNotFoundException extends RuntimeException { + public CartNotFoundException(CartId cartId) { + super("cartId: " + cartId.getId()); + } +} diff --git a/src/main/java/fr/arolla/modec/exception/OrderNotFoundException.java b/src/main/java/fr/arolla/modec/exception/OrderNotFoundException.java new file mode 100644 index 0000000..3716cd7 --- /dev/null +++ b/src/main/java/fr/arolla/modec/exception/OrderNotFoundException.java @@ -0,0 +1,11 @@ +package fr.arolla.modec.exception; + +import fr.arolla.modec.entity.OrderId; + +/** + */ +public class OrderNotFoundException extends RuntimeException { + public OrderNotFoundException(OrderId orderId) { + super("orderId: " + orderId.getId()); + } +} diff --git a/src/main/java/fr/arolla/modec/service/CartService.java b/src/main/java/fr/arolla/modec/service/CartService.java index bff2158..4052035 100644 --- a/src/main/java/fr/arolla/modec/service/CartService.java +++ b/src/main/java/fr/arolla/modec/service/CartService.java @@ -1,14 +1,18 @@ package fr.arolla.modec.service; import fr.arolla.modec.entity.*; +import fr.arolla.modec.exception.CartNotFoundException; import fr.arolla.modec.repository.CartLineRepository; import fr.arolla.modec.repository.CartRepository; import fr.arolla.modec.repository.ProductRepository; import fr.arolla.modec.repository.ShippingServiceRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.List; +@Service public class CartService { private final CartRepository cartRepository; @@ -23,29 +27,34 @@ public CartService(CartRepository cartRepository, ProductRepository productRepos this.shippingServiceRepository = shippingServiceRepository; } + @Transactional public CartId createCart() { return cartRepository.save(new Cart()).getId(); } + @Transactional public void addToCart(CartId cartId, Sku sku, Quantity quantity) { Product product = productRepository.findOneBySku(sku); CartLine line = new CartLine(sku, product.getName(), quantity); cartLineRepository.save(line); - cartRepository.findById(cartId).get().getLines().add(line); + findOrFail(cartId).getLines().add(line); } + @Transactional public List getLines(CartId cartId) { - return cartRepository.findById(cartId).get().getLines(); + // return a copy to load content + return new ArrayList<>(findOrFail(cartId).getLines()); } - public void setShippingAddress(CartId cartId, String fullName, String line1, String city, String zipCode, String isoCountryCode) { - ShippingAddress address = new ShippingAddress(fullName, line1, city, zipCode, isoCountryCode); - cartRepository.findById(cartId).get().setShippingAddress(address); + @Transactional + public void setShippingAddress(CartId cartId, ShippingAddress shippingAddress) { + findOrFail(cartId).setShippingAddress(shippingAddress); } + @Transactional public List getShippingServices(CartId cartId) { List servicesFound = new ArrayList<>(); - Cart cart = cartRepository.findById(cartId).get(); + Cart cart = findOrFail(cartId); if (cart.getShippingAddress() != null && !cart.getLines().isEmpty()) { Sku firstProductSku = cart.getLines().get(0).getProductSku(); Product firstProduct = productRepository.findOneBySku(firstProductSku); @@ -59,8 +68,12 @@ public List getShippingServices(CartId cartId) { return servicesFound; } - public void setRecipient(CartId cartId, String fullName, String eMail) { - Recipient recipient = new Recipient(fullName, eMail); - cartRepository.findById(cartId).get().setRecipient(recipient); + @Transactional + public void setRecipient(CartId cartId, Recipient recipient) { + findOrFail(cartId).setRecipient(recipient); + } + + private Cart findOrFail(CartId cartId) { + return cartRepository.findById(cartId).orElseThrow(() -> new CartNotFoundException(cartId)); } } diff --git a/src/main/java/fr/arolla/modec/service/DeliveryService.java b/src/main/java/fr/arolla/modec/service/DeliveryService.java index b71b4eb..d6d5502 100644 --- a/src/main/java/fr/arolla/modec/service/DeliveryService.java +++ b/src/main/java/fr/arolla/modec/service/DeliveryService.java @@ -3,8 +3,12 @@ import fr.arolla.modec.entity.DeliveryId; import fr.arolla.modec.entity.Order; import fr.arolla.modec.entity.OrderId; +import fr.arolla.modec.exception.OrderNotFoundException; import fr.arolla.modec.repository.OrderRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +@Service public class DeliveryService { private final OrderRepository orderRepository; @@ -13,8 +17,9 @@ public DeliveryService(OrderRepository orderRepository) { this.orderRepository = orderRepository; } + @Transactional public DeliveryId createDeliveryFromOrder(OrderId orderId) { - orderRepository.findById(orderId).get().setStatus(Order.Status.IN_PREPARATION); + orderRepository.findById(orderId).orElseThrow(() -> new OrderNotFoundException(orderId)).setStatus(Order.Status.IN_PREPARATION); return new DeliveryId(orderId.getId()); } } diff --git a/src/main/java/fr/arolla/modec/service/OrderService.java b/src/main/java/fr/arolla/modec/service/OrderService.java index 8c60ac1..6dd8c4c 100644 --- a/src/main/java/fr/arolla/modec/service/OrderService.java +++ b/src/main/java/fr/arolla/modec/service/OrderService.java @@ -1,37 +1,42 @@ package fr.arolla.modec.service; -import fr.arolla.modec.BusinessException; +import fr.arolla.modec.exception.BusinessException; import fr.arolla.modec.entity.*; +import fr.arolla.modec.exception.CartNotFoundException; import fr.arolla.modec.repository.CartRepository; import fr.arolla.modec.repository.OrderLineRepository; import fr.arolla.modec.repository.OrderRepository; -import fr.arolla.modec.service.system.Timestamp; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.time.Clock; import java.util.List; import java.util.stream.Collectors; +@Service public class OrderService { private final CartRepository cartRepository; - private final Timestamp timestamp; + private final Clock clock; private final OrderRepository orderRepository; private final OrderLineRepository orderLineRepository; - public OrderService(CartRepository cartRepository, Timestamp timestamp, OrderRepository orderRepository, OrderLineRepository orderLineRepository) { + public OrderService(CartRepository cartRepository, Clock clock, OrderRepository orderRepository, OrderLineRepository orderLineRepository) { this.cartRepository = cartRepository; - this.timestamp = timestamp; + this.clock = clock; this.orderRepository = orderRepository; this.orderLineRepository = orderLineRepository; } + @Transactional public OrderId createOrderFromCart(CartId cartId) throws BusinessException { - Cart cart = cartRepository.findById(cartId).get(); + Cart cart = cartRepository.findById(cartId).orElseThrow(() -> new CartNotFoundException(cartId)); checkCartForOrder(cart); Order order = new Order(cart.getLines() .stream() .map(cartLine -> orderLineRepository.save(new OrderLine(cartLine.getProductSku(), cartLine.getProductName(), cartLine.getQuantity()))) .collect(Collectors.toList()), - timestamp.getCurrentDate(), + clock.instant(), cart.getRecipient(), cart.getShippingAddress()); order.setStatus(Order.Status.CREATED); @@ -45,6 +50,7 @@ private void checkCartForOrder(Cart cart) throws BusinessException { } } + @Transactional public List getOrdersForEMail(String eMail) { return orderRepository.findByRecipientEmail(eMail); } diff --git a/src/main/java/fr/arolla/modec/service/ProductService.java b/src/main/java/fr/arolla/modec/service/ProductService.java index f9467b9..1268718 100644 --- a/src/main/java/fr/arolla/modec/service/ProductService.java +++ b/src/main/java/fr/arolla/modec/service/ProductService.java @@ -2,7 +2,10 @@ import fr.arolla.modec.entity.Product; import fr.arolla.modec.repository.ProductRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +@Service public class ProductService { private final ProductRepository productRepository; @@ -11,6 +14,7 @@ public ProductService(ProductRepository productRepository) { this.productRepository = productRepository; } + @Transactional public Iterable getList() { return productRepository.findAll(); } diff --git a/src/main/java/fr/arolla/modec/service/TimeConfiguration.java b/src/main/java/fr/arolla/modec/service/TimeConfiguration.java new file mode 100644 index 0000000..0acf25b --- /dev/null +++ b/src/main/java/fr/arolla/modec/service/TimeConfiguration.java @@ -0,0 +1,14 @@ +package fr.arolla.modec.service; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.Clock; + +@Configuration +public class TimeConfiguration { + @Bean + public Clock clock() { + return Clock.systemDefaultZone(); + } +} diff --git a/src/main/java/fr/arolla/modec/service/system/Timestamp.java b/src/main/java/fr/arolla/modec/service/system/Timestamp.java deleted file mode 100644 index 17cf7ad..0000000 --- a/src/main/java/fr/arolla/modec/service/system/Timestamp.java +++ /dev/null @@ -1,7 +0,0 @@ -package fr.arolla.modec.service.system; - -import java.util.Calendar; - -public interface Timestamp { - Calendar getCurrentDate(); -} diff --git a/src/main/java/fr/arolla/modec/service/system/impl/TimestampSystem.java b/src/main/java/fr/arolla/modec/service/system/impl/TimestampSystem.java deleted file mode 100644 index be379a5..0000000 --- a/src/main/java/fr/arolla/modec/service/system/impl/TimestampSystem.java +++ /dev/null @@ -1,16 +0,0 @@ -package fr.arolla.modec.service.system.impl; - -import fr.arolla.modec.service.system.Timestamp; -import org.springframework.stereotype.Service; - -import java.util.Calendar; -import java.util.GregorianCalendar; - -@Service -public class TimestampSystem implements Timestamp { - - @Override - public Calendar getCurrentDate() { - return new GregorianCalendar(); - } -} diff --git a/src/main/java/fr/arolla/modec/util/FixedButMutableClock.java b/src/main/java/fr/arolla/modec/util/FixedButMutableClock.java new file mode 100644 index 0000000..2aad58e --- /dev/null +++ b/src/main/java/fr/arolla/modec/util/FixedButMutableClock.java @@ -0,0 +1,65 @@ +package fr.arolla.modec.util; + +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; + +/** + * Implementation of a clock that always returns the same instant. + * This is typically used for testing. + */ +public class FixedButMutableClock extends Clock { + private Instant instant; + private ZoneId zone; + + public FixedButMutableClock(Instant fixedInstant, ZoneId zone) { + this.instant = fixedInstant; + this.zone = zone; + } + + public void setInstant(Instant instant) { + this.instant = instant; + } + + @Override + public ZoneId getZone() { + return zone; + } + + @Override + public Clock withZone(ZoneId zone) { + if (zone.equals(this.zone)) { // intentional NPE + return this; + } + return new FixedButMutableClock(instant, zone); + } + + @Override + public long millis() { + return instant.toEpochMilli(); + } + + @Override + public Instant instant() { + return instant; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof FixedButMutableClock) { + FixedButMutableClock other = (FixedButMutableClock) obj; + return instant.equals(other.instant) && zone.equals(other.zone); + } + return false; + } + + @Override + public int hashCode() { + return instant.hashCode() ^ zone.hashCode(); + } + + @Override + public String toString() { + return "FixedMutableClock[" + instant + "," + zone + "]"; + } +} diff --git a/src/main/java/fr/arolla/modec/HttpController.java b/src/main/java/fr/arolla/modec/web/HttpController.java similarity index 74% rename from src/main/java/fr/arolla/modec/HttpController.java rename to src/main/java/fr/arolla/modec/web/HttpController.java index fe7c0e2..d7d7926 100644 --- a/src/main/java/fr/arolla/modec/HttpController.java +++ b/src/main/java/fr/arolla/modec/web/HttpController.java @@ -1,7 +1,6 @@ -package fr.arolla.modec; +package fr.arolla.modec.web; import fr.arolla.modec.entity.Product; -import fr.arolla.modec.repository.ProductRepository; import fr.arolla.modec.service.ProductService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; @@ -14,8 +13,8 @@ public class HttpController { private final ProductService product; @Autowired - public HttpController(ProductRepository productRepository) { - this.product = new ProductService(productRepository); + public HttpController(ProductService product) { + this.product = product; } @RequestMapping(value = "/product", method = RequestMethod.GET) diff --git a/src/test/java/fr/arolla/modec/acceptance/AcceptanceTest.java b/src/test/java/fr/arolla/modec/acceptance/AcceptanceTest.java index 7c93ae7..9c1edb4 100644 --- a/src/test/java/fr/arolla/modec/acceptance/AcceptanceTest.java +++ b/src/test/java/fr/arolla/modec/acceptance/AcceptanceTest.java @@ -5,10 +5,6 @@ import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@CucumberOptions( - features = "src/test/java/fr/arolla/modec/acceptance" -) - +@CucumberOptions() public class AcceptanceTest { - } diff --git a/src/test/java/fr/arolla/modec/acceptance/SpringBootBaseStepDefs.java b/src/test/java/fr/arolla/modec/acceptance/SpringBootBaseStepDefs.java index 0fdfd77..7806c33 100644 --- a/src/test/java/fr/arolla/modec/acceptance/SpringBootBaseStepDefs.java +++ b/src/test/java/fr/arolla/modec/acceptance/SpringBootBaseStepDefs.java @@ -1,11 +1,18 @@ package fr.arolla.modec.acceptance; +import fr.arolla.modec.util.FixedButMutableClock; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; import org.springframework.test.context.junit4.SpringRunner; +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; + @RunWith(SpringRunner.class) -@SpringBootTest() +@SpringBootTest(properties = {"spring.main.allow-bean-definition-overriding=true"}) public class SpringBootBaseStepDefs { } diff --git a/src/test/java/fr/arolla/modec/acceptance/StepDefs.java b/src/test/java/fr/arolla/modec/acceptance/StepDefs.java index ca636dd..8c4430c 100644 --- a/src/test/java/fr/arolla/modec/acceptance/StepDefs.java +++ b/src/test/java/fr/arolla/modec/acceptance/StepDefs.java @@ -3,25 +3,29 @@ import cucumber.api.java.en.And; import cucumber.api.java.en.Given; import cucumber.api.java.en.Then; -import fr.arolla.modec.BusinessException; import fr.arolla.modec.entity.*; -import fr.arolla.modec.repository.*; +import fr.arolla.modec.exception.BusinessException; +import fr.arolla.modec.repository.ProductRepository; +import fr.arolla.modec.repository.ShippingServiceRepository; import fr.arolla.modec.service.CartService; import fr.arolla.modec.service.DeliveryService; import fr.arolla.modec.service.OrderService; import fr.arolla.modec.service.ProductService; -import fr.arolla.modec.service.system.Timestamp; +import fr.arolla.modec.util.FixedButMutableClock; import io.cucumber.datatable.DataTable; -import org.mockito.Mockito; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.transaction.annotation.Transactional; -import java.text.SimpleDateFormat; +import java.time.Clock; import java.time.Instant; +import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -32,7 +36,7 @@ public class StepDefs extends SpringBootBaseStepDefs { private ProductRepository productRepository; private ProductService productService; private CartService cartService; - private Timestamp timestamp; + private FixedButMutableClock clock; private ShippingServiceRepository shippingServiceRepository; private OrderService orderService; private DeliveryService deliveryService; @@ -40,30 +44,35 @@ public class StepDefs extends SpringBootBaseStepDefs { private OrderId currentOrderId; private DeliveryId currentDeliveryId; - public StepDefs(ProductRepository productRepository, Timestamp timestamp, ShippingServiceRepository shippingServiceRepository, CartRepository cartRepository, CartLineRepository cartLineRepository, OrderRepository orderRepository, OrderLineRepository orderLineRepository) { + public StepDefs(ProductRepository productRepository, + ProductService productService, + FixedButMutableClock clock, + ShippingServiceRepository shippingServiceRepository, + CartService cartService, + OrderService orderService, + DeliveryService deliveryService) { this.productRepository = productRepository; - this.productService = new ProductService(productRepository); - this.cartService = new CartService(cartRepository, productRepository, cartLineRepository, shippingServiceRepository); - this.timestamp = timestamp; + this.productService = productService; + this.cartService = cartService; + this.clock = clock; this.shippingServiceRepository = shippingServiceRepository; - this.orderService = new OrderService(cartRepository, timestamp, orderRepository, orderLineRepository); - this.deliveryService = new DeliveryService(orderRepository); + this.orderService = orderService; + this.deliveryService = deliveryService; } @TestConfiguration static class TestContextConfiguration { @Bean - public Timestamp timestamp() { - return Mockito.mock(Timestamp.class); + public Clock clock() { + return new FixedButMutableClock(Instant.now(), ZoneId.systemDefault()); } } @Given("^now is \"([^\"]*)\"$") public void nowIs(String stringDate) throws Throwable { - Calendar now = new GregorianCalendar(); - now.setTime(Date.from(Instant.from(ZonedDateTime.parse(stringDate, DateTimeFormatter.ISO_OFFSET_DATE_TIME)))); - Mockito.when(timestamp.getCurrentDate()).thenReturn(now); + Instant instant = Instant.from(ZonedDateTime.parse(stringDate, DateTimeFormatter.ISO_OFFSET_DATE_TIME)); + clock.setInstant(instant); } @Given("^\"([^\"]*)\" as default locale$") @@ -94,19 +103,17 @@ public void aNewCartIsCreated() throws Throwable { this.currentCartId = cartService.createCart(); } - @Transactional @Given("^product \"([^\"]*)\" is added to this cart$") public void productIsAddedToThisCart(String sku) throws Throwable { cartService.addToCart(this.currentCartId, new Sku(sku), new Quantity(1)); } - @Transactional @Given("^\"([^\"]*)\", \"([^\"]*)\", \"([^\"]*)\", \"([^\"]*)\" in \"([^\"]*)\" is set as the shipping address of this cart$") public void inIsSetAsTheShippingAddressOfThisCart(String fullName, String line1, String city, String zipCode, String isoCountryCode) throws Throwable { - cartService.setShippingAddress(this.currentCartId, fullName, line1, city, zipCode, isoCountryCode); + ShippingAddress shippingAddress = new ShippingAddress(fullName, line1, city, zipCode, isoCountryCode); + cartService.setShippingAddress(this.currentCartId, shippingAddress); } - @Transactional @Then("^product catalog should contain:$") public void productCatalogShouldContain(DataTable products) throws Throwable { List> actualList = new ArrayList<>(); @@ -120,7 +127,6 @@ public void productCatalogShouldContain(DataTable products) throws Throwable { assertThat(actualList).isEqualTo(products.asMaps()); } - @Transactional @Then("^cart content should contain:$") public void cartContentShouldContain(DataTable cartLines) throws Throwable { List> actualList = new ArrayList<>(); @@ -135,7 +141,6 @@ public void cartContentShouldContain(DataTable cartLines) throws Throwable { assertThat(actualList).isEqualTo(cartLines.asMaps()); } - @Transactional @Then("^the shipping services available for this cart should be:$") public void theShippingServicesAvailableForThisCartShouldBe(DataTable shippingServices) throws Throwable { List> actualList = new ArrayList<>(); @@ -149,39 +154,35 @@ public void theShippingServicesAvailableForThisCartShouldBe(DataTable shippingSe assertThat(actualList).isEqualTo(shippingServices.asMaps()); } - @Transactional @And("^\"([^\"]*)\" with email address \"([^\"]*)\" is set as the recipient$") public void withEmailAdressIsSetAsTheRecipient(String fullName, String eMail) throws Throwable { - cartService.setRecipient(currentCartId, fullName, eMail); + Recipient recipient = new Recipient(fullName, eMail); + cartService.setRecipient(currentCartId, recipient); } - @Transactional @And("^order is validated$") public void orderIsValidated() throws Throwable { currentOrderId = orderService.createOrderFromCart(this.currentCartId); } - @Transactional @And("^order should not be validated$") public void orderCannotBeValidated() throws Throwable { assertThatThrownBy(() -> orderService.createOrderFromCart(currentCartId)).isInstanceOf(BusinessException.class); } - @Transactional @Then("^the historical orders for recipient \"([^\"]*)\" should be:$") public void theHistoricalOrdersForRecipientShouldBe(String eMail, DataTable orders) throws Throwable { List> actualList = new ArrayList<>(); for (Order order : orderService.getOrdersForEMail(eMail)) { Map orderMap = new HashMap<>(); - orderMap.put("creation date", new SimpleDateFormat("YYYY-MM-dd").format(order.getCreationDate().getTime())); + orderMap.put("creation date", DateTimeFormatter.ofPattern("YYYY-MM-dd").withZone(ZoneId.systemDefault()).format(order.getCreationDate())); orderMap.put("status", order.getStatus().toString()); actualList.add(orderMap); } assertThat(actualList).isEqualTo(orders.asMaps()); } - @Transactional @And("^delivery is validated$") public void deliveryIsValidated() throws Throwable { currentDeliveryId = deliveryService.createDeliveryFromOrder(currentOrderId); diff --git a/src/test/java/fr/arolla/modec/entity/QuantityTest.java b/src/test/java/fr/arolla/modec/entity/QuantityTest.java index 776ca73..4540cdc 100644 --- a/src/test/java/fr/arolla/modec/entity/QuantityTest.java +++ b/src/test/java/fr/arolla/modec/entity/QuantityTest.java @@ -1,21 +1,24 @@ package fr.arolla.modec.entity; import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; public class QuantityTest { @Test public void quantity_can_be_null() { Quantity quantity = new Quantity(0); + assertThat(quantity.getQuantity()).isEqualTo(0); } @Test public void quantity_can_be_positive() { Quantity quantity = new Quantity(1); + assertThat(quantity.getQuantity()).isEqualTo(1); } @Test(expected = IllegalArgumentException.class) public void quantity_cannot_be_negative() { - Quantity quantity = new Quantity(-1); + new Quantity(-1); } } diff --git a/src/test/java/fr/arolla/modec/service/CartServiceFeaturedTest.java b/src/test/java/fr/arolla/modec/service/CartServiceFeaturedTest.java index 500e323..96c5d6f 100644 --- a/src/test/java/fr/arolla/modec/service/CartServiceFeaturedTest.java +++ b/src/test/java/fr/arolla/modec/service/CartServiceFeaturedTest.java @@ -5,10 +5,7 @@ import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@CucumberOptions( - features = "src/test/java/fr/arolla/modec/service" -) - +@CucumberOptions() public class CartServiceFeaturedTest { } diff --git a/src/test/java/fr/arolla/modec/service/CartServiceTest.java b/src/test/java/fr/arolla/modec/service/CartServiceTest.java index a218087..48b67a9 100644 --- a/src/test/java/fr/arolla/modec/service/CartServiceTest.java +++ b/src/test/java/fr/arolla/modec/service/CartServiceTest.java @@ -18,6 +18,9 @@ @RunWith(MockitoJUnitRunner.class) public class CartServiceTest { + private static final Sku SKU_1 = new Sku("sku1"); + private static final Sku SKU_2 = new Sku("sku2"); + @Mock private CartRepository cartRepository; @Mock @@ -34,8 +37,8 @@ public void setUp() throws Exception { Mockito.when(cartRepository.findById(cartId)).thenReturn(Optional.of(cart)); Mockito.when(shippingServiceRepository.findOneByCode("Chrono10")).thenReturn(new ShippingService("Chrono10", "Chronopost", "level")); Mockito.when(shippingServiceRepository.findOneByCode("laposte")).thenReturn(new ShippingService("laposte", "La Poste", "level")); - Mockito.when(productRepository.findOneBySku(new Sku("sku1"))).thenReturn(new Product(new Sku("sku"), "Heavy","A heavy product", new Weight(10))); - Mockito.when(productRepository.findOneBySku(new Sku("sku2"))).thenReturn(new Product(new Sku("sku"), "Light","A light product", new Weight(0.5))); + Mockito.when(productRepository.findOneBySku(SKU_1)).thenReturn(new Product(SKU_1, "Heavy","A heavy product", new Weight(10))); + Mockito.when(productRepository.findOneBySku(SKU_2)).thenReturn(new Product(SKU_2, "Light","A light product", new Weight(0.5))); cartService = new CartService(cartRepository, productRepository, null, shippingServiceRepository); } @@ -46,7 +49,7 @@ public void emptyCartShouldHaveNoShippingService() { @Test public void FilledCartWithNoAddressShouldHaveNoShippingService() { - CartLine line = new CartLine(new Sku("sku1"), "name", new Quantity(1)); + CartLine line = new CartLine(SKU_1, "name", new Quantity(1)); cart.getLines().add(line); assertThat(cartService.getShippingServices(cartId)).isEmpty(); } @@ -59,7 +62,7 @@ public void emptyCartWithShippingShouldHaveNoShippingService() { @Test public void FilledCartWithShippingShouldHaveAShippingService() { - CartLine line = new CartLine(new Sku("sku1"), "name", new Quantity(1)); + CartLine line = new CartLine(SKU_1, "name", new Quantity(1)); cart.getLines().add(line); cart.setShippingAddress(new ShippingAddress("fullName", "line1", "city", "zipCode", "isoCountryCode")); assertThat(cartService.getShippingServices(cartId).size()).isEqualTo(1); @@ -68,7 +71,7 @@ public void FilledCartWithShippingShouldHaveAShippingService() { @Test public void LightCartShouldHaveLaPosteShippingService() { - CartLine line = new CartLine(new Sku("sku2"), "name", new Quantity(1)); + CartLine line = new CartLine(SKU_2, "name", new Quantity(1)); cart.getLines().add(line); cart.setShippingAddress(new ShippingAddress("fullName", "line1", "city", "zipCode", "isoCountryCode")); assertThat(cartService.getShippingServices(cartId).size()).isEqualTo(1); diff --git a/src/test/java/fr/arolla/modec/acceptance/bordercase.feature b/src/test/resources/fr/arolla/modec/acceptance/bordercase.feature similarity index 100% rename from src/test/java/fr/arolla/modec/acceptance/bordercase.feature rename to src/test/resources/fr/arolla/modec/acceptance/bordercase.feature diff --git a/src/test/java/fr/arolla/modec/acceptance/nominal.feature b/src/test/resources/fr/arolla/modec/acceptance/nominal.feature similarity index 100% rename from src/test/java/fr/arolla/modec/acceptance/nominal.feature rename to src/test/resources/fr/arolla/modec/acceptance/nominal.feature diff --git a/src/test/java/fr/arolla/modec/service/CartService.feature b/src/test/resources/fr/arolla/modec/service/CartService.feature similarity index 100% rename from src/test/java/fr/arolla/modec/service/CartService.feature rename to src/test/resources/fr/arolla/modec/service/CartService.feature