diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..a9dcb85 --- /dev/null +++ b/Readme.md @@ -0,0 +1 @@ +Notre groupe est constitué de Marvin Aubert, Maxime Lebreton et de Patrick Felix Vimalaratnam \ No newline at end of file diff --git a/pom.xml b/pom.xml index 27ec78e..66c9447 100644 --- a/pom.xml +++ b/pom.xml @@ -117,6 +117,11 @@ ${mockito.version} test + + org.junit.jupiter + junit-jupiter + test + 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..92e40ca --- /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.Builder; +import lombok.Getter; + +import java.time.LocalDate; +import java.util.UUID; + +@Getter +@Builder + +public class ReviewDTO { + private UUID reviewId; + private UUID customerId; + private UUID bookId; + private Integer note; + private String comment; + private LocalDate purchaseDate; +} 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..5e73ff9 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/ReviewInfo.java @@ -0,0 +1,7 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.review; + +import java.time.LocalDate; +import java.util.UUID; + +public record ReviewInfo(Integer note, String comment, LocalDate purchaseDate) { +} 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..d8ebead --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/converter/ReviewConverter.java @@ -0,0 +1,30 @@ +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; + +public class ReviewConverter { + private ReviewConverter() { + + } + + public static Review toDomain(ReviewInfo newReview) { + return Review.builder() + .note(newReview.note()) + .comment(newReview.comment()) + .purchaseDate(newReview.purchaseDate()) + .build(); + } + + public static ReviewDTO toDTO(Review review) { + return ReviewDTO.builder() + .reviewId(review.getReviewId()) + .customerId(review.getCustomerId()) + .bookId(review.getBookId()) + .note(review.getNote()) + .comment(review.getComment()) + .purchaseDate(review.getPurchaseDate()) + .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..26b80ca --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/entity/Review.java @@ -0,0 +1,29 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.review.entity; + +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDate; +import java.util.UUID; + +@Getter +@Builder + +public class Review { + private UUID reviewId; + private UUID customerId; + private UUID bookId; + private Integer note; + private String comment; + private LocalDate purchaseDate; + + public void setRandomUUID() { + this.reviewId = UUID.randomUUID(); + } + + public void setRandomUUIDCustomerAndBook() { + + this.customerId = UUID.randomUUID(); + this.bookId = UUID.randomUUID(); + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/NotValidReviewException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/NotValidReviewException.java new file mode 100644 index 0000000..241184b --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/NotValidReviewException.java @@ -0,0 +1,7 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.review.exception; + +public class NotValidReviewException extends RuntimeException { + public NotValidReviewException(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..087454e --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/ReviewNotFoundException.java @@ -0,0 +1,25 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.review.exception; + +import java.text.MessageFormat; +import java.util.Optional; +import java.util.UUID; + +public class ReviewNotFoundException extends RuntimeException { + + public static final String THE_REVIEWS_WITH_CUSTOMER_ID_DOES_NOT_EXIST_MESSAGE = "The reviews with the customer id {0} does not exists"; + public static final String THE_REVIEWS_WITH_BOOK_ID_DOES_NOT_EXIST_MESSAGE = "The reviews with the book id {0} does not exists"; + public static final String THE_REVIEWS_WITH_REVIEW_ID_DOES_NOT_EXIST_MESSAGE = "The review with review id {0} does not exists"; + + public ReviewNotFoundException(Optional customerUUID, Optional bookUUID, Optional reviewUUID) { + super(buildMessage(customerUUID, bookUUID, reviewUUID)); + } + + private static String buildMessage(Optional customerUUID, Optional bookUUID, Optional reviewUUID) { + if (customerUUID.isPresent()) { + return MessageFormat.format(THE_REVIEWS_WITH_CUSTOMER_ID_DOES_NOT_EXIST_MESSAGE, customerUUID.get()); + }else if (bookUUID.isPresent()) { + return MessageFormat.format(THE_REVIEWS_WITH_BOOK_ID_DOES_NOT_EXIST_MESSAGE, bookUUID.get()); + } + return MessageFormat.format(THE_REVIEWS_WITH_REVIEW_ID_DOES_NOT_EXIST_MESSAGE, reviewUUID.get()); + } +} 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..378fceb --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/repository/ReviewRepository.java @@ -0,0 +1,79 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.review.repository; + +import fr.iut_fbleau.but3.dev62.mylibrary.review.entity.Review; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +public class ReviewRepository { + + private final List reviews = new ArrayList<>(); + + public List findAll() { + + return reviews; + } + + public void deleteAll() { + + reviews.clear(); + } + + public Review save(Review newReview) { + Optional optionalReviewWithSameReviewId = this.findByReviewId(newReview.getReviewId()); + optionalReviewWithSameReviewId.ifPresent(reviews::remove); + this.reviews.add(newReview); + return newReview; + } + + public ArrayList findByCustomerId(UUID customerUUID) { + return this.reviews.stream() + .filter(review -> review.getCustomerId().equals(customerUUID)) + .collect(Collectors.toCollection(ArrayList::new)); + } + + public ArrayList findByBookId(UUID bookUUID) { + return this.reviews.stream() + .filter(review -> review.getBookId().equals(bookUUID)) + .collect(Collectors.toCollection(ArrayList::new)); + } + + public Optional findByReviewId(UUID reviewUUID) { + return this.reviews.stream() + .filter(review -> review.getReviewId().equals(reviewUUID)) + .findFirst(); + } + + public boolean existsByCustomerId(UUID customerUUID) { + return this.reviews.stream() + .anyMatch(review -> review.getCustomerId().equals(customerUUID)); + } + + public boolean existsByBookId(UUID bookUUID) { + return this.reviews.stream() + .anyMatch(review -> review.getBookId().equals(bookUUID)); + } + + public boolean existsByReviewId(UUID reviewUUID) { + return this.reviews.stream() + .anyMatch(review -> review.getReviewId().equals(reviewUUID)); + } + + public void deleteCustomerReviews(UUID customerUUID) { + + this.reviews.removeIf(review -> review.getCustomerId().equals(customerUUID)); + } + + public void deleteBookReviews(UUID bookUUID) { + + this.reviews.removeIf(review -> review.getBookId().equals(bookUUID)); + } + + public void delete(Review review) { + + this.reviews.remove(review); + } +} 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..ddc80a5 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/usecase/ReviewUseCase.java @@ -0,0 +1,113 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.review.usecase; + +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.converter.ReviewConverter; +import fr.iut_fbleau.but3.dev62.mylibrary.review.entity.Review; +import fr.iut_fbleau.but3.dev62.mylibrary.review.exception.NotValidReviewException; +import fr.iut_fbleau.but3.dev62.mylibrary.review.exception.ReviewNotFoundException; +import fr.iut_fbleau.but3.dev62.mylibrary.review.repository.ReviewRepository; +import fr.iut_fbleau.but3.dev62.mylibrary.review.validator.ReviewValidator; + +import java.util.ArrayList; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +public class ReviewUseCase { + + private final ReviewRepository reviewRepository; + + public ReviewUseCase(ReviewRepository reviewRepository) { + this.reviewRepository = reviewRepository; + } + + public UUID registerReview(ReviewInfo newReview) throws NotValidReviewException { + ReviewValidator.validate(newReview); + Review reviewToRegister = ReviewConverter.toDomain(newReview); + Review reviewToRegistered = reviewRepository.save(reviewToRegister); + return reviewToRegistered.getReviewId(); + } + + public ArrayList findReviewByCustomerId(UUID customerId) { + ArrayList optionalReviews = reviewRepository.findByCustomerId(customerId); + return optionalReviews.stream() + .map(ReviewConverter::toDTO) + .collect(Collectors.toCollection(ArrayList::new)); + } + + public ArrayList findReviewByBookId(UUID bookId) { + ArrayList optionalReviews = reviewRepository.findByBookId(bookId); + return optionalReviews.stream() + .map(ReviewConverter::toDTO) + .collect(Collectors.toCollection(ArrayList::new)); + } + + public Optional findReviewByReviewId(UUID reviewId) { + Optional optionalReview = reviewRepository.findByReviewId(reviewId); + return optionalReview.map(ReviewConverter::toDTO); + } + + public ReviewDTO updateReview(UUID reviewUUID, ReviewInfo reviewInfo) + throws ReviewNotFoundException, NotValidReviewException { + ReviewValidator.validate(reviewInfo); + Review reviewByReviewUUID = getReviewIfDoesNotExistThrowReviewNotFoundException( + reviewUUID); + Review review = Review.builder() + .reviewId(reviewUUID) + .customerId(reviewByReviewUUID.getCustomerId()) + .bookId(reviewByReviewUUID.getBookId()) + .note(reviewByReviewUUID.getNote()) + .comment(reviewByReviewUUID.getComment()) + .purchaseDate(reviewByReviewUUID.getPurchaseDate()) + .build(); + Review updatedReview = reviewRepository.save(review); + return ReviewConverter.toDTO(updatedReview); + } + + public void deleteReview(UUID reviewUUID) throws ReviewNotFoundException { + Review reviewToDelete = getReviewIfDoesNotExistThrowReviewNotFoundException(reviewUUID); + this.reviewRepository.delete(reviewToDelete); + } + + public void deleteCustomerReviews(UUID customerUUID) throws ReviewNotFoundException { + ArrayList reviewsToDelete = getReviewByCustomerIdIfDoesNotExistThrowReviewNotFoundException(customerUUID); + for (Review review : reviewsToDelete) { + reviewRepository.delete(review); + } + } + + public void deleteBookReviews(UUID bookUUID) throws ReviewNotFoundException { + ArrayList reviewsToDelete = getReviewByBookIfDoesNotExistThrowReviewNotFoundException(bookUUID); + for (Review review : reviewsToDelete) { + reviewRepository.delete(review); + } + } + + private Review getReviewIfDoesNotExistThrowReviewNotFoundException(UUID reviewUUID) + throws ReviewNotFoundException { + Optional optionalReviewByReviewId = reviewRepository.findByReviewId(reviewUUID); + if (optionalReviewByReviewId.isEmpty()) { + throw new ReviewNotFoundException(Optional.empty(), Optional.empty(),Optional.of(reviewUUID)); + } + return optionalReviewByReviewId.get(); + } + + private ArrayList getReviewByCustomerIdIfDoesNotExistThrowReviewNotFoundException(UUID customerUUID) + throws ReviewNotFoundException { + ArrayList optionalReviewByReviewId = reviewRepository.findByCustomerId(customerUUID); + if (optionalReviewByReviewId.isEmpty()) { + throw new ReviewNotFoundException(Optional.of(customerUUID), Optional.empty(),Optional.empty()); + } + return optionalReviewByReviewId; + } + + private ArrayList getReviewByBookIfDoesNotExistThrowReviewNotFoundException(UUID bookUUID) + throws ReviewNotFoundException { + ArrayList optionalReviewByReviewId = reviewRepository.findByBookId(bookUUID); + if (optionalReviewByReviewId.isEmpty()) { + throw new ReviewNotFoundException(Optional.empty(), Optional.empty(), Optional.of(bookUUID)); + } + return optionalReviewByReviewId; + } +} 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..9609c5a --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/review/validator/ReviewValidator.java @@ -0,0 +1,52 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.review.validator; + +import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.NotValidBookException; +import fr.iut_fbleau.but3.dev62.mylibrary.review.ReviewInfo; +import fr.iut_fbleau.but3.dev62.mylibrary.review.exception.NotValidReviewException; + +import java.time.LocalDate; + +public class ReviewValidator { + + public static final String NOTE_CANNOT_BE_LOWER_THAN_1 = "Note is greater than or equal to 1"; + public static final String NOTE_CANNOT_BE_UPPER_THAN_5 = "Note is less than or equal to 5"; + public static final String COMMENT_CANNOT_BE_BLANK = "Comment cannot be blank"; + public static final String PURCHASE_DATE_IS_NOT_VALID = "Date is not valid"; + + public ReviewValidator() { + + } + + public static void validate(ReviewInfo newReview) throws NotValidReviewException { + validateNoteLower1(newReview); + validateNoteUpper5(newReview); + validateComment(newReview); + validatePurchaseDate(newReview); + } + + private static void validateNoteLower1(ReviewInfo newReview) + throws NotValidReviewException { + if (newReview.note() <= 1) { + throw new NotValidReviewException(NOTE_CANNOT_BE_LOWER_THAN_1); + } + } + + private static void validateNoteUpper5(ReviewInfo newReview) + throws NotValidReviewException { + if (newReview.note() >= 5) { + throw new NotValidReviewException("Note is less than or equal to 5"); + } + } + + private static void validateComment(ReviewInfo newReview) throws NotValidReviewException { + if (newReview.comment().isBlank()) { + throw new NotValidReviewException(COMMENT_CANNOT_BE_BLANK); + } + } + + private static void validatePurchaseDate(ReviewInfo newReview) throws NotValidReviewException { + if (newReview.purchaseDate().isAfter(LocalDate.now())) { + throw new NotValidReviewException(PURCHASE_DATE_IS_NOT_VALID); + } + } +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/converter/ReviewConverterTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/converter/ReviewConverterTest.java new file mode 100644 index 0000000..135ae7c --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/converter/ReviewConverterTest.java @@ -0,0 +1,68 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.review.converter; + +import java.time.LocalDate; +import java.util.UUID; + +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 org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@DisplayName("ReviewConverterTest Unit Tests") +public class ReviewConverterTest { + + @Nested + @DisplayName("toDomain() method tests") + class ToDomainTests { + + @Test + @DisplayName("Should convert ReviewInfo to Review domain object") + void shouldConvertReviewInfoToDomain() { + // Given + LocalDate purchaseDate = LocalDate.of(2026, 3, 24); + ReviewInfo reviewInfo = new ReviewInfo(5, "tres bon livre", purchaseDate); + + // When + Review result = ReviewConverter.toDomain(reviewInfo); + + // Then + assertNotNull(result); + assertEquals(reviewInfo.note(), result.getNote()); + assertEquals(reviewInfo.comment(), result.getComment()); + assertEquals(reviewInfo.purchaseDate(), result.getPurchaseDate()); + } + } + + @Nested + @DisplayName("toDTO() method tests") + class ToDTOTests { + + @Test + @DisplayName("Should convert Review domain object to ReviewDTO with all fields mapped correctly") + void shouldConvertReviewToDTO() { + LocalDate purchaseDate = LocalDate.of(2026, 3, 24); + Review review = Review.builder() + .reviewId(UUID.randomUUID()) + .customerId(UUID.randomUUID()) + .bookId(UUID.randomUUID()) + .note(5) + .comment("très bon livre") + .purchaseDate(purchaseDate) + .build(); + + ReviewDTO result = ReviewConverter.toDTO(review); + + assertNotNull(result); + assertEquals(review.getCustomerId(), result.getCustomerId()); + assertEquals(review.getBookId(), result.getBookId()); + assertEquals(review.getNote(), result.getNote()); + assertEquals(review.getComment(), result.getComment()); + assertEquals(review.getPurchaseDate(), result.getPurchaseDate()); + } + } +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/entity/ReviewTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/entity/ReviewTest.java new file mode 100644 index 0000000..44e78ea --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/entity/ReviewTest.java @@ -0,0 +1,55 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.review.entity; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +public class ReviewTest { + + @Test + @DisplayName("Builder should create a valid review instance") + void testReviewBuilder() { + UUID customerId = UUID.randomUUID(); + UUID bookId = UUID.randomUUID(); + Integer note = 5; + String comment = "très bon livre"; + LocalDate purchaseDate = LocalDate.of(2026, 3, 24); + + Review review = Review.builder() + .customerId(customerId) + .bookId(bookId) + .note(note) + .comment(comment) + .purchaseDate(purchaseDate) + .build(); + + assertEquals(customerId, review.getCustomerId()); + assertEquals(bookId, review.getBookId()); + assertEquals(note, review.getNote()); + assertEquals(comment, review.getComment()); + assertEquals(purchaseDate, review.getPurchaseDate()); + } + + @Test + @DisplayName("setRandomUUID should change the ID to a new random UUID") + void testSetRandomUUID() { + Review review = Review.builder().build(); + UUID originalReviewId = review.getReviewId(); + UUID originalCustomerId = review.getCustomerId(); + UUID originalBookId = review.getCustomerId(); + + review.setRandomUUID(); + review.setRandomUUIDCustomerAndBook(); + + assertNotNull(review.getReviewId()); + assertNotNull(review.getCustomerId()); + assertNotNull(review.getBookId()); + assertNotEquals(originalReviewId, review.getReviewId()); + assertNotEquals(originalCustomerId, review.getCustomerId()); + assertNotEquals(originalBookId, review.getBookId()); + } +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/NotValidReviewExcpetionTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/NotValidReviewExcpetionTest.java new file mode 100644 index 0000000..f7f3712 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/NotValidReviewExcpetionTest.java @@ -0,0 +1,60 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.review.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; + +public class NotValidReviewExcpetionTest { + + @Test + @DisplayName("Exception should be created with the provided message") + void testExceptionCreation() { + String errorMessage = "Review data is not valid"; + + NotValidReviewException exception = new NotValidReviewException(errorMessage); + + assertEquals(errorMessage, exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = { + "Note is greater than or equal to 1", + "Note is less than or equal to 1", + "Comment cannot be empty" + }) + @DisplayName("Exception should handle different validation messages") + void testExceptionWithDifferentMessages(String errorMessage) { + NotValidReviewException exception = new NotValidReviewException(errorMessage); + + assertEquals(errorMessage, exception.getMessage()); + } + + @Test + @DisplayName("Exception should be properly thrown and caught") + void testExceptionCanBeThrownAndCaught() { + String errorMessage = "Comment field is empty"; + + Exception exception = assertThrows(NotValidReviewException.class, () -> { + throw new NotValidReviewException(errorMessage); + }); + + assertEquals(errorMessage, exception.getMessage()); + } + + @Test + @DisplayName("Exception should be catchable as a general Exception") + void testExceptionInheritance() { + String errorMessage = "Invalid review data"; + + try { + throw new NotValidReviewException(errorMessage); + } catch (Exception e) { + assertEquals(NotValidReviewException.class, e.getClass()); + assertEquals(errorMessage, e.getMessage()); + } + } +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/ReviewNotFoundExceptionTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/ReviewNotFoundExceptionTest.java new file mode 100644 index 0000000..9645ece --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/exception/ReviewNotFoundExceptionTest.java @@ -0,0 +1,98 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.review.exception; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Optional; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ReviewNotFoundExceptionTest { + + @Test + @DisplayName("Exception message should contain the UUID provided for customer") + void testExceptionMessageContainsUUIDForCustomer() { + UUID customerUUID = UUID.randomUUID(); + + ReviewNotFoundException exception = new ReviewNotFoundException(Optional.of(customerUUID), Optional.empty(), Optional.empty()); + + String expectedMessage = String.format("The reviews with the customer id %s does not exists", customerUUID); + assertEquals(expectedMessage, exception.getMessage()); + } + + @Test + @DisplayName("Exception message should contain the UUID provided for book") + void testExceptionMessageContainsUUIDForBook() { + UUID bookUUID = UUID.randomUUID(); + + ReviewNotFoundException exception = new ReviewNotFoundException(Optional.empty(), Optional.of(bookUUID), Optional.empty()); + + String expectedMessage = String.format("The reviews with the book id %s does not exists", bookUUID); + assertEquals(expectedMessage, exception.getMessage()); + } + + @Test + @DisplayName("Exception message should contain the UUID provided for customer and book") + void testExceptionMessageContainsUUIDForCustomerAndBook() { + UUID reviewUUID = UUID.randomUUID(); + + ReviewNotFoundException exception = new ReviewNotFoundException(Optional.empty(), Optional.empty(), Optional.of(reviewUUID)); + + String expectedMessage = String.format("The review with review id %s does not exists", reviewUUID); + assertEquals(expectedMessage, exception.getMessage()); + } + + @Test + @DisplayName("Exception should use the correct constant message format for customer") + void testExceptionUsesConstantMessageCustomerFormat() { + UUID customerUUID = UUID.randomUUID(); + + ReviewNotFoundException exception = new ReviewNotFoundException(Optional.of(customerUUID), Optional.empty(), Optional.empty()); + + String expectedFormatWithPlaceholder = "The reviews with the customer id {0} does not exists"; + assertEquals(ReviewNotFoundException.THE_REVIEWS_WITH_CUSTOMER_ID_DOES_NOT_EXIST_MESSAGE, + expectedFormatWithPlaceholder); + assertTrue(exception.getMessage().contains(customerUUID.toString())); + } + + @Test + @DisplayName("Exception should use the correct constant message format for book") + void testExceptionUsesConstantMessageBookFormat() { + UUID bookUUID = UUID.randomUUID(); + + ReviewNotFoundException exception = new ReviewNotFoundException(Optional.empty(), Optional.of(bookUUID), Optional.empty()); + + String expectedFormatWithPlaceholder = "The reviews with the book id {0} does not exists"; + assertEquals(ReviewNotFoundException.THE_REVIEWS_WITH_BOOK_ID_DOES_NOT_EXIST_MESSAGE, + expectedFormatWithPlaceholder); + assertTrue(exception.getMessage().contains(bookUUID.toString())); + } + + @Test + @DisplayName("Exception should use the correct constant message format for review") + void testExceptionUsesConstantMessageReviewFormat() { + UUID reviewUUID = UUID.randomUUID(); + + ReviewNotFoundException exception = new ReviewNotFoundException(Optional.empty(), Optional.empty(), Optional.of(reviewUUID)); + + String expectedFormatWithPlaceholder = "The review with review id {0} does not exists"; + assertEquals(ReviewNotFoundException.THE_REVIEWS_WITH_REVIEW_ID_DOES_NOT_EXIST_MESSAGE, + expectedFormatWithPlaceholder); + assertTrue(exception.getMessage().contains(reviewUUID.toString())); + } + + @Test + @DisplayName("Exception should be properly thrown and caught") + void testExceptionCanBeThrownAndCaught() { + UUID reviewUUID = UUID.randomUUID(); + + try { + throw new ReviewNotFoundException(Optional.empty(),Optional.empty(), Optional.of(reviewUUID)); + } catch (ReviewNotFoundException e) { + String expectedMessage = String.format("The review with review id %s does not exists", reviewUUID); + assertEquals(expectedMessage, e.getMessage()); + } + } +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/repository/ReviewRepositoryTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/repository/ReviewRepositoryTest.java new file mode 100644 index 0000000..f4b5e4b --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/repository/ReviewRepositoryTest.java @@ -0,0 +1,361 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.review.repository; + +import fr.iut_fbleau.but3.dev62.mylibrary.review.entity.Review; +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.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ReviewRepositoryTest { + + private ReviewRepository repository; + private Review review1; + private Review review2; + private Review review3; + private Review review4; + + @BeforeEach + void setUp() { + repository = new ReviewRepository(); + + LocalDate purchaseDate = LocalDate.of(2026, 3, 24); + review1 = Review.builder() + .note(1) + .comment("nul") + .purchaseDate(purchaseDate) + .build(); + review1.setRandomUUID(); + review1.setRandomUUIDCustomerAndBook(); + + UUID customerId = UUID.randomUUID(); + UUID bookId = UUID.randomUUID(); + review2 = Review.builder() + .customerId(customerId) + .bookId(bookId) + .note(1) + .comment("nul") + .purchaseDate(purchaseDate) + .build(); + review2.setRandomUUID(); + + UUID bookId3 = UUID.randomUUID(); + review3 = Review.builder() + .customerId(customerId) + .bookId(bookId3) + .note(2) + .comment("ça passe") + .purchaseDate(purchaseDate) + .build(); + review3.setRandomUUID(); + + UUID customerId4 = UUID.randomUUID(); + review4 = Review.builder() + .customerId(customerId4) + .bookId(bookId) + .note(2) + .comment("ça passe") + .purchaseDate(purchaseDate) + .build(); + review4.setRandomUUID(); + } + + @Test + @DisplayName("New repository should be empty") + void testNewRepositoryIsEmpty() { + List reviews = repository.findAll(); + + assertTrue(reviews.isEmpty()); + assertEquals(0, reviews.size()); + } + + @Nested + @DisplayName("Save operations") + class SaveOperations { + + @Test + @DisplayName("Save should add a new review") + void testSaveNewReview() { + Review savedReview = repository.save(review1); + + assertEquals(1, repository.findAll().size()); + assertEquals(review1.getCustomerId(), savedReview.getCustomerId()); + assertEquals(review1.getBookId(), savedReview.getBookId()); + } + + @Test + @DisplayName("Save should update existing review with same customer and book ID") + void testSaveUpdatesExistingReview() { + repository.save(review1); + + LocalDate purchaseDate = LocalDate.of(2026, 5, 24); + UUID reviewId = review1.getReviewId(); + UUID customerId = UUID.randomUUID(); + UUID bookId = UUID.randomUUID(); + Review updatedReview = Review.builder() + .reviewId(reviewId) + .customerId(customerId) + .bookId(bookId) + .note(4) + .comment("pas mal") + .purchaseDate(purchaseDate) + .build(); + + Review savedReview = repository.save(updatedReview); + + assertEquals(1, repository.findAll().size()); + assertEquals(reviewId, savedReview.getReviewId()); + assertEquals(customerId, savedReview.getCustomerId()); + assertEquals(bookId, savedReview.getBookId()); + assertEquals(4, savedReview.getNote()); + assertEquals("pas mal", savedReview.getComment()); + assertEquals(purchaseDate, savedReview.getPurchaseDate()); + } + + @Test + @DisplayName("Save multiple review should add all of them") + void testSaveMultipleReviews() { + repository.save(review1); + repository.save(review2); + + List reviews = repository.findAll(); + + assertEquals(2, reviews.size()); + assertTrue(reviews.contains(review1)); + assertTrue(reviews.contains(review2)); + } + } + + @Nested + @DisplayName("Find operations") + class FindOperations { + + @BeforeEach + void setUpReviews() { + repository.save(review1); + repository.save(review2); + repository.save(review3); + repository.save(review4); + } + + @Test + @DisplayName("FindAll should return all reviews") + void testFindAll() { + List reviews = repository.findAll(); + + assertEquals(4, reviews.size()); + assertTrue(reviews.contains(review1)); + assertTrue(reviews.contains(review2)); + assertTrue(reviews.contains(review3)); + assertTrue(reviews.contains(review4)); + } + + @Test + @DisplayName("findByCustomerId should return review with matching customer ID") + void testFindByCustomerId() { + ArrayList foundreviews = repository.findByCustomerId(review2.getCustomerId()); + + assertTrue(!foundreviews.isEmpty()); + boolean allSameCustomer = foundreviews.stream() + .allMatch(review -> review.getCustomerId().equals(review2.getCustomerId())); + assertTrue(allSameCustomer); + } + + @Test + @DisplayName("findByCustomerId should return empty Optional when a review with customer ID doesn't exist") + void testFindByCustomerIdNotFound() { + UUID nonExistentCustomerId = UUID.randomUUID(); + + ArrayList foundreview = repository.findByCustomerId(nonExistentCustomerId); + + assertTrue(foundreview.isEmpty()); + } + + @Test + @DisplayName("findByBookId should return review with matching book ID") + void testFindByBookId() { + ArrayList foundreviews = repository.findByBookId(review2.getBookId()); + + assertTrue(!foundreviews.isEmpty()); + boolean allSameCustomer = foundreviews.stream() + .allMatch(review -> review.getBookId().equals(review2.getBookId())); + assertTrue(allSameCustomer); + } + + @Test + @DisplayName("findByBookId should return empty Optional when a review with book ID doesn't exist") + void testFindByBookIdNotFound() { + UUID nonExistentBookId = UUID.randomUUID(); + + ArrayList foundreview = repository.findByBookId(nonExistentBookId); + + assertTrue(foundreview.isEmpty()); + } + + @Test + @DisplayName("findByReviewId should return review with matching review ID") + void testFindByReviewId() { + Optional foundreview = repository.findByReviewId(review1.getReviewId()); + + assertTrue(foundreview.isPresent()); + assertEquals(review1.getNote(), foundreview.get().getNote()); + assertEquals(review1.getComment(), foundreview.get().getComment()); + } + + @Test + @DisplayName("findByReviewId should return empty Optional when a review with review ID doesn't exist") + void testFindByReviewIdNotFound() { + UUID nonExistentReviewId = UUID.randomUUID(); + + Optional foundreview = repository.findByReviewId(nonExistentReviewId); + + assertTrue(foundreview.isEmpty()); + } + + @Test + @DisplayName("existsByCustomerId should return true when a review with customer ID exists") + void testExistsByCustomerIdExists() { + boolean exists = repository.existsByCustomerId(review1.getCustomerId()); + + assertTrue(exists); + } + + @Test + @DisplayName("existsByCustomerId should return false when a review with customer ID doesn't exist") + void testExistsByCustomerIdNotExists() { + UUID nonExistentCustomerId = UUID.randomUUID(); + + boolean exists = repository.existsByCustomerId(nonExistentCustomerId); + + assertFalse(exists); + } + + @Test + @DisplayName("existsByBookId should return true when a review with book ID exists") + void testExistsByBookIdExists() { + boolean exists = repository.existsByBookId(review1.getBookId()); + + assertTrue(exists); + } + + @Test + @DisplayName("existsByBookId should return false when a review with book ID doesn't exist") + void testExistsByBookIdNotExists() { + UUID nonExistentBookId = UUID.randomUUID(); + + boolean exists = repository.existsByBookId(nonExistentBookId); + + assertFalse(exists); + } + + @Test + @DisplayName("existsByReviewId should return true when a review with review ID exists") + void testExistsByReviewIdExists() { + boolean exists = repository.existsByReviewId(review1.getReviewId()); + + assertTrue(exists); + } + + @Test + @DisplayName("existsByReviewId should return false when review ID doesn't exist") + void testExistsByReviewIdNotExists() { + UUID nonExistentReviewId = UUID.randomUUID(); + + boolean exists = repository.existsByReviewId(nonExistentReviewId); + + assertFalse(exists); + } + } + + @Nested + @DisplayName("Delete operations") + class DeleteOperations { + + @BeforeEach + void setUpReviews() { + repository.save(review1); + repository.save(review2); + repository.save(review3); + repository.save(review4); + } + + @Test + @DisplayName("Delete should remove all reviews of a customer") + void testDeleteCustomerReviews() { + repository.deleteCustomerReviews(review2.getCustomerId()); + + List reviews = repository.findAll(); + + assertEquals(2, reviews.size()); + assertTrue(reviews.contains(review1)); + assertFalse(reviews.contains(review2)); + assertFalse(reviews.contains(review3)); + assertTrue(reviews.contains(review4)); + } + + @Test + @DisplayName("Delete should remove all reviews of a book") + void testDeleteBookReviews() { + repository.deleteBookReviews(review2.getBookId()); + + List reviews = repository.findAll(); + + assertEquals(2, reviews.size()); + assertTrue(reviews.contains(review1)); + assertFalse(reviews.contains(review2)); + assertTrue(reviews.contains(review3)); + assertFalse(reviews.contains(review4)); + } + + @Test + @DisplayName("Delete should remove the specified review") + void testDelete() { + repository.delete(review1); + + List reviews = repository.findAll(); + + assertEquals(3, reviews.size()); + assertFalse(reviews.contains(review1)); + assertTrue(reviews.contains(review2)); + assertTrue(reviews.contains(review3)); + assertTrue(reviews.contains(review4)); + } + + @Test + @DisplayName("DeleteAll should remove all reviews") + void testDeleteAll() { + repository.deleteAll(); + + List reviews = repository.findAll(); + + assertTrue(reviews.isEmpty()); + assertEquals(0, reviews.size()); + } + + @Test + @DisplayName("Delete should not throw exception when review doesn't exist") + void testDeleteNonExistentReview() { + LocalDate purchaseDate = LocalDate.of(2026, 3, 24); + Review nonExistentReview = Review.builder() + .note(1) + .comment("nul") + .purchaseDate(purchaseDate) + .build(); + nonExistentReview.setRandomUUID(); + + assertDoesNotThrow(() -> repository.delete(nonExistentReview)); + + assertEquals(4, repository.findAll().size()); + } + } +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/usecase/ReviewUseCaseTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/usecase/ReviewUseCaseTest.java new file mode 100644 index 0000000..61937b5 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/usecase/ReviewUseCaseTest.java @@ -0,0 +1,318 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.review.usecase; + +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 fr.iut_fbleau.but3.dev62.mylibrary.review.exception.NotValidReviewException; +import fr.iut_fbleau.but3.dev62.mylibrary.review.exception.ReviewNotFoundException; +import fr.iut_fbleau.but3.dev62.mylibrary.review.repository.ReviewRepository; +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.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +import static org.mockito.Mockito.never; + +@ExtendWith(MockitoExtension.class) +public class ReviewUseCaseTest { + + @Mock + private ReviewRepository reviewRepository; + + @InjectMocks + private ReviewUseCase reviewUseCase; + + private UUID reviewId; + private UUID customerId; + private UUID bookId; + private LocalDate purchaseDate; + private Review testReview; + private ReviewInfo validReviewInfo; + + @BeforeEach + void setUp() { + reviewId = UUID.randomUUID(); + customerId = UUID.randomUUID(); + bookId = UUID.randomUUID(); + purchaseDate = LocalDate.of(2026, 5, 24); + testReview = Review.builder() + .reviewId(reviewId) + .customerId(customerId) + .bookId(bookId) + .note(2) + .comment("plutôt mauvais") + .purchaseDate(purchaseDate) + .build(); + + validReviewInfo = new ReviewInfo(2, "plutôt mauvais", purchaseDate); + } + + @Nested + @DisplayName("Register review tests") + class RegisterReviewTests { + + @Test + @DisplayName("Should register review when valid data is provided") + void testRegisterReviewWithValidData() throws NotValidReviewException { + when(reviewRepository.save(any(Review.class))).thenReturn(testReview); + + UUID registeredId = reviewUseCase.registerReview(validReviewInfo); + + assertNotNull(registeredId); + assertEquals(reviewId, registeredId); + verify(reviewRepository, times(1)).save(any(Review.class)); + } + + @Test + @DisplayName("Should throw exception when review data is not valid") + void testRegisterReviewWithInvalidData() { + ReviewInfo invalidReviewInfo = new ReviewInfo(0, "plutôt mauvais", purchaseDate); + + assertThrows(NotValidReviewException.class, + () -> reviewUseCase.registerReview(invalidReviewInfo)); + + verify(reviewRepository, never()).save(any(Review.class)); + } + } + + @Nested + @DisplayName("Find review tests") + class FindReviewTests { + + @Test + @DisplayName("Should return reviews when customer ID exists") + void testFindReviewByCustomerId() { + when(reviewRepository.findByCustomerId(customerId)).thenReturn(new ArrayList(List.of(testReview))); + + ArrayList foundReviews = reviewUseCase.findReviewByCustomerId(customerId); + + assertTrue(!foundReviews.isEmpty()); + boolean allSameCustomer = foundReviews.stream() + .allMatch(review -> review.getCustomerId().equals(customerId)); + assertTrue(allSameCustomer); + verify(reviewRepository, times(1)).findByCustomerId(customerId); + } + + @Test + @DisplayName("Should return empty Optional when customer ID doesn't exist") + void testFindReviewByCustomerIdNotFound() { + UUID nonExistentCustomerId = UUID.randomUUID(); + when(reviewRepository.findByCustomerId(nonExistentCustomerId)).thenReturn(new ArrayList()); + + ArrayList foundReviews = reviewUseCase.findReviewByCustomerId(nonExistentCustomerId); + + assertTrue(foundReviews.isEmpty()); + verify(reviewRepository, times(1)).findByCustomerId(nonExistentCustomerId); + } + + @Test + @DisplayName("Should return reviews when book ID exists") + void testFindReviewByBookId() { + when(reviewRepository.findByBookId(bookId)).thenReturn(new ArrayList(List.of(testReview))); + + ArrayList foundReviews = reviewUseCase.findReviewByBookId(bookId); + + assertTrue(!foundReviews.isEmpty()); + boolean allSameCustomer = foundReviews.stream() + .allMatch(review -> review.getBookId().equals(bookId)); + assertTrue(allSameCustomer); + verify(reviewRepository, times(1)).findByBookId(bookId); + } + + @Test + @DisplayName("Should return empty Optional when book ID doesn't exist") + void testFindReviewByBookIdNotFound() { + UUID nonExistentBookId = UUID.randomUUID(); + when(reviewRepository.findByBookId(nonExistentBookId)).thenReturn(new ArrayList()); + + ArrayList foundReviews = reviewUseCase.findReviewByBookId(nonExistentBookId); + + assertTrue(foundReviews.isEmpty()); + verify(reviewRepository, times(1)).findByBookId(nonExistentBookId); + } + + @Test + @DisplayName("Should return review when Review ID exists") + void testFindReviewByReviewId() { + when(reviewRepository.findByReviewId(reviewId)).thenReturn(Optional.of(testReview)); + + Optional foundReview = reviewUseCase.findReviewByReviewId(reviewId); + + assertTrue(foundReview.isPresent()); + assertEquals(testReview.getBookId(), foundReview.get().getBookId()); + assertEquals(testReview.getNote(), foundReview.get().getNote()); + verify(reviewRepository, times(1)).findByReviewId(reviewId); + } + + @Test + @DisplayName("Should return empty Optional when review ID doesn't exist") + void testFindReviewByReviewIdNotFound() { + UUID nonExistentReviewId = UUID.randomUUID(); + when(reviewRepository.findByReviewId(nonExistentReviewId)).thenReturn(Optional.empty()); + + Optional foundReview = reviewUseCase.findReviewByReviewId(nonExistentReviewId); + + assertTrue(foundReview.isEmpty()); + verify(reviewRepository, times(1)).findByReviewId(nonExistentReviewId); + } + } + + @Nested + @DisplayName("Update review tests") + class UpdateReviewTests { + + @Test + @DisplayName("Should update review when valid data is provided") + void testUpdateReviewWithValidData() throws ReviewNotFoundException, NotValidReviewException { + when(reviewRepository.findByReviewId(reviewId)).thenReturn(Optional.of(testReview)); + + LocalDate updatePurchaseDate = LocalDate.of(2026, 5, 30); + Review updatedReview = Review.builder() + .reviewId(reviewId) + .customerId(customerId) + .bookId(bookId) + .note(4) + .comment("en fait c'est bien") + .purchaseDate(updatePurchaseDate) + .build(); + + when(reviewRepository.save(any(Review.class))).thenReturn(updatedReview); + + ReviewInfo updateInfo = new ReviewInfo(4, "en fait c'est bien", updatePurchaseDate); + + ReviewDTO result = reviewUseCase.updateReview(reviewId, updateInfo); + + assertNotNull(result); + assertEquals(customerId, result.getCustomerId()); + assertEquals(4, result.getNote()); + assertEquals("en fait c'est bien", result.getComment()); + verify(reviewRepository, times(1)).findByReviewId(reviewId); + verify(reviewRepository, times(1)).save(any(Review.class)); + } + + @Test + @DisplayName("Should throw exception when review ID doesn't exist") + void testUpdateReviewNotFound() { + UUID nonExistentReviewId = UUID.randomUUID(); + when(reviewRepository.findByReviewId(nonExistentReviewId)).thenReturn(Optional.empty()); + + LocalDate updatePurchaseDate = LocalDate.of(2026, 5, 24); + ReviewInfo updateInfo = new ReviewInfo(3, "moyen", updatePurchaseDate); + + assertThrows(ReviewNotFoundException.class, + () -> reviewUseCase.updateReview(nonExistentReviewId, updateInfo)); + + verify(reviewRepository, times(1)).findByReviewId(nonExistentReviewId); + verify(reviewRepository, never()).save(any(Review.class)); + } + + @Test + @DisplayName("Should throw exception when update data is not valid") + void testUpdateReviewWithInvalidData() { + LocalDate updatePurchaseDate = LocalDate.of(2026, 5, 24); + ReviewInfo invalidUpdateInfo = new ReviewInfo(0, "éclaté au sol", updatePurchaseDate); + + assertThrows(NotValidReviewException.class, + () -> reviewUseCase.updateReview(reviewId, invalidUpdateInfo)); + + verify(reviewRepository, never()).findByReviewId(any(UUID.class)); + verify(reviewRepository, never()).save(any(Review.class)); + } + } + + @Nested + @DisplayName("Delete review tests") + class DeleteReviewTests { + + @Test + @DisplayName("Should delete reviews when customer ID exists") + void testDeleteCustomerReviews() throws ReviewNotFoundException { + when(reviewRepository.findByCustomerId(customerId)).thenReturn(new ArrayList(List.of(testReview))); + doNothing().when(reviewRepository).delete(testReview); + + reviewUseCase.deleteCustomerReviews(customerId); + + verify(reviewRepository, times(1)).findByCustomerId(customerId); + verify(reviewRepository, times(1)).delete(testReview); + } + + @Test + @DisplayName("Should throw exception when customer ID doesn't exist") + void testDeleteCustomerReviewsNotFound() { + UUID nonExistentCustomerId = UUID.randomUUID(); + when(reviewRepository.findByCustomerId(nonExistentCustomerId)).thenReturn(new ArrayList()); + + assertThrows(ReviewNotFoundException.class, + () -> reviewUseCase.deleteCustomerReviews(nonExistentCustomerId)); + + verify(reviewRepository, times(1)).findByCustomerId(nonExistentCustomerId); + verify(reviewRepository, never()).delete(any(Review.class)); + } + + @Test + @DisplayName("Should delete reviews when book ID exists") + void testDeleteBookReviews() throws ReviewNotFoundException { + when(reviewRepository.findByBookId(bookId)).thenReturn(new ArrayList(List.of(testReview))); + doNothing().when(reviewRepository).delete(testReview); + + reviewUseCase.deleteBookReviews(bookId); + + verify(reviewRepository, times(1)).findByBookId(bookId); + verify(reviewRepository, times(1)).delete(testReview); + } + + @Test + @DisplayName("Should throw exception when book ID doesn't exist") + void testDeleteBookReviewsNotFound() { + UUID nonExistentBookId = UUID.randomUUID(); + when(reviewRepository.findByBookId(nonExistentBookId)).thenReturn(new ArrayList()); + + assertThrows(ReviewNotFoundException.class, + () -> reviewUseCase.deleteBookReviews(nonExistentBookId)); + + verify(reviewRepository, times(1)).findByBookId(nonExistentBookId); + verify(reviewRepository, never()).delete(any(Review.class)); + } + + @Test + @DisplayName("Should delete review when review ID exists") + void testDeleteReview() throws ReviewNotFoundException { + when(reviewRepository.findByReviewId(reviewId)).thenReturn(Optional.of(testReview)); + doNothing().when(reviewRepository).delete(testReview); + + reviewUseCase.deleteReview(reviewId); + + verify(reviewRepository, times(1)).findByReviewId(reviewId); + verify(reviewRepository, times(1)).delete(testReview); + } + + @Test + @DisplayName("Should throw exception when review ID doesn't exist") + void testDeleteReviewNotFound() { + UUID nonExistentReviewId = UUID.randomUUID(); + when(reviewRepository.findByReviewId(nonExistentReviewId)).thenReturn(Optional.empty()); + + assertThrows(ReviewNotFoundException.class, + () -> reviewUseCase.deleteReview(nonExistentReviewId)); + + verify(reviewRepository, times(1)).findByReviewId(nonExistentReviewId); + verify(reviewRepository, never()).delete(any(Review.class)); + } + } +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/validator/ReviewValidatorTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/validator/ReviewValidatorTest.java new file mode 100644 index 0000000..dacdee3 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/review/validator/ReviewValidatorTest.java @@ -0,0 +1,113 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.review.validator; + +import fr.iut_fbleau.but3.dev62.mylibrary.review.ReviewInfo; +import fr.iut_fbleau.but3.dev62.mylibrary.review.exception.NotValidReviewException; +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 static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ReviewValidatorTest { + + @Test + @DisplayName("Should validate review with valid data") + void testValidateValidReview() { + LocalDate purchaseDate = LocalDate.of(2026, 3, 24); + ReviewInfo validReview = new ReviewInfo(3, "Bof", purchaseDate); + + assertDoesNotThrow(() -> ReviewValidator.validate(validReview)); + } + + @Nested + @DisplayName("Note validation tests") + class NoteValidationTests { + + @Test + @DisplayName("Should throw exception when note is lower than 1") + void testValidateNoteLower1() { + LocalDate purchaseDate = LocalDate.of(2026, 3, 24); + ReviewInfo reviewWithNoteLower1 = new ReviewInfo(0, "Bof", purchaseDate); + + NotValidReviewException exception = assertThrows( + NotValidReviewException.class, + () -> ReviewValidator.validate(reviewWithNoteLower1) + ); + + assertEquals(ReviewValidator.NOTE_CANNOT_BE_LOWER_THAN_1, exception.getMessage()); + } + + @Test + @DisplayName("Should throw exception when note is upper than 5") + void testValidateNoteUpper5() { + LocalDate purchaseDate = LocalDate.of(2026, 3, 24); + ReviewInfo reviewWithNoteUpper5 = new ReviewInfo(6, "Bof", purchaseDate); + + NotValidReviewException exception = assertThrows( + NotValidReviewException.class, + () -> ReviewValidator.validate(reviewWithNoteUpper5) + ); + + assertEquals(ReviewValidator.NOTE_CANNOT_BE_UPPER_THAN_5, exception.getMessage()); + } + } + + @Nested + @DisplayName("Comment validation tests") + class CommentValidationTests { + + @Test + @DisplayName("Should throw exception when comment is blank") + void testValidateBlankComment() { + LocalDate purchaseDate = LocalDate.of(2026, 3, 24); + ReviewInfo reviewWithBlankComment = new ReviewInfo(3, "", purchaseDate); + + NotValidReviewException exception = assertThrows( + NotValidReviewException.class, + () -> ReviewValidator.validate(reviewWithBlankComment) + ); + + assertEquals(ReviewValidator.COMMENT_CANNOT_BE_BLANK, exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {" ", " ", "\t", "\n"}) + @DisplayName("Should throw exception when last name contains only whitespace") + void testValidateWhitespaceLastName(String whitespace) { + LocalDate purchaseDate = LocalDate.of(2026, 3, 24); + ReviewInfo reviewWithBlankComment = new ReviewInfo(3, whitespace, purchaseDate); + + NotValidReviewException exception = assertThrows( + NotValidReviewException.class, + () -> ReviewValidator.validate(reviewWithBlankComment) + ); + + assertEquals(ReviewValidator.COMMENT_CANNOT_BE_BLANK, exception.getMessage()); + } + } + + @Nested + @DisplayName("Purchase date validation tests") + class PurchaseDateValidationTests { + + @Test + @DisplayName("Should throw exception when purchase date is after the actual date") + void testValidateFuturPurchaseDate() { + LocalDate futurepurchaseDate = LocalDate.of(2026, 6, 24); + ReviewInfo reviewWithFuturPurchaseDate = new ReviewInfo(2, "Bof", futurepurchaseDate); + + NotValidReviewException exception = assertThrows( + NotValidReviewException.class, + () -> ReviewValidator.validate(reviewWithFuturPurchaseDate) + ); + + assertEquals(ReviewValidator.PURCHASE_DATE_IS_NOT_VALID, exception.getMessage()); + } + } +}