Sauvegarde de l'avancée

This commit is contained in:
2025-06-12 19:24:21 +02:00
parent 7d31067021
commit d5049671e6
11 changed files with 843 additions and 114 deletions

View File

@@ -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;
}

View File

@@ -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<OrderLineDTO> orderLines;
private final double totalPrice;
private final double totalPriceToPay;
private final AddressDTO address;
private final PaymentMethod paymentMethod;
}

View File

@@ -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;
}

View File

@@ -0,0 +1,6 @@
package fr.iut_fbleau.but3.dev62.mylibrary.order;
public enum PaymentMethod {
CREDIT_CARD,
LOYALTY_POINTS
}

View File

@@ -1,19 +1,15 @@
package fr.iut_fbleau.but3.dev62.mylibrary.features.order; package fr.iut_fbleau.but3.dev62.mylibrary.features.order;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.*;
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 fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Order; import fr.iut_fbleau.but3.dev62.mylibrary.order.OrderLineDTO;
import fr.iut_fbleau.but3.dev62.mylibrary.order.OrderRepository;
import fr.iut_fbleau.but3.dev62.mylibrary.order.OrderDTO; 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.OrderInfo;
import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.OrderNotFoundException; import fr.iut_fbleau.but3.dev62.mylibrary.order.repository.OrderRepository;
import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.IllegalOrderPointException;
import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.NotValidOrderException; 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.entity.Customer;
import fr.iut_fbleau.but3.dev62.mylibrary.customer.repository.CustomerRepository; import fr.iut_fbleau.but3.dev62.mylibrary.customer.repository.CustomerRepository;
import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book; import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book;
@@ -28,22 +24,22 @@ import java.util.*;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
public class OrderSteps { public class OrderSteps {
private final OrderRepository orderRepository = new OrderRepository(); private final OrderRepository orderRepository = new OrderRepository();
private final OrderUseCase orderUseCase = new OrderUseCase(orderRepository); private final OrderUserCase orderUserCase = new OrderUseCase(orderRepository);
private NotValidOrderException notValidOrderException;
private IllegalOrderPointException illegalOrderPointException;
private OrderNotFoundException orderNotFoundException;
private Optional<OrderDTO> orderById;
private UUID orderRegistration;
private final CustomerRepository customerRepository = new CustomerRepository(); private final CustomerRepository customerRepository = new CustomerRepository();
private final Map<String, UUID> customerPhoneUUID = new HashMap<>(); private final Map<String, UUID> customerPhoneUUID = new HashMap<>();
private final BookRepository bookRepository = new BookRepository(); private final BookRepository bookRepository = new BookRepository();
private final Map<String, String> BookISBN = new HashMap<>(); private final Map<String, String> bookISBN = new HashMap<>();
private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
private UUID orderRegistration;
private Optional<OrderDTO> orderByUUID;
private Book updatedBook;
private Customer updatedCustomer;
private Exception exception;
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-mm-dd", Locale.ENGLISH);
private ArrayList<String> listOfStrings(String arg) { private ArrayList<String> listOfStrings(String arg) {
return new ArrayList<String>(Arrays.asList(arg.split(",\\s"))); return new ArrayList<String>(Arrays.asList(arg.split(",\\s")));
@@ -74,13 +70,13 @@ public class OrderSteps {
.language(book.get("langue")) .language(book.get("langue"))
.build(); .build();
Book save = bookRepository.save(newBook); Book save = bookRepository.save(newBook);
BookISBN.put(ISBN, save.getIsbn()); bookISBN.put(ISBN, save.getIsbn());
} }
assertEquals(books.size(), bookRepository.findAll().size()); 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) { public void theSystemHasTheFollowingCustomers(DataTable dataTable) {
int size = customerRepository.findAll().size(); int size = customerRepository.findAll().size();
@@ -106,145 +102,198 @@ public class OrderSteps {
} }
@When("I create a new order with the following information:") @When("I create a new order with the following information:")
public void iCreateANewOrderWithTheFollowingInformation(DataTable dataTable) throws NotValidOrderException { public void iCreateANewOrderWithTheFollowingInformation(DataTable dataTable) {
List<Map<String, String>> rows = dataTable.asMaps(String.class, String.class); Map<String, String> orderData = dataTable.asMaps(String.class, String.class).getFirst();
// Extract the first row of data
Map<String, String> orderData = rows.getFirst();
// Create a new OrderInfo object with the correct keys
OrderInfo newOrder = new OrderInfo( OrderInfo newOrder = new OrderInfo(
UUID.fromString(orderData.get("customerId")), UUID.fromString(orderData.get("customerId")),
orderData.get("paymentMethod"), orderData.get("paymentMethod")
Integer.parseInt(orderData.get("loyaltyPoints"))
); );
// Register the order try{
orderRegistration = orderUseCase.registerOrder(newOrder); orderRegistration = orderUseCase.registerOrder(newOrder);
exception = null;
} catch (Exception e) {
exception = e;
orderRegistration = null;
}
} }
@And("the order includes the following books:") @And("the order includes the following books:")
public void theOrderIncludesTheFollowingBooks(DataTable dataTable) { public void theOrderIncludesTheFollowingBooks(DataTable dataTable){
List<Map<String, String>> books = dataTable.asMaps(String.class, String.class); List<Map<String, String>> books = dataTable.asMaps(String.class, String.class);
List<OrderLineDTO> orderLines = new ArrayList<>();
for (Map<String, String> book : books) { for (Map<String, String> book : books) {
String isbn = book.get("isbn"); String isbn = book.get("ISBN");
int quantity = Integer.parseInt(book.get("quantity")); int quantity = Integer.parseInt(book.get("quantity"));
Book bookEntity = bookRepository.findByISBN(BookISBN.get(isbn)) orderLines.add(OrderLineDTO.builder()
.orElseThrow(() -> new IllegalArgumentException("Book not found: " + isbn)); .bookId(isbn)
.quantity(quantity)
double total = bookEntity.getPrice() * quantity; .build());
orderUseCase.addBookToOrder(orderRegistration, bookEntity, quantity, total);
} }
try {
orderUseCase.addBooksToOrder(orderRegistration, orderLines);
exception = null;
} catch (Exception e) {
exception = e;
orderRegistration = null;
}
} }
@And("the delivery address is:") @And("the delivery address is:")
public void theDeliveryAddressIs(DataTable dataTable) { public void theDeliveryAddressIs(DataTable dataTable) {
List<Map<String, String>> addressData = dataTable.asMaps(String.class, String.class); Map<String, String> addressData = dataTable.asMaps(String.class, String.class).getFirst();
if (addressData.isEmpty()) { AddressDTO address = AddressDTO.builder()
throw new IllegalArgumentException("Address data cannot be empty"); .street(addressData.get("street"))
} .city(addressData.get("city"))
Map<String, String> address = addressData.get(0); .postalCode(addressData.get("postalCode"))
String street = address.get("street"); .country(addressData.get("country"))
String city = address.get("city"); .build();
String postalCode = address.get("postalCode");
String country = address.get("country");
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") @Then("a new order is created")
public void aNewOrderIsCreated() { public void aNewOrderIsCreated() {
assertNull(exception, "No exception should be thrown during order creation");
assertNotNull(orderRegistration); assertNotNull(orderRegistration);
} }
@And("the total price is {double}") @And("the total price is {double}")
public void theTotalPriceIs(double price) { public void theTotalPriceIs(double expectedPrice) {
Order order = orderRepository.findById(orderRegistration); Order order = orderRepository.findById(orderRegistration)
assertEquals(price, order.getTotalPrice()); .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("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());
} }
@And("{int} loyalty points are deducted") @And("{int} loyalty points are deducted")
public void loyaltyPointsAreDeducted(int loyaltyPoints) { public void loyaltyPointsAreDeducted(int loyaltyPoints) {
Order order = orderRepository.findById(orderRegistration); Order order = orderRepository.findById(orderRegistration).orElseThrow();
Customer customer = customerRepository.findById(order.getCustomerId()) Customer customer = customerRepository.findById(order.getCustomerId())
.orElseThrow(() -> new IllegalArgumentException("Customer not found")); .orElseThrow(() -> new Exception("Customer not found"));
assertEquals(customer.getLoyaltyPoints(), order.getLoyaltyPoints() - loyaltyPoints); 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}") @And("I receive an error for validation order message containing {string}")
public void iReceiveAnErrorForValidationOrderMessageContaining(String errorMessage) { public void iReceiveAnErrorForValidationOrderMessageContaining(String errorMessage) {
assertEquals(errorMessage, notValidOrderException.getMessage()); 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");
@When("I create a new order with an unknown customer:")
public void iCreateANewOrderWithAnUnknownCustomer() {
} }
@And("I receive an error for not found exception message containing {string}") @And("I receive an error for not found exception message containing {string}")
public void iReceiveAnErrorForNotFoundExceptionMessageContaining(String errorMessage) { 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:") @And("I receive an error for not found user exception message containing {string}")
public void iCreateANewOrderWithInsufficientLoyaltyPoints() {
}
@And("I receive an error for illegal order exception message containing {string}")
public void iReceiveAnErrorForIllegalOrderExceptionMessageContaining(String errorMessage) { public void iReceiveAnErrorForIllegalOrderExceptionMessageContaining(String errorMessage) {
assertEquals(errorMessage, illegalOrderPointException.getMessage()); 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");
@When("I create a new order with an invalid payment method:")
public void iCreateANewOrderWithAnInvalidPaymentMethod() {
} }
@And("the order includes no books") @And("the order includes no books")
public void theOrderIncludesNoBooks() { public void theOrderIncludesNoBooks() {
List<OrderLineDTO> 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}") @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<OrderLineDTO>() {{
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}") @When("I retrieve the order by ID {string}")
public void iRetrieveTheOrderByID(String arg0) { public void iRetrieveTheOrderByID(String orderId) {
} UUID orderUUID = UUID.fromString(orderId);
try {
@When("I request all orders for customer {string}") orderByUUID = orderUseCase.findOrderById(orderUUID);
public void iRequestAllOrdersForCustomer(String arg0) { exception = null;
} } catch (Exception e) {
exception = e;
@Then("the retrieval fails") orderByUUID = Optional.empty();
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() {
} }
@Then("I receive the order details") @Then("I receive the order details")
public void iReceiveTheOrderDetails() { 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<OrderDTO> orders;
try {
orders = orderUseCase.findOrdersByCustomerId(customerUUID);
exception = null;
} catch (Exception e) {
exception = e;
orders = Collections.emptyList();
}
} }
@Then("I receive a list of orders") @Then("I receive a list of orders")
public void iReceiveAListOfOrders() { 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");
} }
} }

View File

@@ -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<OrderLineDTO> 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<OrderLineDTO> 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<OrderLineDTO> 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<OrderLineDTO> 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<OrderLineDTO> 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<OrderLineDTO> 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<OrderLineDTO> 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"));
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -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<OrderDTO> 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<OrderDTO> 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<OrderDTO> 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<OrderDTO> 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);
});
}
}
}

