Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4815d2d0ba | |||
| 7b4b8124a7 | |||
| 84b2880b1d | |||
| 44a458dfbe | |||
| 5e1031bd71 |
@@ -0,0 +1,18 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.book;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record BookInfo(
|
||||||
|
String isbn,
|
||||||
|
String title,
|
||||||
|
String author,
|
||||||
|
String publisher,
|
||||||
|
List<String> categories,
|
||||||
|
String description,
|
||||||
|
String langue,
|
||||||
|
LocalDate publicationDate,
|
||||||
|
double price,
|
||||||
|
int stockInitial
|
||||||
|
) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.book;
|
||||||
|
|
||||||
|
public record BookIsbnDTO(String isbn) {
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.book.entity;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
@Getter
|
||||||
|
|
||||||
|
public class Book {
|
||||||
|
private String isbn;
|
||||||
|
private String title;
|
||||||
|
private String author;
|
||||||
|
private String publisher;
|
||||||
|
private List<String> categories;
|
||||||
|
private String description;
|
||||||
|
private String langue;
|
||||||
|
private LocalDate publicationDate;
|
||||||
|
private double price;
|
||||||
|
private int stock;
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.book.exception;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
|
||||||
|
public class BookNotFoundException extends Exception {
|
||||||
|
|
||||||
|
public static final String THE_BOOK_WITH_ISBN_DOES_NOT_EXIST =
|
||||||
|
"The book with isbn {0} does not exist";
|
||||||
|
|
||||||
|
public BookNotFoundException(String isbn) {
|
||||||
|
super(MessageFormat.format(THE_BOOK_WITH_ISBN_DOES_NOT_EXIST, isbn));
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.book.exception;
|
||||||
|
|
||||||
|
public class NotValidBookException extends Exception {
|
||||||
|
|
||||||
|
public NotValidBookException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.book.repository;
|
||||||
|
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
public final class BookRepository {
|
||||||
|
private final List<Book> books = new ArrayList<>();
|
||||||
|
|
||||||
|
public Book save(Book newBook) {
|
||||||
|
this.findByIsbn(newBook.getIsbn()).ifPresent(books::remove);
|
||||||
|
this.books.add(newBook);
|
||||||
|
return newBook;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Book> findByIsbn(String isbn) {
|
||||||
|
return this.books.stream()
|
||||||
|
.filter(book -> book.getIsbn().equals(isbn))
|
||||||
|
.findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Book> findAll() {
|
||||||
|
return books;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteAll() {
|
||||||
|
books.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.book.usecase;
|
||||||
|
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.BookInfo;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.BookIsbnDTO;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.NotValidBookException;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.repository.BookRepository;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.validator.BookValidator;
|
||||||
|
|
||||||
|
public final class BookUseCase {
|
||||||
|
|
||||||
|
private final BookRepository bookRepository;
|
||||||
|
|
||||||
|
public BookUseCase(BookRepository bookRepository) {
|
||||||
|
this.bookRepository = bookRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BookIsbnDTO registerNewBook(BookInfo newBook) throws NotValidBookException {
|
||||||
|
BookValidator.validate(newBook);
|
||||||
|
Book bookToSave = Book.builder()
|
||||||
|
.isbn(newBook.isbn())
|
||||||
|
.title(newBook.title())
|
||||||
|
.author(newBook.author())
|
||||||
|
.publisher(newBook.publisher())
|
||||||
|
.categories(newBook.categories())
|
||||||
|
.description(newBook.description())
|
||||||
|
.langue(newBook.langue())
|
||||||
|
.publicationDate(newBook.publicationDate())
|
||||||
|
.price(newBook.price())
|
||||||
|
.stock(newBook.stockInitial())
|
||||||
|
.build();
|
||||||
|
Book savedBook = bookRepository.save(bookToSave);
|
||||||
|
return new BookIsbnDTO(savedBook.getIsbn());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.book.validator;
|
||||||
|
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.BookInfo;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.NotValidBookException;
|
||||||
|
|
||||||
|
public final class BookValidator {
|
||||||
|
|
||||||
|
private BookValidator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void validate(BookInfo bookInfo) throws NotValidBookException {
|
||||||
|
if (bookInfo == null) {
|
||||||
|
throw new NotValidBookException("Book cannot be null");
|
||||||
|
}
|
||||||
|
if (isBlank(bookInfo.isbn())) {
|
||||||
|
throw new NotValidBookException("ISBN is required");
|
||||||
|
}
|
||||||
|
if (!bookInfo.isbn().matches("\\d{13}")) {
|
||||||
|
throw new NotValidBookException("ISBN must be 13 digits");
|
||||||
|
}
|
||||||
|
if (isBlank(bookInfo.title())) {
|
||||||
|
throw new NotValidBookException("Title is required");
|
||||||
|
}
|
||||||
|
if (isBlank(bookInfo.author())) {
|
||||||
|
throw new NotValidBookException("Author is required");
|
||||||
|
}
|
||||||
|
if (isBlank(bookInfo.publisher())) {
|
||||||
|
throw new NotValidBookException("Publisher is required");
|
||||||
|
}
|
||||||
|
if (bookInfo.categories() == null) {
|
||||||
|
throw new NotValidBookException("Categories is required");
|
||||||
|
}
|
||||||
|
if (bookInfo.categories().stream().anyMatch(BookValidator::isBlank)) {
|
||||||
|
throw new NotValidBookException("Categories cannot contain empty values");
|
||||||
|
}
|
||||||
|
if (bookInfo.price() <= 0) {
|
||||||
|
throw new NotValidBookException("Price must be positive");
|
||||||
|
}
|
||||||
|
if (bookInfo.stockInitial() < 0) {
|
||||||
|
throw new NotValidBookException("Stock must be greater or equal to 0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBlank(String value) {
|
||||||
|
return value == null || value.isBlank();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.order;
|
||||||
|
|
||||||
|
public record OrderAddressInfo(
|
||||||
|
String street,
|
||||||
|
String city,
|
||||||
|
String postalCode,
|
||||||
|
String country
|
||||||
|
) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.order;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record OrderDTO(
|
||||||
|
UUID orderId,
|
||||||
|
double totalAmount,
|
||||||
|
int earnedLoyaltyPoints
|
||||||
|
) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.order;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record OrderInfo(
|
||||||
|
UUID customerId,
|
||||||
|
List<OrderLineInfo> orderLines,
|
||||||
|
OrderAddressInfo deliveryAddress,
|
||||||
|
PaymentMode paymentMode
|
||||||
|
) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.order;
|
||||||
|
|
||||||
|
public record OrderLineInfo(
|
||||||
|
String bookIsbn,
|
||||||
|
int quantity
|
||||||
|
) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.order;
|
||||||
|
|
||||||
|
public enum PaymentMode {
|
||||||
|
CB,
|
||||||
|
PAYPAL,
|
||||||
|
POINTS_FIDELITE
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.order.exception;
|
||||||
|
|
||||||
|
public class InsufficientStockException extends Exception {
|
||||||
|
|
||||||
|
public InsufficientStockException(String bookIsbn, int requested, int available) {
|
||||||
|
super("Insufficient stock for book " + bookIsbn + ": requested=" + requested
|
||||||
|
+ ", available=" + available);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
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.exception.BookNotFoundException;
|
||||||
|
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.OrderDTO;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.order.OrderInfo;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.order.OrderLineInfo;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.InsufficientStockException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public final class OrderUseCase {
|
||||||
|
|
||||||
|
private final CustomerRepository customerRepository;
|
||||||
|
private final BookRepository bookRepository;
|
||||||
|
|
||||||
|
public OrderUseCase(CustomerRepository customerRepository, BookRepository bookRepository) {
|
||||||
|
this.customerRepository = customerRepository;
|
||||||
|
this.bookRepository = bookRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrderDTO placeOrder(OrderInfo orderInfo)
|
||||||
|
throws CustomerNotFoundException, InsufficientStockException, BookNotFoundException {
|
||||||
|
Customer customer = customerRepository.findById(orderInfo.customerId())
|
||||||
|
.orElseThrow(() -> new CustomerNotFoundException(orderInfo.customerId()));
|
||||||
|
|
||||||
|
double totalAmount = 0;
|
||||||
|
for (OrderLineInfo orderLine : orderInfo.orderLines()) {
|
||||||
|
Book book = bookRepository.findByIsbn(orderLine.bookIsbn())
|
||||||
|
.orElseThrow(() -> new BookNotFoundException(orderLine.bookIsbn()));
|
||||||
|
if (orderLine.quantity() > book.getStock()) {
|
||||||
|
throw new InsufficientStockException(book.getIsbn(), orderLine.quantity(), book.getStock());
|
||||||
|
}
|
||||||
|
totalAmount += book.getPrice() * orderLine.quantity();
|
||||||
|
Book updatedBook = Book.builder()
|
||||||
|
.isbn(book.getIsbn())
|
||||||
|
.title(book.getTitle())
|
||||||
|
.author(book.getAuthor())
|
||||||
|
.publisher(book.getPublisher())
|
||||||
|
.categories(book.getCategories())
|
||||||
|
.description(book.getDescription())
|
||||||
|
.langue(book.getLangue())
|
||||||
|
.publicationDate(book.getPublicationDate())
|
||||||
|
.price(book.getPrice())
|
||||||
|
.stock(book.getStock() - orderLine.quantity())
|
||||||
|
.build();
|
||||||
|
bookRepository.save(updatedBook);
|
||||||
|
}
|
||||||
|
|
||||||
|
int earnedLoyaltyPoints = (int) totalAmount;
|
||||||
|
customer.addLoyaltyPoints(earnedLoyaltyPoints);
|
||||||
|
customerRepository.save(customer);
|
||||||
|
|
||||||
|
return new OrderDTO(UUID.randomUUID(), totalAmount, earnedLoyaltyPoints);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.book.usecase;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.BookInfo;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.BookIsbnDTO;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.NotValidBookException;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.repository.BookRepository;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class BookUseCaseTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private BookRepository bookRepository;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private BookUseCase bookUseCase;
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("Register new book tests")
|
||||||
|
class RegisterNewBookTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should register a new book and return ISBN")
|
||||||
|
void shouldRegisterBookAndReturnIsbn() throws NotValidBookException {
|
||||||
|
BookInfo validBookInfo = new BookInfo(
|
||||||
|
"9783161484100",
|
||||||
|
"Clean Code",
|
||||||
|
"Robert C. Martin",
|
||||||
|
"Prentice Hall",
|
||||||
|
List.of("Programming"),
|
||||||
|
"A handbook of agile software craftsmanship.",
|
||||||
|
"fr",
|
||||||
|
LocalDate.of(2008, 8, 1),
|
||||||
|
42.50,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
|
Book savedBook = Book.builder()
|
||||||
|
.isbn("9783161484100")
|
||||||
|
.title("Clean Code")
|
||||||
|
.author("Robert C. Martin")
|
||||||
|
.publisher("Prentice Hall")
|
||||||
|
.categories(List.of("Programming"))
|
||||||
|
.description("A handbook of agile software craftsmanship.")
|
||||||
|
.langue("fr")
|
||||||
|
.publicationDate(LocalDate.of(2008, 8, 1))
|
||||||
|
.price(42.50)
|
||||||
|
.stock(10)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
when(bookRepository.save(any(Book.class))).thenReturn(savedBook);
|
||||||
|
|
||||||
|
BookIsbnDTO isbn = bookUseCase.registerNewBook(validBookInfo);
|
||||||
|
|
||||||
|
assertNotNull(isbn);
|
||||||
|
assertEquals("9783161484100", isbn.isbn());
|
||||||
|
verify(bookRepository, times(1)).save(any(Book.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should throw when required field is empty")
|
||||||
|
void shouldThrowWhenRequiredFieldIsEmpty() {
|
||||||
|
BookInfo invalidBookInfo = new BookInfo(
|
||||||
|
"9783161484100",
|
||||||
|
"",
|
||||||
|
"Robert C. Martin",
|
||||||
|
"Prentice Hall",
|
||||||
|
List.of("Programming"),
|
||||||
|
"A handbook of agile software craftsmanship.",
|
||||||
|
"fr",
|
||||||
|
LocalDate.of(2008, 8, 1),
|
||||||
|
42.50,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
|
assertThrows(NotValidBookException.class, () -> bookUseCase.registerNewBook(invalidBookInfo));
|
||||||
|
verify(bookRepository, never()).save(any(Book.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should throw when ISBN is not 13 digits")
|
||||||
|
void shouldThrowWhenIsbnIsNot13Digits() {
|
||||||
|
BookInfo invalidBookInfo = new BookInfo(
|
||||||
|
"97831614841X",
|
||||||
|
"Clean Code",
|
||||||
|
"Robert C. Martin",
|
||||||
|
"Prentice Hall",
|
||||||
|
List.of("Programming"),
|
||||||
|
"A handbook of agile software craftsmanship.",
|
||||||
|
"fr",
|
||||||
|
LocalDate.of(2008, 8, 1),
|
||||||
|
42.50,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
|
assertThrows(NotValidBookException.class, () -> bookUseCase.registerNewBook(invalidBookInfo));
|
||||||
|
verify(bookRepository, never()).save(any(Book.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should throw when categories contain empty value")
|
||||||
|
void shouldThrowWhenCategoriesContainEmptyValue() {
|
||||||
|
BookInfo invalidBookInfo = new BookInfo(
|
||||||
|
"9783161484100",
|
||||||
|
"Clean Code",
|
||||||
|
"Robert C. Martin",
|
||||||
|
"Prentice Hall",
|
||||||
|
List.of(""),
|
||||||
|
"A handbook of agile software craftsmanship.",
|
||||||
|
"fr",
|
||||||
|
LocalDate.of(2008, 8, 1),
|
||||||
|
42.50,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
|
assertThrows(NotValidBookException.class, () -> bookUseCase.registerNewBook(invalidBookInfo));
|
||||||
|
verify(bookRepository, never()).save(any(Book.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import static org.junit.jupiter.api.Assertions.*;
|
|||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
||||||
class CustomerTest {
|
class CustomerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -0,0 +1,185 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.order.usecase;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.mockito.Mockito.any;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.BookNotFoundException;
|
||||||
|
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.OrderAddressInfo;
|
||||||
|
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.OrderLineInfo;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.order.PaymentMode;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.InsufficientStockException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class OrderUseCaseTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private CustomerRepository customerRepository;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private BookRepository bookRepository;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private OrderUseCase orderUseCase;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should place order and return expected amount and loyalty points")
|
||||||
|
void shouldPlaceOrderAndReturnExpectedAmountAndLoyaltyPoints()
|
||||||
|
throws CustomerNotFoundException, InsufficientStockException, BookNotFoundException {
|
||||||
|
UUID customerId = UUID.randomUUID();
|
||||||
|
Customer customer = Customer.builder()
|
||||||
|
.id(customerId)
|
||||||
|
.firstName("Jane")
|
||||||
|
.lastName("Doe")
|
||||||
|
.phoneNumber("0612345678")
|
||||||
|
.loyaltyPoints(10)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Book cleanCode = Book.builder()
|
||||||
|
.isbn("9780132350884")
|
||||||
|
.title("Clean Code")
|
||||||
|
.author("Robert C. Martin")
|
||||||
|
.publisher("Prentice Hall")
|
||||||
|
.categories(List.of("Programming"))
|
||||||
|
.description("A handbook of agile software craftsmanship")
|
||||||
|
.langue("fr")
|
||||||
|
.price(40.0)
|
||||||
|
.stock(10)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
OrderInfo orderInfo = new OrderInfo(
|
||||||
|
customerId,
|
||||||
|
List.of(new OrderLineInfo("9780132350884", 2)),
|
||||||
|
new OrderAddressInfo("1 rue de Paris", "Paris", "75000", "France"),
|
||||||
|
PaymentMode.CB
|
||||||
|
);
|
||||||
|
|
||||||
|
when(customerRepository.findById(customerId)).thenReturn(Optional.of(customer));
|
||||||
|
when(bookRepository.findByIsbn("9780132350884")).thenReturn(Optional.of(cleanCode));
|
||||||
|
when(bookRepository.save(any(Book.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||||
|
when(customerRepository.save(any(Customer.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||||
|
|
||||||
|
OrderDTO orderDTO = orderUseCase.placeOrder(orderInfo);
|
||||||
|
|
||||||
|
assertNotNull(orderDTO);
|
||||||
|
assertNotNull(orderDTO.orderId());
|
||||||
|
assertEquals(80.0, orderDTO.totalAmount());
|
||||||
|
assertEquals(80, orderDTO.earnedLoyaltyPoints());
|
||||||
|
assertEquals(90, customer.getLoyaltyPoints());
|
||||||
|
verify(customerRepository, times(1)).findById(customerId);
|
||||||
|
verify(bookRepository, times(1)).findByIsbn("9780132350884");
|
||||||
|
verify(bookRepository, times(1)).save(any(Book.class));
|
||||||
|
verify(customerRepository, times(1)).save(customer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should throw when requested quantity is greater than available stock")
|
||||||
|
void shouldThrowWhenRequestedQuantityIsGreaterThanAvailableStock() throws BookNotFoundException {
|
||||||
|
UUID customerId = UUID.randomUUID();
|
||||||
|
Customer customer = Customer.builder()
|
||||||
|
.id(customerId)
|
||||||
|
.firstName("Jane")
|
||||||
|
.lastName("Doe")
|
||||||
|
.phoneNumber("0612345678")
|
||||||
|
.loyaltyPoints(10)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Book cleanCode = Book.builder()
|
||||||
|
.isbn("9780132350884")
|
||||||
|
.title("Clean Code")
|
||||||
|
.author("Robert C. Martin")
|
||||||
|
.publisher("Prentice Hall")
|
||||||
|
.categories(List.of("Programming"))
|
||||||
|
.description("A handbook of agile software craftsmanship")
|
||||||
|
.langue("fr")
|
||||||
|
.price(40.0)
|
||||||
|
.stock(1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
OrderInfo orderInfo = new OrderInfo(
|
||||||
|
customerId,
|
||||||
|
List.of(new OrderLineInfo("9780132350884", 2)),
|
||||||
|
new OrderAddressInfo("1 rue de Paris", "Paris", "75000", "France"),
|
||||||
|
PaymentMode.CB
|
||||||
|
);
|
||||||
|
|
||||||
|
when(customerRepository.findById(customerId)).thenReturn(Optional.of(customer));
|
||||||
|
when(bookRepository.findByIsbn("9780132350884")).thenReturn(Optional.of(cleanCode));
|
||||||
|
|
||||||
|
assertThrows(InsufficientStockException.class, () -> orderUseCase.placeOrder(orderInfo));
|
||||||
|
verify(customerRepository, times(1)).findById(customerId);
|
||||||
|
verify(bookRepository, times(1)).findByIsbn("9780132350884");
|
||||||
|
verify(bookRepository, never()).save(any(Book.class));
|
||||||
|
verify(customerRepository, never()).save(any(Customer.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should throw when customer does not exist")
|
||||||
|
void shouldThrowWhenCustomerDoesNotExist() {
|
||||||
|
UUID customerId = UUID.randomUUID();
|
||||||
|
OrderInfo orderInfo = new OrderInfo(
|
||||||
|
customerId,
|
||||||
|
List.of(new OrderLineInfo("9780132350884", 1)),
|
||||||
|
new OrderAddressInfo("1 rue de Paris", "Paris", "75000", "France"),
|
||||||
|
PaymentMode.CB
|
||||||
|
);
|
||||||
|
|
||||||
|
when(customerRepository.findById(customerId)).thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
assertThrows(CustomerNotFoundException.class, () -> orderUseCase.placeOrder(orderInfo));
|
||||||
|
verify(customerRepository, times(1)).findById(customerId);
|
||||||
|
verify(bookRepository, never()).findByIsbn(any(String.class));
|
||||||
|
verify(bookRepository, never()).save(any(Book.class));
|
||||||
|
verify(customerRepository, never()).save(any(Customer.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should throw when ordered book does not exist")
|
||||||
|
void shouldThrowWhenOrderedBookDoesNotExist() {
|
||||||
|
UUID customerId = UUID.randomUUID();
|
||||||
|
Customer customer = Customer.builder()
|
||||||
|
.id(customerId)
|
||||||
|
.firstName("Jane")
|
||||||
|
.lastName("Doe")
|
||||||
|
.phoneNumber("0612345678")
|
||||||
|
.loyaltyPoints(10)
|
||||||
|
.build();
|
||||||
|
String unknownIsbn = "9999999999999";
|
||||||
|
OrderInfo orderInfo = new OrderInfo(
|
||||||
|
customerId,
|
||||||
|
List.of(new OrderLineInfo(unknownIsbn, 1)),
|
||||||
|
new OrderAddressInfo("1 rue de Paris", "Paris", "75000", "France"),
|
||||||
|
PaymentMode.CB
|
||||||
|
);
|
||||||
|
|
||||||
|
when(customerRepository.findById(customerId)).thenReturn(Optional.of(customer));
|
||||||
|
when(bookRepository.findByIsbn(unknownIsbn)).thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
assertThrows(BookNotFoundException.class, () -> orderUseCase.placeOrder(orderInfo));
|
||||||
|
verify(customerRepository, times(1)).findById(customerId);
|
||||||
|
verify(bookRepository, times(1)).findByIsbn(unknownIsbn);
|
||||||
|
verify(bookRepository, never()).save(any(Book.class));
|
||||||
|
verify(customerRepository, never()).save(any(Customer.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user