From 443d08cb760d9ed514c2e74e66d80a646a90f950 Mon Sep 17 00:00:00 2001
From: Simon Saye Babu <114667196+SimonSayeBabu@users.noreply.github.com>
Date: Fri, 13 Jun 2025 03:59:04 +0200
Subject: [PATCH] done ?
---
.idea/.gitignore | 3 +
.idea/encodings.xml | 7 +
.idea/inspectionProfiles/Project_Default.xml | 10 +
.idea/misc.xml | 12 +
.idea/vcs.xml | 6 +
.../but3/dev62/mylibrary/book/BookDTO.java | 24 ++
.../but3/dev62/mylibrary/book/BookInfo.java | 17 +
.../but3/dev62/mylibrary/book/Category.java | 24 ++
.../book/converter/BookConverter.java | 40 ++
.../dev62/mylibrary/book/entity/Book.java | 33 ++
.../book/exception/BookNotFoundException.java | 13 +
.../book/exception/NotValidBookException.java | 7 +
.../book/repository/BookRepository.java | 78 ++++
.../mylibrary/book/usecase/BookUseCase.java | 155 ++++++++
.../book/validator/BookValidator.java | 62 +++
.../customer/converter/CustomerConverter.java | 28 +-
.../repository/CustomerRepository.java | 24 +-
.../dev62/mylibrary/order/AddressDTO.java | 14 +
.../but3/dev62/mylibrary/order/OrderDTO.java | 19 +
.../but3/dev62/mylibrary/order/OrderInfo.java | 16 +
.../dev62/mylibrary/order/OrderLineDTO.java | 12 +
.../dev62/mylibrary/order/PaymentMethod.java | 6 +
.../order/converter/OrderConverter.java | 96 +++++
.../dev62/mylibrary/order/entity/Address.java | 14 +
.../dev62/mylibrary/order/entity/Order.java | 19 +
.../mylibrary/order/entity/OrderLine.java | 12 +
.../exception/IllegalOrderPointException.java | 7 +
.../InvalidPaymentMethodException.java | 7 +
.../exception/NotValidOrderException.java | 7 +
.../exception/OrderNotFoundException.java | 7 +
.../exception/OrderQuantityException.java | 7 +
.../exception/WrongAddressException.java | 7 +
.../order/repository/OrderRepository.java | 60 +++
.../mylibrary/order/usecase/OrderUseCase.java | 156 ++++++++
.../order/validator/OrderValidator.java | 66 ++++
.../dev62/mylibrary/review/ReviewDto.java | 19 +
.../dev62/mylibrary/review/ReviewInfo.java | 16 +
.../review/converter/ReviewConverter.java | 29 ++
.../dev62/mylibrary/review/entity/Review.java | 19 +
.../InvalidReviewRatingException.java | 8 +
.../ReviewAlreadyExistsException.java | 8 +
.../exception/ReviewNotFoundException.java | 8 +
.../review/repository/ReviewRepository.java | 44 +++
.../review/usecase/ReviewUseCase.java | 68 ++++
.../review/validator/ReviewValidator.java | 23 ++
.../subscription/SubscriptionDTO.java | 15 +
.../subscription/SubscriptionInfo.java | 7 +
.../converter/SubscriptionConverter.java | 31 ++
.../subscription/entity/Subscription.java | 24 ++
.../NotValidSubscriptionException.java | 8 +
.../SubscriptionNotFoundException.java | 13 +
.../repository/SubscriptionRepository.java | 42 ++
.../usecase/SubscriptionUseCase.java | 45 +++
.../validator/SubscriptionValidator.java | 44 +++
.../book/converter/BookConverterTest.java | 149 +++++++
.../dev62/mylibrary/book/entity/BookTest.java | 128 ++++++
.../exception/BookNotFoundExceptionTest.java | 45 +++
.../exception/NotValidBookExceptionTest.java | 61 +++
.../book/repository/BookRepositoryTest.java | 205 ++++++++++
.../book/usecase/BookUseCaseTest.java | 279 ++++++++++++++
.../book/validator/BookValidatorTest.java | 259 +++++++++++++
.../mylibrary/features/book/BookSteps.java | 320 +++++++++++++++
.../mylibrary/features/order/OrderSteps.java | 364 ++++++++++++++++++
.../features/review/ReviewSteps.java | 282 ++++++++++++++
.../subscription/SubscriptionSteps.java | 161 ++++++++
.../order/converter/OrderConverterTest.java | 134 +++++++
.../mylibrary/order/entity/OrderTest.java | 81 ++++
.../IllegalOrderPointExceptionTest.java | 21 +
.../exception/OrderNotFoundExceptionTest.java | 21 +
.../exception/OrderQuantityExceptionTest.java | 21 +
.../exception/WrongAddressExceptionTest.java | 21 +
.../order/repository/OrderRepositoryTest.java | 73 ++++
.../order/usecase/OrderUseCaseTest.java | 165 ++++++++
.../order/validator/OrderValidatorTest.java | 264 +++++++++++++
.../review/converter/ReviewConverterTest.java | 45 +++
.../mylibrary/review/entity/ReviewTest.java | 80 ++++
.../InvalidReviewRatingExceptionTest.java | 14 +
.../ReviewAlreadyExistsExceptionTest.java | 14 +
.../ReviewNotFoundExceptionTest.java | 14 +
.../repository/ReviewRepositoryTest.java | 106 +++++
.../review/usecase/ReviewUseCaseTest.java | 134 +++++++
.../review/validator/ReviewValidatorTest.java | 59 +++
.../converter/SubscriptionConverterTest.java | 96 +++++
.../subscription/entity/SubscriptionTest.java | 48 +++
.../NotValidSubscriptionExceptionTest.java | 60 +++
.../SubscriptionNotFoundExceptionTest.java | 51 +++
.../SubscriptionRepositoryTest.java | 154 ++++++++
.../usecase/SubscribeUseCaseTest.java | 107 +++++
.../validator/SubscriptionValidatorTest.java | 93 +++++
src/test/resources/features/book.feature | 138 +++++++
src/test/resources/features/order.feature | 205 ++++++++++
src/test/resources/features/review.feature | 73 ++++
.../resources/features/subscription.feature | 47 +++
93 files changed, 6117 insertions(+), 21 deletions(-)
create mode 100644 .idea/.gitignore
create mode 100644 .idea/encodings.xml
create mode 100644 .idea/inspectionProfiles/Project_Default.xml
create mode 100644 .idea/misc.xml
create mode 100644 .idea/vcs.xml
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/BookDTO.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/BookInfo.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/Category.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/converter/BookConverter.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/entity/Book.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/BookNotFoundException.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/NotValidBookException.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/repository/BookRepository.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/BookUseCase.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidator.java
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/OrderInfo.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/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/converter/OrderConverter.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/Address.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/Order.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/OrderLine.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/IllegalOrderPointException.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/InvalidPaymentMethodException.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/NotValidOrderException.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/OrderNotFoundException.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/OrderQuantityException.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/WrongAddressException.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/repository/OrderRepository.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/usecase/OrderUseCase.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/validator/OrderValidator.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/ReviewDto.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/ReviewInfo.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/converter/ReviewConverter.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/entity/Review.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/InvalidReviewRatingException.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/ReviewAlreadyExistsException.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/ReviewNotFoundException.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/repository/ReviewRepository.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/usecase/ReviewUseCase.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/validator/ReviewValidator.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/SubscriptionDTO.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/SubscriptionInfo.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/converter/SubscriptionConverter.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/entity/Subscription.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/exception/NotValidSubscriptionException.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/exception/SubscriptionNotFoundException.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/repository/SubscriptionRepository.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/usecase/SubscriptionUseCase.java
create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/validator/SubscriptionValidator.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/converter/BookConverterTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/entity/BookTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/BookNotFoundExceptionTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/NotValidBookExceptionTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/repository/BookRepositoryTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/BookUseCaseTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidatorTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/book/BookSteps.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/order/OrderSteps.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/review/ReviewSteps.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/subscription/SubscriptionSteps.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/converter/OrderConverterTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/OrderTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/IllegalOrderPointExceptionTest.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/OrderQuantityExceptionTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/WrongAddressExceptionTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/repository/OrderRepositoryTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/usecase/OrderUseCaseTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/order/validator/OrderValidatorTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/converter/ReviewConverterTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/entity/ReviewTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/InvalidReviewRatingExceptionTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/ReviewAlreadyExistsExceptionTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/ReviewNotFoundExceptionTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/repository/ReviewRepositoryTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/usecase/ReviewUseCaseTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/validator/ReviewValidatorTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/converter/SubscriptionConverterTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/entity/SubscriptionTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/exception/NotValidSubscriptionExceptionTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/exception/SubscriptionNotFoundExceptionTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/repository/SubscriptionRepositoryTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/usecase/SubscribeUseCaseTest.java
create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/validator/SubscriptionValidatorTest.java
create mode 100644 src/test/resources/features/book.feature
create mode 100644 src/test/resources/features/order.feature
create mode 100644 src/test/resources/features/review.feature
create mode 100644 src/test/resources/features/subscription.feature
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..aa00ffa
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..146ab09
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..6546f14
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/BookDTO.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/BookDTO.java
new file mode 100644
index 0000000..b85234b
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/BookDTO.java
@@ -0,0 +1,24 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.book;
+
+import java.time.LocalDate;
+import java.util.List;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+
+@Builder
+@Getter
+@AllArgsConstructor
+public class BookDTO {
+ private final String isbn;
+ private final String title;
+ private final String author;
+ private final String publisher;
+ private final LocalDate publicationDate;
+ private final double price;
+ private final int stock;
+ private final List categories;
+ private final String description;
+ private final String language;
+}
\ No newline at end of file
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/BookInfo.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/BookInfo.java
new file mode 100644
index 0000000..062a2f5
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/BookInfo.java
@@ -0,0 +1,17 @@
+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,
+ LocalDate publicationDate,
+ double price,
+ int stock,
+ List categories,
+ String description,
+ String language
+) {}
\ No newline at end of file
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/Category.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/Category.java
new file mode 100644
index 0000000..c86eff7
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/Category.java
@@ -0,0 +1,24 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.book;
+
+public enum Category {
+ FICTION,
+ NON_FICTION,
+ SCIENCE_FICTION,
+ FANTASY,
+ MYSTERY,
+ THRILLER,
+ ROMANCE,
+ BIOGRAPHY,
+ HISTORY,
+ POETRY,
+ CHILDREN,
+ YOUNG_ADULT,
+ SCIENCE,
+ PHILOSOPHY,
+ SELF_HELP,
+ TRAVEL,
+ COOKING,
+ ART,
+ RELIGION,
+ REFERENCE
+}
\ No newline at end of file
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/converter/BookConverter.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/converter/BookConverter.java
new file mode 100644
index 0000000..5c09b6a
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/converter/BookConverter.java
@@ -0,0 +1,40 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.book.converter;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.book.BookDTO;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.BookInfo;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book;
+
+public final class BookConverter {
+
+ private BookConverter() {}
+
+ public static Book toDomain(BookInfo info) {
+ return Book.builder()
+ .isbn(info.isbn())
+ .title(info.title())
+ .author(info.author())
+ .publisher(info.publisher())
+ .publicationDate(info.publicationDate())
+ .price(info.price())
+ .stock(info.stock())
+ .categories(info.categories())
+ .description(info.description())
+ .language(info.language())
+ .build();
+ }
+
+ public static BookDTO toDTO(Book book) {
+ return BookDTO.builder()
+ .isbn(book.getIsbn())
+ .title(book.getTitle())
+ .author(book.getAuthor())
+ .publisher(book.getPublisher())
+ .publicationDate(book.getPublicationDate())
+ .price(book.getPrice())
+ .stock(book.getStock())
+ .categories(book.getCategories())
+ .description(book.getDescription())
+ .language(book.getLanguage())
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/entity/Book.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/entity/Book.java
new file mode 100644
index 0000000..7a38c73
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/entity/Book.java
@@ -0,0 +1,33 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.book.entity;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.book.Category;
+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 LocalDate publicationDate;
+ private double price;
+ private int stock;
+ private List categories;
+ private String description;
+ private String language;
+
+ public void addStock(int quantityToAdd) {
+ this.stock += quantityToAdd;
+ }
+
+ public void removeStock(int quantityToRemove) {
+ if (quantityToRemove > this.stock) {
+ throw new IllegalArgumentException("Pas assez de stock pour retirer : " + quantityToRemove);
+ }
+ this.stock -= quantityToRemove;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/BookNotFoundException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/BookNotFoundException.java
new file mode 100644
index 0000000..9f278f0
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/BookNotFoundException.java
@@ -0,0 +1,13 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.book.exception;
+
+public class BookNotFoundException extends RuntimeException {
+ public static final String THE_BOOK_WITH_ISBN_DOES_NOT_EXIST_MESSAGE = "The book with isbn %s does not exist";
+
+ public BookNotFoundException(String message) {
+ super(message);
+ }
+
+ public static BookNotFoundException forIsbn(String isbn) {
+ return new BookNotFoundException(String.format(THE_BOOK_WITH_ISBN_DOES_NOT_EXIST_MESSAGE, isbn));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/NotValidBookException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/NotValidBookException.java
new file mode 100644
index 0000000..bec0172
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/NotValidBookException.java
@@ -0,0 +1,7 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.book.exception;
+
+public class NotValidBookException extends RuntimeException {
+ public NotValidBookException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/repository/BookRepository.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/repository/BookRepository.java
new file mode 100644
index 0000000..37b1fdb
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/repository/BookRepository.java
@@ -0,0 +1,78 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.book.repository;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.Category;
+import java.util.*;
+import java.util.stream.Collectors;
+import lombok.NoArgsConstructor;
+
+@NoArgsConstructor
+public final class BookRepository {
+ private final List books = new ArrayList<>();
+
+ public List findAll() {
+ return books;
+ }
+
+ public void deleteAll() {
+ books.clear();
+ }
+
+ public Book save(Book newBook) {
+ Optional optionalBookWithSameIsbn = this.findByIsbn(newBook.getIsbn());
+ optionalBookWithSameIsbn.ifPresent(books::remove);
+ books.add(newBook);
+ return newBook;
+ }
+
+ public Optional findByIsbn(String isbn) {
+ return books.stream()
+ .filter(book -> Objects.equals(book.getIsbn(), isbn))
+ .findFirst();
+ }
+
+ public boolean existsByIsbn(String isbn) {
+ return books.stream()
+ .anyMatch(book -> Objects.equals(book.getIsbn(), isbn));
+ }
+
+ public void delete(Book book) {
+ books.remove(book);
+ }
+
+ public List findByTitleContaining(String titlePart) {
+ return books.stream()
+ .filter(book -> book.getTitle() != null && book.getTitle().toLowerCase().contains(titlePart.toLowerCase()))
+ .collect(Collectors.toList());
+ }
+
+ public List findByAuthor(String author) {
+ return books.stream()
+ .filter(book -> book.getAuthor() != null && book.getAuthor().equalsIgnoreCase(author))
+ .collect(Collectors.toList());
+ }
+
+ public List findByPublisher(String publisher) {
+ return books.stream()
+ .filter(book -> book.getPublisher() != null && book.getPublisher().equalsIgnoreCase(publisher))
+ .collect(Collectors.toList());
+ }
+
+ public List findByCategory(Category category) {
+ return books.stream()
+ .filter(book -> book.getCategories() != null && book.getCategories().contains(category))
+ .collect(Collectors.toList());
+ }
+
+ public List findPublishedAfter(java.time.LocalDate date) {
+ return books.stream()
+ .filter(book -> book.getPublicationDate() != null && book.getPublicationDate().isAfter(date))
+ .collect(Collectors.toList());
+ }
+
+ public List findByPriceRange(double min, double max) {
+ return books.stream()
+ .filter(book -> book.getPrice() >= min && book.getPrice() <= max)
+ .collect(Collectors.toList());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/BookUseCase.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/BookUseCase.java
new file mode 100644
index 0000000..9521ef3
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/BookUseCase.java
@@ -0,0 +1,155 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.book.usecase;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.book.BookDTO;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.BookInfo;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.Category;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.converter.BookConverter;
+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.exception.NotValidBookException;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.repository.BookRepository;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.validator.BookValidator;
+
+import java.time.LocalDate;
+import java.util.*;
+import java.util.stream.Collectors;
+
+public final class BookUseCase {
+
+ private final BookRepository bookRepository;
+
+ public BookUseCase(BookRepository bookRepository) {
+ this.bookRepository = bookRepository;
+ }
+
+ public String registerBook(BookInfo newBook) throws NotValidBookException {
+ BookValidator.validate(newBook);
+
+ if (bookRepository.existsByIsbn(newBook.isbn())) {
+ throw new NotValidBookException("Un livre avec cet ISBN existe déjà");
+ }
+
+ Book bookToRegister = BookConverter.toDomain(newBook);
+ Book registeredBook = bookRepository.save(bookToRegister);
+ return registeredBook.getIsbn();
+ }
+
+ public BookDTO findBookByIsbn(String isbn) throws BookNotFoundException {
+ return bookRepository.findByIsbn(isbn)
+ .map(BookConverter::toDTO)
+ .orElseThrow(() -> new BookNotFoundException(isbn));
+ }
+
+ public BookDTO updateBook(String isbn, BookInfo bookInfo)
+ throws BookNotFoundException, NotValidBookException {
+ BookValidator.validate(bookInfo);
+ getBookIfDoesNotExistThrowBookNotFoundException(isbn);
+ Book updated = Book.builder()
+ .isbn(isbn)
+ .title(bookInfo.title())
+ .author(bookInfo.author())
+ .publisher(bookInfo.publisher())
+ .publicationDate(bookInfo.publicationDate())
+ .price(bookInfo.price())
+ .stock(bookInfo.stock())
+ .categories(bookInfo.categories())
+ .description(bookInfo.description())
+ .language(bookInfo.language())
+ .build();
+ Book saved = bookRepository.save(updated);
+ return BookConverter.toDTO(saved);
+ }
+
+ public void deleteBook(String isbn) throws BookNotFoundException {
+ Book bookToDelete = getBookIfDoesNotExistThrowBookNotFoundException(isbn);
+ this.bookRepository.delete(bookToDelete);
+ }
+
+ public int addStock(String isbn, int quantity) throws BookNotFoundException {
+ Book book = getBookIfDoesNotExistThrowBookNotFoundException(isbn);
+ book.addStock(quantity);
+ bookRepository.save(book);
+ return book.getStock();
+ }
+
+ public int removeStock(String isbn, int quantity) throws BookNotFoundException {
+ Book book = getBookIfDoesNotExistThrowBookNotFoundException(isbn);
+ book.removeStock(quantity);
+ bookRepository.save(book);
+ return book.getStock();
+ }
+
+ public Map findBooksPageSorted(int page, int size, String sortBy) {
+ List allBooks = bookRepository.findAll();
+
+ allBooks.sort(Comparator.comparing(
+ book -> {
+ switch (sortBy.toLowerCase()) {
+ case "titre":
+ case "title":
+ return book.getTitle() == null ? "" : book.getTitle().toLowerCase();
+ case "auteur":
+ case "author":
+ return book.getAuthor() == null ? "" : book.getAuthor().toLowerCase();
+ case "editeur":
+ case "publisher":
+ return book.getPublisher() == null ? "" : book.getPublisher().toLowerCase();
+ default:
+ return book.getTitle() == null ? "" : book.getTitle().toLowerCase();
+ }
+ },
+ Comparator.naturalOrder()
+ ));
+
+ int totalElements = allBooks.size();
+ int totalPages = (int) Math.ceil((double) totalElements / size);
+ int fromIndex = Math.min(page * size, totalElements);
+ int toIndex = Math.min(fromIndex + size, totalElements);
+
+ List content = allBooks.subList(fromIndex, toIndex)
+ .stream()
+ .map(BookConverter::toDTO)
+ .collect(Collectors.toList());
+
+ Map result = new HashMap<>();
+ result.put("content", content);
+ result.put("totalElements", totalElements);
+ result.put("totalPages", totalPages);
+ return result;
+ }
+
+ public List findBooksByTitleContaining(String title) {
+ List books = bookRepository.findByTitleContaining(title);
+ return books.stream().map(BookConverter::toDTO).collect(Collectors.toList());
+ }
+
+ public List findBooksByAuthor(String author) {
+ List books = bookRepository.findByAuthor(author);
+ return books.stream().map(BookConverter::toDTO).collect(Collectors.toList());
+ }
+
+ public List findBooksByPublisher(String publisher) {
+ List books = bookRepository.findByPublisher(publisher);
+ return books.stream().map(BookConverter::toDTO).collect(Collectors.toList());
+ }
+
+ public List findBooksByCategory(Category category) {
+ List books = bookRepository.findByCategory(category);
+ return books.stream().map(BookConverter::toDTO).collect(Collectors.toList());
+ }
+
+ public List findBooksPublishedAfter(LocalDate date) {
+ List books = bookRepository.findPublishedAfter(date);
+ return books.stream().map(BookConverter::toDTO).collect(Collectors.toList());
+ }
+
+ public List findBooksByPriceRange(double min, double max) {
+ List books = bookRepository.findByPriceRange(min, max);
+ return books.stream().map(BookConverter::toDTO).collect(Collectors.toList());
+ }
+
+ private Book getBookIfDoesNotExistThrowBookNotFoundException(String isbn) {
+ return bookRepository.findByIsbn(isbn)
+ .orElseThrow(() -> new BookNotFoundException(isbn));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidator.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidator.java
new file mode 100644
index 0000000..925ca9b
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidator.java
@@ -0,0 +1,62 @@
+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 {
+
+ public static final String ISBN_CANNOT_BE_NULL = "L'ISBN ne peut pas être nul";
+ public static final String TITLE_CANNOT_BE_BLANK = "Le titre ne peut pas être vide";
+ public static final String AUTHOR_CANNOT_BE_BLANK = "L'auteur ne peut pas être vide";
+ public static final String PRICE_MUST_BE_POSITIVE = "Le prix doit être positif";
+ public static final String STOCK_CANNOT_BE_NEGATIVE = "Le stock ne peut pas être négatif";
+ public static final String ISBN_IS_NOT_VALID = "L'ISBN n'est pas valide";
+ public static final String PUBLISHER_CANNOT_BE_BLANK = "L'éditeur ne peut pas être vide";
+ public static final String PUBLICATION_DATE_CANNOT_BE_NULL = "La date de publication ne peut pas être nulle";
+ public static final String CATEGORIES_CANNOT_BE_EMPTY = "La liste des catégories ne peut pas être vide";
+ public static final String DESCRIPTION_CANNOT_BE_BLANK = "La description ne peut pas être vide";
+ public static final String LANGUAGE_CANNOT_BE_BLANK = "La langue ne peut pas être vide";
+
+ private BookValidator() {}
+
+ public static void validate(BookInfo bookInfo) throws NotValidBookException {
+ validateIsbn(bookInfo);
+ validateTitle(bookInfo);
+ validateAuthor(bookInfo);
+ validatePrice(bookInfo);
+ validateStock(bookInfo);
+ }
+
+ private static void validateIsbn(BookInfo bookInfo) throws NotValidBookException {
+ if (bookInfo.isbn() == null) {
+ throw new NotValidBookException(ISBN_CANNOT_BE_NULL);
+ }
+ if (!bookInfo.isbn().matches("\\d{13}")) {
+ throw new NotValidBookException(ISBN_IS_NOT_VALID);
+ }
+ }
+
+ private static void validateTitle(BookInfo bookInfo) throws NotValidBookException {
+ if (bookInfo.title() == null || bookInfo.title().isBlank()) {
+ throw new NotValidBookException(TITLE_CANNOT_BE_BLANK);
+ }
+ }
+
+ private static void validateAuthor(BookInfo bookInfo) throws NotValidBookException {
+ if (bookInfo.author() == null || bookInfo.author().isBlank()) {
+ throw new NotValidBookException(AUTHOR_CANNOT_BE_BLANK);
+ }
+ }
+
+ private static void validatePrice(BookInfo bookInfo) throws NotValidBookException {
+ if (bookInfo.price() < 0) {
+ throw new NotValidBookException(PRICE_MUST_BE_POSITIVE);
+ }
+ }
+
+ private static void validateStock(BookInfo bookInfo) throws NotValidBookException {
+ if (bookInfo.stock() < 0) {
+ throw new NotValidBookException(STOCK_CANNOT_BE_NEGATIVE);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/converter/CustomerConverter.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/converter/CustomerConverter.java
index e420020..c5e1202 100644
--- a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/converter/CustomerConverter.java
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/converter/CustomerConverter.java
@@ -3,28 +3,30 @@ package fr.iut_fbleau.but3.dev62.mylibrary.customer.converter;
import fr.iut_fbleau.but3.dev62.mylibrary.customer.CustomerDTO;
import fr.iut_fbleau.but3.dev62.mylibrary.customer.CustomerInfo;
import fr.iut_fbleau.but3.dev62.mylibrary.customer.entity.Customer;
+import java.util.UUID;
public final class CustomerConverter {
- private CustomerConverter(){
-
+ private CustomerConverter() {
+ // util class
}
public static Customer toDomain(CustomerInfo newCustomer) {
return Customer.builder()
- .firstName(newCustomer.firstName())
- .lastName(newCustomer.lastName())
- .phoneNumber(newCustomer.phoneNumber())
- .loyaltyPoints(0)
- .build();
+ .id(UUID.randomUUID()) // ← génération automatique
+ .firstName(newCustomer.firstName())
+ .lastName(newCustomer.lastName())
+ .phoneNumber(newCustomer.phoneNumber())
+ .loyaltyPoints(0)
+ .build();
}
public static CustomerDTO toDTO(Customer customer) {
return CustomerDTO.builder()
- .id(customer.getId())
- .firstName(customer.getFirstName())
- .lastName(customer.getLastName())
- .phoneNumber(customer.getPhoneNumber())
- .loyaltyPoints(customer.getLoyaltyPoints())
- .build();
+ .id(customer.getId())
+ .firstName(customer.getFirstName())
+ .lastName(customer.getLastName())
+ .phoneNumber(customer.getPhoneNumber())
+ .loyaltyPoints(customer.getLoyaltyPoints())
+ .build();
}
}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/repository/CustomerRepository.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/repository/CustomerRepository.java
index 7d79f32..8ad8ab3 100644
--- a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/repository/CustomerRepository.java
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/repository/CustomerRepository.java
@@ -20,27 +20,35 @@ public final class CustomerRepository {
}
public Customer save(Customer newCustomer) {
- Optional optionalCustomerWithSameId = this.findById(newCustomer.getId());
- optionalCustomerWithSameId.ifPresentOrElse(customers::remove, newCustomer::setRandomUUID);
+
+ if (newCustomer.getId() == null) {
+ newCustomer.setRandomUUID();
+ }
+
+ this.findById(newCustomer.getId()).ifPresent(customers::remove);
+
this.customers.add(newCustomer);
return newCustomer;
}
public Optional findById(UUID uuid) {
+ if (uuid == null) {
+ return Optional.empty();
+ }
return this.customers.stream()
- .filter(customer -> customer.getId().equals(uuid))
- .findFirst();
+ .filter(c -> uuid.equals(c.getId()))
+ .findFirst();
}
public boolean existsById(UUID uuid) {
- return this.customers.stream()
- .anyMatch(customer -> customer.getId().equals(uuid));
+ return uuid != null && this.customers.stream()
+ .anyMatch(c -> uuid.equals(c.getId()));
}
public Optional findByPhoneNumber(String phoneNumber) {
return this.customers.stream()
- .filter(customer -> customer.getPhoneNumber().equals(phoneNumber))
- .findFirst();
+ .filter(c -> c.getPhoneNumber().equals(phoneNumber))
+ .findFirst();
}
public void delete(Customer customer) {
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..53243f9
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/AddressDTO.java
@@ -0,0 +1,14 @@
+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;
+}
+
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..690fd04
--- /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 String paymentMethod;
+ private final List orderLines;
+ private final AddressDTO shippingAddress;
+ private final double totalPrice;
+ private final double totalPriceToPay;
+}
+
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/OrderInfo.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/OrderInfo.java
new file mode 100644
index 0000000..406cc00
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/OrderInfo.java
@@ -0,0 +1,16 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.order;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+import java.util.List;
+
+@Getter
+@Setter
+@Builder
+public class OrderInfo {
+ private String customerId;
+ private String paymentMethod;
+ private List orderLines;
+ private AddressDTO address;
+}
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..18735d9
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/OrderLineDTO.java
@@ -0,0 +1,12 @@
+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;
+}
+
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..a4b3ed4
--- /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/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/converter/OrderConverter.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/converter/OrderConverter.java
new file mode 100644
index 0000000..347902e
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/converter/OrderConverter.java
@@ -0,0 +1,96 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.order.converter;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.order.*;
+import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Address;
+import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Order;
+import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.OrderLine;
+import java.util.UUID;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+public final class OrderConverter {
+
+ private OrderConverter() {
+ throw new AssertionError("Cette classe utilitaire ne doit pas être instanciée");
+ }
+
+ public static Order toDomain(OrderInfo info) {
+ if (info == null) return null;
+
+ return Order.builder()
+ .customerId(Optional.ofNullable(info.getCustomerId())
+ .map(UUID::fromString)
+ .orElse(null))
+ .paymentMethod(Optional.ofNullable(info.getPaymentMethod())
+ .map(PaymentMethod::valueOf)
+ .orElse(null))
+ .orderLines(convertOrderLines(info.getOrderLines()))
+ .shippingAddress(convertAddress(info.getAddress()))
+ .totalPrice(0.0)
+ .build();
+ }
+
+ public static OrderDTO toDTO(Order order) {
+ if (order == null) return null;
+
+ return OrderDTO.builder()
+ .id(order.getId())
+ .customerId(order.getCustomerId())
+ .paymentMethod(Optional.ofNullable(order.getPaymentMethod())
+ .map(PaymentMethod::name)
+ .orElse(null))
+ .orderLines(convertOrderLinesToDTO(order.getOrderLines()))
+ .shippingAddress(convertAddressToDTO(order.getShippingAddress()))
+ .totalPrice(order.getTotalPrice())
+ .totalPriceToPay(order.getTotalPriceToPay())
+ .build();
+ }
+
+ private static List convertOrderLines(List orderLines) {
+ return Optional.ofNullable(orderLines)
+ .map(lines -> lines.stream()
+ .filter(Objects::nonNull)
+ .map(line -> OrderLine.builder()
+ .bookId(line.getBookId())
+ .quantity(line.getQuantity())
+ .build())
+ .toList())
+ .orElse(new ArrayList<>());
+ }
+
+ private static List convertOrderLinesToDTO(List orderLines) {
+ return Optional.ofNullable(orderLines)
+ .map(lines -> lines.stream()
+ .filter(Objects::nonNull)
+ .map(line -> OrderLineDTO.builder()
+ .bookId(line.getBookId())
+ .quantity(line.getQuantity())
+ .build())
+ .toList())
+ .orElse(null);
+ }
+
+ private static Address convertAddress(AddressDTO addressDTO) {
+ return Optional.ofNullable(addressDTO)
+ .map(addr -> Address.builder()
+ .street(addr.getStreet())
+ .city(addr.getCity())
+ .postalCode(addr.getPostalCode())
+ .country(addr.getCountry())
+ .build())
+ .orElse(null);
+ }
+
+ private static AddressDTO convertAddressToDTO(Address address) {
+ return Optional.ofNullable(address)
+ .map(addr -> AddressDTO.builder()
+ .street(addr.getStreet())
+ .city(addr.getCity())
+ .postalCode(addr.getPostalCode())
+ .country(addr.getCountry())
+ .build())
+ .orElse(null);
+ }
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/Address.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/Address.java
new file mode 100644
index 0000000..c15d2d9
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/Address.java
@@ -0,0 +1,14 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.order.entity;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.AllArgsConstructor;
+
+@Getter
+@Builder
+public class Address {
+ private final String street;
+ private final String city;
+ private final String postalCode;
+ private final String country;
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/Order.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/Order.java
new file mode 100644
index 0000000..24974c9
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/Order.java
@@ -0,0 +1,19 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.order.entity;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.order.PaymentMethod;
+import lombok.Builder;
+import lombok.Getter;
+import java.util.List;
+import java.util.UUID;
+
+@Getter
+@Builder
+public class Order {
+ private final UUID id;
+ private final UUID customerId;
+ private final PaymentMethod paymentMethod;
+ private final List orderLines;
+ private final Address shippingAddress;
+ private final double totalPrice;
+ private final double totalPriceToPay;
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/OrderLine.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/OrderLine.java
new file mode 100644
index 0000000..b191b9f
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/entity/OrderLine.java
@@ -0,0 +1,12 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.order.entity;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.AllArgsConstructor;
+import lombok.NoArgsConstructor;
+@Getter
+@Builder
+public class OrderLine {
+ private final String bookId;
+ private final int quantity;
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/IllegalOrderPointException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/IllegalOrderPointException.java
new file mode 100644
index 0000000..78dda69
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/IllegalOrderPointException.java
@@ -0,0 +1,7 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.order.exception;
+
+public class IllegalOrderPointException extends RuntimeException {
+ public IllegalOrderPointException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/InvalidPaymentMethodException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/InvalidPaymentMethodException.java
new file mode 100644
index 0000000..ab59e54
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/InvalidPaymentMethodException.java
@@ -0,0 +1,7 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.order.exception;
+
+public class InvalidPaymentMethodException extends RuntimeException {
+ public InvalidPaymentMethodException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/NotValidOrderException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/NotValidOrderException.java
new file mode 100644
index 0000000..2609b42
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/NotValidOrderException.java
@@ -0,0 +1,7 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.order.exception;
+
+public class NotValidOrderException extends RuntimeException {
+ public NotValidOrderException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/OrderNotFoundException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/OrderNotFoundException.java
new file mode 100644
index 0000000..6f0a48f
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/OrderNotFoundException.java
@@ -0,0 +1,7 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.order.exception;
+
+public class OrderNotFoundException extends RuntimeException {
+ public OrderNotFoundException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/OrderQuantityException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/OrderQuantityException.java
new file mode 100644
index 0000000..69291dd
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/OrderQuantityException.java
@@ -0,0 +1,7 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.order.exception;
+
+public class OrderQuantityException extends RuntimeException {
+ public OrderQuantityException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/WrongAddressException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/WrongAddressException.java
new file mode 100644
index 0000000..ea4e10f
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/exception/WrongAddressException.java
@@ -0,0 +1,7 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.order.exception;
+
+public class WrongAddressException extends RuntimeException {
+ public WrongAddressException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/repository/OrderRepository.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/repository/OrderRepository.java
new file mode 100644
index 0000000..fed6f4a
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/repository/OrderRepository.java
@@ -0,0 +1,60 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.order.repository;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Order;
+import java.util.*;
+import lombok.NoArgsConstructor;
+
+@NoArgsConstructor
+public final class OrderRepository {
+ private final List orders = new ArrayList<>();
+
+ public Order save(Order newOrder) {
+ if (newOrder.getId() == null) {
+ Order orderWithId = Order.builder()
+ .id(UUID.randomUUID())
+ .customerId(newOrder.getCustomerId())
+ .paymentMethod(newOrder.getPaymentMethod())
+ .orderLines(newOrder.getOrderLines())
+ .shippingAddress(newOrder.getShippingAddress())
+ .totalPrice(newOrder.getTotalPrice())
+ .totalPriceToPay(newOrder.getTotalPriceToPay())
+ .build();
+ this.orders.add(orderWithId);
+ return orderWithId;
+ }
+
+ Optional optionalOrderWithSameId = this.findById(newOrder.getId());
+ optionalOrderWithSameId.ifPresent(orders::remove);
+ this.orders.add(newOrder);
+ return newOrder;
+ }
+
+ public Optional findById(UUID id) {
+ return this.orders.stream()
+ .filter(order -> Objects.equals(order.getId(), id))
+ .findFirst();
+ }
+
+ public boolean existsById(UUID id) {
+ return this.orders.stream()
+ .anyMatch(order -> Objects.equals(order.getId(), id));
+ }
+
+ public List findByCustomerId(UUID customerId) {
+ return this.orders.stream()
+ .filter(order -> Objects.equals(order.getCustomerId(), customerId))
+ .toList();
+ }
+
+ public List findAll() {
+ return orders;
+ }
+
+ public void deleteAll() {
+ orders.clear();
+ }
+
+ public void delete(Order order) {
+ this.orders.remove(order);
+ }
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/usecase/OrderUseCase.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/usecase/OrderUseCase.java
new file mode 100644
index 0000000..71eca46
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/usecase/OrderUseCase.java
@@ -0,0 +1,156 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.order.usecase;
+
+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.exception.CustomerNotFoundException;
+import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.IllegalCustomerPointException;
+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.PaymentMethod;
+import fr.iut_fbleau.but3.dev62.mylibrary.order.converter.OrderConverter;
+import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Order;
+import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.*;
+import fr.iut_fbleau.but3.dev62.mylibrary.order.repository.OrderRepository;
+import fr.iut_fbleau.but3.dev62.mylibrary.customer.entity.Customer;
+
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+public final class OrderUseCase {
+ private final OrderRepository orderRepository;
+ private final CustomerRepository customerRepository;
+ private final BookRepository bookRepository;
+
+ public OrderUseCase(OrderRepository orderRepository, CustomerRepository customerRepository, BookRepository bookRepository) {
+ this.orderRepository = orderRepository;
+ this.customerRepository = customerRepository;
+ this.bookRepository = bookRepository;
+ }
+
+ public UUID createOrder(OrderInfo orderInfo) throws CustomerNotFoundException, IllegalCustomerPointException, NotValidOrderException {
+ validateOrderInfo(orderInfo);
+
+ PaymentMethod paymentMethod = parsePaymentMethod(orderInfo.getPaymentMethod());
+ UUID customerId = parseCustomerId(orderInfo.getCustomerId());
+ Customer customer = customerRepository.findById(customerId)
+ .orElseThrow(() -> new CustomerNotFoundException(customerId));
+
+ // Pré-chargement des livres pour éviter les appels multiples
+ Map books = orderInfo.getOrderLines().stream()
+ .map(line -> bookRepository.findByIsbn(line.getBookId())
+ .orElseThrow(() -> new BookNotFoundException("Le livre avec l'ISBN " + line.getBookId() + " n'a pas été trouvé")))
+ .collect(Collectors.toMap(fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book::getIsbn, b -> b));
+
+ validateOrderLines(orderInfo, books);
+
+ double totalPrice = calculateTotalPrice(orderInfo, books);
+ double totalPriceToPay = calculateTotalPriceToPay(orderInfo, books);
+
+ Order domainOrder = OrderConverter.toDomain(orderInfo);
+ Order order = Order.builder()
+ .customerId(customerId)
+ .paymentMethod(paymentMethod)
+ .orderLines(domainOrder.getOrderLines())
+ .shippingAddress(domainOrder.getShippingAddress())
+ .totalPrice(totalPrice)
+ .totalPriceToPay(totalPriceToPay)
+ .build();
+
+ if (paymentMethod == PaymentMethod.LOYALTY_POINTS) {
+ handleLoyaltyPointsPayment(customer, totalPrice);
+ }
+
+ return orderRepository.save(order).getId();
+ }
+
+ private void validateOrderInfo(OrderInfo orderInfo) throws NotValidOrderException {
+ if (orderInfo == null) {
+ throw new NotValidOrderException("La commande ne peut pas être nulle");
+ }
+ if (orderInfo.getOrderLines() == null || orderInfo.getOrderLines().isEmpty()) {
+ throw new OrderQuantityException("La commande doit contenir au moins un livre");
+ }
+ if (orderInfo.getAddress() == null) {
+ throw new NotValidOrderException("L'adresse de livraison est obligatoire");
+ }
+ }
+
+ private PaymentMethod parsePaymentMethod(String paymentMethod) {
+ try {
+ return PaymentMethod.valueOf(paymentMethod);
+ } catch (IllegalArgumentException | NullPointerException e) {
+ throw new InvalidPaymentMethodException("Le mode de paiement " + paymentMethod + " n'est pas valide");
+ }
+ }
+
+ private void validateOrderLines(OrderInfo orderInfo, Map books) {
+ orderInfo.getOrderLines().forEach(line -> {
+ if (line.getQuantity() <= 0) {
+ throw new OrderQuantityException("La quantité doit être supérieure à 0 pour le livre " + line.getBookId());
+ }
+ var book = books.get(line.getBookId());
+ if (book.getStock() < line.getQuantity()) {
+ throw new OrderQuantityException("Stock insuffisant pour le livre " + line.getBookId());
+ }
+ });
+ }
+
+ private double calculateTotalPrice(OrderInfo orderInfo, Map books) {
+ return orderInfo.getOrderLines().stream()
+ .mapToDouble(line -> books.get(line.getBookId()).getPrice() * line.getQuantity())
+ .sum();
+ }
+
+ private double calculateTotalPriceToPay(OrderInfo orderInfo, Map books) {
+ // Appliquer ici les éventuelles remises
+ return calculateTotalPrice(orderInfo, books);
+ }
+
+ private UUID parseOrderId(String orderId) {
+ try {
+ return UUID.fromString(orderId);
+ } catch (IllegalArgumentException e) {
+ throw new OrderNotFoundException("La commande avec l'ID " + orderId + " n'a pas été trouvée");
+ }
+ }
+
+ public OrderDTO findOrderById(String orderId) {
+ UUID uuid = parseOrderId(orderId);
+ return orderRepository.findById(uuid)
+ .map(OrderConverter::toDTO)
+ .orElseThrow(() -> new OrderNotFoundException("La commande n'a pas été trouvée"));
+ }
+
+ public List findOrdersByCustomerId(String customerId) throws CustomerNotFoundException {
+ UUID uuid = parseCustomerId(customerId);
+ customerRepository.findById(uuid)
+ .orElseThrow(() -> new CustomerNotFoundException(uuid));
+ return orderRepository.findByCustomerId(uuid)
+ .stream()
+ .map(OrderConverter::toDTO)
+ .toList();
+ }
+
+ private UUID parseCustomerId(String customerId) throws CustomerNotFoundException {
+ if (customerId == null || customerId.trim().isEmpty()) {
+ throw new CustomerNotFoundException(null);
+ }
+ try {
+ return UUID.fromString(customerId);
+ } catch (IllegalArgumentException e) {
+ throw new CustomerNotFoundException(null);
+ }
+ }
+
+ private void handleLoyaltyPointsPayment(Customer customer, double totalPrice) throws IllegalCustomerPointException {
+ int requiredPoints = (int) (totalPrice * 100);
+ if (customer.getLoyaltyPoints() < requiredPoints) {
+ throw new IllegalOrderPointException("Points de fidélité insuffisants pour payer la commande");
+ }
+ customer.removeLoyaltyPoints(requiredPoints);
+ customerRepository.save(customer);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/validator/OrderValidator.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/validator/OrderValidator.java
new file mode 100644
index 0000000..eb7506c
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/order/validator/OrderValidator.java
@@ -0,0 +1,66 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.order.validator;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Address;
+import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Order;
+import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.NotValidOrderException;
+
+public class OrderValidator {
+
+ public static void validate(Order order) {
+ if (order == null) {
+ throw new NotValidOrderException("La commande ne peut pas être nulle");
+ }
+ validateAddress(order);
+ validateOrderLines(order);
+ validatePrices(order);
+ validatePaymentMethod(order);
+ }
+
+ private static void validateAddress(Order order) {
+ Address address = order.getShippingAddress();
+ if (address == null) {
+ throw new NotValidOrderException("L'adresse de livraison est obligatoire");
+ }
+
+ if (isNullOrBlank(address.getStreet()) ||
+ isNullOrBlank(address.getCity()) ||
+ isNullOrBlank(address.getPostalCode()) ||
+ isNullOrBlank(address.getCountry())) {
+ throw new NotValidOrderException("Tous les champs de l'adresse sont obligatoires");
+ }
+ }
+
+ private static void validateOrderLines(Order order) {
+ if (order.getOrderLines() == null || order.getOrderLines().isEmpty()) {
+ throw new NotValidOrderException("La commande doit contenir au moins une ligne");
+ }
+
+ order.getOrderLines().forEach(line -> {
+ if (line.getQuantity() <= 0) {
+ throw new NotValidOrderException("La quantité doit être positive");
+ }
+ });
+ }
+
+ private static void validatePrices(Order order) {
+ if (order.getTotalPrice() <= 0) {
+ throw new NotValidOrderException("Le prix total doit être positif");
+ }
+ if (order.getTotalPriceToPay() <= 0) {
+ throw new NotValidOrderException("Le prix total à payer doit être positif");
+ }
+ if (order.getTotalPriceToPay() > order.getTotalPrice()) {
+ throw new NotValidOrderException("Le prix à payer ne peut pas être supérieur au prix total");
+ }
+ }
+
+ private static void validatePaymentMethod(Order order) {
+ if (order.getPaymentMethod() == null) {
+ throw new NotValidOrderException("Le mode de paiement ne peut pas être nul");
+ }
+ }
+
+ private static boolean isNullOrBlank(String value) {
+ return value == null || value.trim().isEmpty();
+ }
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/ReviewDto.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/ReviewDto.java
new file mode 100644
index 0000000..232a984
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/ReviewDto.java
@@ -0,0 +1,19 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.review;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+
+import java.util.UUID;
+
+@Builder
+@Getter
+@AllArgsConstructor
+public class ReviewDto {
+ private final UUID reviewId;
+ private final long bookId;
+ private final String customerName;
+ private final String comment;
+ private final int rating;
+}
+
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/ReviewInfo.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/ReviewInfo.java
new file mode 100644
index 0000000..df02df8
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/ReviewInfo.java
@@ -0,0 +1,16 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.review;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+
+@Builder
+@Getter
+@AllArgsConstructor
+public class ReviewInfo {
+ private final String customerId;
+ private final long isbn;
+ private final int rating;
+ private final String comment;
+}
+
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/converter/ReviewConverter.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/converter/ReviewConverter.java
new file mode 100644
index 0000000..a7584d2
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/converter/ReviewConverter.java
@@ -0,0 +1,29 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.review.converter;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.review.ReviewDto;
+import fr.iut_fbleau.but3.dev62.mylibrary.review.ReviewInfo;
+import fr.iut_fbleau.but3.dev62.mylibrary.review.entity.Review;
+
+import java.util.UUID;
+
+public class ReviewConverter {
+ public static ReviewDto toDto(Review review, UUID reviewId, String customerName) {
+ return ReviewDto.builder()
+ .reviewId(reviewId)
+ .bookId(review.getIsbn())
+ .customerName(customerName)
+ .comment(review.getComment())
+ .rating(review.getRating())
+ .build();
+ }
+
+ public static Review toDomain(ReviewInfo info) {
+ return Review.builder()
+ .customerId(UUID.fromString(info.getCustomerId()))
+ .isbn(info.getIsbn())
+ .rating(info.getRating())
+ .comment(info.getComment())
+ .build();
+ }
+}
+
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/entity/Review.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/entity/Review.java
new file mode 100644
index 0000000..20376e5
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/entity/Review.java
@@ -0,0 +1,19 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.review.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.util.UUID;
+
+@Builder
+@Getter
+@AllArgsConstructor
+@NoArgsConstructor
+public class Review {
+ private UUID customerId;
+ private long isbn;
+ private int rating;
+ private String comment;
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/InvalidReviewRatingException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/InvalidReviewRatingException.java
new file mode 100644
index 0000000..0b07448
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/InvalidReviewRatingException.java
@@ -0,0 +1,8 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.review.exception;
+
+public class InvalidReviewRatingException extends RuntimeException {
+ public InvalidReviewRatingException(String message) {
+ super(message);
+ }
+}
+
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/ReviewAlreadyExistsException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/ReviewAlreadyExistsException.java
new file mode 100644
index 0000000..5f92fa6
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/ReviewAlreadyExistsException.java
@@ -0,0 +1,8 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.review.exception;
+
+public class ReviewAlreadyExistsException extends RuntimeException {
+ public ReviewAlreadyExistsException(String message) {
+ super(message);
+ }
+}
+
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/ReviewNotFoundException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/ReviewNotFoundException.java
new file mode 100644
index 0000000..4b3268a
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/ReviewNotFoundException.java
@@ -0,0 +1,8 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.review.exception;
+
+public class ReviewNotFoundException extends RuntimeException {
+ public ReviewNotFoundException(String message) {
+ super(message);
+ }
+}
+
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/repository/ReviewRepository.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/repository/ReviewRepository.java
new file mode 100644
index 0000000..2e5e26e
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/repository/ReviewRepository.java
@@ -0,0 +1,44 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.review.repository;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.review.entity.Review;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class ReviewRepository {
+ private final List reviews = new ArrayList<>();
+
+ public void save(Review review) {
+ reviews.add(review);
+ }
+
+ public List findAll() {
+ return new ArrayList<>(reviews);
+ }
+
+ public List findByIsbn(long isbn) {
+ return reviews.stream().filter(r -> r.getIsbn() == isbn).collect(Collectors.toList());
+ }
+
+ public List findByCustomerId(UUID customerId) {
+ return reviews.stream().filter(r -> r.getCustomerId().equals(customerId)).collect(Collectors.toList());
+ }
+
+ public boolean existsByCustomerIdAndIsbn(UUID customerId, long isbn) {
+ return reviews.stream().anyMatch(r -> r.getCustomerId().equals(customerId) && r.getIsbn() == isbn);
+ }
+
+ public void deleteByCustomerIdAndIsbn(UUID customerId, long isbn) {
+ reviews.removeIf(r -> r.getCustomerId().equals(customerId) && r.getIsbn() == isbn);
+ }
+
+ public void update(Review review) {
+ deleteByCustomerIdAndIsbn(review.getCustomerId(), review.getIsbn());
+ save(review);
+ }
+
+ public void deleteAll() {
+ reviews.clear();
+ }
+}
+
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/usecase/ReviewUseCase.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/usecase/ReviewUseCase.java
new file mode 100644
index 0000000..0225291
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/usecase/ReviewUseCase.java
@@ -0,0 +1,68 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.review.usecase;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.book.repository.BookRepository;
+import fr.iut_fbleau.but3.dev62.mylibrary.customer.repository.CustomerRepository;
+import fr.iut_fbleau.but3.dev62.mylibrary.review.entity.Review;
+import fr.iut_fbleau.but3.dev62.mylibrary.review.exception.InvalidReviewRatingException;
+import fr.iut_fbleau.but3.dev62.mylibrary.review.exception.ReviewAlreadyExistsException;
+import fr.iut_fbleau.but3.dev62.mylibrary.review.exception.ReviewNotFoundException;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.BookNotFoundException;
+import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.CustomerNotFoundException;
+import fr.iut_fbleau.but3.dev62.mylibrary.review.repository.ReviewRepository;
+import fr.iut_fbleau.but3.dev62.mylibrary.review.validator.ReviewValidator;
+
+import java.util.List;
+import java.util.UUID;
+
+public class ReviewUseCase {
+ private final ReviewRepository reviewRepository;
+ private final BookRepository bookRepository;
+ private final CustomerRepository customerRepository;
+
+ public ReviewUseCase(ReviewRepository reviewRepository, BookRepository bookRepository, CustomerRepository customerRepository) {
+ this.reviewRepository = reviewRepository;
+ this.bookRepository = bookRepository;
+ this.customerRepository = customerRepository;
+ }
+
+ public void submitReview(Review review) throws CustomerNotFoundException {
+ ReviewValidator.validate(review);
+
+ if (!bookRepository.existsByIsbn(String.valueOf(review.getIsbn()))) {
+ throw new BookNotFoundException("Book not found: " + review.getIsbn());
+ }
+
+ if (!customerRepository.existsById(review.getCustomerId())) {
+ throw new CustomerNotFoundException(review.getCustomerId());
+ }
+
+ if (reviewRepository.existsByCustomerIdAndIsbn(review.getCustomerId(), review.getIsbn())) {
+ throw new ReviewAlreadyExistsException("Review already exists for this customer and book.");
+ }
+
+ reviewRepository.save(review);
+ }
+
+ public List getReviewsByBook(long isbn) {
+ return reviewRepository.findByIsbn(isbn);
+ }
+
+ public List getReviewsByCustomer(UUID customerId) {
+ return reviewRepository.findByCustomerId(customerId);
+ }
+
+ public void updateReview(Review review) {
+ ReviewValidator.validate(review);
+ if (!reviewRepository.existsByCustomerIdAndIsbn(review.getCustomerId(), review.getIsbn())) {
+ throw new ReviewNotFoundException("Review not found for this customer and book.");
+ }
+ reviewRepository.update(review);
+ }
+
+ public void deleteReview(long isbn, UUID customerId) {
+ if (!reviewRepository.existsByCustomerIdAndIsbn(customerId, isbn)) {
+ throw new ReviewNotFoundException("Review not found for this customer and book.");
+ }
+ reviewRepository.deleteByCustomerIdAndIsbn(customerId, isbn);
+ }
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/validator/ReviewValidator.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/validator/ReviewValidator.java
new file mode 100644
index 0000000..ace3c79
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/validator/ReviewValidator.java
@@ -0,0 +1,23 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.review.validator;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.review.entity.Review;
+
+public class ReviewValidator {
+ public static void validate(Review review) {
+ if (review == null) {
+ throw new IllegalArgumentException("Review cannot be null");
+ }
+ if (review.getRating() < 1 || review.getRating() > 5) {
+ throw new fr.iut_fbleau.but3.dev62.mylibrary.review.exception.InvalidReviewRatingException("Rating must be between 1 and 5");
+ }
+ if (review.getComment() == null || review.getComment().trim().isEmpty()) {
+ throw new IllegalArgumentException("Comment cannot be empty");
+ }
+ if (review.getCustomerId() == null) {
+ throw new IllegalArgumentException("CustomerId cannot be null");
+ }
+ if (review.getIsbn() <= 0) {
+ throw new IllegalArgumentException("ISBN must be positive");
+ }
+ }
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/SubscriptionDTO.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/SubscriptionDTO.java
new file mode 100644
index 0000000..6447380
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/SubscriptionDTO.java
@@ -0,0 +1,15 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.subscription;
+
+import lombok.Builder;
+import lombok.Getter;
+import java.util.UUID;
+
+@Builder
+@Getter
+public class SubscriptionDTO {
+ private final UUID id;
+ private final UUID customerId;
+ private final Integer duration;
+ private final String paymentMethod;
+ private final String debutDate;
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/SubscriptionInfo.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/SubscriptionInfo.java
new file mode 100644
index 0000000..2bf5638
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/SubscriptionInfo.java
@@ -0,0 +1,7 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.subscription;
+
+import java.util.UUID;
+
+public record SubscriptionInfo(UUID customerId, Integer duration, String paymentMethod) {
+
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/converter/SubscriptionConverter.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/converter/SubscriptionConverter.java
new file mode 100644
index 0000000..9f8aaca
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/converter/SubscriptionConverter.java
@@ -0,0 +1,31 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.subscription.converter;
+
+
+import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionDTO;
+import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionInfo;
+import fr.iut_fbleau.but3.dev62.mylibrary.subscription.entity.Subscription;
+
+public final class SubscriptionConverter {
+ private SubscriptionConverter() {
+
+ }
+
+ public static Subscription toDomain(SubscriptionInfo newSubscription) {
+ return Subscription.builder()
+ .customerId(newSubscription.customerId())
+ .duration(newSubscription.duration())
+ .paymentMethod(newSubscription.paymentMethod())
+ .build();
+ }
+
+ public static SubscriptionDTO toDTO(Subscription subscription) {
+ return SubscriptionDTO.builder()
+ .id(subscription.getId())
+ .customerId(subscription.getCustomerId())
+ .duration(subscription.getDuration())
+ .paymentMethod(subscription.getPaymentMethod())
+ .debutDate(subscription.getDebutDate())
+ .build();
+ }
+
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/entity/Subscription.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/entity/Subscription.java
new file mode 100644
index 0000000..7d0efe3
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/entity/Subscription.java
@@ -0,0 +1,24 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.subscription.entity;
+
+import java.util.UUID;
+import lombok.Builder;
+import lombok.Getter;
+import java.time.LocalDate;
+
+@Builder
+@Getter
+public class Subscription {
+ private UUID id;
+ private UUID customerId;
+ private Integer duration;
+ private String paymentMethod;
+ private String debutDate;
+
+ public void setRandomUUID() {
+ this.id = UUID.randomUUID();
+ }
+
+ public void setDebutDate(String debutDate) {
+ this.debutDate = LocalDate.now().toString();
+ }
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/exception/NotValidSubscriptionException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/exception/NotValidSubscriptionException.java
new file mode 100644
index 0000000..4ab97d2
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/exception/NotValidSubscriptionException.java
@@ -0,0 +1,8 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception;
+
+public class NotValidSubscriptionException extends Exception {
+
+ public NotValidSubscriptionException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/exception/SubscriptionNotFoundException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/exception/SubscriptionNotFoundException.java
new file mode 100644
index 0000000..3b71ab1
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/exception/SubscriptionNotFoundException.java
@@ -0,0 +1,13 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception;
+
+import java.text.MessageFormat;
+import java.util.UUID;
+
+public class SubscriptionNotFoundException extends Exception {
+
+ public static final String THE_SUBSCRIPTION_WITH_ID_DOES_NOT_EXIST_MESSAGE = "The subscription with id {0} does not exist";
+
+ public SubscriptionNotFoundException(UUID uuid) {
+ super(MessageFormat.format(THE_SUBSCRIPTION_WITH_ID_DOES_NOT_EXIST_MESSAGE, uuid));
+ }
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/repository/SubscriptionRepository.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/repository/SubscriptionRepository.java
new file mode 100644
index 0000000..d7fef2b
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/repository/SubscriptionRepository.java
@@ -0,0 +1,42 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.subscription.repository;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.customer.entity.Customer;
+import fr.iut_fbleau.but3.dev62.mylibrary.subscription.entity.Subscription;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+import lombok.NoArgsConstructor;
+
+public class SubscriptionRepository {
+ private final List subscriptions = new ArrayList<>();
+
+ public List findAll() {
+ return subscriptions;
+ }
+
+ public Subscription save(Subscription newSubscription) {
+ Optional optionalSubscriptionWithSameId = this.findByCustomerId(newSubscription.getCustomerId());
+ optionalSubscriptionWithSameId.ifPresentOrElse(subscriptions::remove, newSubscription::setRandomUUID);
+ this.subscriptions.add(newSubscription);
+ return newSubscription;
+ }
+
+ public Optional findByCustomerId(UUID uuid) {
+ return this.subscriptions.stream()
+ .filter(subscription -> subscription.getCustomerId().equals(uuid))
+ .findFirst();
+ }
+
+ public Optional findById(UUID id) {
+ return this.subscriptions.stream()
+ .filter(subscription -> subscription.getId().equals(id))
+ .findFirst();
+ }
+
+ public boolean existsById(UUID uuid) {
+ return this.subscriptions.stream()
+ .anyMatch(subscription -> subscription.getId().equals(uuid));
+ }
+
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/usecase/SubscriptionUseCase.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/usecase/SubscriptionUseCase.java
new file mode 100644
index 0000000..a8c5e24
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/usecase/SubscriptionUseCase.java
@@ -0,0 +1,45 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.subscription.usecase;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionDTO;
+import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionInfo;
+import fr.iut_fbleau.but3.dev62.mylibrary.subscription.converter.SubscriptionConverter;
+import fr.iut_fbleau.but3.dev62.mylibrary.subscription.entity.Subscription;
+import fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception.SubscriptionNotFoundException;
+import fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception.NotValidSubscriptionException;
+import fr.iut_fbleau.but3.dev62.mylibrary.subscription.repository.SubscriptionRepository;
+import fr.iut_fbleau.but3.dev62.mylibrary.subscription.validator.SubscriptionValidator;
+import java.util.Optional;
+import java.util.UUID;
+
+public class SubscriptionUseCase {
+
+ private final SubscriptionRepository subscriptionRepository;
+
+ public SubscriptionUseCase(SubscriptionRepository subscriptionRepository) {
+ this.subscriptionRepository = subscriptionRepository;
+ }
+
+ public UUID registerSubscription(SubscriptionInfo newSubscription) throws NotValidSubscriptionException {
+ SubscriptionValidator.validate(newSubscription);
+ Subscription subscriptionToRegister = SubscriptionConverter.toDomain(newSubscription);
+ Subscription subscriptionToRegistered = subscriptionRepository.save(subscriptionToRegister);
+ if (subscriptionToRegistered.getDuration() <= 0) {
+ throw new NotValidSubscriptionException("Duration must be positive");
+ }
+ return subscriptionToRegistered.getId();
+ }
+
+ public Optional findSubscriptionByCustomerId(UUID customerId) {
+ Optional optionalSubscription = subscriptionRepository.findByCustomerId(customerId);
+ return optionalSubscription.map(SubscriptionConverter::toDTO);
+ }
+
+ private boolean getSubscriptionIfDoesNotExistThrowSubscriptionNotFoundException(UUID uuid)
+ throws SubscriptionNotFoundException {
+ if (subscriptionRepository.existsById(uuid)) {
+ throw new SubscriptionNotFoundException(uuid);
+ }
+ return subscriptionRepository.existsById(uuid);
+ }
+
+}
diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/validator/SubscriptionValidator.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/validator/SubscriptionValidator.java
new file mode 100644
index 0000000..07de674
--- /dev/null
+++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/validator/SubscriptionValidator.java
@@ -0,0 +1,44 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.subscription.validator;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.customer.CustomerInfo;
+import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.NotValidCustomerException;
+import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionDTO;
+import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionInfo;
+import fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception.NotValidSubscriptionException;
+
+public class SubscriptionValidator {
+
+ public static final String CUSTOMER_ID_CANNOT_BE_NULL = "Customer ID cannot be null";
+ public static final String DURATION_CANNOT_BE_NULL = "Duration is not valid";
+ public static final String PAYMENT_METHOD_CANNOT_BE_BLANK = "Payment Method cannot be blank";
+
+
+ private SubscriptionValidator() {
+
+ }
+
+ public static void validate(SubscriptionInfo newSubscription) throws NotValidSubscriptionException {
+ validateCustomerId(newSubscription);
+ validateDuration(newSubscription);
+ validatePaymentMethod(newSubscription);
+ }
+
+ private static void validateCustomerId(SubscriptionInfo newSubscription) throws NotValidSubscriptionException {
+ if (newSubscription.customerId() == null) {
+ throw new NotValidSubscriptionException(CUSTOMER_ID_CANNOT_BE_NULL);
+ }
+ }
+
+ private static void validateDuration(SubscriptionInfo newSubscription) throws NotValidSubscriptionException {
+ if (newSubscription.duration() == null) {
+ throw new NotValidSubscriptionException(DURATION_CANNOT_BE_NULL);
+ }
+ }
+
+ private static void validatePaymentMethod(SubscriptionInfo newSubscription) throws NotValidSubscriptionException {
+ if (newSubscription.paymentMethod().isBlank()) {
+ throw new NotValidSubscriptionException(PAYMENT_METHOD_CANNOT_BE_BLANK);
+ }
+ }
+
+}
diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/converter/BookConverterTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/converter/BookConverterTest.java
new file mode 100644
index 0000000..ef43cb8
--- /dev/null
+++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/converter/BookConverterTest.java
@@ -0,0 +1,149 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.book.converter;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.book.BookDTO;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.BookInfo;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.Category;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@DisplayName("Tests unitaires du BookConverter")
+class BookConverterTest {
+
+ @Nested
+ @DisplayName("Tests de la méthode toDomain()")
+ class ToDomainTests {
+
+ @Test
+ @DisplayName("Doit convertir BookInfo en Book (objet domaine) avec les valeurs par défaut")
+ void shouldConvertBookInfoToDomain() {
+ // Given
+ BookInfo info = new BookInfo(
+ "1234567890123",
+ "Titre",
+ "Auteur",
+ "Editeur",
+ LocalDate.of(2020, 1, 1),
+ 19.99,
+ 10,
+ List.of(Category.FICTION),
+ "Description",
+ "Francais"
+ );
+
+ // When
+ Book result = BookConverter.toDomain(info);
+
+ // Then
+ assertNotNull(result);
+ assertEquals(info.isbn(), result.getIsbn());
+ assertEquals(info.title(), result.getTitle());
+ assertEquals(info.author(), result.getAuthor());
+ assertEquals(info.publisher(), result.getPublisher());
+ assertEquals(info.publicationDate(), result.getPublicationDate());
+ assertEquals(info.price(), result.getPrice());
+ assertEquals(info.stock(), result.getStock());
+ assertEquals(info.categories(), result.getCategories());
+ assertEquals(info.description(), result.getDescription());
+ assertEquals(info.language(), result.getLanguage());
+ }
+ }
+
+ @Nested
+ @DisplayName("Tests de la méthode toDTO()")
+ class ToDTOTests {
+
+ @Test
+ @DisplayName("Doit convertir Book (domaine) en BookDTO avec tous les champs correctement mappés")
+ void shouldConvertBookToDTO() {
+ Book book = Book.builder()
+ .isbn("9876543210123")
+ .title("Titre2")
+ .author("Auteur2")
+ .publisher("Editeur2")
+ .publicationDate(LocalDate.of(2021, 5, 10))
+ .price(25.5)
+ .stock(5)
+ .categories(List.of(Category.FANTASY))
+ .description("Desc2")
+ .language("Anglais")
+ .build();
+
+ BookDTO result = BookConverter.toDTO(book);
+
+ assertNotNull(result);
+ assertEquals(book.getIsbn(), result.getIsbn());
+ assertEquals(book.getTitle(), result.getTitle());
+ assertEquals(book.getAuthor(), result.getAuthor());
+ assertEquals(book.getPublisher(), result.getPublisher());
+ assertEquals(book.getPublicationDate(), result.getPublicationDate());
+ assertEquals(book.getPrice(), result.getPrice());
+ assertEquals(book.getStock(), result.getStock());
+ assertEquals(book.getCategories(), result.getCategories());
+ assertEquals(book.getDescription(), result.getDescription());
+ assertEquals(book.getLanguage(), result.getLanguage());
+ }
+ }
+
+ @Test
+ @DisplayName("Doit gérer correctement les valeurs null lors de la conversion")
+ void shouldHandleNullValuesGracefully() {
+ Book book = Book.builder()
+ .isbn("0")
+ .title(null)
+ .author(null)
+ .publisher(null)
+ .publicationDate(null)
+ .price(0.0)
+ .stock(0)
+ .categories(Collections.emptyList())
+ .description(null)
+ .language(null)
+ .build();
+
+ BookDTO result = BookConverter.toDTO(book);
+
+ assertNotNull(result);
+ assertNull(result.getTitle());
+ assertNull(result.getAuthor());
+ assertNull(result.getPublisher());
+ assertNull(result.getPublicationDate());
+ assertNull(result.getDescription());
+ assertNull(result.getLanguage());
+ assertEquals(Collections.emptyList(), result.getCategories());
+ }
+
+ @Test
+ @DisplayName("Doit préserver les chaînes vides lors de la conversion")
+ void shouldPreserveEmptyStrings() {
+ BookInfo info = new BookInfo(
+ "1111111111111",
+ "",
+ "",
+ "",
+ LocalDate.of(2022, 2, 2),
+ 0.0,
+ 0,
+ Collections.emptyList(),
+ "",
+ ""
+ );
+
+ Book domainResult = BookConverter.toDomain(info);
+ BookDTO dtoResult = BookConverter.toDTO(domainResult);
+
+ assertEquals("", dtoResult.getTitle());
+ assertEquals("", dtoResult.getAuthor());
+ assertEquals("", dtoResult.getPublisher());
+ assertEquals("", dtoResult.getDescription());
+ assertEquals("", dtoResult.getLanguage());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/entity/BookTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/entity/BookTest.java
new file mode 100644
index 0000000..257a3ef
--- /dev/null
+++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/entity/BookTest.java
@@ -0,0 +1,128 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.book.entity;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.book.Category;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@DisplayName("Tests unitaires de la classe Book")
+class BookTest {
+
+ @Test
+ @DisplayName("Le builder doit créer une instance Book valide")
+ void testBookBuilder() {
+ String isbn = "9781234567890";
+ String title = "Les Mysteres de l'IA";
+ String author = "Jean Dupont";
+ String publisher = "TechEditions";
+ LocalDate publicationDate = LocalDate.of(2024, 1, 15);
+ double price = 29.99;
+ int stock = 10;
+ List categories = List.of(Category.SCIENCE);
+ String description = "Un livre fascinant sur l'IA.";
+ String language = "Francais";
+
+ Book book = Book.builder()
+ .isbn(isbn)
+ .title(title)
+ .author(author)
+ .publisher(publisher)
+ .publicationDate(publicationDate)
+ .price(price)
+ .stock(stock)
+ .categories(categories)
+ .description(description)
+ .language(language)
+ .build();
+
+ assertEquals(isbn, book.getIsbn());
+ assertEquals(title, book.getTitle());
+ assertEquals(author, book.getAuthor());
+ assertEquals(publisher, book.getPublisher());
+ assertEquals(publicationDate, book.getPublicationDate());
+ assertEquals(price, book.getPrice());
+ assertEquals(stock, book.getStock());
+ assertEquals(categories, book.getCategories());
+ assertEquals(description, book.getDescription());
+ assertEquals(language, book.getLanguage());
+ }
+
+ @Nested
+ @DisplayName("Tests sur la gestion du stock")
+ class StockTests {
+
+ @Test
+ @DisplayName("addStock doit correctement incrémenter le stock")
+ void testAddStock() {
+ Book book = Book.builder().stock(10).build();
+ int toAdd = 5;
+ int expected = 15;
+
+ book.addStock(toAdd);
+
+ assertEquals(expected, book.getStock());
+ }
+
+ @Test
+ @DisplayName("addStock doit gérer l'ajout de zéro correctement")
+ void testAddZeroStock() {
+ Book book = Book.builder().stock(10).build();
+
+ book.addStock(0);
+
+ assertEquals(10, book.getStock());
+ }
+
+ @Test
+ @DisplayName("removeStock doit correctement décrémenter le stock")
+ void testRemoveStock() {
+ Book book = Book.builder().stock(10).build();
+ int toRemove = 4;
+ int expected = 6;
+
+ book.removeStock(toRemove);
+
+ assertEquals(expected, book.getStock());
+ }
+
+ @Test
+ @DisplayName("removeStock doit permettre de retirer tout le stock")
+ void testRemoveAllStock() {
+ Book book = Book.builder().stock(10).build();
+
+ book.removeStock(10);
+
+ assertEquals(0, book.getStock());
+ }
+
+ @Test
+ @DisplayName("removeStock doit lever une exception si on retire plus que le stock disponible")
+ void testRemoveTooMuchStock() {
+ Book book = Book.builder().stock(10).build();
+ int toRemove = 15;
+
+ Exception exception = assertThrows(
+ IllegalArgumentException.class,
+ () -> book.removeStock(toRemove)
+ );
+
+ assertEquals(10, book.getStock());
+ assertTrue(exception.getMessage().contains(String.valueOf(toRemove)));
+ }
+
+ @Test
+ @DisplayName("removeStock doit gérer le retrait de zéro correctement")
+ void testRemoveZeroStock() {
+ Book book = Book.builder().stock(10).build();
+
+ book.removeStock(0);
+
+ assertEquals(10, book.getStock());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/BookNotFoundExceptionTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/BookNotFoundExceptionTest.java
new file mode 100644
index 0000000..5d6bc99
--- /dev/null
+++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/BookNotFoundExceptionTest.java
@@ -0,0 +1,45 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.book.exception;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class BookNotFoundExceptionTest {
+
+ @Test
+ @DisplayName("Le message de l'exception doit contenir l'ISBN fourni")
+ void testExceptionMessageContainsIsbn() {
+ String isbn = "9781234567890";
+
+ BookNotFoundException exception = new BookNotFoundException(isbn);
+
+ String expectedMessage = String.format("The book with isbn %s does not exist", isbn);
+ assertEquals(expectedMessage, exception.getMessage());
+ }
+
+ @Test
+ @DisplayName("L'exception doit utiliser le bon format de message constant")
+ void testExceptionUsesConstantMessageFormat() {
+ String isbn = "9781234567890";
+
+ BookNotFoundException exception = new BookNotFoundException(isbn);
+
+ String expectedFormatWithPlaceholder = "The book with isbn {0} does not exist";
+ assertEquals(BookNotFoundException.THE_BOOK_WITH_ISBN_DOES_NOT_EXIST_MESSAGE, expectedFormatWithPlaceholder);
+ assertTrue(exception.getMessage().contains(isbn));
+ }
+
+ @Test
+ @DisplayName("L'exception doit pouvoir être levée et attrapée correctement")
+ void testExceptionCanBeThrownAndCaught() {
+ String isbn = "9781234567890";
+
+ try {
+ throw new BookNotFoundException(isbn);
+ } catch (BookNotFoundException e) {
+ String expectedMessage = String.format("The book with isbn %s does not exist", isbn);
+ assertEquals(expectedMessage, e.getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/NotValidBookExceptionTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/NotValidBookExceptionTest.java
new file mode 100644
index 0000000..4c312fa
--- /dev/null
+++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/NotValidBookExceptionTest.java
@@ -0,0 +1,61 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.book.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 NotValidBookExceptionTest {
+
+ @Test
+ @DisplayName("L'exception doit être créée avec le message fourni")
+ void testExceptionCreation() {
+ String errorMessage = "Les données du livre ne sont pas valides";
+
+ NotValidBookException exception = new NotValidBookException(errorMessage);
+
+ assertEquals(errorMessage, exception.getMessage());
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "Le titre est obligatoire",
+ "L'ISBN est invalide",
+ "Le prix doit être positif",
+ "La quantité ne peut pas être négative"
+ })
+ @DisplayName("L'exception doit gérer différents messages de validation")
+ void testExceptionWithDifferentMessages(String errorMessage) {
+ NotValidBookException exception = new NotValidBookException(errorMessage);
+
+ assertEquals(errorMessage, exception.getMessage());
+ }
+
+ @Test
+ @DisplayName("L'exception doit pouvoir être levée et attrapée correctement")
+ void testExceptionCanBeThrownAndCaught() {
+ String errorMessage = "Champ requis manquant";
+
+ Exception exception = assertThrows(NotValidBookException.class, () -> {
+ throw new NotValidBookException(errorMessage);
+ });
+
+ assertEquals(errorMessage, exception.getMessage());
+ }
+
+ @Test
+ @DisplayName("L'exception doit être attrapable comme une Exception générale")
+ void testExceptionInheritance() {
+ String errorMessage = "Livre invalide";
+
+ try {
+ throw new NotValidBookException(errorMessage);
+ } catch (Exception e) {
+ assertEquals(NotValidBookException.class, e.getClass());
+ assertEquals(errorMessage, e.getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/repository/BookRepositoryTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/repository/BookRepositoryTest.java
new file mode 100644
index 0000000..73aa8c0
--- /dev/null
+++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/repository/BookRepositoryTest.java
@@ -0,0 +1,205 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.book.repository;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book;
+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.List;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class BookRepositoryTest {
+
+ private BookRepository repository;
+ private Book book1;
+ private Book book2;
+
+ @BeforeEach
+ void setUp() {
+ repository = new BookRepository();
+
+ book1 = Book.builder()
+ .isbn("9780132350884")
+ .title("Clean Code")
+ .author("Robert C. Martin")
+ .stock(10)
+ .price(35.0)
+ .build();
+
+ book2 = Book.builder()
+ .isbn("9780134685991")
+ .title("Effective Java")
+ .author("Joshua Bloch")
+ .stock(5)
+ .price(40.0)
+ .build();
+ }
+
+ @Test
+ @DisplayName("Le repository nouvellement créé doit être vide")
+ void testNewRepositoryIsEmpty() {
+ List books = repository.findAll();
+
+ assertTrue(books.isEmpty());
+ assertEquals(0, books.size());
+ }
+
+ @Nested
+ @DisplayName("Opérations de sauvegarde")
+ class SaveOperations {
+
+ @Test
+ @DisplayName("Save doit ajouter un nouveau livre")
+ void testSaveNewBook() {
+ Book savedBook = repository.save(book1);
+
+ assertEquals(1, repository.findAll().size());
+ assertEquals(book1.getIsbn(), savedBook.getIsbn());
+ assertEquals(book1.getTitle(), savedBook.getTitle());
+ }
+
+ @Test
+ @DisplayName("Save doit mettre à jour un livre existant avec le même ISBN")
+ void testSaveUpdatesExistingBook() {
+ repository.save(book1);
+
+ String isbn = book1.getIsbn();
+ Book updatedBook = Book.builder()
+ .isbn(isbn)
+ .title("Clean Code (2nd Edition)")
+ .author("Robert C. Martin")
+ .stock(15)
+ .price(38.0)
+ .build();
+
+ Book savedBook = repository.save(updatedBook);
+
+ assertEquals(1, repository.findAll().size());
+ assertEquals(isbn, savedBook.getIsbn());
+ assertEquals("Clean Code (2nd Edition)", savedBook.getTitle());
+ assertEquals(15, savedBook.getStock());
+ assertEquals(38.0, savedBook.getPrice());
+ }
+
+ @Test
+ @DisplayName("Save de plusieurs livres doit tous les ajouter")
+ void testSaveMultipleBooks() {
+ repository.save(book1);
+ repository.save(book2);
+
+ List books = repository.findAll();
+
+ assertEquals(2, books.size());
+ assertTrue(books.contains(book1));
+ assertTrue(books.contains(book2));
+ }
+ }
+
+ @Nested
+ @DisplayName("Opérations de recherche")
+ class FindOperations {
+
+ @BeforeEach
+ void setUpBooks() {
+ repository.save(book1);
+ repository.save(book2);
+ }
+
+ @Test
+ @DisplayName("FindAll doit retourner tous les livres")
+ void testFindAll() {
+ List books = repository.findAll();
+
+ assertEquals(2, books.size());
+ assertTrue(books.contains(book1));
+ assertTrue(books.contains(book2));
+ }
+
+ @Test
+ @DisplayName("FindByIsbn doit retourner le livre correspondant à l'ISBN")
+ void testFindByIsbn() {
+ Optional foundBook = repository.findByIsbn(book1.getIsbn());
+
+ assertTrue(foundBook.isPresent());
+ assertEquals(book1.getTitle(), foundBook.get().getTitle());
+ assertEquals(book1.getAuthor(), foundBook.get().getAuthor());
+ }
+
+ @Test
+ @DisplayName("FindByIsbn doit retourner un Optional vide si l'ISBN n'existe pas")
+ void testFindByIsbnNotFound() {
+ Optional foundBook = repository.findByIsbn("0");
+
+ assertTrue(foundBook.isEmpty());
+ }
+
+ @Test
+ @DisplayName("ExistsByIsbn doit retourner true si l'ISBN existe")
+ void testExistsByIsbnExists() {
+ boolean exists = repository.existsByIsbn(book1.getIsbn());
+
+ assertTrue(exists);
+ }
+
+ @Test
+ @DisplayName("ExistsByIsbn doit retourner false si l'ISBN n'existe pas")
+ void testExistsByIsbnNotExists() {
+ boolean exists = repository.existsByIsbn("0");
+
+ assertFalse(exists);
+ }
+ }
+
+ @Nested
+ @DisplayName("Opérations de suppression")
+ class DeleteOperations {
+
+ @BeforeEach
+ void setUpBooks() {
+ repository.save(book1);
+ repository.save(book2);
+ }
+
+ @Test
+ @DisplayName("Delete doit supprimer le livre spécifié")
+ void testDelete() {
+ repository.delete(book1);
+
+ List books = repository.findAll();
+
+ assertEquals(1, books.size());
+ assertFalse(books.contains(book1));
+ assertTrue(books.contains(book2));
+ }
+
+ @Test
+ @DisplayName("DeleteAll doit supprimer tous les livres")
+ void testDeleteAll() {
+ repository.deleteAll();
+
+ List books = repository.findAll();
+
+ assertTrue(books.isEmpty());
+ assertEquals(0, books.size());
+ }
+
+ @Test
+ @DisplayName("Delete ne doit pas lever d'exception si le livre n'existe pas")
+ void testDeleteNonExistentBook() {
+ Book nonExistentBook = Book.builder()
+ .isbn("0")
+ .title("Non Existent")
+ .author("Nobody")
+ .stock(0)
+ .price(0.0)
+ .build();
+
+ assertDoesNotThrow(() -> repository.delete(nonExistentBook));
+
+ assertEquals(2, repository.findAll().size());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/BookUseCaseTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/BookUseCaseTest.java
new file mode 100644
index 0000000..82bdb19
--- /dev/null
+++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/BookUseCaseTest.java
@@ -0,0 +1,279 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.book.usecase;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.book.BookDTO;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.BookInfo;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.Category;
+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.exception.NotValidBookException;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.repository.BookRepository;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.time.LocalDate;
+import java.util.*;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class BookUseCaseTest {
+
+ @Mock
+ private BookRepository bookRepository;
+
+ @InjectMocks
+ private BookUseCase bookUseCase;
+
+ private String isbn;
+ private Book testBook;
+ private BookInfo validBookInfo;
+
+ @BeforeEach
+ void setUp() {
+ isbn = "9780132350884";
+ testBook = Book.builder()
+ .isbn(isbn)
+ .title("Clean Code")
+ .author("Robert C. Martin")
+ .publisher("Prentice Hall")
+ .publicationDate(LocalDate.of(2008, 8, 1))
+ .price(35.0)
+ .stock(10)
+ .categories(List.of(Category.SCIENCE))
+ .description("A book about writing clean code.")
+ .language("Francais")
+ .build();
+
+ validBookInfo = new BookInfo(
+ isbn,
+ "Clean Code",
+ "Robert C. Martin",
+ "Prentice Hall",
+ LocalDate.of(2008, 8, 1),
+ 35.0,
+ 10,
+ List.of(Category.SCIENCE),
+ "A book about writing clean code.",
+ "Francais"
+ );
+ }
+
+ @Nested
+ @DisplayName("Register book tests")
+ class RegisterBookTests {
+
+ @Test
+ @DisplayName("Should register book when valid data is provided")
+ void testRegisterBookWithValidData() throws NotValidBookException {
+ when(bookRepository.save(any(Book.class))).thenReturn(testBook);
+
+ String registeredIsbn = bookUseCase.registerBook(validBookInfo);
+
+ assertNotNull(registeredIsbn);
+ assertEquals(isbn, registeredIsbn);
+ verify(bookRepository, times(1)).save(any(Book.class));
+ }
+
+ @Test
+ @DisplayName("Should throw exception when book data is not valid")
+ void testRegisterBookWithInvalidData() {
+ BookInfo invalidBookInfo = new BookInfo(
+ null, "", "", "", null, 0.0, 0, Collections.emptyList(), "", ""
+ );
+
+ assertThrows(NotValidBookException.class,
+ () -> bookUseCase.registerBook(invalidBookInfo));
+
+ verify(bookRepository, never()).save(any(Book.class));
+ }
+ }
+
+ @Nested
+ @DisplayName("Find book tests")
+ class FindBookTests {
+
+ @Test
+ @DisplayName("Should return book when ISBN exists")
+ void testFindBookByIsbn() {
+ when(bookRepository.findByIsbn(isbn)).thenReturn(Optional.of(testBook));
+
+ BookDTO foundBook = bookUseCase.findBookByIsbn(isbn);
+
+ assertNotNull(foundBook);
+ assertEquals(isbn, foundBook.getIsbn());
+ verify(bookRepository, times(1)).findByIsbn(isbn);
+ }
+
+ @Test
+ @DisplayName("Should throw exception when ISBN doesn't exist")
+ void testFindBookByIsbnNotFound() {
+ when(bookRepository.findByIsbn("0")).thenReturn(Optional.empty());
+
+ assertThrows(BookNotFoundException.class,
+ () -> bookUseCase.findBookByIsbn("0"));
+
+ verify(bookRepository, times(1)).findByIsbn("0");
+ }
+ }
+
+ @Nested
+ @DisplayName("Update book tests")
+ class UpdateBookTests {
+
+ @Test
+ @DisplayName("Should update book when valid data is provided")
+ void testUpdateBookWithValidData() throws BookNotFoundException, NotValidBookException {
+ when(bookRepository.findByIsbn(isbn)).thenReturn(Optional.of(testBook));
+
+ Book updatedBook = Book.builder()
+ .isbn(isbn)
+ .title("Clean Code (2nd Edition)")
+ .author("Robert C. Martin")
+ .publisher("Prentice Hall")
+ .publicationDate(LocalDate.of(2008, 8, 1))
+ .price(38.0)
+ .stock(15)
+ .categories(List.of(Category.SCIENCE))
+ .description("Updated description")
+ .language("Francais")
+ .build();
+
+ when(bookRepository.save(any(Book.class))).thenReturn(updatedBook);
+
+ BookInfo updateInfo = new BookInfo(
+ isbn,
+ "Clean Code (2nd Edition)",
+ "Robert C. Martin",
+ "Prentice Hall",
+ LocalDate.of(2008, 8, 1),
+ 38.0,
+ 15,
+ List.of(Category.SCIENCE),
+ "Updated description",
+ "Francais"
+ );
+
+ BookDTO result = bookUseCase.updateBook(isbn, updateInfo);
+
+ assertNotNull(result);
+ assertEquals("Clean Code (2nd Edition)", result.getTitle());
+ assertEquals(38.0, result.getPrice());
+ verify(bookRepository, times(1)).findByIsbn(isbn);
+ verify(bookRepository, times(1)).save(any(Book.class));
+ }
+
+ @Test
+ @DisplayName("Should throw exception when book ISBN doesn't exist")
+ void testUpdateBookNotFound() {
+ when(bookRepository.findByIsbn("0")).thenReturn(Optional.empty());
+
+ BookInfo updateInfo = validBookInfo;
+
+ assertThrows(BookNotFoundException.class,
+ () -> bookUseCase.updateBook("0", updateInfo));
+
+ verify(bookRepository, times(1)).findByIsbn("0");
+ verify(bookRepository, never()).save(any(Book.class));
+ }
+
+ @Test
+ @DisplayName("Should throw exception when update data is not valid")
+ void testUpdateBookWithInvalidData() {
+ BookInfo invalidUpdateInfo = new BookInfo(
+ null, "", "", "", null, 0.0, 0, Collections.emptyList(), "", ""
+ );
+
+ assertThrows(NotValidBookException.class,
+ () -> bookUseCase.updateBook(isbn, invalidUpdateInfo));
+
+ verify(bookRepository, never()).findByIsbn(any(String.class));
+ verify(bookRepository, never()).save(any(Book.class));
+ }
+ }
+
+ @Nested
+ @DisplayName("Delete book tests")
+ class DeleteBookTests {
+
+ @Test
+ @DisplayName("Should delete book when ISBN exists")
+ void testDeleteBook() throws BookNotFoundException {
+ when(bookRepository.findByIsbn(isbn)).thenReturn(Optional.of(testBook));
+ doNothing().when(bookRepository).delete(testBook);
+
+ bookUseCase.deleteBook(isbn);
+
+ verify(bookRepository, times(1)).findByIsbn(isbn);
+ verify(bookRepository, times(1)).delete(testBook);
+ }
+
+ @Test
+ @DisplayName("Should throw exception when book ISBN doesn't exist")
+ void testDeleteBookNotFound() {
+ when(bookRepository.findByIsbn("0")).thenReturn(Optional.empty());
+
+ assertThrows(BookNotFoundException.class,
+ () -> bookUseCase.deleteBook("0"));
+
+ verify(bookRepository, times(1)).findByIsbn("0");
+ verify(bookRepository, never()).delete(any(Book.class));
+ }
+ }
+
+ @Nested
+ @DisplayName("Stock management tests")
+ class StockManagementTests {
+
+ @Test
+ @DisplayName("Should add stock correctly")
+ void testAddStock() {
+ when(bookRepository.findByIsbn(isbn)).thenReturn(Optional.of(testBook));
+ when(bookRepository.save(any(Book.class))).thenReturn(testBook);
+
+ int result = bookUseCase.addStock(isbn, 5);
+
+ assertEquals(15, result);
+ verify(bookRepository, times(1)).findByIsbn(isbn);
+ verify(bookRepository, times(1)).save(testBook);
+ }
+
+ @Test
+ @DisplayName("Should remove stock correctly")
+ void testRemoveStock() {
+ when(bookRepository.findByIsbn(isbn)).thenReturn(Optional.of(testBook));
+ when(bookRepository.save(any(Book.class))).thenReturn(testBook);
+
+ int result = bookUseCase.removeStock(isbn, 5);
+
+ assertEquals(5, result);
+ verify(bookRepository, times(1)).findByIsbn(isbn);
+ verify(bookRepository, times(1)).save(testBook);
+ }
+ }
+
+ @Nested
+ @DisplayName("Search tests")
+ class SearchTests {
+
+ @Test
+ @DisplayName("Should find books by title")
+ void testFindBooksByTitle() {
+ List books = List.of(testBook);
+ when(bookRepository.findByTitleContaining("Clean")).thenReturn(books);
+
+ List result = bookUseCase.findBooksByTitleContaining("Clean");
+
+ assertEquals(1, result.size());
+ assertEquals("Clean Code", result.get(0).getTitle());
+ verify(bookRepository, times(1)).findByTitleContaining("Clean");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidatorTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidatorTest.java
new file mode 100644
index 0000000..7f81bc4
--- /dev/null
+++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidatorTest.java
@@ -0,0 +1,259 @@
+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.Category;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.NotValidBookException;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.time.LocalDate;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class BookValidatorTest {
+
+ @Test
+ @DisplayName("Valide un livre avec des données correctes")
+ void testValidateValidBook() {
+ BookInfo validBook = new BookInfo(
+ "9780132350884",
+ "Clean Code",
+ "Robert C. Martin",
+ "Prentice Hall",
+ LocalDate.of(2008, 8, 1),
+ 35.0,
+ 10,
+ List.of(Category.SCIENCE),
+ "Un livre sur le clean code.",
+ "Francais"
+ );
+ assertDoesNotThrow(() -> BookValidator.validate(validBook));
+ }
+
+ @Nested
+ @DisplayName("Validation de l'ISBN")
+ class IsbnValidationTests {
+
+ @Test
+ @DisplayName("Doit lever une exception si l'ISBN est null")
+ void testIsbnNull() {
+ BookInfo book = new BookInfo(
+ null, "Titre", "Auteur", "Editeur", LocalDate.now(), 10.0, 1,
+ List.of(Category.FICTION), "Desc", "Francais"
+ );
+ NotValidBookException ex = assertThrows(
+ NotValidBookException.class,
+ () -> BookValidator.validate(book)
+ );
+ assertEquals(BookValidator.ISBN_CANNOT_BE_NULL, ex.getMessage());
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"", "0", "-1", "123"})
+ @DisplayName("Doit lever une exception si l'ISBN est invalide")
+ void testIsbnInvalid(String isbn) {
+ BookInfo book = new BookInfo(
+ isbn, "Titre", "Auteur", "Editeur", LocalDate.now(), 10.0, 1,
+ List.of(Category.FICTION), "Desc", "Francais"
+ );
+ NotValidBookException ex = assertThrows(
+ NotValidBookException.class,
+ () -> BookValidator.validate(book)
+ );
+ assertEquals(BookValidator.ISBN_IS_NOT_VALID, ex.getMessage());
+ }
+ }
+
+ @Nested
+ @DisplayName("Validation du titre")
+ class TitleValidationTests {
+
+ @Test
+ @DisplayName("Doit lever une exception si le titre est vide")
+ void testTitleBlank() {
+ BookInfo book = new BookInfo(
+ "9780132350884", "", "Auteur", "Editeur", LocalDate.now(), 10.0, 1,
+ List.of(Category.FICTION), "Desc", "Francais"
+ );
+ NotValidBookException ex = assertThrows(
+ NotValidBookException.class,
+ () -> BookValidator.validate(book)
+ );
+ assertEquals(BookValidator.TITLE_CANNOT_BE_BLANK, ex.getMessage());
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {" ", "\t", "\n"})
+ @DisplayName("Doit lever une exception si le titre ne contient que des espaces")
+ void testTitleWhitespace(String whitespace) {
+ BookInfo book = new BookInfo(
+ "9780132350884", whitespace, "Auteur", "Editeur", LocalDate.now(), 10.0, 1,
+ List.of(Category.FICTION), "Desc", "Francais"
+ );
+ NotValidBookException ex = assertThrows(
+ NotValidBookException.class,
+ () -> BookValidator.validate(book)
+ );
+ assertEquals(BookValidator.TITLE_CANNOT_BE_BLANK, ex.getMessage());
+ }
+ }
+
+ @Nested
+ @DisplayName("Validation de l'auteur")
+ class AuthorValidationTests {
+
+ @Test
+ @DisplayName("Doit lever une exception si l'auteur est vide")
+ void testAuthorBlank() {
+ BookInfo book = new BookInfo(
+ "9780132350884", "Titre", "", "Editeur", LocalDate.now(), 10.0, 1,
+ List.of(Category.FICTION), "Desc", "Francais"
+ );
+ NotValidBookException ex = assertThrows(
+ NotValidBookException.class,
+ () -> BookValidator.validate(book)
+ );
+ assertEquals(BookValidator.AUTHOR_CANNOT_BE_BLANK, ex.getMessage());
+ }
+ }
+
+ @Nested
+ @DisplayName("Validation de l'éditeur")
+ class PublisherValidationTests {
+
+ @Test
+ @DisplayName("Doit lever une exception si l'éditeur est vide")
+ void testPublisherBlank() {
+ BookInfo book = new BookInfo(
+ "9780132350884", "Titre", "Auteur", "", LocalDate.now(), 10.0, 1,
+ List.of(Category.FICTION), "Desc", "Francais"
+ );
+ NotValidBookException ex = assertThrows(
+ NotValidBookException.class,
+ () -> BookValidator.validate(book)
+ );
+ assertEquals(BookValidator.PUBLISHER_CANNOT_BE_BLANK, ex.getMessage());
+ }
+ }
+
+ @Nested
+ @DisplayName("Validation de la date de publication")
+ class PublicationDateValidationTests {
+
+ @Test
+ @DisplayName("Doit lever une exception si la date de publication est null")
+ void testPublicationDateNull() {
+ BookInfo book = new BookInfo(
+ "9780132350884", "Titre", "Auteur", "Editeur", null, 10.0, 1,
+ List.of(Category.FICTION), "Desc", "Francais"
+ );
+ NotValidBookException ex = assertThrows(
+ NotValidBookException.class,
+ () -> BookValidator.validate(book)
+ );
+ assertEquals(BookValidator.PUBLICATION_DATE_CANNOT_BE_NULL, ex.getMessage());
+ }
+ }
+
+ @Nested
+ @DisplayName("Validation du prix")
+ class PriceValidationTests {
+
+ @ParameterizedTest
+ @ValueSource(doubles = {0.0, -1.0, -10.5})
+ @DisplayName("Doit lever une exception si le prix est négatif ou nul")
+ void testInvalidPrice(double price) {
+ BookInfo book = new BookInfo(
+ "9780132350884", "Titre", "Auteur", "Editeur", LocalDate.now(), price, 1,
+ List.of(Category.FICTION), "Desc", "Francais"
+ );
+ NotValidBookException ex = assertThrows(
+ NotValidBookException.class,
+ () -> BookValidator.validate(book)
+ );
+ assertEquals(BookValidator.PRICE_MUST_BE_POSITIVE, ex.getMessage());
+ }
+ }
+
+ @Nested
+ @DisplayName("Validation du stock")
+ class StockValidationTests {
+
+ @ParameterizedTest
+ @ValueSource(ints = {-1, -10})
+ @DisplayName("Doit lever une exception si le stock est négatif")
+ void testNegativeStock(int stock) {
+ BookInfo book = new BookInfo(
+ "9780132350884", "Titre", "Auteur", "Editeur", LocalDate.now(), 10.0, stock,
+ List.of(Category.FICTION), "Desc", "Francais"
+ );
+ NotValidBookException ex = assertThrows(
+ NotValidBookException.class,
+ () -> BookValidator.validate(book)
+ );
+ assertEquals(BookValidator.STOCK_CANNOT_BE_NEGATIVE, ex.getMessage());
+ }
+ }
+
+ @Nested
+ @DisplayName("Validation des catégories")
+ class CategoriesValidationTests {
+
+ @Test
+ @DisplayName("Doit lever une exception si la liste des catégories est vide")
+ void testEmptyCategories() {
+ BookInfo book = new BookInfo(
+ "9780132350884", "Titre", "Auteur", "Editeur", LocalDate.now(), 10.0, 1,
+ Collections.emptyList(), "Desc", "Francais"
+ );
+ NotValidBookException ex = assertThrows(
+ NotValidBookException.class,
+ () -> BookValidator.validate(book)
+ );
+ assertEquals(BookValidator.CATEGORIES_CANNOT_BE_EMPTY, ex.getMessage());
+ }
+ }
+
+ @Nested
+ @DisplayName("Validation de la description")
+ class DescriptionValidationTests {
+
+ @Test
+ @DisplayName("Doit lever une exception si la description est vide")
+ void testDescriptionBlank() {
+ BookInfo book = new BookInfo(
+ "9780132350884", "Titre", "Auteur", "Editeur", LocalDate.now(), 10.0, 1,
+ List.of(Category.FICTION), "", "Francais"
+ );
+ NotValidBookException ex = assertThrows(
+ NotValidBookException.class,
+ () -> BookValidator.validate(book)
+ );
+ assertEquals(BookValidator.DESCRIPTION_CANNOT_BE_BLANK, ex.getMessage());
+ }
+ }
+
+ @Nested
+ @DisplayName("Validation de la langue")
+ class LanguageValidationTests {
+
+ @Test
+ @DisplayName("Doit lever une exception si la langue est vide")
+ void testLanguageBlank() {
+ BookInfo book = new BookInfo(
+ "9780132350884", "Titre", "Auteur", "Editeur", LocalDate.now(), 10.0, 1,
+ List.of(Category.FICTION), "Desc", ""
+ );
+ NotValidBookException ex = assertThrows(
+ NotValidBookException.class,
+ () -> BookValidator.validate(book)
+ );
+ assertEquals(BookValidator.LANGUAGE_CANNOT_BE_BLANK, ex.getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/book/BookSteps.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/book/BookSteps.java
new file mode 100644
index 0000000..a43a2df
--- /dev/null
+++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/book/BookSteps.java
@@ -0,0 +1,320 @@
+package fr.iut_fbleau.but3.dev62.mylibrary.features.book;
+
+import io.cucumber.datatable.DataTable;
+import io.cucumber.java.en.And;
+import io.cucumber.java.en.Given;
+import io.cucumber.java.en.Then;
+import io.cucumber.java.en.When;
+
+import java.time.LocalDate;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import fr.iut_fbleau.but3.dev62.mylibrary.book.BookDTO;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.BookInfo;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.Category;
+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.exception.NotValidBookException;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.repository.BookRepository;
+import fr.iut_fbleau.but3.dev62.mylibrary.book.usecase.BookUseCase;
+
+public class BookSteps {
+
+ private final BookRepository bookRepository = new BookRepository();
+ private final BookUseCase bookUseCase = new BookUseCase(bookRepository);
+
+ private String bookRegistration;
+ private BookDTO bookByIsbn;
+ private BookDTO updatedBook;
+ private String deletedBookIsbn;
+ private Exception storedException;
+ private List booksPageResult;
+ private int totalBooks;
+ private int totalPages;
+ private List filteredBooksResult;
+ private NotValidBookException notValidBookException;
+
+ @Given("Le systeme possedent les livres suivants :")
+ public void leSystemePossedeLesLivresSuivants(DataTable dataTable) {
+ bookRepository.deleteAll();
+ List