View File

@@ -42,6 +42,21 @@ Feature: Manage customer orders
And 49 loyalty points are deducted And 49 loyalty points are deducted
And customer "11111111-1111-1111-1111-111111111111" now has 51 loyalty points 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 Scenario: Attempt to create an order with invalid address
When I create a new order with the following information: When I create a new order with the following information:
| customerId | paymentMethod | | customerId | paymentMethod |
@@ -49,14 +64,14 @@ Feature: Manage customer orders
And the order includes the following books: And the order includes the following books:
| bookId | quantity | | bookId | quantity |
| 1234567890123 | 1 | | 1234567890123 | 1 |
And I try to set the delivery address to: And the delivery address is:
| street | city | postalCode | country | | street | city | postalCode | country |
| | | | | | | | | |
Then the creation fails Then the creation fails
And I receive an error for validation order message containing "Address fields are required" And I receive an error for validation order message containing "Address fields are required"
Scenario: Attempt to create an order with unknown customer 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 | | customerId | paymentMethod |
| 00000000-0000-0000-0000-000000000000 | CREDIT_CARD | | 00000000-0000-0000-0000-000000000000 | CREDIT_CARD |
And the order includes the following books: And the order includes the following books:
@@ -66,10 +81,10 @@ Feature: Manage customer orders
| street | city | postalCode | country | | street | city | postalCode | country |
| 12 Main St. | Paris | 75000 | France | | 12 Main St. | Paris | 75000 | France |
Then the creation fails 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 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 | | customerId | paymentMethod |
| 22222222-2222-2222-2222-222222222222 | LOYALTY_POINTS | | 22222222-2222-2222-2222-222222222222 | LOYALTY_POINTS |
And the order includes the following books: And the order includes the following books:
@@ -79,23 +94,23 @@ Feature: Manage customer orders
| street | city | postalCode | country | | street | city | postalCode | country |
| 42 Book Street | Lyon | 69000 | France | | 42 Book Street | Lyon | 69000 | France |
Then the creation fails 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 Scenario: Attempt to order more books than available stock
When I create a new order with the following information: When I create a new order with the following information:
| customerId | paymentMethod | | customerId | paymentMethod |
| 11111111-1111-1111-1111-111111111111 | CREDIT_CARD | | 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 | | bookId | quantity |
| 1234567890123 | 50 | | 1234567890123 | 50 |
And the delivery address is: And the delivery address is:
| street | city | postalCode | country | | street | city | postalCode | country |
| 12 Main St. | Paris | 75000 | France | | 12 Main St. | Paris | 75000 | France |
Then the creation fails 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 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 | | customerId | paymentMethod |
| 11111111-1111-1111-1111-111111111111 | UNKNOWN | | 11111111-1111-1111-1111-111111111111 | UNKNOWN |
And the order includes the following books: 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: When I create a new order with the following information:
| customerId | paymentMethod | | customerId | paymentMethod |
| 11111111-1111-1111-1111-111111111111 | CREDIT_CARD | | 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 | | bookId | quantity |
| unknownBookId | 1 | | unknownBookId | 1 |
And the delivery address is: And the delivery address is:
@@ -131,6 +146,19 @@ Feature: Manage customer orders
Then the creation fails Then the creation fails
And I receive an error for validation order message containing "Book list cannot be empty" 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 #Get orders
Scenario: Retrieve an order by ID Scenario: Retrieve an order by ID
@@ -150,4 +178,4 @@ Feature: Manage customer orders
Scenario: Attempt to retrieve orders for an unknown customer Scenario: Attempt to retrieve orders for an unknown customer
When I request all orders for customer "00000000-0000-0000-0000-000000000000" When I request all orders for customer "00000000-0000-0000-0000-000000000000"
Then the retrieval fails 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"