From 8fd4822c8534dd52b55d0d48b425f62e0160ee4c Mon Sep 17 00:00:00 2001 From: Wilfried BRIGITTE Date: Sat, 6 Jun 2026 13:09:58 +0200 Subject: [PATCH] debut order --- .../mylibrary/order/AdresseLivraison.java | 5 + .../mylibrary/order/LigneCommandeInfo.java | 7 + .../dev62/mylibrary/order/ModePaiement.java | 8 + .../but3/dev62/mylibrary/order/OrderDTO.java | 16 ++ .../but3/dev62/mylibrary/order/OrderInfo.java | 13 + .../order/converter/OrderConverter.java | 35 +++ .../mylibrary/order/entity/LigneCommande.java | 16 ++ .../dev62/mylibrary/order/entity/Order.java | 27 +++ .../exception/NotValidOrderException.java | 9 + .../exception/OrderNotFoundException.java | 14 ++ .../order/repository/OrderRepository.java | 51 ++++ .../mylibrary/order/usecase/OrderUseCase.java | 80 +++++++ .../order/validator/OrderValidator.java | 53 +++++ .../avis/converter/AvisConverterTest.java | 1 + .../order/converter/OrderConverterTest.java | 90 +++++++ .../mylibrary/order/entity/OrderTest.java | 81 +++++++ .../exception/NotValidOrderExceptionTest.java | 62 +++++ .../exception/OrderNotFoundExceptionTest.java | 48 ++++ .../order/repository/OrderRepositoryTest.java | 202 ++++++++++++++++ .../order/usecase/OrderUseCaseTest.java | 223 ++++++++++++++++++ .../order/validator/OrderValidatorTest.java | 155 ++++++++++++ src/test/resources/features/order.feature | 49 ++++ 22 files changed, 1245 insertions(+) create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/AdresseLivraison.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/LigneCommandeInfo.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/ModePaiement.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/OrderDTO.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/OrderInfo.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/converter/OrderConverter.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/LigneCommande.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/Order.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/NotValidOrderException.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/OrderNotFoundException.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/repository/OrderRepository.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/usecase/OrderUseCase.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/validator/OrderValidator.java create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/converter/OrderConverterTest.java create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/OrderTest.java create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/NotValidOrderExceptionTest.java create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/OrderNotFoundExceptionTest.java create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/repository/OrderRepositoryTest.java create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/usecase/OrderUseCaseTest.java create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/validator/OrderValidatorTest.java create mode 100644 src/test/resources/features/order.feature diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/AdresseLivraison.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/AdresseLivraison.java new file mode 100644 index 0000000..1227699 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/AdresseLivraison.java @@ -0,0 +1,5 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order; + +public record AdresseLivraison(String rue, String ville, String codePostal, String pays) { +} + \ No newline at end of file diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/LigneCommandeInfo.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/LigneCommandeInfo.java new file mode 100644 index 0000000..25bad52 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/LigneCommandeInfo.java @@ -0,0 +1,7 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order; + +import java.util.UUID; + +public record LigneCommandeInfo(UUID livreId, int quantite) { +} + \ No newline at end of file diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/ModePaiement.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/ModePaiement.java new file mode 100644 index 0000000..ba49374 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/ModePaiement.java @@ -0,0 +1,8 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order; + +public enum ModePaiement { + CB, + PAYPAL, + POINTS_FIDELITE +} + \ No newline at end of file diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/OrderDTO.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/OrderDTO.java new file mode 100644 index 0000000..7a2ef07 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/OrderDTO.java @@ -0,0 +1,16 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order; + +import lombok.Builder; +import lombok.Getter; + +import java.math.BigDecimal; +import java.util.UUID; + +@Builder +@Getter +public class OrderDTO { + private final UUID commandeId; + private final BigDecimal montantTotal; + private final int pointsFideliteGagnes; +} + \ No newline at end of file diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/OrderInfo.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/OrderInfo.java new file mode 100644 index 0000000..fa082e2 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/OrderInfo.java @@ -0,0 +1,13 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order; + +import java.util.List; +import java.util.UUID; + +public record OrderInfo( + UUID clientId, + List lignesCommande, + AdresseLivraison adresseLivraison, + ModePaiement modePaiement +) { +} + \ No newline at end of file diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/converter/OrderConverter.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/converter/OrderConverter.java new file mode 100644 index 0000000..99680b2 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/converter/OrderConverter.java @@ -0,0 +1,35 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order.converter; + +import fr.iut_fbleau.but3.dev62.mylibrary.order.OrderDTO; +import fr.iut_fbleau.but3.dev62.mylibrary.order.OrderInfo; +import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.LigneCommande; +import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Order; + +import java.math.BigDecimal; +import java.util.List; + +public final class OrderConverter { + + private OrderConverter() { + } + + public static Order toDomain(OrderInfo orderInfo, List lignes, + BigDecimal montantTotal, int pointsFideliteGagnes) { + return Order.builder() + .clientId(orderInfo.clientId()) + .lignesCommande(lignes) + .adresseLivraison(orderInfo.adresseLivraison()) + .modePaiement(orderInfo.modePaiement()) + .montantTotal(montantTotal) + .pointsFideliteGagnes(pointsFideliteGagnes) + .build(); + } + + public static OrderDTO toDTO(Order order) { + return OrderDTO.builder() + .commandeId(order.getId()) + .montantTotal(order.getMontantTotal()) + .pointsFideliteGagnes(order.getPointsFideliteGagnes()) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/LigneCommande.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/LigneCommande.java new file mode 100644 index 0000000..e70a249 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/LigneCommande.java @@ -0,0 +1,16 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order.entity; + +import lombok.Builder; +import lombok.Getter; + +import java.math.BigDecimal; +import java.util.UUID; + +@Builder +@Getter +public class LigneCommande { + private UUID livreId; + private int quantite; + private BigDecimal prixUnitaire; +} + \ No newline at end of file diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/Order.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/Order.java new file mode 100644 index 0000000..f38cea2 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/Order.java @@ -0,0 +1,27 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order.entity; + +import fr.iut_fbleau.but3.dev62.mylibrary.order.AdresseLivraison; +import fr.iut_fbleau.but3.dev62.mylibrary.order.ModePaiement; +import lombok.Builder; +import lombok.Getter; + +import java.math.BigDecimal; +import java.util.List; +import java.util.UUID; + +@Builder +@Getter +public class Order { + private UUID id; + private UUID clientId; + private List lignesCommande; + private AdresseLivraison adresseLivraison; + private ModePaiement modePaiement; + private BigDecimal montantTotal; + private int pointsFideliteGagnes; + + public void setRandomUUID() { + this.id = UUID.randomUUID(); + } +} + \ No newline at end of file diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/NotValidOrderException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/NotValidOrderException.java new file mode 100644 index 0000000..9d782c1 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/NotValidOrderException.java @@ -0,0 +1,9 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order.exception; + +public class NotValidOrderException extends Exception { + + public NotValidOrderException(String message) { + super(message); + } +} + \ No newline at end of file diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/OrderNotFoundException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/OrderNotFoundException.java new file mode 100644 index 0000000..a8d62ca --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/OrderNotFoundException.java @@ -0,0 +1,14 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order.exception; + +import java.text.MessageFormat; +import java.util.UUID; + +public class OrderNotFoundException extends Exception { + + public static final String THE_ORDER_WITH_ID_DOES_NOT_EXIST_MESSAGE = "The order with id {0} does not exist"; + + public OrderNotFoundException(UUID uuid) { + super(MessageFormat.format(THE_ORDER_WITH_ID_DOES_NOT_EXIST_MESSAGE, uuid)); + } +} + \ No newline at end of file diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/repository/OrderRepository.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/repository/OrderRepository.java new file mode 100644 index 0000000..c773123 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/repository/OrderRepository.java @@ -0,0 +1,51 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order.repository; + +import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Order; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@NoArgsConstructor +public final class OrderRepository { + + private final List orders = new ArrayList<>(); + + public List findAll() { + return orders; + } + + public void deleteAll() { + orders.clear(); + } + + public Order save(Order newOrder) { + Optional existing = this.findById(newOrder.getId()); + existing.ifPresentOrElse(orders::remove, newOrder::setRandomUUID); + this.orders.add(newOrder); + return newOrder; + } + + public Optional findById(UUID uuid) { + return this.orders.stream() + .filter(order -> order.getId().equals(uuid)) + .findFirst(); + } + + public boolean existsById(UUID uuid) { + return this.orders.stream() + .anyMatch(order -> order.getId().equals(uuid)); + } + + public List findByClientId(UUID clientId) { + return this.orders.stream() + .filter(order -> order.getClientId().equals(clientId)) + .toList(); + } + + public void delete(Order order) { + this.orders.remove(order); + } +} \ No newline at end of file diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/usecase/OrderUseCase.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/usecase/OrderUseCase.java new file mode 100644 index 0000000..5af112b --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/usecase/OrderUseCase.java @@ -0,0 +1,80 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order.usecase; + +import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book; +import fr.iut_fbleau.but3.dev62.mylibrary.book.repository.BookRepository; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.entity.Customer; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.CustomerNotFoundException; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.repository.CustomerRepository; +import fr.iut_fbleau.but3.dev62.mylibrary.order.LigneCommandeInfo; +import fr.iut_fbleau.but3.dev62.mylibrary.order.ModePaiement; +import fr.iut_fbleau.but3.dev62.mylibrary.order.OrderDTO; +import fr.iut_fbleau.but3.dev62.mylibrary.order.OrderInfo; +import fr.iut_fbleau.but3.dev62.mylibrary.order.converter.OrderConverter; +import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.LigneCommande; +import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Order; +import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.NotValidOrderException; +import fr.iut_fbleau.but3.dev62.mylibrary.order.repository.OrderRepository; +import fr.iut_fbleau.but3.dev62.mylibrary.order.validator.OrderValidator; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public final class OrderUseCase { + + private final OrderRepository orderRepository; + private final CustomerRepository customerRepository; + private final BookRepository bookRepository; + + public OrderUseCase(OrderRepository orderRepository, CustomerRepository customerRepository, + BookRepository bookRepository) { + this.orderRepository = orderRepository; + this.customerRepository = customerRepository; + this.bookRepository = bookRepository; + } + + public OrderDTO passerCommande(OrderInfo orderInfo) + throws NotValidOrderException, CustomerNotFoundException { + OrderValidator.validate(orderInfo); + + Customer customer = customerRepository.findById(orderInfo.clientId()) + .orElseThrow(() -> new CustomerNotFoundException(orderInfo.clientId())); + + List lignes = new ArrayList<>(); + BigDecimal montantTotal = BigDecimal.ZERO; + + for (LigneCommandeInfo ligneInfo : orderInfo.lignesCommande()) { + Book book = bookRepository.findById(ligneInfo.livreId()) + .orElseThrow(() -> new NotValidOrderException("Book not found: " + ligneInfo.livreId())); + + BigDecimal prixLigne = book.getPrice().multiply(BigDecimal.valueOf(ligneInfo.quantite())); + montantTotal = montantTotal.add(prixLigne); + + lignes.add(LigneCommande.builder() + .livreId(ligneInfo.livreId()) + .quantite(ligneInfo.quantite()) + .prixUnitaire(book.getPrice()) + .build()); + } + + int pointsGagnes = montantTotal.intValue(); + + if (orderInfo.modePaiement() == ModePaiement.POINTS_FIDELITE) { + customer.addLoyaltyPoints(-montantTotal.intValue()); + } else { + customer.addLoyaltyPoints(pointsGagnes); + } + customerRepository.save(customer); + + Order order = OrderConverter.toDomain(orderInfo, lignes, montantTotal, pointsGagnes); + Order savedOrder = orderRepository.save(order); + + return OrderConverter.toDTO(savedOrder); + } + + public Optional findOrderById(UUID uuid) { + return orderRepository.findById(uuid).map(OrderConverter::toDTO); + } +} \ No newline at end of file diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/validator/OrderValidator.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/validator/OrderValidator.java new file mode 100644 index 0000000..b7b1b04 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/validator/OrderValidator.java @@ -0,0 +1,53 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order.validator; + +import fr.iut_fbleau.but3.dev62.mylibrary.order.LigneCommandeInfo; +import fr.iut_fbleau.but3.dev62.mylibrary.order.OrderInfo; +import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.NotValidOrderException; + +public final class OrderValidator { + + public static final String CLIENT_ID_CANNOT_BE_NULL = "Client id cannot be null"; + public static final String LIGNES_COMMANDE_CANNOT_BE_EMPTY = "Order must have at least one item"; + public static final String QUANTITE_MUST_BE_POSITIVE = "Quantity must be positive"; + public static final String MODE_PAIEMENT_CANNOT_BE_NULL = "Payment method cannot be null"; + public static final String ADRESSE_LIVRAISON_CANNOT_BE_NULL = "Delivery address cannot be null"; + + private OrderValidator() { + } + + public static void validate(OrderInfo orderInfo) throws NotValidOrderException { + validateClientId(orderInfo); + validateLignesCommande(orderInfo); + validateAdresseLivraison(orderInfo); + validateModePaiement(orderInfo); + } + + private static void validateClientId(OrderInfo orderInfo) throws NotValidOrderException { + if (orderInfo.clientId() == null) { + throw new NotValidOrderException(CLIENT_ID_CANNOT_BE_NULL); + } + } + + private static void validateLignesCommande(OrderInfo orderInfo) throws NotValidOrderException { + if (orderInfo.lignesCommande() == null || orderInfo.lignesCommande().isEmpty()) { + throw new NotValidOrderException(LIGNES_COMMANDE_CANNOT_BE_EMPTY); + } + for (LigneCommandeInfo ligne : orderInfo.lignesCommande()) { + if (ligne.quantite() <= 0) { + throw new NotValidOrderException(QUANTITE_MUST_BE_POSITIVE); + } + } + } + + private static void validateAdresseLivraison(OrderInfo orderInfo) throws NotValidOrderException { + if (orderInfo.adresseLivraison() == null) { + throw new NotValidOrderException(ADRESSE_LIVRAISON_CANNOT_BE_NULL); + } + } + + private static void validateModePaiement(OrderInfo orderInfo) throws NotValidOrderException { + if (orderInfo.modePaiement() == null) { + throw new NotValidOrderException(MODE_PAIEMENT_CANNOT_BE_NULL); + } + } +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/avis/converter/AvisConverterTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/avis/converter/AvisConverterTest.java index 0f8e3ac..8bd0f71 100644 --- a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/avis/converter/AvisConverterTest.java +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/avis/converter/AvisConverterTest.java @@ -10,6 +10,7 @@ import org.junit.jupiter.api.Test; import java.time.LocalDate; import java.util.UUID; + import static org.junit.jupiter.api.Assertions.*; @DisplayName("AvisConverter Unit Tests") diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/converter/OrderConverterTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/converter/OrderConverterTest.java new file mode 100644 index 0000000..e527210 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/converter/OrderConverterTest.java @@ -0,0 +1,90 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order.converter; + +import fr.iut_fbleau.but3.dev62.mylibrary.order.*; +import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.LigneCommande; +import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Order; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +@DisplayName("OrderConverter Unit Tests") +class OrderConverterTest { + + private final UUID clientId = UUID.randomUUID(); + private final UUID livreId = UUID.randomUUID(); + private final AdresseLivraison adresse = new AdresseLivraison("1 rue de Paris", "Paris", "75001", "France"); + + @Nested + @DisplayName("toDomain() method tests") + class ToDomainTests { + + @Test + @DisplayName("Should convert OrderInfo to Order domain object") + void shouldConvertOrderInfoToDomain() { + OrderInfo orderInfo = new OrderInfo( + clientId, + List.of(new LigneCommandeInfo(livreId, 2)), + adresse, + ModePaiement.CB + ); + + List lignes = List.of( + LigneCommande.builder() + .livreId(livreId) + .quantite(2) + .prixUnitaire(new BigDecimal("12.90")) + .build() + ); + + Order result = OrderConverter.toDomain(orderInfo, lignes, new BigDecimal("25.80"), 25); + + assertNotNull(result); + assertEquals(clientId, result.getClientId()); + assertEquals(adresse, result.getAdresseLivraison()); + assertEquals(ModePaiement.CB, result.getModePaiement()); + assertEquals(new BigDecimal("25.80"), result.getMontantTotal()); + assertEquals(25, result.getPointsFideliteGagnes()); + assertEquals(1, result.getLignesCommande().size()); + } + + @Test + @DisplayName("Should have null ID after toDomain (set by repository)") + void shouldHaveNullIdAfterToDomain() { + OrderInfo orderInfo = new OrderInfo(clientId, List.of(new LigneCommandeInfo(livreId, 1)), adresse, ModePaiement.CB); + + Order result = OrderConverter.toDomain(orderInfo, List.of(), BigDecimal.ZERO, 0); + + assertNull(result.getId()); + } + } + + @Nested + @DisplayName("toDTO() method tests") + class ToDTOTests { + + @Test + @DisplayName("Should convert Order domain object to OrderDTO with all fields mapped correctly") + void shouldConvertOrderToDTO() { + UUID orderId = UUID.randomUUID(); + Order order = Order.builder() + .id(orderId) + .clientId(clientId) + .montantTotal(new BigDecimal("25.80")) + .pointsFideliteGagnes(25) + .build(); + + OrderDTO result = OrderConverter.toDTO(order); + + assertNotNull(result); + assertEquals(orderId, result.getCommandeId()); + assertEquals(new BigDecimal("25.80"), result.getMontantTotal()); + assertEquals(25, result.getPointsFideliteGagnes()); + } + } +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/OrderTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/OrderTest.java new file mode 100644 index 0000000..04f7411 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/OrderTest.java @@ -0,0 +1,81 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order.entity; + +import fr.iut_fbleau.but3.dev62.mylibrary.order.AdresseLivraison; +import fr.iut_fbleau.but3.dev62.mylibrary.order.ModePaiement; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +class OrderTest { + + @Test + @DisplayName("Builder should create a valid Order instance") + void testOrderBuilder() { + UUID id = UUID.randomUUID(); + UUID clientId = UUID.randomUUID(); + AdresseLivraison adresse = new AdresseLivraison("1 rue de Paris", "Paris", "75001", "France"); + + Order order = Order.builder() + .id(id) + .clientId(clientId) + .adresseLivraison(adresse) + .modePaiement(ModePaiement.CB) + .montantTotal(new BigDecimal("25.80")) + .pointsFideliteGagnes(25) + .lignesCommande(List.of()) + .build(); + + assertEquals(id, order.getId()); + assertEquals(clientId, order.getClientId()); + assertEquals(adresse, order.getAdresseLivraison()); + assertEquals(ModePaiement.CB, order.getModePaiement()); + assertEquals(new BigDecimal("25.80"), order.getMontantTotal()); + assertEquals(25, order.getPointsFideliteGagnes()); + } + + @Test + @DisplayName("setRandomUUID should set a new non-null UUID") + void testSetRandomUUID() { + Order order = Order.builder().build(); + UUID originalId = order.getId(); + + order.setRandomUUID(); + + assertNotNull(order.getId()); + assertNotEquals(originalId, order.getId()); + } + + @Test + @DisplayName("Two setRandomUUID calls should produce different UUIDs") + void testSetRandomUUIDTwice() { + Order order = Order.builder().build(); + order.setRandomUUID(); + UUID firstId = order.getId(); + + order.setRandomUUID(); + UUID secondId = order.getId(); + + assertNotEquals(firstId, secondId); + } + + @Test + @DisplayName("Builder should create a valid LigneCommande instance") + void testLigneCommandeBuilder() { + UUID livreId = UUID.randomUUID(); + + LigneCommande ligne = LigneCommande.builder() + .livreId(livreId) + .quantite(3) + .prixUnitaire(new BigDecimal("12.90")) + .build(); + + assertEquals(livreId, ligne.getLivreId()); + assertEquals(3, ligne.getQuantite()); + assertEquals(new BigDecimal("12.90"), ligne.getPrixUnitaire()); + } +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/NotValidOrderExceptionTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/NotValidOrderExceptionTest.java new file mode 100644 index 0000000..3c13c35 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/NotValidOrderExceptionTest.java @@ -0,0 +1,62 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order.exception; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class NotValidOrderExceptionTest { + + @Test + @DisplayName("Exception should be created with the provided message") + void testExceptionCreation() { + String errorMessage = "Order data is not valid"; + + NotValidOrderException exception = new NotValidOrderException(errorMessage); + + assertEquals(errorMessage, exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = { + "Client id cannot be null", + "Order must have at least one item", + "Quantity must be positive", + "Payment method cannot be null", + "Delivery address cannot be null" + }) + @DisplayName("Exception should handle different validation messages") + void testExceptionWithDifferentMessages(String errorMessage) { + NotValidOrderException exception = new NotValidOrderException(errorMessage); + + assertEquals(errorMessage, exception.getMessage()); + } + + @Test + @DisplayName("Exception should be properly thrown and caught") + void testExceptionCanBeThrownAndCaught() { + String errorMessage = "Order must have at least one item"; + + Exception exception = assertThrows(NotValidOrderException.class, () -> { + throw new NotValidOrderException(errorMessage); + }); + + assertEquals(errorMessage, exception.getMessage()); + } + + @Test + @DisplayName("Exception should be catchable as a general Exception") + void testExceptionInheritance() { + String errorMessage = "Quantity must be positive"; + + try { + throw new NotValidOrderException(errorMessage); + } catch (Exception e) { + assertEquals(NotValidOrderException.class, e.getClass()); + assertEquals(errorMessage, e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/OrderNotFoundExceptionTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/OrderNotFoundExceptionTest.java new file mode 100644 index 0000000..ce4937d --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/OrderNotFoundExceptionTest.java @@ -0,0 +1,48 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order.exception; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class OrderNotFoundExceptionTest { + + @Test + @DisplayName("Exception message should contain the UUID provided") + void testExceptionMessageContainsUUID() { + UUID uuid = UUID.randomUUID(); + + OrderNotFoundException exception = new OrderNotFoundException(uuid); + + String expectedMessage = String.format("The order with id %s does not exist", uuid); + assertEquals(expectedMessage, exception.getMessage()); + } + + @Test + @DisplayName("Exception should use the correct constant message format") + void testExceptionUsesConstantMessageFormat() { + UUID uuid = UUID.randomUUID(); + + OrderNotFoundException exception = new OrderNotFoundException(uuid); + + assertEquals("The order with id {0} does not exist", + OrderNotFoundException.THE_ORDER_WITH_ID_DOES_NOT_EXIST_MESSAGE); + assertTrue(exception.getMessage().contains(uuid.toString())); + } + + @Test + @DisplayName("Exception should be properly thrown and caught") + void testExceptionCanBeThrownAndCaught() { + UUID uuid = UUID.randomUUID(); + + try { + throw new OrderNotFoundException(uuid); + } catch (OrderNotFoundException e) { + String expectedMessage = String.format("The order with id %s does not exist", uuid); + assertEquals(expectedMessage, e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/repository/OrderRepositoryTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/repository/OrderRepositoryTest.java new file mode 100644 index 0000000..75bd662 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/repository/OrderRepositoryTest.java @@ -0,0 +1,202 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order.repository; + +import fr.iut_fbleau.but3.dev62.mylibrary.order.AdresseLivraison; +import fr.iut_fbleau.but3.dev62.mylibrary.order.ModePaiement; +import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Order; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +class OrderRepositoryTest { + + private OrderRepository repository; + private Order order1; + private Order order2; + private UUID clientId1; + private UUID clientId2; + + @BeforeEach + void setUp() { + repository = new OrderRepository(); + clientId1 = UUID.randomUUID(); + clientId2 = UUID.randomUUID(); + + AdresseLivraison adresse = new AdresseLivraison("1 rue de Paris", "Paris", "75001", "France"); + + order1 = Order.builder() + .clientId(clientId1) + .adresseLivraison(adresse) + .modePaiement(ModePaiement.CB) + .montantTotal(new BigDecimal("25.80")) + .pointsFideliteGagnes(25) + .lignesCommande(List.of()) + .build(); + order1.setRandomUUID(); + + order2 = Order.builder() + .clientId(clientId2) + .adresseLivraison(adresse) + .modePaiement(ModePaiement.PAYPAL) + .montantTotal(new BigDecimal("9.50")) + .pointsFideliteGagnes(9) + .lignesCommande(List.of()) + .build(); + order2.setRandomUUID(); + } + + @Test + @DisplayName("New repository should be empty") + void testNewRepositoryIsEmpty() { + assertTrue(repository.findAll().isEmpty()); + assertEquals(0, repository.findAll().size()); + } + + @Nested + @DisplayName("Save operations") + class SaveOperations { + + @Test + @DisplayName("Save should add a new order") + void testSaveNewOrder() { + Order saved = repository.save(order1); + + assertEquals(1, repository.findAll().size()); + assertEquals(order1.getId(), saved.getId()); + } + + @Test + @DisplayName("Save should update existing order with same ID") + void testSaveUpdatesExistingOrder() { + repository.save(order1); + UUID id = order1.getId(); + + Order updatedOrder = Order.builder() + .id(id) + .clientId(clientId1) + .adresseLivraison(new AdresseLivraison("2 rue de Lyon", "Lyon", "69001", "France")) + .modePaiement(ModePaiement.PAYPAL) + .montantTotal(new BigDecimal("50.00")) + .pointsFideliteGagnes(50) + .lignesCommande(List.of()) + .build(); + + Order saved = repository.save(updatedOrder); + + assertEquals(1, repository.findAll().size()); + assertEquals(id, saved.getId()); + assertEquals(new BigDecimal("50.00"), saved.getMontantTotal()); + } + + @Test + @DisplayName("Save multiple orders should add all of them") + void testSaveMultipleOrders() { + repository.save(order1); + repository.save(order2); + + assertEquals(2, repository.findAll().size()); + } + } + + @Nested + @DisplayName("Find operations") + class FindOperations { + + @BeforeEach + void setUpOrders() { + repository.save(order1); + repository.save(order2); + } + + @Test + @DisplayName("FindAll should return all orders") + void testFindAll() { + assertEquals(2, repository.findAll().size()); + } + + @Test + @DisplayName("FindById should return order with matching ID") + void testFindById() { + Optional found = repository.findById(order1.getId()); + + assertTrue(found.isPresent()); + assertEquals(order1.getId(), found.get().getId()); + } + + @Test + @DisplayName("FindById should return empty Optional when ID doesn't exist") + void testFindByIdNotFound() { + Optional found = repository.findById(UUID.randomUUID()); + + assertTrue(found.isEmpty()); + } + + @Test + @DisplayName("FindByClientId should return all orders for a client") + void testFindByClientId() { + List found = repository.findByClientId(clientId1); + + assertEquals(1, found.size()); + assertEquals(order1.getId(), found.getFirst().getId()); + } + + @Test + @DisplayName("ExistsById should return true when ID exists") + void testExistsByIdExists() { + assertTrue(repository.existsById(order1.getId())); + } + + @Test + @DisplayName("ExistsById should return false when ID doesn't exist") + void testExistsByIdNotExists() { + assertFalse(repository.existsById(UUID.randomUUID())); + } + } + + @Nested + @DisplayName("Delete operations") + class DeleteOperations { + + @BeforeEach + void setUpOrders() { + repository.save(order1); + repository.save(order2); + } + + @Test + @DisplayName("Delete should remove the specified order") + void testDelete() { + repository.delete(order1); + + assertEquals(1, repository.findAll().size()); + assertFalse(repository.findAll().contains(order1)); + assertTrue(repository.findAll().contains(order2)); + } + + @Test + @DisplayName("DeleteAll should remove all orders") + void testDeleteAll() { + repository.deleteAll(); + + assertTrue(repository.findAll().isEmpty()); + } + + @Test + @DisplayName("Delete should not throw exception when order doesn't exist") + void testDeleteNonExistentOrder() { + Order nonExistent = Order.builder().build(); + nonExistent.setRandomUUID(); + + assertDoesNotThrow(() -> repository.delete(nonExistent)); + assertEquals(2, repository.findAll().size()); + } + } +} + \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/usecase/OrderUseCaseTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/usecase/OrderUseCaseTest.java new file mode 100644 index 0000000..591ad3d --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/usecase/OrderUseCaseTest.java @@ -0,0 +1,223 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order.usecase; + +import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book; +import fr.iut_fbleau.but3.dev62.mylibrary.book.repository.BookRepository; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.entity.Customer; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.CustomerNotFoundException; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.repository.CustomerRepository; +import fr.iut_fbleau.but3.dev62.mylibrary.order.*; +import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Order; +import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.NotValidOrderException; +import fr.iut_fbleau.but3.dev62.mylibrary.order.repository.OrderRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class OrderUseCaseTest { + + @Mock + private OrderRepository orderRepository; + + @Mock + private CustomerRepository customerRepository; + + @Mock + private BookRepository bookRepository; + + @InjectMocks + private OrderUseCase orderUseCase; + + private UUID customerId; + private UUID bookId; + private Customer testCustomer; + private Book testBook; + private AdresseLivraison adresse; + private OrderInfo validOrderInfo; + + @BeforeEach + void setUp() { + customerId = UUID.randomUUID(); + bookId = UUID.randomUUID(); + + testCustomer = Customer.builder() + .id(customerId) + .firstName("Marie") + .lastName("Dupont") + .phoneNumber("0612345678") + .loyaltyPoints(100) + .build(); + + testBook = Book.builder() + .id(bookId) + .isbn("9782016289308") + .title("Le Petit Prince") + .author("Antoine de Saint-Exupery") + .publisher("Gallimard") + .publicationDate(LocalDate.of(1943, 4, 6)) + .price(new BigDecimal("12.90")) + .stock(10) + .categories(List.of("Roman")) + .description("Un classique") + .language("FR") + .build(); + + adresse = new AdresseLivraison("1 rue de Paris", "Paris", "75001", "France"); + + validOrderInfo = new OrderInfo( + customerId, + List.of(new LigneCommandeInfo(bookId, 2)), + adresse, + ModePaiement.CB + ); + } + + @Nested + @DisplayName("PasserCommande tests") + class PasserCommandeTests { + + @Test + @DisplayName("Should place order when valid data is provided") + void testPasserCommandeWithValidData() throws NotValidOrderException, CustomerNotFoundException { + when(customerRepository.findById(customerId)).thenReturn(Optional.of(testCustomer)); + when(bookRepository.findById(bookId)).thenReturn(Optional.of(testBook)); + when(customerRepository.save(any(Customer.class))).thenReturn(testCustomer); + + UUID orderId = UUID.randomUUID(); + Order savedOrder = Order.builder() + .id(orderId) + .clientId(customerId) + .montantTotal(new BigDecimal("25.80")) + .pointsFideliteGagnes(25) + .build(); + when(orderRepository.save(any(Order.class))).thenReturn(savedOrder); + + OrderDTO result = orderUseCase.passerCommande(validOrderInfo); + + assertNotNull(result); + assertEquals(orderId, result.getCommandeId()); + verify(orderRepository, times(1)).save(any(Order.class)); + verify(customerRepository, times(1)).save(any(Customer.class)); + } + + @Test + @DisplayName("Should throw exception when customer does not exist") + void testPasserCommandeWithUnknownCustomer() { + when(customerRepository.findById(customerId)).thenReturn(Optional.empty()); + + assertThrows(CustomerNotFoundException.class, + () -> orderUseCase.passerCommande(validOrderInfo)); + + verify(orderRepository, never()).save(any(Order.class)); + } + + @Test + @DisplayName("Should throw exception when order has no items") + void testPasserCommandeWithNoItems() { + OrderInfo emptyOrder = new OrderInfo(customerId, List.of(), adresse, ModePaiement.CB); + + assertThrows(NotValidOrderException.class, + () -> orderUseCase.passerCommande(emptyOrder)); + + verify(orderRepository, never()).save(any(Order.class)); + } + + @Test + @DisplayName("Should throw exception when quantity is zero or negative") + void testPasserCommandeWithInvalidQuantity() { + OrderInfo invalidQty = new OrderInfo( + customerId, + List.of(new LigneCommandeInfo(bookId, 0)), + adresse, + ModePaiement.CB + ); + + assertThrows(NotValidOrderException.class, + () -> orderUseCase.passerCommande(invalidQty)); + + verify(orderRepository, never()).save(any(Order.class)); + } + + @Test + @DisplayName("Should throw exception when clientId is null") + void testPasserCommandeWithNullClientId() { + OrderInfo nullClient = new OrderInfo( + null, + List.of(new LigneCommandeInfo(bookId, 1)), + adresse, + ModePaiement.CB + ); + + assertThrows(NotValidOrderException.class, + () -> orderUseCase.passerCommande(nullClient)); + + verify(orderRepository, never()).save(any(Order.class)); + } + + @Test + @DisplayName("Should throw exception when payment method is null") + void testPasserCommandeWithNullModePaiement() { + OrderInfo nullPayment = new OrderInfo( + customerId, + List.of(new LigneCommandeInfo(bookId, 1)), + adresse, + null + ); + + assertThrows(NotValidOrderException.class, + () -> orderUseCase.passerCommande(nullPayment)); + + verify(orderRepository, never()).save(any(Order.class)); + } + } + + @Nested + @DisplayName("FindOrder tests") + class FindOrderTests { + + @Test + @DisplayName("Should return order when ID exists") + void testFindOrderById() { + UUID orderId = UUID.randomUUID(); + Order order = Order.builder() + .id(orderId) + .clientId(customerId) + .montantTotal(new BigDecimal("25.80")) + .pointsFideliteGagnes(25) + .build(); + + when(orderRepository.findById(orderId)).thenReturn(Optional.of(order)); + + Optional result = orderUseCase.findOrderById(orderId); + + assertTrue(result.isPresent()); + assertEquals(orderId, result.get().getCommandeId()); + } + + @Test + @DisplayName("Should return empty Optional when ID does not exist") + void testFindOrderByIdNotFound() { + UUID nonExistentId = UUID.randomUUID(); + when(orderRepository.findById(nonExistentId)).thenReturn(Optional.empty()); + + Optional result = orderUseCase.findOrderById(nonExistentId); + + assertTrue(result.isEmpty()); + } + } +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/validator/OrderValidatorTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/validator/OrderValidatorTest.java new file mode 100644 index 0000000..4401d7b --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/validator/OrderValidatorTest.java @@ -0,0 +1,155 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order.validator; + +import fr.iut_fbleau.but3.dev62.mylibrary.order.AdresseLivraison; +import fr.iut_fbleau.but3.dev62.mylibrary.order.LigneCommandeInfo; +import fr.iut_fbleau.but3.dev62.mylibrary.order.ModePaiement; +import fr.iut_fbleau.but3.dev62.mylibrary.order.OrderInfo; +import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.NotValidOrderException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +class OrderValidatorTest { + + private final UUID clientId = UUID.randomUUID(); + private final UUID livreId = UUID.randomUUID(); + private final AdresseLivraison adresse = new AdresseLivraison("1 rue de Paris", "Paris", "75001", "France"); + + private OrderInfo validOrder() { + return new OrderInfo( + clientId, + List.of(new LigneCommandeInfo(livreId, 1)), + adresse, + ModePaiement.CB + ); + } + + @Test + @DisplayName("Should validate order with valid data") + void testValidateValidOrder() { + assertDoesNotThrow(() -> OrderValidator.validate(validOrder())); + } + + @Nested + @DisplayName("ClientId validation tests") + class ClientIdValidationTests { + + @Test + @DisplayName("Should throw exception when clientId is null") + void testValidateNullClientId() { + OrderInfo order = new OrderInfo(null, List.of(new LigneCommandeInfo(livreId, 1)), adresse, ModePaiement.CB); + + NotValidOrderException exception = assertThrows(NotValidOrderException.class, + () -> OrderValidator.validate(order)); + + assertEquals(OrderValidator.CLIENT_ID_CANNOT_BE_NULL, exception.getMessage()); + } + } + + @Nested + @DisplayName("LignesCommande validation tests") + class LignesCommandeValidationTests { + + @Test + @DisplayName("Should throw exception when lignesCommande is empty") + void testValidateEmptyLignesCommande() { + OrderInfo order = new OrderInfo(clientId, List.of(), adresse, ModePaiement.CB); + + NotValidOrderException exception = assertThrows(NotValidOrderException.class, + () -> OrderValidator.validate(order)); + + assertEquals(OrderValidator.LIGNES_COMMANDE_CANNOT_BE_EMPTY, exception.getMessage()); + } + + @Test + @DisplayName("Should throw exception when lignesCommande is null") + void testValidateNullLignesCommande() { + OrderInfo order = new OrderInfo(clientId, null, adresse, ModePaiement.CB); + + NotValidOrderException exception = assertThrows(NotValidOrderException.class, + () -> OrderValidator.validate(order)); + + assertEquals(OrderValidator.LIGNES_COMMANDE_CANNOT_BE_EMPTY, exception.getMessage()); + } + + @Test + @DisplayName("Should throw exception when quantite is zero") + void testValidateZeroQuantite() { + OrderInfo order = new OrderInfo(clientId, List.of(new LigneCommandeInfo(livreId, 0)), adresse, ModePaiement.CB); + + NotValidOrderException exception = assertThrows(NotValidOrderException.class, + () -> OrderValidator.validate(order)); + + assertEquals(OrderValidator.QUANTITE_MUST_BE_POSITIVE, exception.getMessage()); + } + + @Test + @DisplayName("Should throw exception when quantite is negative") + void testValidateNegativeQuantite() { + OrderInfo order = new OrderInfo(clientId, List.of(new LigneCommandeInfo(livreId, -1)), adresse, ModePaiement.CB); + + NotValidOrderException exception = assertThrows(NotValidOrderException.class, + () -> OrderValidator.validate(order)); + + assertEquals(OrderValidator.QUANTITE_MUST_BE_POSITIVE, exception.getMessage()); + } + } + + @Nested + @DisplayName("AdresseLivraison validation tests") + class AdresseLivraisonValidationTests { + + @Test + @DisplayName("Should throw exception when adresseLivraison is null") + void testValidateNullAdresseLivraison() { + OrderInfo order = new OrderInfo(clientId, List.of(new LigneCommandeInfo(livreId, 1)), null, ModePaiement.CB); + + NotValidOrderException exception = assertThrows(NotValidOrderException.class, + () -> OrderValidator.validate(order)); + + assertEquals(OrderValidator.ADRESSE_LIVRAISON_CANNOT_BE_NULL, exception.getMessage()); + } + } + + @Nested + @DisplayName("ModePaiement validation tests") + class ModePaiementValidationTests { + + @Test + @DisplayName("Should throw exception when modePaiement is null") + void testValidateNullModePaiement() { + OrderInfo order = new OrderInfo(clientId, List.of(new LigneCommandeInfo(livreId, 1)), adresse, null); + + NotValidOrderException exception = assertThrows(NotValidOrderException.class, + () -> OrderValidator.validate(order)); + + assertEquals(OrderValidator.MODE_PAIEMENT_CANNOT_BE_NULL, exception.getMessage()); + } + + @Test + @DisplayName("Should validate with CB payment method") + void testValidateWithCB() { + OrderInfo order = new OrderInfo(clientId, List.of(new LigneCommandeInfo(livreId, 1)), adresse, ModePaiement.CB); + assertDoesNotThrow(() -> OrderValidator.validate(order)); + } + + @Test + @DisplayName("Should validate with PAYPAL payment method") + void testValidateWithPaypal() { + OrderInfo order = new OrderInfo(clientId, List.of(new LigneCommandeInfo(livreId, 1)), adresse, ModePaiement.PAYPAL); + assertDoesNotThrow(() -> OrderValidator.validate(order)); + } + + @Test + @DisplayName("Should validate with POINTS_FIDELITE payment method") + void testValidateWithPointsFidelite() { + OrderInfo order = new OrderInfo(clientId, List.of(new LigneCommandeInfo(livreId, 1)), adresse, ModePaiement.POINTS_FIDELITE); + assertDoesNotThrow(() -> OrderValidator.validate(order)); + } + } +} \ No newline at end of file diff --git a/src/test/resources/features/order.feature b/src/test/resources/features/order.feature new file mode 100644 index 0000000..8e5c7fb --- /dev/null +++ b/src/test/resources/features/order.feature @@ -0,0 +1,49 @@ +# language: en + +Feature: Place an order + + Background: + Given the system has the following customers for order: + | prenom | nom | numeroTelephone | pointsFidelite | + | Marie | Dupont | 0612345678 | 100 | + | Jean | Martin | 0687654321 | 50 | + And the catalog has the following books for order: + | isbn | titre | auteur | editeur | datePublication | prix | stockInitial | categories | description | langue | + | 9782016289308 | Le Petit Prince | Antoine de Saint-Exupery | Gallimard | 1943-04-06 | 12.90 | 10 | Roman | Un classique | FR | + | 9782070409189 | L Etranger | Albert Camus | Gallimard | 1942-05-19 | 9.50 | 5 | Roman | Philosophique| FR | + + Scenario: Place an order with CB payment + When I place an order for customer "0612345678" with: + | livreIsbn | quantite | rue | ville | codePostal | pays | modePaiement | + | 9782016289308 | 2 | 1 rue de Paris | Paris | 75001 | France | CB | + Then a new order is created + And the order total is 25.80 + And the customer "0612345678" now has 125 loyalty points + + Scenario: Place an order with PAYPAL payment + When I place an order for customer "0687654321" with: + | livreIsbn | quantite | rue | ville | codePostal | pays | modePaiement | + | 9782070409189 | 1 | 5 rue de Lyon | Lyon | 69001 | France | PAYPAL | + Then a new order is created + And the order total is 9.50 + And the customer "0687654321" now has 59 loyalty points + + Scenario: Attempt to place an order with no items + When I try to place an order for customer "0612345678" with no items: + | rue | ville | codePostal | pays | modePaiement | + | 1 rue de Paris | Paris | 75001 | France | CB | + Then the order placement fails + And I receive a validation order error containing "Order must have at least one item" + + Scenario: Attempt to place an order with invalid quantity + When I try to place an order for customer "0612345678" with invalid quantity: + | livreIsbn | quantite | rue | ville | codePostal | pays | modePaiement | + | 9782016289308 | 0 | 1 rue de Paris | Paris | 75001 | France | CB | + Then the order placement fails + And I receive a validation order error containing "Quantity must be positive" + + Scenario: Attempt to place an order for unknown customer + When I try to place an order for an unknown customer with: + | livreIsbn | quantite | rue | ville | codePostal | pays | modePaiement | + | 9782016289308 | 1 | 1 rue de Paris | Paris | 75001 | France | CB | + Then the order placement fails with customer not found