From 009ac7f5f791875c24e077e2d2a055d2013ce332 Mon Sep 17 00:00:00 2001 From: RKaraMos_EVO Date: Fri, 12 Jun 2026 23:32:17 +0200 Subject: [PATCH] ajout du validator pour les livres + tests --- .../book/validator/BookValidator.java | 93 ++++++++++ .../book/validator/BookValidatorTest.java | 167 ++++++++++++++++++ 2 files changed, 260 insertions(+) create mode 100644 mylibrary/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidator.java create mode 100644 mylibrary/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidatorTest.java diff --git a/mylibrary/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidator.java b/mylibrary/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidator.java new file mode 100644 index 0000000..b278fc5 --- /dev/null +++ b/mylibrary/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidator.java @@ -0,0 +1,93 @@ +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 int TITLE_MAX_LENGTH = 255; + + public static final String ISBN_MUST_BE_POSITIVE = "Isbn must be a positive number"; + public static final String TITLE_CANNOT_BE_BLANK = "Title cannot be blank"; + public static final String TITLE_TOO_LONG = "Title cannot exceed " + TITLE_MAX_LENGTH + " characters"; + public static final String AUTHOR_CANNOT_BE_BLANK = "Author cannot be blank"; + public static final String PUBLISHER_CANNOT_BE_BLANK = "Publisher cannot be blank"; + public static final String PUBLICATION_DATE_REQUIRED = "Publication date is required"; + public static final String PRICE_MUST_BE_POSITIVE = "Price must be strictly positive"; + public static final String QUANTITY_MUST_BE_POSITIVE_OR_ZERO = "Quantity must be >= 0"; + public static final String CATEGORIES_REQUIRED = "At least one category is required"; + public static final String LANGUAGE_CANNOT_BE_BLANK = "Language cannot be blank"; + + private BookValidator() { + + } + + public static void validate(BookInfo book) throws NotValidBookException { + validateIsbn(book); + validateTitle(book); + validateAuthor(book); + validatePublisher(book); + validatePublicationDate(book); + validatePrice(book); + validateQuantity(book); + validateCategories(book); + validateLanguage(book); + } + + private static void validateIsbn(BookInfo book) throws NotValidBookException { + if (book.isbn() <= 0) { + throw new NotValidBookException(ISBN_MUST_BE_POSITIVE); + } + } + + private static void validateTitle(BookInfo book) throws NotValidBookException { + if (book.title() == null || book.title().isBlank()) { + throw new NotValidBookException(TITLE_CANNOT_BE_BLANK); + } + if (book.title().length() > TITLE_MAX_LENGTH) { + throw new NotValidBookException(TITLE_TOO_LONG); + } + } + + private static void validateAuthor(BookInfo book) throws NotValidBookException { + if (book.author() == null || book.author().isBlank()) { + throw new NotValidBookException(AUTHOR_CANNOT_BE_BLANK); + } + } + + private static void validatePublisher(BookInfo book) throws NotValidBookException { + if (book.publisher() == null || book.publisher().isBlank()) { + throw new NotValidBookException(PUBLISHER_CANNOT_BE_BLANK); + } + } + + private static void validatePublicationDate(BookInfo book) throws NotValidBookException { + if (book.publicationDate() == null) { + throw new NotValidBookException(PUBLICATION_DATE_REQUIRED); + } + } + + private static void validatePrice(BookInfo book) throws NotValidBookException { + if (book.price() <= 0) { + throw new NotValidBookException(PRICE_MUST_BE_POSITIVE); + } + } + + private static void validateQuantity(BookInfo book) throws NotValidBookException { + if (book.quantity() < 0) { + throw new NotValidBookException(QUANTITY_MUST_BE_POSITIVE_OR_ZERO); + } + } + + private static void validateCategories(BookInfo book) throws NotValidBookException { + if (book.categories() == null || book.categories().isEmpty()) { + throw new NotValidBookException(CATEGORIES_REQUIRED); + } + } + + private static void validateLanguage(BookInfo book) throws NotValidBookException { + if (book.language() == null || book.language().isBlank()) { + throw new NotValidBookException(LANGUAGE_CANNOT_BE_BLANK); + } + } +} diff --git a/mylibrary/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidatorTest.java b/mylibrary/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidatorTest.java new file mode 100644 index 0000000..0ec3938 --- /dev/null +++ b/mylibrary/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidatorTest.java @@ -0,0 +1,167 @@ +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.entity.Category; +import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.NotValidBookException; +import java.time.LocalDate; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.junit.jupiter.api.Assertions.*; + +class BookValidatorTest { + + private BookInfo valid() { + return new BookInfo( + 9780321125217L, + "Domain-Driven Design", + "Eric Evans", + "Addison-Wesley", + LocalDate.of(2003, 8, 22), + 54.99, + 10, + List.of(Category.SCIENCE), + "Tackling complexity in the heart of software", + "EN" + ); + } + + @Test + @DisplayName("Should accept a valid BookInfo") + void testValidBook() { + assertDoesNotThrow(() -> BookValidator.validate(valid())); + } + + @Nested + @DisplayName("ISBN validation") + class IsbnTests { + + @ParameterizedTest + @ValueSource(longs = {0L, -1L, -9999L}) + @DisplayName("Should reject ISBN that is not strictly positive") + void testInvalidIsbn(long isbn) { + BookInfo info = new BookInfo(isbn, "T", "A", "P", LocalDate.now(), 1.0, 0, List.of(Category.SCIENCE), "", "EN"); + NotValidBookException ex = assertThrows(NotValidBookException.class, () -> BookValidator.validate(info)); + assertEquals(BookValidator.ISBN_MUST_BE_POSITIVE, ex.getMessage()); + } + } + + @Nested + @DisplayName("Title validation") + class TitleTests { + + @ParameterizedTest + @ValueSource(strings = {"", " ", "\t", "\n"}) + @DisplayName("Should reject blank title") + void testBlankTitle(String title) { + BookInfo info = new BookInfo(1L, title, "A", "P", LocalDate.now(), 1.0, 0, List.of(Category.SCIENCE), "", "EN"); + NotValidBookException ex = assertThrows(NotValidBookException.class, () -> BookValidator.validate(info)); + assertEquals(BookValidator.TITLE_CANNOT_BE_BLANK, ex.getMessage()); + } + + @Test + @DisplayName("Should reject null title") + void testNullTitle() { + BookInfo info = new BookInfo(1L, null, "A", "P", LocalDate.now(), 1.0, 0, List.of(Category.SCIENCE), "", "EN"); + NotValidBookException ex = assertThrows(NotValidBookException.class, () -> BookValidator.validate(info)); + assertEquals(BookValidator.TITLE_CANNOT_BE_BLANK, ex.getMessage()); + } + + @Test + @DisplayName("Should reject title longer than 255 characters") + void testTitleTooLong() { + String longTitle = "a".repeat(256); + BookInfo info = new BookInfo(1L, longTitle, "A", "P", LocalDate.now(), 1.0, 0, List.of(Category.SCIENCE), "", "EN"); + NotValidBookException ex = assertThrows(NotValidBookException.class, () -> BookValidator.validate(info)); + assertEquals(BookValidator.TITLE_TOO_LONG, ex.getMessage()); + } + } + + @Nested + @DisplayName("Author / Publisher validation") + class AuthorPublisherTests { + + @ParameterizedTest + @ValueSource(strings = {"", " ", "\t"}) + @DisplayName("Should reject blank author") + void testBlankAuthor(String author) { + BookInfo info = new BookInfo(1L, "T", author, "P", LocalDate.now(), 1.0, 0, List.of(Category.SCIENCE), "", "EN"); + NotValidBookException ex = assertThrows(NotValidBookException.class, () -> BookValidator.validate(info)); + assertEquals(BookValidator.AUTHOR_CANNOT_BE_BLANK, ex.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {"", " ", "\t"}) + @DisplayName("Should reject blank publisher") + void testBlankPublisher(String publisher) { + BookInfo info = new BookInfo(1L, "T", "A", publisher, LocalDate.now(), 1.0, 0, List.of(Category.SCIENCE), "", "EN"); + NotValidBookException ex = assertThrows(NotValidBookException.class, () -> BookValidator.validate(info)); + assertEquals(BookValidator.PUBLISHER_CANNOT_BE_BLANK, ex.getMessage()); + } + } + + @Nested + @DisplayName("Date / Price / Quantity validation") + class NumericTests { + + @Test + @DisplayName("Should reject null publication date") + void testNullDate() { + BookInfo info = new BookInfo(1L, "T", "A", "P", null, 1.0, 0, List.of(Category.SCIENCE), "", "EN"); + NotValidBookException ex = assertThrows(NotValidBookException.class, () -> BookValidator.validate(info)); + assertEquals(BookValidator.PUBLICATION_DATE_REQUIRED, ex.getMessage()); + } + + @ParameterizedTest + @ValueSource(doubles = {0.0, -0.01, -10.0}) + @DisplayName("Should reject non-positive price") + void testNonPositivePrice(double price) { + BookInfo info = new BookInfo(1L, "T", "A", "P", LocalDate.now(), price, 0, List.of(Category.SCIENCE), "", "EN"); + NotValidBookException ex = assertThrows(NotValidBookException.class, () -> BookValidator.validate(info)); + assertEquals(BookValidator.PRICE_MUST_BE_POSITIVE, ex.getMessage()); + } + + @ParameterizedTest + @ValueSource(ints = {-1, -100}) + @DisplayName("Should reject negative quantity") + void testNegativeQuantity(int quantity) { + BookInfo info = new BookInfo(1L, "T", "A", "P", LocalDate.now(), 1.0, quantity, List.of(Category.SCIENCE), "", "EN"); + NotValidBookException ex = assertThrows(NotValidBookException.class, () -> BookValidator.validate(info)); + assertEquals(BookValidator.QUANTITY_MUST_BE_POSITIVE_OR_ZERO, ex.getMessage()); + } + } + + @Nested + @DisplayName("Categories / Language validation") + class MetadataTests { + + @Test + @DisplayName("Should reject empty categories") + void testEmptyCategories() { + BookInfo info = new BookInfo(1L, "T", "A", "P", LocalDate.now(), 1.0, 0, List.of(), "", "EN"); + NotValidBookException ex = assertThrows(NotValidBookException.class, () -> BookValidator.validate(info)); + assertEquals(BookValidator.CATEGORIES_REQUIRED, ex.getMessage()); + } + + @Test + @DisplayName("Should reject null categories") + void testNullCategories() { + BookInfo info = new BookInfo(1L, "T", "A", "P", LocalDate.now(), 1.0, 0, null, "", "EN"); + NotValidBookException ex = assertThrows(NotValidBookException.class, () -> BookValidator.validate(info)); + assertEquals(BookValidator.CATEGORIES_REQUIRED, ex.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {"", " ", "\t"}) + @DisplayName("Should reject blank language") + void testBlankLanguage(String language) { + BookInfo info = new BookInfo(1L, "T", "A", "P", LocalDate.now(), 1.0, 0, List.of(Category.SCIENCE), "", language); + NotValidBookException ex = assertThrows(NotValidBookException.class, () -> BookValidator.validate(info)); + assertEquals(BookValidator.LANGUAGE_CANNOT_BE_BLANK, ex.getMessage()); + } + } +}