Api java #6

Merged
Riad KARA-MOSTEFA merged 5 commits from api_java into main 2026-06-12 23:34:30 +02:00
2 changed files with 260 additions and 0 deletions
Showing only changes of commit 009ac7f5f7 - Show all commits
@@ -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);
}
}
}
@@ -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());
}
}
}