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 index fa3e92b..1c80d77 100644 --- 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 @@ -21,11 +21,8 @@ public final class BookRepository { } public Book save(Book newBook) { - if (newBook.getId() == null) { - newBook.setRandomUUID(); - } Optional optionalBookWithSameId = this.findById(newBook.getId()); - optionalBookWithSameId.ifPresent(books::remove); + optionalBookWithSameId.ifPresentOrElse(books::remove, newBook::setRandomUUID); this.books.add(newBook); return newBook; } diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/converter/BookConverterTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/converter/BookConverterTest.java new file mode 100644 index 0000000..9f8bcdb --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/converter/BookConverterTest.java @@ -0,0 +1,108 @@ +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; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +@DisplayName("BookConverter Unit Tests") +class BookConverterTest { + + @Nested + @DisplayName("toDomain() method tests") + class ToDomainTests { + + @Test + @DisplayName("Should convert BookInfo to Book domain object") + void shouldConvertBookInfoToDomain() { + BookInfo bookInfo = new BookInfo( + "9782016289308", + "Le Petit Prince", + "Antoine de Saint-Exupéry", + "Gallimard", + LocalDate.of(1943, 4, 6), + new BigDecimal("12.90"), + 10, + List.of("Roman", "Jeunesse"), + "Un classique", + "FR" + ); + + Book result = BookConverter.toDomain(bookInfo); + + assertNotNull(result); + assertEquals(bookInfo.isbn(), result.getIsbn()); + assertEquals(bookInfo.title(), result.getTitle()); + assertEquals(bookInfo.author(), result.getAuthor()); + assertEquals(bookInfo.publisher(), result.getPublisher()); + assertEquals(bookInfo.publicationDate(), result.getPublicationDate()); + assertEquals(bookInfo.price(), result.getPrice()); + assertEquals(bookInfo.initialStock(), result.getStock()); + assertEquals(bookInfo.categories(), result.getCategories()); + assertEquals(bookInfo.description(), result.getDescription()); + assertEquals(bookInfo.language(), result.getLanguage()); + } + + @Test + @DisplayName("Should have null ID after toDomain (set by repository)") + void shouldHaveNullIdAfterToDomain() { + BookInfo bookInfo = new BookInfo( + "9782016289308", "Titre", "Auteur", "Editeur", + LocalDate.now(), new BigDecimal("10.00"), 5, + List.of("Roman"), "Description", "FR" + ); + + Book result = BookConverter.toDomain(bookInfo); + + assertNull(result.getId()); + } + } + + @Nested + @DisplayName("toDTO() method tests") + class ToDTOTests { + + @Test + @DisplayName("Should convert Book domain object to BookDTO with all fields mapped correctly") + void shouldConvertBookToDTO() { + UUID id = UUID.randomUUID(); + Book book = Book.builder() + .id(id) + .isbn("9782016289308") + .title("Le Petit Prince") + .author("Antoine de Saint-Exupéry") + .publisher("Gallimard") + .publicationDate(LocalDate.of(1943, 4, 6)) + .price(new BigDecimal("12.90")) + .stock(10) + .categories(List.of("Roman", "Jeunesse")) + .description("Un classique") + .language("FR") + .build(); + + BookDTO result = BookConverter.toDTO(book); + + assertNotNull(result); + assertEquals(book.getId(), result.getId()); + 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()); + } + } +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/entity/BookTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/entity/BookTest.java new file mode 100644 index 0000000..d3e5184 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/entity/BookTest.java @@ -0,0 +1,71 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.book.entity; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +class BookTest { + + @Test + @DisplayName("Builder should create a valid Book instance") + void testBookBuilder() { + UUID id = UUID.randomUUID(); + + Book book = Book.builder() + .id(id) + .isbn("9782016289308") + .title("Le Petit Prince") + .author("Antoine de Saint-Exupéry") + .publisher("Gallimard") + .publicationDate(LocalDate.of(1943, 4, 6)) + .price(new BigDecimal("12.90")) + .stock(10) + .categories(List.of("Roman", "Jeunesse")) + .description("Un classique") + .language("FR") + .build(); + + assertEquals(id, book.getId()); + assertEquals("9782016289308", book.getIsbn()); + assertEquals("Le Petit Prince", book.getTitle()); + assertEquals("Antoine de Saint-Exupéry", book.getAuthor()); + assertEquals("Gallimard", book.getPublisher()); + assertEquals(LocalDate.of(1943, 4, 6), book.getPublicationDate()); + assertEquals(new BigDecimal("12.90"), book.getPrice()); + assertEquals(10, book.getStock()); + assertEquals(List.of("Roman", "Jeunesse"), book.getCategories()); + assertEquals("Un classique", book.getDescription()); + assertEquals("FR", book.getLanguage()); + } + + @Test + @DisplayName("setRandomUUID should set a new non-null UUID") + void testSetRandomUUID() { + Book book = Book.builder().build(); + UUID originalId = book.getId(); + + book.setRandomUUID(); + + assertNotNull(book.getId()); + assertNotEquals(originalId, book.getId()); + } + + @Test + @DisplayName("Two setRandomUUID calls should produce different UUIDs") + void testSetRandomUUIDTwice() { + Book book = Book.builder().build(); + book.setRandomUUID(); + UUID firstId = book.getId(); + + book.setRandomUUID(); + UUID secondId = book.getId(); + + assertNotEquals(firstId, secondId); + } +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/exception/BookNotFoundExceptionTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/exception/BookNotFoundExceptionTest.java new file mode 100644 index 0000000..90ae6e3 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/exception/BookNotFoundExceptionTest.java @@ -0,0 +1,47 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.book.exception; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +class BookNotFoundExceptionTest { + + @Test + @DisplayName("Exception message should contain the UUID provided") + void testExceptionMessageContainsUUID() { + UUID uuid = UUID.randomUUID(); + + BookNotFoundException exception = new BookNotFoundException(uuid); + + String expectedMessage = String.format("The book with id %s does not exist", uuid); + assertEquals(expectedMessage, exception.getMessage()); + } + + @Test + @DisplayName("Exception should use the correct constant message format") + void testExceptionUsesConstantMessageFormat() { + UUID uuid = UUID.randomUUID(); + + BookNotFoundException exception = new BookNotFoundException(uuid); + + assertEquals("The book with id {0} does not exist", + BookNotFoundException.THE_BOOK_WITH_ID_DOES_NOT_EXIST_MESSAGE); + assertTrue(exception.getMessage().contains(uuid.toString())); + } + + @Test + @DisplayName("Exception should be properly thrown and caught") + void testExceptionCanBeThrownAndCaught() { + UUID uuid = UUID.randomUUID(); + + try { + throw new BookNotFoundException(uuid); + } catch (BookNotFoundException e) { + String expectedMessage = String.format("The book with id %s does not exist", uuid); + assertEquals(expectedMessage, e.getMessage()); + } + } +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/exception/NotValidBookExceptionTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/exception/NotValidBookExceptionTest.java new file mode 100644 index 0000000..427bd1b --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/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.*; + +class NotValidBookExceptionTest { + + @Test + @DisplayName("Exception should be created with the provided message") + void testExceptionCreation() { + String errorMessage = "Book data is not valid"; + + NotValidBookException exception = new NotValidBookException(errorMessage); + + assertEquals(errorMessage, exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = { + "ISBN is not valid", + "Title cannot be blank", + "Author cannot be blank", + "Publisher cannot be blank", + "Price must be positive" + }) + @DisplayName("Exception should handle different validation messages") + void testExceptionWithDifferentMessages(String errorMessage) { + NotValidBookException exception = new NotValidBookException(errorMessage); + + assertEquals(errorMessage, exception.getMessage()); + } + + @Test + @DisplayName("Exception should be properly thrown and caught") + void testExceptionCanBeThrownAndCaught() { + String errorMessage = "ISBN is not valid"; + + Exception exception = assertThrows(NotValidBookException.class, () -> { + throw new NotValidBookException(errorMessage); + }); + + assertEquals(errorMessage, exception.getMessage()); + } + + @Test + @DisplayName("Exception should be catchable as a general Exception") + void testExceptionInheritance() { + String errorMessage = "Price must be positive"; + + try { + throw new NotValidBookException(errorMessage); + } catch (Exception e) { + assertEquals(NotValidBookException.class, e.getClass()); + assertEquals(errorMessage, e.getMessage()); + } + } +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/repository/BookRepositoryTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/repository/BookRepositoryTest.java new file mode 100644 index 0000000..3024b91 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/repository/BookRepositoryTest.java @@ -0,0 +1,219 @@ +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.math.BigDecimal; +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +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("9782016289308") + .title("Le Petit Prince") + .author("Antoine de Saint-Exupéry") + .publisher("Gallimard") + .publicationDate(LocalDate.of(1943, 4, 6)) + .price(new BigDecimal("12.90")) + .stock(10) + .categories(List.of("Roman")) + .description("Un classique") + .language("FR") + .build(); + book1.setRandomUUID(); + + book2 = Book.builder() + .isbn("9782070409189") + .title("L'Étranger") + .author("Albert Camus") + .publisher("Gallimard") + .publicationDate(LocalDate.of(1942, 5, 19)) + .price(new BigDecimal("9.50")) + .stock(5) + .categories(List.of("Roman")) + .description("Roman philosophique") + .language("FR") + .build(); + book2.setRandomUUID(); + } + + @Test + @DisplayName("New repository should be empty") + void testNewRepositoryIsEmpty() { + assertTrue(repository.findAll().isEmpty()); + assertEquals(0, repository.findAll().size()); + } + + @Nested + @DisplayName("Save operations") + class SaveOperations { + + @Test + @DisplayName("Save should add a new book") + void testSaveNewBook() { + Book saved = repository.save(book1); + + assertEquals(1, repository.findAll().size()); + assertEquals(book1.getId(), saved.getId()); + assertEquals(book1.getIsbn(), saved.getIsbn()); + } + + @Test + @DisplayName("Save should update existing book with same ID") + void testSaveUpdatesExistingBook() { + repository.save(book1); + UUID id = book1.getId(); + + Book updatedBook = Book.builder() + .id(id) + .isbn("9782016289308") + .title("Le Petit Prince - Edition collector") + .author("Antoine de Saint-Exupéry") + .publisher("Gallimard") + .publicationDate(LocalDate.of(1943, 4, 6)) + .price(new BigDecimal("19.90")) + .stock(3) + .categories(List.of("Roman")) + .description("Edition collector") + .language("FR") + .build(); + + Book saved = repository.save(updatedBook); + + assertEquals(1, repository.findAll().size()); + assertEquals(id, saved.getId()); + assertEquals("Le Petit Prince - Edition collector", saved.getTitle()); + assertEquals(new BigDecimal("19.90"), saved.getPrice()); + } + + @Test + @DisplayName("Save multiple books should add all of them") + void testSaveMultipleBooks() { + repository.save(book1); + repository.save(book2); + + assertEquals(2, repository.findAll().size()); + assertTrue(repository.findAll().contains(book1)); + assertTrue(repository.findAll().contains(book2)); + } + } + + @Nested + @DisplayName("Find operations") + class FindOperations { + + @BeforeEach + void setUpBooks() { + repository.save(book1); + repository.save(book2); + } + + @Test + @DisplayName("FindAll should return all books") + void testFindAll() { + assertEquals(2, repository.findAll().size()); + } + + @Test + @DisplayName("FindById should return book with matching ID") + void testFindById() { + Optional found = repository.findById(book1.getId()); + + assertTrue(found.isPresent()); + assertEquals(book1.getIsbn(), found.get().getIsbn()); + assertEquals(book1.getTitle(), found.get().getTitle()); + } + + @Test + @DisplayName("FindById should return empty Optional when ID doesn't exist") + void testFindByIdNotFound() { + Optional found = repository.findById(UUID.randomUUID()); + + assertTrue(found.isEmpty()); + } + + @Test + @DisplayName("FindByIsbn should return book with matching ISBN") + void testFindByIsbn() { + Optional found = repository.findByIsbn("9782016289308"); + + assertTrue(found.isPresent()); + assertEquals(book1.getId(), found.get().getId()); + } + + @Test + @DisplayName("FindByIsbn should return empty Optional when ISBN doesn't exist") + void testFindByIsbnNotFound() { + Optional found = repository.findByIsbn("0000000000000"); + + assertTrue(found.isEmpty()); + } + + @Test + @DisplayName("ExistsById should return true when ID exists") + void testExistsByIdExists() { + assertTrue(repository.existsById(book1.getId())); + } + + @Test + @DisplayName("ExistsById should return false when ID doesn't exist") + void testExistsByIdNotExists() { + assertFalse(repository.existsById(UUID.randomUUID())); + } + } + + @Nested + @DisplayName("Delete operations") + class DeleteOperations { + + @BeforeEach + void setUpBooks() { + repository.save(book1); + repository.save(book2); + } + + @Test + @DisplayName("Delete should remove the specified book") + void testDelete() { + repository.delete(book1); + + assertEquals(1, repository.findAll().size()); + assertFalse(repository.findAll().contains(book1)); + assertTrue(repository.findAll().contains(book2)); + } + + @Test + @DisplayName("DeleteAll should remove all books") + void testDeleteAll() { + repository.deleteAll(); + + assertTrue(repository.findAll().isEmpty()); + } + + @Test + @DisplayName("Delete should not throw exception when book doesn't exist") + void testDeleteNonExistentBook() { + Book nonExistent = Book.builder().isbn("0000000000000").build(); + nonExistent.setRandomUUID(); + + assertDoesNotThrow(() -> repository.delete(nonExistent)); + assertEquals(2, repository.findAll().size()); + } + } +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/validator/BookValidatorTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/validator/BookValidatorTest.java new file mode 100644 index 0000000..d4bed20 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/validator/BookValidatorTest.java @@ -0,0 +1,184 @@ +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; +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.math.BigDecimal; +import java.time.LocalDate; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class BookValidatorTest { + + private BookInfo validBook() { + return new BookInfo( + "9782016289308", + "Le Petit Prince", + "Antoine de Saint-Exupéry", + "Gallimard", + LocalDate.of(1943, 4, 6), + new BigDecimal("12.90"), + 10, + List.of("Roman"), + "Un classique", + "FR" + ); + } + + @Test + @DisplayName("Should validate book with valid data") + void testValidateValidBook() { + assertDoesNotThrow(() -> BookValidator.validate(validBook())); + } + + @Nested + @DisplayName("ISBN validation tests") + class IsbnValidationTests { + + @Test + @DisplayName("Should throw exception when ISBN is blank") + void testValidateBlankIsbn() { + BookInfo book = new BookInfo("", "Titre", "Auteur", "Editeur", + LocalDate.now(), new BigDecimal("10.00"), 1, List.of("Roman"), "Desc", "FR"); + + NotValidBookException exception = assertThrows(NotValidBookException.class, + () -> BookValidator.validate(book)); + + assertEquals(BookValidator.ISBN_IS_NOT_VALID, exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {"123456", "978201628930", "97820162893088", "abcdefghijklm", "978-016289308"}) + @DisplayName("Should throw exception when ISBN format is invalid") + void testValidateInvalidIsbnFormat(String invalidIsbn) { + BookInfo book = new BookInfo(invalidIsbn, "Titre", "Auteur", "Editeur", + LocalDate.now(), new BigDecimal("10.00"), 1, List.of("Roman"), "Desc", "FR"); + + NotValidBookException exception = assertThrows(NotValidBookException.class, + () -> BookValidator.validate(book)); + + assertEquals(BookValidator.ISBN_IS_NOT_VALID, exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {"9782016289308", "9782070409189", "9782253006329"}) + @DisplayName("Should validate when ISBN has exactly 13 digits") + void testValidateValidIsbn(String validIsbn) { + BookInfo book = new BookInfo(validIsbn, "Titre", "Auteur", "Editeur", + LocalDate.now(), new BigDecimal("10.00"), 1, List.of("Roman"), "Desc", "FR"); + + assertDoesNotThrow(() -> BookValidator.validate(book)); + } + } + + @Nested + @DisplayName("Title validation tests") + class TitleValidationTests { + + @Test + @DisplayName("Should throw exception when title is blank") + void testValidateBlankTitle() { + BookInfo book = new BookInfo("9782016289308", "", "Auteur", "Editeur", + LocalDate.now(), new BigDecimal("10.00"), 1, List.of("Roman"), "Desc", "FR"); + + NotValidBookException exception = assertThrows(NotValidBookException.class, + () -> BookValidator.validate(book)); + + assertEquals(BookValidator.TITLE_CANNOT_BE_BLANK, exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {" ", " ", "\t", "\n"}) + @DisplayName("Should throw exception when title contains only whitespace") + void testValidateWhitespaceTitle(String whitespace) { + BookInfo book = new BookInfo("9782016289308", whitespace, "Auteur", "Editeur", + LocalDate.now(), new BigDecimal("10.00"), 1, List.of("Roman"), "Desc", "FR"); + + NotValidBookException exception = assertThrows(NotValidBookException.class, + () -> BookValidator.validate(book)); + + assertEquals(BookValidator.TITLE_CANNOT_BE_BLANK, exception.getMessage()); + } + } + + @Nested + @DisplayName("Author validation tests") + class AuthorValidationTests { + + @Test + @DisplayName("Should throw exception when author is blank") + void testValidateBlankAuthor() { + BookInfo book = new BookInfo("9782016289308", "Titre", "", "Editeur", + LocalDate.now(), new BigDecimal("10.00"), 1, List.of("Roman"), "Desc", "FR"); + + NotValidBookException exception = assertThrows(NotValidBookException.class, + () -> BookValidator.validate(book)); + + assertEquals(BookValidator.AUTHOR_CANNOT_BE_BLANK, exception.getMessage()); + } + } + + @Nested + @DisplayName("Publisher validation tests") + class PublisherValidationTests { + + @Test + @DisplayName("Should throw exception when publisher is blank") + void testValidateBlankPublisher() { + BookInfo book = new BookInfo("9782016289308", "Titre", "Auteur", "", + LocalDate.now(), new BigDecimal("10.00"), 1, List.of("Roman"), "Desc", "FR"); + + NotValidBookException exception = assertThrows(NotValidBookException.class, + () -> BookValidator.validate(book)); + + assertEquals(BookValidator.PUBLISHER_CANNOT_BE_BLANK, exception.getMessage()); + } + } + + @Nested + @DisplayName("Price validation tests") + class PriceValidationTests { + + @Test + @DisplayName("Should throw exception when price is negative") + void testValidateNegativePrice() { + BookInfo book = new BookInfo("9782016289308", "Titre", "Auteur", "Editeur", + LocalDate.now(), new BigDecimal("-5.00"), 1, List.of("Roman"), "Desc", "FR"); + + NotValidBookException exception = assertThrows(NotValidBookException.class, + () -> BookValidator.validate(book)); + + assertEquals(BookValidator.PRICE_MUST_BE_POSITIVE, exception.getMessage()); + } + + @Test + @DisplayName("Should throw exception when price is zero") + void testValidateZeroPrice() { + BookInfo book = new BookInfo("9782016289308", "Titre", "Auteur", "Editeur", + LocalDate.now(), BigDecimal.ZERO, 1, List.of("Roman"), "Desc", "FR"); + + NotValidBookException exception = assertThrows(NotValidBookException.class, + () -> BookValidator.validate(book)); + + assertEquals(BookValidator.PRICE_MUST_BE_POSITIVE, exception.getMessage()); + } + + @Test + @DisplayName("Should throw exception when price is null") + void testValidateNullPrice() { + BookInfo book = new BookInfo("9782016289308", "Titre", "Auteur", "Editeur", + LocalDate.now(), null, 1, List.of("Roman"), "Desc", "FR"); + + NotValidBookException exception = assertThrows(NotValidBookException.class, + () -> BookValidator.validate(book)); + + assertEquals(BookValidator.PRICE_MUST_BE_POSITIVE, exception.getMessage()); + } + } +}