From d5049671e66dbb26f81f85815780e06f66c7175f Mon Sep 17 00:00:00 2001 From: Tom Moguljak Date: Thu, 12 Jun 2025 19:24:21 +0200 Subject: [PATCH] =?UTF-8?q?Sauvegarde=20de=20l'avanc=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev62/mylibrary/order/AddressDTO.java | 13 + .../but3/dev62/mylibrary/order/OrderDTO.java | 19 ++ .../dev62/mylibrary/order/OrderLineDTO.java | 11 + .../dev62/mylibrary/order/PaymentMethod.java | 6 + .../mylibrary/features/order/OrderSteps.java | 257 ++++++++++------- .../mylibrary/order/entity/OrderTest.java | 265 ++++++++++++++++++ .../exception/NotValidOrderExceptionTest.java | 57 ++++ .../exception/OrderNotFoundExceptionTest.java | 43 +++ .../exception/UserNotFoundExceptionTest.java | 43 +++ .../order/repository/OrderRepositoryTest.java | 195 +++++++++++++ src/test/resources/features/oder.feature | 48 +++- 11 files changed, 843 insertions(+), 114 deletions(-) create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/AddressDTO.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/OrderLineDTO.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/PaymentMethod.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/exception/UserNotFoundExceptionTest.java create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/repository/OrderRepositoryTest.java diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/AddressDTO.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/AddressDTO.java new file mode 100644 index 0000000..c55202e --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/AddressDTO.java @@ -0,0 +1,13 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class AddressDTO { + private final String street; + private final String city; + private final String postalCode; + private final String country; +} \ 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..212b5f8 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/OrderDTO.java @@ -0,0 +1,19 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; +import java.util.UUID; + +@Builder +@Getter +public class OrderDTO { + private final UUID id; + private final UUID customerId; + private final List orderLines; + private final double totalPrice; + private final double totalPriceToPay; + private final AddressDTO address; + private final PaymentMethod paymentMethod; +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/OrderLineDTO.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/OrderLineDTO.java new file mode 100644 index 0000000..63f77c3 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/OrderLineDTO.java @@ -0,0 +1,11 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class OrderLineDTO { + private final String bookId; + private final int quantity; +} \ No newline at end of file diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/PaymentMethod.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/PaymentMethod.java new file mode 100644 index 0000000..e7ccac9 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/PaymentMethod.java @@ -0,0 +1,6 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order; + +public enum PaymentMethod { + CREDIT_CARD, + LOYALTY_POINTS +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/order/OrderSteps.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/order/OrderSteps.java index 10b0eb0..2f1375f 100644 --- a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/order/OrderSteps.java +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/order/OrderSteps.java @@ -1,19 +1,15 @@ package fr.iut_fbleau.but3.dev62.mylibrary.features.order; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; -import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Order; -import fr.iut_fbleau.but3.dev62.mylibrary.order.OrderRepository; +import fr.iut_fbleau.but3.dev62.mylibrary.order.OrderLineDTO; import fr.iut_fbleau.but3.dev62.mylibrary.order.OrderDTO; +import fr.iut_fbleau.but3.dev62.mylibrary.order.AddressDTO; import fr.iut_fbleau.but3.dev62.mylibrary.order.OrderInfo; -import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.OrderNotFoundException; -import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.IllegalOrderPointException; +import fr.iut_fbleau.but3.dev62.mylibrary.order.repository.OrderRepository; import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.NotValidOrderException; -import fr.iut_fbleau.but3.dev62.mylibrary.order.usecase.OrderUseCase; +import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.OrderNotFoundException; +import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.UserNotFoundException; import fr.iut_fbleau.but3.dev62.mylibrary.customer.entity.Customer; import fr.iut_fbleau.but3.dev62.mylibrary.customer.repository.CustomerRepository; import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book; @@ -28,22 +24,22 @@ import java.util.*; import java.text.ParseException; import java.text.SimpleDateFormat; - public class OrderSteps { private final OrderRepository orderRepository = new OrderRepository(); - private final OrderUseCase orderUseCase = new OrderUseCase(orderRepository); - private NotValidOrderException notValidOrderException; - private IllegalOrderPointException illegalOrderPointException; - private OrderNotFoundException orderNotFoundException; - - private Optional orderById; - private UUID orderRegistration; - + private final OrderUserCase orderUserCase = new OrderUseCase(orderRepository); private final CustomerRepository customerRepository = new CustomerRepository(); private final Map customerPhoneUUID = new HashMap<>(); private final BookRepository bookRepository = new BookRepository(); - private final Map BookISBN = new HashMap<>(); - private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH); + private final Map bookISBN = new HashMap<>(); + + private UUID orderRegistration; + private Optional orderByUUID; + private Book updatedBook; + private Customer updatedCustomer; + + private Exception exception; + + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-mm-dd", Locale.ENGLISH); private ArrayList listOfStrings(String arg) { return new ArrayList(Arrays.asList(arg.split(",\\s"))); @@ -74,13 +70,13 @@ public class OrderSteps { .language(book.get("langue")) .build(); Book save = bookRepository.save(newBook); - BookISBN.put(ISBN, save.getIsbn()); + bookISBN.put(ISBN, save.getIsbn()); } assertEquals(books.size(), bookRepository.findAll().size()); } - @Given("the system has the following customers:") + @And("the system has the following customers:") public void theSystemHasTheFollowingCustomers(DataTable dataTable) { int size = customerRepository.findAll().size(); @@ -106,145 +102,198 @@ public class OrderSteps { } @When("I create a new order with the following information:") - public void iCreateANewOrderWithTheFollowingInformation(DataTable dataTable) throws NotValidOrderException { - List> rows = dataTable.asMaps(String.class, String.class); - - // Extract the first row of data - Map orderData = rows.getFirst(); - - // Create a new OrderInfo object with the correct keys + public void iCreateANewOrderWithTheFollowingInformation(DataTable dataTable) { + Map orderData = dataTable.asMaps(String.class, String.class).getFirst(); OrderInfo newOrder = new OrderInfo( UUID.fromString(orderData.get("customerId")), - orderData.get("paymentMethod"), - Integer.parseInt(orderData.get("loyaltyPoints")) + orderData.get("paymentMethod") ); - // Register the order - orderRegistration = orderUseCase.registerOrder(newOrder); + try{ + orderRegistration = orderUseCase.registerOrder(newOrder); + exception = null; + } catch (Exception e) { + exception = e; + orderRegistration = null; + } } @And("the order includes the following books:") - public void theOrderIncludesTheFollowingBooks(DataTable dataTable) { + public void theOrderIncludesTheFollowingBooks(DataTable dataTable){ List> books = dataTable.asMaps(String.class, String.class); + List orderLines = new ArrayList<>(); + for (Map book : books) { - String isbn = book.get("isbn"); + String isbn = book.get("ISBN"); int quantity = Integer.parseInt(book.get("quantity")); - Book bookEntity = bookRepository.findByISBN(BookISBN.get(isbn)) - .orElseThrow(() -> new IllegalArgumentException("Book not found: " + isbn)); - - double total = bookEntity.getPrice() * quantity; - orderUseCase.addBookToOrder(orderRegistration, bookEntity, quantity, total); + orderLines.add(OrderLineDTO.builder() + .bookId(isbn) + .quantity(quantity) + .build()); } + + try { + orderUseCase.addBooksToOrder(orderRegistration, orderLines); + exception = null; + } catch (Exception e) { + exception = e; + orderRegistration = null; + } + } @And("the delivery address is:") public void theDeliveryAddressIs(DataTable dataTable) { - List> addressData = dataTable.asMaps(String.class, String.class); - if (addressData.isEmpty()) { - throw new IllegalArgumentException("Address data cannot be empty"); - } - Map address = addressData.get(0); - String street = address.get("street"); - String city = address.get("city"); - String postalCode = address.get("postalCode"); - String country = address.get("country"); + Map addressData = dataTable.asMaps(String.class, String.class).getFirst(); + AddressDTO address = AddressDTO.builder() + .street(addressData.get("street")) + .city(addressData.get("city")) + .postalCode(addressData.get("postalCode")) + .country(addressData.get("country")) + .build(); - orderUseCase.setDeliveryAddress(orderRegistration, street, city, postalCode, country); + try { + orderUseCase.setDeliveryAddress(orderRegistration, address); + exception = null; + } catch (Exception e) { + exception = e; + orderRegistration = null; + } } @Then("a new order is created") public void aNewOrderIsCreated() { + assertNull(exception, "No exception should be thrown during order creation"); assertNotNull(orderRegistration); } @And("the total price is {double}") - public void theTotalPriceIs(double price) { - Order order = orderRepository.findById(orderRegistration); - assertEquals(price, order.getTotalPrice()); - } - - @And("the loyalty points remain unchanged at {int}") - public void theLoyaltyPointsRemainUnchanged(int loyaltyPoints){ - Order order = orderRepository.findById(orderRegistration); - Customer customer = customerRepository.findById(order.getCustomerId()) - .orElseThrow(() -> new IllegalArgumentException("Customer not found")); - assertEquals(loyaltyPoints, customer.getLoyaltyPoints()); + public void theTotalPriceIs(double expectedPrice) { + Order order = orderRepository.findById(orderRegistration) + .orElseThrow(() -> new Exception("Order not found")); + double totalPrice = order.getTotalPrice(); + assertEquals(expectedPrice, totalPrice, "The total price of the order should match the expected price"); } @And("{int} loyalty points are deducted") public void loyaltyPointsAreDeducted(int loyaltyPoints) { - Order order = orderRepository.findById(orderRegistration); + Order order = orderRepository.findById(orderRegistration).orElseThrow(); Customer customer = customerRepository.findById(order.getCustomerId()) - .orElseThrow(() -> new IllegalArgumentException("Customer not found")); - assertEquals(customer.getLoyaltyPoints(), order.getLoyaltyPoints() - loyaltyPoints); + .orElseThrow(() -> new Exception("Customer not found")); + int remainingPoints = customer.getLoyaltyPoints() - loyaltyPoints; + assertEquals(remainingPoints, customer.getLoyaltyPoints(), "The customer's loyalty points should be deducted correctly"); } @And("I receive an error for validation order message containing {string}") public void iReceiveAnErrorForValidationOrderMessageContaining(String errorMessage) { - assertEquals(errorMessage, notValidOrderException.getMessage()); - } - - @When("I create a new order with an unknown customer:") - public void iCreateANewOrderWithAnUnknownCustomer() { - + assertNotNull(exception, "An exception should be thrown during order creation"); + assertInstanceOf(NotValidOrderException.class, exception, "The exception should be of type NotValidOrderException"); + assertEquals(errorMessage, exception.getMessage(), "The error message should match the expected message"); } @And("I receive an error for not found exception message containing {string}") public void iReceiveAnErrorForNotFoundExceptionMessageContaining(String errorMessage) { - assertEquals(errorMessage, orderNotFoundException.getMessage()); + assertNotNull(exception, "An exception should be thrown during order retrieval"); + assertInstanceOf(OrderNotFoundException.class, exception, "The exception should be of type OrderNotFoundException"); + assertEquals(errorMessage, exception.getMessage(), "The error message should match the expected message"); } - @When("I create a new order with insufficient loyalty points:") - public void iCreateANewOrderWithInsufficientLoyaltyPoints() { - } - - @And("I receive an error for illegal order exception message containing {string}") + @And("I receive an error for not found user exception message containing {string}") public void iReceiveAnErrorForIllegalOrderExceptionMessageContaining(String errorMessage) { - assertEquals(errorMessage, illegalOrderPointException.getMessage()); - } - - @When("I create a new order with an invalid payment method:") - public void iCreateANewOrderWithAnInvalidPaymentMethod() { + assertNotNull(exception, "An exception should be thrown during user processing"); + assertInstanceOf(UserNotFoundException.class, exception, "The exception should be of type UserNotFoundException"); + assertEquals(errorMessage, exception.getMessage(), "The error message should match the expected message"); } @And("the order includes no books") public void theOrderIncludesNoBooks() { + List orderLines = new ArrayList<>(); + + try { + orderUseCase.addBooksToOrder(orderRegistration, orderLines); + exception = null; + } catch (Exception e) { + exception = e; + orderRegistration = null; + } } @Given("an order with ID {string} exists for customer {string}") - public void anOrderWithIDExistsForCustomer(String arg0, String arg1) { + public void anOrderWithIDExistsForCustomer(String orderId, String customerId) { + UUID orderUUID = UUID.fromString(orderId); + UUID customerUUID = UUID.fromString(customerId); + + Order order = Order.builder() + .id(orderUUID) + .customerId(customerUUID) + .orderLines(new ArrayList() {{ + add(OrderLineDTO.builder() + .bookId("1234567890123") + .quantity(2) + .build()); + add(OrderLineDTO.builder() + .bookId("9876543210987") + .quantity(1) + .build()); + }}) + .totalPrice(60.0) + .totalPriceToPay(60.0) + .address(AddressDTO.builder() + .street("123 Main St") + .city("Springfield") + .postalCode("12345") + .country("USA") + .build()) + .paymentMethod("CREDIT_CARD") + .build(); + + + orderRepository.save(order); } @When("I retrieve the order by ID {string}") - public void iRetrieveTheOrderByID(String arg0) { - } - - @When("I request all orders for customer {string}") - public void iRequestAllOrdersForCustomer(String arg0) { - } - - @Then("the retrieval fails") - public void theRetrievalFails() { - } - - @And("I try to set the delivery address to:") - public void iTryToSetTheDeliveryAddressTo() { - } - - @And("I try to order more books than available stock:") - public void iTryToOrderMoreBooksThanAvailableStock() { - } - - @And("I try to order a book that does not exist:") - public void iTryToOrderABookThatDoesNotExist() { + public void iRetrieveTheOrderByID(String orderId) { + UUID orderUUID = UUID.fromString(orderId); + try { + orderByUUID = orderUseCase.findOrderById(orderUUID); + exception = null; + } catch (Exception e) { + exception = e; + orderByUUID = Optional.empty(); + } } @Then("I receive the order details") public void iReceiveTheOrderDetails() { + assertTrue(orderByUUID.isPresent(), "The order should be found by ID"); + OrderDTO order = orderByUUID.get(); + assertNotNull(order, "The retrieved order should not be null"); + assertEquals(orderRegistration, order.getId(), "The retrieved order ID should match the expected ID"); + } + + @When("I request all orders for customer {string}") + public void iRequestAllOrdersForCustomer(String customerId) { + UUID customerUUID = UUID.fromString(customerId); + + List orders; + try { + orders = orderUseCase.findOrdersByCustomerId(customerUUID); + exception = null; + } catch (Exception e) { + exception = e; + orders = Collections.emptyList(); + } } @Then("I receive a list of orders") public void iReceiveAListOfOrders() { + assertNull(exception, "No exception should be thrown during order retrieval"); + assertNotNull(orders, "The list of orders should not be null"); + } + + @Then("the retrieval fails") + public void theRetrievalFails() { + assertNotNull(exception, "An exception should be thrown during order retrieval"); } } 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..cb668d1 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/OrderTest.java @@ -0,0 +1,265 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order.entity; + +import fr.iut_fbleau.but3.dev62.mylibrary.order.*; +import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.NotValidOrderException; +import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.OrderNotFoundException; +import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.UserNotFoundException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import java.util.*; +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 customerId = UUID.randomUUID(); + List orderLines = List.of( + OrderLineDTO.builder().bookId("1234567890123").quantity(2).build(), + OrderLineDTO.builder().bookId("9876543210123").quantity(1).build() + ); + double totalPrice = 89.98; + double totalPriceToPay = 89.98; + AddressDTO address = AddressDTO.builder() + .street("12 Main St.") + .city("Paris") + .postalCode("75000") + .country("France") + .build(); + PaymentMethod paymentMethod = PaymentMethod.CREDIT_CARD; + + OrderDTO order = OrderDTO.builder() + .id(id) + .customerId(customerId) + .orderLines(orderLines) + .totalPrice(totalPrice) + .totalPriceToPay(totalPriceToPay) + .address(address) + .paymentMethod(paymentMethod) + .build(); + + assertEquals(id, order.getId()); + assertEquals(customerId, order.getCustomerId()); + assertEquals(orderLines, order.getOrderLines()); + assertEquals(totalPrice, order.getTotalPrice()); + assertEquals(totalPriceToPay, order.getTotalPriceToPay()); + assertEquals(address, order.getAddress()); + assertEquals(paymentMethod, order.getPaymentMethod()); + } + + @Nested + @DisplayName("Order business logic tests") + class OrderBusinessLogicTests { + @Test + @DisplayName("Total price should be the sum of order lines") + void testTotalPriceCalculation() { + List orderLines = List.of( + OrderLineDTO.builder().bookId("1234567890123").quantity(2).build(), + OrderLineDTO.builder().bookId("9876543210123").quantity(1).build() + ); + // Supposons prix 39.99 et 49.99 + double price1 = 39.99; + double price2 = 49.99; + double expectedTotal = 2 * price1 + 1 * price2; + + // Ici, on simule la logique métier (à adapter selon l'implémentation réelle) + double total = 2 * price1 + 1 * price2; + assertEquals(expectedTotal, total); + } + + @Test + @DisplayName("Order with no lines should be invalid") + void testOrderWithNoLines() { + List emptyLines = List.of(); + assertTrue(emptyLines.isEmpty(), "La liste des lignes de commande doit être vide"); + } + } + + @Test + @DisplayName("Delivery address should be properly set") + void testOrderAddress() { + AddressDTO address = AddressDTO.builder() + .street("42 Book Street") + .city("Lyon") + .postalCode("69000") + .country("France") + .build(); + assertEquals("42 Book Street", address.getStreet()); + assertEquals("Lyon", address.getCity()); + assertEquals("69000", address.getPostalCode()); + assertEquals("France", address.getCountry()); + } + + @Test + @DisplayName("Payment method should be correct") + void testOrderPaymentMethod() { + PaymentMethod method = PaymentMethod.LOYALTY_POINTS; + assertEquals(PaymentMethod.LOYALTY_POINTS, method); + } + + @Nested + @DisplayName("Order business rules and validation") + class OrderBusinessRules { + @Test + @DisplayName("Create order with credit card - success") + void testCreateOrderWithCreditCard() { + // Arrange + UUID customerId = UUID.fromString("11111111-1111-1111-1111-111111111111"); + List orderLines = List.of( + OrderLineDTO.builder().bookId("1234567890123").quantity(2).build() + ); + AddressDTO address = AddressDTO.builder() + .street("12 Main St.").city("Paris").postalCode("75000").country("France").build(); + double expectedTotal = 79.98; + // Act + OrderDTO order = OrderDTO.builder() + .id(UUID.randomUUID()) + .customerId(customerId) + .orderLines(orderLines) + .totalPrice(expectedTotal) + .totalPriceToPay(expectedTotal) + .address(address) + .paymentMethod(PaymentMethod.CREDIT_CARD) + .build(); + // Assert + assertEquals(expectedTotal, order.getTotalPrice()); + assertEquals(PaymentMethod.CREDIT_CARD, order.getPaymentMethod()); + assertEquals(customerId, order.getCustomerId()); + } + + @Test + @DisplayName("Create order with loyalty points - success") + void testCreateOrderWithLoyaltyPoints() { + UUID customerId = UUID.fromString("11111111-1111-1111-1111-111111111111"); + List orderLines = List.of( + OrderLineDTO.builder().bookId("9876543210123").quantity(1).build() + ); + AddressDTO address = AddressDTO.builder() + .street("42 Book Street").city("Lyon").postalCode("69000").country("France").build(); + double expectedTotal = 49.99; + OrderDTO order = OrderDTO.builder() + .id(UUID.randomUUID()) + .customerId(customerId) + .orderLines(orderLines) + .totalPrice(expectedTotal) + .totalPriceToPay(0.0) + .address(address) + .paymentMethod(PaymentMethod.LOYALTY_POINTS) + .build(); + assertEquals(expectedTotal, order.getTotalPrice()); + assertEquals(PaymentMethod.LOYALTY_POINTS, order.getPaymentMethod()); + } + + @Test + @DisplayName("Order with multiple books - total price calculation") + void testOrderWithMultipleBooks() { + List orderLines = List.of( + OrderLineDTO.builder().bookId("1234567890123").quantity(3).build(), + OrderLineDTO.builder().bookId("9876543210123").quantity(4).build() + ); + double price1 = 39.99, price2 = 49.99; + double expectedTotal = 3 * price1 + 4 * price2; + double total = 3 * price1 + 4 * price2; + assertEquals(expectedTotal, total); + } + + @Test + @DisplayName("Order with invalid address - should throw NotValidOrderException") + void testOrderWithInvalidAddress() { + AddressDTO address = AddressDTO.builder().street("").city("").postalCode("").country("").build(); + Exception exception = assertThrows(NotValidOrderException.class, () -> { + if (address.getStreet().isEmpty() || address.getCity().isEmpty() || address.getPostalCode().isEmpty() || address.getCountry().isEmpty()) { + throw new NotValidOrderException("Address fields are required"); + } + }); + assertTrue(exception.getMessage().contains("Address fields are required")); + } + + @Test + @DisplayName("Order with unknown customer - should throw UserNotFoundException") + void testOrderWithUnknownCustomer() { + UUID unknownCustomerId = UUID.fromString("00000000-0000-0000-0000-000000000000"); + Exception exception = assertThrows(UserNotFoundException.class, () -> { + throw new UserNotFoundException("Customer not found"); + }); + assertTrue(exception.getMessage().contains("Customer not found")); + } + + @Test + @DisplayName("Order with insufficient loyalty points - should throw NotValidOrderException") + void testOrderWithInsufficientLoyaltyPoints() { + int availablePoints = 10; + double orderPrice = 49.99; + Exception exception = assertThrows(NotValidOrderException.class, () -> { + if (orderPrice > availablePoints) { + throw new NotValidOrderException("Not enough loyalty points"); + } + }); + assertTrue(exception.getMessage().contains("Not enough loyalty points")); + } + + @Test + @DisplayName("Order with quantity greater than stock - should throw NotValidOrderException") + void testOrderWithInsufficientStock() { + int stock = 10; + int requested = 50; + Exception exception = assertThrows(NotValidOrderException.class, () -> { + if (requested > stock) { + throw new NotValidOrderException("Insufficient book stock"); + } + }); + assertTrue(exception.getMessage().contains("Insufficient book stock")); + } + + @Test + @DisplayName("Order with invalid payment method - should throw NotValidOrderException") + void testOrderWithInvalidPaymentMethod() { + String invalidMethod = "UNKNOWN"; + Exception exception = assertThrows(NotValidOrderException.class, () -> { + try { + PaymentMethod.valueOf(invalidMethod); + } catch (IllegalArgumentException e) { + throw new NotValidOrderException("Payment method is not valid"); + } + }); + assertTrue(exception.getMessage().contains("Payment method is not valid")); + } + + @Test + @DisplayName("Order with unknown book - should throw OrderNotFoundException") + void testOrderWithUnknownBook() { + String unknownBookId = "unknownBookId"; + Exception exception = assertThrows(OrderNotFoundException.class, () -> { + throw new OrderNotFoundException("Book not found"); + }); + assertTrue(exception.getMessage().contains("Book not found")); + } + + @Test + @DisplayName("Order with empty book list - should throw NotValidOrderException") + void testOrderWithEmptyBookList() { + List emptyLines = List.of(); + Exception exception = assertThrows(NotValidOrderException.class, () -> { + if (emptyLines.isEmpty()) { + throw new NotValidOrderException("Book list cannot be empty"); + } + }); + assertTrue(exception.getMessage().contains("Book list cannot be empty")); + } + + @Test + @DisplayName("Order with negative quantity - should throw NotValidOrderException") + void testOrderWithNegativeQuantity() { + int quantity = -1; + Exception exception = assertThrows(NotValidOrderException.class, () -> { + if (quantity < 0) { + throw new NotValidOrderException("Quantity must be positive"); + } + }); + assertTrue(exception.getMessage().contains("Quantity must be positive")); + } + } +} 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..5aa2fd1 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/NotValidOrderExceptionTest.java @@ -0,0 +1,57 @@ +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 = { + "Address fields are required", + "Book list cannot be empty", + "Quantity must be positive", + "Not enough loyalty points", + "Insufficient book stock", + "Payment method is not valid" + }) + @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 = "Required field is missing"; + 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 = "Invalid order data"; + try { + throw new NotValidOrderException(errorMessage); + } catch (Exception e) { + assertEquals(NotValidOrderException.class, e.getClass()); + assertEquals(errorMessage, e.getMessage()); + } + } +} 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..756f132 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/OrderNotFoundExceptionTest.java @@ -0,0 +1,43 @@ +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); + String expectedFormatWithPlaceholder = "The order with id {0} does not exist"; + assertEquals(OrderNotFoundException.THE_ORDER_WITH_ID_DOES_NOT_EXIST_MESSAGE, expectedFormatWithPlaceholder); + 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()); + } + } +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/UserNotFoundExceptionTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/UserNotFoundExceptionTest.java new file mode 100644 index 0000000..d5e4415 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/UserNotFoundExceptionTest.java @@ -0,0 +1,43 @@ +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 UserNotFoundExceptionTest { + + @Test + @DisplayName("Exception message should contain the UUID provided") + void testExceptionMessageContainsUUID() { + UUID uuid = UUID.randomUUID(); + UserNotFoundException exception = new UserNotFoundException(uuid); + String expectedMessage = String.format("The customer 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(); + UserNotFoundException exception = new UserNotFoundException(uuid); + String expectedFormatWithPlaceholder = "The customer with id {0} does not exist"; + assertEquals(UserNotFoundException.THE_CUSTOMER_WITH_ID_DOES_NOT_EXIST_MESSAGE, expectedFormatWithPlaceholder); + assertTrue(exception.getMessage().contains(uuid.toString())); + } + + @Test + @DisplayName("Exception should be properly thrown and caught") + void testExceptionCanBeThrownAndCaught() { + UUID uuid = UUID.randomUUID(); + try { + throw new UserNotFoundException(uuid); + } catch (UserNotFoundException e) { + String expectedMessage = String.format("The customer with id %s does not exist", uuid); + assertEquals(expectedMessage, e.getMessage()); + } + } +} 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..769b8a0 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/repository/OrderRepositoryTest.java @@ -0,0 +1,195 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.order.repository; + +import fr.iut_fbleau.but3.dev62.mylibrary.order.*; +import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.NotValidOrderException; +import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.OrderNotFoundException; +import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.UserNotFoundException; +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.util.*; +import static org.junit.jupiter.api.Assertions.*; + +class OrderRepositoryTest { + + private OrderRepository repository; + private OrderDTO order1; + private OrderDTO order2; + private UUID customerId1; + private UUID customerId2; + + @BeforeEach + void setUp() { + repository = new OrderRepository(); + customerId1 = UUID.fromString("11111111-1111-1111-1111-111111111111"); + customerId2 = UUID.fromString("22222222-2222-2222-2222-222222222222"); + order1 = OrderDTO.builder() + .id(UUID.randomUUID()) + .customerId(customerId1) + .orderLines(List.of(OrderLineDTO.builder().bookId("1234567890123").quantity(2).build())) + .totalPrice(79.98) + .totalPriceToPay(79.98) + .address(AddressDTO.builder().street("12 Main St.").city("Paris").postalCode("75000").country("France").build()) + .paymentMethod(PaymentMethod.CREDIT_CARD) + .build(); + order2 = OrderDTO.builder() + .id(UUID.randomUUID()) + .customerId(customerId2) + .orderLines(List.of(OrderLineDTO.builder().bookId("9876543210123").quantity(1).build())) + .totalPrice(49.99) + .totalPriceToPay(0.0) + .address(AddressDTO.builder().street("42 Book Street").city("Lyon").postalCode("69000").country("France").build()) + .paymentMethod(PaymentMethod.LOYALTY_POINTS) + .build(); + } + + @Test + @DisplayName("New repository should be empty") + void testNewRepositoryIsEmpty() { + List orders = repository.findAll(); + assertTrue(orders.isEmpty()); + assertEquals(0, orders.size()); + } + + @Nested + @DisplayName("Save operations") + class SaveOperations { + @Test + @DisplayName("Save should add a new order") + void testSaveNewOrder() { + OrderDTO savedOrder = repository.save(order1); + assertEquals(1, repository.findAll().size()); + assertEquals(order1.getId(), savedOrder.getId()); + assertEquals(order1.getCustomerId(), savedOrder.getCustomerId()); + } + + @Test + @DisplayName("Save multiple orders should add all of them") + void testSaveMultipleOrders() { + repository.save(order1); + repository.save(order2); + List orders = repository.findAll(); + assertEquals(2, orders.size()); + assertTrue(orders.contains(order1)); + assertTrue(orders.contains(order2)); + } + } + + @Nested + @DisplayName("Find operations") + class FindOperations { + @BeforeEach + void setUpOrders() { + repository.save(order1); + repository.save(order2); + } + + @Test + @DisplayName("FindById should return order with matching ID") + void testFindById() { + Optional foundOrder = repository.findById(order1.getId()); + assertTrue(foundOrder.isPresent()); + assertEquals(order1.getCustomerId(), foundOrder.get().getCustomerId()); + } + + @Test + @DisplayName("FindById should throw OrderNotFoundException when ID doesn't exist") + void testFindByIdNotFound() { + UUID nonExistentId = UUID.randomUUID(); + assertThrows(OrderNotFoundException.class, () -> { + repository.findByIdOrThrow(nonExistentId); + }); + } + + @Test + @DisplayName("FindByCustomerId should return all orders for a customer") + void testFindByCustomerId() { + List orders = repository.findByCustomerId(customerId1); + assertFalse(orders.isEmpty()); + assertTrue(orders.stream().allMatch(o -> o.getCustomerId().equals(customerId1))); + } + + @Test + @DisplayName("FindByCustomerId should throw UserNotFoundException when customer doesn't exist") + void testFindByCustomerIdNotFound() { + UUID nonExistentCustomer = UUID.randomUUID(); + assertThrows(UserNotFoundException.class, () -> { + repository.findByCustomerIdOrThrow(nonExistentCustomer); + }); + } + } + + @Nested + @DisplayName("Validation and error handling") + class ValidationAndErrorHandling { + @Test + @DisplayName("Save should throw NotValidOrderException for invalid address") + void testSaveInvalidAddress() { + OrderDTO invalidOrder = OrderDTO.builder() + .id(UUID.randomUUID()) + .customerId(customerId1) + .orderLines(List.of(OrderLineDTO.builder().bookId("1234567890123").quantity(1).build())) + .totalPrice(39.99) + .totalPriceToPay(39.99) + .address(AddressDTO.builder().street("").city("").postalCode("").country("").build()) + .paymentMethod(PaymentMethod.CREDIT_CARD) + .build(); + assertThrows(NotValidOrderException.class, () -> { + repository.saveOrThrow(invalidOrder); + }); + } + + @Test + @DisplayName("Save should throw NotValidOrderException for empty book list") + void testSaveEmptyBookList() { + OrderDTO invalidOrder = OrderDTO.builder() + .id(UUID.randomUUID()) + .customerId(customerId1) + .orderLines(List.of()) + .totalPrice(0.0) + .totalPriceToPay(0.0) + .address(AddressDTO.builder().street("12 Main St.").city("Paris").postalCode("75000").country("France").build()) + .paymentMethod(PaymentMethod.CREDIT_CARD) + .build(); + assertThrows(NotValidOrderException.class, () -> { + repository.saveOrThrow(invalidOrder); + }); + } + + @Test + @DisplayName("Save should throw NotValidOrderException for negative quantity") + void testSaveNegativeQuantity() { + OrderDTO invalidOrder = OrderDTO.builder() + .id(UUID.randomUUID()) + .customerId(customerId1) + .orderLines(List.of(OrderLineDTO.builder().bookId("1234567890123").quantity(-1).build())) + .totalPrice(-39.99) + .totalPriceToPay(-39.99) + .address(AddressDTO.builder().street("12 Main St.").city("Paris").postalCode("75000").country("France").build()) + .paymentMethod(PaymentMethod.CREDIT_CARD) + .build(); + assertThrows(NotValidOrderException.class, () -> { + repository.saveOrThrow(invalidOrder); + }); + } + + @Test + @DisplayName("Save should throw NotValidOrderException for null payment method") + void testSaveNullPaymentMethod() { + OrderDTO invalidOrder = OrderDTO.builder() + .id(UUID.randomUUID()) + .customerId(customerId1) + .orderLines(List.of(OrderLineDTO.builder().bookId("1234567890123").quantity(1).build())) + .totalPrice(39.99) + .totalPriceToPay(39.99) + .address(AddressDTO.builder().street("12 Main St.").city("Paris").postalCode("75000").country("France").build()) + .paymentMethod(null) // Invalid payment method + .build(); + assertThrows(NotValidOrderException.class, () -> { + repository.saveOrThrow(invalidOrder); + }); + } + } +} + diff --git a/src/test/resources/features/oder.feature b/src/test/resources/features/oder.feature index 31dabd3..678ba98 100644 --- a/src/test/resources/features/oder.feature +++ b/src/test/resources/features/oder.feature @@ -42,6 +42,21 @@ Feature: Manage customer orders And 49 loyalty points are deducted And customer "11111111-1111-1111-1111-111111111111" now has 51 loyalty points + Scenario: Create an order with multiple books + When I create a new order with the following information: + | customerId | paymentMethod | + | 11111111-1111-1111-1111-111111111111 | CREDIT_CARD | + And the order includes the following books: + | bookId | quantity | + | 1234567890123 | 3 | + | 9876543210123 | 4 | + And the delivery address is: + | street | city | postalCode | country | + | 12 Main St. | Paris | 75000 | France | + Then a new order is created + And the total price is 239.92 + And customer "11111111-1111-1111-1111-111111111111" now has 100 loyalty points + Scenario: Attempt to create an order with invalid address When I create a new order with the following information: | customerId | paymentMethod | @@ -49,14 +64,14 @@ Feature: Manage customer orders And the order includes the following books: | bookId | quantity | | 1234567890123 | 1 | - And I try to set the delivery address to: + And the delivery address is: | street | city | postalCode | country | | | | | | Then the creation fails And I receive an error for validation order message containing "Address fields are required" Scenario: Attempt to create an order with unknown customer - When I create a new order with an unknown customer: + When I create a new order with the following information: | customerId | paymentMethod | | 00000000-0000-0000-0000-000000000000 | CREDIT_CARD | And the order includes the following books: @@ -66,10 +81,10 @@ Feature: Manage customer orders | street | city | postalCode | country | | 12 Main St. | Paris | 75000 | France | Then the creation fails - And I receive an error for not found exception message containing "Customer not found" + And I receive an error for not found user exception message containing "Customer not found" Scenario: Attempt to create an order with insufficient loyalty points - When I create a new order with insufficient loyalty points: + When I create a new order with the following information: | customerId | paymentMethod | | 22222222-2222-2222-2222-222222222222 | LOYALTY_POINTS | And the order includes the following books: @@ -79,23 +94,23 @@ Feature: Manage customer orders | street | city | postalCode | country | | 42 Book Street | Lyon | 69000 | France | Then the creation fails - And I receive an error for illegal order exception message containing "Not enough loyalty points" + And I receive an error for validation order message containing "Not enough loyalty points" Scenario: Attempt to order more books than available stock When I create a new order with the following information: | customerId | paymentMethod | | 11111111-1111-1111-1111-111111111111 | CREDIT_CARD | - And I try to order more books than available stock: + And the order includes the following books: | bookId | quantity | | 1234567890123 | 50 | And the delivery address is: | street | city | postalCode | country | | 12 Main St. | Paris | 75000 | France | Then the creation fails - And I receive an error for illegal order exception message containing "Insufficient book stock" + And I receive an error for validation order message containing "Insufficient book stock" Scenario: Attempt to create an order with invalid payment method - When I create a new order with an invalid payment method: + When I create a new order with the following information: | customerId | paymentMethod | | 11111111-1111-1111-1111-111111111111 | UNKNOWN | And the order includes the following books: @@ -111,7 +126,7 @@ Feature: Manage customer orders When I create a new order with the following information: | customerId | paymentMethod | | 11111111-1111-1111-1111-111111111111 | CREDIT_CARD | - And I try to order a book that does not exist: + And the order includes the following books: | bookId | quantity | | unknownBookId | 1 | And the delivery address is: @@ -131,6 +146,19 @@ Feature: Manage customer orders Then the creation fails And I receive an error for validation order message containing "Book list cannot be empty" + Scenario: Attempt to create an order with a negative quantity + When I create a new order with the following information: + | customerId | paymentMethod | + | 11111111-1111-1111-1111-111111111111 | CREDIT_CARD | + And the order includes the following books: + | bookId | quantity | + | 1234567890123 | -1 | + And the delivery address is: + | street | city | postalCode | country | + | 12 Main St. | Paris | 75000 | France | + Then the creation fails + And I receive an error for validation order message containing "Quantity must be positive" + #Get orders Scenario: Retrieve an order by ID @@ -150,4 +178,4 @@ Feature: Manage customer orders Scenario: Attempt to retrieve orders for an unknown customer When I request all orders for customer "00000000-0000-0000-0000-000000000000" Then the retrieval fails - And I receive an error for not found exception message containing "Customer not found" + And I receive an error for not found user exception message containing "Customer not found"